From 82942d9cce6b4c3b93d147bcac8ca4a599017fec Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 18 Dec 1998 22:00:30 +0000 Subject: [PATCH 0001/2594] 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 0000000000..e69de29bb2 diff --git a/version.py b/version.py new file mode 100644 index 0000000000..918a1ded43 --- /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 From 5e72268255a51c78cbeb6ef82a66af3c7b90cef8 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 18 Dec 1998 23:46:33 +0000 Subject: [PATCH 0002/2594] 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 0000000000..b5fe1874e1 --- /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 From 53be0347c57aecd9b1144d71edeb727f86d6ffa0 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 22 Dec 1998 12:42:04 +0000 Subject: [PATCH 0003/2594] 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 b5fe1874e1..3eee9b3507 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -15,6 +15,7 @@ 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 From bb0ab0bba658a8f0467ed870bcc687aab0d1d4ff Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 6 Jan 1999 14:46:06 +0000 Subject: [PATCH 0004/2594] 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 3eee9b3507..1d8231abc7 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -11,24 +11,26 @@ __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 From 4698daf69a1f9760e8870609c995bb18c14c7a62 Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Wed, 6 Jan 1999 16:28:34 +0000 Subject: [PATCH 0005/2594] Update and add docstrings. --- sysconfig.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 1d8231abc7..04551a77aa 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 @@ 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) From 18d23e41a229ed4ec07f17ed7499bd1cdbfab606 Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Mon, 11 Jan 1999 15:34:55 +0000 Subject: [PATCH 0006/2594] 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 04551a77aa..e71ae4668a 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -16,8 +16,8 @@ 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.""" From 7c5088ade77bf14bf5ca03309fd684093733c1e9 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 13 Jan 1999 16:12:04 +0000 Subject: [PATCH 0007/2594] 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 0000000000..9fff941ec9 --- /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) + From e57683381739ada3b56e7fc02e52b4c81f7defea Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 18 Jan 1999 17:08:16 +0000 Subject: [PATCH 0008/2594] 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 9fff941ec9..9e1a73b798 100644 --- a/text_file.py +++ b/text_file.py @@ -9,7 +9,7 @@ __revision__ = "$Id$" from types import * -import os, string, re +import sys, os, string, re class TextFile: @@ -67,6 +67,15 @@ def close (self): 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 = '' From ecab4f612281cac63885b065866195177947e5e3 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 8 Mar 1999 21:46:11 +0000 Subject: [PATCH 0009/2594] Added collapse_ws option. --- text_file.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/text_file.py b/text_file.py index 9e1a73b798..eab498d76c 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 @@ def readline (self): 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 From 681a078a9561a64f87802bbe5d7d1e3f7abb4208 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 22 Mar 1999 14:52:19 +0000 Subject: [PATCH 0010/2594] 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 0000000000..3a7443c78e --- /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 0000000000..6605ad2cb1 --- /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 0000000000..c63ce61b8e --- /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 0000000000..f6cae82dc5 --- /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 0000000000..7c13abe09d --- /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 () From cc115bf7157ec6636b0baa75630f61f03ddc6906 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 22 Mar 1999 14:54:09 +0000 Subject: [PATCH 0011/2594] 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 f6cae82dc5..0000000000 --- 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 From df17f2c56422e29ed2b2bc07dfaf373b2f24eede Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 22 Mar 1999 14:55:25 +0000 Subject: [PATCH 0012/2594] 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 0000000000..9567fd13fb --- /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 0000000000..788609282d --- /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 0000000000..ae133f9c2a --- /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 0000000000..1f457807e1 --- /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 0000000000..f2fa842087 --- /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 0000000000..f2fa842087 --- /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 From 99942c9809e7014fa412f7d73d0121dd9c70f68c Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 23 Mar 1999 14:00:06 +0000 Subject: [PATCH 0013/2594] 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 eab498d76c..6153dea3df 100644 --- a/text_file.py +++ b/text_file.py @@ -13,9 +13,6 @@ 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 @@ def __init__ (self, filename=None, **options): 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 From 6c18a676e72670f94bd1f1a625d33d627fabb119 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 26 Mar 1999 21:48:59 +0000 Subject: [PATCH 0014/2594] 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 6153dea3df..1d579565d5 100644 --- a/text_file.py +++ b/text_file.py @@ -52,6 +52,11 @@ def __init__ (self, filename=None, file=None, **options): 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 @@ def readline (self): 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 @@ def readline (self): 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 @@ def readline (self): 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 @@ def readline (self): 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 @@ def readline (self): # 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: From b7509c18d82c99dc663763d51209a091f7306763 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 29 Mar 1999 18:01:49 +0000 Subject: [PATCH 0015/2594] 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 1d579565d5..bc56a4906b 100644 --- a/text_file.py +++ b/text_file.py @@ -53,10 +53,10 @@ def __init__ (self, filename=None, file=None, **options): 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 @@ def warn (self, msg): 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 @@ def readline (self): 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 @@ def readline (self): 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 @@ def readline (self): 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 @@ def readline (self): # 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 @@ def readlines (self): lines.append (line) + def unreadline (self, line): + self.linebuf.append (line) + + if __name__ == "__main__": test_data = """# test file From a3af0a0d6a3cd60ff4a97d970b7ebe0a2dd6748b Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 4 Apr 1999 02:46:29 +0000 Subject: [PATCH 0016/2594] 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 ae133f9c2a..0ed8486ea0 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -84,11 +84,10 @@ def run (self): 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 @@ def run (self): # 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 f2fa842087..22ab71e799 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -33,9 +33,7 @@ def run (self): # 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 f2fa842087..22ab71e799 100644 --- a/command/install_py.py +++ b/command/install_py.py @@ -33,9 +33,7 @@ def run (self): # 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 () From 4e091bdd15624bc17b8f1f45091848e23e8be099 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 4 Apr 1999 02:54:20 +0000 Subject: [PATCH 0017/2594] 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 7c13abe09d..f4a1df791f 100644 --- a/util.py +++ b/util.py @@ -18,7 +18,7 @@ # 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 () From 542cc6a75696e8edf46dd30c53b1fe5b73e86a2e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 4 Apr 1999 02:58:07 +0000 Subject: [PATCH 0018/2594] 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 3a7443c78e..980ba5b30d 100644 --- a/core.py +++ b/core.py @@ -10,10 +10,11 @@ __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 @@ def run_peer (self, 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 From 8806d89547055f3a8b1246bf100338c5270b9fe9 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 15 Apr 1999 17:50:19 +0000 Subject: [PATCH 0019/2594] '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 bc56a4906b..a27df06d45 100644 --- a/text_file.py +++ b/text_file.py @@ -72,12 +72,14 @@ def close (self): 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") From 4da83200ca7258a4c4d175c53f7b1b310db013cb Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 2 May 1999 21:39:13 +0000 Subject: [PATCH 0020/2594] 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 788609282d..d0e939e9b6 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 @@ def set_default_options (self): 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 0ed8486ea0..d956eef396 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -15,21 +15,15 @@ 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 @@ def run (self): 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 1f457807e1..ec73b1c1a8 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 @@ def set_default_options (self): 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 22ab71e799..f147af47f3 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 @@ def set_final_options (self): 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 @@ def run (self): # 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 22ab71e799..f147af47f3 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 @@ def set_final_options (self): 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 @@ def run (self): # 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 From 8e84928345bf830b0a8fe08f37627d315d5f897a Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 2 May 1999 21:42:05 +0000 Subject: [PATCH 0021/2594] 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 980ba5b30d..4b8c78384e 100644 --- a/core.py +++ b/core.py @@ -636,10 +636,10 @@ 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) + 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 @@ def copy_tree (self, infile, outfile, """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 f4a1df791f..7aedc1c6df 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 () From 4c91f4ee1b115815b34eb36da15553c41be1ef80 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 8 Jun 1999 01:58:36 +0000 Subject: [PATCH 0022/2594] 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 e71ae4668a..5c60ca4a49 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 @@ def _init_posix(): exec "_init_%s()" % os.name del _init_posix +del _init_nt From acd36b84863fab989ea5d731f787300f579e7861 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 8 Jun 1999 02:02:00 +0000 Subject: [PATCH 0023/2594] 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 4b8c78384e..8a6cf72037 100644 --- a/core.py +++ b/core.py @@ -136,6 +136,10 @@ def __init__ (self, attrs=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 = {} # The rest of these are really the business of various commands, @@ -152,9 +156,23 @@ def __init__ (self, attrs=None): 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 @@ def parse_command_line (self, args): # 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 @@ def get_options (self, *options): # -- 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): From 50fc61eddf6d3a2028dddaeeb106bc783de0efd5 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 8 Jun 1999 02:04:36 +0000 Subject: [PATCH 0024/2594] 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 ec73b1c1a8..54b81879a5 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 @@ def set_default_options (self): 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 @@ def set_final_options (self): 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 @@ def run (self): 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). From 93d1813d1383f5f6c7cd004143fd97c8d18cbecf Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 8 Jun 1999 17:05:21 +0000 Subject: [PATCH 0025/2594] 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 7aedc1c6df..9a299dfd83 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 From 3cba55ebdce6957864921de0b015f6118afdf2c0 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 10 Jul 1999 02:01:44 +0000 Subject: [PATCH 0026/2594] 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 6605ad2cb1..f5ef385120 100644 --- a/errors.py +++ b/errors.py @@ -12,9 +12,9 @@ __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 @@ class DistutilsFileError (DistutilsError): 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 @@ class DistutilsOptionError (DistutilsError): DistutilsArgError = 'DistutilsArgError' DistutilsFileError = 'DistutilsFileError' DistutilsOptionError = 'DistutilsOptionError' + DistutilsPlatformError = 'DistutilsPlatformError' + +del types From 029a6337d35a2ddabacbf5e00a07bfb87e163fc7 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 10 Jul 1999 02:02:31 +0000 Subject: [PATCH 0027/2594] 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 54b81879a5..01ea0052e6 100644 --- a/command/install.py +++ b/command/install.py @@ -165,6 +165,14 @@ def set_final_options (self): # 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: From 6c7798416f22b36ecf283e3f379f29173cad60c5 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 10 Jul 1999 02:03:53 +0000 Subject: [PATCH 0028/2594] 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 0000000000..7be0ba223d --- /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 From 49295f84d81b8262a04a374d1c1c33e8bbbff6e2 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 10 Jul 1999 02:04:22 +0000 Subject: [PATCH 0029/2594] 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 0000000000..fb5ed6677c --- /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 () From 8c9d03208b4de8de842b039922fad2373ea6bf0f Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 10 Aug 1999 20:09:38 +0000 Subject: [PATCH 0030/2594] 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 a27df06d45..2e034301c5 100644 --- a/text_file.py +++ b/text_file.py @@ -15,7 +15,6 @@ 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 @@ def __init__ (self, filename=None, file=None, **options): # 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 @@ def warn (self, msg, line=None): 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 @@ def readline (self): 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 @@ def readline (self): 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 @@ def test_input (count, description, file, expected_result): 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) From 6c5612c1cbdcc3fe4762d0ef80948a37a7cbdc3b Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 14 Aug 1999 23:43:45 +0000 Subject: [PATCH 0031/2594] Added DistutilsExecError, DistutilsValueError. --- errors.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/errors.py b/errors.py index f5ef385120..17d1abc796 100644 --- a/errors.py +++ b/errors.py @@ -52,12 +52,22 @@ class DistutilsFileError (DistutilsError): 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 @@ class DistutilsPlatformError (DistutilsError): DistutilsArgError = 'DistutilsArgError' DistutilsFileError = 'DistutilsFileError' DistutilsOptionError = 'DistutilsOptionError' + DistutilsValueError = 'DistutilsValueError' DistutilsPlatformError = 'DistutilsPlatformError' - + DistutilsExecError = 'DistutilsExecError' + del types From 08b303ebb482dc760d1f7b793ad61475885b98bc Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 14 Aug 1999 23:44:37 +0000 Subject: [PATCH 0032/2594] 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 c63ce61b8e..125dceb3e1 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) From d1423866abe4c8a4416b514ca8c9f06f9283dcd7 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 14 Aug 1999 23:47:21 +0000 Subject: [PATCH 0033/2594] Comment tweak. --- core.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/core.py b/core.py index 8a6cf72037..a9c573258a 100644 --- a/core.py +++ b/core.py @@ -166,11 +166,11 @@ def __init__ (self, attrs=None): # 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__ () From 5cf06667069e8044604eaa23c4d78bd61a31de74 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 14 Aug 1999 23:50:50 +0000 Subject: [PATCH 0034/2594] 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 7be0ba223d..d5519cab1c 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -11,6 +11,7 @@ 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 @@ def __init__ (self): # 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 @@ def __init__ (self): # '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 @@ def set_library_dirs (self, dirs): 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 @@ def link_static_lib (self, 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 @@ def link_shared_object (self, 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 From 570f701e1116a7f768361b28323f16def9c3da31 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 14 Aug 1999 23:53:53 +0000 Subject: [PATCH 0035/2594] 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 fb5ed6677c..c8468f965a 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -17,7 +17,7 @@ __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 @@ 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 @@ def __init__ (self): 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 @@ def compile (self, 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 @@ def link_shared_lib (self, 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 From e78649a4e18096a1722b66030a160e4f8b90431f Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 14 Aug 1999 23:57:17 +0000 Subject: [PATCH 0036/2594] 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 0000000000..24e76ef10e --- /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 () From d8eaecdab17b05bfe965bac07a69b59edba79799 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 14 Aug 1999 23:57:49 +0000 Subject: [PATCH 0037/2594] 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 0000000000..cb9da68dd9 --- /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 From e168908a1284d0f1bccbdaa6b34ef9ee56be675f Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 19 Aug 1999 20:02:10 +0000 Subject: [PATCH 0038/2594] 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 01ea0052e6..cf45004f07 100644 --- a/command/install.py +++ b/command/install.py @@ -226,7 +226,7 @@ def replace_sys_prefix (self, config_attr, fallback_postfix, use_exec=0): # 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 () From 6ad6cbda7915f2134e949639fa5deec460b2338f Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 29 Aug 1999 18:15:07 +0000 Subject: [PATCH 0039/2594] 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 0000000000..ff66f913be --- /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 () + + From 39a6cfad1bbad1865216c88bf6b9a24c0a0a98f7 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 29 Aug 1999 18:17:36 +0000 Subject: [PATCH 0040/2594] 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 d5519cab1c..5f27ebc9b7 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -294,7 +294,8 @@ def link_shared_lib (self, 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 @@ def link_shared_object (self, 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 @@ def link_shared_object (self, # -- 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 From 0ae40f52f7ec42a97ac5ba7f5a19dc7e61b06427 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 29 Aug 1999 18:18:26 +0000 Subject: [PATCH 0041/2594] 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 cb9da68dd9..a0464b4ea0 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -178,10 +178,9 @@ def build_extensions (self, extensions): 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 () From 0ddb1bcf7e8dd498d71f29941f3b148dc81f55e4 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 29 Aug 1999 18:19:01 +0000 Subject: [PATCH 0042/2594] 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 d956eef396..d75bf3f615 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -55,6 +55,10 @@ def run (self): # 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.*'] From 0911797ba33945835d936fde6db2cd1c57fda9f2 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 29 Aug 1999 18:19:37 +0000 Subject: [PATCH 0043/2594] 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 f147af47f3..876a34ce61 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -9,7 +9,7 @@ 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 @@ def run (self): 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 f147af47f3..876a34ce61 100644 --- a/command/install_py.py +++ b/command/install_py.py @@ -9,7 +9,7 @@ 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 @@ def run (self): 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. ;-( From cb5a002f13cc594941024531f7949013f47e598b Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 29 Aug 1999 18:20:32 +0000 Subject: [PATCH 0044/2594] Patch from Perry Stoll: import types module. --- core.py | 1 + 1 file changed, 1 insertion(+) diff --git a/core.py b/core.py index a9c573258a..2d5d066145 100644 --- a/core.py +++ b/core.py @@ -12,6 +12,7 @@ import sys, os import string, re +from types import * from distutils.errors import * from distutils.fancy_getopt import fancy_getopt from distutils import util From 8d83e47358a6a67a91fb5d2d2fe180c717b0a2be Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 29 Aug 1999 18:20:56 +0000 Subject: [PATCH 0045/2594] 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 24e76ef10e..3a0702dc9d 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, From c152a6391d7f1e688764758c6a156219ab081ce5 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 29 Aug 1999 18:22:13 +0000 Subject: [PATCH 0046/2594] 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 5c60ca4a49..8eaf17dc35 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 From badcf0591e3b166bd7c6e52926e54390d07a96e0 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 29 Aug 1999 18:23:32 +0000 Subject: [PATCH 0047/2594] 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 c8468f965a..6594043c73 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 @@ def link_shared_lib (self, 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 @@ def link_shared_object (self, 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 From 8fb6d44119d8eadaa89e54db50e804726f6fa98f Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 8 Sep 1999 02:23:28 +0000 Subject: [PATCH 0048/2594] [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 3a0702dc9d..eee8e7f4a0 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) From 053838f4381b3616afaa3cd161a81d61969559ab Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 8 Sep 1999 02:29:08 +0000 Subject: [PATCH 0049/2594] 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 5f27ebc9b7..b1167ac25a 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -290,6 +290,30 @@ def link_static_lib (self, 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 () From f57477b38afe2a8084580f112be920884c74fcb5 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 8 Sep 1999 02:32:19 +0000 Subject: [PATCH 0050/2594] 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 6594043c73..a868451462 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -21,7 +21,7 @@ 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 @@ def compile (self, 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 @@ def compile (self, 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 @@ def link_shared_object (self, 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 @@ def library_filename (self, libname): 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 () From 74deaef6c7323d3512b25e14cfea5f13fbfcb37e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 8 Sep 1999 02:36:01 +0000 Subject: [PATCH 0051/2594] 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 ff66f913be..f328232bc0 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -11,23 +11,13 @@ 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 @@ def __init__ (self, 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 @@ def __init__ (self, 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 @@ def compile (self, 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 @@ def compile (self, 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 @@ def link_static_lib (self, 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 @@ def link_shared_object (self, 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 @@ def shared_library_filename (self, libname): 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 () - - From 7fc78eeacf803248087fcf97f900c43656e02946 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 8 Sep 1999 02:41:09 +0000 Subject: [PATCH 0052/2594] 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 2d5d066145..8d2572afca 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 @@ def __init__ (self, attrs=None): 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 @@ def __init__ (self, attrs=None): # 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 @@ def __init__ (self, attrs=None): # '.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 @@ def parse_command_line (self, args): 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 @@ def parse_command_line (self, args): "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 @@ def run_command (self, command): 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 @@ def get_option (self, option): 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 @@ def get_options (self, *options): 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 @@ def set_option (self, option, value): 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 @@ def set_options (self, **optval): # -- 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 From ba6f5bc4f0534bb3ebab3a17f16686f613e42707 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 8 Sep 1999 02:42:30 +0000 Subject: [PATCH 0053/2594] 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 a0464b4ea0..a3982c1b22 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 @@ def set_final_options (self): 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 @@ def run (self): # 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 @@ def run (self): 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 d75bf3f615..28aefa9e7e 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 @@ def run (self): self.set_final_options () - (modules, package) = \ - self.distribution.get_options ('py_modules', 'package') - package = package or '' - infiles = [] outfiles = [] missing = [] @@ -56,20 +56,20 @@ def run (self): # 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: From cb448b16d3ad06c7158fe5a63cdb2f66b401935d Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 13 Sep 1999 03:03:01 +0000 Subject: [PATCH 0054/2594] 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 8d2572afca..a6c378d36c 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 @@ def set_option (self, option, value): 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 @@ def set_peer_option (self, command, option, value): 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 @@ def copy_tree (self, infile, outfile, 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): From e9f33b359886fec9cc43caf900f3f1e8a12a44ae Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 13 Sep 1999 03:07:24 +0000 Subject: [PATCH 0055/2594] 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 b1167ac25a..0e505338be 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -12,6 +12,7 @@ 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 @@ def __init__ (self, 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 @@ def set_link_objects (self, objects): 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 @@ def compile (self, 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 @@ def link_static_lib (self, def link_shared_lib (self, objects, output_libname, + output_dir=None, libraries=None, library_dirs=None, build_info=None): @@ -330,25 +338,37 @@ def link_shared_lib (self, 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 @@ def shared_library_filename (self, libname): 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 @@ def shared_library_name (self, inname): # -- 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 From 38e16941af7fee0698b7c2f53508a3a6b461a30c Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 13 Sep 1999 03:09:38 +0000 Subject: [PATCH 0056/2594] 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 9a299dfd83..bb790af438 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 () From d16f82401008b9b8c470bd3506c34fe29cd4f8d7 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 13 Sep 1999 03:10:25 +0000 Subject: [PATCH 0057/2594] 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 0000000000..360a8027b7 --- /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 From 6d41846de7faa855ac30ea57349780d3cc074f96 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 13 Sep 1999 03:12:53 +0000 Subject: [PATCH 0058/2594] 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 a868451462..0149313110 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -17,12 +17,13 @@ __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 @@ def __init__ (self, 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 @@ def compile (self, 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 @@ def compile (self, 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 @@ def link_shared_object (self, 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 ) From 7a95432812567cf0ce8135334619b557229c7a15 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 13 Sep 1999 13:52:12 +0000 Subject: [PATCH 0059/2594] 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 d0e939e9b6..187dddc0b7 100644 --- a/command/build.py +++ b/command/build.py @@ -30,7 +30,7 @@ def set_final_options (self): 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 @@ def run (self): self.run_peer ('build_py') # And now 'build_ext' - #self.run_peer ('build_ext') + self.run_peer ('build_ext') # end class Build From 3ae6c30ea10386dbf794f794af67fb5fe467d4e2 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 13 Sep 1999 13:54:06 +0000 Subject: [PATCH 0060/2594] 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 28aefa9e7e..0bbe339aa2 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -26,6 +26,9 @@ def set_default_options (self): 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 = '' From a66fe4c0aa10aa6856e000034efc58d7369aeee1 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 13 Sep 1999 13:55:34 +0000 Subject: [PATCH 0061/2594] 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 a3982c1b22..299158128b 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 @@ def set_default_options (self): 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 @@ def run (self): 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 @@ def build_extensions (self, extensions): 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 () From fc9f4cea6fc24a4e9fd19b16fbabdfad394e9742 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 13 Sep 1999 13:57:26 +0000 Subject: [PATCH 0062/2594] 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 cf45004f07..6fb853618b 100644 --- a/command/install.py +++ b/command/install.py @@ -139,42 +139,33 @@ def set_final_options (self): 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 @@ def run (self): # 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 () From 4c89c1907bed072e12306b6e7380b7e46b57285b Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 13 Sep 1999 13:58:34 +0000 Subject: [PATCH 0063/2594] 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 876a34ce61..a2ba16cc35 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -23,15 +23,23 @@ def set_default_options (self): 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 876a34ce61..a2ba16cc35 100644 --- a/command/install_py.py +++ b/command/install_py.py @@ -23,15 +23,23 @@ def set_default_options (self): 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')) From cc443544d4b1095c5d358652a98a79c743599abb Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 21 Sep 1999 18:22:34 +0000 Subject: [PATCH 0064/2594] 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 0bbe339aa2..187e93b58c 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -7,6 +7,9 @@ __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 @@ def set_default_options (self): 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 @@ def run (self): 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 From 27fd97e6fa9b27d71a675771e3921954fe4f1ae8 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 21 Sep 1999 18:27:12 +0000 Subject: [PATCH 0065/2594] 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 299158128b..a0ab61b389 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -16,9 +16,10 @@ 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 @@ def set_default_options (self): 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 @@ def set_final_options (self): 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 @@ def build_extensions (self, extensions): 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 @@ def build_extensions (self, extensions): 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 From 6b9caf68d52702013783cd2225e4ad562c26e14b Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 21 Sep 1999 18:27:55 +0000 Subject: [PATCH 0066/2594] 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 187dddc0b7..b74e51cfea 100644 --- a/command/build.py +++ b/command/build.py @@ -40,10 +40,14 @@ def run (self): # 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 From 0fd123490a06bd8a56edc91e707293b3b37c3514 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 21 Sep 1999 18:31:14 +0000 Subject: [PATCH 0067/2594] 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 6fb853618b..0e4ad3f016 100644 --- a/command/install.py +++ b/command/install.py @@ -7,8 +7,10 @@ __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 @@ def set_default_options (self): 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 @@ def set_final_options (self): # 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 @@ def run (self): # 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 From 36722463169bd14a78f8b0773ed38786fb73cd41 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 21 Sep 1999 18:33:09 +0000 Subject: [PATCH 0068/2594] 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 9567fd13fb..9a5aef2002 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', ] From 79d143e97dffc76896eb203f2c0303337c051466 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 21 Sep 1999 18:35:09 +0000 Subject: [PATCH 0069/2594] 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 0e505338be..2a80739c7b 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: From b2bb06683462c1b7e9df90413d5af326002d93f3 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 21 Sep 1999 18:36:15 +0000 Subject: [PATCH 0070/2594] 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 0149313110..02fbc66aa7 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -202,7 +202,17 @@ def link_shared_object (self, # 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] From 602f5dabbeffa74e600e9fca91ef7295521affce Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 21 Sep 1999 18:37:51 +0000 Subject: [PATCH 0071/2594] 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 bb790af438..85b04e7a80 100644 --- a/util.py +++ b/util.py @@ -15,6 +15,10 @@ 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 () From 5c182c049b03573ef283d05d1079319d201c669d Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 21 Sep 1999 18:41:36 +0000 Subject: [PATCH 0072/2594] 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 a6c378d36c..e53392133d 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 @@ def __init__ (self, attrs=None): 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 @@ def __init__ (self, attrs=None): # 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 @@ def __init__ (self, attrs=None): # '.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 @@ def __init__ (self, attrs=None): # 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 @@ def get_command_name (self): # 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 @@ def run_peer (self, 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 From 366fb4718e7bad951328391a0439b6d07852b8fe Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 22 Sep 1999 15:24:04 +0000 Subject: [PATCH 0073/2594] 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 e69de29bb2..18deaadf44 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$" From d2ce9d4fb72071aabbe879af96eb405739ee1dee Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 29 Sep 1999 12:12:19 +0000 Subject: [PATCH 0074/2594] 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 e53392133d..f0c97aa3ca 100644 --- a/core.py +++ b/core.py @@ -416,30 +416,25 @@ def run_command (self, command): 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 @@ def get_command_option (self, 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'.""" + """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 @@ def __init__ (self, dist): 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 @@ def set_undefined_options (self, src_cmd, *option_pairs): 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 @@ def move_file (self, src, dst, level=1): 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): From 9a476d732350a106b3dc6ac876320a51a5d22053 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 29 Sep 1999 12:14:16 +0000 Subject: [PATCH 0075/2594] 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 85b04e7a80..aee95d3668 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 From e748eefe84902c7daee5e2bc2e34400b972fe08e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 29 Sep 1999 12:20:55 +0000 Subject: [PATCH 0076/2594] 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 2a80739c7b..bfbb50faf3 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -7,7 +7,7 @@ __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 @@ def compile (self, 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 @@ def compile (self, '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 @@ def link_static_lib (self, 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 @@ def link_static_lib (self, '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 @@ def link_shared_lib (self, 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 @@ def move_file (self, src, dst): # 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 From 5a10a6e29b3f8bab23620ad6385efb0d7f5b4486 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 29 Sep 1999 12:22:50 +0000 Subject: [PATCH 0077/2594] 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 02fbc66aa7..f3d9591b4a 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 @@ def compile (self, 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 @@ def compile (self, 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 @@ def link_shared_lib (self, 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 @@ def link_shared_lib (self, output_dir, libraries, library_dirs, - build_info) + extra_preargs, + extra_postargs) def link_shared_object (self, @@ -182,7 +192,8 @@ def link_shared_object (self, 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 @@ def link_shared_object (self, 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 @@ def link_shared_object (self, 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) From 746c9403521ce115a30d553275740a65af7efb63 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 29 Sep 1999 12:29:10 +0000 Subject: [PATCH 0078/2594] 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 f328232bc0..64b2730713 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 @@ def __init__ (self, _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 @@ def compile (self, 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 @@ def compile (self, 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 @@ def link_static_lib (self, 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 @@ def link_shared_object (self, 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 ) From ef61002f9b2e49fe27ff73f7da6003844eac6943 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 29 Sep 1999 12:38:18 +0000 Subject: [PATCH 0079/2594] 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 b74e51cfea..1586e60b07 100644 --- a/command/build.py +++ b/command/build.py @@ -12,31 +12,32 @@ 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 0e4ad3f016..cd12f6fc5b 100644 --- a/command/install.py +++ b/command/install.py @@ -16,16 +16,16 @@ 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 @@ def set_final_options (self): # 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 @@ def replace_sys_prefix (self, config_attr, fallback_postfix, use_exec=0): 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 360a8027b7..3fb756c6a1 100644 --- a/command/install_ext.py +++ b/command/install_ext.py @@ -11,28 +11,27 @@ 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 a2ba16cc35..978bb3b958 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -8,7 +8,7 @@ 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 @@ def set_final_options (self): # 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 a2ba16cc35..978bb3b958 100644 --- a/command/install_py.py +++ b/command/install_py.py @@ -8,7 +8,7 @@ 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 @@ def set_final_options (self): # 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 From dc070bb95b5ab859dc3a9fa613a960a62c977f45 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 29 Sep 1999 12:44:57 +0000 Subject: [PATCH 0080/2594] 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 187e93b58c..3a7a43bb1e 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -17,19 +17,19 @@ 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 @@ def run (self): # metadata to know that a file is meant to be interpreted by # Python?) - self.set_final_options () - infiles = [] outfiles = [] missing = [] @@ -155,7 +153,7 @@ def check_module (self, module, module_file): # 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 @@ def find_modules (self, package, package_dir): 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 @@ def build_modules (self): 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 @@ def build_modules (self): # 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 @@ def build_packages (self): # 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) From 0e1de2192cf0ccfd9196aec77cdae0ae04eda971 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 29 Sep 1999 12:49:35 +0000 Subject: [PATCH 0081/2594] 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 a0ab61b389..8c5065f6bc 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 @@ def set_default_options (self): 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 @@ def set_final_options (self): '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 @@ def set_final_options (self): 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 @@ def check_extensions_list (self, extensions): # 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 @@ def build_extensions (self, extensions): 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 @@ def build_extensions (self, extensions): 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 () From 23e30bcec4cff109e9545d49763f6166688b88f1 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 29 Sep 1999 12:50:13 +0000 Subject: [PATCH 0082/2594] 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 0000000000..222296f363 --- /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! + + From 3a5f9618d77b220f7e47e6a2c0204472333a7356 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 29 Sep 1999 13:03:32 +0000 Subject: [PATCH 0083/2594] 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 2e034301c5..7b29ef4aa5 100644 --- a/text_file.py +++ b/text_file.py @@ -14,6 +14,65 @@ 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 @@ def __init__ (self, filename=None, file=None, **options): 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 @@ def close (self): 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 @@ def warn (self, msg, line=None): 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 @@ def readline (self): 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 @@ def readline (self): # 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 @@ def readline (self): 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 @@ def readlines (self): 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 @@ def unreadline (self, line): continues on next line """ + # result 1: no fancy options result1 = map (lambda x: x + "\n", string.split (test_data, "\n")[0:-1]) From c0b843e02b1e09998d6a56ce259c88c010287b39 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 29 Sep 1999 13:14:27 +0000 Subject: [PATCH 0084/2594] 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 222296f363..3f309745ad 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 @@ def run (self): 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): From 7bea18d0ecf34847d08afd9af1a2dc40799d8119 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 3 Oct 1999 20:41:02 +0000 Subject: [PATCH 0085/2594] 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 bfbb50faf3..486f03ad04 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 @@ def link_static_lib (self, '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 @@ def announce (self, msg, level=1): 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 @@ def move_file (self, src, dst): 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 From 63c863ce22ff96c294f7e57858730b8a400debc9 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 3 Oct 1999 20:45:33 +0000 Subject: [PATCH 0086/2594] 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 f3d9591b4a..8f689196e1 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 @@ def compile (self, # 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 @@ def link_shared_object (self, 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 @@ def link_shared_object (self, # 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 @@ def link_shared_object (self, 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 @@ def shared_object_filename (self, source_filename, output_dir=None): 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 From dba870e5c21cdf14d45ac7f322c75a59a73d249f Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 3 Oct 1999 20:47:52 +0000 Subject: [PATCH 0087/2594] 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 64b2730713..5ac60b2e47 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 @@ def link_shared_object (self, 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 @@ def shared_object_filename (self, source_filename): 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 @@ def shared_library_filename (self, libname): 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 From 364a45f4e008a7361d45a0fdc9a9161d9710f7db Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 3 Oct 1999 20:48:53 +0000 Subject: [PATCH 0088/2594] 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 125dceb3e1..3df2d1da65 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -21,7 +21,12 @@ # 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 From ae141dd4aa010194531808f5c954ca947a9dd8b2 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 3 Oct 1999 20:50:41 +0000 Subject: [PATCH 0089/2594] 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 aee95d3668..4f93cd44d5 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") From bf658c0131376f194bac577d8681e4d8038ae131 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 3 Oct 1999 21:02:48 +0000 Subject: [PATCH 0090/2594] 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 f0c97aa3ca..c9ccd794ed 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 @@ def __init__ (self, attrs=None): # 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 @@ def __init__ (self, attrs=None): 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 @@ def parse_command_line (self, args): "(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 @@ def __init__ (self, dist): 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 @@ def __init__ (self, dist): # 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 @@ 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: + + if self.verbose >= level: print msg @@ -720,15 +755,13 @@ def warn (self, 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 (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 @@ def execute (self, func, args, msg=None, level=1): 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 @@ def execute (self, func, args, msg=None, level=1): 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 @@ def make_file (self, infiles, outfile, func, args, 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 @@ def make_file (self, infiles, outfile, func, args, # 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 From 5fb0798f6108306b956fbb5628ab020b00b0f570 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 3 Oct 1999 21:03:26 +0000 Subject: [PATCH 0091/2594] 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 978bb3b958..35deaa812f 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -66,8 +66,8 @@ def run (self): 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 978bb3b958..35deaa812f 100644 --- a/command/install_py.py +++ b/command/install_py.py @@ -66,8 +66,8 @@ def run (self): 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 From c83db037302c04d9b2510552bcc4e75d5bf6a2ee Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 3 Oct 1999 21:07:21 +0000 Subject: [PATCH 0092/2594] 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 3a7a43bb1e..4067ca4cd6 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -12,7 +12,7 @@ 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): From 04b548270ee36bdff4114fc797e84025925a0ef4 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 3 Oct 1999 21:08:42 +0000 Subject: [PATCH 0093/2594] 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 8c5065f6bc..f284092b3e 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -121,8 +121,9 @@ def run (self): # 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: From 95fff421e1c4f3293d15344a07f2361e4742b5b4 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 3 Oct 1999 21:09:14 +0000 Subject: [PATCH 0094/2594] 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 3f309745ad..99a05225bf 100644 --- a/command/dist.py +++ b/command/dist.py @@ -220,7 +220,7 @@ def find_defaults (self): 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 @@ def read_manifest (self): # 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: From 97cc7bbe9d4650bac48da8831dd70d987fb4562e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 23 Oct 1999 19:06:20 +0000 Subject: [PATCH 0095/2594] 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 99a05225bf..959413f49c 100644 --- a/command/dist.py +++ b/command/dist.py @@ -405,7 +405,7 @@ def make_zipfile (self, base_dir): # 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): From d2649fd50531766567cef19ae50a6760f411cf9b Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 23 Oct 1999 19:06:56 +0000 Subject: [PATCH 0096/2594] 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 c9ccd794ed..23fc3ccad0 100644 --- a/core.py +++ b/core.py @@ -849,7 +849,7 @@ def make_file (self, infiles, outfile, func, args, # 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 From 78805a7cc9ab2cf178276ca4b84b1d61ad29a6f5 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 23 Oct 1999 19:10:59 +0000 Subject: [PATCH 0097/2594] 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 959413f49c..57db1d78dd 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! - - From 86d2c36a34934224784e597575f6d33bf0e426b4 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 23 Oct 1999 19:25:05 +0000 Subject: [PATCH 0098/2594] 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 57db1d78dd..abbd625b51 100644 --- a/command/dist.py +++ b/command/dist.py @@ -129,7 +129,7 @@ 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 @@ def make_release_tree (self, base_dir, files): # 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 @@ def make_distribution (self): 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 From 2bfd1886680134fd1e76d6260abd7388c35079ca Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 3 Dec 1999 16:18:56 +0000 Subject: [PATCH 0099/2594] [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 4067ca4cd6..b3cc1e95cc 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -93,12 +93,15 @@ def get_package_dir (self, package): 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 @@ def get_package_dir (self, package): 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 @@ def check_package (self, package, package_dir): "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 @@ 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" # 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) From 948163cdcbc541ae0bd3f7af825adad6062cb0e5 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 12 Dec 1999 16:51:44 +0000 Subject: [PATCH 0100/2594] 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 23fc3ccad0..13bf9c7fcb 100644 --- a/core.py +++ b/core.py @@ -13,19 +13,25 @@ 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 @@ def __init__ (self, attrs=None): 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 @@ def parse_command_line (self, args): '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 @@ def parse_command_line (self, args): # 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 @@ def parse_command_line (self, args): # 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 @@ def parse_command_line (self, args): # 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 @@ def parse_command_line (self, args): # 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 from it, and returns the class object. 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 @@ def find_command_obj (self, command, create=1): '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 @@ def __init__ (self, dist): 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 From 8fa8e3f254c307213ed4ebd194454e144578cd64 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 12 Dec 1999 16:54:55 +0000 Subject: [PATCH 0101/2594] 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 3df2d1da65..86e9f326ad 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -12,7 +12,7 @@ __rcsid__ = "$Id$" -import string, re +import sys, string, re from types import * import getopt from distutils.errors import * @@ -33,7 +33,7 @@ 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 From 6b3ff9eef6bd4d9f99572022a776b7874097466b Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 12 Dec 1999 16:57:47 +0000 Subject: [PATCH 0102/2594] 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 8f689196e1..edff4f0a0e 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -92,7 +92,7 @@ def compile (self, sources, output_dir=None, macros=None, - includes=None, + include_dirs=None, extra_preargs=None, extra_postargs=None): @@ -100,18 +100,19 @@ def compile (self, 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 @@ def link_shared_object (self, 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) From c2cadc8e316e5b60fb44864110d970f068e977b2 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 12 Dec 1999 17:01:01 +0000 Subject: [PATCH 0103/2594] 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 f284092b3e..d38cb18b5c 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -179,7 +179,7 @@ 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: + 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 @@ 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: + 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 @@ def build_extensions (self, extensions): 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 From 749d2ea556fa8790ab5c5188aa163ba170b07dee Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 12 Dec 1999 17:03:59 +0000 Subject: [PATCH 0104/2594] 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 b3cc1e95cc..6bc5aa8007 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -6,7 +6,7 @@ __rcsid__ = "$Id$" -import string, os +import sys, string, os from types import * from glob import glob @@ -40,11 +40,21 @@ def set_final_options (self): 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 @@ def run (self): 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 @@ def run (self): else: self.build_packages () - # run () @@ -162,9 +171,13 @@ def check_module (self, module, module_file): 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 @@ def build_module (self, module, module_file, package): 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): From a65fd32d4d8280c553b631c6407010b271634468 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 12 Dec 1999 17:07:22 +0000 Subject: [PATCH 0105/2594] 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 abbd625b51..cdd4dfc8cc 100644 --- a/command/dist.py +++ b/command/dist.py @@ -10,6 +10,7 @@ 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 @@ def set_default_options (self): self.formats = None self.manifest = None self.list_only = 0 + self.keep_tree = 0 def set_final_options (self): @@ -202,8 +207,8 @@ def check_metadata (self): 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 @@ def read_manifest (self): # 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 @@ def make_release_tree (self, base_dir, files): # 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 @@ def make_distribution (self): elif fmt == 'zip': self.make_zipfile (base_dir) + if not self.keep_tree: + self.nuke_release_tree (base_dir) + # class Dist From 8ae570b96d2c1a923c26b02f85d2826561638fba Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 12 Dec 1999 17:19:58 +0000 Subject: [PATCH 0106/2594] 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 486f03ad04..8c2ddf7658 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -262,7 +262,7 @@ def compile (self, 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 @@ def compile (self, 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 5ac60b2e47..d93c74c76d 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -63,19 +63,20 @@ def compile (self, 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') From fd51c3b988a972a4d21813364202d7e2f04a4c9a Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 13 Dec 1999 21:38:57 +0000 Subject: [PATCH 0107/2594] 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 cdd4dfc8cc..2e4aacddfc 100644 --- a/command/dist.py +++ b/command/dist.py @@ -274,7 +274,7 @@ def search_dir (self, dir, patterns): 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 @@ 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]): + if regexp.search (self.files[i]): del self.files[i] From cca87fce7c57072fd109389de01d23572d0d5dec Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 16 Dec 1999 01:14:15 +0000 Subject: [PATCH 0108/2594] 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 2e4aacddfc..9931e3752d 100644 --- a/command/dist.py +++ b/command/dist.py @@ -402,8 +402,16 @@ def make_release_tree (self, base_dir, files): 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"): From 29fed792b97b31c545bb4b7f90a0b3a2e8a8ab4a Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 16 Dec 1999 01:19:05 +0000 Subject: [PATCH 0109/2594] 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 13bf9c7fcb..fd5ba907fb 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: From 1e138ca57132305e61c88abc02f5f42c1f733584 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 9 Jan 2000 22:39:32 +0000 Subject: [PATCH 0110/2594] 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 9931e3752d..80af990321 100644 --- a/command/dist.py +++ b/command/dist.py @@ -338,8 +338,8 @@ def read_manifest (self): # 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 @@ def read_manifest (self): # 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 @@ def read_manifest (self): # 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' From b4325f5828cd4c7a744909360776702dd72a4a13 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 9 Jan 2000 22:41:02 +0000 Subject: [PATCH 0111/2594] 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 8c2ddf7658..036cbe9f48 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -292,17 +292,10 @@ def compile (self, 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 @@ def link_shared_object (self, 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, From c77e34abbcc3b20c0091b26e3a9142599b98753a Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 9 Jan 2000 22:47:53 +0000 Subject: [PATCH 0112/2594] 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 edff4f0a0e..fb58269cc1 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 @@ def __init__ (self, (self.ld_shared, self.ldflags_shared) = \ _split_command (LDSHARED) + self.ld_exec = self.cc + def compile (self, sources, @@ -122,7 +131,7 @@ def compile (self, # 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 @@ def compile (self, # 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 @@ def link_shared_object (self, 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 @@ def link_shared_object (self, # 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 @@ def link_shared_object (self, # 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): From 070f8639264c4ef9d748a2592cad810d189e8f4e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 9 Jan 2000 22:48:59 +0000 Subject: [PATCH 0113/2594] '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 4f93cd44d5..953c2e26e2 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 From 9b0d91692d8ed2c33df2133484d19b66b4fe6aa4 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 17 Jan 2000 16:25:17 +0000 Subject: [PATCH 0114/2594] 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 6bc5aa8007..e27a36d464 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -12,7 +12,6 @@ from distutils.core import Command from distutils.errors import * -from distutils.util import mkpath, copy_file class BuildPy (Command): From 705babe53bea546b02923e3a54ad837b7fd0f5f4 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 17 Jan 2000 16:25:59 +0000 Subject: [PATCH 0115/2594] 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 cd12f6fc5b..6f5d6715ca 100644 --- a/command/install.py +++ b/command/install.py @@ -130,9 +130,9 @@ def set_final_options (self): # 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 @@ def replace_sys_prefix (self, config_attr, fallback_postfix, use_exec=0): 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) From dcb45ff5424f902d4e82572b682549b8e1576c89 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 17 Jan 2000 18:00:04 +0000 Subject: [PATCH 0116/2594] 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 d93c74c76d..48453768d2 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -202,18 +202,15 @@ def shared_object_filename (self, source_filename): 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): From e5eb54ac61619eb08aa9c2f2975afe01b747ef5f Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 17 Jan 2000 18:04:04 +0000 Subject: [PATCH 0117/2594] 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 80af990321..b588fe95a9 100644 --- a/command/dist.py +++ b/command/dist.py @@ -435,12 +435,39 @@ def make_tarball (self, base_dir, compress="gzip"): 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): From 102e89700b1f852de2496db33885228989f8161a Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 17 Jan 2000 20:23:34 +0000 Subject: [PATCH 0118/2594] 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 b588fe95a9..2d05f17447 100644 --- a/command/dist.py +++ b/command/dist.py @@ -13,6 +13,7 @@ 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 @@ def make_release_tree (self, base_dir, files): 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 @@ def make_zipfile (self, base_dir): 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 From 696403a48232186753c41267731124d0105892e3 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 17 Jan 2000 20:40:48 +0000 Subject: [PATCH 0119/2594] 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 48453768d2..50fd4622f3 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -37,7 +37,7 @@ def __init__ (self, 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'] From ab853dd09598817baa291d816f22a8a0b0c784c8 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 17 Jan 2000 21:57:17 +0000 Subject: [PATCH 0120/2594] 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 50fd4622f3..a07b4b8ea6 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -37,7 +37,7 @@ def __init__ (self, 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'] From 57794609e7637324ea1c7c31686e8889571bccd6 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 17 Jan 2000 21:57:55 +0000 Subject: [PATCH 0121/2594] 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 eee8e7f4a0..9a88ac8987 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 () From 0026c2ec06718cd6767b38cb5cc72bb77ed548b7 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 17 Jan 2000 21:58:07 +0000 Subject: [PATCH 0122/2594] 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 2d05f17447..0f9e30bad8 100644 --- a/command/dist.py +++ b/command/dist.py @@ -472,8 +472,8 @@ def make_zipfile (self, base_dir): "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: From 6de13bfacf9bf7d1722fc1ff5c5d8e801512ae04 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 30 Jan 2000 15:07:56 +0000 Subject: [PATCH 0123/2594] 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 3fb756c6a1..b5673d235a 100644 --- a/command/install_ext.py +++ b/command/install_ext.py @@ -27,6 +27,9 @@ def set_final_options (self): 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 35deaa812f..50939a3431 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -46,6 +46,9 @@ def set_final_options (self): 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 35deaa812f..50939a3431 100644 --- a/command/install_py.py +++ b/command/install_py.py @@ -46,6 +46,9 @@ def set_final_options (self): 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) From f8864bbadfdd49102b589272837a8ec1a93ca0fc Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 30 Jan 2000 18:30:32 +0000 Subject: [PATCH 0124/2594] 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 fd5ba907fb..7a646dad22 100644 --- a/core.py +++ b/core.py @@ -30,8 +30,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 @@ def __init__ (self, attrs=None): 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 @@ def parse_command_line (self, args): # 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 @@ def parse_command_line (self, args): # 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 @@ def parse_command_line (self, args): # 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 From 40b35bdeccac9d3b970f084d10fd7d0502bca864 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 30 Jan 2000 18:31:34 +0000 Subject: [PATCH 0125/2594] Added 'dist' command. --- command/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/command/__init__.py b/command/__init__.py index 9a5aef2002..8659307cbf 100644 --- a/command/__init__.py +++ b/command/__init__.py @@ -9,6 +9,7 @@ install install_py install_ext + dist but this list will undoubtedly grow with time.""" @@ -20,4 +21,5 @@ 'install', 'install_py', 'install_ext', + 'dist', ] From fb6034d5ef6f4eccc9ee98e7aa353a4b2ae51235 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 30 Jan 2000 18:34:15 +0000 Subject: [PATCH 0126/2594] 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 1586e60b07..e6c87bfcae 100644 --- a/command/build.py +++ b/command/build.py @@ -12,6 +12,8 @@ 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 d38cb18b5c..4f7e53ff30 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -11,8 +11,6 @@ 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 @@ 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 @@ def set_default_options (self): 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 @@ def set_final_options (self): # 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 @@ def set_final_options (self): 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 @@ def build_extensions (self, extensions): 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 e27a36d464..57ddf7e7ea 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -16,6 +16,8 @@ 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 0f9e30bad8..76332b2751 100644 --- a/command/dist.py +++ b/command/dist.py @@ -131,6 +131,8 @@ 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 6f5d6715ca..0e5b01cc98 100644 --- a/command/install.py +++ b/command/install.py @@ -8,13 +8,14 @@ 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 @@ def replace_sys_prefix (self, config_attr, fallback_postfix, use_exec=0): 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 b5673d235a..599a37e1ce 100644 --- a/command/install_ext.py +++ b/command/install_ext.py @@ -11,6 +11,8 @@ 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 50939a3431..2e8a670686 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -8,6 +8,8 @@ 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 50939a3431..2e8a670686 100644 --- a/command/install_py.py +++ b/command/install_py.py @@ -8,6 +8,8 @@ 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"), From 4cf86996ee90c2109a98829865f736683ca5cfac Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 30 Jan 2000 19:57:48 +0000 Subject: [PATCH 0127/2594] 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 953c2e26e2..58d58439e0 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, From 6be52b45d1a442faf400b853e190db26b718d1c9 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 30 Jan 2000 20:22:27 +0000 Subject: [PATCH 0128/2594] 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 76332b2751..ea61a4806e 100644 --- a/command/dist.py +++ b/command/dist.py @@ -219,12 +219,24 @@ def check_metadata (self): 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: From 439aa60615c8ada638e789a78159d26091838ffe Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 2 Feb 2000 00:05:14 +0000 Subject: [PATCH 0129/2594] 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 8eaf17dc35..0e40cbc350 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -13,15 +13,20 @@ 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 From 0298b16d8f10a056dbd74b2b68c425fcf873d6e2 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 2 Feb 2000 00:07:14 +0000 Subject: [PATCH 0130/2594] 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 57ddf7e7ea..048962b29f 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -160,8 +160,8 @@ def check_package (self, package, package_dir): 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 From b97cf9ec1ea46ad32766cabf2f8b0697be681944 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 3 Feb 2000 23:07:19 +0000 Subject: [PATCH 0131/2594] 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 fb58269cc1..9ace98606c 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -137,34 +137,27 @@ def compile (self, 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 From 7c7b43f5c4d40dd9eb5524a4babd50f526cee53d Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 3 Feb 2000 23:07:54 +0000 Subject: [PATCH 0132/2594] 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 4f7e53ff30..126cf60aab 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -197,10 +197,13 @@ def build_extensions (self, extensions): 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 From 139d5ff59d0b42ffd4a768d3fffe2b8f0320bc9d Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 5 Feb 2000 02:23:16 +0000 Subject: [PATCH 0133/2594] 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 126cf60aab..b253c55dce 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -103,6 +103,11 @@ def set_final_options (self): 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 @@ def run (self): 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 @@ def check_extensions_list (self, extensions): 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 @@ def build_extensions (self, extensions): 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) From 57d9abc5b58cf0fdc8a19692abdb934f5e07f52b Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 5 Feb 2000 02:23:59 +0000 Subject: [PATCH 0134/2594] 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 0000000000..c638fd5ffc --- /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 0000000000..c638fd5ffc --- /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 From ed153d7c608a30aeda273198189cd1539d01be2b Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 5 Feb 2000 02:24:16 +0000 Subject: [PATCH 0135/2594] 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 e6c87bfcae..768db12062 100644 --- a/command/build.py +++ b/command/build.py @@ -48,6 +48,12 @@ def run (self): 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: From 47356dea6774b33b37dc74067ef67696a40567e6 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 5 Feb 2000 02:24:52 +0000 Subject: [PATCH 0136/2594] 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 7a646dad22..88e889b22f 100644 --- a/core.py +++ b/core.py @@ -188,6 +188,7 @@ def __init__ (self, attrs=None): 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 @@ def parse_command_line (self, args): # 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__ From b08bf9ee895d020cd5431304cc50aead7c0ba272 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 8 Feb 2000 02:37:15 +0000 Subject: [PATCH 0137/2594] 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 b253c55dce..0f2d1a7bf3 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -236,18 +236,7 @@ def build_extensions (self, extensions): 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) From 23cd2df59e48224e36288f7437d180356b55da39 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 8 Feb 2000 02:39:44 +0000 Subject: [PATCH 0138/2594] 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 a07b4b8ea6..10cd6081d3 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -10,10 +10,96 @@ 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 @@ def __init__ (self, 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'] From 7a31064ca3c8fed8497ec3db7cdb3ee89aba2731 Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Tue, 8 Feb 2000 15:55:42 +0000 Subject: [PATCH 0139/2594] 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 0e40cbc350..e291aec21e 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -19,9 +19,12 @@ 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 @@ def _init_nt(): else: exec "_init_%s()" % os.name + del _init_posix del _init_nt From 0c9bccee1cec8339ed0fa0f331f2730ad2c41d21 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 9 Feb 2000 02:16:14 +0000 Subject: [PATCH 0140/2594] 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 036cbe9f48..7d2d9f58b7 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -263,6 +263,7 @@ def compile (self, 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 @@ def compile (self, 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 @@ def compile (self, 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 @@ def link_static_lib (self, '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 @@ def link_static_lib (self, 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 @@ def link_shared_object (self, 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 @@ def link_executable (self, 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 From 8c5bff29a20dcd4e9ce9b6bd2b4e96f8e6b31624 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 9 Feb 2000 02:17:00 +0000 Subject: [PATCH 0141/2594] 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 9ace98606c..cc0d7723ad 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -102,6 +102,7 @@ def compile (self, output_dir=None, macros=None, include_dirs=None, + debug=0, extra_preargs=None, extra_postargs=None): @@ -149,6 +150,8 @@ def compile (self, # (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 @@ def _fix_link_args (self, output_dir, libraries, library_dirs): 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 @@ def link_shared_lib (self, 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 @@ def link_shared_lib (self, output_dir, libraries, library_dirs, + debug, extra_preargs, extra_postargs) @@ -254,6 +260,7 @@ def link_shared_object (self, output_dir=None, libraries=None, library_dirs=None, + debug=0, extra_preargs=None, extra_postargs=None): @@ -279,6 +286,8 @@ def link_shared_object (self, 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 @@ def link_executable (self, output_dir=None, libraries=None, library_dirs=None, + debug=0, extra_preargs=None, extra_postargs=None): @@ -318,6 +328,8 @@ def link_executable (self, 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: From 04c8638c9348a33880f8025b4cce8dc488117aa1 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 9 Feb 2000 02:18:39 +0000 Subject: [PATCH 0142/2594] 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 10cd6081d3..cf4be0dc43 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -159,6 +159,7 @@ def compile (self, output_dir=None, macros=None, include_dirs=None, + debug=0, extra_preargs=None, extra_postargs=None): @@ -190,6 +191,8 @@ def compile (self, 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 @@ def compile (self, 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 @@ def link_static_lib (self, 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 @@ def link_shared_lib (self, output_dir=None, libraries=None, library_dirs=None, + debug=0, extra_preargs=None, extra_postargs=None): @@ -267,6 +275,8 @@ def link_shared_object (self, 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: From f0b2eb38689af9acf61db1e126458b28f36dff7d Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 9 Feb 2000 02:19:49 +0000 Subject: [PATCH 0143/2594] 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 768db12062..82a4e6c292 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 @@ def set_default_options (self): # (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 From bc5bb15bc3510ccab5986fd22151e75d33776a46 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 9 Feb 2000 02:20:14 +0000 Subject: [PATCH 0144/2594] 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 c638fd5ffc..6571281816 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -28,7 +28,9 @@ 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 @@ def set_default_options (self): 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 @@ def build_libraries (self, libraries): 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 0f2d1a7bf3..14c2234e3c 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 @@ def set_default_options (self): 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 @@ def build_extensions (self, extensions): 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 @@ def build_extensions (self, extensions): 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 @@ def build_extensions (self, extensions): 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 c638fd5ffc..6571281816 100644 --- a/command/build_lib.py +++ b/command/build_lib.py @@ -28,7 +28,9 @@ 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 @@ def set_default_options (self): 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 @@ def build_libraries (self, libraries): 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 From b1228096c7a2efeaab3d677ec72cdb912c0d5252 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 10 Feb 2000 02:15:52 +0000 Subject: [PATCH 0145/2594] 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 cf4be0dc43..7c179782df 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -114,10 +114,6 @@ def __init__ (self, 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 @@ def __init__ (self, 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 @@ def compile (self, 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 @@ def compile (self, 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 @@ def link_shared_lib (self, # 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 @@ def link_shared_object (self, 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 @@ def link_shared_object (self, 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: From e9cac8bea6e2f62aa1dd28e6c91958394ba0aecf Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 10 Feb 2000 02:17:06 +0000 Subject: [PATCH 0146/2594] 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 14c2234e3c..42eab4796d 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -241,9 +241,21 @@ def build_extensions (self, extensions): 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) From 5d779ebd6d24ee05e11c670f6ed2bac639b33a6d Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 10 Feb 2000 02:51:32 +0000 Subject: [PATCH 0147/2594] 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 cc0d7723ad..770a543c75 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -106,6 +106,8 @@ def compile (self, 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 @@ def link_static_lib (self, "'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 @@ def link_shared_object (self, 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) From 7fffec2291d4e832411729c8d653c2cbce6a17f7 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 10 Feb 2000 02:52:42 +0000 Subject: [PATCH 0148/2594] 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 7c179782df..90ebae0a58 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -15,17 +15,26 @@ 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++, From 8224dcdac16e8f63eb008892d84bd192e245b11f Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 11 Feb 2000 02:47:15 +0000 Subject: [PATCH 0149/2594] 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 90ebae0a58..3073aaea32 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -17,12 +17,12 @@ 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 @@ def __init__ (self, 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'] From eed228691703b5775b5ad0a6f626d20e73b72091 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 11 Feb 2000 02:52:39 +0000 Subject: [PATCH 0150/2594] 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 3073aaea32..e489c1c86e 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 @@ def __init__ (self, 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) From 333dbe885f8da8229084192e350fcf4d4881ada8 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 17 Feb 2000 23:54:55 +0000 Subject: [PATCH 0151/2594] 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 ea61a4806e..0000000000 --- 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 From 5caa9f1cf7ce67b78a7db4f2a177ac5a7e5cb16b Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 17 Feb 2000 23:56:15 +0000 Subject: [PATCH 0152/2594] 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 0000000000..c8101a708e --- /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 () From d03109b3f6d31ba77bdcee2559d786776a9430ce Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 18 Feb 2000 00:11:52 +0000 Subject: [PATCH 0153/2594] 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 8659307cbf..ea979f9b50 100644 --- a/command/__init__.py +++ b/command/__init__.py @@ -21,5 +21,5 @@ 'install', 'install_py', 'install_ext', - 'dist', + 'sdist', ] From 0fe8bb260644b6dfaf64c07189108d2a045a1895 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 18 Feb 2000 00:13:53 +0000 Subject: [PATCH 0154/2594] 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 82a4e6c292..bcbb9332e4 100644 --- a/command/build.py +++ b/command/build.py @@ -10,7 +10,7 @@ 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 6571281816..749b0c2ba2 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -26,7 +26,7 @@ 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 42eab4796d..ea0e294a70 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -20,7 +20,7 @@ (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 6571281816..749b0c2ba2 100644 --- a/command/build_lib.py +++ b/command/build_lib.py @@ -26,7 +26,7 @@ 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 048962b29f..f492ee374f 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -14,7 +14,7 @@ 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 0e5b01cc98..b57ec6f2f1 100644 --- a/command/install.py +++ b/command/install.py @@ -12,7 +12,7 @@ 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 599a37e1ce..ba343d2e42 100644 --- a/command/install_ext.py +++ b/command/install_ext.py @@ -9,7 +9,7 @@ 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 2e8a670686..e898ce0c18 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -6,7 +6,7 @@ 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 2e8a670686..e898ce0c18 100644 --- a/command/install_py.py +++ b/command/install_py.py @@ -6,7 +6,7 @@ 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 c8101a708e..d890832862 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -17,7 +17,7 @@ from distutils.errors import DistutilsExecError -class Sdist (Command): +class sdist (Command): description = "create a source distribution (tarball, zip file, etc.)" From d393e804e26ad082c98d3070238a37e416e9e0af Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 18 Feb 2000 00:14:21 +0000 Subject: [PATCH 0155/2594] 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 88e889b22f..f3951ac71c 100644 --- a/core.py +++ b/core.py @@ -447,8 +447,7 @@ class from it, and returns the class object. 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) From 76c84dcb7f05dd257f26c74e2949e9020e1f633d Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 18 Feb 2000 00:25:39 +0000 Subject: [PATCH 0156/2594] 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 bcbb9332e4..d46132f308 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 749b0c2ba2..26b89b3aba 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -28,9 +28,10 @@ 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 ea0e294a70..963abc2977 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 749b0c2ba2..26b89b3aba 100644 --- a/command/build_lib.py +++ b/command/build_lib.py @@ -28,9 +28,10 @@ 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 f492ee374f..eecf88b9d3 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 b57ec6f2f1..798460b27a 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 ba343d2e42..016a514e1e 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 e898ce0c18..6dfebfbe0d 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 e898ce0c18..6dfebfbe0d 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 d890832862..aaedb30421 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', From 42a137f33452bf81103e2960c6c33836b8260dda Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 18 Feb 2000 00:26:23 +0000 Subject: [PATCH 0157/2594] 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 f3951ac71c..6463bb4931 100644 --- a/core.py +++ b/core.py @@ -24,9 +24,10 @@ # 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 @@ def parse_command_line (self, args): # 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 @@ def parse_command_line (self, args): 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 @@ def parse_command_line (self, args): 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 From 7697546dd8208145f70f4d5dd97d6fc268842aaa Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 18 Feb 2000 00:35:22 +0000 Subject: [PATCH 0158/2594] 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 d46132f308..28241e104f 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 @@ def set_default_options (self): 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 26b89b3aba..9cf53da032 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 @@ def set_default_options (self): 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 @@ def set_final_options (self): # 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 963abc2977..8ed3a97339 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 @@ def set_default_options (self): 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 @@ def set_final_options (self): # 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 26b89b3aba..9cf53da032 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 @@ def set_default_options (self): 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 @@ def set_final_options (self): # 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 eecf88b9d3..7ae207b8cc 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 798460b27a..ffd687978a 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 @@ def set_default_options (self): 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 @@ def set_final_options (self): # 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 016a514e1e..b046dfbf3b 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 6dfebfbe0d..919873c6f2 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 6dfebfbe0d..919873c6f2 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 aaedb30421..042b1fc54a 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 @@ def set_default_options (self): 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: From 18f73d2efe63baed2730c5d607901c429f7460ed Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 18 Feb 2000 00:36:20 +0000 Subject: [PATCH 0159/2594] 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 6463bb4931..ddd39a2c79 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 @@ def __init__ (self, dist): 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 @@ def __init__ (self, dist): # 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 @@ def __getattr__ (self, attr): 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 @@ def ensure_ready (self): # 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 @@ def set_default_options (self): 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 @@ def set_final_options (self): 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 @@ def set_undefined_options (self, src_cmd, *option_pairs): # 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 @@ 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 '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): From d7fe6215f8ae6b39c44a475925c6ae0aa005157a Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 24 Feb 2000 03:17:43 +0000 Subject: [PATCH 0160/2594] 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 042b1fc54a..6b838bdef2 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -198,6 +198,7 @@ def find_defaults (self): 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 From 0e7f7e1c36820e01bde9edbbcd9acc623445b045 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 26 Feb 2000 00:49:04 +0000 Subject: [PATCH 0161/2594] 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 ffd687978a..a89bc0259f 100644 --- a/command/install.py +++ b/command/install.py @@ -10,7 +10,7 @@ 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 @@ def finalize_options (self): # 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 @@ def finalize_options (self): 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 @@ def finalize_options (self): # 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 b046dfbf3b..f1a3e24725 100644 --- a/command/install_ext.py +++ b/command/install_ext.py @@ -26,7 +26,7 @@ def initialize_options (self): 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 919873c6f2..ab82e04bf0 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -27,22 +27,12 @@ def initialize_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 - # 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 @@ 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!) + # 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 @@ def run (self): 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 919873c6f2..ab82e04bf0 100644 --- a/command/install_py.py +++ b/command/install_py.py @@ -27,22 +27,12 @@ def initialize_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 - # 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 @@ 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!) + # 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 @@ def run (self): 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': From 06c2f98eb01493396435bb99b6e0264e3888353a Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 26 Feb 2000 00:49:40 +0000 Subject: [PATCH 0162/2594] 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 ddd39a2c79..a92bff9573 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 () From 23c5fe111b4770b4ec04b5be7fbbd081928df5c4 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 1 Mar 2000 01:19:37 +0000 Subject: [PATCH 0163/2594] 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 7ae207b8cc..05a1beff4e 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 @@ def build_module (self, module, module_file, package): "'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 @@ def build_modules (self): 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 @@ def build_packages (self): 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) From 403c4a31e1ceb3e900a8e1241d5ab9e1093d0171 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 1 Mar 2000 01:26:45 +0000 Subject: [PATCH 0164/2594] 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 28241e104f..e25ef81cad 100644 --- a/command/build.py +++ b/command/build.py @@ -6,9 +6,9 @@ __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 @@ 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) - 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): From d762d7cd369f81c7e0a359929d90a8a617d8dfeb Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 1 Mar 2000 01:43:28 +0000 Subject: [PATCH 0165/2594] 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 8ed3a97339..2bbd89d891 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 @@ def finalize_options (self): 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 @@ def build_extensions (self, extensions): # 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 @@ def build_extensions (self, extensions): 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 @@ def build_extensions (self, extensions): # 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 From f5aaad4ab595030d314a3f2d815f3533ba7364e9 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 1 Mar 2000 14:40:15 +0000 Subject: [PATCH 0166/2594] 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 58d58439e0..641a35af8d 100644 --- a/util.py +++ b/util.py @@ -11,7 +11,7 @@ __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() From 8aea04d670e4b002754734358b5ebdfda20d8666 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 1 Mar 2000 14:42:07 +0000 Subject: [PATCH 0167/2594] 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 a92bff9573..83a98a84d6 100644 --- a/core.py +++ b/core.py @@ -506,6 +506,7 @@ def find_command_obj (self, command, create=1): 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 From bacc83b29ce5519b298bcb9ec9d558a2fec94701 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 1 Mar 2000 14:43:12 +0000 Subject: [PATCH 0168/2594] 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 7d2d9f58b7..72b77573c6 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -12,7 +12,7 @@ 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 @@ def spawn (self, cmd): 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 From cbad9d0a7c7889f0593b839d4a87966fd54e5a81 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 1 Mar 2000 14:43:49 +0000 Subject: [PATCH 0169/2594] 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 770a543c75..77d12d32bc 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -159,6 +159,8 @@ def compile (self, 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 @@ def compile (self, # 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 @@ def link_static_lib (self, 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 @@ def link_shared_object (self, 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 @@ def link_executable (self, 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) From 872379e98b29803bb5ec360686fe3365456c7313 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 2 Mar 2000 01:21:54 +0000 Subject: [PATCH 0170/2594] 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 77d12d32bc..518132f670 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -100,6 +100,7 @@ def __init__ (self, def compile (self, sources, output_dir=None, + keep_dir=0, macros=None, include_dirs=None, debug=0, @@ -134,7 +135,11 @@ def compile (self, # 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 @@ def compile (self, # 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): From 76ec448446c6c95e8942e53ffc6d5fae0b90a690 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 2 Mar 2000 01:27:36 +0000 Subject: [PATCH 0171/2594] 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 9cf53da032..9cb584a1ce 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -28,12 +28,21 @@ 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 @@ def initialize_options (self): # 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 @@ 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'), @@ -110,6 +131,12 @@ def check_library_list (self, libraries): 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 @@ def check_library_list (self, libraries): # 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 @@ def build_libraries (self, libraries): 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 9cf53da032..9cb584a1ce 100644 --- a/command/build_lib.py +++ b/command/build_lib.py @@ -28,12 +28,21 @@ 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 @@ def initialize_options (self): # 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 @@ 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'), @@ -110,6 +131,12 @@ def check_library_list (self, libraries): 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 @@ def check_library_list (self, libraries): # 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 @@ def build_libraries (self, libraries): 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 From 720accdc1c3e3d7926cfd26d58c368b87b551c12 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 2 Mar 2000 01:32:21 +0000 Subject: [PATCH 0172/2594] 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 2bbd89d891..fd5cd7a53a 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -164,7 +164,15 @@ def run (self): 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 @@ def build_extensions (self, extensions): 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 @@ def build_extensions (self, extensions): 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': From 41b233728bac2f952bf2cc9b5ffb19409b9a48e9 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 2 Mar 2000 01:49:45 +0000 Subject: [PATCH 0173/2594] 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 18deaadf44..1f23b972e4 100644 --- a/__init__.py +++ b/__init__.py @@ -8,4 +8,4 @@ setup (...) """ -__rcsid__ = "$Id$" +__revision__ = "$Id$" diff --git a/ccompiler.py b/ccompiler.py index 72b77573c6..4819b23c3c 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -5,7 +5,7 @@ # 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 ea979f9b50..40595ba949 100644 --- a/command/__init__.py +++ b/command/__init__.py @@ -13,7 +13,7 @@ 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 e25ef81cad..d81bc88511 100644 --- a/command/build.py +++ b/command/build.py @@ -4,7 +4,7 @@ # 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 9cb584a1ce..955cf5650c 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -7,7 +7,7 @@ # 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 fd5cd7a53a..6da02fedfa 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -6,7 +6,7 @@ # 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 9cb584a1ce..955cf5650c 100644 --- a/command/build_lib.py +++ b/command/build_lib.py @@ -7,7 +7,7 @@ # 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 05a1beff4e..2d0ad1ce09 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -4,7 +4,7 @@ # 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 a89bc0259f..1df558434a 100644 --- a/command/install.py +++ b/command/install.py @@ -4,7 +4,7 @@ # 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 f1a3e24725..8d23fa4cde 100644 --- a/command/install_ext.py +++ b/command/install_ext.py @@ -4,7 +4,7 @@ # 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 ab82e04bf0..33cf6894e2 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 ab82e04bf0..33cf6894e2 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 6b838bdef2..726458a246 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -4,7 +4,7 @@ # 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 83a98a84d6..a31e60ce4a 100644 --- a/core.py +++ b/core.py @@ -8,7 +8,7 @@ # 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 17d1abc796..86d91dd6c0 100644 --- a/errors.py +++ b/errors.py @@ -10,7 +10,7 @@ # created 1999/03/03, Greg Ward -__rcsid__ = "$Id$" +__revision__ = "$Id$" import types diff --git a/fancy_getopt.py b/fancy_getopt.py index 86e9f326ad..3110ab30db 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -10,7 +10,7 @@ # 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 e489c1c86e..2dd8dc10ca 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -6,7 +6,7 @@ # created 1999/08/19, Perry Stoll # -__rcsid__ = "$Id$" +__revision__ = "$Id$" import os import sys diff --git a/spawn.py b/spawn.py index 9a88ac8987..847346cc2f 100644 --- a/spawn.py +++ b/spawn.py @@ -5,7 +5,7 @@ # 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 518132f670..7765faf88b 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -15,7 +15,7 @@ # 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 641a35af8d..85f3a345e6 100644 --- a/util.py +++ b/util.py @@ -9,7 +9,7 @@ # created 1999/03/08, Greg Ward -__rcsid__ = "$Id$" +__revision__ = "$Id$" import os, string from distutils.errors import * From e67e55e012bc55359655cabd78e2bf9e4ec0d340 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 2 Mar 2000 01:57:12 +0000 Subject: [PATCH 0174/2594] 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 d81bc88511..97466fdb48 100644 --- a/command/build.py +++ b/command/build.py @@ -87,7 +87,7 @@ def run (self): # 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 955cf5650c..7a6ef7a802 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.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 6da02fedfa..bbdf062bd6 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -166,9 +166,9 @@ def run (self): 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 955cf5650c..0000000000 --- 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 From e5881f84002247373f54ef2108e050b03f30fcf2 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 3 Mar 2000 03:00:02 +0000 Subject: [PATCH 0175/2594] 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 85f3a345e6..b20f5be92c 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) From bea7ea527619fd6e4360157daea66bb5f7bf946e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 3 Mar 2000 03:00:27 +0000 Subject: [PATCH 0176/2594] 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 2d0ad1ce09..4e5255a67c 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -146,7 +146,7 @@ def check_package (self, package, package_dir): 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 From 0b68db614df691a1089dc6c6a290bf0d8b81f424 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 6 Mar 2000 03:36:50 +0000 Subject: [PATCH 0177/2594] 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 bbdf062bd6..050a5c65b7 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -245,7 +245,6 @@ def build_extensions (self, extensions): 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 @@ def build_extensions (self, extensions): 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': From 4ffaf24264f6c274ece3c928bdcf717fb12543cd Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 6 Mar 2000 03:37:45 +0000 Subject: [PATCH 0178/2594] 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 7a6ef7a802..c49e3ca737 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -183,7 +183,6 @@ def build_libraries (self, libraries): 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) From 5696561cb0643ab61ee1e719217ba98f5029f674 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 6 Mar 2000 03:40:29 +0000 Subject: [PATCH 0179/2594] 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 4819b23c3c..2336e96589 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -12,7 +12,7 @@ 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 @@ def set_link_objects (self, objects): 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 @@ def compile (self, 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 @@ def compile (self, '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 @@ def link_executable (self, - # -- 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 2dd8dc10ca..847c611d29 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -5,12 +5,13 @@ # 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 @@ def __init__ (self, 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 @@ def __init__ (self, # -- 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 @@ def compile (self, 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 @@ def link_static_lib (self, 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 @@ def link_shared_lib (self, 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 @@ def link_shared_object (self, 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 7765faf88b..35390de0ce 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -20,10 +20,9 @@ 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 @@ def __init__ (self, 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 @@ def compile (self, 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 @@ def link_static_lib (self, 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 @@ def link_shared_lib (self, 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 @@ def link_shared_object (self, 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 @@ def link_executable (self, 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 @@ def link_executable (self, # 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 From 5cf4767279d301842dc5fef525414412320ca947 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 6 Mar 2000 03:44:32 +0000 Subject: [PATCH 0180/2594] 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 b20f5be92c..03c0c88cae 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 () From 7155260372c2a9d8cecb81dd908df0adcafd8d01 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 7 Mar 2000 03:25:20 +0000 Subject: [PATCH 0181/2594] 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 847346cc2f..ae3d09fa31 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: From 0e7330777448758b059ab24d6db4b527c0269001 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 7 Mar 2000 03:27:08 +0000 Subject: [PATCH 0182/2594] 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 03c0c88cae..683d167641 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 () From 01efeb23f10c8b27f9d961264d930d5e3f187248 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 7 Mar 2000 03:30:09 +0000 Subject: [PATCH 0183/2594] Patch from Corran Webster : add '_init_mac()'. --- sysconfig.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/sysconfig.py b/sysconfig.py index e291aec21e..2107ffe4bc 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 @@ def _init_nt(): del _init_posix del _init_nt +del _init_mac From 922f2b62bed5285c5a0d6e6d42f77fe7086e303e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 7 Mar 2000 03:34:16 +0000 Subject: [PATCH 0184/2594] 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 683d167641..2f193fb43c 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) From 86ff65237970cb480db4f9f909902892973fdf02 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 9 Mar 2000 03:16:05 +0000 Subject: [PATCH 0185/2594] 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 2107ffe4bc..49e58eb919 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -17,20 +17,83 @@ 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") From 13a17ccd50ded25c15ce6065dd516d85fb52bd7b Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Thu, 9 Mar 2000 15:54:52 +0000 Subject: [PATCH 0186/2594] 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 49e58eb919..2c7318c04d 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -13,65 +13,73 @@ 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 From 970b9e96ac35abc8a46525a25c5d0e7dd5c2c53a Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 10 Mar 2000 01:48:32 +0000 Subject: [PATCH 0187/2594] 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 2336e96589..46dabadee9 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -449,11 +449,11 @@ class documentation. In any event, they are intended as an 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 @@ def link_shared_lib (self, 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 @@ def link_shared_lib (self, 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 @@ def link_executable (self, 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 35390de0ce..0d2858de1c 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -140,11 +140,11 @@ def compile (self, # 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 @@ def link_static_lib (self, else: self.announce ("skipping %s (up-to-date)" % output_filename) - # link_static_lib () + # create_static_lib () def link_shared_lib (self, From 475a7ee2b018683744a4d4d739ce14b735fb11e8 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 10 Mar 2000 01:49:26 +0000 Subject: [PATCH 0188/2594] 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 847c611d29..bf5257f1d6 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -168,6 +168,7 @@ def __init__ (self, 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 @@ def __init__ (self, # 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 @@ def compile (self, # 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, From f148d847825da4d45f345c97a6e7ad8a48b02774 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 10 Mar 2000 02:02:44 +0000 Subject: [PATCH 0189/2594] 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 c49e3ca737..6560fb4774 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -190,9 +190,9 @@ def build_libraries (self, libraries): # 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 From a447a64793487f03b2f059298a2a1bc1b069a9e2 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 18 Mar 2000 15:19:51 +0000 Subject: [PATCH 0190/2594] 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 46dabadee9..4a8c1d38c8 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 bf5257f1d6..7324b8e1c6 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -307,7 +307,9 @@ def link_shared_object (self, 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 @@ def link_shared_object (self, 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 0d2858de1c..ec85571ddf 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -197,7 +197,9 @@ def link_shared_object (self, 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 @@ def link_executable (self, 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 @@ def link_executable (self, 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 From f11f6f3246f288422067b7c49aa0ab7d20992048 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 18 Mar 2000 15:21:03 +0000 Subject: [PATCH 0191/2594] 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 050a5c65b7..dfe64a114e 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -117,6 +117,9 @@ def finalize_options (self): 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? From 52b70f2aefe7f937e5090dce65c43eef3f8e6051 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 18 Mar 2000 15:37:26 +0000 Subject: [PATCH 0192/2594] 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 0000000000..9785de9938 --- /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) From b0b51892b77115a91a1cbf12dc63faba193de7a3 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 18 Mar 2000 15:42:22 +0000 Subject: [PATCH 0193/2594] 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 2f193fb43c..7a07b65648 100644 --- a/util.py +++ b/util.py @@ -11,7 +11,7 @@ __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, From d8cd9e2b90b4f3ea1603bc44cbf1a156172c0f76 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 18 Mar 2000 15:43:42 +0000 Subject: [PATCH 0194/2594] 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 726458a246..0c15177893 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -10,9 +10,8 @@ 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 @@ def make_release_tree (self, base_dir, files): # 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 @@ def make_distribution (self): 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 From 9403029df5514984078943c729e96ba310319613 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 18 Mar 2000 17:33:18 +0000 Subject: [PATCH 0195/2594] 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 9785de9938..4f0f7e3e94 100644 --- a/command/clean.py +++ b/command/clean.py @@ -12,13 +12,14 @@ 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 @@ def initialize_options(self): 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 From 8f7de70136acae5a467212cbb16c09ce2c364fdb Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 18 Mar 2000 17:35:12 +0000 Subject: [PATCH 0196/2594] 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 a31e60ce4a..dfe411b0cc 100644 --- a/core.py +++ b/core.py @@ -506,7 +506,6 @@ def find_command_obj (self, command, create=1): 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 @@ def find_peer (self, command, create=1): 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): From bfc2b54d5a0bc8ba8a3b115aa8abaf3911cfc9c9 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 18 Mar 2000 17:36:09 +0000 Subject: [PATCH 0197/2594] 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 40595ba949..d2b37a8ce1 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 @@ 'install', 'install_py', 'install_ext', + 'clean', 'sdist', ] From 2176088ac051cb11a5b4aee4928f32b831ec2063 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 22 Mar 2000 00:11:21 +0000 Subject: [PATCH 0198/2594] 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 dfe64a114e..1d8794a6e4 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -146,8 +146,7 @@ def run (self): # 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: From d852781fe93c7a37b90387e2b388bf18ed3823d1 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 22 Mar 2000 00:12:51 +0000 Subject: [PATCH 0199/2594] 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 8d23fa4cde..14730909bb 100644 --- a/command/install_ext.py +++ b/command/install_ext.py @@ -25,8 +25,8 @@ def initialize_options (self): 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): From d80b9efb03fd6cb33c60de06877d7cc04405b576 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 22 Mar 2000 00:15:45 +0000 Subject: [PATCH 0200/2594] 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 1df558434a..13baa1a756 100644 --- a/command/install.py +++ b/command/install.py @@ -9,33 +9,76 @@ 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 @@ def initialize_options (self): # 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 @@ def finalize_options (self): # 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 @@ def run (self): # 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 From 4b8aee534fe757703ad35e112a716bd732ae0cc6 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 22 Mar 2000 00:20:46 +0000 Subject: [PATCH 0201/2594] 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 dfe411b0cc..2f3a36d7d0 100644 --- a/core.py +++ b/core.py @@ -197,7 +197,7 @@ def __init__ (self, attrs=None): 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 @@ def set_undefined_options (self, src_cmd, *option_pairs): # 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 @@ def set_peer_option (self, command, option, value): 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 () From bcfea0afe66af5445e7660e4828a422161cf4126 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 22 Mar 2000 00:22:44 +0000 Subject: [PATCH 0202/2594] 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 7a07b65648..b308b90dd0 100644 --- a/util.py +++ b/util.py @@ -11,7 +11,7 @@ __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 () From 03db2e6f44914364b5e32fa45de7002e13e08142 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 22 Mar 2000 00:30:54 +0000 Subject: [PATCH 0203/2594] 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 13baa1a756..a1b5d06aec 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 @@ def initialize_options (self): 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 @@ def finalize_options (self): 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! From 6ec82e3176107f789ba8386705b60c2f65bf0999 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 22 Mar 2000 00:31:37 +0000 Subject: [PATCH 0204/2594] 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 2f3a36d7d0..df3646765f 100644 --- a/core.py +++ b/core.py @@ -851,26 +851,6 @@ def set_undefined_options (self, src_cmd, *option_pairs): 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 From eb49a5318baf4799d7b24e8ec6e4b9012b6ad0b1 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 22 Mar 2000 00:35:16 +0000 Subject: [PATCH 0205/2594] 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 14730909bb..0000000000 --- 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 From d3774f58433e614f683b466370188b4281c35735 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 22 Mar 2000 00:37:16 +0000 Subject: [PATCH 0206/2594] 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 33cf6894e2..0000000000 --- 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 From 3603215dbb368ef0ffa0375174e2aeafc2d32fb1 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 22 Mar 2000 00:40:16 +0000 Subject: [PATCH 0207/2594] 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 a1b5d06aec..00aa072047 100644 --- a/command/install.py +++ b/command/install.py @@ -388,19 +388,10 @@ 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 - # 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 () From 2f194a8f5701e42aef8801075f4b72da7a482fd8 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 22 Mar 2000 00:51:18 +0000 Subject: [PATCH 0208/2594] 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 00aa072047..ed74971efa 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 @@ def initialize_options (self): 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 @@ def finalize_options (self): 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 @@ def finalize_other (self): # Windows and Mac OS for now 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] From 51d7237f93809f4d6de8ed358a525777793e74d7 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 23 Mar 2000 04:37:11 +0000 Subject: [PATCH 0209/2594] 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 33cf6894e2..eaaa1c7da2 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -6,7 +6,7 @@ from distutils.core import Command from distutils.util import copy_tree -class install_py (Command): +class install_lib (Command): description = "install pure Python modules" From 89cec6fde3704c17ce83732bee0c0da4ef54a7a4 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 23 Mar 2000 04:38:36 +0000 Subject: [PATCH 0210/2594] 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 ae3d09fa31..4e6f206991 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, From 890ea186e16b49a61d19f9a9055c5e67a106ce3d Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 23 Mar 2000 04:39:16 +0000 Subject: [PATCH 0211/2594] Import fix. --- util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util.py b/util.py index b308b90dd0..caf9906281 100644 --- a/util.py +++ b/util.py @@ -11,7 +11,7 @@ __revision__ = "$Id$" -import os, string, re, shutil +import sys, os, string, re, shutil from distutils.errors import * From db89798d96147692062b0ecad0513f2476082c3e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 26 Mar 2000 21:37:09 +0000 Subject: [PATCH 0212/2594] 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 4a8c1d38c8..ffad294706 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -338,16 +338,11 @@ def _prep_compile (self, sources, output_dir): # _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 @@ def _fix_link_args (self, objects, 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" + 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 @@ def link_shared_lib (self, output_dir=None, libraries=None, library_dirs=None, + runtime_library_dirs=None, debug=0, extra_preargs=None, extra_postargs=None): @@ -522,6 +535,7 @@ def link_shared_object (self, output_dir=None, libraries=None, library_dirs=None, + runtime_library_dirs=None, debug=0, extra_preargs=None, extra_postargs=None): @@ -540,6 +554,7 @@ def link_executable (self, output_dir=None, libraries=None, library_dirs=None, + runtime_library_dirs=None, debug=0, extra_preargs=None, extra_postargs=None): From 3aa1d946cf86103148fc70e553115286b9777694 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 26 Mar 2000 21:40:19 +0000 Subject: [PATCH 0213/2594] 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 ec85571ddf..ec766f5405 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -146,7 +146,7 @@ def create_static_lib (self, 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 @@ def link_shared_lib (self, output_dir=None, libraries=None, library_dirs=None, + runtime_library_dirs=None, debug=0, extra_preargs=None, extra_postargs=None): @@ -178,6 +179,7 @@ def link_shared_lib (self, output_dir, libraries, library_dirs, + runtime_library_dirs, debug, extra_preargs, extra_postargs) @@ -189,16 +191,17 @@ def link_shared_object (self, 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 @@ def link_executable (self, 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: From f5e23859b6bba32958929ab54176f28e01a5b9f0 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 26 Mar 2000 21:42:28 +0000 Subject: [PATCH 0214/2594] 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 7324b8e1c6..bc27cea9b7 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -253,8 +253,7 @@ def create_static_lib (self, 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 @@ def link_shared_lib (self, output_dir=None, libraries=None, library_dirs=None, + runtime_library_dirs=None, debug=0, extra_preargs=None, extra_postargs=None): @@ -299,16 +299,21 @@ def link_shared_object (self, 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" From 362881283944c95f424b461590f9d2f81e378d0a Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 26 Mar 2000 21:45:14 +0000 Subject: [PATCH 0215/2594] 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 1d8794a6e4..ff680fa953 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 @@ def initialize_options (self): 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 @@ def finalize_options (self): 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 @@ def run (self): # 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 @@ def run (self): 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 @@ def run (self): 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 @@ def build_extensions (self, extensions): 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 @@ def build_extensions (self, extensions): 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) From 5da6867520bab8d1abc3f7699207ac55bc6a03f9 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 26 Mar 2000 21:47:00 +0000 Subject: [PATCH 0216/2594] 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 4e6f206991..86ea3dfb50 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, From ece89d8b1b55c795e1de3e8842e22ca3eebd3f3d Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 26 Mar 2000 21:48:43 +0000 Subject: [PATCH 0217/2594] 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 df3646765f..5090f25bc3 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 () From 05135cd1557034010a85409726c822d34dcc5043 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 29 Mar 2000 02:10:51 +0000 Subject: [PATCH 0218/2594] 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 4e5255a67c..3baddc62c7 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -58,17 +58,6 @@ def run (self): # 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 @@ def check_module (self, module, module_file): 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 @@ def find_modules (self): 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 @@ def get_source_files (self): 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 @@ def build_module (self, module, module_file, package): # 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 @@ def build_module (self, module, module_file, package): 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 @@ def build_modules (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 + # '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 () From be2cdd814051b3e077646a20ad98330a52e2f707 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 29 Mar 2000 02:13:09 +0000 Subject: [PATCH 0219/2594] 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 ff680fa953..7d88c06332 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -141,9 +141,6 @@ def run (self): 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 @@ def run (self): # 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 @@ def run (self): 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 @@ def get_source_files (self): 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 @@ def build_extensions (self, extensions): 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: From d71b7f8620d2a8953c38ccc08541db8a7f087c51 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 29 Mar 2000 02:14:21 +0000 Subject: [PATCH 0220/2594] 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 97466fdb48..2da589ab29 100644 --- a/command/build.py +++ b/command/build.py @@ -80,18 +80,18 @@ def run (self): # 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 From 5d534b66bc25e3d1455e98944b5e5b6dfb1b7eb1 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 29 Mar 2000 02:15:57 +0000 Subject: [PATCH 0221/2594] 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 ed74971efa..ac9ec86517 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 @@ def run (self): # 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 @@ def run (self): # 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") From 66878bbd6f4d2c871a13c3eb34a8c8a988f7eaa3 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 29 Mar 2000 02:17:42 +0000 Subject: [PATCH 0222/2594] 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 eaaa1c7da2..7e1f2e2a4d 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 @@ def finalize_options (self): 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 @@ def run (self): # 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 From f925cbf975d85ad247f6d790159af67f3967d7b2 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 29 Mar 2000 02:18:39 +0000 Subject: [PATCH 0223/2594] 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 d2b37a8ce1..b7973c6142 100644 --- a/command/__init__.py +++ b/command/__init__.py @@ -8,9 +8,9 @@ __all__ = ['build', 'build_py', 'build_ext', + 'build_clib', 'install', - 'install_py', - 'install_ext', + 'install_lib', 'clean', 'sdist', ] From 104be39562c08bd0c87944ce3a13014a30459c43 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 29 Mar 2000 02:20:45 +0000 Subject: [PATCH 0224/2594] 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 5090f25bc3..08a1d641d1 100644 --- a/core.py +++ b/core.py @@ -618,7 +618,27 @@ def get_command_options (self, command, *options): 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 @@ def make_file (self, infiles, outfile, func, args, # make_file () -# end class Command +# class Command From 4aa19c2a2cd09156c6706104f484738302ca7901 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 29 Mar 2000 02:47:29 +0000 Subject: [PATCH 0225/2594] 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 0c15177893..19e1773111 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -11,7 +11,7 @@ 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 @@ def make_release_tree (self, base_dir, files): # 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): From 872994b9087f38871f5fba8b6b0062db9a2714cf Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 29 Mar 2000 02:48:40 +0000 Subject: [PATCH 0226/2594] 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 caf9906281..b40373c486 100644 --- a/util.py +++ b/util.py @@ -13,7 +13,7 @@ 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 (match, local_vars=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 () From 4546cfbf7ed2f8292aebd4f795132e0656a518d7 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 29 Mar 2000 02:53:02 +0000 Subject: [PATCH 0227/2594] 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 b40373c486..6351bc7edc 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 () From 408127fea3cd246776bfaac7c16cce3e250f0bee Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 29 Mar 2000 02:56:34 +0000 Subject: [PATCH 0228/2594] 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 6351bc7edc..8242e10fc1 100644 --- a/util.py +++ b/util.py @@ -24,11 +24,13 @@ # 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 From 6d6e77e2094148c5f828f314e7858209a4c78d25 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 29 Mar 2000 03:27:45 +0000 Subject: [PATCH 0229/2594] 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 8242e10fc1..272961948e 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 From 90d8d95cef6f3195a62348dfcaf06afa616cdb65 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 29 Mar 2000 03:29:34 +0000 Subject: [PATCH 0230/2594] 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 7e1f2e2a4d..64f7cbcf27 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -59,21 +59,16 @@ def run (self): 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 () From 93bb283e4ed96e98f1203996045abe8ddd0b69fa Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 29 Mar 2000 04:13:00 +0000 Subject: [PATCH 0231/2594] 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 bc27cea9b7..298991049f 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -166,9 +166,9 @@ def __init__ (self, 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) From c8a944d9a695816880e7f7aec21b351aa7df6185 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 29 Mar 2000 04:13:49 +0000 Subject: [PATCH 0232/2594] 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 7d88c06332..96e7ce524f 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -113,7 +113,9 @@ def finalize_options (self): 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) From 97eeb322fc5bdf19b0fa8fe387c09b11addb040e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 30 Mar 2000 19:47:22 +0000 Subject: [PATCH 0233/2594] 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 96e7ce524f..f2e0b319b3 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -259,7 +259,7 @@ def get_outputs (self): # get_outputs () - def build_extensions (self, extensions): + def build_extensions (self): # First, sanity-check the 'extensions' list self.check_extensions_list (self.extensions) From 986617164899c1f6a746c99318c4bebe6a0a8646 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 31 Mar 2000 02:50:04 +0000 Subject: [PATCH 0234/2594] 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 19e1773111..7dbd40138a 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -11,7 +11,8 @@ 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 @@ def finalize_options (self): 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 @@ def find_defaults (self): 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 @@ def read_manifest (self): 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 @@ def make_release_tree (self, base_dir, files): # 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 @@ def make_distribution (self): 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) From fa293d87ba55e60937559c9647bcef24827cba77 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 31 Mar 2000 02:52:02 +0000 Subject: [PATCH 0235/2594] 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 ac9ec86517..995fd875c3 100644 --- a/command/install.py +++ b/command/install.py @@ -365,9 +365,9 @@ def run (self): # 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 @@ 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: + 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") From 3e2f02534c35d876d827a766dac2cf397092d04f Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 31 Mar 2000 02:53:07 +0000 Subject: [PATCH 0236/2594] 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 64f7cbcf27..5740c5eed2 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -110,4 +110,24 @@ def get_outputs (self): # 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 From bdd1b78665f1d2ad66df4add652548e04474a315 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 31 Mar 2000 02:55:12 +0000 Subject: [PATCH 0237/2594] 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 0000000000..5630828580 --- /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 From 24d3df10c8c218a584fd5f3d5f1701fd31dc9730 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 31 Mar 2000 02:56:34 +0000 Subject: [PATCH 0238/2594] 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 0000000000..4383a8fa9f --- /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 From 55e4b812a77be3b24b104184c9c8970a874c805c Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 31 Mar 2000 02:57:31 +0000 Subject: [PATCH 0239/2594] Added DistutilsInternalError. --- errors.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/errors.py b/errors.py index 86d91dd6c0..f9d5c8de5a 100644 --- a/errors.py +++ b/errors.py @@ -68,6 +68,11 @@ class DistutilsPlatformError (DistutilsError): 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 @@ class DistutilsExecError (DistutilsError): DistutilsValueError = 'DistutilsValueError' DistutilsPlatformError = 'DistutilsPlatformError' DistutilsExecError = 'DistutilsExecError' + DistutilsInternalError = 'DistutilsInternalError' del types From 2ffc581e227586358f5f8e262afe5e6aac8e3cc8 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 31 Mar 2000 03:02:22 +0000 Subject: [PATCH 0240/2594] 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 272961948e..6674d0aa18 100644 --- a/util.py +++ b/util.py @@ -19,6 +19,11 @@ # 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 (match, local_vars=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 visit (z, dirname, names): 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 () From 5b64d0853985580ffcb024ead685aac6a42f25af Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 31 Mar 2000 03:05:18 +0000 Subject: [PATCH 0241/2594] 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 08a1d641d1..025e1c0df5 100644 --- a/core.py +++ b/core.py @@ -638,6 +638,13 @@ def is_pure (self): 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 @@ 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) + cmd_obj = self.find_peer (command) return cmd_obj.get_option (option) @@ -939,12 +946,13 @@ def mkpath (self, name, mode=0777): 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 @@ def spawn (self, cmd, search_path=1, level=1): 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): From 71554a0d965d88e26b49b9352b9f6c16d9dcdd4b Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 31 Mar 2000 03:14:51 +0000 Subject: [PATCH 0242/2594] 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 b7973c6142..385330b5e0 100644 --- a/command/__init__.py +++ b/command/__init__.py @@ -13,4 +13,6 @@ 'install_lib', 'clean', 'sdist', + 'bdist', + 'bdist_dumb', ] From a65325be6c89763d12b4948bfccb2f5e51171a4e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 31 Mar 2000 03:34:09 +0000 Subject: [PATCH 0243/2594] 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 298991049f..c906e11469 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -158,9 +158,6 @@ def __init__ (self, 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: From 62173c6e6b912a50267e06e7ad96074a8aa75a41 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 31 Mar 2000 03:50:23 +0000 Subject: [PATCH 0244/2594] 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 f2e0b319b3..2cb1c610f0 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -122,9 +122,24 @@ def finalize_options (self): 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 @@ def run (self): 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 @@ def build_extensions (self): 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 @@ def get_ext_fullname (self, ext_name): 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 From ac24d4ea40f9c79d063743d6b454f801a81c93e7 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 31 Mar 2000 03:52:16 +0000 Subject: [PATCH 0245/2594] 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 c906e11469..2828711e7b 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -321,10 +321,6 @@ def link_shared_object (self, 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 From acb7e0a92ecd47d4fb0e64424123a136b23a2b6c Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 31 Mar 2000 04:37:40 +0000 Subject: [PATCH 0246/2594] 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 6560fb4774..f48e6476fb 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -89,8 +89,7 @@ def run (self): 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: From f382f8cd5035da5ab29cf6d33f00f4f6a43c0c21 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 31 Mar 2000 04:40:25 +0000 Subject: [PATCH 0247/2594] 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 6674d0aa18..63b6bec05a 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 From ca78e832a4c8ecb5a9308ed104166e71b393d729 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 31 Mar 2000 04:53:41 +0000 Subject: [PATCH 0248/2594] 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 5630828580..12397fcac1 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -8,6 +8,7 @@ __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', } From 39f9a0206365461772e5c76d01a3cde0be9d1c10 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 31 Mar 2000 05:08:50 +0000 Subject: [PATCH 0249/2594] 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 12397fcac1..889cdba857 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -10,14 +10,15 @@ 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 @@ def finalize_options (self): 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() From 23873c733a5189d48d90a3ab638d0e7d155fa234 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 31 Mar 2000 05:20:27 +0000 Subject: [PATCH 0250/2594] 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 63b6bec05a..22fc437547 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 From f35f3bd982e3447106ba061b91ebe6f72a47d20a Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 31 Mar 2000 05:21:27 +0000 Subject: [PATCH 0251/2594] 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 889cdba857..685f52508e 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -63,6 +63,7 @@ def run (self): sub_cmd = self.find_peer (cmd_name) sub_cmd.set_option ('format', self.format) + self.run_peer (cmd_name) # run() From f9e57445d36b3438f083a1b69b5793e8b9f8145f Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 31 Mar 2000 05:22:47 +0000 Subject: [PATCH 0252/2594] 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 4383a8fa9f..5456e57ac3 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -10,7 +10,7 @@ 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 @@ def run (self): # 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() From 802b36518e79ab0103afe61bb5c1ee9e92c2f433 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 31 Mar 2000 16:47:40 +0000 Subject: [PATCH 0253/2594] 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 2828711e7b..07096e95db 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -17,6 +17,35 @@ 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 From 8b2185f27ae8706e420bcf52ee071a8c3f4ee5dd Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 31 Mar 2000 16:53:42 +0000 Subject: [PATCH 0254/2594] 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 07096e95db..b38aadbece 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -20,30 +20,30 @@ _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 From 373df8bfb5f251907217641652a46ad18603f4b1 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 31 Mar 2000 19:04:25 +0000 Subject: [PATCH 0255/2594] 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 b38aadbece..43a8596085 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -21,15 +21,25 @@ 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 @@ 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 From fe6451bacbd0075114cc247c568bde94f2b61caf Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 4 Apr 2000 01:40:52 +0000 Subject: [PATCH 0256/2594] 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 0000000000..ad78703713 --- /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 025e1c0df5..3df54a5e83 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 0000000000..50e755697b --- /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" From f3cceb54cf92355a4d0ce52a5b7b568c490753b3 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 4 Apr 2000 02:05:59 +0000 Subject: [PATCH 0257/2594] 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 0000000000..a28eed1ad8 --- /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 0000000000..97812edaf5 --- /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 0000000000..c049bbd217 --- /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 0000000000..91545abb18 --- /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 22fc437547..ddcf0d2862 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 @@ 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 (match, local_vars=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 () From 668f7321264af838cecd5c2b2fb57a01ece71857 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 6 Apr 2000 02:07:41 +0000 Subject: [PATCH 0258/2594] 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 50e755697b..7246b74001 100644 --- a/dist.py +++ b/dist.py @@ -183,9 +183,9 @@ def parse_command_line (self, args): 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 -- From 1941b15adc3750d84b149fb84af75e36a633ad61 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 9 Apr 2000 02:31:45 +0000 Subject: [PATCH 0259/2594] 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 0000000000..f1db3c6c40 --- /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$ From 79cc38ef59bfe78ac6a3eae934cebb9ef10798ba Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 9 Apr 2000 03:48:37 +0000 Subject: [PATCH 0260/2594] 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 ad78703713..b2fb517e73 100644 --- a/cmd.py +++ b/cmd.py @@ -388,3 +388,7 @@ def make_file (self, infiles, outfile, func, args, # make_file () # class Command + + +if __name__ == "__main__": + print "ok" From 1633033ac04ecddfdb450f8e60efa54c7eb894d5 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 9 Apr 2000 03:49:20 +0000 Subject: [PATCH 0261/2594] 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 3df54a5e83..9a801063c7 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 () From 4973bedd82d22fe0b181d0a045565eac3e93a37f Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 9 Apr 2000 03:51:40 +0000 Subject: [PATCH 0262/2594] 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 7dbd40138a..349a824afd 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 @@ def initialize_options (self): self.force_manifest = 0 self.formats = None - self.list_only = 0 self.keep_tree = 0 @@ -228,6 +225,10 @@ def find_defaults (self): 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 From 4bd72ccc78221cffe25ef0048895e24c4a63ba6b Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 10 Apr 2000 00:02:16 +0000 Subject: [PATCH 0263/2594] 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 1f23b972e4..9305554999 100644 --- a/__init__.py +++ b/__init__.py @@ -9,3 +9,5 @@ """ __revision__ = "$Id$" + +__version__ = "0.8" From 515669ce62c1021d1cecfadc1c006bb074beb202 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 10 Apr 2000 00:18:16 +0000 Subject: [PATCH 0264/2594] 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 b2fb517e73..76a76919cd 100644 --- a/cmd.py +++ b/cmd.py @@ -58,7 +58,6 @@ def __init__ (self, dist): # (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 @@ def __init__ (self, dist): 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 @@ def mkpath (self, name, mode=0777): 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 @@ 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.""" + 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 @@ def make_archive (self, base_name, format, 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 @@ def make_file (self, infiles, outfile, func, args, # 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 7246b74001..cb18031470 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 @@ def __init__ (self, attrs=None): # Default values for our command-line options self.verbose = 1 self.dry_run = 0 - self.force = 0 self.help = 0 self.help_commands = 0 From 5f99c51af634e6d53353955add8bda432fa2d9ad Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 10 Apr 2000 00:19:42 +0000 Subject: [PATCH 0265/2594] 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 2da589ab29..bca0ece808 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 @@ def initialize_options (self): 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 f48e6476fb..2311187484 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 @@ def initialize_options (self): self.define = None self.undef = None self.debug = None + self.force = 0 # initialize_options() @@ -65,7 +68,8 @@ def finalize_options (self): 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 2cb1c610f0..ddb01d4ef2 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 @@ def initialize_options (self): self.rpath = None self.link_objects = None self.debug = None + self.force = None def finalize_options (self): @@ -92,7 +95,8 @@ def finalize_options (self): 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 3baddc62c7..d719cec8d9 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 @@ def initialize_options (self): 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. From b3602e53092a10f2c8fd167c7621150b6c71249a Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 10 Apr 2000 01:15:06 +0000 Subject: [PATCH 0266/2594] 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 2c7318c04d..9cf9540db3 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -16,8 +16,8 @@ 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: From 82199403a5964745b104e69851f05de78387c2e5 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 10 Apr 2000 01:17:49 +0000 Subject: [PATCH 0267/2594] 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 9cf9540db3..3f345fbd12 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -20,7 +20,7 @@ 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, From 888eb4fbd70664a685df5f61ea3e4acb4fb738ca Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 10 Apr 2000 01:30:44 +0000 Subject: [PATCH 0268/2594] 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 76a76919cd..16008c4fd0 100644 --- a/cmd.py +++ b/cmd.py @@ -79,6 +79,11 @@ def __getattr__ (self, attr): 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 @@ def mkpath (self, name, mode=0777): 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 @@ 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 (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 @@ def make_file (self, infiles, outfile, func, args, # 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 From fa60eae5bb33a7b14d744cf26b0c0565e727db8a Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 10 Apr 2000 01:31:58 +0000 Subject: [PATCH 0269/2594] 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 bca0ece808..7fb0b56657 100644 --- a/command/build.py +++ b/command/build.py @@ -45,8 +45,6 @@ def initialize_options (self): 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 d719cec8d9..2a1fdd62c9 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -32,11 +32,9 @@ def initialize_options (self): 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. From a37b56ceefcc8d1b790a3c4061251b11daf1d26f Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 10 Apr 2000 13:11:51 +0000 Subject: [PATCH 0270/2594] 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 16008c4fd0..b3d16648d0 100644 --- a/cmd.py +++ b/cmd.py @@ -59,6 +59,12 @@ def __init__ (self, dist): 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 @@ def __getattr__ (self, attr): 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 From b40196ff52fb4a503022ee6e91b53ad5ec9c7317 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 14 Apr 2000 00:39:31 +0000 Subject: [PATCH 0271/2594] 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 3f345fbd12..f7c2e78326 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) From 0a8038198900ef7afaafc5e7c4a5b0fa584cf823 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 14 Apr 2000 00:48:15 +0000 Subject: [PATCH 0272/2594] 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 ec766f5405..0944461b9a 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -20,8 +20,7 @@ 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 @@ def __init__ (self, # 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 @@ def create_static_lib (self, 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) From a213af0d45d592238ea3d6e56beca78271100d20 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 14 Apr 2000 00:49:30 +0000 Subject: [PATCH 0273/2594] 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 349a824afd..f3cc041e7d 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -12,7 +12,7 @@ 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 @@ def read_template (self): 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 @@ def read_template (self): 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 @@ def read_template (self): action) continue - dir_pattern = words[1] + dir_pattern = native_path (words[1]) else: template.warn ("invalid manifest template line: " + @@ -347,9 +347,9 @@ def read_template (self): # 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 From 860fb8e70fb18c06ef9e82438fa2b5af33f926c0 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 14 Apr 2000 00:50:49 +0000 Subject: [PATCH 0274/2594] 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 ddb01d4ef2..f08c97e9e7 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -105,12 +105,9 @@ def finalize_options (self): # 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 @@ def finalize_options (self): # 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] From cde4940c51df0bc44586a948ba99d32218cbc75d Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 14 Apr 2000 13:53:34 +0000 Subject: [PATCH 0275/2594] 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 0944461b9a..5e1524c67c 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -158,11 +158,13 @@ def create_static_lib (self, 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) From 046fbde66e644516ce1dbe2b4decbc2348b0a137 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 15 Apr 2000 22:15:07 +0000 Subject: [PATCH 0276/2594] 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 b3d16648d0..d8e413798e 100644 --- a/cmd.py +++ b/cmd.py @@ -161,38 +161,28 @@ def announce (self, msg, level=1): 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 @@ def set_option (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 + AttributeError if any of the options specified as keyword arguments are not known.""" for k in optval.keys(): @@ -236,14 +226,10 @@ def set_undefined_options (self, src_cmd, *option_pairs): 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 2311187484..31170730e6 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -115,33 +115,33 @@ 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; + 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 @@ 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 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 f08c97e9e7..422b8cae1f 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -205,26 +205,26 @@ 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; + 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 @@ def build_extensions (self): 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 cb18031470..408b9f5c50 100644 --- a/dist.py +++ b/dist.py @@ -152,7 +152,7 @@ def __init__ (self, attrs=None): if hasattr (self, key): setattr (self, key, val) else: - raise DistutilsOptionError, \ + raise DistutilsSetupError, \ "invalid distribution option '%s'" % key # __init__ () @@ -447,27 +447,18 @@ def run_commands (self): 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 @@ def run_command (self, command): 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 @@ def get_command_options (self, command, *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) + for opt in options: + values.append (getattr (cmd_obj, option)) return tuple (values) diff --git a/errors.py b/errors.py index f9d5c8de5a..61cdb72c2a 100644 --- a/errors.py +++ b/errors.py @@ -46,15 +46,18 @@ class DistutilsArgError (DistutilsError): 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 @@ class DistutilsInternalError (DistutilsError): DistutilsArgError = 'DistutilsArgError' DistutilsFileError = 'DistutilsFileError' DistutilsOptionError = 'DistutilsOptionError' - DistutilsValueError = 'DistutilsValueError' DistutilsPlatformError = 'DistutilsPlatformError' DistutilsExecError = 'DistutilsExecError' DistutilsInternalError = 'DistutilsInternalError' diff --git a/msvccompiler.py b/msvccompiler.py index 43a8596085..c7a69c3594 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -360,6 +360,9 @@ def link_shared_object (self, 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 ddcf0d2862..be3a1d6e0f 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) From 30e154ec0f2489c3376e6cfc371e25a26a1fed70 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 15 Apr 2000 22:23:47 +0000 Subject: [PATCH 0277/2594] 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 61cdb72c2a..d66043a1fe 100644 --- a/errors.py +++ b/errors.py @@ -16,64 +16,65 @@ 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 From 02d779e3f493d0fe4faebc32f4dfc9636b0de179 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 19 Apr 2000 02:16:49 +0000 Subject: [PATCH 0278/2594] 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 c7a69c3594..3667afcd2d 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -333,15 +333,13 @@ def link_shared_object (self, (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 @@ def link_shared_object (self, 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 ----------------------------------------- From 32b92081ec16b3958ef792eff9e6588dcdff9e8c Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 19 Apr 2000 02:18:09 +0000 Subject: [PATCH 0279/2594] 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 f7c2e78326..c6341c105d 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) From 1822bd769bbf61b61f469351555b24c6765f08a9 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 19 Apr 2000 02:22:07 +0000 Subject: [PATCH 0280/2594] 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 c6341c105d..5cc71dc0ba 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 From 4b2a001255107048e4117ac88b0a3d1aa371fd47 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 19 Apr 2000 02:23:21 +0000 Subject: [PATCH 0281/2594] 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 9305554999..5b8a662888 100644 --- a/__init__.py +++ b/__init__.py @@ -10,4 +10,4 @@ __revision__ = "$Id$" -__version__ = "0.8" +__version__ = "0.8.1" From 8b97bc929e1390ec9ad9998d10c173923b0b91a0 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 21 Apr 2000 01:41:54 +0000 Subject: [PATCH 0282/2594] 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 3110ab30db..94e654e896 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -27,11 +27,244 @@ # 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__": From bacfe767f341ed061b5cc1c0ac1454d7f3a72d2e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 21 Apr 2000 01:44:00 +0000 Subject: [PATCH 0283/2594] 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 94e654e896..8ee27748da 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -266,127 +266,6 @@ def get_option_order (): # 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) From 97f6eb18591a6e8a62dc727e4900ef036c801637 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 21 Apr 2000 02:09:26 +0000 Subject: [PATCH 0284/2594] 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 8ee27748da..c8112331a0 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -64,7 +64,7 @@ def __init__ (self, option_table=None): # 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 @@ def __init__ (self, option_table=None): # __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 @@ def _grok_option_table (self): 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 @@ def getopt (self, args=None, object=None): # 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.""" From cae8078a1df69595f030390f134fd59452b6db77 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 21 Apr 2000 02:28:14 +0000 Subject: [PATCH 0285/2594] 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 408b9f5c50..bedd9d252a 100644 --- a/dist.py +++ b/dist.py @@ -12,7 +12,7 @@ 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 @@ def __init__ (self, attrs=None): 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 @@ def __init__ (self, attrs=None): 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 @@ def __init__ (self, attrs=None): # 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 @@ def parse_command_line (self, args): # 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 @@ def parse_command_line (self, args): 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 @@ def parse_command_line (self, args): # 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 @@ def parse_command_line (self, args): # 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 @@ def announce (self, msg, level=1): 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 @@ def is_pure (self): 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 () From 56ff5710cb6403a6135fdb2104a3545a27cdff64 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 21 Apr 2000 02:31:07 +0000 Subject: [PATCH 0286/2594] 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 c8112331a0..cfebab4920 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -105,6 +105,19 @@ def add_option (self, long_option, short_option=None, help_string=None): 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 @@ def _grok_option_table (self): ("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 From fe34d8a966eede0315681b03ec003d9b8fb0c681 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 21 Apr 2000 04:22:01 +0000 Subject: [PATCH 0287/2594] 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 cfebab4920..629da29eef 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -66,6 +66,10 @@ def __init__ (self, option_table=None): 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 @@ def get_attr_name (self, long_option): 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 @@ def _grok_option_table (self): 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 @@ def getopt (self, args=None, object=None): 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, \ From 7ee4ef65de0dc64230420e7ed910385384094967 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 21 Apr 2000 04:22:49 +0000 Subject: [PATCH 0288/2594] 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 bedd9d252a..a209212778 100644 --- a/dist.py +++ b/dist.py @@ -233,13 +233,10 @@ def parse_command_line (self, args): 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 From e90d86fe9ee732ff165ac44bf174312ac828e9c9 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 21 Apr 2000 04:31:10 +0000 Subject: [PATCH 0289/2594] 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 f3cc041e7d..59dd624813 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -310,24 +310,25 @@ def read_template (self): # 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 @@ def read_template (self): # 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 From bf586ddbeea4df44ebf9a21a17e12243bad0a327 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 21 Apr 2000 04:37:12 +0000 Subject: [PATCH 0290/2594] 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 59dd624813..43007d5970 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -107,23 +107,23 @@ def run (self): 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: From 5a78411da74f438590a1d76d71213a5b667c379e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 22 Apr 2000 02:51:25 +0000 Subject: [PATCH 0291/2594] 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 5456e57ac3..0a68d00a09 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -70,7 +70,7 @@ def run (self): # 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 43007d5970..2cf8e385b8 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -502,7 +502,7 @@ def make_distribution (self): # 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! From 9a32e742ff8308ffcf088fe46eb527fbffd0e812 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 22 Apr 2000 02:52:44 +0000 Subject: [PATCH 0292/2594] 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 a209212778..4a83c794ff 100644 --- a/dist.py +++ b/dist.py @@ -123,9 +123,11 @@ def __init__ (self, attrs=None): # 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 From 71ceb366185124bc76998bd06c7ef6676f7d7827 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 22 Apr 2000 03:09:56 +0000 Subject: [PATCH 0293/2594] 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 a28eed1ad8..bae425be06 100644 --- a/archive_util.py +++ b/archive_util.py @@ -102,6 +102,20 @@ def visit (z, dirname, names): # 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: From ff4f4d515fa616a7480b7a3be0d42fb6b10cf7d5 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 22 Apr 2000 03:11:17 +0000 Subject: [PATCH 0294/2594] 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 9a801063c7..814f4418c1 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 () From b106a335675ced0bd73d4a70addc388feef883e4 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 22 Apr 2000 03:11:55 +0000 Subject: [PATCH 0295/2594] 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 2cf8e385b8..8aa4618ecc 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -11,10 +11,10 @@ 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 @@ def finalize_options (self): 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): From b99aaeebb214b61283c589940ed59489353062f8 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 22 Apr 2000 03:20:49 +0000 Subject: [PATCH 0296/2594] 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 814f4418c1..4b6969284b 100644 --- a/core.py +++ b/core.py @@ -9,7 +9,7 @@ __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: From c0aaf184cfb98c03241a7a455720f56074e1877d Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 22 Apr 2000 15:14:58 +0000 Subject: [PATCH 0297/2594] 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 be3a1d6e0f..9c436b9474 100644 --- a/util.py +++ b/util.py @@ -18,6 +18,30 @@ 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 From 32155114f6d362c883a9d0f7e337ab91bf7a2187 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 22 Apr 2000 15:17:14 +0000 Subject: [PATCH 0298/2594] 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 91545abb18..32245109d5 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]) From cc06f983649b572614316eeaf64846a5dcecd5bf Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 23 Apr 2000 02:50:45 +0000 Subject: [PATCH 0299/2594] 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 629da29eef..39450e8079 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -304,7 +304,7 @@ def get_option_order (self): 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 @@ def generate_help (header=None): # 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): From 2db555c9b072cb91541f1f9a98d3398fde1a8601 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 25 Apr 2000 01:33:11 +0000 Subject: [PATCH 0300/2594] 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 9c436b9474..ca20ae047a 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 From b2fe902127ca56285d80dbc3befd671ad467ba7c Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 25 Apr 2000 01:38:20 +0000 Subject: [PATCH 0301/2594] 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 bae425be06..050ea41796 100644 --- a/archive_util.py +++ b/archive_util.py @@ -15,12 +15,12 @@ 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 visit (z, dirname, names): 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 685f52508e..cde8dd63e8 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 8aa4618ecc..8d9a4650bd 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)"), From 9bf82f12921aae17ef321f30eb613aa3678ca513 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 25 Apr 2000 01:55:58 +0000 Subject: [PATCH 0302/2594] 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 5b8a662888..32fc276c40 100644 --- a/__init__.py +++ b/__init__.py @@ -10,4 +10,4 @@ __revision__ = "$Id$" -__version__ = "0.8.1" +__version__ = "0.8.2" From 622804da554351a32c6a379badb7520673a38060 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 26 Apr 2000 01:12:40 +0000 Subject: [PATCH 0303/2594] 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 8d9a4650bd..67917188de 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -11,7 +11,8 @@ 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 @@ def write_manifest (self): 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 () From ed0fd8fbc0c9d218ea3c1dfd6d64936c790080d9 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 26 Apr 2000 01:14:33 +0000 Subject: [PATCH 0304/2594] 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 67917188de..f644e9fbfb 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)"), From 36f1d229a8213decc8b304e83dc49b81739b8e4d Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 26 Apr 2000 02:26:55 +0000 Subject: [PATCH 0305/2594] 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 4a83c794ff..ae95009fee 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 @@ def __init__ (self): self.url = None self.licence = None self.description = None + self.long_description = None # -- Metadata query methods ---------------------------------------- @@ -680,7 +683,10 @@ def get_licence(self): 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__": From c5f056b71b4498a249e5af284d31ea665ebd2a52 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 26 Apr 2000 02:29:51 +0000 Subject: [PATCH 0306/2594] 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 0a68d00a09..23672a63e4 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -11,7 +11,7 @@ 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): From 48f223a6efaadc98c4e9dd36f41f5f769a0db99d Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 26 Apr 2000 02:38:01 +0000 Subject: [PATCH 0307/2594] 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 995fd875c3..98b08cd7c6 100644 --- a/command/install.py +++ b/command/install.py @@ -9,6 +9,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.errors import DistutilsOptionError @@ -182,15 +183,47 @@ def finalize_options (self): # 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 @@ def finalize_other (self): # Windows and Mac OS for now 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): From 6becff488178f722e192b09ba308afb6417ec772 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 27 Apr 2000 01:53:46 +0000 Subject: [PATCH 0308/2594] 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 ca20ae047a..8e642e16c6 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 From edd515dffffa31cf704c25c1bd4138ba4f6c85d1 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 27 Apr 2000 01:56:38 +0000 Subject: [PATCH 0309/2594] 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 98b08cd7c6..c70ed9a1d1 100644 --- a/command/install.py +++ b/command/install.py @@ -10,7 +10,7 @@ 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 @@ def initialize_options (self): # 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 @@ def finalize_options (self): # 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 @@ def finalize_options (self): } 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 @@ def finalize_options (self): # 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 @@ def finalize_options (self): 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 @@ def finalize_options (self): # 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 @@ def _expand_attrs (self, attrs): def expand_basedirs (self): self._expand_attrs (['install_base', - 'install_platbase']) + 'install_platbase', + 'root']) def expand_dirs (self): self._expand_attrs (['install_purelib', From 671dda81fc33e545e873e0e5c3b8d81db93a2deb Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 6 May 2000 13:12:59 +0000 Subject: [PATCH 0310/2594] 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 422b8cae1f..a430c2b3cf 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -289,11 +289,13 @@ def build_extensions (self): # 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 From d5ac3b7d4bbd40e5f118c04f768a4ece54b43d55 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 7 May 2000 15:29:15 +0000 Subject: [PATCH 0311/2594] 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 d8e413798e..abb23c9fb2 100644 --- a/cmd.py +++ b/cmd.py @@ -157,47 +157,6 @@ def announce (self, msg, level=1): 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 @@ def set_undefined_options (self, src_cmd, *option_pairs): 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 @@ def get_peer_option (self, command, option): 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): From f1d400d10a1f601cc70a56c403a039bc3e9661b1 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 7 May 2000 15:30:31 +0000 Subject: [PATCH 0312/2594] 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 ae95009fee..f1ac35e48f 100644 --- a/dist.py +++ b/dist.py @@ -261,8 +261,8 @@ def parse_command_line (self, args): 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 @@ def run_commands (self): 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 @@ def run_command (self, command): 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): From f60583c2d16f6fce44276326164967fcb0ccd965 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 7 May 2000 15:32:13 +0000 Subject: [PATCH 0313/2594] 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 cde8dd63e8..01b4e162d3 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -64,7 +64,7 @@ def run (self): "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 5740c5eed2..852e3f63a2 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -79,7 +79,7 @@ def _mutate_outputs (self, has_any, build_cmd, cmd_option, output_dir): 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 = [] From 6ef49c947019b2eb196a1e735e0e231ad60cc8f6 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 9 May 2000 01:50:41 +0000 Subject: [PATCH 0314/2594] 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 a430c2b3cf..b234b9199a 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -308,6 +308,16 @@ def build_extensions (self): 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: From 46983102defd6428d66f50100e05d7746325b419 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Fri, 12 May 2000 00:33:14 +0000 Subject: [PATCH 0315/2594] 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 7fb0b56657..7d753b1126 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 @@ def initialize_options (self): 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 31170730e6..681cd76b56 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 @@ def initialize_options (self): self.undef = None self.debug = None self.force = 0 + self.compiler = None # initialize_options() @@ -68,6 +71,7 @@ def finalize_options (self): self.set_undefined_options ('build', ('build_temp', 'build_clib'), ('build_temp', 'build_temp'), + ('compiler', 'compiler'), ('debug', 'debug'), ('force', 'force')) @@ -93,9 +97,11 @@ def run (self): 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: From 7a4246d402981cf4382d566232f9f7100ed00572 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Fri, 12 May 2000 00:34:12 +0000 Subject: [PATCH 0316/2594] 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 b234b9199a..2142d05fdc 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 @@ def initialize_options (self): self.link_objects = None self.debug = None self.force = None + self.compiler = None def finalize_options (self): @@ -95,6 +98,7 @@ def finalize_options (self): self.set_undefined_options ('build', ('build_lib', 'build_lib'), ('build_temp', 'build_temp'), + ('compiler', 'compiler'), ('debug', 'debug'), ('force', 'force')) @@ -169,7 +173,8 @@ def run (self): # 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 @@ def build_extensions (self): 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) From 61b34f69cabd9929f8c6c7d3d7141acec8fb817c Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Fri, 12 May 2000 00:40:00 +0000 Subject: [PATCH 0317/2594] 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 8e642e16c6..b660b4bf2b 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): From a5a461bdbf26a573b747000752c03ff9dbf58819 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Fri, 12 May 2000 00:41:33 +0000 Subject: [PATCH 0318/2594] 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 f1ac35e48f..a0c6c9ea3d 100644 --- a/dist.py +++ b/dist.py @@ -8,11 +8,12 @@ __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 @@ def __init__ (self, attrs=None): # 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 @@ def __init__ (self, attrs=None): # __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 @@ def print_commands (self): # -- 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 From a228f136965785d37f5d53b274e92a9fb73ba007 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Fri, 12 May 2000 00:42:19 +0000 Subject: [PATCH 0319/2594] 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 4b6969284b..a39bccd307 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 () From 316a284532e000c26f077afbbdfbd6640b077210 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Fri, 12 May 2000 00:52:23 +0000 Subject: [PATCH 0320/2594] 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 abb23c9fb2..393734401d 100644 --- a/cmd.py +++ b/cmd.py @@ -344,5 +344,35 @@ def make_file (self, infiles, outfile, func, args, # 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 385330b5e0..573ae5138e 100644 --- a/command/__init__.py +++ b/command/__init__.py @@ -11,6 +11,8 @@ 'build_clib', 'install', 'install_lib', + 'install_scripts', + 'install_data', 'clean', 'sdist', 'bdist', diff --git a/command/install.py b/command/install.py index c70ed9a1d1..3f6fa33fc6 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 0000000000..f86d95e54b --- /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 0000000000..665208eb78 --- /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 a0c6c9ea3d..998cff7190 100644 --- a/dist.py +++ b/dist.py @@ -154,6 +154,8 @@ def __init__ (self, attrs=None): 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 From 593bfaaa0c09ea0939a54bdb050a2e91ab0e295e Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Fri, 12 May 2000 01:31:37 +0000 Subject: [PATCH 0321/2594] 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 393734401d..28d8617949 100644 --- a/cmd.py +++ b/cmd.py @@ -8,7 +8,7 @@ __revision__ = "$Id$" -import sys, string +import sys, os, string from types import * from distutils.errors import * from distutils import util @@ -355,23 +355,20 @@ def initialize_options (self): 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__": From a6ee82aab954f10c941860047e35bb26dcc2866b Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Fri, 12 May 2000 01:32:30 +0000 Subject: [PATCH 0322/2594] 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 665208eb78..5c3f2fb4c2 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 From d5eb448fcce25d02a65b8ebaabbc119d21be5672 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Fri, 12 May 2000 01:34:33 +0000 Subject: [PATCH 0323/2594] 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 f86d95e54b..d96a1def89 100644 --- a/command/install_data.py +++ b/command/install_data.py @@ -8,7 +8,4 @@ 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) + self._copy_files(self.distribution.data) From 58a11985342e4e261381916e6b43210b58ae0908 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Fri, 12 May 2000 01:46:47 +0000 Subject: [PATCH 0324/2594] 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 3f6fa33fc6..4e68e00e95 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 @@ def initialize_options (self): 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 @@ 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) + 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 @@ def handle_extra_path (self): 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 852e3f63a2..2d0a7190f8 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 @@ def initialize_options (self): self.build_dir = None self.compile = 1 self.optimize = 1 + self.skip_build = None def finalize_options (self): @@ -34,16 +36,19 @@ def finalize_options (self): ('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 From 5ff2cdf00f98ecaf7ed6140145e7253425171252 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Fri, 12 May 2000 01:54:50 +0000 Subject: [PATCH 0325/2594] 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 4e68e00e95..ba4110cb8a 100644 --- a/command/install.py +++ b/command/install.py @@ -356,7 +356,9 @@ 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'): - 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): From 23b8295f824f1520612add07320e9bf62bc71709 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Fri, 12 May 2000 01:58:29 +0000 Subject: [PATCH 0326/2594] 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 d96a1def89..95ef423127 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 5c3f2fb4c2..4e23efc8b6 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 From ea70d0b5ebf3197a83a0965f25125f27d0b05d88 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sat, 13 May 2000 01:35:33 +0000 Subject: [PATCH 0327/2594] 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 2142d05fdc..deb3b130b9 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -342,13 +342,11 @@ def build_extensions (self): # 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) From 0ad597128936e67ed4025f170e46d66a61a61ce3 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sat, 13 May 2000 01:48:15 +0000 Subject: [PATCH 0328/2594] 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 573ae5138e..cd7753fe64 100644 --- a/command/__init__.py +++ b/command/__init__.py @@ -17,4 +17,5 @@ 'sdist', 'bdist', 'bdist_dumb', + 'bdist_rpm', ] From 68504c5151605eb5da935805f7973fcb70e6f3aa Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sat, 13 May 2000 01:49:56 +0000 Subject: [PATCH 0329/2594] 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 01b4e162d3..970779dcaa 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 @@ def run (self): 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() From e1e8fd9c6397cccb920d93434f7b7393b361b564 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sat, 13 May 2000 01:52:14 +0000 Subject: [PATCH 0330/2594] 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 deb3b130b9..aa9ea0d034 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -295,6 +295,14 @@ def build_extensions (self): 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, From 25c47ab8b0225c1a451b36eb720cfe73954af9b1 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sat, 13 May 2000 01:56:55 +0000 Subject: [PATCH 0331/2594] 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 4f0f7e3e94..78166f2642 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 @@ def initialize_options(self): 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 @@ def finalize_options(self): 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 @@ def run(self): # 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 From 714539777115782361abaa82992217496a2d932e Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sat, 13 May 2000 01:58:19 +0000 Subject: [PATCH 0332/2594] 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 ba4110cb8a..e6ed984166 100644 --- a/command/install.py +++ b/command/install.py @@ -12,6 +12,7 @@ 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 @@ def initialize_options (self): #self.install_html = None #self.install_info = None + self.record = None def finalize_options (self): @@ -441,6 +445,22 @@ def run (self): "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 () From 1c914c4988122ad3436aac8bb4e2ab833aef511f Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sat, 13 May 2000 02:01:22 +0000 Subject: [PATCH 0333/2594] 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 e6ed984166..163b018749 100644 --- a/command/install.py +++ b/command/install.py @@ -437,14 +437,6 @@ def run (self): 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 @@ def run (self): 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 () From 6406c80d46402cc93bdfd1fa565acba9a7a33213 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sat, 13 May 2000 02:11:10 +0000 Subject: [PATCH 0334/2594] 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 2d0a7190f8..63c7a6bf00 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -72,8 +72,6 @@ def run (self): 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 @@ def _mutate_outputs (self, has_any, build_cmd, cmd_option, output_dir): 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 @@ def get_outputs (self): 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 () From b9143e831d39d846dd31c84f8e2252161355b346 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sat, 13 May 2000 02:13:53 +0000 Subject: [PATCH 0335/2594] 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 163b018749..2429f1b1f9 100644 --- a/command/install.py +++ b/command/install.py @@ -440,12 +440,7 @@ def run (self): # 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:] From 1f4bdba5ba8806643d7ff97ae965cb0edf1d99fe Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sat, 13 May 2000 02:16:45 +0000 Subject: [PATCH 0336/2594] 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 2429f1b1f9..a4986e5d5d 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 @@ def run (self): 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 From 2d34b0395476d92bd2fbec6f4fcd99cae6d2a383 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sat, 13 May 2000 02:20:43 +0000 Subject: [PATCH 0337/2594] Typo fix. --- command/clean.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/clean.py b/command/clean.py index 78166f2642..8706e57a1e 100644 --- a/command/clean.py +++ b/command/clean.py @@ -62,7 +62,7 @@ def run(self): # 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: From b7d9b1267bfe9638d965a16556a7ca408f6e6ff2 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sat, 13 May 2000 02:30:15 +0000 Subject: [PATCH 0338/2594] 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 8706e57a1e..d6999060bd 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 @@ def initialize_options(self): 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 @@ def finalize_options(self): 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 @@ def run(self): # 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 From 75835e5b552eacae52c565cb1f2172bc0c03405f Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sat, 13 May 2000 03:06:56 +0000 Subject: [PATCH 0339/2594] 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 23672a63e4..2de2befc14 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 @@ def initialize_options (self): 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 @@ def finalize_options (self): 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 From 046f4ee3aab641fbb6c72931d116e1c58bb99511 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sat, 13 May 2000 03:07:53 +0000 Subject: [PATCH 0340/2594] 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 95ef423127..448614b050 100644 --- a/command/install_data.py +++ b/command/install_data.py @@ -18,3 +18,6 @@ def finalize_options (self): 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 4e23efc8b6..43e5fc18bc 100644 --- a/command/install_scripts.py +++ b/command/install_scripts.py @@ -32,4 +32,7 @@ def run (self): 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 From 783f98d5381de82ad91e71bb529bf001d0e69456 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sat, 13 May 2000 03:08:28 +0000 Subject: [PATCH 0341/2594] 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 970779dcaa..892712ecfb 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -11,13 +11,16 @@ 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 @@ def finalize_options (self): #elif type (self.format) is StringType: # self.format = string.split (self.format, ',') - # finalize_options() From d3e1d51b64dc3cbddc78212a358a69cb51737e8e Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sat, 13 May 2000 03:09:50 +0000 Subject: [PATCH 0342/2594] 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 448614b050..fd9836b607 100644 --- a/command/install_data.py +++ b/command/install_data.py @@ -17,7 +17,7 @@ def finalize_options (self): 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 998cff7190..24c8d2b853 100644 --- a/dist.py +++ b/dist.py @@ -155,7 +155,7 @@ def __init__ (self, attrs=None): 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 From cc5ee731de127fa418aa290fa486531f9ce2e9e8 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sat, 13 May 2000 03:10:30 +0000 Subject: [PATCH 0343/2594] 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 28d8617949..c544b86440 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')) From 6c282fc092bc44258e4d614b541ff59d15bb6582 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sat, 13 May 2000 03:11:40 +0000 Subject: [PATCH 0344/2594] 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 0000000000..9c3aca3bc1 --- /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 From e46022cedb21e05ca1292aae4f598817b0c195c6 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sat, 13 May 2000 03:32:36 +0000 Subject: [PATCH 0345/2594] 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 0000000000..f0329aa9f9 --- /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 From ed02ed8aaba4efefd9e172b8f39e4f82e93a627c Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sat, 13 May 2000 03:35:05 +0000 Subject: [PATCH 0346/2594] 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 f0329aa9f9..73c1c190db 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() From 33eb8cbf408e379e5e965c32b1365d3f961003a4 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 20 May 2000 13:23:21 +0000 Subject: [PATCH 0347/2594] 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 ffad294706..51f5ae02a9 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -493,6 +493,7 @@ def link_shared_lib (self, libraries=None, library_dirs=None, runtime_library_dirs=None, + export_symbols=None, debug=0, extra_preargs=None, extra_postargs=None): @@ -515,7 +516,13 @@ def link_shared_lib (self, 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 @@ def link_shared_object (self, 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 3667afcd2d..b6ff432ce3 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -304,6 +304,7 @@ def link_shared_lib (self, libraries=None, library_dirs=None, runtime_library_dirs=None, + export_symbols=None, debug=0, extra_preargs=None, extra_postargs=None): @@ -313,6 +314,8 @@ def link_shared_lib (self, 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 @@ def link_shared_object (self, libraries=None, library_dirs=None, runtime_library_dirs=None, + export_symbols=None, debug=0, extra_preargs=None, extra_postargs=None): @@ -350,8 +354,12 @@ def link_shared_object (self, 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 5e1524c67c..40f564ab88 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -178,6 +178,7 @@ def link_shared_lib (self, libraries=None, library_dirs=None, runtime_library_dirs=None, + export_symbols=None, debug=0, extra_preargs=None, extra_postargs=None): @@ -188,6 +189,7 @@ def link_shared_lib (self, libraries, library_dirs, runtime_library_dirs, + export_symbols, debug, extra_preargs, extra_postargs) @@ -200,6 +202,7 @@ def link_shared_object (self, libraries=None, library_dirs=None, runtime_library_dirs=None, + export_symbols=None, debug=0, extra_preargs=None, extra_postargs=None): From 05fa00ab977197b8e02c7a99443e3541cdfc3624 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 20 May 2000 13:31:32 +0000 Subject: [PATCH 0348/2594] 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 51f5ae02a9..42221769e6 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -575,6 +575,33 @@ def link_executable (self, + # -- 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 From 022c5e143c2e7f68bc794a3f108a70193b302533 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 20 May 2000 14:07:59 +0000 Subject: [PATCH 0349/2594] Added missing import. --- dist.py | 1 + 1 file changed, 1 insertion(+) diff --git a/dist.py b/dist.py index 24c8d2b853..0f131980b8 100644 --- a/dist.py +++ b/dist.py @@ -12,6 +12,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.util import check_environ From dcc3e4d4cbc4029ba8c71691613faef73a4c1c4d Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 20 May 2000 15:06:48 +0000 Subject: [PATCH 0350/2594] Added 'has_scripts()', 'has_data_files()' methods. --- dist.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/dist.py b/dist.py index 0f131980b8..03b20e1f57 100644 --- a/dist.py +++ b/dist.py @@ -639,6 +639,12 @@ def has_c_libraries (self): 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 From 81213a2c15fe76d6d0a05621cad1959ac8deecad Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 20 May 2000 15:08:57 +0000 Subject: [PATCH 0351/2594] 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 63c7a6bf00..5b3d77a399 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -53,7 +53,12 @@ def run (self): # 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 From 8e79f197fb13982a17cfc1dfa4187444f290f7a5 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 20 May 2000 15:17:09 +0000 Subject: [PATCH 0352/2594] 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 a4986e5d5d..392fe507c9 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 @@ def handle_extra_path (self): # 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 @@ def run (self): # 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 @@ def get_outputs (self): 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 From a745efa83a06a337f54b8a80362957bdb4cdf4bc Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 20 May 2000 16:05:34 +0000 Subject: [PATCH 0353/2594] 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 32245109d5..a73db42b54 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 From 0f61807acb702df144c2266a92fc7a60988c9581 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 23 May 2000 01:42:17 +0000 Subject: [PATCH 0354/2594] 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 03b20e1f57..3fd29d9f48 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 @@ def __init__ (self, attrs=None): # 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 @@ def __init__ (self, attrs=None): 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 @@ def __init__ (self, attrs=None): # __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 @@ def find_config_files (self): 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 @@ def parse_config_files (self, filenames=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) + 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 @@ def parse_command_line (self, args): 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 @@ def parse_command_line (self, args): # 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 @@ def handle_display_options (self, option_order): 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 @@ def print_command_list (self, commands, header, max_length): 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 @@ def print_commands (self): 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 @@ def find_command_class (self, command): (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 @@ def find_command_obj (self, command, create=1): 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 @@ def run_commands (self): # -- 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 From 1ea22ad71cc61f4f7467262c768ec43fabbb81d2 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 23 May 2000 01:43:08 +0000 Subject: [PATCH 0355/2594] Tweaked usage message. --- core.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core.py b/core.py index a39bccd307..3eeba1df7e 100644 --- a/core.py +++ b/core.py @@ -21,10 +21,10 @@ # 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): From 138ae247bb47e34672d909a46fa998a4b86b90f6 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 23 May 2000 01:44:20 +0000 Subject: [PATCH 0356/2594] 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 39450e8079..588c6ba73f 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -239,7 +239,7 @@ def getopt (self, args=None, object=None): 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__": From 3f5a39b346e9ed623bba60da5b773e238721ef71 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 23 May 2000 01:55:01 +0000 Subject: [PATCH 0357/2594] 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 c544b86440..0cf88d41f5 100644 --- a/cmd.py +++ b/cmd.py @@ -183,7 +183,7 @@ def set_undefined_options (self, src_cmd, *option_pairs): # 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 @@ def set_undefined_options (self, src_cmd, *option_pairs): 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 2de2befc14..eaa192732b 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -63,7 +63,7 @@ def run (self): # 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) From ed327f4988e7fe976ba91586893e4babb655409c Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 23 May 2000 01:55:16 +0000 Subject: [PATCH 0358/2594] 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 5b3d77a399..e9cd80db35 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -8,7 +8,7 @@ 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"), From 684f4fcf396f5109aae531d4229699b55419b9e6 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 23 May 2000 03:47:35 +0000 Subject: [PATCH 0359/2594] 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 3fd29d9f48..3ceadf1985 100644 --- a/dist.py +++ b/dist.py @@ -258,8 +258,11 @@ def parse_config_files (self, filenames=None): 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 @@ def parse_config_files (self, filenames=None): 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 @@ def _parse_command_opts (self, parser, args): 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 @@ def _parse_command_opts (self, parser, args): 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 @@ def get_command_obj (self, command, create=1): """ 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 From 9e14daca28b2a95ede58edbb6a4a87ff81ac803f Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 23 May 2000 03:53:10 +0000 Subject: [PATCH 0360/2594] 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 588c6ba73f..a593354c9d 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -239,7 +239,7 @@ def getopt (self, args=None, object=None): 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 From ecde8dd1d2bee1ac529e903b23c1a874c220b846 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 23 May 2000 03:54:16 +0000 Subject: [PATCH 0361/2594] 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 3eeba1df7e..e22db939c9 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: From a5620289f67a4feeb2f0cb54e6c7b481da3f5ea4 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 23 May 2000 04:11:14 +0000 Subject: [PATCH 0362/2594] 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 3ceadf1985..7bdd9aa09b 100644 --- a/dist.py +++ b/dist.py @@ -627,6 +627,10 @@ def get_command_obj (self, command, create=1): 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 From 9449a2faa1832da6ab9fae2b656333f6548ebfb6 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 23 May 2000 23:14:00 +0000 Subject: [PATCH 0363/2594] 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 5cc71dc0ba..a5f3816a16 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(): From 9cd0dff83db07c62d3a941dbd82cbeb8b2652bcf Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 25 May 2000 01:10:04 +0000 Subject: [PATCH 0364/2594] 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 9c3aca3bc1..d10d0762ca 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -388,3 +388,5 @@ def _check_string_list(self, var_name, default_value = []): '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 7d753b1126..aab0d6f177 100644 --- a/command/build.py +++ b/command/build.py @@ -100,4 +100,4 @@ def run (self): 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 681cd76b56..dba9a40a8b 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -207,4 +207,4 @@ def build_libraries (self, libraries): # build_libraries () -# class BuildLib +# class build_lib diff --git a/command/build_ext.py b/command/build_ext.py index aa9ea0d034..4fb51ac713 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -406,4 +406,4 @@ def get_ext_libname (self, ext_name): 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 2a1fdd62c9..72c6157ceb 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -322,4 +322,4 @@ def build_packages (self): # build_packages () -# end class BuildPy +# class build_py diff --git a/command/clean.py b/command/clean.py index d6999060bd..62307a1968 100644 --- a/command/clean.py +++ b/command/clean.py @@ -74,3 +74,5 @@ def run(self): self.announce ("removing '%s'" % self.build_base) except OSError: pass + +# class clean diff --git a/command/install.py b/command/install.py index 392fe507c9..5398175f45 100644 --- a/command/install.py +++ b/command/install.py @@ -519,4 +519,4 @@ def create_path_file (self): "installations)") % filename) -# class Install +# class install diff --git a/command/sdist.py b/command/sdist.py index f644e9fbfb..56bc4c9556 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -519,7 +519,7 @@ def make_distribution (self): if not self.keep_tree: remove_tree (base_dir, self.verbose, self.dry_run) -# class Dist +# class sdist # ---------------------------------------------------------------------- From 6a76fd2a072f87312fcd0affe20e4715d4ec0a98 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 25 May 2000 01:19:18 +0000 Subject: [PATCH 0365/2594] 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 cd7753fe64..229c8a3486 100644 --- a/command/__init__.py +++ b/command/__init__.py @@ -9,6 +9,7 @@ 'build_py', 'build_ext', 'build_clib', + 'build_scripts', 'install', 'install_lib', 'install_scripts', diff --git a/command/build.py b/command/build.py index aab0d6f177..96d41d5e0b 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 @@ def initialize_options (self): 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 @@ def finalize_options (self): 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 @@ def run (self): 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 fd9836b607..65d188f7b3 100644 --- a/command/install_data.py +++ b/command/install_data.py @@ -7,17 +7,52 @@ __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 43e5fc18bc..9b78326373 100644 --- a/command/install_scripts.py +++ b/command/install_scripts.py @@ -8,23 +8,39 @@ __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 @@ def run (self): def get_inputs (self): return self.distribution.scripts or [] + def get_outputs(self): + return self.outfiles or [] + # class install_scripts From a561b7d0b579bb9f7bdf5e861daf54b20145a8c1 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 25 May 2000 01:20:15 +0000 Subject: [PATCH 0366/2594] 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 0000000000..1e7279d022 --- /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) From dec17861b3cc97457f3eef132a26ebe5c55d976a Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 25 May 2000 01:21:54 +0000 Subject: [PATCH 0367/2594] 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 96d41d5e0b..d9eed7685c 100644 --- a/command/build.py +++ b/command/build.py @@ -105,7 +105,7 @@ def run (self): 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 From ef17c6ecdc8fcc0285ed78c359093824cc040d50 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 25 May 2000 01:26:23 +0000 Subject: [PATCH 0368/2594] 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 0cf88d41f5..09727331d4 100644 --- a/cmd.py +++ b/cmd.py @@ -344,6 +344,11 @@ def make_file (self, infiles, outfile, func, args, # 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. From eaf0c26b00302e889666ceeff1ebfddf519cef59 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 25 May 2000 02:03:56 +0000 Subject: [PATCH 0369/2594] 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 1e7279d022..18297348c0 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -6,15 +6,16 @@ __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 @@ def finalize_options (self): 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 From 14a7a370319757cb72bc70c020c6944077f555e3 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 25 May 2000 02:14:26 +0000 Subject: [PATCH 0370/2594] 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 65d188f7b3..acc89aa395 100644 --- a/command/install_data.py +++ b/command/install_data.py @@ -10,6 +10,7 @@ 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 @@ def run (self): 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) From 62bcf92f91582ef01086ae94e446a1aee8b82274 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 25 May 2000 20:05:52 +0000 Subject: [PATCH 0371/2594] 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 18297348c0..6467e65b83 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -52,7 +52,7 @@ def copy_scripts (self): 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) From 514f94d06bedf2f945a7ec777616e0c47ec881a3 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 26 May 2000 00:44:06 +0000 Subject: [PATCH 0372/2594] 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 72c6157ceb..92a37f20c4 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -117,8 +117,17 @@ def get_package_dir (self, package): 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 @@ def check_package (self, package, 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): + 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 @@ def find_package_modules (self, package, package_dir): 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 @@ def find_modules (self): # 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 @@ def find_modules (self): 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 @@ def find_modules (self): if not self.check_module (module, module_file): continue - modules.append ((package, module, module_file)) + modules.append ((package, module_base, module_file)) return modules From a9e47e475f9420277cc20e1eccf5b67c42adf8b7 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 26 May 2000 00:54:52 +0000 Subject: [PATCH 0373/2594] 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 e22db939c9..cd0f8e9694 100644 --- a/core.py +++ b/core.py @@ -27,6 +27,11 @@ """ % ((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 () From bab7e2589a1ecb58045e27f6bce0938d7fee5377 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 26 May 2000 01:00:15 +0000 Subject: [PATCH 0374/2594] 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 7bdd9aa09b..33b3b657b1 100644 --- a/dist.py +++ b/dist.py @@ -185,11 +185,9 @@ def __init__ (self, attrs=None): 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 @@ def __init__ (self, attrs=None): # __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 @@ def parse_config_files (self, filenames=None): 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 @@ def _parse_command_opts (self, parser, args): # 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 From b960e574912ef3567a40ec67e3684d2f07d772a8 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 26 May 2000 01:31:53 +0000 Subject: [PATCH 0375/2594] 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 4fb51ac713..acf1d7b7d5 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -12,7 +12,7 @@ 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 @@ def build_extensions (self): "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 @@ def build_extensions (self): 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, From 4fb6d85e29cc25c036f2ecf88b746dd7de51387d Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 27 May 2000 01:25:16 +0000 Subject: [PATCH 0376/2594] 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 229c8a3486..56c26fe151 100644 --- a/command/__init__.py +++ b/command/__init__.py @@ -12,6 +12,7 @@ '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 0000000000..33edb945dd --- /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 From 2f4fee98094cc689a133ac05186a61fdffc0ea7f Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 27 May 2000 01:33:12 +0000 Subject: [PATCH 0377/2594] 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 5398175f45..d89ce4d255 100644 --- a/command/install.py +++ b/command/install.py @@ -18,24 +18,28 @@ '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 @@ def initialize_options (self): # 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 @@ def finalize_options (self): # 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 @@ def finalize_unix (self): 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 @@ def finalize_other (self): # Windows and Mac OS for now 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 @@ def expand_dirs (self): self._expand_attrs (['install_purelib', 'install_platlib', 'install_lib', + 'install_headers', 'install_scripts', 'install_data',]) @@ -478,6 +492,9 @@ def has_lib (self): 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() From 3269156427c4908f5388f8f214436797d1b41810 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 27 May 2000 01:33:49 +0000 Subject: [PATCH 0378/2594] 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 9b78326373..5888cafd44 100644 --- a/command/install_scripts.py +++ b/command/install_scripts.py @@ -11,12 +11,12 @@ 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"), ] From 1b401b74ff3cac71a809383c5fde85fe31a26ce7 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 27 May 2000 01:35:27 +0000 Subject: [PATCH 0379/2594] '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 c049bbd217..194183ae25 100644 --- a/dir_util.py +++ b/dir_util.py @@ -7,7 +7,8 @@ __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 From 20cf8c4a9698ff498f8ed64b06734a86f11af75c Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 27 May 2000 01:36:14 +0000 Subject: [PATCH 0380/2594] 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 33b3b657b1..1e8c63243b 100644 --- a/dist.py +++ b/dist.py @@ -146,6 +146,7 @@ def __init__ (self, attrs=None): 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 @@ def has_c_libraries (self): 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 From 0bf1c3e9874ed43aa7353c5e03884fc6775486c3 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 27 May 2000 03:03:23 +0000 Subject: [PATCH 0381/2594] 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 56bc4c9556..0a57ba364f 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -440,6 +440,13 @@ def read_template (self): # 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 () From 8b3691a0dc7439ba4ce8c91bc7cd112c3ad9b49f Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 27 May 2000 17:27:23 +0000 Subject: [PATCH 0382/2594] 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 09727331d4..c21ea03e6d 100644 --- a/cmd.py +++ b/cmd.py @@ -69,11 +69,11 @@ def __init__ (self, dist): # 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 @@ def __getattr__ (self, attr): 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 @@ def set_undefined_options (self, src_cmd, *option_pairs): # 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 892712ecfb..df4e3a0350 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -53,7 +53,7 @@ def finalize_options (self): # 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 @@ def run (self): "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 eaa192732b..51e35e4b15 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -40,7 +40,7 @@ def initialize_options (self): 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 @@ def finalize_options (self): 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 @@ def run (self): 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 d10d0762ca..d07f9249da 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 @@ def run (self): # 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 @@ def run (self): # 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 @@ def run (self): 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 @@ def _get_package_data(self): 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 @@ def _get_package_data(self): 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 @@ def _make_spec_file(self): '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 @@ def _make_spec_file(self): 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 @@ def _check_string_list(self, var_name, default_value = []): '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 d9eed7685c..b0894b8313 100644 --- a/command/build.py +++ b/command/build.py @@ -92,20 +92,20 @@ def run (self): # 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 acf1d7b7d5..cef3ca827d 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -167,7 +167,7 @@ def run (self): # 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 @@ def build_extensions (self): 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 d89ce4d255..bc5d8f33be 100644 --- a/command/install.py +++ b/command/install.py @@ -454,11 +454,11 @@ def run (self): # 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 @@ def get_outputs (self): # 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 @@ def get_inputs (self): # 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 e9cd80db35..d866d8cc82 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -46,9 +46,9 @@ 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_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 @@ 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_cmd = self.get_finalized_command (build_cmd) build_files = build_cmd.get_outputs() build_dir = getattr (build_cmd, cmd_option) @@ -138,11 +138,11 @@ def get_inputs (self): 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 5888cafd44..3eb3963c95 100644 --- a/command/install_scripts.py +++ b/command/install_scripts.py @@ -35,7 +35,7 @@ def finalize_options (self): 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 0a57ba364f..6626b9e66a 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -223,15 +223,15 @@ def find_defaults (self): 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 @@ def read_template (self): # 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 1e8c63243b..ece23a6ba3 100644 --- a/dist.py +++ b/dist.py @@ -681,7 +681,7 @@ def run_command (self, command): 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 From e98b1cb14ebf43c54f0ec74be9483e02d69d900e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 28 May 2000 23:47:00 +0000 Subject: [PATCH 0383/2594] 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 62307a1968..31147b58ef 100644 --- a/command/clean.py +++ b/command/clean.py @@ -34,13 +34,6 @@ def initialize_options(self): 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 @@ def run(self): # 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 @@ def run(self): # '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 From 5988ea06ee6f64f6b1da4397b66b1ea9b227484c Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 28 May 2000 23:47:31 +0000 Subject: [PATCH 0384/2594] 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 bc5d8f33be..13fa88ee19 100644 --- a/command/install.py +++ b/command/install.py @@ -8,7 +8,7 @@ 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 @@ def finalize_options (self): 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 @@ def finalize_options (self): # 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): From 691c62d56f771c3a2f1f914bc44f9958ad2d7c28 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 28 May 2000 23:49:03 +0000 Subject: [PATCH 0385/2594] 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 56c26fe151..95bce8d8c1 100644 --- a/command/__init__.py +++ b/command/__init__.py @@ -10,12 +10,12 @@ 'build_ext', 'build_clib', 'build_scripts', + 'clean', 'install', 'install_lib', 'install_headers', 'install_scripts', 'install_data', - 'clean', 'sdist', 'bdist', 'bdist_dumb', From b09a8fe0d18535ae8db3f8f6c980d5f2fde3e19f Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 28 May 2000 23:53:06 +0000 Subject: [PATCH 0386/2594] 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 ece23a6ba3..3391e53f5e 100644 --- a/dist.py +++ b/dist.py @@ -217,6 +217,35 @@ def get_option_dict (self, 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 = 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 @@ def get_command_obj (self, command, create=1): # 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 ---------------------- From d7dd557d81c46cd532939cdbf4a420f34d46bb1c Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 28 May 2000 23:54:00 +0000 Subject: [PATCH 0387/2594] Added 'dump_options()' for debugging output. --- cmd.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/cmd.py b/cmd.py index c21ea03e6d..f80338920e 100644 --- a/cmd.py +++ b/cmd.py @@ -135,6 +135,21 @@ def finalize_options (self): 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 From 73ce40de7dd0c8ef02a2f2b99d3cc308d9062916 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 30 May 2000 01:56:44 +0000 Subject: [PATCH 0388/2594] 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 42221769e6..33caf86830 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -15,6 +15,22 @@ 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 @@ def compile (self, 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 @@ def create_static_lib (self, '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 @@ def link_shared_lib (self, '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 @@ def link_shared_object (self, 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 @@ def link_executable (self, 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 b6ff432ce3..06b415ebde 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -14,7 +14,8 @@ 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 @@ def compile (self, 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 @@ def create_static_lib (self, 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 @@ def link_shared_object (self, 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 @@ def link_executable (self, 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 40f564ab88..c2f841ff67 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -21,7 +21,10 @@ 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 @@ def compile (self, 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 @@ def create_static_lib (self, # 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 @@ def link_shared_object (self, 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 @@ def link_executable (self, 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) From 8f4b133d35191a4e6efc52c8a743c01cc7be8cd7 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 30 May 2000 02:02:14 +0000 Subject: [PATCH 0389/2594] 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 d66043a1fe..161705633c 100644 --- a/errors.py +++ b/errors.py @@ -12,82 +12,81 @@ __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.""" + + From 16536d61d54c05d033267117413465693856737e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 30 May 2000 02:02:48 +0000 Subject: [PATCH 0390/2594] 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 33caf86830..834d543e33 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -15,22 +15,6 @@ 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 From 665fe5e7211490f9540308db3a3c625d0303ddc4 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 30 May 2000 02:02:49 +0000 Subject: [PATCH 0391/2594] 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 06b415ebde..06d8501afa 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -12,11 +12,11 @@ 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 c2f841ff67..4d38e4d8c1 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -22,9 +22,9 @@ 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 From ad92da0dcc01f36dfd7c4b5084c77a20a7352f7b Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 30 May 2000 02:04:29 +0000 Subject: [PATCH 0392/2594] Catch CCompiler exceptions in 'setup()'. --- core.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core.py b/core.py index cd0f8e9694..15a8814712 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: From 4c3307aa998ac380beb50662b75648fa6e5d80ea Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 30 May 2000 02:04:54 +0000 Subject: [PATCH 0393/2594] Cosmetic tweak. --- command/command_template | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/command/command_template b/command/command_template index 73c1c190db..50bbab7b6e 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 From 31ba84d5006258eea4f524076aebb085a1168aac Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 31 May 2000 01:05:35 +0000 Subject: [PATCH 0394/2594] 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 0000000000..9d2a6fa15c --- /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 From 69fc9746d19d0bdb9cf5d2d18eaebe561da1210b Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 31 May 2000 01:09:52 +0000 Subject: [PATCH 0395/2594] 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 cef3ca827d..f487a68cab 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -13,6 +13,7 @@ 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 @@ 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: + # '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 @@ def run (self): 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 @@ 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 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 @@ def get_outputs (self): # 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 @@ def build_extensions (self): # 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 @@ def build_extensions (self): 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 @@ def build_extensions (self): # 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 @@ def build_extensions (self): 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 @@ def build_extensions (self): # 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 () From 56c6ea7b26cbbbd156403430021ae019172da3ff Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 31 May 2000 01:11:20 +0000 Subject: [PATCH 0396/2594] 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 15a8814712..8903603925 100644 --- a/core.py +++ b/core.py @@ -12,8 +12,12 @@ 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 From 794b1fe1cd5521c67511325600ef8679c48a53e6 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 31 May 2000 02:14:32 +0000 Subject: [PATCH 0397/2594] 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 b660b4bf2b..6063aa6847 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" From f96f4e6d4a60e6d7c8cddc8656e6affbe456c61a Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 31 May 2000 02:17:19 +0000 Subject: [PATCH 0398/2594] 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 050ea41796..218450a6ba 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) From 378e81b1c72a9d54682c8e53a433d8d5cb2f81f7 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 31 May 2000 02:32:10 +0000 Subject: [PATCH 0399/2594] 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 13fa88ee19..7176fab890 100644 --- a/command/install.py +++ b/command/install.py @@ -10,7 +10,7 @@ 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 @@ def handle_extra_path (self): # 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 6626b9e66a..c06860fb65 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -11,7 +11,7 @@ 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 @@ def read_template (self): 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 @@ def read_template (self): 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 @@ def read_template (self): 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 6063aa6847..575463843b 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): From c0ed34f0d17691d413a195bb719cc004e50fd3fd Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 31 May 2000 23:56:45 +0000 Subject: [PATCH 0400/2594] 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 d07f9249da..e7e4ca7ef6 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 @@ def finalize_options (self): "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 @@ def run (self): 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 @@ def _get_package_data(self): 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')) From 0534a48d54c3fbf99ad65bff7b472b26eae70534 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 1 Jun 2000 00:40:25 +0000 Subject: [PATCH 0401/2594] 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 e7e4ca7ef6..c84fda873e 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -10,7 +10,7 @@ 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 @@ def initialize_options (self): 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 @@ def finalize_options (self): 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 From 7b2992eb1483e6c551d33f6e896569a420b1c88b Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 1 Jun 2000 01:07:55 +0000 Subject: [PATCH 0402/2594] 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 218450a6ba..3159c284e4 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 () From 71848a5af214fca715bf55b482e778cb4763c81b Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 1 Jun 2000 01:08:52 +0000 Subject: [PATCH 0403/2594] 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 f80338920e..852acadbea 100644 --- a/cmd.py +++ b/cmd.py @@ -215,6 +215,10 @@ def get_finalized_command (self, command, create=1): 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 @@ def spawn (self, cmd, search_path=1, level=1): 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, From 2d35a4ef38fc12bf2e61791fe1ba5bd2b99015ab Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 1 Jun 2000 01:09:47 +0000 Subject: [PATCH 0404/2594] 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 3391e53f5e..de4f7ef0a2 100644 --- a/dist.py +++ b/dist.py @@ -711,7 +711,7 @@ def reinitialize_command (self, command): command_name = command.get_command_name() if not command.finalized: - return + return command command.initialize_options() command.finalized = 0 self._set_command_options(command) From d9112d61365041f3bd448c3084b2e53a99b432bc Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 1 Jun 2000 01:10:56 +0000 Subject: [PATCH 0405/2594] 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 c06860fb65..03de85b1ae 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -64,6 +64,8 @@ def initialize_options (self): self.formats = None self.keep_tree = 0 + self.archive_files = None + def finalize_options (self): if self.manifest is None: @@ -520,12 +522,22 @@ def make_distribution (self): 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 From fcdde577abf375ececdcf97aed74e3d0d5e73463 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 2 Jun 2000 00:44:53 +0000 Subject: [PATCH 0406/2594] 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 852acadbea..b0a4b95942 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 @@ 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 @@ def ensure_finalized (self): # 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 @@ def ensure_finalized (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, - '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 @@ def dump_options (self, header=None, indent=""): 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 @@ def get_command_name (self): 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 @@ def set_undefined_options (self, src_cmd, *option_pairs): 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 @@ def reinitialize_command (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.""" - + 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 @@ def warn (self, 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.""" + """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 @@ 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.""" - + and force flags. + """ return util.copy_tree (infile, outfile, preserve_mode,preserve_times,preserve_symlinks, not self.force, @@ -302,6 +305,7 @@ def move_file (self, src, dst, level=1): 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 @@ def make_archive (self, base_name, format, 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 8903603925..9e390ac13a 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 @@ 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 de4f7ef0a2..6acc0544a8 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 @@ 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 @@ def parse_command_line (self, args): # 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 @@ def get_command_obj (self, command, create=1): 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'). From c857db21dbfe9234d637d3b65ffe37f49464d7d5 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 2 Jun 2000 01:49:58 +0000 Subject: [PATCH 0407/2594] 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 c84fda873e..b09b657fb6 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -7,7 +7,7 @@ __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 @@ def initialize_options (self): 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 @@ def finalize_options (self): 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 @@ def _get_package_data(self): 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 @@ def _make_spec_file(self): ] # 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 @@ def _make_spec_file(self): '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 @@ def _make_spec_file(self): 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 @@ def _make_spec_file(self): ]) # 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 @@ def _make_spec_file(self): '%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([ From 4e66ce5352c3529cb79bf189c35da0b990b0993b Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 2 Jun 2000 01:52:04 +0000 Subject: [PATCH 0408/2594] 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 b09b657fb6..d37d609094 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -318,73 +318,6 @@ def run (self): # 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 @@ def _make_spec_file(self): 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 From 1870024f295ec8b39849c562a75f6a11deb725b7 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 2 Jun 2000 01:55:36 +0000 Subject: [PATCH 0409/2594] 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 9e390ac13a..2bad10e438 100644 --- a/core.py +++ b/core.py @@ -70,8 +70,6 @@ class found in 'cmdclass' is used in place of the default, which is 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 @@ class found in 'cmdclass' is used in place of the default, which is # 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 @@ class found in 'cmdclass' is used in place of the default, which is 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: From 1d343163373ad5f9fa7fe5e11e44d4f7af9eabae Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 2 Jun 2000 01:59:33 +0000 Subject: [PATCH 0410/2594] 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 6acc0544a8..64f63add57 100644 --- a/dist.py +++ b/dist.py @@ -294,15 +294,16 @@ def find_config_files (self): 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 @@ def parse_command_line (self, args): # 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 @@ def _parse_command_opts (self, parser, args): 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 @@ def get_command_obj (self, command, create=1): 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) From a3be3dbd094620fea37917c49fd6d93cabd4a16c Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 2 Jun 2000 02:01:51 +0000 Subject: [PATCH 0411/2594] 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 d37d609094..aecf5c9dec 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -9,7 +9,7 @@ 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 @@ def ensure_filename (self, option, default=None): 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: From 9057f17f11ad3f506bab940ada1bd488f9add36a Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 2 Jun 2000 02:23:42 +0000 Subject: [PATCH 0412/2594] 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 32fc276c40..2ba82bd58d 100644 --- a/__init__.py +++ b/__init__.py @@ -10,4 +10,4 @@ __revision__ = "$Id$" -__version__ = "0.8.2" +__version__ = "0.9pre" From 821ff992cc9ca03f511e1e7d69a71c37f1e80938 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 3 Jun 2000 00:44:30 +0000 Subject: [PATCH 0413/2594] 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 a5f3816a16..53da48264e 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -6,7 +6,7 @@ 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""" From c5268b3d895061e69e712f4e375e8997a03de8e3 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 3 Jun 2000 01:02:06 +0000 Subject: [PATCH 0414/2594] 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 2bad10e438..8bcf2a87d6 100644 --- a/core.py +++ b/core.py @@ -80,7 +80,10 @@ class found in 'cmdclass' is used in place of the default, which is # 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. From 85ef7f4621c78621e4a2858b21c950cede345f8f Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 3 Jun 2000 01:03:55 +0000 Subject: [PATCH 0415/2594] 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 aecf5c9dec..9bce04c1ad 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -308,9 +308,8 @@ def run (self): 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) From 4e412ef49e4dc89329ceb1a213f4b695f611e080 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 4 Jun 2000 13:42:52 +0000 Subject: [PATCH 0416/2594] 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 92a37f20c4..0405d392d3 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 @@ def finalize_options (self): # 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 @@ def run (self): # 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 @@ def run (self): # 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 @@ def find_package_modules (self, package, package_dir): 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 @@ def find_modules (self): # 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 @@ def find_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.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 = [] From 32b0296e148d27447ca6b7154f8fd2d94b6a6a5e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 4 Jun 2000 14:20:57 +0000 Subject: [PATCH 0417/2594] 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 9bce04c1ad..4e7675e806 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -7,7 +7,7 @@ __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 @@ def finalize_package_data (self): # 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: From f126db7e1143b197ef275f5545fc39258124fa87 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 4 Jun 2000 14:21:28 +0000 Subject: [PATCH 0418/2594] 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 b0a4b95942..9f63f838a7 100644 --- a/cmd.py +++ b/cmd.py @@ -9,7 +9,7 @@ __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 @@ def announce (self, msg, level=1): 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): From bf680c9cda3ae15b841455b9b8f8b858eb9fffd4 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 4 Jun 2000 15:00:34 +0000 Subject: [PATCH 0419/2594] 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 4e7675e806..612629fe4c 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 @@ def initialize_options (self): 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 @@ def initialize_options (self): self.clean = 1 self.use_rpm_opt_flags = 1 + self.rpm3_mode = 1 # initialize_options() @@ -160,31 +164,28 @@ def finalize_package_data (self): 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 @@ def run (self): 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 @@ def run (self): 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 @@ def _make_spec_file(self): # ]) # 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 @@ def _make_spec_file(self): 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 From 2b0f2098a922464c8891848b2ef9da402ed0398b Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 4 Jun 2000 15:12:51 +0000 Subject: [PATCH 0420/2594] 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 03de85b1ae..af88eba07a 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -73,6 +73,7 @@ def finalize_options (self): 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 @@ def finalize_options (self): 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: From a74d191319d246a424b5de2e1ca55dce5cd4d799 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 4 Jun 2000 15:30:35 +0000 Subject: [PATCH 0421/2594] 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 612629fe4c..0959ad8f90 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 @@ def initialize_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" + 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 @@ def run (self): 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 @@ def run (self): 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 @@ def _make_spec_file(self): ('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: From 448d6882a1ad6730e07c74b34be274cfadfbd514 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 6 Jun 2000 02:18:13 +0000 Subject: [PATCH 0422/2594] '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 acc89aa395..5481dabe66 100644 --- a/command/install_data.py +++ b/command/install_data.py @@ -56,4 +56,4 @@ def get_inputs (self): return self.data_files or [] def get_outputs (self): - return self.outfiles + return self.outfiles or [] From 6a1d2a4ef333b3c52fbd11a6f4ba261349aae97c Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 6 Jun 2000 02:51:38 +0000 Subject: [PATCH 0423/2594] 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 df4e3a0350..3cd1eb0342 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 @@ def finalize_options (self): 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() From ea60038bb5f5d80fefb16772d62e90d2d2508dc5 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 6 Jun 2000 02:52:36 +0000 Subject: [PATCH 0424/2594] 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 64f63add57..44f5c8806a 100644 --- a/dist.py +++ b/dist.py @@ -712,6 +712,7 @@ def reinitialize_command (self, command): return command command.initialize_options() command.finalized = 0 + self.have_run[command_name] = 0 self._set_command_options(command) return command From 356f7580cd5f33456c296a1c8addd207d67a4548 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 6 Jun 2000 02:57:07 +0000 Subject: [PATCH 0425/2594] 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 0000000000..cad75bcb27 --- /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 From 86b8b566efbb265aaa76d90658759dad90a5d31c Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 7 Jun 2000 02:26:19 +0000 Subject: [PATCH 0426/2594] 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 44f5c8806a..ea4ec2e7e9 100644 --- a/dist.py +++ b/dist.py @@ -263,24 +263,25 @@ def find_config_files (self): 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): From daadfabdb5b0395712504823a574111262fd5361 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 7 Jun 2000 02:29:03 +0000 Subject: [PATCH 0427/2594] 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 ea4ec2e7e9..0b8a99e1cd 100644 --- a/dist.py +++ b/dist.py @@ -265,20 +265,23 @@ def find_config_files (self): 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) From daf86a83ea964ce51f67cff37b365f4129e93134 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 7 Jun 2000 03:00:06 +0000 Subject: [PATCH 0428/2594] 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 3159c284e4..27aa8c0bfc 100644 --- a/archive_util.py +++ b/archive_util.py @@ -110,11 +110,11 @@ def visit (z, dirname, names): 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 834d543e33..b146f89011 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -726,10 +726,22 @@ def mkpath (self, name, mode=0777): # 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 3cd1eb0342..66ef1133f3 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 @@ def run (self): 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 b0894b8313..c064f8394b 100644 --- a/command/build.py +++ b/command/build.py @@ -9,6 +9,7 @@ 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 dba9a40a8b..72df372fbf 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -23,7 +23,7 @@ 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 f487a68cab..53a265cc2e 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -14,6 +14,7 @@ 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 af88eba07a..221a4d9934 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -13,7 +13,7 @@ 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 0b8a99e1cd..88bd94a470 100644 --- a/dist.py +++ b/dist.py @@ -437,16 +437,38 @@ def _parse_command_opts (self, parser, args): 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 @@ def _show_help (self, 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 @@ def _show_help (self, return # _show_help () - + def handle_display_options (self, option_order): """If there were any non-global "display-only" options From e2136df03285e1476e460aab0d646aa929b69651 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 8 Jun 2000 00:02:36 +0000 Subject: [PATCH 0429/2594] 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 9f63f838a7..c9b5624f8a 100644 --- a/cmd.py +++ b/cmd.py @@ -172,6 +172,15 @@ def announce (self, msg, level=1): 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) From 33a33497961e25ab9467ff6de472a4c8fb2e6230 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 8 Jun 2000 00:08:14 +0000 Subject: [PATCH 0430/2594] 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 221a4d9934..b06c515656 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -272,21 +272,22 @@ def search_dir (self, dir, pattern=None): # 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 @@ def recursive_exclude_pattern (self, dir, pattern=None): (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 @@ def read_template (self): 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 @@ def read_template (self): # 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 @@ def read_template (self): 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 @@ def read_template (self): 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 @@ def read_template (self): 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 @@ def read_template (self): (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 @@ def read_template (self): 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 @@ def read_template (self): # 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 @@ def write_manifest (self): self.execute(write_file, (self.manifest, self.files), - "writing manifest file") + "writing manifest file '%s'" % self.manifest) # write_manifest () @@ -483,6 +541,7 @@ def read_manifest (self): 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 @@ def read_manifest (self): # read_manifest () - def make_release_tree (self, base_dir, files): # Create all the directories under 'base_dir' necessary to @@ -533,7 +591,7 @@ def make_distribution (self): # 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 From 0a3e5710675c4013d56a4f72ecccdc230282ec00 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 8 Jun 2000 00:14:18 +0000 Subject: [PATCH 0431/2594] 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 b06c515656..eabf5c7936 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -11,9 +11,9 @@ 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'} From 3acc171c28a904839b263f8a6cbb617c9894d184 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 8 Jun 2000 00:24:01 +0000 Subject: [PATCH 0432/2594] 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 eabf5c7936..cd7f258b53 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -68,8 +68,6 @@ def show_formats (): 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 @@ def check_metadata (self): 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 @@ def find_defaults (self): 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 @@ def search_dir (self, dir, pattern=None): 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 @@ def recursive_exclude_pattern (self, dir, pattern=None): 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 @@ def exclude_pattern (self, files, pattern, anchor=1, prefix=None): 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 @@ def write_manifest (self): 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: From 74f4d48df9b4e3afe675b6fe446c6d4ad1dee325 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 8 Jun 2000 00:35:33 +0000 Subject: [PATCH 0433/2594] 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 a593354c9d..6adfc819c2 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -369,9 +369,6 @@ def generate_help (self, header=None): 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 @@ def generate_help (self, header=None): else: lines.append (" --%-*s" % opt_names) + for l in text[1:]: + lines.append (big_indent + l) + # for self.option_table return lines From 691fce64def6c4aca873465234a5039c6a6c8480 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 8 Jun 2000 00:46:45 +0000 Subject: [PATCH 0434/2594] 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 cd7f258b53..2a4d346fff 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 @@ def run (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 + any are missing. + """ metadata = self.distribution.metadata missing = [] @@ -215,7 +220,16 @@ def get_file_list (self): 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 @@ 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'. + 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 @@ def read_manifest (self): 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 @@ def make_release_tree (self, base_dir, files): 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 @@ def get_archive_files (self): # 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: From 99ddd15c2fca235eaf905472d648564831458a89 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 8 Jun 2000 00:52:52 +0000 Subject: [PATCH 0435/2594] 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 2a4d346fff..26241353ac 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -189,7 +189,7 @@ def get_file_list (self): # 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 @@ def get_file_list (self): # 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 @@ def find_defaults (self): 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 @@ def search_dir (self, dir, pattern=None): # 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 @@ def exclude_pattern (self, files, pattern, anchor=1, prefix=None): 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, From 5f4a8d08d7029e5d4e199ddb50ca40743d836c2d Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 8 Jun 2000 01:06:02 +0000 Subject: [PATCH 0436/2594] 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 26241353ac..5a78cc58d2 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -195,6 +195,9 @@ def get_file_list (self): 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 @@ def read_template (self): # 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 @@ def make_distribution (self): # 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: From 1e78c7432ebfb62f570e7c380f0011ac4eb37e58 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 8 Jun 2000 01:22:48 +0000 Subject: [PATCH 0437/2594] 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 5a78cc58d2..48e9793e5e 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -233,6 +233,10 @@ def add_defaults (self): 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 @@ def add_defaults (self): 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: From 4ea6b39b83cf4405bad1110e81ad7cd240fbecfe Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 8 Jun 2000 14:21:23 +0000 Subject: [PATCH 0438/2594] 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 0959ad8f90..7f58a1da6b 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -436,6 +436,8 @@ def _make_spec_file(self): 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) From 524777aabcda3e1fafe2ce35eed3df0ac2be07ef Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 17 Jun 2000 01:34:45 +0000 Subject: [PATCH 0439/2594] 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 7176fab890..21d971b8d3 100644 --- a/command/install.py +++ b/command/install.py @@ -18,7 +18,7 @@ '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', }, From 1f24f7dc91a4145dafa4e7678bd50f47822730b5 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 17 Jun 2000 01:58:14 +0000 Subject: [PATCH 0440/2594] 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 194183ae25..e5b24fe475 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) From c5c7d04269747c746f02bfaec075f2710e7cd309 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 17 Jun 2000 02:16:46 +0000 Subject: [PATCH 0441/2594] 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 575463843b..279f246726 100644 --- a/util.py +++ b/util.py @@ -154,3 +154,23 @@ def _subst (match, local_vars=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 From f0ceb6e0a9eda6011c72bf4b0e3c7ed52eddd271 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 17 Jun 2000 02:17:45 +0000 Subject: [PATCH 0442/2594] 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 8bcf2a87d6..35afa84b7e 100644 --- a/core.py +++ b/core.py @@ -112,16 +112,7 @@ class found in 'cmdclass' is used in place of the default, which is 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") From 3b27bed6ec2871c31925d0f22bfbb8203dc81a5c Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 17 Jun 2000 02:18:19 +0000 Subject: [PATCH 0443/2594] 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 e5b24fe475..838a870d9b 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) From dab81b6a1e465fdc70dc1051a6bb4b9440737555 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 17 Jun 2000 02:19:30 +0000 Subject: [PATCH 0444/2594] 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 838a870d9b..85f8a18d2c 100644 --- a/dir_util.py +++ b/dir_util.py @@ -13,7 +13,7 @@ # 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( From 0e20e9c1fe3d9a35d483d251ef45a9675a7b36d0 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 17 Jun 2000 23:04:31 +0000 Subject: [PATCH 0445/2594] 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 53a265cc2e..3d6d17c8d8 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -160,7 +160,7 @@ def run (self): # '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 @@ def build_extensions (self): 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 @@ def build_extensions (self): 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 @@ def build_extensions (self): # 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 @@ def get_ext_fullname (self, ext_name): 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 From 895819ab90dd0a4851f356f2153b68f4e2888b9d Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 18 Jun 2000 15:45:55 +0000 Subject: [PATCH 0446/2594] '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 279f246726..74df8aa6a4 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): From 2591d3a3e93d7f39325d7e0e707f9457c7a1631a Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 21 Jun 2000 02:58:46 +0000 Subject: [PATCH 0447/2594] 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 b146f89011..53d4fa5301 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -414,6 +414,22 @@ def _need_link (self, objects, output_file): # -- 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 4d38e4d8c1..47d8ad6325 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -21,6 +21,7 @@ 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 @@ def __init__ (self, # __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, From 90843282f35eb7ff25172ea35d52d77e5135d56e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 21 Jun 2000 02:59:14 +0000 Subject: [PATCH 0448/2594] Oops, import 'grok_environment_error()'. --- core.py | 1 + 1 file changed, 1 insertion(+) diff --git a/core.py b/core.py index 35afa84b7e..4c982a07ad 100644 --- a/core.py +++ b/core.py @@ -13,6 +13,7 @@ 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 From ebbf910d513041fe74deacf0244b1b79b2bad2f5 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 21 Jun 2000 03:00:50 +0000 Subject: [PATCH 0449/2594] 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 cad75bcb27..570f71a5c1 100644 --- a/command/config.py +++ b/command/config.py @@ -13,7 +13,7 @@ __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 @@ def initialize_options (self): 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 @@ def _check_compiler (self): 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 @@ def _check_compiler (self): 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 @@ def _clean (self, *filenames): 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 @@ def _clean (self, *filenames): # 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() From ed71d7548070c87c61c8ac7b2ca5f1f4479d2622 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 21 Jun 2000 03:09:02 +0000 Subject: [PATCH 0450/2594] 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 21d971b8d3..5e8ade8d80 100644 --- a/command/install.py +++ b/command/install.py @@ -272,7 +272,8 @@ def finalize_options (self): # 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) From db2316094f167954780793b08eeb1a62dbad543c Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 21 Jun 2000 03:13:51 +0000 Subject: [PATCH 0451/2594] 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 5481dabe66..f8ed0a7540 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 @@ def run (self): 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 @@ def run (self): 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 33edb945dd..afcc887a69 100644 --- a/command/install_headers.py +++ b/command/install_headers.py @@ -7,6 +7,7 @@ __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 @@ def run (self): 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 From f888626d57eb7caa92c9b814e81802e2e2fd24c4 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 21 Jun 2000 03:14:27 +0000 Subject: [PATCH 0452/2594] 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 afcc887a69..991985b4ad 100644 --- a/command/install_headers.py +++ b/command/install_headers.py @@ -43,6 +43,5 @@ def get_inputs (self): def get_outputs (self): return self.outfiles - # run() # class install_headers From 88259b3d075b18c55429fd5c2413c2e08cbb8a56 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 21 Jun 2000 03:29:57 +0000 Subject: [PATCH 0453/2594] 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 48e9793e5e..ded8ec2269 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -173,14 +173,37 @@ def get_file_list (self): 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 " + From bed42c8495831545d7e190a1221bb59cacf441f3 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 21 Jun 2000 03:33:03 +0000 Subject: [PATCH 0454/2594] 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 0000000000..cc2ed5db66 --- /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 From ae6b225478590a6a5d5462307ff1bb565980619e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 23 Jun 2000 01:42:40 +0000 Subject: [PATCH 0455/2594] 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 a73db42b54..2d0148f3f1 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 () From 0b55a059cd131e263c36fb412c5556f9e32d2437 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 24 Jun 2000 00:18:24 +0000 Subject: [PATCH 0456/2594] 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 9d2a6fa15c..f0f68b9390 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) From 1f3bafb167859eb32d5a0fcec63c92bc72e4e65a Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 24 Jun 2000 00:19:35 +0000 Subject: [PATCH 0457/2594] 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 3d6d17c8d8..f3ff157183 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -367,11 +367,12 @@ def build_extensions (self): 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 @@ def build_extensions (self): # 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): From 9285ec887624587ca83a53445ce64b0bafd34ea9 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 24 Jun 2000 00:23:20 +0000 Subject: [PATCH 0458/2594] 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 27aa8c0bfc..08a3c8310c 100644 --- a/archive_util.py +++ b/archive_util.py @@ -110,10 +110,10 @@ def visit (z, dirname, names): 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 53d4fa5301..5be8c25a14 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -742,20 +742,30 @@ def mkpath (self, name, mode=0777): # 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 66ef1133f3..164699362d 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 @@ def finalize_options (self): 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 c064f8394b..d5513fc737 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 72df372fbf..9a82ac0917 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 ded8ec2269..93e53bbd66 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 @@ def show_formats (): 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 88bd94a470..2e4951ff88 100644 --- a/dist.py +++ b/dist.py @@ -437,11 +437,14 @@ def _parse_command_opts (self, parser, args): 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 @@ def _parse_command_opts (self, parser, args): 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 @@ def _show_help (self, 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 @@ def get_long_description(self): # 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" From 4fc435d3e805645f360a11cb97bf4d3e93e5af17 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 24 Jun 2000 01:22:41 +0000 Subject: [PATCH 0459/2594] 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 2e4951ff88..287137ee13 100644 --- a/dist.py +++ b/dist.py @@ -443,13 +443,14 @@ def _parse_command_opts (self, parser, args): 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 @@ def _parse_command_opts (self, parser, args): 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 From 869c587086b76265a98af7c65def177261b593cc Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 24 Jun 2000 01:23:37 +0000 Subject: [PATCH 0460/2594] 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 164699362d..47d4cbc965 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -14,6 +14,18 @@ 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 d5513fc737..1e87f23bbc 100644 --- a/command/build.py +++ b/command/build.py @@ -9,7 +9,12 @@ 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 9a82ac0917..7106882d79 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -23,7 +23,11 @@ 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 @@ def run (self): 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 f3ff157183..6b7ec74190 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -14,7 +14,6 @@ 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 @@ (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 93e53bbd66..5627ebb9e5 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -13,11 +13,27 @@ 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 @@ def show_formats (): default_format = { 'posix': 'gztar', 'nt': 'zip' } - def initialize_options (self): # 'template' and 'manifest' are, respectively, the names of # the manifest template and manifest file. From 614a927fbf22310f55f5ee3f895ce6a138bf6d35 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 24 Jun 2000 02:22:49 +0000 Subject: [PATCH 0461/2594] 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 5be8c25a14..4dd8645bc5 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -671,7 +671,9 @@ def object_filenames (self, 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, From 2bfcf462aa13cdd68bc582ec69ed80e2d52d6eb5 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 24 Jun 2000 17:22:39 +0000 Subject: [PATCH 0462/2594] 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 5e8ade8d80..1be49046ec 100644 --- a/command/install.py +++ b/command/install.py @@ -20,28 +20,28 @@ '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', } } From 3cac3f4156d58d1a827929273bb02e8645762484 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 24 Jun 2000 17:36:24 +0000 Subject: [PATCH 0463/2594] 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 f8ed0a7540..716febb5c6 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 @@ def run (self): 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) From 0a5cf577d2738162be426bc2a95b04d55bf3cbed Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 24 Jun 2000 18:10:48 +0000 Subject: [PATCH 0464/2594] 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 4dd8645bc5..9aa41adb9d 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -17,18 +17,17 @@ 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 @@ def _find_macro (self, name): 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 @@ def _check_macro_definitions (self, definitions): # -- 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 @@ def define_macro (self, name, value=None): 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 @@ def undefine_macro (self, name): 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 @@ def set_link_objects (self, objects): # (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 @@ def _fix_compile_args (self, output_dir, macros, include_dirs): 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 @@ def _prep_compile (self, sources, output_dir): 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 @@ def _prep_compile (self, sources, 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'.""" - + """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 @@ def _fix_object_args (self, 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.""" - + '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 @@ def _fix_lib_args (self, 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'.""" - + """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 @@ def compile (self, 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 @@ def create_static_lib (self, 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 @@ def link_shared_lib (self, 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 @@ def link_shared_object (self, 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 @@ def link_executable (self, 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 @@ def link_executable (self, # 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: From 57d36e94b071791b0183ad6df383ff293b2ecbc7 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 24 Jun 2000 20:40:02 +0000 Subject: [PATCH 0465/2594] 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 74df8aa6a4..5c1de78997 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 () From d710c3f8dee703894ec6e3e3ab6a4ead1b827be7 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 25 Jun 2000 02:05:29 +0000 Subject: [PATCH 0466/2594] 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 47d8ad6325..85ce5df719 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -20,7 +20,6 @@ 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 @@ 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 @@ def preprocess (self, (_, 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 @@ def preprocess (self, 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 @@ def compile (self, # 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 @@ def compile (self, 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 @@ def create_static_lib (self, 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 @@ def create_static_lib (self, # 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 @@ def link_shared_object (self, 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 @@ def link_shared_object (self, 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 @@ def link_executable (self, 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 @@ def find_library_file (self, dirs, lib): # 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:]) From 69604851149442fe1a0cba66cadf75e9fc6947fb Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 25 Jun 2000 02:08:18 +0000 Subject: [PATCH 0467/2594] 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 9aa41adb9d..e97c97776a 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -12,7 +12,10 @@ 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 @@ def __init__ (self, # 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 @@ def preprocess (self, 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 @@ def compile (self, 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 From c0d32c47f1960aae8b43c2efd4deeaf996740fc6 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 25 Jun 2000 02:09:14 +0000 Subject: [PATCH 0468/2594] 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 53da48264e..330d3b333c 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" From 9ce4a2b21ef10a62a81c34e979aa56e0f592d2b4 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 25 Jun 2000 02:10:46 +0000 Subject: [PATCH 0469/2594] 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 6b7ec74190..f8df87a724 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -12,6 +12,7 @@ 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 @@ def run (self): 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 @@ def swig_sources (self, sources): 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() From ef29e55a0092ccf9c959cd1cdb5edc96edf4dc8f Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 25 Jun 2000 02:10:58 +0000 Subject: [PATCH 0470/2594] 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 7106882d79..450dae1754 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -23,6 +23,7 @@ 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 @@ def run (self): 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) From e4b7595cc2c3b3cfb5830579792155aa2b82494a Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 25 Jun 2000 02:12:14 +0000 Subject: [PATCH 0471/2594] 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 161705633c..a718f01a40 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.""" From b6aafbc5a3d6007b6579cc491ce55c9b34a64620 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 25 Jun 2000 02:23:11 +0000 Subject: [PATCH 0472/2594] 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 f8df87a724..0a5fa9cd0d 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -188,7 +188,8 @@ def run (self): # 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 @@ def build_extensions (self): 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 @@ def build_extensions (self): 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 @@ def find_swig (self): # 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 @@ def prelink_hook (self): # 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 ----------------------------------------------- From 2ae918a80f3a4fd7b6cfad6add81e149bc52e8c4 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 25 Jun 2000 02:30:15 +0000 Subject: [PATCH 0473/2594] 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 0a5fa9cd0d..26d6981805 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -188,8 +188,7 @@ def run (self): # 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 @@ def build_extensions (self): # 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 @@ def build_extensions (self): # 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': From 7d785e9c7c3410b9c883c7bab41ebdcdfcab8244 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 25 Jun 2000 02:31:16 +0000 Subject: [PATCH 0474/2594] 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 06d8501afa..2e80ed5fd7 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 @@ def create_static_lib (self, 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 From 2bc3b231e4cc5a7d5570923a0d49df5500171d35 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 27 Jun 2000 01:21:22 +0000 Subject: [PATCH 0475/2594] 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 570f71a5c1..5c3f26a716 100644 --- a/command/config.py +++ b/command/config.py @@ -112,24 +112,26 @@ def _gen_temp_sourcefile (self, body, headers, lang): 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 @@ def _clean (self, *filenames): # 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 @@ def try_cpp (self, body=None, headers=None, lang="c"): 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 @@ def search_cpp (self, pattern, body=None, headers=None, lang="c"): 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 @@ def try_compile (self, body, headers=None, lang="c"): 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 @@ def try_link (self, 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 @@ def try_link (self, 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 @@ def try_run (self, 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 @@ def try_run (self, # (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 @@ def check_func (self, func, headers=None, 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 From 1930cf43c4152c2db35164b0dca4a8a85f58c37d Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 27 Jun 2000 01:24:07 +0000 Subject: [PATCH 0476/2594] 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 95bce8d8c1..ef8e9ad694 100644 --- a/command/__init__.py +++ b/command/__init__.py @@ -20,4 +20,5 @@ 'bdist', 'bdist_dumb', 'bdist_rpm', + 'bdist_wininst', ] diff --git a/command/bdist.py b/command/bdist.py index 47d4cbc965..3fcbf7793d 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'] From 78fe2e5e7ee63ad56dc426dd2afbd2c3f986d196 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 27 Jun 2000 01:24:38 +0000 Subject: [PATCH 0477/2594] 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 0000000000..6a4b4722ce --- /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: [PATCH 0478/2594] 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 26d6981805..106faccb28 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 @@ def initialize_options (self): self.debug = None self.force = None self.compiler = None + self.swig_cpp = None def finalize_options (self): @@ -443,15 +446,20 @@ def swig_sources (self, sources): 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 @@ def swig_sources (self, sources): 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 @@ def msvc_prelink_hack (self, sources, ext, extra_args): 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)) From f73b0b9df04af727d477c1f326fc1b82639b8ead Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 27 Jun 2000 01:43:24 +0000 Subject: [PATCH 0479/2594] 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 106faccb28..f8aa91cae3 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -155,6 +155,7 @@ def finalize_options (self): # 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 @@ def msvc_prelink_hack (self, sources, ext, extra_args): # 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)) From 603c444d6b9ead884a9c09ca25b5fb707ca622b6 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 27 Jun 2000 01:59:06 +0000 Subject: [PATCH 0480/2594] 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 330d3b333c..9ca94ed9c8 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(): From ecb70087dbb4512152365262d97e3879509cbec1 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 27 Jun 2000 01:59:43 +0000 Subject: [PATCH 0481/2594] 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 9ca94ed9c8..b2aa3f2d1e 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) From 9a02fc21cdba079187b15393495606987817c20e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 28 Jun 2000 00:36:40 +0000 Subject: [PATCH 0482/2594] 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 51e35e4b15..f9c81fc247 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -58,12 +58,7 @@ 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 = self.reinitialize_command('install') install.root = self.bdist_dir self.announce ("installing to %s" % self.bdist_dir) From 60c49dce0d05efd5482eb0d8dc99f969a6b981f8 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 28 Jun 2000 00:56:20 +0000 Subject: [PATCH 0483/2594] 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 6a4b4722ce..d3d17721e1 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -7,14 +7,14 @@ __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 @@ 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 = 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 @@ def run (self): 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 @@ def run (self): 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 @@ def run (self): # 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 @@ def create_exe (self, arcname): 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") From 834a327e9f342c7903be01c24ecca10ff2c8138a Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 28 Jun 2000 01:20:35 +0000 Subject: [PATCH 0484/2594] 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 0000000000..6c9ac827c1 --- /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 + From 6d74cfe58c300042b6a6e67053efb05ef972621e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 28 Jun 2000 01:25:27 +0000 Subject: [PATCH 0485/2594] Typo fix. --- dist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist.py b/dist.py index 287137ee13..bb4760657c 100644 --- a/dist.py +++ b/dist.py @@ -730,7 +730,7 @@ def _set_command_options (self, command_obj, option_dict=None): 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 From de30582dec6027d2b0441578781cbbb71033a72f Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 28 Jun 2000 01:29:09 +0000 Subject: [PATCH 0486/2594] 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 e97c97776a..20a5191ba3 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -492,7 +492,6 @@ def compile (self, 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 @@ def link_shared_lib (self, 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 @@ def link_shared_object (self, 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 @@ def mkpath (self, name, mode=0777): "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 2e80ed5fd7..db8bd35aca 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 @@ def link_shared_lib (self, 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 @@ def link_shared_lib (self, 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 @@ def link_shared_object (self, 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 85ce5df719..4772e1a8b5 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 @@ def link_shared_lib (self, 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 @@ def link_shared_lib (self, export_symbols, debug, extra_preargs, - extra_postargs) + extra_postargs, + build_temp) def link_shared_object (self, @@ -223,7 +227,8 @@ def link_shared_object (self, 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) = \ From 2a385bb0eca83d0735d51671439c5e1978ca4bd2 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 28 Jun 2000 01:29:37 +0000 Subject: [PATCH 0487/2594] 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 f8aa91cae3..adf85d3287 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -430,7 +430,8 @@ def build_extensions (self): 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 () From cce2862dcd7df9f3ed14f8c8277e9ba3fd768d98 Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Wed, 28 Jun 2000 14:48:01 +0000 Subject: [PATCH 0488/2594] 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 c9b5624f8a..d450ad320c 100644 --- a/cmd.py +++ b/cmd.py @@ -323,7 +323,7 @@ def execute (self, func, args, msg=None, level=1): 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 adf85d3287..54d48467bc 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 @@ def build_extensions (self): # 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 5c1de78997..ebfdf0abf5 100644 --- a/util.py +++ b/util.py @@ -28,7 +28,7 @@ def abspath(path): 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 918a1ded43..8b9ef10670 100644 --- a/version.py +++ b/version.py @@ -207,7 +207,7 @@ def __cmp__ (self, other): # 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 From 78d7f6ae8b4d39977f5bc4efc043a136d80e9c96 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 29 Jun 2000 02:06:29 +0000 Subject: [PATCH 0489/2594] 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 5627ebb9e5..6f867e89d8 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 @@ def initialize_options (self): # '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 @@ def get_file_list (self): 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 @@ def read_template (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: 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 @@ def select_pattern (self, files, pattern, anchor=1, prefix=None): (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 @@ def select_pattern (self, files, pattern, anchor=1, prefix=None): # 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: From 0350ebe9b29608aada89136c1db880c4f3552330 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 29 Jun 2000 02:16:24 +0000 Subject: [PATCH 0490/2594] 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 54d48467bc..9bb7e77001 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -309,7 +309,7 @@ def check_extensions_list (self, extensions): def get_source_files (self): - + self.check_extension_list() filenames = [] # Wouldn't it be neat if we knew the names of header files too... From 41192ad621b245fd3985b4191348ab27cab4360d Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 29 Jun 2000 22:57:55 +0000 Subject: [PATCH 0491/2594] 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 cc2ed5db66..7d43f02ff3 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 @@ def __init__ (self, 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 @@ def link_shared_object (self, 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 @@ def __init__ (self, 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) From ac971ce5217faf3cac2d2b9bf69d347dac0ca08c Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 29 Jun 2000 22:59:10 +0000 Subject: [PATCH 0492/2594] 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 db8bd35aca..6acd4efd0d 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -20,14 +20,14 @@ _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: From 9d71d05b4198ad054d20534d5714d1a1a8ce8877 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 29 Jun 2000 23:04:59 +0000 Subject: [PATCH 0493/2594] 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 6acd4efd0d..ae5e2d767a 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -20,7 +20,11 @@ _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 From 6165d95cb15b50fb4e20dd91ae8879229e231f06 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 29 Jun 2000 23:09:20 +0000 Subject: [PATCH 0494/2594] 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 9bb7e77001..4b5aaab8ef 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -528,12 +528,6 @@ def msvc_prelink_hack (self, sources, ext, extra_args): # 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) From 734a9d18a8887ba73bcba4bf355a7fd271b2a7d9 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 29 Jun 2000 23:50:19 +0000 Subject: [PATCH 0495/2594] 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 d3d17721e1..b81c4d4179 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 @@ def finalize_options (self): 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 @@ def run (self): # 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: From 67211d1e7cc101f970c3053015b8da87d889de28 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 30 Jun 2000 02:54:36 +0000 Subject: [PATCH 0496/2594] Bump version to 0.9. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 2ba82bd58d..ded0899c76 100644 --- a/__init__.py +++ b/__init__.py @@ -10,4 +10,4 @@ __revision__ = "$Id$" -__version__ = "0.9pre" +__version__ = "0.9" From a951e20878ee09dfd00a555b89ca61fabe4f79ff Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 30 Jun 2000 19:37:59 +0000 Subject: [PATCH 0497/2594] 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 ae5e2d767a..e58e6c10c8 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -20,10 +20,7 @@ _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 From 07f383457ff22083cc4b09c58be345d5d09b80ea Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 5 Jul 2000 03:06:46 +0000 Subject: [PATCH 0498/2594] 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 6f867e89d8..2d31640f44 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 @@ def initialize_options (self): self.formats = None self.keep_tree = 0 + self.dist_dir = None self.archive_files = None @@ -118,6 +122,9 @@ def finalize_options (self): 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 @@ def make_distribution (self): # 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 From 0b393c97f576061b8565a7b373788f7046f28148 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 5 Jul 2000 03:07:18 +0000 Subject: [PATCH 0499/2594] 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 3fcbf7793d..100563a9d1 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 @@ def finalize_options (self): 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() From 8efd9f49e8f828de05c505aed52f32b17a9a91ad Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 5 Jul 2000 03:07:37 +0000 Subject: [PATCH 0500/2594] 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 f9c81fc247..805aa0a9eb 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 @@ def initialize_options (self): self.bdist_dir = None self.format = None self.keep_tree = 0 + self.dist_dir = None # initialize_options() @@ -51,6 +54,8 @@ def finalize_options (self): ("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 @@ def run (self): 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: From 93518e1e37741f335e5aa118799826d5e6b5abbc Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 5 Jul 2000 03:08:55 +0000 Subject: [PATCH 0501/2594] 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 b81c4d4179..0c53bf9e6b 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 @@ def initialize_options (self): self.target_compile = 0 self.target_optimize = 0 self.target_version = None + self.dist_dir = None # initialize_options() @@ -57,6 +60,8 @@ def finalize_options (self): short_version) self.target_version = short_version + self.set_undefined_options('bdist', ('dist_dir', 'dist_dir')) + # finalize_options() @@ -92,7 +97,10 @@ def run (self): # 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 @@ def run (self): 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 @@ def create_inifile (self): # 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 @@ def create_exe (self, arcname): 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") From 9f4f35f3c5fe3f538355af050e65e951997b2dfb Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Fri, 7 Jul 2000 20:45:21 +0000 Subject: [PATCH 0502/2594] 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 20a5191ba3..933060aeb2 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 4b5aaab8ef..6456ad498f 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -370,12 +370,12 @@ def build_extensions (self): 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 991985b4ad..2e6ce8690b 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 bb4760657c..5f81c88773 100644 --- a/dist.py +++ b/dist.py @@ -275,13 +275,13 @@ def find_config_files (self): # 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 @@ def _parse_command_opts (self, parser, args): # 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 @@ def _parse_command_opts (self, parser, args): 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 @@ def _parse_command_opts (self, parser, args): (`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 @@ def _show_help (self, 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 From e522e80e068bcf976636e4454d1e6e127436f551 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Fri, 14 Jul 2000 13:35:07 +0000 Subject: [PATCH 0503/2594] 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 6456ad498f..37d19c7db4 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -309,7 +309,7 @@ def check_extensions_list (self, extensions): 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... From ac99c62e48886255ec93847fec136b5b81d9122e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 27 Jul 2000 01:21:54 +0000 Subject: [PATCH 0504/2594] 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 37d19c7db4..2ede447fed 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -292,7 +292,7 @@ def check_extensions_list (self, extensions): 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") From 20550ceda600efdce508bd686c887ce88b2c09e9 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 27 Jul 2000 01:23:19 +0000 Subject: [PATCH 0505/2594] 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 4772e1a8b5..799560c396 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -205,7 +205,7 @@ def link_shared_lib (self, 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 @@ def library_option (self, 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)) + 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 From ba1b7bf236f18fb1997ae789505856bb315c8f37 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 27 Jul 2000 01:58:45 +0000 Subject: [PATCH 0506/2594] 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 2d31640f44..3b91170cb3 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -319,26 +319,6 @@ def add_defaults (self): # 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 From 01105a3143f4096e4916f9fd95e219dc9d6d3fde Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 27 Jul 2000 02:13:20 +0000 Subject: [PATCH 0507/2594] 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 933060aeb2..eb6200f638 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -304,7 +304,7 @@ def set_runtime_library_dirs (self, 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 + 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 d450ad320c..8beb5d443e 100644 --- a/cmd.py +++ b/cmd.py @@ -55,7 +55,7 @@ def __init__ (self, dist): # 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 7f58a1da6b..3302eea24b 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -400,7 +400,7 @@ def _make_spec_file(self): ] 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 2ede447fed..c04036b02e 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -394,7 +394,7 @@ def build_extensions (self): # 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 6467e65b83..17fae8f7b5 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -46,7 +46,7 @@ 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. + 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 716febb5c6..9193f91924 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 7d43f02ff3..650627f5cf 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -145,7 +145,7 @@ def link_shared_object (self, ] # 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 5f81c88773..ed829fe480 100644 --- a/dist.py +++ b/dist.py @@ -167,7 +167,7 @@ def __init__ (self, attrs=None): # 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 @@ def get_command_class (self, 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 + 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 ebfdf0abf5..0bff3a5b85 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/ From 5338783b1c404117bb02b80412e4cc94b5380469 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 27 Jul 2000 02:17:40 +0000 Subject: [PATCH 0508/2594] 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 ded0899c76..f6af3c9dac 100644 --- a/__init__.py +++ b/__init__.py @@ -10,4 +10,4 @@ __revision__ = "$Id$" -__version__ = "0.9" +__version__ = "0.9.1pre" From e709ad9b04b0dca03c690faa222979297b759f74 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 30 Jul 2000 00:04:17 +0000 Subject: [PATCH 0509/2594] 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 0000000000..ee7051b97e --- /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 () From 8c4ad7e3aecd45e6207ee27003cceef9ed92b8d2 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 30 Jul 2000 00:08:13 +0000 Subject: [PATCH 0510/2594] 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 ee7051b97e..78f2a8d859 100644 --- a/filelist.py +++ b/filelist.py @@ -19,15 +19,19 @@ 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 @@ def __init__(self, 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 @@ def __debug_print (self, msg): 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 From aab3b0d0ea6785e13a0bdeb71853544d4302f568 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 30 Jul 2000 00:21:36 +0000 Subject: [PATCH 0511/2594] 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 78f2a8d859..5310ae5a67 100644 --- a/filelist.py +++ b/filelist.py @@ -16,6 +16,7 @@ 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 @@ def __debug_print (self, msg): 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, From ea8670808399ab267f60d8cfcab189102e069b8e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 30 Jul 2000 00:36:25 +0000 Subject: [PATCH 0512/2594] 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 5310ae5a67..749fa6124b 100644 --- a/filelist.py +++ b/filelist.py @@ -105,7 +105,7 @@ def _parse_template_line (self, 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 + # 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 @@ def process_template_line (self, line): 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 @@ def process_template_line (self, line): 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 @@ def process_template_line (self, line): 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 @@ def process_template_line (self, line): 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 @@ def process_template_line (self, line): # 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 @@ def select_pattern (self, pattern, """ 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 @@ def select_pattern (self, pattern, 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. """ From 86f9edf31c0f992aacc6f9fb0892a57e749e60f4 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 30 Jul 2000 00:37:04 +0000 Subject: [PATCH 0513/2594] Ditched the unused 'recursive_exclude_pattern()' method. --- filelist.py | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/filelist.py b/filelist.py index 749fa6124b..2eaff83705 100644 --- a/filelist.py +++ b/filelist.py @@ -248,30 +248,6 @@ def exclude_pattern (self, pattern, # 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 From 2a8fc1009cee9fe11b303b246f81dfc71806e647 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 30 Jul 2000 01:03:31 +0000 Subject: [PATCH 0514/2594] Added DistutilsTemplateError. --- errors.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/errors.py b/errors.py index a718f01a40..bec3464511 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): From 689482195099b5777e8d3ae68f3049df64c46d7a Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 30 Jul 2000 01:04:22 +0000 Subject: [PATCH 0515/2594] Typo fix. --- filelist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filelist.py b/filelist.py index 2eaff83705..a8049287df 100644 --- a/filelist.py +++ b/filelist.py @@ -97,7 +97,7 @@ def _parse_template_line (self, line): else: raise DistutilsTemplateError, "unknown action '%s'" % action - return (action, pattern, dir, dir_pattern) + return (action, patterns, dir, dir_pattern) # _parse_template_line () From 29d6ebdae92bac92b53c8cb2d69c710b12c8b1a9 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 30 Jul 2000 01:05:02 +0000 Subject: [PATCH 0516/2594] 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 3b91170cb3..045054b86a 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -6,16 +6,16 @@ __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 @@ def add_defaults (self): # 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 @@ def read_template (self): 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 @@ def prune_file_list (self): """ 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 @@ def get_archive_files (self): 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 () From ab16bbe1a660891123c029e7dbdd66e61a585a1c Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 30 Jul 2000 01:30:31 +0000 Subject: [PATCH 0517/2594] 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 045054b86a..4c2acf678f 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -128,8 +128,9 @@ def finalize_options (self): 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 @@ def run (self): # 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 @@ def check_metadata (self): 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 @@ def get_file_list (self): # 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 @@ def get_file_list (self): 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 @@ def get_file_list (self): # 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 @@ def add_defaults (self): 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 @@ def add_defaults (self): 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 @@ def add_defaults (self): 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 @@ def read_template (self): 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 @@ def prune_file_list (self): 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 @@ def write_manifest (self): 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 @@ def read_manifest (self): break if line[-1] == '\n': line = line[0:-1] - self.files.append (line) + self.filelist.append (line) # read_manifest () @@ -451,7 +435,7 @@ def make_distribution (self): 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) From d3cb1ab466d37d52bad2f5b44d852bb6009c2214 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 30 Jul 2000 01:45:42 +0000 Subject: [PATCH 0518/2594] 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 a8049287df..84f36d2c4b 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 @@ def __debug_print (self, msg): 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 @@ def process_template_line (self, line): # process_template_line () + # -- Filtering/selection methods ----------------------------------- + def include_pattern (self, pattern, anchor=1, prefix=None, is_regex=0): @@ -213,7 +244,8 @@ def include_pattern (self, pattern, 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): From c27f92a2c9bbf5835159d9b0d3e56ae258ef31be Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 30 Jul 2000 01:47:16 +0000 Subject: [PATCH 0519/2594] 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 4c2acf678f..4765d7fa13 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -14,7 +14,7 @@ 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 @@ def get_file_list (self): "(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 @@ def read_template (self): 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 () From 06830ada1cda3595d2a1f222fd8cbed35bdb691e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 1 Aug 2000 23:54:29 +0000 Subject: [PATCH 0520/2594] 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 c04036b02e..1ffe3234d4 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 @@ def check_extensions_list (self, extensions): # 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 @@ def build_extensions (self): 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 @@ def find_swig (self): # 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 @@ def get_ext_libname (self, ext_name): 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 From 7ef5e9f9cda6b4b01a7f64ef3b23052c0303e23f Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 2 Aug 2000 00:00:30 +0000 Subject: [PATCH 0521/2594] 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 e58e6c10c8..b86e0b3023 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -380,10 +380,22 @@ def link_shared_object (self, 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 From 247bbf98667922b35c599456d86afe9b69664773 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 2 Aug 2000 00:01:56 +0000 Subject: [PATCH 0522/2594] Ditched some debugging prints. --- msvccompiler.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/msvccompiler.py b/msvccompiler.py index b86e0b3023..a1dedb0e53 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -397,9 +397,6 @@ def link_shared_object (self, 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) From d43aeac154d817eefdde7f84243bd273d5542254 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 2 Aug 2000 00:04:13 +0000 Subject: [PATCH 0523/2594] 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 f0f68b9390..95a2ece8e8 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 @@ def __init__ (self, name, sources, 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 @@ def __init__ (self, name, sources, 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 From 336876f5fbc5664836629ae225b680d31020eb55 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 2 Aug 2000 00:37:32 +0000 Subject: [PATCH 0524/2594] 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 0bff3a5b85..d69626e1f1 100644 --- a/util.py +++ b/util.py @@ -18,16 +18,6 @@ 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()' From b4ff40df511d1477c3457931578bb2ab9630ab1b Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 2 Aug 2000 01:03:23 +0000 Subject: [PATCH 0525/2594] 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 6c9ac827c1..7daa597b75 100644 --- a/bcppcompiler.py +++ b/bcppcompiler.py @@ -11,17 +11,6 @@ # 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 @@ 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 @@ def compile (self, 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 @@ def link_shared_object (self, 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 @@ def link_shared_object (self, 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 @@ def library_dir_option (self, dir): 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 From 6904a1cecea7706215802d00436350ed15771192 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 2 Aug 2000 01:08:02 +0000 Subject: [PATCH 0526/2594] 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 86ea3dfb50..651124d682 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() From 8be2f9c465909ad6d71cc1bb116df7f87fa25422 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 2 Aug 2000 01:09:11 +0000 Subject: [PATCH 0527/2594] 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 b2aa3f2d1e..f6d941ac13 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(): From cc5431ba2d4c6e6fb0ab740721ea8734ca9dc3b8 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 2 Aug 2000 01:31:56 +0000 Subject: [PATCH 0528/2594] 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 650627f5cf..3f9a5bd585 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -6,53 +6,54 @@ 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 @@ def __init__ (self, 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 @@ def link_shared_object (self, 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 @@ def link_shared_object (self, 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 @@ def __init__ (self, 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) + From 8370736a2f17bc52b2aae9f0497cbf39a4851987 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 2 Aug 2000 01:34:18 +0000 Subject: [PATCH 0529/2594] 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 d866d8cc82..1c15db1494 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -74,7 +74,7 @@ def run (self): 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 () From 9624e74733a8888f50d4cc7bef25e7e3c78de50e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 2 Aug 2000 01:37:30 +0000 Subject: [PATCH 0530/2594] 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 d69626e1f1..37cd4b554e 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() From 20b387f6de0ca9835df40e2b4728a0bd9e629521 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 2 Aug 2000 01:37:53 +0000 Subject: [PATCH 0531/2594] 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 8beb5d443e..c905edecb4 100644 --- a/cmd.py +++ b/cmd.py @@ -318,31 +318,7 @@ def warn (self, 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 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): From edbbc73e92e3889f5f3ea0ac9f5a2e9445c5751d Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 2 Aug 2000 01:38:20 +0000 Subject: [PATCH 0532/2594] 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 eb6200f638..d8d8ab916c 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -15,7 +15,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 +from distutils.util import split_quoted, execute class CCompiler: @@ -784,6 +784,9 @@ def announce (self, msg, level=1): 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) From bb7acec3a19a3e0488ef95e2d5e41c0df0ed446b Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 2 Aug 2000 01:44:44 +0000 Subject: [PATCH 0533/2594] 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 100563a9d1..1c862d585d 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): From a509be9cada73fce73f71a7cf74a175eac6d530a Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 2 Aug 2000 01:49:40 +0000 Subject: [PATCH 0534/2594] Typo fix. --- sysconfig.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sysconfig.py b/sysconfig.py index f6d941ac13..2246327781 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: From e989444d5f0c7def7be57bbfa1d1529298fa1b07 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 4 Aug 2000 01:28:39 +0000 Subject: [PATCH 0535/2594] 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 d8d8ab916c..141af796a0 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -682,10 +682,12 @@ def library_option (self, lib): """ 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 799560c396..9339850102 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -317,7 +317,7 @@ def library_option (self, lib): 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 ( From be27b1c8bcaa43999f0008d56c4f5c502495e346 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 4 Aug 2000 01:29:27 +0000 Subject: [PATCH 0536/2594] 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 a1dedb0e53..eecbb620ed 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -474,13 +474,18 @@ 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): + # 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 From 8e0f15f62975ef32643724e05c08eebf8376db4b Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 4 Aug 2000 01:30:03 +0000 Subject: [PATCH 0537/2594] 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 7daa597b75..1d897de61a 100644 --- a/bcppcompiler.py +++ b/bcppcompiler.py @@ -357,28 +357,26 @@ def library_option (self, lib): 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 From 7555345e8cd473cae9a0b4ee91fb265885bb35c5 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 4 Aug 2000 01:31:13 +0000 Subject: [PATCH 0538/2594] Added 'debug_print()'. --- ccompiler.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ccompiler.py b/ccompiler.py index 141af796a0..ce3f2be69d 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -783,6 +783,11 @@ def announce (self, msg, level=1): 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) From 242c59cb2be299e3229b9bcde18dd47eff67fd4d Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 5 Aug 2000 01:25:24 +0000 Subject: [PATCH 0539/2594] 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 37cd4b554e..96266d266e 100644 --- a/util.py +++ b/util.py @@ -11,26 +11,6 @@ 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 From 177d3705cc1ffcd1b068776baea88dd5f44259cb Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 5 Aug 2000 01:31:54 +0000 Subject: [PATCH 0540/2594] 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 805aa0a9eb..e93c6b7272 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -10,7 +10,8 @@ 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 3302eea24b..e45d7a368b 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -10,7 +10,8 @@ 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 0c53bf9e6b..38e973b66d 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -9,7 +9,8 @@ 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 31147b58ef..1d8c7b2200 100644 --- a/command/clean.py +++ b/command/clean.py @@ -8,7 +8,7 @@ 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 1be49046ec..6385fdc224 100644 --- a/command/install.py +++ b/command/install.py @@ -10,7 +10,8 @@ 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 1c15db1494..879a7d00e2 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -4,7 +4,7 @@ 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 4765d7fa13..b2d7a86f1f 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -10,9 +10,7 @@ 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 @@ def finalize_options (self): "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 @@ def get_file_list (self): # 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 @@ def get_file_list (self): # 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 @@ def write_manifest (self): 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 @@ def make_release_tree (self, base_dir, files): """ # 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 @@ def make_distribution (self): 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 From d6bdb1ca69a126e1c651d06d304c283ca304254a Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 7 Aug 2000 00:45:51 +0000 Subject: [PATCH 0541/2594] 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 6adfc819c2..a62bc0df75 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -93,6 +93,7 @@ def __init__ (self, option_table=None): def _build_index (self): + self.option_index.clear() for option in self.option_table: self.option_index[option[0]] = option @@ -153,6 +154,10 @@ def _grok_option_table (self): 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 From f777456bab4933786d6890e00ccd603912e382b1 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 7 Aug 2000 00:48:04 +0000 Subject: [PATCH 0542/2594] 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 6385fdc224..2a59e16b96 100644 --- a/command/install.py +++ b/command/install.py @@ -273,7 +273,7 @@ def finalize_options (self): # 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)) From 5dcbeda034a291131d3b1106ea80896749366a9e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 8 Aug 2000 14:38:13 +0000 Subject: [PATCH 0543/2594] 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 96266d266e..2487f6dab6 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 From bb6a4ba2745a9747b1a152f6d6a1dbf5cc20574f Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 13 Aug 2000 00:36:47 +0000 Subject: [PATCH 0544/2594] 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 c905edecb4..474f8f321b 100644 --- a/cmd.py +++ b/cmd.py @@ -12,7 +12,7 @@ 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 @@ def execute (self, func, args, msg=None, level=1): 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 @@ def copy_file (self, infile, outfile, 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 @@ def copy_tree (self, infile, outfile, """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 @@ def spawn (self, cmd, search_path=1, level=1): 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 @@ def make_file (self, infiles, outfile, func, args, # 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 From 5351623c97c7a5adc612074a46d601dbd9f948c5 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 13 Aug 2000 00:38:58 +0000 Subject: [PATCH 0545/2594] Typo fix in docstring. --- extension.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/extension.py b/extension.py index 95a2ece8e8..f0021d0edc 100644 --- a/extension.py +++ b/extension.py @@ -23,8 +23,7 @@ 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 From f04ac5dd09157b550218a607a0721aa1bb86e821 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 13 Aug 2000 00:41:40 +0000 Subject: [PATCH 0546/2594] 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 f0021d0edc..1d3112b652 100644 --- a/extension.py +++ b/extension.py @@ -104,6 +104,6 @@ def __init__ (self, name, sources, 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 From dff17c86072b76f6491300166e4b9670149ddd9b Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 13 Aug 2000 00:42:35 +0000 Subject: [PATCH 0547/2594] 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 1ffe3234d4..aca6dac407 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -549,14 +549,10 @@ def get_export_symbols (self, ext): 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 From 9ef0502074aa149aa35ac81368f89253308a8012 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 13 Aug 2000 00:43:16 +0000 Subject: [PATCH 0548/2594] 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 1d897de61a..930490245b 100644 --- a/bcppcompiler.py +++ b/bcppcompiler.py @@ -67,8 +67,8 @@ def __init__ (self, 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 @@ def link_shared_object (self, # 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 @@ def link_shared_object (self, # 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 From 2c9c53b17589f71e434be1a8dae4ead6dd6e3f1e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 13 Aug 2000 00:43:56 +0000 Subject: [PATCH 0549/2594] 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 3f9a5bd585..da0d60b0c2 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -44,16 +44,19 @@ __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 @@ def __init__ (self, 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 @@ def link_shared_object (self, 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. From b9d00036db2020f271d9509152265dc170ac729b Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 13 Aug 2000 00:54:39 +0000 Subject: [PATCH 0550/2594] 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 930490245b..8ad9e4f2d4 100644 --- a/bcppcompiler.py +++ b/bcppcompiler.py @@ -253,6 +253,16 @@ def link_shared_object (self, 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 From 5042724ac26c16206dffec481827d36d8778a182 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 13 Aug 2000 01:18:55 +0000 Subject: [PATCH 0551/2594] 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 da0d60b0c2..2ac2678e73 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -65,11 +65,13 @@ def __init__ (self, 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 @@ def __init__ (self, # 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. From e016e0767f54a5256595679a34f7ce581508d573 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 15 Aug 2000 13:01:25 +0000 Subject: [PATCH 0552/2594] 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 0405d392d3..87e3efa218 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -93,12 +93,7 @@ def get_package_dir (self, package): 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 @@ def find_modules (self): 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: From 56988c3650a6601eca05bb51e07e84faaf629ca2 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 15 Aug 2000 13:03:16 +0000 Subject: [PATCH 0553/2594] 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 e45d7a368b..1da0b81f39 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -8,6 +8,7 @@ __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 @@ def finalize_options (self): 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 @@ def run (self): # 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 @@ def run (self): 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 @@ def run (self): 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() From 802df4b37327ad870f0a6fe0be1551740f138758 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 15 Aug 2000 13:05:35 +0000 Subject: [PATCH 0554/2594] 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 1da0b81f39..026a3ba4d5 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -295,14 +295,17 @@ def run (self): # 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() From 91251698bde14e2f5f9697626ff61d6ba3f408cb Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 15 Aug 2000 13:14:27 +0000 Subject: [PATCH 0555/2594] 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 f6af3c9dac..7fe2f913e9 100644 --- a/__init__.py +++ b/__init__.py @@ -10,4 +10,4 @@ __revision__ = "$Id$" -__version__ = "0.9.1pre" +__version__ = "0.9.1" From 7ec708f7b894796c2fe1d28216e47819fc9fcf87 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 22 Aug 2000 01:48:54 +0000 Subject: [PATCH 0556/2594] 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 08a3c8310c..26cd7fb2ec 100644 --- a/archive_util.py +++ b/archive_util.py @@ -10,7 +10,7 @@ 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 @@ def visit (z, dirname, names): '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): From a535c80ee5091193512f8e7456e92b827d1725df Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 22 Aug 2000 01:49:41 +0000 Subject: [PATCH 0557/2594] 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 b2d7a86f1f..2351ebbe37 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -442,8 +442,6 @@ def make_distribution (self): 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) From e1e774a29223c5fafee139a64411fec416e1fdb4 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 26 Aug 2000 02:21:55 +0000 Subject: [PATCH 0558/2594] 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 aca6dac407..4d779b8264 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -278,7 +278,9 @@ def check_extensions_list (self, extensions): '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') From 43abfd378a945259c7164cabc5fb16b44dc8a5f1 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 26 Aug 2000 02:37:07 +0000 Subject: [PATCH 0559/2594] 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 7fe2f913e9..afd65545b4 100644 --- a/__init__.py +++ b/__init__.py @@ -10,4 +10,4 @@ __revision__ = "$Id$" -__version__ = "0.9.1" +__version__ = "0.9.2pre" From 8294ae149b20ee146afc6c57c410979b440f7934 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 26 Aug 2000 02:40:10 +0000 Subject: [PATCH 0560/2594] 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 38e973b66d..9b600a9e1e 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 @@ def run (self): 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 @@ def create_inifile (self): # '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 @@ def create_inifile (self): 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 @@ def create_inifile (self): # 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: [PATCH 0561/2594] 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 9b600a9e1e..78a5c9c573 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -121,7 +121,7 @@ def create_inifile (self): # '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'): From 23be7d21e3e70fd3a397ce710a25166adfa8a037 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 29 Aug 2000 01:15:18 +0000 Subject: [PATCH 0562/2594] 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 87e3efa218..66f50241c1 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -177,13 +177,15 @@ 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 (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 2351ebbe37..06c8f1cb10 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -202,7 +202,10 @@ def get_file_list (self): # 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 4c982a07ad..39f1f54b29 100644 --- a/core.py +++ b/core.py @@ -25,26 +25,30 @@ # 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 @@ class found in 'cmdclass' is used in place of the default, which is 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 @@ class found in 'cmdclass' is used in place of the default, which is # 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 ed829fe480..1552dc0c41 100644 --- a/dist.py +++ b/dist.py @@ -131,6 +131,12 @@ def __init__ (self, attrs=None): # 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 @@ def parse_config_files (self, filenames=None): # -- 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 @@ def parse_command_line (self, args): 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 @@ def _show_help (self, 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 @@ def _show_help (self, 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 @@ def handle_display_options (self, option_order): 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 @@ def handle_display_options (self, option_order): 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 From 67d6f9504294214af2808483dbf15e6246a5a2d4 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 30 Aug 2000 17:16:27 +0000 Subject: [PATCH 0563/2594] 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 a62bc0df75..eaf6073760 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 [] From 630ec75678315f7043e3a14aa21b539d77979854 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 30 Aug 2000 17:32:24 +0000 Subject: [PATCH 0564/2594] 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 eecbb620ed..6c3f8dab71 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. From 1020b190ea0add7c81905e2bc2814678d0fc414a Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 31 Aug 2000 00:31:07 +0000 Subject: [PATCH 0565/2594] 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 6c3f8dab71..ae08e7fdcf 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -219,8 +219,9 @@ def __init__ (self, 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 = [ From 58ad88e9ee05aaf0a3ee7e3d6c23900d3277d399 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 1 Sep 2000 00:52:45 +0000 Subject: [PATCH 0566/2594] 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 39f1f54b29..5dfe2db839 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 @@ class found in 'cmdclass' is used in place of the default, which is 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 @@ class found in 'cmdclass' is used in place of the default, which is # 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 @@ class found in 'cmdclass' is used in place of the default, which is 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 @@ class found in 'cmdclass' is used in place of the default, which is 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 @@ class found in 'cmdclass' is used in place of the default, which is 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 () From 0a344796c68ac40b4796d2c07fa3b37104a4157b Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 1 Sep 2000 01:00:40 +0000 Subject: [PATCH 0567/2594] 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 afd65545b4..16a3984b0c 100644 --- a/__init__.py +++ b/__init__.py @@ -10,4 +10,4 @@ __revision__ = "$Id$" -__version__ = "0.9.2pre" +__version__ = "0.9.2" From 8553e855805f2e75372d582f60ffc9517a91f01e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 1 Sep 2000 01:23:26 +0000 Subject: [PATCH 0568/2594] 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 2246327781..3774ab25bc 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""" From 18201c35b97437685a0cbf43a86e188d471f22e2 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 1 Sep 2000 01:24:31 +0000 Subject: [PATCH 0569/2594] Rene Liebscher: comment fixes. --- cygwinccompiler.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cygwinccompiler.py b/cygwinccompiler.py index 2ac2678e73..f547d540f5 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -18,7 +18,7 @@ # # 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 @@ # (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.) From bb458fa60d179f85c4290c807987cf8f93a52e1d Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 1 Sep 2000 01:28:33 +0000 Subject: [PATCH 0570/2594] 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 8ad9e4f2d4..2b73b12f0d 100644 --- a/bcppcompiler.py +++ b/bcppcompiler.py @@ -224,17 +224,6 @@ def link_shared_object (self, 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 @@ def link_shared_object (self, 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 @@ def find_library_file (self, dirs, lib, debug=0): # 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: From 6d4ed7968e6a6ebde7b50b53361e180c1a1d8f2c Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 1 Sep 2000 01:44:45 +0000 Subject: [PATCH 0571/2594] 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 78a5c9c573..d51ea0a851 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -63,6 +63,12 @@ def finalize_options (self): 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 @@ def run (self): # 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 @@ def create_inifile (self): 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) From 47192a5d2460a813c4a177c8a2d27339c7fbecc1 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 6 Sep 2000 02:06:27 +0000 Subject: [PATCH 0572/2594] 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 4d779b8264..3f714c54d8 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 66f50241c1..5fcd18e788 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)"), ] From 1565bb9efd2c7943171ecd4c07774d02d6785038 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 6 Sep 2000 02:08:24 +0000 Subject: [PATCH 0573/2594] 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 06c8f1cb10..1bf297fdcb 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -219,10 +219,14 @@ def get_file_list (self): # 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 @@ def add_defaults (self): 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 From 6df7b0fff005b77bbf39fc0dc1c0e50284d60ede Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 6 Sep 2000 02:18:59 +0000 Subject: [PATCH 0574/2594] 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 1bf297fdcb..6de703ec92 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -405,9 +405,11 @@ def make_release_tree (self, base_dir, files): 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 @@ def make_release_tree (self, base_dir, files): 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 () From 9f06911a43f19a6b026ffb25fbe8858d330b1e69 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 7 Sep 2000 02:38:42 +0000 Subject: [PATCH 0575/2594] 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 026a3ba4d5..daf55b766a 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\"]"), From c8a74343d6d9f35457e17ff5dbf40f15ac533ec7 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Thu, 7 Sep 2000 15:59:22 +0000 Subject: [PATCH 0576/2594] 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 d51ea0a851..0ebc566838 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 @@ def finalize_options (self): 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 @@ def run (self): 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 @@ def get_inidata (self): 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 @@ def create_exe (self, arcname, fullname): 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 @@ def get_exe_bytes (self): # 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 --- From b5474c8672e2a3114d5225e07dfc855424ddc737 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Sat, 9 Sep 2000 19:52:49 +0000 Subject: [PATCH 0577/2594] 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 0ebc566838..5e94047c13 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -201,7 +201,7 @@ def get_exe_bytes (self): AAAA8AAAAA4fug4AtAnNIbgBTM0hVGhpcyBwcm9ncmFtIGNhbm5vdCBiZSBydW4gaW4gRE9TIG1v ZGUuDQ0KJAAAAAAAAADqs5WMrtL7367S+9+u0vvf1c7336/S+98tzvXfrNL731Hy/9+s0vvfzM3o 36bS+9+u0vrf89L7367S+9+j0vvfUfLx36PS+99p1P3fr9L731JpY2iu0vvfAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAUEUAAEwBAwDytrc5AAAAAAAAAADgAA8BCwEGAABAAAAAEAAAAJAAAPDUAAAA +AAAAAAAAAAAAAAAAUEUAAEwBAwBmkbo5AAAAAAAAAADgAA8BCwEGAABAAAAAEAAAAJAAABDVAAAA oAAAAOAAAAAAQAAAEAAAAAIAAAQAAAAAAAAABAAAAAAAAAAA8AAAAAQAAAAAAAACAAAAAAAQAAAQ AAAAABAAABAAAAAAAAAQAAAAAAAAAAAAAAAw4QAAcAEAAADgAAAwAQAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA @@ -214,7 +214,7 @@ def get_exe_bytes (self): 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 @@ def get_exe_bytes (self): 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 From fdee9cba1357af7cf490d43b5a994fb215155582 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Sat, 9 Sep 2000 21:15:12 +0000 Subject: [PATCH 0578/2594] 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 5e94047c13..2e535d51ec 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -84,6 +84,13 @@ def run (self): 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 @@ def get_inidata (self): # 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 @@ def get_exe_bytes (self): EXEDATA = """\ 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 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAGsAAACQAACAbAAAALgAAIBtAAAA4AAAgG4AAAAIAQCA -AAAAAAAAAAAAAAAAAAABAAkEAACoAAAAOKsAAH4BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAJ -BAAA0AAAALisAABuAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEACQQAAPgAAAAorgAAWgIAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAABAAkEAAAgAQAAiLAAAFwBAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAABAAkEAACoAAAAOKsAAKABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAJ +BAAA0AAAANisAABiAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEACQQAAPgAAABArgAAWgIAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAABAAkEAAAgAQAAoLAAAFwBAAAAAAAAAAAAAAAAAAAAAAAAAAAA APThAAC84QAAAAAAAAAAAAAAAAAAAeIAAMzhAAAAAAAAAAAAAAAAAAAO4gAA1OEAAAAAAAAAAAAA AAAAABviAADc4QAAAAAAAAAAAAAAAAAAJeIAAOThAAAAAAAAAAAAAAAAAAAw4gAA7OEAAAAAAAAA AAAAAAAAAAAAAAAAAAAAOuIAAEjiAABY4gAAAAAAAGbiAAAAAAAAdOIAAAAAAACE4gAAAAAAAI7i AAAAAAAAlOIAAAAAAABLRVJORUwzMi5ETEwAQURWQVBJMzIuZGxsAENPTUNUTDMyLmRsbABHREkz Mi5kbGwATVNWQ1JULmRsbABVU0VSMzIuZGxsAABMb2FkTGlicmFyeUEAAEdldFByb2NBZGRyZXNz -AABFeGl0UHJvY2VzcwAAAFJlZ0Nsb3NlS2V5AAAAUHJvcGVydHlTaGVldEEAAFRleHRPdXRBAABl -eGl0AABFbmRQYWludAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AABFeGl0UHJvY2VzcwAAAFJlZ0Nsb3NlS2V5AAAAUHJvcGVydHlTaGVldEEAAFRleHRPdXRBAABm +cmVlAABFbmRQYWludAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA From cde132acfb8b07bd59966d7392c82c4fb2de2b2b Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 10 Sep 2000 01:21:27 +0000 Subject: [PATCH 0579/2594] 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 daf55b766a..9bddddce24 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -7,7 +7,7 @@ __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 @@ 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 @@ -159,6 +167,15 @@ def finalize_options (self): "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 @@ def run (self): # 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 @@ def _make_spec_file(self): # 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 @@ def _make_spec_file(self): ('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), From 9a1e68da5031587d22fa58f20a36e3d12f76a9aa Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 11 Sep 2000 00:47:35 +0000 Subject: [PATCH 0580/2594] 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 1c862d585d..cc0c3985cf 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 @@ def initialize_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() + # '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 e93c6b7272..0fb8e83b2f 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 @@ def initialize_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') @@ -55,7 +60,9 @@ def finalize_options (self): ("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 @@ def run (self): # 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), From 445af86d1bb43c3904c1f0542efb72d1fe21ba1e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 11 Sep 2000 00:50:37 +0000 Subject: [PATCH 0581/2594] 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 0fb8e83b2f..2da9c48795 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -82,8 +82,6 @@ def run (self): # 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) From 561b706a78a2adb9f8909bf17b31c031f42a8cc9 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 12 Sep 2000 00:07:49 +0000 Subject: [PATCH 0582/2594] 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 1d8c7b2200..2f3597fdbc 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 @@ 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 @@ -37,6 +40,7 @@ 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')) @@ -50,27 +54,16 @@ def run(self): 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 From e4b265d05af821755af79ff534c0afb4f4cdfedb Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 13 Sep 2000 00:12:11 +0000 Subject: [PATCH 0583/2594] 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 2a59e16b96..2cb7871fdf 100644 --- a/command/install.py +++ b/command/install.py @@ -38,8 +38,8 @@ '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', From f322172314e80fb68beeb9438853db40cef0e116 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 13 Sep 2000 00:44:09 +0000 Subject: [PATCH 0584/2594] 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 16a3984b0c..34f55a2fd7 100644 --- a/__init__.py +++ b/__init__.py @@ -10,4 +10,4 @@ __revision__ = "$Id$" -__version__ = "0.9.2" +__version__ = "0.9.3pre" From 1d693fbcc485be61709c3a033b0b915a97cd31ea Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 13 Sep 2000 01:02:25 +0000 Subject: [PATCH 0585/2594] 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 2cb7871fdf..2332770c6b 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 @@ def initialize_options (self): 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 9193f91924..6cfc7d41f3 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 2e6ce8690b..5c06d574d6 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 879a7d00e2..2d19e5b96a 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 @@ 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 = 1 self.optimize = 1 self.skip_build = None @@ -35,6 +37,7 @@ def finalize_options (self): 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 3eb3963c95..d506f90f51 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 @@ 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) + 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. From 5f191c24e8ad0103340029f2d342a8d643f8e1fc Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 15 Sep 2000 00:03:13 +0000 Subject: [PATCH 0586/2594] 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 3774ab25bc..27c08dbf19 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 From c0c287bddf8631bd8bd8ea78a02b9e4bc2724a8a Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 15 Sep 2000 01:15:08 +0000 Subject: [PATCH 0587/2594] 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 27c08dbf19..de35d96a8d 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -15,7 +15,7 @@ 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) From 4f4854922673817fb057229ff42e7c47fded3f2c Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 15 Sep 2000 01:16:14 +0000 Subject: [PATCH 0588/2594] 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 2487f6dab6..468887127f 100644 --- a/util.py +++ b/util.py @@ -13,11 +13,49 @@ 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): From 4959d02600d04cf81f26e89cf083cc7455c5b736 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 15 Sep 2000 01:19:03 +0000 Subject: [PATCH 0589/2594] 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 3f714c54d8..76da00476c 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -528,12 +528,13 @@ def get_ext_filename (self, ext_name): "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. From 637280f9c621536fbea5a6030b991aa0c3b2364f Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 15 Sep 2000 01:20:10 +0000 Subject: [PATCH 0590/2594] 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 2332770c6b..938922a675 100644 --- a/command/install.py +++ b/command/install.py @@ -9,7 +9,7 @@ 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 @@ def finalize_options (self): # 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 () From 4018076a7ebe9adf03891e2eef88f35b3f6b2a1f Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 15 Sep 2000 01:21:07 +0000 Subject: [PATCH 0591/2594] 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 6cfc7d41f3..af348f5190 100644 --- a/command/install_data.py +++ b/command/install_data.py @@ -30,7 +30,9 @@ def initialize_options (self): 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 @@ def run (self): 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: From 1507bd55580f4662f9319edd26efdcc698a33cb2 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 16 Sep 2000 01:44:45 +0000 Subject: [PATCH 0592/2594] 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 76da00476c..f880a7a8d5 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', From 8a0e231dcf1cd16a0075cca6c78a7996144eb6d2 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 16 Sep 2000 01:54:46 +0000 Subject: [PATCH 0593/2594] 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 1e87f23bbc..1d901282ad 100644 --- a/command/build.py +++ b/command/build.py @@ -67,6 +67,8 @@ def finalize_options (self): # 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 @@ def finalize_options (self): 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 @@ def finalize_options (self): # "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 () From df1d24963b20a7532d52a441e7f39cb3c9b7c892 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 16 Sep 2000 01:59:06 +0000 Subject: [PATCH 0594/2594] Typo fix. --- command/install.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/install.py b/command/install.py index 938922a675..7182857d2c 100644 --- a/command/install.py +++ b/command/install.py @@ -226,7 +226,7 @@ def finalize_options (self): # 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(), From e24f5d6128e2325fa37f0f37819c253b35fbaae0 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 16 Sep 2000 02:06:45 +0000 Subject: [PATCH 0595/2594] 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 1d901282ad..15476ca11b 100644 --- a/command/build.py +++ b/command/build.py @@ -62,12 +62,7 @@ def initialize_options (self): 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 @@ def finalize_options (self): 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 @@ def finalize_options (self): # "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 () From fe2e79b7e47c5f822e28eb0aebf83e652fa9192b Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 16 Sep 2000 15:06:57 +0000 Subject: [PATCH 0596/2594] 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 7182857d2c..04b325a6d2 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 @@ def handle_extra_path (self): # 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 @@ def run (self): # 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 @@ def create_path_file (self): "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 From 2534b87e2eb30b3657de351d70266460069506dd Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 16 Sep 2000 15:09:17 +0000 Subject: [PATCH 0597/2594] 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 474f8f321b..61d234bb4e 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 @@ def run_command (self, 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): From 09dbe7ef0f1498e27bfdeef00ffd238e1c13b62f Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 16 Sep 2000 15:23:28 +0000 Subject: [PATCH 0598/2594] 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 1552dc0c41..b126a99757 100644 --- a/dist.py +++ b/dist.py @@ -733,7 +733,7 @@ def _set_command_options (self, command_obj, option_dict=None): (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 @@ def reinitialize_command (self, command): '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 @@ def reinitialize_command (self, command): 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 From 927edd6665097270bd2930210f37501f692bbe55 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 16 Sep 2000 15:25:55 +0000 Subject: [PATCH 0599/2594] 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 61d234bb4e..7866d1b607 100644 --- a/cmd.py +++ b/cmd.py @@ -316,8 +316,9 @@ def get_finalized_command (self, command, create=1): # 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 From 8db8a763ae8c5c0456a161c93a765483887d9130 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 16 Sep 2000 15:27:17 +0000 Subject: [PATCH 0600/2594] 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 b126a99757..c5fe86ebbf 100644 --- a/dist.py +++ b/dist.py @@ -752,9 +752,6 @@ def reinitialize_command (self, command, reinit_subcommands=0): 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 @@ def reinitialize_command (self, command, reinit_subcommands=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) From ba1539c0c415bebf7bf237cb91e0dc0c007b00bd Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 16 Sep 2000 15:30:47 +0000 Subject: [PATCH 0601/2594] 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 2da9c48795..afeb4ad35f 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -71,12 +71,11 @@ def run (self): 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. From 7275ee4bb9a86ab4d196242acc902697a70caa67 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 16 Sep 2000 15:53:41 +0000 Subject: [PATCH 0602/2594] 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 afeb4ad35f..1fdbf4253b 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 @@ def initialize_options (self): 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 @@ def run (self): 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() From cedc5acb59abb40151af3df16d4e1c7751608c78 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 16 Sep 2000 15:54:18 +0000 Subject: [PATCH 0603/2594] 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 9bddddce24..2afc714670 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 @@ def initialize_options (self): 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 @@ def run (self): 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) From 55c0d1f2782c95996bf76de2418aa62fe28de33a Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 16 Sep 2000 15:56:32 +0000 Subject: [PATCH 0604/2594] 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 2e535d51ec..5030ef9057 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 @@ def run (self): 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() From 8e1b5544e34c5186aa2c8a1c63fd426d2dfae782 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 16 Sep 2000 16:04:59 +0000 Subject: [PATCH 0605/2594] 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 cc0c3985cf..8651e70954 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -90,7 +90,8 @@ def finalize_options (self): # "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 @@ def finalize_options (self): 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() From 41ea88ac7aefa3d488f04485e277ad53e8c023f6 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 16 Sep 2000 18:04:55 +0000 Subject: [PATCH 0606/2594] [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 7b29ef4aa5..f22b3e9167 100644 --- a/text_file.py +++ b/text_file.py @@ -9,18 +9,18 @@ __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 @@ def readline (self): "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 @@ def readline (self): 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 @@ def unreadline (self, line): test_data = """# test file line 3 \\ -continues on next line + continues on next line """ @@ -303,16 +298,21 @@ def unreadline (self, 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 @@ def test_input (count, description, file, expected_result): 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) From c21a9987e9f21985cccaa2e3a244a0c456091692 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 16 Sep 2000 18:06:31 +0000 Subject: [PATCH 0607/2594] [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 f22b3e9167..b731762230 100644 --- a/text_file.py +++ b/text_file.py @@ -134,6 +134,22 @@ def close (self): 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 @@ def warn (self, msg, line=None): 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): From 878b6ad60e8f5e4e163ae13074582e08f4ae4b3b Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 16 Sep 2000 18:09:22 +0000 Subject: [PATCH 0608/2594] 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 b731762230..c983afa425 100644 --- a/text_file.py +++ b/text_file.py @@ -256,7 +256,7 @@ def readline (self): # 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: From 0bd2aefe51af28256934aa5b567efb49d09f2f7a Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 16 Sep 2000 18:33:36 +0000 Subject: [PATCH 0609/2594] 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 c983afa425..37bffe6139 100644 --- a/text_file.py +++ b/text_file.py @@ -201,8 +201,10 @@ def readline (self): 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 @@ def readline (self): 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 @@ def readline (self): 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 @@ def readline (self): # well, I guess there's some actual content there: return it return line - # end readline + # readline () def readlines (self): @@ -298,21 +311,26 @@ def unreadline (self, line): 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 From 7774801f0603d1cb9323ece86720102e31eaac79 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 17 Sep 2000 00:45:18 +0000 Subject: [PATCH 0610/2594] 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 1d3112b652..a63ede233c 100644 --- a/extension.py +++ b/extension.py @@ -7,6 +7,7 @@ __revision__ = "$Id$" +import os, string from types import * @@ -89,9 +90,8 @@ def __init__ (self, name, sources, 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 @@ def __init__ (self, name, sources, 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 () From 0e6eb0bf665aed42a3e42d4319a636b3ca6fa38c Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 17 Sep 2000 00:53:02 +0000 Subject: [PATCH 0611/2594] 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 de35d96a8d..605e95dfb2 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(): From 7ce5bceb8f36d5b1b416bde08b9f161ca4bf51d4 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 17 Sep 2000 00:54:58 +0000 Subject: [PATCH 0612/2594] 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 f880a7a8d5..d578b846ac 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -403,6 +403,10 @@ def build_extensions (self): # 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 @@ def build_extensions (self): 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) From 7d833392261181af33c821afed6dff05ed17b3b0 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 18 Sep 2000 00:41:10 +0000 Subject: [PATCH 0613/2594] 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 6de703ec92..9b9f6064c5 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -332,7 +332,7 @@ def read_template (self): join_lines=1, lstrip_ws=1, rstrip_ws=1, - collapse_ws=1) + collapse_join=1) while 1: line = template.readline() From 3d12a4154867139b39890b8debcf250286f6b5e7 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Tue, 19 Sep 2000 11:10:23 +0000 Subject: [PATCH 0614/2594] 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 5030ef9057..4637d4518c 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -74,6 +74,17 @@ def run (self): 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 @@ def run (self): 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) From 5054d1229b94a8fe66d72b69de38e62955997685 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 19 Sep 2000 23:56:43 +0000 Subject: [PATCH 0615/2594] *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 ae08e7fdcf..ea58a79cd3 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 @@ def __init__ (self, 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 @@ def __init__ (self, 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 @@ def __init__ (self, # -- 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 @@ def compile (self, 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] + From ec1f37f7faad9e09826aa1b6003f006676f0a9ef Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 21 Sep 2000 01:23:35 +0000 Subject: [PATCH 0616/2594] 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 468887127f..b60e39c709 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, \ From d56ce29dfa83433ccaa14e7fc1cd3d732cc08955 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 22 Sep 2000 01:05:43 +0000 Subject: [PATCH 0617/2594] 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 b60e39c709..3f06807854 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 () From ade6e34965cd938919f4177788b89e96c6f16d99 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 22 Sep 2000 01:31:08 +0000 Subject: [PATCH 0618/2594] 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 04b325a6d2..573e0740eb 100644 --- a/command/install.py +++ b/command/install.py @@ -33,19 +33,24 @@ '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 @@ def initialize_options (self): # 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 @@ def finalize_options (self): 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 @@ def finalize_options (self): # 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 @@ def finalize_unix (self): 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 @@ 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: @@ -369,7 +372,7 @@ def finalize_other (self): # Windows and Mac OS for now 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 @@ def expand_dirs (self): '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 @@ def handle_extra_path (self): # 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 @@ def run (self): "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 @@ def create_path_file (self): (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. From 7d6f876b662e94221ed957643e07cacff9352e7b Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 22 Sep 2000 01:32:34 +0000 Subject: [PATCH 0619/2594] 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 4637d4518c..b4c6d9b4b6 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -75,16 +75,11 @@ def run (self): 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 From 3790253376ae5bac233c686320b37e6789fe51f5 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 23 Sep 2000 00:59:34 +0000 Subject: [PATCH 0620/2594] 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 2d0148f3f1..0e85a74b69 100644 --- a/file_util.py +++ b/file_util.py @@ -18,11 +18,11 @@ 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() From 2d5928fb5d508496758ddafa463e5a55712f637c Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 23 Sep 2000 01:10:10 +0000 Subject: [PATCH 0621/2594] 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 2d19e5b96a..dac644ee68 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -34,30 +34,29 @@ 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_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 @@ def run (self): 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 @@ 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_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 @@ def _bytecode_filenames (self, py_filenames): 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 @@ 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()'.""" - + 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 From 5b0913a16d97aa227bbe0bcc2aaf6d39bc502934 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 23 Sep 2000 01:20:19 +0000 Subject: [PATCH 0622/2594] 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 dac644ee68..b104fa9cfc 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -46,31 +46,46 @@ def finalize_options (self): 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 @@ def run (self): 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 @@ def _bytecode_filenames (self, py_filenames): 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 From 16f0821b07cee481a0ed13bf10ce67c589acd618 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 25 Sep 2000 01:23:52 +0000 Subject: [PATCH 0623/2594] 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 c5fe86ebbf..f821e0aa30 100644 --- a/dist.py +++ b/dist.py @@ -15,7 +15,7 @@ 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 @@ def parse_config_files (self, filenames=None): 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 @@ def parse_config_files (self, filenames=None): # 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 @@ def parse_command_line (self): 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 @@ def _set_command_options (self, command_obj, option_dict=None): 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 @@ def _set_command_options (self, command_obj, option_dict=None): 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 From a9f1b90db48367bd932b4d19310d184a2e5cf317 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 25 Sep 2000 01:25:06 +0000 Subject: [PATCH 0624/2594] 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 3f06807854..367985aceb 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` From 0881101deede0e48530b1245c88f396acc1cbf6f Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 25 Sep 2000 01:41:15 +0000 Subject: [PATCH 0625/2594] 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 1fdbf4253b..520098db19 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 2afc714670..d585e8c660 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 b4c6d9b4b6..16dd8022a0 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 15476ca11b..f30f4ee1da 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 450dae1754..775b7ade5a 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 d578b846ac..7fdfd1458d 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 5fcd18e788..ea92c2be0f 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 17fae8f7b5..eacf798996 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 2f3597fdbc..4f04f08be5 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 573e0740eb..4ad652d990 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 af348f5190..9ce118394b 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 5c06d574d6..ec0cf4412d 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 b104fa9cfc..a603b4f59e 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 d506f90f51..b8938c48de 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 9b9f6064c5..ec443a3068 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, From 0dc98e6007d2b9a56fa302fd96b0c043e2e3da05 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 25 Sep 2000 01:51:01 +0000 Subject: [PATCH 0626/2594] 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 ec443a3068..adaf4925f2 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 @@ def initialize_options (self): 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 @@ def prune_file_list (self): 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 @@ def make_distribution (self): 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 @@ def make_distribution (self): 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): From 680d30f3685bcd7d4230598198edcf5bb634d692 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 25 Sep 2000 01:53:01 +0000 Subject: [PATCH 0627/2594] 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 d585e8c660..c293f1f5e4 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 From e0a10947148eeed6a56ef67c5ea170315fbe3c48 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 25 Sep 2000 01:58:07 +0000 Subject: [PATCH 0628/2594] 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 f821e0aa30..0069b5fb4c 100644 --- a/dist.py +++ b/dist.py @@ -14,7 +14,7 @@ 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 @@ def handle_display_options (self, option_order): 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 @@ def _set_command_options (self, command_obj, option_dict=None): 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: From d5e007365aa1d4b127c7eee4826590ca86aeb3b6 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 25 Sep 2000 01:58:31 +0000 Subject: [PATCH 0629/2594] Added 'translate_longopt()' function. --- fancy_getopt.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/fancy_getopt.py b/fancy_getopt.py index eaf6073760..f93520019f 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: From e67685fc403dc1e6cc25b0381aac29230aca571d Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 26 Sep 2000 01:52:25 +0000 Subject: [PATCH 0630/2594] 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 0069b5fb4c..91b820e9b4 100644 --- a/dist.py +++ b/dist.py @@ -118,7 +118,7 @@ def __init__ (self, attrs=None): # 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 @@ def __init__ (self, attrs=None): # 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 @@ def __init__ (self, attrs=None): # 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 @@ def parse_command_line (self): # 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 @@ def _parse_command_opts (self, parser, args): # 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 @@ def _parse_command_opts (self, parser, args): # 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 @@ def _parse_command_opts (self, parser, args): # 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 @@ def _show_help (self, 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 @@ def _show_help (self, 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 @@ def handle_display_options (self, option_order): # 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 @@ def print_command_list (self, commands, header, max_length): 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 @@ def print_commands (self): 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 @@ def announce (self, msg, level=1): 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 @@ def run_command (self, 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): + 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 @@ def is_pure (self): 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" From cb2e23a951132584f35e3f5ab94e42034b4c4e28 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 26 Sep 2000 01:56:15 +0000 Subject: [PATCH 0631/2594] 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 5dfe2db839..2aab7c4dd3 100644 --- a/core.py +++ b/core.py @@ -84,7 +84,7 @@ class found in 'cmdclass' is used in place of the default, which is # 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 @@ class found in 'cmdclass' is used in place of the default, which is # 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 @@ class found in 'cmdclass' is used in place of the default, which is # 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 9339850102..ff0341a5ce 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -91,8 +91,8 @@ def preprocess (self, 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 @@ def preprocess (self, 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 @@ def compile (self, 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 @@ def compile (self, # 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 @@ def create_static_lib (self, 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 @@ def create_static_lib (self, # 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 @@ def link_shared_lib (self, 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 @@ def link_shared_object (self, 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 @@ def link_shared_object (self, 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 @@ def link_executable (self, 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 @@ def library_option (self, 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')) - 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 367985aceb..d52c69b46f 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 8b9ef10670..9d3d172429 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 @@ def parse (self, vstring): 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 @@ def __cmp__ (self, other): 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 @@ def parse (self, vstring): # 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 @@ def __str__ (self): 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 From f5d5642c6b067bcbf6f82020cc1fda18dbb74511 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 26 Sep 2000 02:00:51 +0000 Subject: [PATCH 0632/2594] 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 651124d682..1eed7a8abc 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: From 310173b2ba5b272a6ef87f9057c36381712d42c5 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 26 Sep 2000 02:03:34 +0000 Subject: [PATCH 0633/2594] Whitespace fix. --- file_util.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/file_util.py b/file_util.py index 0e85a74b69..d05b456161 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) From b7ed5395cfb4acb46268fe41f6f8188e2f3da77d Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 26 Sep 2000 02:12:31 +0000 Subject: [PATCH 0634/2594] 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 7866d1b607..ce44829498 100644 --- a/cmd.py +++ b/cmd.py @@ -59,13 +59,13 @@ def __init__ (self, dist): # 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 @@ def __init__ (self, dist): 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 @@ def __getattr__ (self, attr): def ensure_finalized (self): if not self.finalized: - self.finalize_options () + self.finalize_options() self.finalized = 1 @@ -273,7 +273,7 @@ def ensure_dirname (self, option): # -- 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 @@ def set_undefined_options (self, src_cmd, *option_pairs): # 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 @@ def get_finalized_command (self, command, create=1): '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 @@ def run_command (self, 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 @@ def get_sub_commands (self): # -- 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 @@ def copy_tree (self, infile, outfile, 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 @@ def make_file (self, infiles, outfile, func, args, """ 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 @@ def make_file (self, infiles, outfile, func, args, # 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 85f8a18d2c..768cb4ebe7 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 f93520019f..6f8b8c06f1 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -22,14 +22,14 @@ # 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 @@ def add_option (self, long_option, short_option=None, help_string=None): "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 @@ 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) + return string.translate(long_option, longopt_xlate) def _check_alias_dict (self, aliases, what): @@ -137,7 +137,7 @@ def _check_alias_dict (self, aliases, what): 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 @@ def set_negative_aliases (self, negative_alias): '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 @@ def _grok_option_table (self): (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 @@ def _grok_option_table (self): "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 @@ def _grok_option_table (self): # 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 @@ def getopt (self, args=None, object=None): (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 @@ def getopt (self, args=None, object=None): 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 @@ def getopt (self, args=None, object=None): 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 @@ def getopt (self, args=None, object=None): 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 @@ def getopt (self, args=None, object=None): 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 @@ def get_option_order (self): 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 @@ def generate_help (self, header=None): 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 @@ def generate_help (self, header=None): 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 @@ def generate_help (self, header=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") - # 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 @@ def __init__ (self, options=[]): 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 84f36d2c4b..211b65f8d2 100644 --- a/filelist.py +++ b/filelist.py @@ -55,7 +55,7 @@ def findall (self, dir=os.curdir): # -- 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 @@ def sort (self): 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 @@ def remove_duplicates (self): # -- "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 @@ def _parse_template_line (self, line): 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 @@ def process_template_line (self, line): 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 @@ def process_template_line (self, line): 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 @@ def process_template_line (self, line): 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 @@ def process_template_line (self, line): 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 @@ def process_template_line (self, line): # -- 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 @@ def include_pattern (self, pattern, 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 @@ def include_pattern (self, pattern, 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 @@ def exclude_pattern (self, pattern, 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 () From 208764c9d80a704b06463dbadf392709af561b57 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 26 Sep 2000 02:13:49 +0000 Subject: [PATCH 0635/2594] 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 26cd7fb2ec..61bc25ea0f 100644 --- a/archive_util.py +++ b/archive_util.py @@ -15,13 +15,13 @@ 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 97812edaf5..4b93ed023c 100644 --- a/dep_util.py +++ b/dep_util.py @@ -14,14 +14,13 @@ 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 From 60381acee5ec5b115938eff6e66cb4277ec2a84d Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 26 Sep 2000 02:51:09 +0000 Subject: [PATCH 0636/2594] 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 34f55a2fd7..b2b9b569a7 100644 --- a/__init__.py +++ b/__init__.py @@ -10,4 +10,4 @@ __revision__ = "$Id$" -__version__ = "0.9.3pre" +__version__ = "0.9.3" From f2886a0174116e2d8dfd3a5275de79233b5481b4 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 27 Sep 2000 00:15:37 +0000 Subject: [PATCH 0637/2594] 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 91b820e9b4..92d390f777 100644 --- a/dist.py +++ b/dist.py @@ -755,9 +755,10 @@ def _set_command_options (self, command_obj, option_dict=None): 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) From 3baf36cad7c938199bb27b95d25bcb5db2bb6cae Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 27 Sep 2000 00:17:08 +0000 Subject: [PATCH 0638/2594] 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 b2b9b569a7..6113dcf8c3 100644 --- a/__init__.py +++ b/__init__.py @@ -10,4 +10,4 @@ __revision__ = "$Id$" -__version__ = "0.9.3" +__version__ = "0.9.4" From 5937fb6402fd3826b37192fc61b0869f7c3e441d Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 27 Sep 2000 02:08:14 +0000 Subject: [PATCH 0639/2594] 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 2b73b12f0d..bb9557fbb3 100644 --- a/bcppcompiler.py +++ b/bcppcompiler.py @@ -63,7 +63,7 @@ def __init__ (self, # 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 @@ def __init__ (self, 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 @@ def compile (self, 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 @@ def create_static_lib (self, # 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 @@ def link_shared_object (self, 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 @@ def link_shared_object (self, # 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 @@ def link_shared_object (self, 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 @@ def link_shared_object (self, 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 @@ def find_library_file (self, dirs, lib, debug=0): # 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 ce3f2be69d..97949060fb 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -561,24 +561,32 @@ def create_static_lib (self, 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 @@ def link_shared_lib (self, 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 @@ def link_shared_object (self, 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 @@ def link_executable (self, 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 @@ def shared_object_filename (self, 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 f547d540f5..5b06d3d79c 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -39,14 +39,17 @@ # 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 @@ def __init__ (self, # 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 @@ def __init__ (self, 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 @@ def __init__ (self, # __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 @@ def link_shared_object (self, 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 @@ def __init__ (self, 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 ea58a79cd3..0325b48508 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -205,7 +205,7 @@ def __init__ (self, 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 @@ def __init__ (self, 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 @@ def create_static_lib (self, # 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 @@ def link_shared_object (self, 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 @@ def link_shared_object (self, # 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 @@ def link_shared_object (self, 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 ff0341a5ce..f7eb93ae43 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -190,45 +190,19 @@ def create_static_lib (self, # 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 @@ def link_shared_object (self, 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 ----------------------------------------- From a50a92cb127350ef11b3bf567d9724e61ce063af Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 27 Sep 2000 02:24:21 +0000 Subject: [PATCH 0640/2594] 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 97949060fb..b10ee67e3c 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -621,7 +621,7 @@ def link (self, 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 @@ def link_shared_lib (self, 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 @@ def link_shared_object (self, 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 @@ def link_executable (self, 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 ----------------------------------------- From 117c5cbfe52d2dfc01e823e96051f24f80f60038 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 27 Sep 2000 02:26:57 +0000 Subject: [PATCH 0641/2594] Bump version to 1.0pre. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 6113dcf8c3..23315cdeb9 100644 --- a/__init__.py +++ b/__init__.py @@ -10,4 +10,4 @@ __revision__ = "$Id$" -__version__ = "0.9.4" +__version__ = "1.0pre" From b0ce6a9888ab8fc85326e696c0e114dddfdeaeaa Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Thu, 28 Sep 2000 19:28:35 +0000 Subject: [PATCH 0642/2594] 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 7fdfd1458d..9147c3d07b 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -158,7 +158,6 @@ def finalize_options (self): # 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 @@ def get_ext_filename (self, ext_name): 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 @@ def get_libraries (self, ext): # 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] From 96979143f471579f54c80afc1aedf0831a98e58a Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Fri, 29 Sep 2000 11:36:55 +0000 Subject: [PATCH 0643/2594] 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 16dd8022a0..3251bac07c 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 @@ def run (self): 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 @@ def run (self): "%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 @@ def get_inidata (self): 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 @@ def get_exe_bytes (self): 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 @@ def get_exe_bytes (self): AAAAAAAAAAAAAAAAAAAAOuIAAEjiAABY4gAAAAAAAGbiAAAAAAAAdOIAAAAAAACE4gAAAAAAAI7i AAAAAAAAlOIAAAAAAABLRVJORUwzMi5ETEwAQURWQVBJMzIuZGxsAENPTUNUTDMyLmRsbABHREkz Mi5kbGwATVNWQ1JULmRsbABVU0VSMzIuZGxsAABMb2FkTGlicmFyeUEAAEdldFByb2NBZGRyZXNz -AABFeGl0UHJvY2VzcwAAAFJlZ0Nsb3NlS2V5AAAAUHJvcGVydHlTaGVldEEAAFRleHRPdXRBAABm -cmVlAABFbmRQYWludAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AABFeGl0UHJvY2VzcwAAAFJlZ0Nsb3NlS2V5AAAAUHJvcGVydHlTaGVldEEAAFRleHRPdXRBAABl +eGl0AABFbmRQYWludAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA From 2ab90e20a50ed93e808c40772d010c8427c283c6 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 30 Sep 2000 17:05:37 +0000 Subject: [PATCH 0644/2594] 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 4ad652d990..8c6aa1cff8 100644 --- a/command/install.py +++ b/command/install.py @@ -173,6 +173,13 @@ def initialize_options (self): 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 @@ def change_roots (self, *names): 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 @@ def run (self): # 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 @@ def get_outputs (self): return outputs - def get_inputs (self): # XXX gee, this looks familiar ;-( inputs = [] @@ -526,15 +528,23 @@ def get_inputs (self): 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. From 157e91886f9d060d88fc35c34e2f5239d43c6af3 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 30 Sep 2000 17:08:12 +0000 Subject: [PATCH 0645/2594] 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 f30f4ee1da..1f785996ec 100644 --- a/command/build.py +++ b/command/build.py @@ -97,26 +97,34 @@ def finalize_options (self): 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 From 939159b5a3ded23234a997e930543af87d19c524 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 30 Sep 2000 17:09:39 +0000 Subject: [PATCH 0646/2594] 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 d52c69b46f..016119ce66 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 From 3e90f681ce2c2f2bfb9b5015e78a0f207135997a Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 30 Sep 2000 17:29:35 +0000 Subject: [PATCH 0647/2594] 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 d05b456161..a9e12ce3f3 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 () From 4e493b610cddc4325efb115e2ebf61d2aaba8068 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 30 Sep 2000 17:33:05 +0000 Subject: [PATCH 0648/2594] 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 ea92c2be0f..ebe30e8106 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -307,7 +307,7 @@ def build_module (self, module, module_file, package): 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): From 5b94288789b79b3b730f5eb2d40eb5e617f93f2f Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 30 Sep 2000 17:34:50 +0000 Subject: [PATCH 0649/2594] 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 9ce118394b..d9a486936c 100644 --- a/command/install_data.py +++ b/command/install_data.py @@ -52,7 +52,7 @@ def run (self): 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 @@ def run (self): 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 ec0cf4412d..2d72a07b14 100644 --- a/command/install_headers.py +++ b/command/install_headers.py @@ -41,7 +41,7 @@ def run (self): 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): From 28c8c4911fe821cda508e839dbeca6781e9dd924 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 30 Sep 2000 17:35:26 +0000 Subject: [PATCH 0650/2594] 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 a603b4f59e..2c92f3fe4a 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -56,7 +56,8 @@ def run (self): outfiles = self.install() # (Optionally) compile .py to .pyc - self.bytecompile(outfiles) + if outfiles is not None: + self.bytecompile(outfiles) # run () From 48647d7afb29872232fd752601c017a428c47233 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 30 Sep 2000 17:47:17 +0000 Subject: [PATCH 0651/2594] 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 768cb4ebe7..a1578bed6a 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( From cade95e31186a5f2c4119d538e60ea58a44fa920 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 30 Sep 2000 18:27:54 +0000 Subject: [PATCH 0652/2594] 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 8651e70954..c0cb1d3acd 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -132,7 +132,7 @@ def run (self): # 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 520098db19..8dfc3271df 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -71,24 +71,24 @@ def finalize_options (self): 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 c293f1f5e4..f421590488 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -258,7 +258,7 @@ def run (self): # 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 3251bac07c..afe6955e77 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -56,8 +56,8 @@ def finalize_options (self): 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 @@ def run (self): ("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 @@ def run (self): 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 @@ def run (self): # 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 @@ def get_inidata (self): # 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 @@ def create_exe (self, arcname, fullname): 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 @@ def finalize_options (self): # '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 775b7ade5a..2726b975fa 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -80,22 +80,22 @@ def finalize_options (self): # 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 @@ def run (self): # 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 @@ def check_library_list (self, libraries): # 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 @@ def check_library_list (self, libraries): "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 @@ def get_library_names (self): 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 @@ 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): + 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 9147c3d07b..70e8a73455 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -112,12 +112,12 @@ def initialize_options (self): 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 @@ def finalize_options (self): 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 @@ def finalize_options (self): # 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 @@ def run (self): # 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 @@ def run (self): # 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 @@ def get_source_files (self): # 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 @@ 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) + 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 @@ def get_outputs (self): 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 @@ def build_extensions (self): 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 @@ def swig_sources (self, sources): 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 @@ def find_swig (self): # 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 @@ def get_ext_filename (self, ext_name): """ 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 ebe30e8106..6a8a7f43f9 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -34,9 +34,9 @@ def initialize_options (self): 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 @@ def run (self): # 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 @@ def get_package_dir (self, package): 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 @@ def get_package_dir (self, package): tail.insert(0, pdir) if tail: - return apply (os.path.join, tail) + return apply(os.path.join, tail) else: return '' @@ -140,22 +140,22 @@ def check_package (self, package, package_dir): # 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 @@ def check_package (self, package, package_dir): 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 @@ def check_module (self, module, module_file): 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 @@ def find_modules (self): # - 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 @@ def find_modules (self): # 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 @@ def find_all_modules (self): '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 @@ def find_all_modules (self): 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 @@ def build_modules (self): # 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 @@ def build_packages (self): # 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 eacf798996..495f4c372a 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -32,9 +32,9 @@ def initialize_options (self): 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 4f04f08be5..fb8822f766 100644 --- a/command/clean.py +++ b/command/clean.py @@ -50,29 +50,29 @@ def finalize_options(self): 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 5c3f26a716..a13055cf8c 100644 --- a/command/config.py +++ b/command/config.py @@ -87,10 +87,10 @@ def _check_compiler (self): # 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 8c6aa1cff8..e9528c635d 100644 --- a/command/install.py +++ b/command/install.py @@ -210,10 +210,10 @@ def finalize_options (self): "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 @@ def finalize_options (self): # 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 @@ def finalize_options (self): '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 @@ def finalize_options (self): 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 @@ def finalize_options (self): # 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 @@ def finalize_options (self): 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 @@ def dump_dirs (self, msg): 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 @@ def finalize_unix (self): 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 @@ def finalize_unix (self): 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 @@ def finalize_unix (self): 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 @@ def select_scheme (self, name): 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 @@ def handle_extra_path (self): 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 @@ def handle_extra_path (self): # 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 @@ def run (self): # 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 @@ def run (self): # 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 @@ def get_outputs (self): # 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 @@ def get_inputs (self): # 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 b8938c48de..3bc23e7460 100644 --- a/command/install_scripts.py +++ b/command/install_scripts.py @@ -33,11 +33,11 @@ def initialize_options (self): 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 adaf4925f2..5116868e76 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -118,7 +118,7 @@ def finalize_options (self): "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 @@ def run (self): # 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 @@ def run (self): # 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 @@ def check_metadata (self): 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 @@ def add_defaults (self): 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 @@ def read_template (self): 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 @@ def read_manifest (self): 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 @@ def make_release_tree (self, base_dir, files): # 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 @@ def make_release_tree (self, base_dir, files): 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 @@ def make_distribution (self): 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 From e125b16ff055a9b864f749cc48dcc94e8191a123 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 30 Sep 2000 18:40:42 +0000 Subject: [PATCH 0653/2594] Reformat docstrings. --- util.py | 47 +++++++++++++++++++++++------------------------ 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/util.py b/util.py index 016119ce66..4f972ffd91 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) From 2cefa8512551672a267241aa0c7c783c272325e0 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 30 Sep 2000 18:49:14 +0000 Subject: [PATCH 0654/2594] 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 4f972ffd91..1a1ec6d858 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 (match, local_vars=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 () From 016c9e3595524f13467fe2192e5509f9469725a6 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 30 Sep 2000 20:37:56 +0000 Subject: [PATCH 0655/2594] 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 1a1ec6d858..03234847a6 100644 --- a/util.py +++ b/util.py @@ -8,8 +8,9 @@ __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) From c2c1bbc37b9dea94262111b0d050c6d6e9fd024e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 30 Sep 2000 20:39:09 +0000 Subject: [PATCH 0656/2594] 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 2c92f3fe4a..6ad0a54b32 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -5,6 +5,7 @@ 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 @@ def install (self): 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 ----------------------------------------------- From 9a39df987d712514e23769e43651dd72768e0289 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 1 Oct 2000 23:49:30 +0000 Subject: [PATCH 0657/2594] 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 03234847a6..563e47c4bd 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 @@ def byte_compile (py_files, 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 From 1e229b65b2fd026127f9507e1a5eb2bf5716b6d3 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 1 Oct 2000 23:50:13 +0000 Subject: [PATCH 0658/2594] 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 6ad0a54b32..2396eedad3 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -57,7 +57,7 @@ def run (self): 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 () From 0c6e44515410c371cbb4f4d3589f8554e7f217ab Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 2 Oct 2000 02:09:55 +0000 Subject: [PATCH 0659/2594] 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 563e47c4bd..8a8b77afef 100644 --- a/util.py +++ b/util.py @@ -369,6 +369,7 @@ def byte_compile (py_files, 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 From af0b04bbeb4b4811fcf2892856cfc4154841bda7 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 2 Oct 2000 02:15:08 +0000 Subject: [PATCH 0660/2594] 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 2396eedad3..80da3acc86 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 @@ def initialize_options (self): 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 @@ def finalize_options (self): ('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 @@ def run (self): # (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 @@ def install (self): 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 @@ def _mutate_outputs (self, has_any, build_cmd, cmd_option, output_dir): 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 From 6bccb12b568be281d7e54b5ef3c5ff67ca6163bc Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 2 Oct 2000 02:16:04 +0000 Subject: [PATCH 0661/2594] 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 e9528c635d..303ae4c02d 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 @@ def initialize_options (self): 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 From 7acf599bd33799d9e5b000b8d7e233a13464211d Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 2 Oct 2000 02:19:04 +0000 Subject: [PATCH 0662/2594] 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 6a8a7f43f9..6fd4417f48 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 @@ def initialize_options (self): 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 @@ def finalize_options (self): 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 @@ def run (self): else: self.build_packages() + self.byte_compile(self.get_outputs(include_bytecode=0)) + # run () @@ -284,13 +302,19 @@ def get_module_outfile (self, build_dir, package, module): 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 @@ def build_packages (self): 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 From d2497ea9a9cfcf3a2869d5251437cfea6ff473cc Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Mon, 2 Oct 2000 02:25:51 +0000 Subject: [PATCH 0663/2594] 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 80da3acc86..804dcffa60 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -162,7 +162,7 @@ def _bytecode_filenames (self, py_filenames): 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 From 94052cd86e69fe242219496b66427f1e429994df Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 3 Oct 2000 03:31:05 +0000 Subject: [PATCH 0664/2594] 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 8a8b77afef..7522ee2499 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 @@ def byte_compile (py_files, 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 From 5e53de4c1f29689ed46a3edcedab0594b2ab9b7e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 3 Oct 2000 03:31:52 +0000 Subject: [PATCH 0665/2594] 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 303ae4c02d..330f324867 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): From 7bfd1e33100e73e990f07bffe02e06f5f8cd2e60 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 3 Oct 2000 03:32:37 +0000 Subject: [PATCH 0666/2594] 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 804dcffa60..6c4c7fe993 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -71,8 +71,6 @@ def finalize_options (self): 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) From f07662ad4804864ca561414b1aeab71901df8c64 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Tue, 3 Oct 2000 03:48:43 +0000 Subject: [PATCH 0667/2594] Bump version to 1.0. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 23315cdeb9..2073617627 100644 --- a/__init__.py +++ b/__init__.py @@ -10,4 +10,4 @@ __revision__ = "$Id$" -__version__ = "1.0pre" +__version__ = "1.0" From aa3edfa3ec76a4cc13fca1dee25fda6f0baeb771 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Thu, 12 Oct 2000 19:31:13 +0000 Subject: [PATCH 0668/2594] 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 afe6955e77..b45089b724 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -218,9 +218,9 @@ def get_exe_bytes (self): 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 @@ def get_exe_bytes (self): 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 From 3440959467b0aa5fa7f21378e1657a9bbc047b67 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 14 Oct 2000 03:40:20 +0000 Subject: [PATCH 0669/2594] 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 a13055cf8c..18d7992abe 100644 --- a/command/config.py +++ b/command/config.py @@ -14,6 +14,7 @@ __revision__ = "$Id$" import sys, os, string, re +from types import * from distutils.core import Command from distutils.errors import DistutilsExecError @@ -69,7 +70,21 @@ def initialize_options (self): 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 From 806c2d4c0c20efab95c6519462cc891f93d96c0c Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 14 Oct 2000 03:47:07 +0000 Subject: [PATCH 0670/2594] 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 330f324867..012b9f4577 100644 --- a/command/install.py +++ b/command/install.py @@ -497,10 +497,12 @@ def run (self): "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") % From 89b5ec9d38d646bd3b9e1c7f746f3ba1801315fb Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 14 Oct 2000 03:56:42 +0000 Subject: [PATCH 0671/2594] 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 18d7992abe..ce6cff8cef 100644 --- a/command/config.py +++ b/command/config.py @@ -325,16 +325,19 @@ def check_func (self, func, # 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"): From 760b926a79252cf94800f28d378990773ed82bde Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 14 Oct 2000 04:06:40 +0000 Subject: [PATCH 0672/2594] 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 c0cb1d3acd..a75303e689 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 0fed6b489e..cf35b45d01 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 2726b975fa..063da91552 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 fb8822f766..b4a9be45f8 100644 --- a/command/clean.py +++ b/command/clean.py @@ -60,7 +60,7 @@ def run(self): # 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 012b9f4577..6aee1b35d9 100644 --- a/command/install.py +++ b/command/install.py @@ -498,7 +498,7 @@ def run (self): 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 d9a486936c..dba108af14 100644 --- a/command/install_data.py +++ b/command/install_data.py @@ -38,10 +38,10 @@ def initialize_options (self): 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 5116868e76..1f9e9184d7 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 92d390f777..abbc160067 100644 --- a/dist.py +++ b/dist.py @@ -284,7 +284,7 @@ def find_config_files (self): 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 @@ def _parse_command_opts (self, parser, args): 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 @@ def _parse_command_opts (self, parser, args): 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 @@ def _show_help (self, return # _show_help () - + def handle_display_options (self, option_order): """If there were any non-global "display-only" options From ac94b8701f445c75e671f29ed64ea3b577a9b3a5 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 14 Oct 2000 04:07:39 +0000 Subject: [PATCH 0673/2594] Removed debugging code at bottom. --- util.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/util.py b/util.py index 7522ee2499..dd9de854f9 100644 --- a/util.py +++ b/util.py @@ -427,11 +427,3 @@ def byte_compile (py_files, (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) From dc35143fa2af5382aa1428c9da4b140237bf9391 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sun, 15 Oct 2000 19:20:20 +0000 Subject: [PATCH 0674/2594] 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 2073617627..55951090ff 100644 --- a/__init__.py +++ b/__init__.py @@ -10,4 +10,4 @@ __revision__ = "$Id$" -__version__ = "1.0" +__version__ = "1.0.1" From f64d6b6db2fa6882b19b75d53156c4a541b53588 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Sat, 11 Nov 2000 02:47:11 +0000 Subject: [PATCH 0675/2594] 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 abbc160067..fe728c3b60 100644 --- a/dist.py +++ b/dist.py @@ -172,6 +172,12 @@ def __init__ (self, attrs=None): # 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 @@ def print_commands (self): # 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 ---------------------------------- From 41f2923e4cb8e646dd04aea546f43752850c091b Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Tue, 12 Dec 2000 23:11:42 +0000 Subject: [PATCH 0676/2594] 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 5b06d3d79c..f40d1a2d4a 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -241,7 +241,7 @@ def link (self, 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 From f749040280c9b9f4151fc1852e7d3ec524d2a1b4 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Wed, 20 Dec 2000 00:48:12 +0000 Subject: [PATCH 0677/2594] 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 6aee1b35d9..806da01afa 100644 --- a/command/install.py +++ b/command/install.py @@ -146,6 +146,7 @@ def initialize_options (self): self.install_data = None self.compile = None + self.no_compile = None self.optimize = None # These two are for putting non-packagized distributions into their From aa73c5a0fd86980d14bb7bfc07fb41189c2f58cb Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Tue, 9 Jan 2001 03:15:47 +0000 Subject: [PATCH 0678/2594] 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 70e8a73455..936a5f633a 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -149,6 +149,8 @@ def finalize_options (self): 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 = [] From abec99bc72e4002a5348f4a6822f7891bb4f4589 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 11 Jan 2001 15:35:16 +0000 Subject: [PATCH 0679/2594] Delete unused import of pprint module --- sysconfig.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sysconfig.py b/sysconfig.py index 605e95dfb2..d57b915911 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() From 12123de064e383654dafdd825e35948ef01c2825 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Mon, 15 Jan 2001 16:09:35 +0000 Subject: [PATCH 0680/2594] 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 b10ee67e3c..53901b3362 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -838,6 +838,7 @@ def mkpath (self, name, mode=0777): # 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 @@ def mkpath (self, name, mode=0777): "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 fe728c3b60..41d5dbbc0b 100644 --- a/dist.py +++ b/dist.py @@ -172,12 +172,6 @@ def __init__ (self, attrs=None): # 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 @@ def parse_command_line (self): 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 0000000000..2edc8259bf --- /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 + + From cacfb4f1472c9b83df8d0b1799834f9f5e322e47 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Tue, 16 Jan 2001 03:10:43 +0000 Subject: [PATCH 0681/2594] 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 53901b3362..5e0b328042 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -366,6 +366,7 @@ def _prep_compile (self, sources, output_dir): """ # Get the list of expected output (object) files objects = self.object_filenames (sources, + strip_dir=1, output_dir=output_dir) if self.force: From 3f4a8e719b6611b77347bb1f8970b5a0141bd400 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Tue, 16 Jan 2001 16:16:03 +0000 Subject: [PATCH 0682/2594] 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 806da01afa..40e00705d3 100644 --- a/command/install.py +++ b/command/install.py @@ -530,7 +530,11 @@ def get_outputs (self): 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 From e26cdf35269dc6e049dd19c5b39bd312cddeb81a Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Tue, 16 Jan 2001 16:33:28 +0000 Subject: [PATCH 0683/2594] 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 d57b915911..4da376898c 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 From 6aef8efc56535f695fdbbddcb651bf715bfbc79e Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Wed, 17 Jan 2001 15:16:52 +0000 Subject: [PATCH 0684/2594] 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 4da376898c..3ae0a5f91d 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -19,6 +19,19 @@ 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") From dd5ccc0082a1bf655dd242d3881620cd88f0d980 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Fri, 19 Jan 2001 16:26:12 +0000 Subject: [PATCH 0685/2594] 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 dd9de854f9..80e4814389 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) From 86f47bdacaf41dd4535e115c74162bfcfb87f267 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Wed, 24 Jan 2001 15:43:09 +0000 Subject: [PATCH 0686/2594] 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 936a5f633a..2876c3593f 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -163,6 +163,17 @@ def finalize_options (self): 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 @@ def get_libraries (self, ext): # 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 From 11b83157df263f7aa40e1d42536200ee07a34b32 Mon Sep 17 00:00:00 2001 From: Neil Schemenauer Date: Wed, 24 Jan 2001 17:17:20 +0000 Subject: [PATCH 0687/2594] 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 3ae0a5f91d..ae9b37f3a1 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") From 5634233ca85a0f27b1feca9ccf899b688c276cbf Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Thu, 25 Jan 2001 20:10:32 +0000 Subject: [PATCH 0688/2594] 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 80e4814389..550d94abb6 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 (match, local_vars=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 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: [PATCH 0689/2594] 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 2876c3593f..8f59523935 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -356,104 +356,105 @@ def get_outputs (self): # 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): From a2946a75cbbeb464dbe57bba2a67327381da73ac Mon Sep 17 00:00:00 2001 From: Jack Jansen Date: Sun, 28 Jan 2001 12:22:14 +0000 Subject: [PATCH 0690/2594] 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 dba108af14..503c1aa8ac 100644 --- a/command/install_data.py +++ b/command/install_data.py @@ -10,7 +10,7 @@ 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 @@ def run (self): 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 @@ def run (self): 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) From d14a7b0d085ee7397a87141e7cd2ea0503def6b8 Mon Sep 17 00:00:00 2001 From: Jack Jansen Date: Sun, 28 Jan 2001 12:23:32 +0000 Subject: [PATCH 0691/2594] 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 550d94abb6..d4891b3c2a 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 () From 2b9227d208d3f9196b66758a601352a58ae4253f Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Wed, 31 Jan 2001 20:07:17 +0000 Subject: [PATCH 0692/2594] 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 a9e12ce3f3..39f6eea289 100644 --- a/file_util.py +++ b/file_util.py @@ -8,6 +8,7 @@ __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): From b0762d40180e6930b7f5fa403e7c1084c1f598da Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Mon, 5 Feb 2001 17:43:11 +0000 Subject: [PATCH 0693/2594] 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 503c1aa8ac..28f593866c 100644 --- a/command/install_data.py +++ b/command/install_data.py @@ -64,7 +64,7 @@ def run (self): 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) From 6b6d7c364eec3b44b99f1f1b5a41be2920183783 Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Fri, 9 Feb 2001 11:14:08 +0000 Subject: [PATCH 0694/2594] 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 bb9557fbb3..00ccec548b 100644 --- a/bcppcompiler.py +++ b/bcppcompiler.py @@ -14,7 +14,7 @@ __revision__ = "$Id$" -import sys, os, string +import sys, os from distutils.errors import \ DistutilsExecError, DistutilsPlatformError, \ CompileError, LibError, LinkError From 877b037a3e40bfb3285be20db1d161ca44b1e8db Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Fri, 9 Feb 2001 11:51:27 +0000 Subject: [PATCH 0695/2594] String method conversion. --- cmd.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd.py b/cmd.py index ce44829498..cec4bff237 100644 --- a/cmd.py +++ b/cmd.py @@ -9,7 +9,7 @@ __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 @@ def dump_options (self, header=None, indent=""): 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 @@ def make_file (self, infiles, outfile, func, args, """ 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 From 31bfd423882c406175d12a99256a75411487bb93 Mon Sep 17 00:00:00 2001 From: "Eric S. Raymond" Date: Fri, 9 Feb 2001 12:20:51 +0000 Subject: [PATCH 0696/2594] 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 f40d1a2d4a..42318ad3d4 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 a63ede233c..f49abad003 100644 --- a/extension.py +++ b/extension.py @@ -7,7 +7,7 @@ __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 9d3d172429..2916eb79a1 100644 --- a/version.py +++ b/version.py @@ -112,12 +112,12 @@ def parse (self, vstring): 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 @@ def parse (self, vstring): 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]) From e5fb9e92092d08b97107b3ae88108bc404a00bdb Mon Sep 17 00:00:00 2001 From: Neil Schemenauer Date: Fri, 16 Feb 2001 03:31:13 +0000 Subject: [PATCH 0697/2594] 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 ae9b37f3a1..2b0bce33e9 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 From abba167c662eed5cea8b58a24df09483eac2fd96 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Sat, 17 Feb 2001 04:48:41 +0000 Subject: [PATCH 0698/2594] 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 8f59523935..14cfebf720 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -151,8 +151,11 @@ def finalize_options (self): 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. 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: [PATCH 0699/2594] 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 5e0b328042..0a30640fe2 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -7,7 +7,7 @@ __revision__ = "$Id$" -import sys, os +import sys, os, re from types import * from copy import copy from distutils.errors import * @@ -835,12 +835,44 @@ def mkpath (self, name, mode=0777): # 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: From a8b3af8796b6d404864ae381ffb958e46632d836 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Mon, 19 Feb 2001 09:20:30 +0000 Subject: [PATCH 0700/2594] 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 b45089b724..e3964b6b4b 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 @@ def initialize_options (self): 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 @@ def run (self): ("'%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 @@ def get_inidata (self): 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 @@ def get_inidata (self): # 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 @@ def create_exe (self, arcname, fullname): "%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 0701/2594] 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 d4891b3c2a..009ba6cb93 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) From e49a11577577efa7493fe079aec5b257a1796d00 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Tue, 27 Feb 2001 19:13:15 +0000 Subject: [PATCH 0702/2594] 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 0a30640fe2..4a282d4cff 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -842,7 +842,10 @@ def mkpath (self, name, mode=0777): _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'), From 9a2d4238fc67219b220c86cdea11776e83cf8371 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Tue, 27 Feb 2001 19:25:42 +0000 Subject: [PATCH 0703/2594] 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 009ba6cb93..e596150422 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. From a491120a1f3f62675249b45d01c90e690c13000f Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Wed, 28 Feb 2001 19:40:27 +0000 Subject: [PATCH 0704/2594] Placate tabnanny --- sysconfig.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sysconfig.py b/sysconfig.py index 2b0bce33e9..91f9279d87 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 From fde8019f9e24a1b85b69bbdddc0073996df538aa Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Wed, 28 Feb 2001 20:59:33 +0000 Subject: [PATCH 0705/2594] 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 495f4c372a..1f68899ce9 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -10,8 +10,10 @@ 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): From 925f20a7baabbecc1a696330747546e2f2d8e2a3 Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Fri, 2 Mar 2001 07:28:03 +0000 Subject: [PATCH 0706/2594] 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 1f68899ce9..ee7f4e2d1e 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -45,7 +45,7 @@ def run (self): 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 @@ def copy_scripts (self): 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 -- From baf6ba89cd2afee636c0590384219bf555f3b02f Mon Sep 17 00:00:00 2001 From: Ka-Ping Yee Date: Sat, 10 Mar 2001 09:33:14 +0000 Subject: [PATCH 0707/2594] 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 14cfebf720..866697577d 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -545,7 +545,7 @@ def get_ext_fullname (self, ext_name): 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"). """ From c30a37b5c788097c52695b55e76916e109d32ac4 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Fri, 16 Mar 2001 20:57:37 +0000 Subject: [PATCH 0708/2594] 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 e3964b6b4b..f1dd633297 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 @@ def get_exe_bytes (self): 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 --- From 517c0d40da7055c00cdc68e76207ae86a72756bf Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Fri, 16 Mar 2001 21:00:18 +0000 Subject: [PATCH 0709/2594] 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 55951090ff..bd9761cc25 100644 --- a/__init__.py +++ b/__init__.py @@ -10,4 +10,4 @@ __revision__ = "$Id$" -__version__ = "1.0.1" +__version__ = "1.0.2pre" From 59c4620e1fd084da8d9067c4eeadf763feb4760d Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Sat, 17 Mar 2001 19:59:26 +0000 Subject: [PATCH 0710/2594] 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 41d5dbbc0b..6bda869e2e 100644 --- a/dist.py +++ b/dist.py @@ -206,6 +206,10 @@ def __init__ (self, attrs=None): raise DistutilsSetupError, \ "invalid distribution option '%s'" % key + if self.metadata.version is None: + raise DistutilsSetupError, \ + "No version number specified for distribution" + # __init__ () From d23ca0098c0ef28b79e4833fcf6ed85fae2aa11a Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Sat, 17 Mar 2001 20:15:41 +0000 Subject: [PATCH 0711/2594] 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 866697577d..f732373ea3 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -177,6 +177,21 @@ def finalize_options (self): # 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 () From 61fe3191e144fa397832ea37932156cdf904320c Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 22 Mar 2001 03:03:41 +0000 Subject: [PATCH 0712/2594] Patch #407434: add rfc822_escape utility function --- util.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/util.py b/util.py index e596150422..010db9a3b0 100644 --- a/util.py +++ b/util.py @@ -443,3 +443,13 @@ def byte_compile (py_files, (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 + + From c5fc89cfdabbf37241d499f96c4c1bf08973b436 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 22 Mar 2001 03:06:52 +0000 Subject: [PATCH 0713/2594] 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 6bda869e2e..23466965ad 100644 --- a/dist.py +++ b/dist.py @@ -15,7 +15,7 @@ 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 @@ def __init__ (self, attrs=None): 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 @@ def _parse_command_opts (self, parser, 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 + 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 @@ def handle_display_options (self, option_order): 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 @@ def __init__ (self): 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 @@ def get_description(self): 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 From adced399695befb6c925c42e5ccae882a1f4924f Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 22 Mar 2001 03:10:05 +0000 Subject: [PATCH 0714/2594] 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 1f9e9184d7..894d7d44c5 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 @@ def make_release_tree (self, base_dir, files): 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 From 047c580a97cb9ef9101c572e241cb5f33773c502 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 22 Mar 2001 03:48:31 +0000 Subject: [PATCH 0715/2594] 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 cec4bff237..ce44829498 100644 --- a/cmd.py +++ b/cmd.py @@ -9,7 +9,7 @@ __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 @@ def dump_options (self, header=None, indent=""): 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 @@ def make_file (self, infiles, outfile, func, args, """ 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 42318ad3d4..f40d1a2d4a 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 f49abad003..a63ede233c 100644 --- a/extension.py +++ b/extension.py @@ -7,7 +7,7 @@ __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 2916eb79a1..9d3d172429 100644 --- a/version.py +++ b/version.py @@ -112,12 +112,12 @@ def parse (self, vstring): 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 @@ def parse (self, vstring): 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]) From 7e76bc2645746985a3025843e7476b540c8c7316 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 22 Mar 2001 03:50:09 +0000 Subject: [PATCH 0716/2594] Remove redundant import --- cygwinccompiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cygwinccompiler.py b/cygwinccompiler.py index f40d1a2d4a..92def164ad 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: From 52e9a5d7a9f246c15dfefe605c6a4e37a706001d Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 22 Mar 2001 15:32:23 +0000 Subject: [PATCH 0717/2594] 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 23466965ad..7ef3a42b76 100644 --- a/dist.py +++ b/dist.py @@ -992,8 +992,8 @@ def write_pkg_info (self, base_dir): 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() ) From 9f790bc4fe4815ba2d112430a83d527a8b13f8b0 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Fri, 23 Mar 2001 17:30:26 +0000 Subject: [PATCH 0718/2594] 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 010db9a3b0..01abd346d5 100644 --- a/util.py +++ b/util.py @@ -446,10 +446,11 @@ def byte_compile (py_files, 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 From 3d12c8d570fc54ac95ec9d25869b79b5b862c24a Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Sat, 31 Mar 2001 02:41:01 +0000 Subject: [PATCH 0719/2594] 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 7ef3a42b76..1ac9786d8d 100644 --- a/dist.py +++ b/dist.py @@ -534,10 +534,6 @@ def finalize_options (self): 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: From 74314af647d7117a502d009c6c465851867e00e4 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 5 Apr 2001 15:46:48 +0000 Subject: [PATCH 0720/2594] 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 f7eb93ae43..9ecfb6d13b 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" From c6c4c5c787dad0ef56656ef9123aa7ae49be9c45 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Tue, 10 Apr 2001 18:57:07 +0000 Subject: [PATCH 0721/2594] 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 f1dd633297..477b733cd3 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -237,7 +237,7 @@ def get_exe_bytes (self): AAAA4AAAAA4fug4AtAnNIbgBTM0hVGhpcyBwcm9ncmFtIGNhbm5vdCBiZSBydW4gaW4gRE9TIG1v ZGUuDQ0KJAAAAAAAAABwv7aMNN7Y3zTe2N803tjfT8LU3zXe2N+3wtbfNt7Y39zB3N823tjfVsHL 3zze2N803tnfSN7Y3zTe2N853tjf3MHS3zne2N+M2N7fNd7Y31JpY2g03tjfAAAAAAAAAABQRQAA -TAEDAE55sjoAAAAAAAAAAOAADwELAQYAAEAAAAAQAAAAoAAAsOwAAACwAAAA8AAAAABAAAAQAAAA +TAEDABAF0joAAAAAAAAAAOAADwELAQYAAEAAAAAQAAAAoAAA8OwAAACwAAAA8AAAAABAAAAQAAAA AgAABAAAAAAAAAAEAAAAAAAAAAAAAQAABAAAAAAAAAIAAAAAABAAABAAAAAAEAAAEAAAAAAAABAA AAAAAAAAAAAAADDxAABsAQAAAPAAADABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA @@ -250,7 +250,7 @@ def get_exe_bytes (self): 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 @@ def get_exe_bytes (self): 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 @@ def get_exe_bytes (self): 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 From 4d5c70781a289e136da686d05bcb1a0767e08cea Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Sat, 14 Apr 2001 16:17:00 +0000 Subject: [PATCH 0722/2594] 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 61bc25ea0f..4c5641b951 100644 --- a/archive_util.py +++ b/archive_util.py @@ -100,7 +100,7 @@ def visit (z, dirname, names): 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) From 48343f32fa28556d2d229b5a661bfe5e0b4b0634 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Mon, 23 Apr 2001 16:01:06 +0000 Subject: [PATCH 0723/2594] Bump version # for final release --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index bd9761cc25..16d387a46a 100644 --- a/__init__.py +++ b/__init__.py @@ -10,4 +10,4 @@ __revision__ = "$Id$" -__version__ = "1.0.2pre" +__version__ = "1.0.2" From 09b62d24b0c662a7e9146fcafe7a4f29bff0d463 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Mon, 23 Apr 2001 17:13:03 +0000 Subject: [PATCH 0724/2594] Fix typo in docstring --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 16d387a46a..27e79a54ac 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 From 59653f9ebdb6ead7454c56aadfb40bfcb33b0bfa Mon Sep 17 00:00:00 2001 From: Jack Jansen Date: Thu, 17 May 2001 12:52:01 +0000 Subject: [PATCH 0725/2594] 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 2edc8259bf..981fd95f01 100644 --- a/mwerkscompiler.py +++ b/mwerkscompiler.py @@ -114,6 +114,8 @@ def link (self, # 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 91f9279d87..473faea85e 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") From 9ac36316de9ed5d0c147f7493e9b472480c7b4f7 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Thu, 17 May 2001 15:03:14 +0000 Subject: [PATCH 0726/2594] 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 473faea85e..d013d1b832 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 From 4fb616ab1845cdb8a67b68055a8a348db3c6f99c Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Mon, 21 May 2001 20:34:38 +0000 Subject: [PATCH 0727/2594] 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 f421590488..4659494533 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -189,7 +189,7 @@ def finalize_package_data (self): 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? From e7ab99d0e57404a593cd9e44f9381d3f08b0925c Mon Sep 17 00:00:00 2001 From: Just van Rossum Date: Tue, 19 Jun 2001 19:44:02 +0000 Subject: [PATCH 0728/2594] 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 981fd95f01..1b416715ea 100644 --- a/mwerkscompiler.py +++ b/mwerkscompiler.py @@ -164,7 +164,7 @@ def link (self, 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 From 37936adf51754abcc600a93964e105b20a052e20 Mon Sep 17 00:00:00 2001 From: Jack Jansen Date: Tue, 19 Jun 2001 21:23:11 +0000 Subject: [PATCH 0729/2594] - _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 1b416715ea..46e16e2e94 100644 --- a/mwerkscompiler.py +++ b/mwerkscompiler.py @@ -62,10 +62,13 @@ def compile (self, 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 @@ def link (self, 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 @@ def _filename_to_abs(self, filename): 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, ':') From debc45c4d667d5573afe333e9ac6d9ede0d07da1 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 4 Jul 2001 16:52:02 +0000 Subject: [PATCH 0730/2594] 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 477b733cd3..16a6cc418c 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -216,7 +216,6 @@ def create_exe (self, arcname, fullname, bitmap=None): def get_exe_bytes (self): import base64 return base64.decodestring(EXEDATA) - # class bdist_wininst if __name__ == '__main__': From 78fe57542d08d417655af49304c2b266b993a2ff Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Mon, 16 Jul 2001 14:19:20 +0000 Subject: [PATCH 0731/2594] [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 9ecfb6d13b..91e7d5e03b 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -99,12 +99,13 @@ def preprocess (self, 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: From 4490899b03853d52c7f718b6a2c5a54a2e865829 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Mon, 16 Jul 2001 14:46:13 +0000 Subject: [PATCH 0732/2594] 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 91e7d5e03b..da1f2a4e8e 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -105,7 +105,7 @@ def preprocess (self, # 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: From 89b261c72363d96d8c2e380370cff3396b22b488 Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Wed, 18 Jul 2001 18:39:56 +0000 Subject: [PATCH 0733/2594] 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 d013d1b832..16e80231d1 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 From cfca016e6101c3590ecefd1731c56be76f4f6b83 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Fri, 20 Jul 2001 19:29:04 +0000 Subject: [PATCH 0734/2594] 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 01abd346d5..25ddbdf450 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: From 9dad3ab68accd527b9d7027b9d005303abfbdc35 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 25 Jul 2001 19:48:03 +0000 Subject: [PATCH 0735/2594] 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 39f6eea289..a3767bbd93 100644 --- a/file_util.py +++ b/file_util.py @@ -8,7 +8,6 @@ __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, \ From 49b8a8e81d33296018e5454c1d42c9a8321510da Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Wed, 25 Jul 2001 20:20:11 +0000 Subject: [PATCH 0736/2594] 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 ee7f4e2d1e..653b8d8ace 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -10,10 +10,8 @@ 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): 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 0737/2594] 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 f732373ea3..259a8447fb 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -125,7 +125,7 @@ def finalize_options (self): 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 @@ def get_libraries (self, ext): """ # 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 92def164ad..07e16655b0 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -73,7 +73,7 @@ def __init__ (self, (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 @@ def __init__ (self, # 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 @@ def __init__ (self, 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 16e80231d1..529d0e6dd3 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(): 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: [PATCH 0738/2594] Add backwards compatibility. --- sysconfig.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sysconfig.py b/sysconfig.py index 529d0e6dd3..bbf7c4afab 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(): From 6f9753a79698a3fd9c882c8567cd75a351e6e04a Mon Sep 17 00:00:00 2001 From: Just van Rossum Date: Sun, 29 Jul 2001 21:39:18 +0000 Subject: [PATCH 0739/2594] 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 653b8d8ace..611767e7bc 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -9,6 +9,7 @@ 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 @@ def copy_scripts (self): 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): From 27487d75c92e72a5a28d25d0b75bbb26c49ec5b4 Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Thu, 2 Aug 2001 20:03:12 +0000 Subject: [PATCH 0740/2594] Miscellaneous minor cleanups. --- sysconfig.py | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index bbf7c4afab..1f0d14539f 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -29,14 +29,15 @@ 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) From 6daca2f31cd60949e816919a9c1163b016c02a71 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 9 Aug 2001 20:57:46 +0000 Subject: [PATCH 0741/2594] 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 6f8b8c06f1..83d07216a7 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -275,7 +275,7 @@ def getopt (self, args=None, object=None): 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: From db61a5d336c081391a6621918a63df03e9b64e04 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 9 Aug 2001 20:59:53 +0000 Subject: [PATCH 0742/2594] 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 a3767bbd93..991d8357b5 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) From ff1cb1dae11e3fb8f12fba68a62f9c0085ff9e50 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 9 Aug 2001 21:02:34 +0000 Subject: [PATCH 0743/2594] Import UnknownFileError --- bcppcompiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bcppcompiler.py b/bcppcompiler.py index 00ccec548b..2742b5fe33 100644 --- a/bcppcompiler.py +++ b/bcppcompiler.py @@ -17,7 +17,7 @@ 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 From 34771a9b3649f5b2762c5090a5587d49dc7e9698 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Fri, 10 Aug 2001 18:49:23 +0000 Subject: [PATCH 0744/2594] [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 1ac9786d8d..824a0d3e6a 100644 --- a/dist.py +++ b/dist.py @@ -13,7 +13,6 @@ 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 @@ def parse_command_line (self): 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 @@ def _show_help (self, 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 @@ def __init__ (self): 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 @@ def write_pkg_info (self, base_dir): 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 @@ def get_contact_email(self): 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" From 404bfc2ce6499d3f1e0f3f7bc318805fb1f53feb Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Fri, 10 Aug 2001 18:50:11 +0000 Subject: [PATCH 0745/2594] 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 4659494533..92de935825 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -347,7 +347,7 @@ def _make_spec_file(self): 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}', ]) From a23893242a626332e830cf29243efe00df23c38f Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Fri, 10 Aug 2001 18:59:30 +0000 Subject: [PATCH 0746/2594] 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 824a0d3e6a..3803f5cc68 100644 --- a/dist.py +++ b/dist.py @@ -375,8 +375,8 @@ def parse_command_line (self): 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 @@ def _parse_command_opts (self, parser, args): 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 From 67be7feb397ea9c635a125a3efa771e38454e78c Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Fri, 10 Aug 2001 18:59:59 +0000 Subject: [PATCH 0747/2594] Add forgotten import --- command/install.py | 1 + 1 file changed, 1 insertion(+) diff --git a/command/install.py b/command/install.py index 40e00705d3..1d0a34e4cd 100644 --- a/command/install.py +++ b/command/install.py @@ -10,6 +10,7 @@ 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 From cd27545f369affed2ce5537416fd463fb1180c1c Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Fri, 10 Aug 2001 19:00:15 +0000 Subject: [PATCH 0748/2594] 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 92de935825..150fdeca62 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -167,7 +167,7 @@ def finalize_options (self): ("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 From f7cb031597baffc1532b640a2c5fb2d08f306524 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Fri, 10 Aug 2001 19:00:41 +0000 Subject: [PATCH 0749/2594] 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 611767e7bc..16024e5fed 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -51,7 +51,6 @@ def copy_scripts (self): 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 From 45de3e623e5f498ee233b53b22a76a48fc542a08 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Fri, 10 Aug 2001 20:24:33 +0000 Subject: [PATCH 0750/2594] [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 063da91552..69ed044551 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -184,9 +184,25 @@ def get_library_names (self): # 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') From 33c706bf78fbd8f9aad6ec837164a703b8a9a2e2 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Mon, 13 Aug 2001 13:56:24 +0000 Subject: [PATCH 0751/2594] 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 ce6cff8cef..d409691874 100644 --- a/command/config.py +++ b/command/config.py @@ -272,8 +272,8 @@ def try_run (self, body, 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): From dbdaae041996677d97be3ed6bed2b0bd59643989 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 16 Aug 2001 13:56:40 +0000 Subject: [PATCH 0752/2594] [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 d409691874..2df42a5722 100644 --- a/command/config.py +++ b/command/config.py @@ -187,7 +187,7 @@ def try_cpp (self, body=None, headers=None, include_dirs=None, lang="c"): 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 @@ def search_cpp (self, pattern, body=None, """ 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 @@ def search_cpp (self, pattern, body=None, line = file.readline() if line == '': break - if pattern.search(pattern): + if pattern.search(line): match = 1 break @@ -231,7 +231,7 @@ def try_compile (self, body, headers=None, include_dirs=None, lang="c"): 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 From 95bdf9afbb9a6455ee440381fc6226fd00aede8f Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 16 Aug 2001 14:08:02 +0000 Subject: [PATCH 0753/2594] [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 2df42a5722..b89997a293 100644 --- a/command/config.py +++ b/command/config.py @@ -148,10 +148,13 @@ def _link (self, body, 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): From d786b1a936ba0f38409c95d2906c908d1ba245a1 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 16 Aug 2001 20:17:41 +0000 Subject: [PATCH 0754/2594] [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 2742b5fe33..5c0fae8b71 100644 --- a/bcppcompiler.py +++ b/bcppcompiler.py @@ -21,7 +21,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 class BCPPCompiler(CCompiler) : """Concrete class that implements an interface to the Borland C/C++ @@ -373,3 +373,37 @@ def object_filenames (self, 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() From f5528a1e2f78f25f3208d3365f6b0ed3f5e7e74e Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Thu, 23 Aug 2001 20:53:27 +0000 Subject: [PATCH 0755/2594] 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 1d0a34e4cd..5af4cf1055 100644 --- a/command/install.py +++ b/command/install.py @@ -16,6 +16,23 @@ 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 @@ '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 1f0d14539f..558ff2938e 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: From 679698bbb368dc2cee79992418c7720f2acce34a Mon Sep 17 00:00:00 2001 From: Jack Jansen Date: Mon, 27 Aug 2001 15:08:16 +0000 Subject: [PATCH 0756/2594] 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 4a282d4cff..4efd93407b 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -792,8 +792,8 @@ def library_filename (self, 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 da1f2a4e8e..a4f0ac4d04 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 @@ 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')) + 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 @@ def find_library_file (self, dirs, lib, debug=0): # 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 From 31ba5afaf948150804377ad1f4a508e9578d1549 Mon Sep 17 00:00:00 2001 From: Neil Schemenauer Date: Wed, 29 Aug 2001 23:57:22 +0000 Subject: [PATCH 0757/2594] 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 ce44829498..85a7f46182 100644 --- a/cmd.py +++ b/cmd.py @@ -188,6 +188,7 @@ def announce (self, msg, level=1): """ 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 @@ def debug_print (self, msg): from distutils.core import DEBUG if DEBUG: print msg + sys.stdout.flush() From 8035ea689daa1273bbcb133c80e51c70a4fa3ca6 Mon Sep 17 00:00:00 2001 From: Neil Schemenauer Date: Mon, 3 Sep 2001 15:47:21 +0000 Subject: [PATCH 0758/2594] 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 3803f5cc68..40dcc96e27 100644 --- a/dist.py +++ b/dist.py @@ -122,9 +122,7 @@ def __init__ (self, attrs=None): # 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 From 66b732945438e944b73f2417ac97195af45c49d7 Mon Sep 17 00:00:00 2001 From: Jack Jansen Date: Tue, 4 Sep 2001 12:01:49 +0000 Subject: [PATCH 0759/2594] 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 558ff2938e..cc663a8341 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 " From 6d5c86825f122583f4fec4be228f10e2c712e61e Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Tue, 4 Sep 2001 20:06:43 +0000 Subject: [PATCH 0760/2594] [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 5af4cf1055..022e34a2e0 100644 --- a/command/install.py +++ b/command/install.py @@ -537,8 +537,7 @@ def create_path_file (self): # -- 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 @@ def get_outputs (self): 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): From 33914718f28f60a0d0ff5f79bc6662748fd1dc6b Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Tue, 4 Sep 2001 20:42:08 +0000 Subject: [PATCH 0761/2594] [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 28f593866c..d0091ce238 100644 --- a/command/install_data.py +++ b/command/install_data.py @@ -63,10 +63,18 @@ def run (self): 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 [] From 4a7fbb7560c4f661bf8314166f6d26c0bfd25882 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Wed, 5 Sep 2001 12:02:59 +0000 Subject: [PATCH 0762/2594] [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 cc663a8341..935372cd2b 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 From e9d279e9c8eb2b7b6bbdf3f9c6e01eb27684dce7 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Wed, 5 Sep 2001 13:00:40 +0000 Subject: [PATCH 0763/2594] 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 16a6cc418c..8e4a7964a9 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -83,46 +83,41 @@ def run (self): 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 @@ def get_exe_bytes (self): EXEDATA = """\ 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 From 74eebff9df62f424e6be3262778cd50611dd7dbd Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Fri, 5 Oct 2001 20:40:48 +0000 Subject: [PATCH 0764/2594] 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 8e4a7964a9..7cdf385553 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -140,13 +140,13 @@ def get_inidata (self): # 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 @@ def get_exe_bytes (self): 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 @@ -252,8 +252,8 @@ def get_exe_bytes (self): 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 @@ def get_exe_bytes (self): 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 -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 --- From b00902df9fcae870190f460790a9293c8eda1c61 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Fri, 5 Oct 2001 20:43:09 +0000 Subject: [PATCH 0765/2594] 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 27e79a54ac..1055aa39c1 100644 --- a/__init__.py +++ b/__init__.py @@ -10,4 +10,4 @@ __revision__ = "$Id$" -__version__ = "1.0.2" +__version__ = "1.0.3" From 333b9da550f41bac8faf5a4b3bfccc53a54da671 Mon Sep 17 00:00:00 2001 From: Jack Jansen Date: Sat, 10 Nov 2001 23:20:22 +0000 Subject: [PATCH 0766/2594] 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 46e16e2e94..e759456a36 100644 --- a/mwerkscompiler.py +++ b/mwerkscompiler.py @@ -16,7 +16,7 @@ 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 @@ def link (self, 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() From 6fdff7d1bce93929138b277904edd9508b635978 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 6 Dec 2001 16:32:05 +0000 Subject: [PATCH 0767/2594] [Bug #459270] Fix incorrect docstring --- dist.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/dist.py b/dist.py index 40dcc96e27..3e1bc648d4 100644 --- a/dist.py +++ b/dist.py @@ -262,14 +262,11 @@ def find_config_files (self): 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() From 1a4ad0259c7382d0520aa9dad7e6af852b3d7ad9 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Thu, 6 Dec 2001 20:44:19 +0000 Subject: [PATCH 0768/2594] 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 3e1bc648d4..d5bfa05dc4 100644 --- a/dist.py +++ b/dist.py @@ -1012,7 +1012,7 @@ def get_name (self): 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()) From 68cb7fe9ae037b7a517b692213c03c6c0e54f7e4 Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Thu, 6 Dec 2001 20:51:35 +0000 Subject: [PATCH 0769/2594] 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 4c5641b951..58d9a062e1 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 5c0fae8b71..9ebba2d85b 100644 --- a/bcppcompiler.py +++ b/bcppcompiler.py @@ -102,7 +102,7 @@ def compile (self, 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 @@ def compile (self, 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 @@ def create_static_lib (self, 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 @@ def link (self, 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 @@ def link (self, # 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 @@ def link (self, 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 4efd93407b..21bf0c1715 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 @@ def set_executable(self, key, value): setattr(self, key, split_quoted(value)) else: setattr(self, key, value) - + def _find_macro (self, name): @@ -352,7 +352,7 @@ def _fix_compile_args (self, output_dir, macros, include_dirs): 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 @@ def _prep_compile (self, sources, output_dir): 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 @@ def _fix_object_args (self, objects, output_dir): 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 @@ def create_static_lib (self, Raises LibError on failure. """ pass - + # values for target_desc parameter in link() SHARED_OBJECT = "shared_object" @@ -621,7 +621,7 @@ def link (self, """ raise NotImplementedError - + # Old 'link_*()' methods, rewritten to use the new 'link()' method. def link_shared_lib (self, @@ -636,13 +636,13 @@ def link_shared_lib (self, 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 @@ def link_executable (self, 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 @@ def mkpath (self, name, mode=0777): # 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 85a7f46182..65060d6700 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 @@ def ensure_finalized (self): if not self.finalized: self.finalize_options() self.finalized = 1 - + # Subclasses must define: # initialize_options() @@ -133,12 +133,12 @@ def initialize_options (self): 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 @@ def debug_print (self, msg): 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 @@ def ensure_string_list (self, option): 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 @@ def copy_tree (self, infile, outfile, 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 @@ def make_file (self, infiles, outfile, func, args, (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 2aab7c4dd3..97a741c812 100644 --- a/core.py +++ b/core.py @@ -108,7 +108,7 @@ class found in 'cmdclass' is used in place of the default, which is # 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 @@ class found in 'cmdclass' is used in place of the default, which is raise else: raise SystemExit, error - + except (DistutilsExecError, DistutilsFileError, DistutilsOptionError, diff --git a/cygwinccompiler.py b/cygwinccompiler.py index 07e16655b0..1d97282322 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -18,28 +18,28 @@ # # 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 @@ def __init__ (self, "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 @@ def __init__ (self, 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 @@ def compile (self, 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 @@ def compile (self, 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 @@ def link (self, 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 @@ def link (self, (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 @@ def link (self, 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 @@ def link (self, 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 @@ def link (self, extra_preargs, extra_postargs, build_temp) - + # link () # -- Miscellaneous methods ----------------------------------------- @@ -288,7 +288,7 @@ def object_filenames (self, 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 @@ def __init__ (self, 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 @@ def __init__ (self, 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 4b93ed023c..9edba4c8ae 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 a1578bed6a..77007c976b 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 d5bfa05dc4..b648f24eb7 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 @@ def __init__ (self, attrs=None): "invalid distribution option '%s'" % key self.finalize_options() - + # __init__ () @@ -251,7 +251,7 @@ def dump_option_dicts (self, header=None, commands=None, indent=""): print indent + " " + line # dump_option_dicts () - + # -- Config file finding/parsing methods --------------------------- @@ -378,7 +378,7 @@ def parse_command_line (self): 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 @@ def parse_command_line (self): # 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 @@ def _parse_command_opts (self, parser, args): "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 @@ def _set_command_options (self, command_obj, option_dict=None): (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 @@ def reinitialize_command (self, command, reinit_subcommands=0): 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 @@ def reinitialize_command (self, command, reinit_subcommands=0): 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 @@ def __init__ (self): 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 @@ def write_pkg_info (self, base_dir): pkg_info.write('Platform: %s\n' % platform ) pkg_info.close() - + # write_pkg_info () - + # -- Metadata query methods ---------------------------------------- def get_name (self): @@ -1045,7 +1045,7 @@ def get_url(self): 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 a63ede233c..fbae7c5226 100644 --- a/extension.py +++ b/extension.py @@ -16,7 +16,7 @@ # 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 83d07216a7..e65302fc0b 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -73,7 +73,7 @@ def __init__ (self, option_table=None): # '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 @@ def __init__ (self, option_table=None): self.option_order = [] # __init__ () - + def _build_index (self): self.option_index.clear() @@ -117,7 +117,7 @@ def has_option (self, 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 + """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 @@ def _check_alias_dict (self, aliases, what): 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 @@ def __init__ (self, options=[]): setattr(self, opt, None) # class OptionDummy - + if __name__ == "__main__": text = """\ diff --git a/file_util.py b/file_util.py index 991d8357b5..526e4cf593 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 211b65f8d2..f7222fd927 100644 --- a/filelist.py +++ b/filelist.py @@ -7,7 +7,7 @@ # 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 @@ def findall (self, dir=os.curdir): # -- 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 @@ def remove_duplicates (self): # -- "File template" methods --------------------------------------- - + def _parse_template_line (self, line): words = string.split(line) action = words[0] @@ -129,9 +129,9 @@ def _parse_template_line (self, line): 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 @@ def process_template_line (self, line): 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 @@ def include_pattern (self, pattern, self.debug_print(" adding " + name) self.files.append(name) files_found = 1 - + return files_found # include_pattern () @@ -261,7 +261,7 @@ 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. + 'include_pattern()', above. The list 'self.files' is modified in place. Return 1 if files are found. """ @@ -274,7 +274,7 @@ def exclude_pattern (self, pattern, 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 0325b48508..8a67dfc823 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -50,8 +50,8 @@ 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 @@ def compile (self, 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 @@ def create_static_lib (self, 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 @@ def link (self, 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 @@ def link (self, 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 e759456a36..7c77b8bcef 100644 --- a/mwerkscompiler.py +++ b/mwerkscompiler.py @@ -52,8 +52,8 @@ def __init__ (self, force=0): CCompiler.__init__ (self, verbose, dry_run, force) - - + + def compile (self, sources, output_dir=None, @@ -62,14 +62,14 @@ def compile (self, 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 @@ def link (self, 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 @@ def _filename_to_abs(self, 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) + 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 1eed7a8abc..07dc81484a 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 935372cd2b..feaf318ccd 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 37bffe6139..7086b1af56 100644 --- a/text_file.py +++ b/text_file.py @@ -86,7 +86,7 @@ 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'" + "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 @@ def __init__ (self, filename=None, file=None, **options): # 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 @@ def readline (self): # 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 @@ def readline (self): 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 @@ def readline (self): 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 @@ def test_input (count, description, file, expected_result): print expected_result print "** received:" print result - + filename = "test.txt" out_file = open (filename, "w") @@ -382,4 +382,3 @@ def test_input (count, description, file, expected_result): test_input (6, "join lines with collapsing", in_file, result6) os.remove (filename) - diff --git a/unixccompiler.py b/unixccompiler.py index a4f0ac4d04..a9b5de51ce 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -103,8 +103,8 @@ def preprocess (self, 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 @@ def compile (self, 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 @@ def compile (self, return objects # compile () - + def create_static_lib (self, objects, @@ -193,7 +193,7 @@ def create_static_lib (self, def link (self, - target_desc, + target_desc, objects, output_filename, output_dir=None, @@ -219,7 +219,7 @@ def link (self, 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 @@ def link (self, 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 @@ def link (self, # -- 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 25ddbdf450..1541e02de9 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 @@ def byte_compile (py_files, 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 9d3d172429..02502dac95 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 @@ def parse (self, vstring): def __str__ (self): - + if self.version[2] == 0: vstring = string.join(map(str, self.version[0:2]), '.') else: @@ -133,7 +133,7 @@ def __str__ (self): vstring = vstring + self.prerelease[0] + str(self.prerelease[1]) return vstring - + def __cmp__ (self, other): if isinstance(other, StringType): @@ -170,7 +170,7 @@ def __cmp__ (self, other): # 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 @@ def __cmp__ (self, other): # - 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 @@ def __cmp__ (self, other): # 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 @@ def __cmp__ (self, other): # 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 @@ def __cmp__ (self, other): other = LooseVersion(other) return cmp(self.version, other.version) - + # end class LooseVersion From 4263b3189f9532a313b1bb52e478168f8a545729 Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Thu, 6 Dec 2001 21:01:19 +0000 Subject: [PATCH 0770/2594] 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 a75303e689..2b1951fd32 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -17,7 +17,7 @@ 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 @@ def finalize_options (self): 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 150fdeca62..037ed9e8f9 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 @@ def finalize_package_data (self): 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 @@ def finalize_package_data (self): 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 @@ def run (self): else: raise DistutilsFileError, \ "icon file '%s' does not exist" % self.icon - + # build package self.announce('building RPMs') @@ -368,17 +368,17 @@ def _make_spec_file(self): 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 @@ def _format_changelog(self, changelog): 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 7cdf385553..357ba519d3 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -196,7 +196,7 @@ def create_exe (self, arcname, fullname, bitmap=None): 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: [PATCH 0771/2594] [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 16024e5fed..31750b7c1d 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -12,7 +12,7 @@ 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): From bf0c7b1e25b1e08c625bb9f5cfd1e6db68360aa6 Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Thu, 6 Dec 2001 22:59:54 +0000 Subject: [PATCH 0772/2594] 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 c9e3062306..7a39314bcd 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -456,6 +456,17 @@ def build_extension(self, ext): 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. From 730b95b729f9b7acfb9d84225ce74b7f20b8f263 Mon Sep 17 00:00:00 2001 From: "Michael W. Hudson" Date: Mon, 10 Dec 2001 15:28:30 +0000 Subject: [PATCH 0773/2594] 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 cf35b45d01..6bc92a43b8 100644 --- a/command/build.py +++ b/command/build.py @@ -90,7 +90,8 @@ def finalize_options (self): 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 () From 158c496ca9d641e38b8469143db7cf49ff35b8a8 Mon Sep 17 00:00:00 2001 From: "Michael W. Hudson" Date: Mon, 10 Dec 2001 16:15:44 +0000 Subject: [PATCH 0774/2594] 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 31750b7c1d..7286bf1f3f 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -7,6 +7,7 @@ __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 @@ def copy_scripts (self): (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: From 54b68c95834551d0995f388151974be7b967a471 Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Tue, 11 Dec 2001 05:04:24 +0000 Subject: [PATCH 0775/2594] 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 a9b5de51ce..356587d600 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -20,6 +20,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 @@ -249,7 +250,23 @@ def library_dir_option (self, dir): 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 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: [PATCH 0776/2594] 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 7286bf1f3f..bfa33c3a10 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -80,7 +80,7 @@ def copy_scripts (self): 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" % From 5ccf2307126f5e3f989925d2653239860c9b53db Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Tue, 18 Dec 2001 20:13:40 +0000 Subject: [PATCH 0777/2594] 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 357ba519d3..9ff9461307 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -112,13 +112,16 @@ def run (self): # 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) From 74bfeea5bba971e7e23323973affee72cfd37c60 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Tue, 18 Dec 2001 21:08:15 +0000 Subject: [PATCH 0778/2594] 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 9ff9461307..7c34cffd85 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -234,7 +234,7 @@ def get_exe_bytes (self): AAAA8AAAAA4fug4AtAnNIbgBTM0hVGhpcyBwcm9ncmFtIGNhbm5vdCBiZSBydW4gaW4gRE9TIG1v ZGUuDQ0KJAAAAAAAAAA/SHa+eykY7XspGO17KRjtADUU7XkpGO0UNhLtcCkY7fg1Fu15KRjtFDYc 7XkpGO0ZNgvtcykY7XspGe0GKRjteykY7XYpGO19ChLteSkY7bwvHu16KRjtUmljaHspGO0AAAAA -AAAAAAAAAAAAAAAAUEUAAEwBAwBWGr47AAAAAAAAAADgAA8BCwEGAABQAAAAEAAAAKAAAODuAAAA +AAAAAAAAAAAAAAAAUEUAAEwBAwCUrh88AAAAAAAAAADgAA8BCwEGAABQAAAAEAAAAKAAANDuAAAA sAAAAAABAAAAQAAAEAAAAAIAAAQAAAAAAAAABAAAAAAAAAAAEAEAAAQAAAAAAAACAAAAAAAQAAAQ AAAAABAAABAAAAAAAAAQAAAAAAAAAAAAAAAwAQEAbAEAAAAAAQAwAQAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA @@ -247,7 +247,7 @@ def get_exe_bytes (self): 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 @@ def get_exe_bytes (self): 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 From b8b4137be241dd8aeb636a704150fea716b63a30 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Fri, 21 Dec 2001 15:34:17 +0000 Subject: [PATCH 0779/2594] 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 fbae7c5226..a31ccbce8d 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? 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 0780/2594] 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 2b1951fd32..fc18bd0c80 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 @@ def initialize_options (self): 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 8dfc3271df..dbe862bb42 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 @@ def initialize_options (self): self.format = None self.keep_temp = 0 self.dist_dir = None + self.skip_build = 0 # initialize_options() @@ -71,10 +74,12 @@ def finalize_options (self): 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 7c34cffd85..4a16eec45e 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 @@ def initialize_options (self): self.dist_dir = None self.bitmap = None self.title = None + self.skip_build = 0 # initialize_options() @@ -79,10 +82,12 @@ def run (self): ("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 From 877f0c18a6a831ef6f840a30e04d9ad8414d9aef Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Fri, 18 Jan 2002 20:30:53 +0000 Subject: [PATCH 0781/2594] 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 7a39314bcd..98617f7fcd 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -511,7 +511,7 @@ def swig_sources (self, sources): 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 @@ def swig_sources (self, sources): 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++") 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: [PATCH 0782/2594] 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 8a67dfc823..35336642ae 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 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: [PATCH 0783/2594] 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 21bf0c1715..f4fc4bcbae 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -846,6 +846,7 @@ def mkpath (self, name, mode=0777): # 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 @@ def get_default_compiler(osname=None, platform=None): "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 fc18bd0c80..68609f346a 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 dbe862bb42..b627a86a43 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 @@ def run (self): # 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 98617f7fcd..91fee5ee6e 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -167,6 +167,11 @@ def finalize_options (self): 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 @@ def find_swig (self): 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 @@ def get_ext_filename (self, ext_name): 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 @@ def get_export_symbols (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, 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 @@ def get_libraries (self, ext): # 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 8755a1424a..4d78d3aa6f 100644 --- a/command/install.py +++ b/command/install.py @@ -50,6 +50,13 @@ }, '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 07dc81484a..5b6016e0d8 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 feaf318ccd..d773f14905 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 1541e02de9..a51ce6601d 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) From f666dda63137a66f6d619529d79c9238dfbae94d Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 31 Jan 2002 22:08:38 +0000 Subject: [PATCH 0784/2594] 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 3bc23e7460..d4cbaa3a0a 100644 --- a/command/install_scripts.py +++ b/command/install_scripts.py @@ -50,7 +50,7 @@ def run (self): 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) From 4431a9867079962ba92f8671378c33555fe45a10 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Fri, 1 Feb 2002 09:44:09 +0000 Subject: [PATCH 0785/2594] 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 527e81d9ae..97d094b1b2 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -12,6 +12,7 @@ from distutils.core import Command from distutils.errors import * +from distutils.util import convert_path class build_py (Command): @@ -50,7 +51,10 @@ def finalize_options (self): # 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!!!) From 0592edfb320ccaba1ac5478560dab5f5dbdccdd9 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Fri, 1 Feb 2002 18:29:34 +0000 Subject: [PATCH 0786/2594] [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 526e4cf593..14772fb74a 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): 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: [PATCH 0787/2594] 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 0000000000..58a0d812e4 --- /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) + From e4a3cf2f6d0118090f23e5ed8b9d61afd9b2b6f5 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Fri, 8 Feb 2002 14:41:31 +0000 Subject: [PATCH 0788/2594] 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 35336642ae..79a4901bea 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 From ab694beb3331ca6d37bf29f0f783c2cd57c52bb5 Mon Sep 17 00:00:00 2001 From: Just van Rossum Date: Mon, 11 Feb 2002 15:31:50 +0000 Subject: [PATCH 0789/2594] 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 356587d600..7e63c56afe 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -17,7 +17,7 @@ __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 From 32ee66a4511be5b957e95bf1e71f148b611f9736 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Wed, 20 Feb 2002 08:01:19 +0000 Subject: [PATCH 0790/2594] 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 4a16eec45e..fe52f393dc 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 @@ def initialize_options (self): self.bitmap = None self.title = None self.skip_build = 0 + self.install_script = None # initialize_options() @@ -71,6 +75,11 @@ def finalize_options (self): 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 @@ def get_inidata (self): # 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 @@ def get_exe_bytes (self): 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 @@ def get_exe_bytes (self): EXEDATA = """\ TVqQAAMAAAAEAAAA//8AALgAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAA8AAAAA4fug4AtAnNIbgBTM0hVGhpcyBwcm9ncmFtIGNhbm5vdCBiZSBydW4gaW4gRE9TIG1v -ZGUuDQ0KJAAAAAAAAAA/SHa+eykY7XspGO17KRjtADUU7XkpGO0UNhLtcCkY7fg1Fu15KRjtFDYc -7XkpGO0ZNgvtcykY7XspGe0GKRjteykY7XYpGO19ChLteSkY7bwvHu16KRjtUmljaHspGO0AAAAA -AAAAAAAAAAAAAAAAUEUAAEwBAwCUrh88AAAAAAAAAADgAA8BCwEGAABQAAAAEAAAAKAAANDuAAAA -sAAAAAABAAAAQAAAEAAAAAIAAAQAAAAAAAAABAAAAAAAAAAAEAEAAAQAAAAAAAACAAAAAAAQAAAQ -AAAAABAAABAAAAAAAAAQAAAAAAAAAAAAAAAwAQEAbAEAAAAAAQAwAQAAAAAAAAAAAAAAAAAAAAAA +AAAA+AAAAA4fug4AtAnNIbgBTM0hVGhpcyBwcm9ncmFtIGNhbm5vdCBiZSBydW4gaW4gRE9TIG1v +ZGUuDQ0KJAAAAAAAAAA/1p0ge7fzc3u383N7t/NzAKv/c3m383MUqPlzcLfzc/ir/XN5t/NzFKj3 +c3m383N7t/NzdLfzc3u38nPzt/NzGajgc3C383N9lPlzebfzc7yx9XN6t/NzUmljaHu383MAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAABQRQAATAEDAIRxcjwAAAAAAAAAAOAADwELAQYAAFAAAAAQAAAA +oAAA0PMAAACwAAAAAAEAAABAAAAQAAAAAgAABAAAAAAAAAAEAAAAAAAAAAAQAQAABAAAAAAAAAIA +AAAAABAAABAAAAAAEAAAEAAAAAAAABAAAAAAAAAAAAAAADABAQCgAQAAAAABADABAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABVUFgwAAAAAACgAAAAEAAAAAAAAAAEAAAA -AAAAAAAAAAAAAACAAADgVVBYMQAAAAAAUAAAALAAAABCAAAABAAAAAAAAAAAAAAAAAAAQAAA4C5y -c3JjAAAAABAAAAAAAQAABAAAAEYAAAAAAAAAAAAAAAAAAEAAAMAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFVQWDAAAAAAAKAAAAAQAAAA +AAAAAAQAAAAAAAAAAAAAAAAAAIAAAOBVUFgxAAAAAABQAAAAsAAAAEYAAAAEAAAAAAAAAAAAAAAA +AABAAADgLnJzcmMAAAAAEAAAAAABAAAEAAAASgAAAAAAAAAAAAAAAAAAQAAAwAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAkSW5mbzogVGhpcyBmaWxlIGlz IHBhY2tlZCB3aXRoIHRoZSBVUFggZXhlY3V0YWJsZSBwYWNrZXIgaHR0cDovL3VweC50c3gub3Jn ICQKACRJZDogVVBYIDEuMDEgQ29weXJpZ2h0IChDKSAxOTk2LTIwMDAgdGhlIFVQWCBUZWFtLiBB -bGwgUmlnaHRzIFJlc2VydmVkLiAkCgBVUFghDAkCCjD69l3lQx/kVsgAAME+AAAAsAAAJgEA4P/b -//9TVVaLdCQUhfZXdH2LbCQci3wMgD4AdHBqXFb/5vZv/xU0YUAAi/BZHVl0X4AmAFcRvGD9v/n+ -2IP7/3Unag+4hcB1E4XtdA9XaBBw/d/+vw1qBf/Vg8QM6wdXagEJWVn2wxB1HGi3ABOyna0ALbQp -Dcb3/3/7BlxGdYssWF9eXVvDVYvsg+wMU1ZXiz3ALe/uf3cz9rs5wDl1CHUHx0UIAQxWaIBMsf9v -bxFWVlMFDP/Xg/j/iUX8D4WIY26+vZnUEQN1GyEg/3UQ6Bf/b7s31wBopw+EA0HrsR9QdAmPbduz +bGwgUmlnaHRzIFJlc2VydmVkLiAkCgBVUFghDAkCCqI5dyI/g8/W3dgAAMlDAAAA0AAAJgEA1//b +//9TVVaLdCQUhfZXdH2LbCQci3wMgD4AdHBqXFb/5vZv/xVMcUAAi/BZHVl0X4AmAFcRzHD9v/n+ +2IP7/3Unag/IhcB1E4XtdA9XaBCA/d/+vw1qBf/Vg8QM6wdXagEJWVn2wxB1HGi3ABOyna0ALcQp +Dcb3/3/7BlxGdYssWF9eXVvDVYvsg+wMU1ZXiz2oLe/uf3cz9rs5wDl1CHUHx0UIAQxWaIBMsf9v +bxFWVlMFDP/Xg/j/iUX8D4WIY26+vZmsEQN1GyEg/3UQ6Bf/b7s31wBopw+EA0HrsR9QdAmPbduz UI/rL1wgGOpTDGoCrM2W7f9VIPDALmcQZronYy91JS67aFTH6Xbf891TAes7B1kO8yR0Cq3QHvkT -A41F9G4GAgx7n4UYQtB9/BIDvO7NNEioNBR1CQvIlgbTfTN/DlZqBFYQxBD7GlyEyHyJfg9hOIKz -3drmPOsmpSsCUyqs+b5tW1OnCCWLBDvGdRcnEMKGNuEoco4KM8BsC+3/5FvJOIN9EAhTi10IaUOS -druwffI4k8jdUOjITCJFsnzb3AwvUMgIFEBqAcz+c7ftGF4G2CVoqFEq8VCJXdS/sHDrLSIbfRw7 -dGn/dChQaO72+b6QmBlLBCPsjnQTGnOd+5YNfIsEyYr2IR8byFn3Inw6Lh9kQ+2w0VoDxUUSPsgP -3ea+U5ccGY1e8MwUxuPO8GHOgewo4auLVRBExv/tf4tMAvqNXALqV5/gK0MMK8GD6BaLG//L7f/P +A41F9G4GAgx7n4UYQrB9/BIDvO7NNEjQNBR1CQvYlgbTfTN/DlZqBFYQ1BD7GlyE2HyJfg9hOIKz +3drmPOsmpSsCUyq8+b5tW1OnCCWLBDvGdRcnEMKGNuEoco4KM8BsC+3/5FvJOIN9EAhTi10IaUOS +druwffI4k8jdUOjIU4JFsnzb3AwvUMgIFEBqAcz+c7ftGF4G2CVoqFEq8VCJXdS/sLDtLSos1xw7 +dGn/dChQaO72+b6QmBlLBCtMjnQTGnOd+5YNfIsEyYr2IR8byFn3Kdw6Lh9kQ+2w0VoDxUUSPsgP +3ea+U5d8GY1e8KQUxuPO8GHOgewo4auLVRBExv/tf4tMAvqNXALqV5/gK0MMK8GD6BaLG//L7f/P gTtQSwUGiX3o8GsCg2UUAGaDewoA/5v77g+OYA7rCYtN7D/M6ItEESqNNBEDttkubzP6gT4BAjA6 gT8Lv3Wf7QMEPC7BLpSJMQPKD79WbY/dth4I9AZOIAwcA1UV0W370rwITxyJwVcaA9CbEBYjNP72 -6I1EAipF3I2F2P6baTShezdgCy7dgLwF1w9cMseY4VkYaLATHShPFz4bMyvtlGX4hoQFtrRhexFS -5PaDOMA+Cn5jL9vwDfzw/zBSUAp19HyWzNYNNOwPEMoA2zn3Py38/0X4g8AILzU9dciruTo3e0ca -UGU0aEAKsIG8zJWwBXitrPuG25p0SqZmi0YMUAQOQ5prbDh2ueRQVCyrvwb30UclIicbCBt2FMyz -/bZRDdxKAfqZGNJ7+9g2mRjJFXlQKUMKUEO7PbexagbBGLwPtRQ5Aiq4rnsPjE1h6ZFw+7pg7rFf -pjTIjRzIlv9zBNSo2W/uNig3XxrwJvQDyCvYGSY5hIWz/HYQKksgHMDeL1mNT8H+tr2KCID5MwUE -L3UCQEtT9p1hKQg3W+A6oQQbj9elLHjrA+quXCT937eGBAcRO4TJdAs6A8YAXEB175OtLyeXQAwP -dBfGFI2d2VzxTthIBmncTd/YSzbAV1AU1GPYnF1b/m+2DAzQagqZWff5M8lolHFRAPmc4zYeaLyu -CZVgakvNVk8wUELXGrHtZusxFBVIvuCPBFb0d7fa1llQUg8BGh04GP/TaGAdvAzH9BdgIzvwvnYK -ARXTqShfmjlzpjyfnszwvm331vnywhAA2rgABgA9PI6tWejhTumUgQkQGrsbcEtSrAz8fQhXIQdD -3Y6G7aF0ozxZJRQ8aHIiaDWd++ABBAB5pFUG7FBsLrT/Ko/HBCSAoQyfAPCgcRvGZ8Ea6DXkvBrs -9u/WXegDQdZoQJAWaP0MeBvZHOChAARfrF7rJ7oCT+93gXgIOAEZW35wNfO95x3RdFzgKZ9tw/3V -gz1sp6tCCHR1OW8kuq1WxyQpqEGhHbh1629Ai1AKjUgOAlFS21FWs3TvWUZEozAsDPwN0nBCXvwQ -3vCwq7BFiMPXKNaswRpotFoFnsKS9FvfNt4RK9ArflIP+CtV8GNSs612+pkrwtH46xW0xw3LjcgI -hax1g3wC2K7Q8X4GuOjAw64OCAIMxUH4+30FuLgTuAwRnouEdQEXrgwLIzdOV/2wu7ks5QK0JCAT -iy1/LcIATWdY2PQrSLYVRS4ov91utrv+C2Y7xxQAwegQHoQBaYjwo062C13PzhB79gsJC9W7MKJA -OlNoZgwHWbiAV1bbuAtsvOnaANkWAUZIixMvbZ0Z1ViJUxB0EqqD7WADRUjCaIYZiyvQvbvDdDSA -PWWxUy/GOpeMX324ciZMFRw2hpthIWk3fFHkkLGFz0NAKSBu4QCZdOtKRBc8H7r3ahBoNB5JtMMd -7ANjwehmSV/TQ8OI2MK5ILvECE7x11JW6ChBkb+ojjtkG9v6aO+j0wjwYJsVpIuBSgqgjgQWwdJ4 -dnav1hvsGGiZeLs+DtNcss2WNFNfKeSKYMh0g0BAWCbTQcaOb1pQHolo0Eps0LnuZKMEHP4bHHPc -sfZ+m90CdR//NSEFIrpDiJkMrkQQMBDLXqHZDFDrQOvNS08Da5c79u4NjegmUWiDj0MKKAu80RDK -x3wEF5MRdU8IdBMYrv93BBMcL1n4gg4KWfHw60uMQzIdySz9O/TT7AgfM/9XV6do/i0D4dbChq91 -BKvrIANXYJejhDUf1PmjdNZpgcRU/oHc4O14+5Le+FMz23cZAALxUHPSEVigKb38XBhgIUfthjYH -NrgMxFMAKmyGZn5TUI1FmMc5oyf41lkgshwx9R+FZkeHPCO8YUU/C3ywWxM7wy90GDgYP43M9mjs -TZhR8nLeNpx4NhfoU1AvSP8oc9bWRi32wxBk/gHubJ5tcNcc6Cn4/vxys7O1kmVi7MdmS9ZmIKrG -Newzy9be8P1OABbwDAiahtgbEB8bKXBZLmDD7jfoaJp09xhYwT2w/PKEG6BYEsGETLxGUP2mg4+W -WbboW7iu6fpZpV4t8QKsPaZjlShvuGplXQIEAJGwHUSYcs7EaOwQ2+B25uROEsBhjA6dHbBOWZNG -ATXr2dgGkyXkFiBomlYGkwO4c5TuH33JdGybxUAIPTHsnqUeW3k9iZ/rAxJjIQyLNVM9PiT+NL7M -iT1EoqiAuF8jFSjkC58XC1ZjZ7AgHKAUE+I1paQCYZK7nQvP0e0jaKS7U2ako2gML77fYiApQKAF -xDggpptK7TMCybGAOTW8vfBKBdyjcEiTODpyd/uL2mcOoVke1KEZE2hs0G5WSGcaBRV1iUlMwiRd -EVpbPGlzpI5XdRkPVs906/BOaA+sNeg6S/3bLyZw4fiFVQWDyP/rclNv5CY2IZhg7qx0QJgHSXPS -bdH4Coz8eOg0J83waPRYVnczUWB1o2jAhIkJ2HRbiX8C+O70D1mqRCthx+UQsblUHtmPnF5bX+lQ -AWoFE2+htkMSToUFKOTZlQYG2B9aq/8EQev2D7fBweBn2MNWuw2zMR5SNlPQKb1rXbIfoVZVEEEU -yVFPROLthT2EkQB1Gfe4u9742BvAg+D0wGOJ7/82/wURSMYPGGjYdG0dwB0p9/+UJHQv/3QEJrAC -LXZvKjS5LDCb2pgsywOSEF4kuGUzAogseZfLvX2LdgSUdYSLUvXzhTcCPP2UANTc0AObrVsQEAD8 -VQyiq8OLbW0xtcR9RnbDKQbpAJt0e6yb27ICXiEPhf6hZKEFhc+T4JmDPHURS05kOGBoXucCVjub -9MgGNIem1i59LdmpV/QQqDmtHIMXnJNQPScR28imd6wZajAbtTR1CPZ00sButPRzinDS2dfchiAg -ZNZqfIHZHBUNYE5H7iTwSgsfgHU2H8avYH9osuuWfWlvWWdFQibs6xtXCCaxlKuH6nMIgcYReBiT -eD+JBmkYo10ikkYEAyJev4VLS3wyVjlivgp0NRaa4wuCTQhQUbwFgxHe7GxUiYyGFXBWkSWu5HOQ -MwmxXY4GiB0olLqvATudK/B5ElY04PG6mE0j/FS5z9QhsiFAMVa8f6Xm0gFe+qCOPdMHAnQ4GO7F -jWrE3HU1BFO3EiNxtF0GwQQHdad3Q+zNxwUq8+vBiT5l08RJucNRMBwC3EuVaKWaX6Y95LnONTTW -Qx8Km8gbaJzMnAnEIuvbAUAD5Sw4HDy/2cCd7alLiP705NsZLPIMdw0ISWwNOTg0o6qooYTC7Hsj -gGgP0wVipHNn7th2ETgFYylU9gyzQ9EXJEYswjB3rF1ozDART0etzcVIKkQ/ZSVZRMrVGNYfJwPj -HTYZFbg1n5jAbALIYc4JM0jc+6x2vBRbYe2ssPxQQVxQAIzU7OyJVwBdGxM/JFL2SpRff4N98AEk -7CzYdRw9jHzChGYOaApVIPwQms0mbI4ZuN+RGvYYQIkCYHZq/LM3zCwUZBRpDQQbkEBXYqYwuSQP -hHawLHZUGbQ4sS48HZy0RpZGts147GR0QLDSO8w1/gMjpRuwxw8cQL7w36pWpsKwsiNWonkYDWUy -qwyDVjwbsXdyNfxsBTwgMl5Innf0ETYL3SvWGzO7JLReJoh9p3TByllnZSiLJ8F0SWF1ZamWeMBS -knDBllBgaCHkegsGcYRcSBYEXfF20yG4dFRqC1kRjX3EpRvdLgPzqwb0iQCrqwDbNvahaLsMqxqQ -E4wbv9bcyMEACO5UwDCJL7WgFYUveUh7O7CwHNwcJJsb2HEWB2DigVsGzGv+1qzoTOdcaCsSbCAT -nlwy1kEZ9G3LHM6TS074buEldOdm2J+JjlyMNHyYy2baHJgFlCwFrIzbsv12f5Agm3W0AryoD6Qo -QvmgBHco2eINE8uE9D81aKPdxFsaa2y9GVcUVbvow6Ve1hO+yGLAd9+ptHs41xgQaoQqPhimztyB -iUoTQVWQ2AvasrgoDr0n2wvabCM9xSisjtTBJnstVCNonHSHTsYV1aOaFIznm6GEkEUKaDCi30g3 -kHxbgHSyaLB9WDMZqhkOUCGigSaZD6IWWlWZqw0Xr6E7DW9b0QzGc0A2E4AH5BaPRygrKgAQLfHW -1LdWGld0b7wQFP8pDG0aZoP/AnZhl7e3v191TopIAUAIMHxKBDN+Hm5032L/lwxydTtAxgYNRusz -BgMKRk9PxBIOGqfkJlH8NXrSOXw8CgsfT4gG2qJ/M9QG6wWIDkZAT72ZjNXbFXhrgCaoRiiPwqEk -wbW8qOn4yI1WK9gD3JYVQADkFsCxYeADAH+xIYkuA4++P/CAnVzhH2YTlUxghdi7CHjvmq0AmyL4 -5CX0ZoR52Op3Fx/8TdxdHYOBJnbY7yrQ02Y4kHdWjYzQZRKlWKLtEWlktNasuQQLLUauV8gRDbhi -cbgKEhLtTAQG+GEhni2Tg+OTVaQEI1sbYIhTuHikRGRzO36g/VR4xjoZ7AsRUGytZD07FfvtRNk9 -O78420hm4BA3ETkcEoFgL5gQRR1o8MKxngepakQlqF5WNcDBSMYiMTpXXXMjXdSeThxQty3cZru3 -U1NEKlNmTdgfps7APqlDFufx7bXdAV3Wag8YoC8KW5ztUMYNZLdgNvdtI+wsyAjWLBNcujiEdzUj -U0w0hZZ6fWpb2PfYsrJ036Db2UNqXVMN+P8IuSr9PIAnAEcsTOAcskTrA4AXqQhTSzwISKVEMgQU -0k2eYAhpXXKUHDI9LQjVKzZSLTpi313oJdF1AjmhmA5GgzgBftx/PvgQD74GajaUWesRiw2QCcdW -re4ViwmKqlkIrwYCO2nQVsBeT3w0kjxFdBSObaEaG7IIwLX4AhNcbpT0CV388ArGmtrRbQlT7+R5 -kD2LG+AsSbXqCf92VR4/Y+xnzB5oyBsIrFk7w1mkprtUFXUWH7BTaUzqI8HXH95qKE+Rdu83cDv7 -dQtooCIZHpiLNWl6neyii2gWs818gLM5Wx8X8hChcFkMBAMVQw4E94Q16xoIFv7GEmYaDUBO68SA -pDWiJMT7SwBSGOGtwd/TiQSPQTtNhAl8G4MKHDMHvIPDKFNssySJA5nZjcathVWxTXIFgzNcJHRr -neoSMecvaJhMZlORoox+ipB5ajO1hEwmhT+xuV0Mnlj4uk+OHw91LfEsc9wCSvjxXxdMRtwe/zBT -ZISubIxgOxSLGNC2PFj04fdu1mo7x3VFLhwzuxv/AMfhofONSn7YFgEKSYagmS+2AmC2UpjfCI85 -sMhmMfxeKIEc2AF0GngQwF6ezxAb46L060Mp2CMD8vx4CA/rICH2yIEc6Ad5WYPqIhcKhUula/P+ -ve+sMX5YnwVkFBG0kTCgs2fs/LhmBhpRCl+dQGCpFlZuePpLvpl7FOsbFh8ccME3CA+xQPRcFmrU -EieCRZgSwKF0hWFqmQUMnoNYXTpZw1e+bQBiiwPvVjT/VaAOOPExHCC6WaPGquGZaYMOCHpLtOt7 -wI5fEGKi9cIhMmCW6ILEo2jTEBdaYYv0iA2mKCEHOZqOtW8sDDAOfRyDBz9/SPACYB4fQHQcagzp -cI0Gc2e1MFDClSpZABKq6FQDo5ESQUlAJM5fUaKrMToXrTPRCIDiqaX4iMACgz0rTQkoTYBkgCiA -BLja+wxXNAFLdQShG0wE5GIJgl/XljCQ2KiECAhGAJbK/TeLVQgai0zAW9HeZCtBEAJ2IoE5d9TG -HV6NNBAIw9s+elZqbjLbNBILtziMrwBOo/5vUfLZDYvWK1YEK9GJFSC1sVu1K0a9ELtX/gzUkkS/ -gIkBK34EarFGd5Yoe1H8m4hWtmYld1Lq0hbajGugsxM/hBs2dHbICALQmiLyeQnYvRUISAx0LhdX -UEkjxBcEqsyG68bCcFszbEWiRt/+Pw7MzEgz0jvCVnQzi0hLynQsiUL/L9tQFAIIGItxDPfeG/ZS -g+bYF7C7/Ykxi0AcIBRRPyhMcDPAUoWvJ7icCAVHMOyQAH0JZqED+PZ0OotGEzMXJEtD7bYsPRQN -ClbmNgjptnhzHhooUFHkJA3H+AhgbgAAVOJWsDVfLQMp94oBDVBmYRemOsF/597BYbvNGDgK3ILA -O/d1Ck3fYsQ/TmQgiX4YzwprfENvYCDwR7x+KDl+JIQOcI1deSQQSIFqGGGEpGubIUMniYY+/PcX -fptMJYl4FItWF8+Jegx9DLR1b/9+99nHQAwBePkIfFkED39UH7h/YWv/EdPgiUoQUtdRN9ob0lD3 -0gTu0/aB4sBGZVJ+KMwZHYL/NXhBT1Y5ehR1DyOxBvCWbg5PC4zwZLNWG8lfuPppECrPE5ZxU1UQ -ux3Kpc4EBHYK+QOhE4Xmtj4AE/ADVCOC/fsOevoEv/tzlcNLvQXB4/uJXB3w7aEZiQjIDQ+HxBok -NFvh4Y0QOBkEtj2ISbe2o20eiQ3fQYsvBYsO9RvfxooRHAQ1FhAEg+EPQuDc3yixLhZ0FccADVXd -fXfJvGwY5Hpy66Iii1AQwenJgd24KMEIXXYYJNDztbaB8CQuFwW9BG+7ws0RSDPJjmYIQHaLXhzY -HremiUsGib0fAxOJ93+Jt3ZDBMFmA8H39YXSdCHHA1Y8Xcy9lNHdX7hoZ3Mbn/bBICWBYykHtByx -YSYc2HHeKrh+2il8X6Ri/XUYZigE+6MCVfNaLLa1DQqCApIiAU8Al9rsaQJzoDONSDbnIm0CUh4S -RFQMyTfbOvkL2Aw54wh7wZx5LQJj5O3hzzXemkrcweEYSAvk+Lpka0k0CfhKVqEw1m6DSEKJBjoc -FJAG7G9dgUg34hADyolIOQpILpJLvgibLblkC4Q2P5Y53Jg5SDQSNoLZDBHr5TNZ6QMhIAegpDvo -h2xoAnUJi8dlwggOzrllp2dyamN3JrPQpBZQR27HAQOWEB5gORZITzfOlqXhigobUOHRPlaTHCBH -AgQO0mGHECYgiSizEkJKGCEfstdsF3hOMPMGuPg7hmlYRmkskHAsm80KACVqW5JvKwD9DEMBKf3w -i7klBjgLRzTZLLtGAgO0Nu4tN9Msm2ZotDU1otfLLJtlETZL7Df4W4sHksB/01fyKgGH23o8iUNC -rcXWFrIEDwQFTL46sMEb60coUqZXygqcut51BnUNPldPKtuNdwTqKMfyAUY0AjBsLQhsDjjuUQgg -1oUJ+HQOMbLQH4FkbbdgRzDAw9/8nWsQX21qqmRjIFDiixK+SfbYjkYXcsEHTyhcSYEDrgUYGl/Z -s9IFQ5d6VyiMkEXuMQbqw3JAc9ActrNQKCgfnyusgXOnUR4uojZ1qxokAiAD2JCYLbweiV4svDjI -BHrZi8U9qgCD7NBadB+VOFNvOFX7bq2xNSlDsmsSSC5LNLb4ZgJeEDBWO8iwVNeWf9cKFURzBSvB -SOsFLAce8Ll8AYwDg/gJGQyFLDDUb3BAfhiD/QNzPIkN15Oh0JYNxuR//9/2SIoPxxRMlIvRi83T -4oPFCGML8kfae9d1MYk4iS9yzusEN6+mFvitmQeLyNHotQFyWeCm+4lLGHeRY1SD7QMZAWx6/+3N -HAfB7gPT7ivpP7MpvirUUR1BSFFSFyd2t41xjQ0wUQ44Us46el3DRxwkXCE0+NpRPlDi7Q8sUhDe -EDgcOfMV6BSJrrXvXMBiZuxYcQZhFHWHN+QD+P1YFHBuXbzOIHMsqfr6oAY9ly3QP0wsT/Z8QOJC -m8EnAPLUiovOFnhXaoLhB3LqEDPRr6K6tbf/OO2LwTvF+gSJbFxLJgFbYthBi4kD6UzSF4dNdG68 -KsccBYWdFqsbvmt8GkQ71nUjv4t7KIsUvmu8GYvXO7EVcwcrwkhX1x3bLmQr8nOJNXVntExB7XCh -QkgE91M0KA6s+2JrB0cwatajTDocbcPbMSvKSf9LLAcEkJHv9j5VdSBi99Znm9x88k6LzsKLyKRe -oWGYcLALBclp6N5gdp3CO8EFwT4U+yUKrUQwJIEC86WLyi07PP6CjeEDK9DzpNpcJbvRtrdEA1IN -S10V8CsMlaEzXRaJeBwpwli1uf5o/UMYkwcqOZDHGJYOczgyDxnjKg6S0iX/bLE5uj8lyCCYH4cd -3bHQtwbW0DzgCIH6oGxdwc0FE/IFegV9H82Z6BtGjYQIAjp3A3w2ztJIKPlQYQyNBSxgvvEOSA7H -QwhKA0bT2PvrCK5xU5IIEXm60OgKg2Itc2hZMkymkt++NAYDJToiLSwITrGL/Nh+bIoYpEsMxQSR -YQjDXKE1CAOGamdk74fdcpgwuBOhyHMhPDRzhffaxzFpNaA3IPSl7XRy33AaJG9DEI1TNmdosFFS -NFfx41BdAzibUUAsEPCFWMi2mSH7COYFguHDV09l0DTiHzc1byfezgJdD4N70lk76HNr78ejM+NK -OwXr+vmE5t5rSpj29PkHl441N/ou+c2LyUCOXHsHf7kUI8bmVMEBjeY0u9tq0Ha0VRCXNHMbySy4 -1nYr6tEMRYQSiu+2wzVxQKQ3L4AjErnNeDy+0HQDM/KD6BLNWdhfcpcrJPgLH8ALO+lzO5lg3QJ5 -4AQfMJ1cezRy6cnsfHf2Gv3uVYsMjakjziYOFDXea7Vi1JAb1+lcRjgVHOGMCh7c6936A9A7Koep -ddMqoUaJpTkQ6ZnwfHMxd4KTFQ3aHYr86wIP314qAKgMQUiZj/x19XeJTBxIaF56goWY+kC3vRVA -JCZRUECNrWMzZt8JLCRRElI8E/DXcDY7P1FCBUOByEYBa88UZcsO86wJB0AGD0WMJHfS9DgfFUwk -CmuzjyYZCCU0z3c9bN/TgJ88ICsceVDluWMIpE6EVwQEPMC2kAYpSA9zLVoLC15rPDCX2PF1q4sE -0CudOANWTEGrOXjozk3urdGpr+dRXEmxe12JZp5AdFZdtlQDLl6cAB0nTWcGicI+DSMYsZAmDgUp -zCEYBuZrJYnSACzjYC7JAKGdz4smttt6bWialtrplUxRdxJozb2F2hewkMNuh1yhMwYww+DNtHFo -UVxh/csz7blZ4xhgez9VUfLkksF++Ndq/SvRwwPqUE5LWLYnu0yNMYtpOVHQtwibaysBZpLqLxVS -UdosgWw6Q4Uyrb1l32rHQRhAg0tGQIYw92NISFGJeQRGRBg24cAREUsg6LOsmEVwjfKEp4Qjgb1B -FVLIxlQ+Ay7eysQAzjkFhU58QQSTiuCewb3R9wPug1FP0VqCKYFYuEXwISwYE5/Pnmr8UNJQCIGU -eZAcQuEdUozPK44bCaRAGJ39PYyy2XUGW6VPUesYbJGoOtciaJTYMiIkFHyeXasyRruRUgbhwGXd -UAY1z4J7CWkA2v6BGGKFTP1fLaRzISRMEFkg4YDsGFKEPiOFPUIJO1w9Wyl5SFBSpgcMd4fX60Cm -ZudBUFZT98hyQnRLU9F0N6HtI7S5e+ggNy6JVgR/ZFuq/FAr1YtuCONufT7GAfKtZggYMbXwPTJD -LovHTFZVxWmyNWhjQ0tWmRCSXgo7nYQJAemYoJcNQSaBIRiRU2PtqwlPsP5FQ0g3nsJeKkP/1DkU -zTpyuVxuA0A7azwaPQE+bJbL5VBA90ReRaI4OsyATbNWuDlBGyADXFLvDKIU4AAXuFZXGEfAU7hS -WGndi36vUS5YRigYDRgIV9gBDnBj6U8xSDUWt7vvQM+AG911CuzCDOO7d7HAXPnbD4bvEVWB+7AV -mY+7+L3DcgW4CCvYgg+Moa3xW7Ti6MHt22EQihaDxnL2LgobrFbxA/kI8sghhxzz9PUhhxxy9vf4 -hxxyyPn6+/wccsgh/f7/lWCD7QNNvGSf3rbOQFkVFhJGE0h19G3sbqWxDbnx8vfxTL93q233CIs1 -9/fri/WHEzEOLxFbXRdbCV8LwQgm+DEJn5UIUG5QtxDKTdZQHwhGx7s0dEwEww8fHKHRFC2pN7mK -3nFXqE+jRYhQEFoMiEgR3EFvBHUAAA9IGMNXPBwG3xR/IHbBhKGFzgNGkvCiLUFjVsjabgzC1cLm -wQw0wX7FB9unabwQwkYsB4kzxQ0o0E063/4GQoc2fmzoT089HBqztiPQnc4QCgqSbGr5g5EoRnos -iX47Swts5YwpKyJ7rfktldpWhYkGZdxVDTu6LQpFlFZSIk0RT1XdU8eAEHdIfOrIo34zXSuZHLhI -nSgNGWsFrkCumaMw+r9GA3KldBNJ99kbyf1iqzcZAoPB701hOJ2pVqLPZmMQuBK26q2xYkWyRVj4 -c0TnYsXDQFwEug61AV6xi+0wALKOnPuSe8/T4NAAxwgLyDZ52eiu/eAsQT8KLHK8roVjS3Xf+CMg -CFbISRh49CsEJRTT6LiCS79GbsFFK/hAigE0WyTexRaLSY+VCAZ0F3rMr6gQdNXgD66Lki7WSq8F -Ih8C2qK6bUCvRcOo/+O5IWcOJx8Hgr3PHNLaQhqvSNz5hgXsedDn2Ahv5uQjvosETLlNBANda629 -yM6tkbDUcgO1qDYz19OOJBgMzfVFzGVeJYwiOZYDRAFpmIRkDEQEhJsFw4XwUmUMjQzBiAB5gBBB -2ALkkEOGDAwFQwEKDG9+Azfg2IBrFdV1A8IrOVOTejdA1h8NrxDt7SOWsVoBVeL5Uc2FlywtoLTZ -Po51IT4wO8ERPTip1FQtKQz7ceqIsAjrD39nhtIkShsUUoVyYpIhMzI8DG1iG+zkBl1jYSJebons -kI9intsB90kYIZBC8wmISv8R9xQ590FIO1AIZgdOYHN0PAxmSWHPKAIb0mA3sADjk/FRgeBNCogV -YWDvCkJIRL32LQNPwM8UiysK4gMFjtHHQx8rzRMXJ4mQNRGq9BTDIPDNdEoJMBgofEBiyI/AG1Bl -av0rzVNtrjA3VlBJEOu08iALlZiKiQN/74eyPoP/B3YVPzyD7whneK8EkUyJTDfqUFhCULaLstgx -F7TqYrNOIDr4S5neK21uPPlTK/2La0ZYoTdk74kLW/4kEwmPEkEBi2QiWzv+s9xu1ZC0vnFJA0xK -0C6Xy22OSwcETCJN905vT68MgFkt3/nokUWQDCBRU6djoXhsIPcTdhBVN4NEZ9jbdQnAj4+OoVtZ -dRyyVlXcQF0XWY26U+sgUlUTla4FowET9LbaMtHcotP+NxoTtX+JW1NSx0cYdI2KV/jtXYo0XV5M -Hvt0BoN9i5puo5gMH1C+wmGxsJgwKc+7yv09gezwoowk9Ab8tCRugX4Q8O1Xz0QDmqZpmkhMUFRY -XGmapmlgZGhscFzAm6Z0eHyJrCRvv1BigzIB735chESNRF2gS28DQ0qJuu05CHUf6L/y1XEYgZRu -wIkpiSrGFl6EFI8anBe5G+ipLxGNmDtDOSjQDeALPUGDwAQmdvNuPD5tdvnNcwaaYroPG/0f+yu0 -eDkudQhKg+4EO9UFO7/Ntov6pSx2JVT6vlGJO+7t/8bT5q9zEo1cjEQrM3glU8ME0REZIrTBcvJv -laOFF+hWuBwMRI0DK/G6QHm6k82oEBGiA87liPe3NvgsC/ZKhzPbA0wcSEkW4X435YwcF3Xv3T3I -QF8xi7TN/wHb4dYcFYyEHD0oPN6OdXKMDYlceEKJERIjvmkoexwIQzvZcsVXNjJ2u4vf90KMFDWU -iSGmocBpXQNxJHOO6HseYcffABJ8xG+nxB08D4+BAjM0hyJRaGWHDbm3wHuBCjtJhdLsKz4gwfbe -Nv07TQ+OB2AUOFhys8jWLC34bDPR/y+6OAPfK9NFA8871/AmdNQt0RrXHCBJy5n+nwS4jX0BO8d2 -J4PP//fAbViiGi3HbhhBBLtbWFiufb7FbeAfByvHEmPLUrRy7aEkvzvnyFFftouxfAP4gf+ITx/O -2NjvJiArLMIvjajewd6UhNg2iTgTYden3tkqdDhDiEygtIQsmth+EdbLiAUxvca18Osl14tK/O+L -9dPB3Y2Fb0Mr8IkUO3Sf6wlKGIoN7z0o4PAGj//tDUfoWoxuitAJHCrTiD0Db3y6MYsIDJF/cgfG -Du+KbmPA6583KQyT8XMUgXYX/qP+yRvSg+Kg9mCIcesgkPtNVyAUweYCihQxDC3erR1sgMJLNDEh -sRa+aLkE9g6HJEe62MTWRuK8tDsVcx7Gb9Fdt8UAgzB3iTmNPNWkhG5nOHEEhh1y5tUUel9wZWKN -wjGBhcJ0CLQW4fYz0NHoB3X4WEoO0UZoOChgjByNBe8K7YMxJE8j+ss6XxiD6AQX7Ee5T4gmK985 -M4xx4lgII3XcdRXIqaEOT0ogK9LCHKePj4dSkEDrwZowvY1XHk6RG0KydFff1zv1dBeRLAF0Tfu4 -gLUWAQwKhMCwCCQPXx7LA62jYThoEncGkAZkGAtfNHA4gWY0VWQY8FaPkzRS09hoGGPYQe4CwJhi -BBVVUowb1BJwQIXTRVhEIeAk80DamWywTChIOHtGd24nFkwQZFFWHlu/B/aoUlFLdSQngzoWCAAY -gN+B/Wp3Ez8sJ/CWHavkT1HIhy3ZII4e+3UfHlkQeASO4yP8dMhekg8C4C8jwM6AwUu8QpglMJic -RSMvLpAkD98NSPyAd4No3gChTAq7m3IXnIkCEJTHAVARxwJxOuDhUIxAyFHtDGoAG7Bja9d7wNt+ -nNp2/cF3dgMVLBFE0PVGe+876FjokYatwzcyIPcI6iCF2kr8VhQrxQPV5jBWllSIX4I4cA6LSzxV -BT1uAm42QzwSzYv3pKk+YlKmWcqmO8e/cgPFF0ssA/2iCnV+0bmtakFEKA2RdVvYnW4fczTqmivu -nxCEkOXkCldHV9RYBzlWRzB8zfdaiy1e+IR7guSMnHpRsIphWr5SXTAoVIlRcjUYvXjBEl4fzBdu -Nw5Z+YtpnFEgO3EwHLtRCzc4HTvuUUEculzUPzlzCSv1Tv7OSSj3qqUxzYE2fEnTTbQOHCwgg/hR -J5pLPCKLSUEKKLHVEYulyBpb7O3e6QvWRx1y4liiVzDciL/BI8rIihzOjTTOLISOuAK8c8IyTgHT -6gRnFqB1Ecc5BL4j3T7AD2sMnWBeBDYDy/0DyIE4VXTHg+MPK8P2FeiCNDFODavLIyaZSLakDw8g -yJQtaTScMTNlI+QFAZTPLuwBeDvDcytZGIP51X4OaOfVh9dBJi1nS3yXcgc8WU76bI7WqM9wwe7H -9RAKuKJI15QH3+BCvEkoETv3cheLGnywf/dFig5GiE3/BoPrAusB8O1YI+sncSwfO992Ezv7WyuL -HRwARUZPdfYYKBCWbGcGS57rGb8GCvT83AQZcEVJgWGrH7EjEnI6DnIz+WrrHLVI2LWcEEkEbY5f -FRN0K/M+rPAR4Bfqsq078w+C3CcCzc22S9h0LdnFZQV67O3B6x7ZcwLeOCv5MzE2xuqNFM2awsQc -+t5BXIIWU0YI6s+JPiuskisUZ1YNVukAe+HUc2IgdFZX2GxWpM9a2712gFwocj8QlWoy8mb+9YhB -t7adaAMrQVhAizE6eC+xQTl3X4lBZ5r9jeKmd2af/yU4fYyMjGwFPERITFvxit7MzFE90wtyHPt9 -C4fpCy0EhQEXc+xNJW5dmMQMi+Fgz1CpMCPbw8w9UFxFfZcffGr/aIhTEF5koaFQVNx6S3QlBxho -U7lf/r+lZegz24ld/GoC/xX4WYMNeKM/7Si2swZ8FPy0Dbh35Lk98QgNAGG0oQQMd+8K9wCjgCjr -/TkdkBh1DGj3z9zK/l1OCGEY6GgMcIIN1PtsCHAn4qGwP/OU280tUWCsDAmcUAOQR7RUNKBcEPUX -gCFfBDIATqEUu79BfW4wxYA+InU6RgiKBh6Qb/s6w3QEPA3yEgQgdvKLu2bb1NBOpLDB9kXQM+ft -rWoRvtTrDisgdtgsFS366/VqCliV62jXoJ5Uih/3kTMI9IZfGGtF7FQJiU2Iy7C94OLcWQou/3WI -HyAVjYyNYyQFHAxhdu2NmAMELC9OEi4krLCsw5IA3fRgqJLtfPBgAABpvgKpVBUQEZqmG6QSCAMH -CQZpmqZpCgULBAym6ZqmAw0CPw4Bf/t/kA8gaW5mbGF0ZSAxLgEzIENvcHn/3337cmlnaHQPOTk1 -LQQ4IE1hcmsgQWRsZXIg7733ZktXY297g7733nt/e3drX6cTaZqm6bMXGx8jK6ZpmqYzO0NTY56m -aZpzg6PD4wEZsosQJQEDAiEZkiEDBGWnGZIFAHBft4RZskcvf/eapum+8xk/ITFBYdl1p2mBwUCB -AwECpmmapgMEBggMmqZpmhAYIDBAYMhGtsLn18eEJCzhBqerrxnkW8KzAwsM0QBBBg3muqoozDWX -zgMAv12AD0NyZaVEaQZjdG9yeez/n/ogKCVzKY9NYXBWaWV3T2ZGaWxlFbJ3bxYrEB1waW5nF/YT -YJYQ+kVuZCAZwoK5/3R1cm5zICVkUxcUYGA/WBNJbml0MhjBYNU9NjNcHIywjoBSV4iEB8iyGWx8 -D3RocWDJkwM2AC9McUxxu3/7V1NvZnR3YYBcTWljcm9zDVxX/2/tb5tkb3dzXEOTF250VmVyc2lv -blxVb+3tl25zdGFsbFdpYlwSvC1wYb3F3v1ja2FnZXOsREFUQU9FaXB0f/v/7hELQ1JJUFRTAEhF -QURFUgdQTEFUTEn2t5+XQlVSRVRpbTsgUm9tYW4LdqFt7WhpCnl6ijx3aWTeWiHYIGwTFnwgeW/f -frvdjCBjKXB1dnIuIENsrWsgTmXC1lzheHQgvRelLnVg23trhcgZS2NlbBUcaQzWsHUdaBVTXXBb -Lq3Q2gd/eRYybAENNtbcLmTOjw8g6CA3uxvBFrYAS25vdIkna4fN2k5UKhJhdpuG1wylZvESbMoZ -7DW2Z8h0UGhXdtZ27A5zHXF1cmQs4+8p7LXtY2gFYRNiQnXLumFDO2k+L3JHNwjOKhGBLuRsyRLe -sDCYBHVzZTrjN3ew2UwGQ28RV1xJJZdtZzJQM2izVuw0LNkonJgoUyoYDCs3p8J24Wt6J2Ybc4cu -c28uAJtFjrAbY4kcuAvhLRTpYoHgWsImJOiLqLrX8LgDSWYnVG4srnbaVniYyRRpEmczLCzG2wR5 -KktAYaztLiV0dHZzLCpvQlYYwBiGZVF3w9tvy0v3U3lzX0c/T2JqgKs1GjsPX0//2CEY2y50W1xn -D1I9X1MQcNCt/VxhUztkM19GCHz9UsdzIwufUHpncmFtTve+nqECPhMXaSEPRphx+ExvYWQUtyoA -1G3u3e8lY39Y4HQaX80GrOEdNTsLLgcjfth2nnInMCe3MTAwgAsMXW1kEvo6NasjXm6DgAAyF8mx -c6002BhF/1sfG81MOyZPyndy+SCSDWvO2ekWJx7tSSgcKV3HPwoK4O0fXmgG7FlFU0dBTFdBWQnf -sYewby4sCnAtTk8sTiKksNZFVjsrgxxxaMt3u873dwxCsq10IulSZW32yu9wRylleGUiIC0UAt/C -scItziwubIQiT3et8JC1YgMuADA0AxDWsJVudURCG1V1AVsZaK0J210CPUL/lV5JOlzhYXnBs0dh -T7IZKDsyS2V5ORiMdNMKC3VsZP9jSayCe+0gax1LkoOFswJu2SPbjCFGG4SOU8BjgyoA97u2JYzK -CnJKd1kvKZ777yVtL4BIOiVNICenO02ZS9n1E0dmXFgK2x5zaEgrYWtbizSLZP4WZBVmwNad8QBu -zgCRZxZfFqTJggcPbycPLG/BGKzzYnVpX4X3HE0hb98FQ97DsAh8GgDMB1xqswbCACOhZ9ZoemCh -w81hSCvOYNhhxTfhQzxmPMUcQ2ZVD87QsG0XZ0dvrnCR6JHse6Zk+hbzOhUKGO3TIwAuYg5rg7Wd -YCU0IRtk4GEVLDoDOwxkaQD2caCRxlhkI01YS3IKFh9jvmQFkvMTUJNkscwQMqYiE9lKeu9+ESfS -F8KaLWsGUzLgHYF2AEFvaHN1CAYGX0JxhwqZcCGx1b0bbb4/O7HQIjdjfWW63t0DzXRybcMZm21B -cuhYGE8EY/ekZhwFYsUbj5oxvld6JxAfx08FV6/dwtVqFwhtYmRMCZwRcyS/K3BjRWiggfh2WGRQ -2YsIrg6iN38iSWpob1mV0XlPaVYLxmJ5VFIYm0mvbSknY0QX12vtQEsCpR9CxDs9vB1+ZKxuZWXw -Yz8YnB42h+fxct4gPW3Z2xyxCmuXFxHGsGENg3IZxejcFjgNc0eOa3R3bmVwByJoQVpQ0Bxc1otk -L2LCgj49DK0mFa3NW29vmzE70SccGGr37IXNgfdYeU1vbHM/WuHgmHN/DZCFY8sOwS9jXxh0poAZ -tXlaX7Sm2Z7RBHxz+HsD6Nzam22ayLigexvnta9kObpOYnwpC7hvBt1mZvVlYmdzEcMwHC03aZkt -Mcsa2rAhn3JtLy3hyA5wG24PBazQluh+XcfDZpujA6kJL+IdTbSMROMFYPwBa5qzI1AABxBUcx82 -yMmmUh8AcDBAMkjTDcAfUApgglGDDCCgiBlkkME/gEDgZJDBBgYfWBhkkKYbkH9TO3ikaQYZONBR -EZBBBhloKLBBBhlkCIhIBhtkkPAEVAcUBhmsaVXjfyt0GWSQQTTIDWSQQQZkJKiQQQYZBIREDDbZ -ZOifXB8cDNI0g5hUU3wNwiCDPNifFzLIIIP/bCy4yCCDDAyMTCCDDDL4A1KDDDLIEqMjcgwyyCAy -xAsyyCCDYiKkyCCDDAKCQiCDDDLkB1qDDDLIGpRDegwyyCA61BMyyCCDaiq0yCCDDAqKSiCDDDL0 -BVYggzTNFsAAM4MMMsh2NswPDDLIIGYmrDLIIIMGhkbIIIMM7AleIIMMMh6cY4MMMsh+PtwbDTLI -YB9uLrwyyGCDDw4fjk6DMCQN/P9R/xEgQ9Igg/9xIEMyyDHCYYMMMsghogGBQzLIIEHiWUMyyCAZ -knlDMsggOdJpDDLIICmyCTLIIIOJSfKb3iBDVRUX/wIBgwxyIXU1yoMMMiRlJaoMMsggBYVFDDIk -g+pdHQwyJIOafT0MMiSD2m0tMsggg7oNjTIkgwxN+lMyJIMME8NzMiSDDDPGY8gggwwjpgMkgwwy -g0PmJIMMMlsbliSDDDJ7O9Yggwwyayu2gwwyyAuLS/aEDDIkVxckgwwydzfOIIMMMmcnroMMMsgH -h0fugwwyJF8fnoMMMiR/P96DDDYkbx8vvmSwyWYPn48fT5Khkhj+/8EoGUqGoeGQmKFkkdFQyVBy -sfEMJUPJyanpyVAylJnZlQwlQ7n5UDKUDMWlDCVDyeWV1clQMpS19SVDyVDNrVAylAztnQwlQ8nd -vf0ylAyVw6MlQ8lQ45NQMpQM07NDyVDJ88urMpQMJeubJUPJUNu7lAyVDPvHQ8lQMqfnlzKUDCXX -t8lQyVD3z5QMJUOv70PJUDKf37+d9A0l/38Fn1f3NN3jB+8PEVsQ35rlaToPBVkEVUGe7uxpXUA/ -Aw9YAs49TeevDyFcIJ8PmmZ5mglaCFaBwEEGOXtgfwKBOeTkkBkYBwZDTg45YWAE5OSQkwMxMA1D -LDk5DMGvoBvhotPdZHmFWkZc6GljWtZVb6LScmXVtHN1YnOxbIW9EmJlZCdLRhYLCXYeR4hLkcAj -YXR5cKV4Sc0UGx7Llg2Mo7MoL2Upez1jHwOapmmaAQMHDx8/aZqnaX//AQMHq2iapg8fP39toUgY -xW/8UoEqCnuQUAAEjeCAgCirfIJ4lm4sBEWgCVwut5UAAOcA3gDWy+VyuQC9AIQAQgA5ALlcLpcx -ACkAGAAQAAhBdvJbP97/AKVj7gAVjqBsN+9elB2YmwYABf8X3KxL2P83D/4GCNlbWcAFFw83LGVv -Mu8GABfdzle2N/+2vwamphc2c64IDA4LF6b77wN7Bjf7UltK+lJBQloFYtsbu1lSWgtbFyfvC3g+ -sPcRBjf2ICalFed2iWgVrwUUEN4b2W1Axhf+7iYFBna7+cA3+kBK+1ExUTFaBbEB+7oAWgtaF1oF -1lxb2BBKb2C6dQVz/+u2VBVuFAVldYamEBY3FxuysVgLHRZvEdnd5t6eXQNHQEYBBRHNWI3sZGNv -+gv5QG97g7nXuhVdeQEAEugAczODRgsdb7mTB/lBMVhIUlgQBU/ZZ66FDQtK+lHfFGVk9xv55BAl -EBampmR1FZUXYYB1MwsKAG9DkG122HVICxcxLmhk3wUxb+rBDOYJsxWmzwuQfcMKWRcFFN9z54zH -+wojWgMLYTfMMToXBUJXTxvGGSF6/pMIW4Y7rL8LtgWfbyRLHSHw/HL+YfaGvQ0DBgTJYEla2G8R -B+8lm70FA3cL9xs2I2Q3+QcFISVb2OcP78I3G3buSQcF9lct7M0SD/s3Qjh777nZBwX6x4yQvVkP -IW/542z2WmoHBQMVQw2wZQybbxmzy4JVb0cFm3Q6pWxvgfK5L9nMAWtpdRbna4pxgW8RE+xab5DP -Jg0Fb0dRMaTZsoYAW291GGGvl28Db0wr28bzWQJbbxd9b4E9m9/NciYX2CuA3w1vSZMlbML8+T0D -b1rxIiSS+rcJAtlk7/tph/bfGa9tkOtS1xG/L4xJK0s38YcyWq/oFehVnxmTVrY38fMigOTcWgsM -D5ek00pvZusLsm8htQz3C/43hL1ksOIJC2IgymKHAX1Gv6y5QADASAl7AbJoIYoE5bt0dzCohd9w -sAFNE+peR10gA2E9cwkhcvTCaCNhZjZQfSAa1Eb99zFzG9QPDf+CQ2glMU23ue5XB3o/NWQNd2yd -uc91ASAHUXQZDyW3uc2NLW8VBXkHhXIJus91TWNtj3UpeS4TQ+a6rusvaRlrC04VeBsp3OfOzHQv -bgtddRtk3dj3UUdDwWMRbCuWvcG+OWk7aCv/uidsyLcu7AQIsO8ftstGboMA/YEcAgMOL2gzXFAG -P1OjK7uw1g4PA30AAkPhzQymo2cjFJ9kIpApCAyhe92XJ2wDY/9PeQPppoTDO5lhGWmwrpswN39z -OTpgoLaon4AIgVC/WbU82UhYZe8T74kANzdh38l2g1B1RGWE7CFYcpGzeWGM3DQvdwMBoRhqAP6D -GTlLhaed8IQBeQqeAEJJDyNaZSmzHSL87L5CAQcAMm8CBIAARmHeRzCeDW95oS4BPFBIyzWn9gAf -6w6SkktiD2erlMJY0iEb7zQk95dJbbvpi2kz3WVNcj92BXeVvtjnJmNVJWdbCXlExpKxA2aPse69 -j4d0D0MNLFOR9dxl0UItCTUV1gKsDQFrbpqHS4CdDgDrbX10Dd2HBWwHX5dy82fZR9WNcwEzK1AV -BmlkDDEpI/ayRYZr7FN7Y2QkQjo6C1+EDARyA/cPZgwhV/8dCJxujGhlddV0mRJYyRB3e6wSmgEp -gmd6cCAZgS2D3Amue7dziWMBeWYNAWFQ+zV5jXogArAAAIoNuJzEAFRQmEe2AsWmbWl2lgZvtdu7 -Ih1JbnRBFkRlCfHvIgHLDFJlc3VtZVRo28i2FS5kMVNvAnSAXiyKeTJD9sF2kxxDY2USTW9kdUSo -WNn5SGFuZGiQqcLFiRnPcg6KEkMQDWCDxQJFSEFJ8RwVL4xPkA0L6EFxrZ+B4pslH1P3DFRAw5Zt -IXAwEeag6AzUDUbMVR9rULhf7oBjYWxGzba2a0w6bHOVNW4yFuzF/oRBZGRy0R+l8WEWEAYVChuQ -eewNEpNUaW2txQZCSED/SgsVSxSISdFBYlW0YyxMYXw70B5gEwTgQXSfKAhJvip1dGVzpAl/IyGV -E2xvc4Fuu7C7clVubYNEHEQyMbDLfp9Ub6lHiT0U4gAceXNnxF6omGI0RXhBECoG5mYlEA5irZ0d -aBBRCLwPudh7wzGRMAzzsBbFhBxPNl1t1qIYRboOhtCScZreJB4rwiYYvj15U2hlpsUTC+g0XTLr -MAs0YQbQkKjxO7wEPkNvbGgKT3XTJMyW8SVNbwxmKDyEjUlC1kJC3w7rOEJrlGUaU0xpZEJyo3Ga -7XVzaHb13DRVXMe3QtsHX3NucOl0Ct9luztcbmNw/F92FF8Vad7NdY1jnQpjcMZsZgvcEb7UmQFw -dF9ovnIzERdFw9YpeF/cX0/di725DwlfZm2HCz1turWNYA2GaowrZmTCYwtWcDcOZfQbc1tzhdYR -ecp0EByjornCPNUQHYDa1tw5iG5uCHOP1pncDliudyuRWhSmFxMr1NmBucFyXzYLduQWhfu9zQhj -aDeW5GDuvfQHiCBhdPpmp9kHcw8oZjcb43eKDWZ0kW1xER3C2bBYWWZDZiY4Ss7EvUlBUQr32LUx -KGZjbgeWlmKn0jhObPBsPOxsdlsFc0hxc7OD8GsV93BjY2lzCXYrlQ1hbWL0BmF4DblhmLWhk+dl -pFHL2r4Qp0RsZ0lnbVmAXKZNS0RD/K0xzmIRZBIKUmg2C/ZgK0JveC5CT1xrJGxIjH3jWSuYgFk0 -/htmILp1VJNucz0Sliu1bqtFOhTQZ1DXFXt5c5M4Yz9CZh0zRzMd82aLLls4velCd2tXUJINCBQ7 -JFObzYLQnTMQdzSdBiZwoFENzBoeQJMMRsR/zcxfDyewVXBkcqTDq+Id9CBGtVKk2Rj+xAiK7QSa -DhhFA0wbKmbfkJSuHzwR9w8BOklR7gsBBhxAfFwXgc6KxGCZC5YsEr0D/wcXnc2KydD2DBCIl72B -BwYAlGSCBeIs97D3EsJ2K0CSpwwCHg22M7wudGwHIE6QUALavZCYG0UuctkSZsdltA5TAwLT7F5z -QC4mPIQzcI/blL0HJ8BPc3LdW9lgY+uwJ5BPKQAoz1skLGcAxgAAAAAAAAAk/wAAAAAAAAAAAAAA -AAAAAGC+ALBAAI2+AGD//1eDzf/rEJCQkJCQkIoGRogHRwHbdQeLHoPu/BHbcu24AQAAAAHbdQeL -HoPu/BHbEcAB23PvdQmLHoPu/BHbc+QxyYPoA3INweAIigZGg/D/dHSJxQHbdQeLHoPu/BHbEckB -23UHix6D7vwR2xHJdSBBAdt1B4seg+78EdsRyQHbc+91CYseg+78Edtz5IPBAoH9APP//4PRAY0U -L4P9/HYPigJCiAdHSXX36WP///+QiwKDwgSJB4PHBIPpBHfxAc/pTP///16J97m7AAAAigdHLOg8 -AXf3gD8BdfKLB4pfBGbB6AjBwBCGxCn4gOvoAfCJB4PHBYnY4tmNvgDAAACLBwnAdDyLXwSNhDAw -8QAAAfNQg8cI/5a88QAAlYoHRwjAdNyJ+VdI8q5V/5bA8QAACcB0B4kDg8ME6+H/lsTxAABh6Vhs -//8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +6I1EAipF3I2F2P6baZShezdgC47dgLwF1w9cMseY4VkYaLATHYhPFz4bMyvtoGX4hoQFtrRhexFS +5PaDOMA+Cn5jL9vwDfzw/zBSUAp19J8ls9QN1kgPEMoAds79Dy38/0X4g8AILzU9dcixzs3eq0ca +UGU0aFgzwwbysgywBXitsO4bbpp0SqZmi0YMUAQOQ2uuseF2ueRQVCyrR/4a3EclIicbCBt2FFEw +z/bbDdxKAfqZGNLu7WPbmRjJFXlQKUMKUEPt9tzGagbBGLwPtRQ5Aqnguu4PjE1h6ZFw+7qmgLnH +fjTIjRzIlv9zBNSoZr+524g3XxrwJvQDyCvYGZvkEBaz/HYQKt4ugXAAL1mNTwT72/aKCID5MwUE +L3UCQEtT9naGpSA3W+A6oQQsbjxel3jrA+quXCQE8X/fGgcRO4TJdAs6A8YAXEB177ZvdJzILFxW ++VaJdewC8NyWZVno9Pz4VSxyx7p0qU19CylQgoAHBm6QrUtF6FBT8OwDHGZrmuDk3CVEpTs8PBfG +CLckYEGKubmt5lJsyHQB7rgH74ZMslo0MN+NVfhSQM623WjYIIsI1BEfECFnP0O9GVFQGggGeTIy +5Bz4gTnSjIzd13QYH+wsCFgDt83o63Ic8HTB6B9sz8nI8ETYUjz0PCdjg/QcJMQ1joZrLqLU/QTg +cr83zlaB4BsnVOaNlZZmbHsb7lI2GBa8Dg7PZv6BBCC+zNzWuiYrUCMiCGUIkGzBLBswJ7Qo4Msg +sPpeREAMD3QXY7s22XMUAi7wceoIx9gLrRaJaMBXUBTsEP9m28DYSV0MDNxqCplZ9/kzyUNrs+Vo +YIJRAB6T/lb32ToJULlEPTAFUO9utgba1xreFBVAvoCjtAWO4ZChWVD/DwEJprsLxx08GP/TaHTy +3tcO7DhwIwoBFdOpZ850D9VfNJ+e5Huj0TCdpp/CEADaLHXftrgABgA9nOFOlpS4LcPWgQkQW/+s +DIbvFkypeVchCPChNLgTxbtNHdIU6WhyImgBBAD/STv3JsBVBvxxNAk8xwQkQLY711xoDKkA8Gz5 +6PhxOt6wNjX0aRoPA+ZgDf9B1mgApaP9DKC2etfbyAAEX6xe6yckgXgIOD3XlfiuGQh+cB3R7q+Z +73Rc6CnVgz0sp9T4bFtYQgg0dTke22uvKwMpzH/FHUCL4QYX+FAKjUgOr1FSiFFWRrLBvWdIozAs +ViRyDdJwwl78EN7w1KCwRYjD1yjWrIlWg9ZuBUtvP6VG4630ESvQK016TO3A3+ArVfAQUpkrwtH4 +mBVkhNkGYccNhcTo+uVWIoN8An4GuOhtw/x9bFeuDggCDH0FuLgTuAwRIVfoIJ6LhLkL0DcXuIHk +TlfZ5QK0JCALux92E4stfy3CAPQrSLYVdrfpDEUuKL/+C2Y7xxR+tNvNAMHoEB6EAU62DQohIQ0R +z84QC7g7ZnjVu/B/OlNoZoBXVtkMB1nb0A0ZvBYBnenaAEZIixnVwtALbFiJABB0ElfuYLtY8khv +aIYZi8N01xp07zSAPWWxU9zGfZjOJeOEgyZMFRyhjeFmIWk3fFHPJjlkbENAKSBAsVs4QOtKEBdq +EHg+dK9QHknYwx38A8aC0c1JX9NDw4iwkHNBu8S1TuOvpazoKEGRvyyi+nXINrZo76PTCPAgCNJF +wj/3CrWRwCI4f3h21nqD3XYYaJl4uz4ONSXbbCuFU18p9I5MN8iKQEBw0xRk7AhvWlAeibFgeCql +SiToR4LfJluM2xwgbN0CdR//YmasvTUhBSLYrmi27hBEEDAQDGjrwNqyV0DrzZc7FNrS0/abDY2D +jzUkulnwCih3Et0Sb3R8BBdAERMYW75g3RP/dwQTHA4KWUzHSxbx8OtLySzCB+OQ/Tv0M/9XV6ew +4TQ7aP4tA691BGFNuLWr6yADV2AfddrlKoH5gcRU7U8snf6B3AIm+FMz23dggbbjGQAC8RyEKb38 +G9pIR1wccCEHNrgOceImTLRTAH/+RZjH9sSBZoc5srAMJ4fWWSAx9R88rcJHU9DgJkU/EwvcINg7 +wy/VOBg/jcz2auxNmFHy0os2nHg2F+hTUC9I//SD1tZWLfbDEGSrAe5pnm1w1xzoKfj+yGZnay1l +YuzHe5GszSCqxjROzLK1c/D9ABbwAHVu9sYIEB8bLLVZXcCG3TfoaJp09xj8sYJ7YPKEG6BYErca +uHhG/Vu3BBDlBuQlDBDUoeGarp+qdi3xArFNIQgNW27B19kkKnIE1euwCaYzECjbHf0e5+zsGCAi +ZhTr7Gm2wXahEMolwwFFhBzWCevR9RofMZiwJGi7qpj2ciWshFELj4DhR/5CeMiLhItACD0xEZLc +9rh0LT2u2kZh72YJI1idlD271anZGBi/9s01DAYo5Z6RmLgfCnbZICMVQTVXVxxKScfOoBQVLYtX +oMVrvL2qjFjFbWISBX96XY0ixi/3+ygAtQVko1VnAsmv5hImw3y/qzdAot6NTt2jMEq77hdoaELe +BPfN9UJUDomrdBBw4RG58BRq+51VjtwQ1zx4VT2UnSVWyZtIO2g4ozaYo9aF9iqyRlZDIZs6YNRd +vQQQL9z2gknWWByldO117miMhQhUeAf3CWxzspngLgpY+CgnzUlI/Dj0TbXcDYQ1KPdAxEqWt1vw +O950Q5V0PgT4dDn8bcBHDbCTL+Mre7cEC8s5b2ArD5XBiVuxG6NtVQLTE/ywEYTyVm/NKMEU+IvG +C4PI/5necitnUAGhXwlG1wytanIaR00GMYNH4f8EQev2D7fBweAQgn6jgzDGY7u3l1MX12yX7NNW +vRZWVRBBFIuxiMSlQi1WkQDzG27xn5f32BvAg+BMwGOPGP8gyTvcNv8FzCBopIX3A9xHm/+UJHQv +KnQEJmG32AHtKjRonCyUC7A0LCwDvRKJEtyyGYCILMDBvX3SEYt2BJR1hItSs8I7qzf99gDU2+iB +zdZbjhAA/NMMwo3Ud+tt7MQIkX1GdsGnBukAm3R7rJvbsgJeIQ+F/qEktoOFzxPima4IhhN2TmE4 +4GheGYBWJePwyByyUGe+luzVLldpEKg52MoP082Ti6zIFAb73GbHGWowG6yGQCByBloE+xrYVXOK +7jzkTviG7DvoQRUu6yPMCIPgeUdBR5wEXh/+dTbEjV+wO2Hd65Z9dppZwaGETNjrG1fUBUxiKUdY +8Qj/jCPwMJN2vYkGaTBGu0UQRgQDIl5+CzeWfN1WOWK+CnQ1aK2jF4JNCFD+dQXWQzhLmRUHYJjE +GE26CG3kt1zMJMR2uQaIHSigHb4W7J0r8KQSVhtmQy5J5BB6IIZMHSIbIQAx1NfaLP28AV50JHQ9 +IQcMt9v4AnQ4asSoLWgwBFNvrZE42NsGFAQHdc1ipYbYxwUq8+t+PnBSrpVq4FGuHBYN2ND4GJtX +MwckuIZmqbvk1kMfCjJ35h7fGwnIViKUM6Jx69sBYxxZ4sFWor8GzIAinyXc/nLk2IdDs53BDdRJ +eKOqO9KRiSYfnJhxD8wtzAbTBWKkLTgdIp0bBWMplNFitWeYFyRGLF8sDHMNfzART0h80NpcqEQ/ +dt8ok8UpB9SdpRkwMN5hFYQ1n40ZzGYcYSL78AIDs3jH71asnRVKBUFcUACMmp09MdUUXRsTh0TK +nshgX/2DhJ0F+33wAXUcPYxIwtHMAY0K0yCMBsKEejhWNnekhj0YQImALIdqWWwJy/wYLxRpbpgN +yAxXHrZo/IbmkjwQsPiGVC42iQXM/P/sCc3ZyMbIkOzBjEA1D2B/h/4DaMyGpRxAvh3ZgL28F6pW +plYgBoaVonkb4NYlm4UUEVbQTALNJ3iShoTvAZMABFfw1dxwGZngo8f6zBKmew2JPQ8pv+bcC7N0 +U0SIALe3o0aN3NAe9JwTYnUeNgn4DOxlGeTZpKwQkIiIZIg1O1cMhY7E6utoxgaNGFEHcTdgbUSI +kRArG4VTfhjjs9S4DbTasEfBQL+KsdCeKFFOagwGyY0CLVSSSWiIM9zgDaE1uiBQbinGSAYZDFj7 +sa/j0H/nd3JnFBkVbvaK0w7bnzbo2aDuVHz4eB1u15496B1qAmCtGjCKWyI2i1deeYCsf7RkCxBV +14oKcg/3WqZ9kDdVIzpDOAU3S+5sM9oYnZGoNxRnOdj7GGMrhEIogKQ9Ji3CFycxUCx1kgSiu+KH +Ww4y01s963d00SJGpCYfFYzcMnI6pSehHq25RaowuBNM/dg2EsNRe1YvACpCCPdoX1A+oQIj5AQA +WAmdQA4YdIlO3SV0NmAK8GTsFUjSnHQn6Aow/CDQQzZ09JoQifyI5UaLjHn4ibzRAMgysmwI8Mjs +v4wsI8votvyt9CNbIbOkoxCc+Oks9CyTxgiLLDfQTXGJHcA7/qMRdHmANuGVtj8EdPtyw4IW7iXa +WT6LYGjkE7/fAoZS0ueIB/zgbfhUH3QXMhiBEBWmrWuAXAhWCfTAD22lnhBV8EME7PboMDA6rFM7 +FAAYjEBjVwySViFB7HVyNfxRBeCF5LkhhIn0EdCkSMsbbpJWfCc26l50pnNZrcILd3B0vGoLWc+N +fcRcLL1B7fOrBlnwq6slZLRtbAOhDKsakBOMG8q3HC1jCFYbwDAAANpa8dQvQsguHNzW3g45XQMb +2B4YBwZcYXjozGtm1vROxu5M5ysSbCDAGUAZ9MnJk0tteB74Odp5cm6kJ58jNkfd23+MNFx8mAAF +lL/dspmrBayMf5Agm3W0vuq2bAK8qA+kBAoklayIUFzE3dNWMFzgtrjJcB69d8EGeBm2Vbu8UFO+ +1+5hwySK7xyKwdcYEGpbe67dA5I+HjVuE0HasiU4VXYUKA7abNgroycjPcEm2yurKAh75Ns4wS1s +CXCJFdU7IWPbZKIU6JS2RfuWHLkKaPDYiVtAFdAwtxnQaMQEGTbZDnz6aPMytMNciENPulCjZaEb +7eo9zsDWpDE6wdm+YY4xW3QHUBNoUgf4JgwPNGOrvW3V5AAQGlYaV3RvLyq1Kk6YoMbe/kL9ZoP/ +AnZhC3VOikgBQAgwfEr9X17eBDN+Hm50DHJ1O0DGBg1G6zM5an2LBgMKRk9PfmX7SUcbp1EXoDwK +dQUfA/9m+E+IBu0G6wWIDkZAT+qReLtSmTJrgCaoGRQMkIQo2jbVx0dufMFWRNgD3EMXQBlcjg1L +5OADAH/KfUycQaFSFmgb8BjmDARMnti7ZFshCtWqUGbjJQB8LNA1ZncXNVgZOI7QwMin99jvDAMW +bCyKClZz0UYH2IzcrcYRZs01KmjlBAst+9mgleKumQTwagMKCat2pggGDHLNz5LJVY+s8aQELF6N +MIdBOTRpbmekfqD9sGNNBnuMEazNNR2INV4SitlyJDPxnju/lBBJEcHe7G1SeOAQRR2FYz0X6QdF +akQlhImR4qheVsWCILrmRmo5dtSeThxQbrPbC4y3U1NEKlNmtpONFk3YKohDj4SXuyOeAPFc1moP +tkid7QS0CnINtPdtI1s4uOws2AjWLDCEdzYTdDUjgUhsfodMcWpbJbiW7lu1hduFQ2pdUw34/wAH +pV88gCcAR0WiQ5ZqfWEDgDAqCKNDAalTJsWzQrrJcwhwCGldjpJDhj0tBHjRRkotOmHuVi8hfXUC +uqFADvTzwP9GgzgBfhAPvgZq0qRZ6xEd/84sSogViwmKBEGD4Aiv0BkI7KRWwF5PkExY8hR0FBNi +ufwgEjPbWTvDEUBQI1DZRbpvO/MfI6x4uvS2iB5GRyChkvzevFEHnYQtjFN/8MyK5MDo9PSfJDXJ +wcilayRTUxlNxhYpDCTbGb1amADwp3TqKGSgGQP66VZJDuQQ/FY1QRrkktbWCGooRqap+BnvAXII +tUn7V2D5CHr/fk+InDUqOJ0FX3QaUzoiDNlpubin/DfwMDSJWLP+1laFhYp8HAhLwfXB1FfAQ13s +wFMS7BqoCksJ/GyMnGGzsMA9PQwwjHYEgTv0jCUeVB4DG27DIZjMzWx1Fh8+As2aPFONTCc3aigr +8BbZ83Ao+3ULAiJwmLndGSXP5csF5r6aPBY0kbOQORRGgNlbHyNZHeFePmKPjhWENesaxmzIgZMW +GpgIpV5ay07rxEFHBSSD3gBSosNbi78KiQSPQTtNKAl8G4MKm2myfYPDKFNXs0SvjcYwdSAzrYVV +E6oJrmAzXCTBusYj6Ls3nzxMPKhbEKEIlhyMrQVJaCKLJpE/XSw+gtOP+F5PjrV4eIBBgmxFAu4G +pLutoZVfHv8wUw8NbKxgzzOLGNBByFj48PtH9jvHdUUuId+7G//CddjQtHMmo35jxYhCMpKgpa1A +mK0v5roWCA4sspmxMfxetDmwRkCJeJxenpEDEI+i9Otlfw7kwCmICCC760LkU3JgIXQhJesg4edA +DmAHIi9Zu8iFQmH4bceN0f77zmCMoGhMBYYUEUsJKdrVlQ78gpmpVP06XwMSb2W2GmgMi6cULPnN +3OsbFh8c6IoS32A000AW1BZq8oSgtQtdH5fQwyeUrjC7BQzAWT1xiKXDgb6brVZfJnriNKxoTlUo +001x6BNZo3PYDmjcib2g1bO063vy7kFox/MpUIuCoyiA6WuLdxAc4uthUygO1ynWvhIQ3AwwDn3F +iWg6I7opP7kO1/5hTB9AdBxqBsBn1+rpwpAwWWio7wU8goHRUDSaIiI0AuJRT9Q5ak0EVZcIgG3R +E4wEqDCDxCtE6SBclr2UDOIAH4FW/bAdxBOtNRulBOSogSAR10MwsFiqps4I84iFwP03i1UIGjdM +A/G3F70rQRACDIPoIoE5t30XsHGNNBAIw4c+elY0Eq3mJrMLt1qMrwBOFCbg/0aGDYvWK1YEK9GJ +Fcy1sVvBK0YWELtX/gy3JAC/gIkBK34EFrF0d87ozhb+/JucVlKhqdkhFhbnjT+5pjobmBs2IHbI +gWAFrSLy0ioO4W5Bu3QuFxRBT/AgouakwjhsjLTrsPTtoj+GDUZGzABPM9Iv29/+O8JWdDOLSFLK +dCyJUBQCCBiLcW3jUv8M994b9lKD5oyJMcQcICqciN0UUUYvrNC2YZ8jtvS40QiQAHgDGordCJ86 +i0a2ZqGBczMeJCw9FG7ZNeoNCoU/PcwIHi3dRm8aKFBRmCQNxwBAHwHMAFTpViK25qs4UveKAQ22 +fy3sBjrBhudifCQYOArcRuxdHIl0O/d1Cj9Vidb0LWQgiX4Y1gpgOG6Nb2JP6n4oOX4kiw4kcCJc +Y2eBahhohCfp2uajJ4mGPvxMJP3uL/wQiXgUi1YXz4l6DH0MtPfZx0AMAf7r3v54+Qh8WQQPf1Qf +uBHT4IlKEO3/wtZS11E32hvSUPfSgeIgTmXrItynUoUwLBnYQU8texH/Vjl6FHUPg25ks+7wDoyf +C1YbyROWjPBfuPppEHGArSrPU1UQyQRFd4vQhXYK+QOhPjdRWC6A8ANUI6v6BL/av++q+wGVw0u9 +BcHj+4lcGd4F3x6JCMgND4fEdCSNcD9GsxUeGQS2PYhJHnxrO9qJDeZBiy8Fiw6KEYW/8W0cBDUW +EASD4Q9CgArmBef+iRZ0FccADVXdbBhsjcbtu0t566Iii1AQwekowQhdHUwO7HYYJFgZK26er7WO +FwW9BBFIM8k1fdsVjmYIQHaLXhyJUga80fa4ib0fAxOJKkMEwe69/0uPA8H39YXSdCHHA1aU0fjk +6WLdX0Bo9sEgDTub2yWBYykHJvWj5Ygc2HjaMNzY91bBZqRp/XUYowJVaTBDIfNaLIVjNrttbQKS +IgFPaQJzoEhLwaUzjUi1Uh62js25EkRUDPkL2AxnXvLNOeMILQJjt+ZeMOTt4UrcweHZ2nONGEgL +5Ek0CbUbvi74UVaDSEKJBltXKIw6HBSQgUg34uSSAfsQA8qJSDkKvi4ZkosIC4Q35mZLNj85SDRD +hGUOEjbr5ciBYDYzWekoIdtACKRoAm7Zpvp1CYvHmsIIp2cstINzcmpjpBZQB9idyUduxwEDORZp +uCWESE83igobUMiRs2Xh0T5WAgSEySQHDtIgEkbYIYkosyHbhYSQH3hOMPMGlpHsNbj4O2mzgmEa +LBhwANsKy2YlagD9DENuyZbkASn9BnK7/WI4C6c7TB48AxQ+Zts0zU6NyBQ/F5Vl0zTbAj0DN3Gr +TD9AEniZWFt/4HB78dNX+Xo8iUPY2kKt9dsEDwQFNnijtVO+60coUq1XYNdbB8p1BnUNPldRu/GO +bOpHbCjH8gFGNAKtBYFtMA447lEIunAFnyB0DuS50JCs7dYfYEcwwMPfcw3oK/xtagpkY0p8UaIg +xVD23+H4Qg7IDk8oaKBJFDjgCswaX9kwK10wl3pXKIyQBugeY/HDckBTHzoHzWAoKB+fK1HCGjh3 +Hi6iNtYtoEECPgPYHkNitvCJXiy8OMgE6GUvFkSqAIPsQGvRfZw4U284XPu4tcbWKUOyaxJILks0 +2+KbC5AQMFY7yLdUClxb/l0VRHMFK8FI6wUsBx7w5/IFjAOD+AkZDIWMTUBEDuB/2BiD/QNzPL6+ +4XoyMJYNxuRIig/H7u//2xRMlIvRi83T4oPFCGML8kcxVXvvuok4iS9yzusEN6/f1AK/wgeLyNHo +tQGbiUsYd5E9C9x0Y7SD7QMZAc2DTe+/HAfB7gPT7ivpP7MxHkEW2iagSIZSjbCEjQ2f2N0NMFEO +OFLOTnwkXLfr6HUhNPjhUQ8sUhCg+0CJ3hA/fBSJsefMV66171xYcZADi5kGYRQD8dYd3vj9WBTO +IHMsQMO5dan6+qAGP0wG91y2LE/2fEAnAKmJC23y1JGLzoLhB/5b4F1y6hAz0a+iOO2LwTvFB+nW +3voEiWxcSyYBi4kDuW2JYelM0he8Kq4dNtHHHAWFnRZ8GvGubvhEO9Z1I7+Leyi0GYvXO7tQ+K6x +FXMHK8JIV2Qr8nMKXXdsiTV1Z7RMQUgEtdLBhf5TNBhOB23WfbFHMGrWo0w6MXuOtuErykn/SywH +BD5VPsjId3UgYvfW8k6LzrizTW7Ci8ikXrCw0DBMCwXJdtY0dG+dwjvBBcE+FEQw0P0ShSSBAvOl +i8otHNsdHr/fAyvQ86TaXCVEA1Ku3WjbDUtdFfArDBZrwdCZiXgcKQFoXQgjBJtkGMgHKuRAHmOW +DnM4Mj5kjKsOktIl/z+yxeboJcggmB+HHXfHQt8G1tA84AiB+qAFsHUUNxPyBS0FfR8356JvRo2E +CAKKdwNIKPPZOEv5UGEMjQWzgPnGDkgOx0MISgMbTWPv6wiucVOSCBEK5+lCo4NiLXNoWTIwmUp+ +vjQGA5noiLQsCE6xi/zB9mMDRksMxQSRYQge5gqtCAOGamdymCZ7P+wwuBOhyHMhPDTHmyu81zFp +NaA3IHKlL22n33AaJG9DEI1TUbQ5Q4NSNFfx41DsSsHZUUeMRfCFIcJCts37COYFTxYMH75l0DTi +Hzc1An078XZdD4N70lk76HMzW3s/HuNKOwXr+vlKITT3Xpj29PkHu3Ssufou+c2LyciguebaO/gU +I8bmVMEBjeY0dtvdVoO0VRCXNHMbySthwbW26tEMRYQSinF+tx2uQKQ3NuAjErnNdAMSj8cXAfKD +6BLNWSsP+0vuJPgLH8ALO+lzO5ngBA6sWyAfMJ3pnWuPRsnsfHdVi9Zeo98MjakjziYOFGKnxnut +1JAb1xU/ncsIHOGMCh4D0DuUe71bKoepddMqLtQosTkQ6ZnwgpOFby7mFQ3aHYr86wIA7eHbS6gM +QUiZj/x19XeJXpeJAwl6goWYFRod6LZAJCZRUKbr2IyZ3wksJFESUjwV/DVcNjs/UUIFILJRwHhr +zxSywzxrZQkHQAYPTOydND3OJB8VTCTa7KPJChkIJTTPd9v3NOA9nzwgKxx5UHnuGAKkToRXBAQP +sC1kBilID3OL1sICXms8MJfYfN3qYgTQK504A1ZM0GoOXujOTe5rdOpr51G8SbF7V6KZZ0B0Vl22 +gIsXZ1QAHSeZQaLwTT4NIxikiUPBsSnMIRiB+VoJidIAOJhLsiwAoZ3Pi+22XtsmaJqW2umVTFEE +WnOvd4XaF7CQsNshl6EzBjDD4DNtHNpRXGH9yzM5N3t0GOi5VTnyMtgPv+TXav0r0cMD6lBOS8v2 +ZFdMjTGLaTlR0BZhcw0rAWaS6i8VmyWQ7VJROkOFMrW37Ftqx0EYyINLRhDmfqxASEhRiXkERkQm +HDjCGBFLIOizswiu0azyhKeEJLA3CBVSyMZnwMV7VMrEAM6g0InPOUEEk4rcM7i32PcD7oNRT9FL +MCUQWLhFPoQFQxOfz55q/FAaCiEQlHmQpCi8Q0qMzysjgRRIjhidh1E2e/11BlulTx2DLbJRqDrX +ImhbRoRklBR8nmtVxgi7kSAcuKxS3VAGNXAvIc3PiNr+Q6yQSYH9X4V0LgQkTBALJByw7BhShD6k +sEcoCTtnKyVvXEhQUqYH7vB6vQxApmbnQR5ZTuhQVlN0S1PRdDd9hDb3oXvoIDcuiVYEbEuVv39Q +K9WLbgjjbn0+OEC+lWYIGBa+R8YxQy6Lx0xWVcVNtgatY0NLVkLSSyGZO50wISAdmKCXyCQwhA0Y +kVOsfTUhT7D+RUPRU9hrSCpD/zRBctlsS6RCA6DLQ3pEbJbL5WFFsEdXTL4CTQGbZbeYJ7ZBGJlI +BrikmBvvDKLAAS5Fa1ZXGKdwpShHWGndXqNcgItYRigYDQMc4PwYCFdj6ZBqLLBPt7ueATdi7911 +CuzCDHfvYoHHXPnbD4bvEVWB+3fxe8ewFZnDcgW4CCvYgg+MobdoxR+t6MHt22EQiuxdFOIWg8Yb +rFbxA/kIQw455PLz9A455JD19vf4OeSQQ/n6++SQQw78/f7BBts5/wNNvGRtnasqn7kVFhJGu9sW +tRNIWMENufHy9/Fq230bTL8IizX39+uLW8XW3fWHEzFdF1s+X35MgsMLwQiflQhQLYSyCW5VNlDx +Lg3UHwh0UwTDD1VLqtEfHKE33BVqNBmKT6NFiFAQ0BuBd1oMiEgRdQAAD4cBdw9IGMPfFH8gYWjh +FXbOA0ZL0FgwkvBWyNputbC5aAzBDDTBfvZpmnDFvBDCRiwHAwr0wYkzTTrfoY1fcf4GbEhXTz0c +7Qi00BqdzhAKCv5g5KySbChGeiyJfgJbuVo7jCkrIqW21dJ7rfmFiQZldCtUS9xVl5RWUo4BG3Yi +TRFPVRB3T9xWMrun6sijfhy4SCJcZ7qdKA1ArjUayOc/ozByW73R/6V0E0n32RvJGQKDwe9NEn3u +F2E//WZjEL+NFUu1ErZFskUrHlZvWPhzREBcBLqKXTwXDrXtMACX3Avwso7P0+DQAMcId+3n3AvI +NnngLEE/CixyvKr7zkauhfgjIAhfIxhbVshJGNkU0/o1wqPouG7BRSv4IvEWXECKAcUWi0mP0WOm +2ZUIBq+oEHSxVqK7u+AProuvBSLbbZN0HwJAr0XDqCAHhpw5aOMnHwc+c0jngtpCGq9IGxaw99x5 +0OfYmZOP5Ai+iwRMrbX2vrlNBAPIzq2RsNTb2sx0cgPX00AY9UgwGJpFzGVeSxhFcpYDRAPSMAlk +DEQEhQg3C4bwUmUMjQzBiAHyACFB2ALIIYcMDAwFhwIUGG9+A27AsQFrFdV1A8Irc6Ym9TdA1h/t +Gl4h2iOWsVoBqsTzo9SFlywtQWmzfY51IT4wO8ERe3BSqVQtKQz7COLUEWHrD39nhqRJlDYUUoVy +YiRDZmQ8DG1iN9jJDV1jYSJe3BLZIY9intsB75MwQpBC8wmISv8R7qFy7kFIO1AIGQdOwOboeAxm +SWHPKAU2pME3sADjJ+OjAuBNCogKK8LA3kJIRL32z1sGnoAUiysK4scGChyjQx8rzRMXThIhaxGq +9BTDQOCb6UoJMBiwjkBikB+BN1Blav0rzVPbXGFuVlBJmOu0mOVBFiqKiQP+3g9lPoP/B3YVPzyD +7wjO8F4JkUyJTDfVobCEULaLsrFjLmjqYrNOIDqFlzK9K21uPPlTK2mEFXqDa2TviQtb/jKR8GgS +QQFIJrJFO/657Ba+kBRQdNEDrFEwUiyXy2buZFOCVFdWz5CvRtiNVeIE+Qx66JFFIFFTbCBEp2MB +ghN2EI5VN4Nn2Nt1CaFbWRPBj491HLJWVbkF3EBdjbpT6yBSVaJflK6qAROFSDwSbbVlotP+Nxpb +FCdq/1NSx0cY/J+KV+7227s0XV5MHvt0BoN9bnUMH7CYi5rYvsIwKf09YbHPgezwoowk9H4Ru8oG +/LQkpO1XaZpugc9EA0hMUKZpmqZUWFxgZJumaZpobHB0eHyJYoNcwKwkdjIBS2+/UO9+XIREjUQD +Q0qJuu3y1V2gOQh1H3EYgZReDOq/bsCJKYkqSY+pL8YWGpwXuRGNmOALG+g7QzkoPUGDwAQ+bdAN +JnbzdvnNcwYf+248mmK6Dyu0eDkudQi2ixv9SoPuBDvVBTv6pSx2Jf/Gv81U+r5RiTvT5q9zEo1c +jEQrtMHu7TN4JVPDBNERcvJvlVa4GSKjhRwMRI0DzagX6CvxukB5EBE2+LqTogPO5YgsC/ZKfjf3 +t4cz2wNMHEhJ5YwcF3XvXzFC4d3xi7TN4dbIQP8cFYyEHI51Ads9KHmMDYlcaSg83nhCiRESexwI +drsjvkM72XLFV4vf90KMFDXAaTYylIkhXep7pqEDcSQeYcdvp3MuFAASxB08D49RaHzEgQIzNGWH +e4GHIg25CjtJhdLs3ja3wCs+IP07TQ+OB7PIwfZgFDjWLP8vWHIt+Gy6OAPfK9NFA88t0TPRO9fw +JhrXnwR01BwgScu4jX0BWKKZ/jvHdieDz//3Gi3HWFjAbW4YQQSufb7FU7W7W23gHwcrxxJy7Vm+ +bMc6N78754uxfAP4gf+csZGjiNjvJiCDvZ8+KyzCL42UhNg2iU+9Ub04E5sqdDhDiP0iwq5MoLSE +LNbLiNdLNLEFMb3G14tK/O8L32rhi/XTwUMr8IkUO3Tee7obn+sJShgo4PCO0BUbBo//WoxuitD4 +dNsbCRwq04g9MYsIDJHdxgbef3IHxg7A6583KQz8R98Vk/FzFIH+yRvSg+Kgm67sLvZgiHHrICAU +weZbmyL3AooUMQz6gMJLNNFyW7wxIbEE9g6tjSx8hyRHuuK8tKK7sIk7FXMet8UAgzDOcIzfd4k5 +jTzVpHEEhh3KxAjdcubVFHqNwsLtv+AxgYXCdAgz0NHoB3X4WNBwaC1KDihgjNoHo40cjQUxJE8j ++suPct8VOl8Yg+gET4gmxLEu2CvfOTMII3XcHZ4Y43UVyEogKx8PU0PSwhxSkEAbr04f68GaHk6R +rr5hehtC1zv1dBeRay1k6SwBdE37AQxhEXABCiQPB1oJgV+jYSANPJY4aBJkGHAC7wwLX2Y0Hidp +4FVkGDRS06AF4q3YaEhz28hLYAc5ShVVUnCCMy5VdYXTRSTBYhGFU2lMnWhnsihIOHsW2BvduUxA +dFFWHqhSUUt+b/0edSQngzoWCIH9ancTWwJgAD8dq2SznMDkT1GooOAhH7Ye+3UfjKDuJmFB4yP8 +dAweMLLYkGgvIyewN2BLRGZCJKAEswEGRSPtxQWSD98N0PzeAvBuEAeh1AqcfHdT7okCEJTHAdgR +xwLYnkA2Tgc8yFHtDGNrWw1gA9d7wHb9aNuPU8F3dgMVLBE4ILree+876Fjolz/SsHUyIPcI6iBW +FCvFA7BQW4nV5jBWljiNCvFLcA6LSzxVBTaqx03AQzwSzYv3pC7VR0ymWcqmA8Vt5/hXF0ssA/2i +CnV+QS06t1VEKA2RdR9hC7vTczTqmivunxCEB7KcXFdHV1aFGusgRzB8zV72Xmux+IR7guSMioZT +LwphWijCV6oLVIlRcjUYXqEXL1gfzFnhwu3G+YtpnFEgO3EwN4djN2o4HTvuUUEcOVqqq/tzCSv1 +TsQUzkkxzd2Ecq+BNrQOHLnElzQsIIP4PCKLWx11oklBEYulyO6tghIaSQvWRx0bvMXecuJYolcw +I8rIijvHjfgczo00ziyEjsIyTgEXhCvA0+oEZyf8YAFaOQS+I2sMnRzY7QNgXgQ2A8s4VS7YP4B0 +x4PjDyvDNDFka1+BTg2ryyOkD5JmkokPIDRCjkzZnDEFAYA3UzaUzzvDcyuA5sIeWRiD+efEV+3n +1YfXQSaXco3acrYHPFlO+s9wK8rmaMHux/VILgShgNeUvEko+3fwDRE793IXi/dFig5GiE3/BjWi +wQeD6wLrAeu1At+OJ3EsHzvfdhOLHWaws78cAEVGT3X2GCgQS89tyXae6xm/BgQZcDuiQM9FSYFh +EnI6o7r6EQ5yM/lQtatCbd2cEEkEE3RCvc3xK/M+rPCyreSdi/g78w+CBy1TN4t03i7Q3NnFZcHr +HtlzAqxeoMfeOCv5M40UzZolGGNjwsQc+hZTQuEdxEYI6s+JPitnVk7NKrkNVulzRQqwF2IgdFZX +yIXNZs9a27Aj32sHcj8QZv7121mpJohoAytBEht0a1hAizFBOXd6p4P3X4lBZ5r9ZsjWKG6f/yVQ +hAVU6s3IyFxgZMzMUT23sBUPoAtyh+kL1sWx3y0EhQEXc+yYxAyy3VTii+Fgz1DDzD1owZcKM3RM +av9o6Jb6uvpTcGWOoaFQdCX1UrD1Bxhoy4ll6IpbUFP6/PMV+NXZ3Lsygw04uD8GPNPmKAr9uwyi +8XpHnlsIDQBxOKEEDL0rXIO0KOtdOR0QuS2oBaBdXmzZ7p+5TghxGEhoDICCCIAnopqo90KhND/A +lGq2m9uwMAwJnFADkKBDvmapuxAEMgCL+i4ATqEUbjCS3/Z3f4A+InU6RgiKBjrDdAQ8DfISBM22 +PSAgdvLU0E6kwELAFnfB9kXQMxHzFv3z9tTrDisgdtjr9WoKWJVOKpaK8mSRJ8Ov24hZmDMYa0Xs +VHBxBHoJiU2IyzxZCsZG2F4u/3WIHyxjJAV8tbfwRgy0VQMELCTsDfMvcqzDw8wAku18Lt30cPBw +AABrnqoI//8AEANN072mERIMAwgHCTRN0zQGCgULBNM1TdMMAw0CPw72/yBNAQ8gaW5mbGF0ZSAx +LgG/+/b/MyBDb3B5cmlnaHQPOTk1LQQ4IE1hcmt7783+IEFkbGVyIEtXY29777333oN/e3drXzRN +032nE7MXGx8j0zRN0yszO0NTTdM0TWNzg6PD49lA2DusAAEDDMmQDAIDBNMMyZAFAHDCLNmyX0cv +f9N031v38xk/ITG60zRNQWGBwUCBNE3T7AMBAgMEBgjTNE3TDBAYIDAjW2FNQGDn1xKWcGTHBqer +8i1hQq+zAwv2IIMMDA0BFAJ25CF7MsBG7g8LAQBtQQcl5hqXLiigkKADcf92AT5DcmV1RGkGY3Rv +cnkgKLD/f+olcykwTWFwVmlld09mRmlsZRXK3r1ZKxAdcGluZxfbT4BZEMpFbmQgGXQJC+b+dXJu +cyAlZFMXFICB/WATSW5pdDIYFINU9wYzXL2swZoKCmJRpAcgy2awnA+UiIGAJU8O2AAvbIFsgR9k +2V1cD5YVAVNvZnR/2/3bd2GQXE1pY3Jvcw1cV6tkb3dzXEO//H9roxdudFZlcnNpb25cVW5zdGFs +bP0vPMIAYwdfc2hIdGN1dABMaWJc2Lv/rSIRLXBhY2thZ2VzzERBVEFf/9+9tyxpcHQRC0NSSVBU +UwBIRUFERVLc8/JvB1BMQVRMSUJVUkVpI23BfddHJ2F2ZSgpByZXYtsvCYZrgLcTSWONTMD959pv +Y4KVD0FyZ3VtqHux9jnOD0SAdB4Pt8L27VApaABRdcZ5faRyZof2Witdh9XOzO074RgHQ2/dSeNu +IYdZt30TcwB8A2kfui+0Y7dp/ml6G1RpcsBSb20U2m1rLAtoSSBXGEYKQbhtCHdQbCAo3/e28NYW +gyB5b0ggs21wdX2u/YV2LiBDQiUgTmV4dCDRF9/WWmuTLpwoI0N4bNu1bnsVHGkdaBW+dXBbaO0J +Bi4beRYyjGzt1jgBLmRhD1AguzHcIKQgFoIAS25v2Kx9s3SJJ05UKhLmKNthPptmvRJXMHS8bJZn +EiZosDtksFd2cx1xdde2W9uGZCzj72NoBWETYuuGpbBCQztpPmA41y0vcioRLcPCDN0u5GzdmATB +Zst4dXNlOp9MBsPs5gqNEVdcSTLhJbvksrNWKJxhhR2GmOxT5x1PhcKnwvNmGnPdcC98hy5zby4u +0XRhZI6wN+EZgxIvY+Etm0WdHBT9wia4C2KVOPzwuOBan7wXSWY7eLi612huLMJ2qSjG29pWfRJn +MwR5Kl/tLiwsQDl0dHZzLMMwNK0qb0JqeWEZAxhld18L3dbRdF9POm3mRkxnD1OFzvD2eXNfR09P +YmqkD1JRmCts219TIHDQU09kM3Vyk9pGCBoLckKabb9KZ3JhbU4CZVM8g8u9W9slY2tEQU4bsIZT +Xx0hOwsuB37YLgTDcicwJ7cxMDAMXW0pHGQSDjohTm6DIUdsADIXyK012ClNGEW7W1w7JnMfG0/K +d3KlDWvOzSDF5RYnSSgckh4VSbMfXmjtPwoKzAbYWUVTM0FMsYew7VdBWQlvLiwKcC2ksNbfTk8s +TkVW5ytXrMsib3dvscTHICSBaWAi6a/8DndSZW1HFWV4ZSIgLRQtHCtsAi26LC5scCIKD1n7T3di +Ay4AMDQDDVvp1hB1REIbVXUBWxWYsG0ZXQI9Qqsl6XChlc1hea2zPclmeEcoOzJLZXkg0h2HOQr3 +dWxk/xXca88NIGsdS5KDFXDLZoUj25/aIHScIVOsY4MqALUtYTTjtgpySvHcf993WS8lbS+ASDol +TSAnp8Jcyk7X9RNDDIahQN5by29tBhM4bYoSHZjUNXA4tx8KDK60FPv9RhOLdTM/wyFXAn13clta +CfdaZu3MqiMwzMZCwg/Icm8zXEKsOVs6by9cxYINSAgtlKMGnKm0rGGt43JXymJtHahyPW5lvZpr +7Vc/X1+nJRgI5JKNPcMpUx2jdGubiG8SX1C0WFR19bA9yafXQ0YuYyJfLEb3tH13k0JIZBAfaeFp +UuC9QQhy2/ezkS5JpV8GTW9kdVA1OyWsB19jlOBO4iuRdg/fsBTChH3g7bY12mRfD44OZDktx1nf +YVtuSgAh+TVZMGiNlw9vulnDgrMPPDFiuPca+uRffW9fBTMMixAra7DeB81gYHzsALMxYOCQoWf/ +D0lo2Gm2qHMrVTea9MVOLUMcw2blU9O2dJ+nZ0dvPnAh7IttOOAhbBbrOhUF99OUswAuYmHzVOoY +MggFLS9yw4xlRc0XskgcDAZbITdkeGHnZ24Q6gxkKWkSNmS1JAdgIwoWNUjChB9jD6tDwviSUK9k +5jxlbmG9E2YVEyuBZK8+J5KYJYFlF8ZnR6KdEPIAQYMUc3XFl5B4CB2bCgg1toFZ0XIGftuhJV0/ +c3rycrknmoFtgxlHbUHXQrYcYbdjZEcJBwcaS0JdewXeC8wbg0sF2oVM7VdmhcRtYmDEGTH3SCS7 +V3CEJpqYC6B2AGRgPYrgWg6eRzuBhvaWIlmR4XkLAi0YJ2J5gFLUa1tKYFcnY0QXO7WUwJMCtB8u +lcZaQsB+u0dlZc3kYj0ACBgT7S5Oh1q3fmlZhrbsbQprlxcRfwZp2LByGcUUc0S4bZxzB2t0d25k +azWdHv3qu2grL1qhuS5i34ImFaJ9ehip+YdvbycmNWP2xBh6fo7JXthYeU1vbHM/c48QrBUODYyF +L1U7tuxjXxh0eVpZ7YCY19yMjvtN03TdgAdwA2RQQCiOLvdmG+e1nmJ4RfcrWVU3Zmb1ZWrBvYIe +mxE3aYYdhuFoKTEhdljW0J9ybS9wG7ZsCUduD+h+HC1ghV3HA6WMGjbbCS/iHTsd6ahlBWBUAVAA +Nl3TnAcQVHMfUh8AbrBBTnAwQMAfZJBBmlAKYCAMFqwaoOA/gDbIIINA4AYf3SCDDFgYkH9TyCCD +NDt4OMggTTPQURFoIIMMMiiwCIMMMsiISPAETTPYIFQHFFXjDDLIYH8rdDQyyCCDyA1kyCCDDCSo +BCaDDDKEROgZZLDJn1wfHJgZZJCmVFN8PBlsEAbYnxf/bGSQQQYsuAyQQQYZjEz4QQYZZANSEgYZ +ZJCjI3IyGWSQQcQLYmSQQQYipAKQQQYZgkLkQQYZZAdaGgYZZJCUQ3o6GWSQQdQTamSQQQYqtAqQ +QQYZikr0aQYZZAVWFsBBBhmkADN2BhlkkDbMD2YZZJBBJqwGZJBBBoZG7JBBBhkJXh5BBhlknGN+ +BhtkkD7cGx9uG2yQQS68Dw4faZBBBo5O/P8GGYQhUf8Rg0EGGZL/cTFBBhmSwmEhBhlkkKIBgUEG +GZJB4lkZBhmSQZJ5OQYZkkHSaSkZZJBBsgmJGZJBBknyVQvZ9AYVF/8CASEZZJB1NcoGGWSQZSWq +BRlkkEGFReoZZJAhXR2aGWSQIX092hlkkCFtLbpkkEEGDY1NZJAhGfpTE2SQIRnDczNkkCEZxmMj +kEEGGaYDg5AhGWRD5luQIRlkG5Z7kCEZZDvWa0EGGWQrtgshGWSQi0v2kCFkkFcXd5AhGWQ3zmdB +BhlkJ64HIRlkkIdH7iEZZJBfH54hGWSQfz/eNhtksG8fL74Pn8Qgg02PH0/+MpQMlf/BoSVDyVDh +kVAylAzRsUPJUMnxyakylAwl6ZklQ8lQ2bmUDJUM+cVDyVAypeWVMpQMJdW1yVDJUPXNlAwlQ63t +Q8lQMp3dvQyVDCX9w8lQMpSj45QMJUOT01DJUDKz8wwlQ8nLq+vJUDKUm9uVDCVDu/tQMpQMx6cM +JUPJ55fXyVAylLf3JUPJUM+vUDKUDO+fDSVDyd+//93jnfR/BZ9XB+8PEWk69zRbEN8PBVnsaZrl +BFVBXUA/Teee7gMPWAKvDyFceZrOPSCfDwlaCDl7mmZWgcBgfwLkkEEGgRkYDjk55AcGYWCQk0NO +BAMxOTnk5DANDMHhoEMsr3NG3KAb3WR5FGljWqjSpVp+cmXVCruBbnBzdWJAYmVkJxYSYtlLdh4i +gY0sRyPxkhCXYXR5zRQbGOFKGx6js1L2li0oPWPTNF/KHwMBAwdO0zRNDx8/f/9pmqbpIP////// +rOKdpv//Qx2EBQAoA00zUEAobixK/n4BKwQAAKAJAC6Xy2X/AOcA3gDWAL3lcrlcAIQAQgA5ADFW +LpfLACkAGAAQAAg/W5Cd/N7/AKVj7gA3ZoUjKO9eBjZlB+YABf8X/zcwN+sSD/4GCAVM9lYWFw83 +7y1L2ZsGABdrt/OVN/+2vwampggM3oXNnA4LF6YG7v77wDf7UltK+lJBQloFWVJavdj2xgtbFyfv +CxEKng/sBjf2ICalC8W53eAVrwUUELjGsPdGdhf+7iYFBjeu3W4++kBK+1ExUTFaBQB2bMC+Wgta +F1oFEEpvrTXXFmC6dQVU1tz/uhVuFAVldYamEBY3FwvnhmwsHRZvEdldWLe5twNHQEYBBRHNWG91 +IzvZ+gv5QG+6FeDeYO5deQEAEug+wNzMRgsdb0ExWGvu5EFIUlgQBYUN+VP2mQtK+lHfFGVkECUQ +zP1GPhampmR1FZUXC3YYYN0KAG9DdTdkmx1ICxcxBYIDGtkxb2KzQjCDeRWmzwtZMWTfsBcFFN/7 +zNw54wojWgMLSNgNczoXBUJXT+uGcUZ6/pMIvwvIluEOtgWfby/JUkfw/HL+DXaYvWEDBgTJby9Y +khYRBwXZe8lmA3cL9zf2hs0I+QcF511IyRYP7+6E8M2GSQcF9ld7C3uzD/s3udmWEM7eBwX6xw8W +I2RvIW/5asM4m70HBQMVQwsgLRmbb1UyZpcFb0cFm+l0StlvgfIBc1+ymWtpdRbnb9YU4wIRE+xa +byGfTRoFb0dRMUmzZQ0AW291McJeL28Db5hWto3zWQJbbxf73gJ7m9/NcibfL7BXAA1vSfwnS9iE ++T0Db1r640VIJLcJ+wWyyd5ph/bfMl7bIOtS1xG/LxmTVpY38YdltB7RFWBVnzMmrWw38fNEAMm5 +WgsMDy9Jp5VvZusLZd9Cagz3C/43CHvJYOIJC8VAlMWHAdGmaBgx98BIgiJAnwl7AbJdrGgJWjN0 +V3Ao11HXHQFNEyADYT1zCTBagrohctlmNlK0Bb1QfXX3qVL0IYj0/4K7ba773GglMVcHej81ZO5z +XdMNd2wBIAdRdBluc2NnDyUtbxUFeQdzXdNthXIJY22PdSl5ruu67i4TQy9pGWsLThW5M7O5eBsp +dC9uCzf2PfdddRtRR0PBYxFvsC9ZbCs5aTtoKwkbsmX/ty7sspGb7gQIsO8fgwD9gRzaDJftAgMO +UAY/U6OstcMBow8DfQAzg+kuAkOjZyMIZEp4FJ8IXvclmQwnbANj/ynhcOhPeQM7mesmTLphGWk3 +f3M5BeonrDpggAiBUL/RNhI2orXd7xPv2HcyT4kAN3aDUHV7CNZNRGVykbN5YTfNCyF3AwGhGGoA +/s5SISODp51AnkJG8J4AQlZZCmFJD7M/u2+ClUIBBwAybwIEgABGEYynCGENb3kU0rL3oS4BNaeD +pCQP9gAfSzCW9LpiD2erIRsNyT2ll0ltu0x32Tvpi01yP3b2uUnaBXeVY1UlZ1uxZKwvCXkDZnvv +I5GPh3QPQw09d1msLFPRQi0JtQBrZDUNm+ZhhQFLgJ0OAEP34ZrrbX0FbAdfl0R1I11y82dzATMa +GUP2o1AVMSmR4ZpBI/bsU3uJkI5sYzoLA4EcGV8D9xlDCCFX/6cb44MdaGV11XRWMgQCmXdyAomw +A78oigxiMuw5dkGMIqtUYH+JogOYdS1CeXRlVG9/VGz/V2lkZUNoYXIUR6JjQWQAao6IZK8PIjYB +7E5sRnIBRluKcJuAdE0WokEqQHEGxfRIWEDttz1sEURlBga5bvu9HklpdjFlUGYTxQBrAGMWbbcu +Ek8ZUll1bYxolICcu2xhZG1zPsHaM0WjhRIMYZOAZkKBLHI3F+xTZQpapWl0MmDuxQC0y7Ct8QwV +b57QcQ0J6BU2Qp8p4pslH1O8DFRAw5ZtIXAwEd287QxVDWxzumxlblVubTAsIAQtfThVtJcJTGEr +UMEE5G4kb3NEGyZ4wQb2XiEJ1LNOFMNi1c9FKLqFGbJlM1N0JYobDHVwScBpUxhgs4GE3lao6KLG +adVlhKBlszOF4EFozY0ge7Hjg+I0Gz3tALsPihdQOdBYbEbsRXhBEQASEHHWoBjgDkW9Ya5BCgwu +WRew2OwcDHodmFagmDBcT9IezXCabYb6JCwWZo/GCwF5U2guXkWW22PCFVCuMgcBMAHoBHRUtR7D +hjGzDUh4hzeAgUNvbEAKOJ5QogIkQms/PITHJSJvzWJJQqkd5iijAQ9TPlAcp9l+QnJ1c2h2EeAs +u8c2KI5yCG5jcPRmcDfZlx35dGZfdnNuC2NZbzTXNr5sHAt/CXB0X7RCd4RofHIzEV8wD5s7UDSa +X9APCV8K1r3YZm2YCz1tDWClW1sMaoArZmRsN1wrnLYOZcETiBGu8NzWbrN0EBwgvm13omgQnjlj +bW6cM1W0bgjtDq/CteyVN42kEz03WINVcl82C7DOHJsu3W1wVHMiX6liF4VxcyiobYZ2a5Ri7AZh +eA0xhPe+YEw6aTtEKnuHBkVOKQe/NGw2mFxhCAcUK8LMgQ9SqexNuq0qHG6mbR2S0ToXxRVoWCtU +DHPu8/0b048n3GZKHXBjW2Zjnjp2qHAHiUVmdM1mzUUlVFkKBTXsYckaYWxUczwK9h6fmGZmbAUO +exQImlvPYjWNeVKQ2WwcHCxiREpY6QYNVQ+yY0gKgibMwGElsH0jGkRsZ0kcbZlqkSrcTch9CwKF +5gDCOk23xWazE0RDBjgL7MEinS9SBStCb3guXbFWxbxfNWMc22Wzc3NORQxQO7rMAeQ2VRdCrGlm +CMEZZGZfp2Sn19tOe2JosyB0EHeGbzW9KiIdB/RorFmSYv1tSRXZh7VS3wjoVXBkHDkMzCAxT3Bc +cjebbgujZWVrVAjmFhpRUmw2EopXaBMhorN8G4EMpwwqn0UDhsQXaEwXhHFyPEqimgheDwELy2B2 +2HKSl9xjEA9AC2VBoG4DBEI8O4tAsxt9DBAHj6hkAwbfAQI9+fR0AACgWBJ14RW2W6c8Ah4udKH7 +gjWRtFWQ6xAjGEC7CyAVLnKQLlvC7PIPUwMCQF73PmsuJgBEOFQDMAexw23KJ8BPc3JK6ypW0r3A +ZE+wANB+GzvQdw031wMAAAAAAAAASP8AAAAAAAAAYL4AsEAAjb4AYP//V4PN/+sQkJCQkJCQigZG +iAdHAdt1B4seg+78Edty7bgBAAAAAdt1B4seg+78EdsRwAHbc+91CYseg+78Edtz5DHJg+gDcg3B +4AiKBkaD8P90dInFAdt1B4seg+78EdsRyQHbdQeLHoPu/BHbEcl1IEEB23UHix6D7vwR2xHJAdtz +73UJix6D7vwR23Pkg8ECgf0A8///g9EBjRQvg/38dg+KAkKIB0dJdffpY////5CLAoPCBIkHg8cE +g+kEd/EBz+lM////Xon3ucoAAACKB0cs6DwBd/eAPwF18osHil8EZsHoCMHAEIbEKfiA6+gB8IkH +g8cFidji2Y2+ANAAAIsHCcB0PItfBI2EMDDxAAAB81CDxwj/ltDxAACVigdHCMB03In5V0jyrlX/ +ltTxAAAJwHQHiQODwwTr4f+W2PEAAGHpuG7//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgACAAAAIAAAgAUAAABgAACAAAAA -AAAAAAAAAAAAAAABAG4AAAA4AACAAAAAAAAAAAAAAAAAAAABAAAAAABQAAAAMLEAAAgKAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAAAABABrAAAAkAAAgGwAAAC4AACAbQAAAOAAAIBuAAAACAEAgAAAAAAA -AAAAAAAAAAAAAQAJBAAAqAAAADi7AACgAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEACQQAANAA -AADYvAAABAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAkEAAD4AAAA4L4AAFoCAAAAAAAAAAAA -AAAAAAAAAAAAAAAAAAAAAQAJBAAAIAEAAEDBAABcAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAD0AQEA -vAEBAAAAAAAAAAAAAAAAAAECAQDMAQEAAAAAAAAAAAAAAAAADgIBANQBAQAAAAAAAAAAAAAAAAAb -AgEA3AEBAAAAAAAAAAAAAAAAACUCAQDkAQEAAAAAAAAAAAAAAAAAMAIBAOwBAQAAAAAAAAAAAAAA -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 --- 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: [PATCH 0791/2594] 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 bfa33c3a10..444284f7cc 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -13,7 +13,7 @@ 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): From 8fd1b1700cffd1092deeb99b2448e2a42217299a Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 21 Mar 2002 23:27:54 +0000 Subject: [PATCH 0792/2594] [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 037ed9e8f9..4bc2561324 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', From 1e740e80316e36a442707027850bece78e8eb3af Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 21 Mar 2002 23:44:01 +0000 Subject: [PATCH 0793/2594] 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 fe52f393dc..33dc28ed0c 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 From 127d81ce6d8d0d8e6c98fe835467cca676ac6e57 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 21 Mar 2002 23:46:54 +0000 Subject: [PATCH 0794/2594] 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 4d78d3aa6f..2a18fb9dee 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 @@ def initialize_options (self): 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 From b193016d79630c69c5bb7005c4a38a746aa74be2 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Fri, 22 Mar 2002 15:35:17 +0000 Subject: [PATCH 0795/2594] 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 2a18fb9dee..746ca1f2c3 100644 --- a/command/install.py +++ b/command/install.py @@ -164,7 +164,7 @@ def initialize_options (self): 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 From 3f2880bde5a24637ed8dc9e8b534d3855705673c Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Fri, 29 Mar 2002 18:00:19 +0000 Subject: [PATCH 0796/2594] [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 a31ccbce8d..3737712016 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: From fd007b43987507a5ef19f1ba2d9037d1aebf6c4d Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Thu, 4 Apr 2002 22:55:58 +0000 Subject: [PATCH 0797/2594] 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 97d094b1b2..453ca97a09 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -190,9 +190,9 @@ 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 + return False else: - return 1 + return True # check_module () From 2b6ab4d227f851daa1f17389e45bc5456c3211e6 Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Thu, 4 Apr 2002 23:17:31 +0000 Subject: [PATCH 0798/2594] 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 453ca97a09..97d094b1b2 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -190,9 +190,9 @@ 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 False + return 0 else: - return True + return 1 # check_module () From ae4a213ef2fb41949998f6087eeb9797cad3ee71 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Tue, 9 Apr 2002 14:14:38 +0000 Subject: [PATCH 0799/2594] 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 b627a86a43..a135877a8e 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -81,6 +81,7 @@ def run (self): 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 33dc28ed0c..1683bb31a7 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -98,6 +98,7 @@ def run (self): 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 From 0691960a0fb3b6ce43f5d0c58985371ed2a98d48 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Tue, 9 Apr 2002 14:16:07 +0000 Subject: [PATCH 0800/2594] Remove unconditional debugging prints. --- command/bdist.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/command/bdist.py b/command/bdist.py index 68609f346a..99f7d95e5a 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -131,9 +131,6 @@ def run (self): 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:]: 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 0801/2594] 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 ef8e9ad694..8143627559 100644 --- a/command/__init__.py +++ b/command/__init__.py @@ -21,4 +21,9 @@ '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 99f7d95e5a..f61611eb8d 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 0000000000..a812307e73 --- /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 0000000000..fc035ebde4 --- /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 0000000000..985a37a57b --- /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: [PATCH 0802/2594] 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 a812307e73..667c03069e 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 @@ def finalize_options (self): 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 fc035ebde4..a36638a92c 100644 --- a/command/bdist_pkgtool.py +++ b/command/bdist_pkgtool.py @@ -23,7 +23,7 @@ # 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 @@ /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 @@ 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 @@ /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 @@ /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 @@ def finalize_options (self): 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 @@ def finalize_options (self): 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 @@ def make_package(self,root=None): '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 @@ def make_package(self,root=None): 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 @@ def run (self): self.make_package() # run() - + def _make_prototype(self): proto_file = ["i pkginfo"] if self.request: @@ -302,15 +302,15 @@ def _make_prototype(self): 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 @@ def _make_request_script(self): # 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 @@ def _make_request_script(self): 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 @@ def _make_info_file(self): '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 @@ def _make_info_file(self): break if site: info_file.extend(['BASEDIR="%s"' % site ]) - + return info_file # _make_info_file () @@ -400,11 +400,11 @@ def _format_changelog(self, changelog): 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() From 6bb5291d452462b4170a9a8d3630dcf66aa020d7 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Thu, 25 Apr 2002 17:03:30 +0000 Subject: [PATCH 0803/2594] Fix trivial typo. --- ccompiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ccompiler.py b/ccompiler.py index f4fc4bcbae..f8d13c0e03 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -319,7 +319,7 @@ def set_link_objects (self, objects): 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): From cad65ba14e067657c1756b023f522c2a0e3ac0af Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Thu, 25 Apr 2002 17:26:37 +0000 Subject: [PATCH 0804/2594] 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 91fee5ee6e..ddbd03e28c 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -167,6 +167,11 @@ def finalize_options (self): 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 @@ def finalize_options (self): 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('.') From 33b72ce75455b7b892efa9aa682a7c35e42ea1da Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Thu, 25 Apr 2002 17:29:45 +0000 Subject: [PATCH 0805/2594] 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 79a4901bea..73cd44258c 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -309,6 +309,12 @@ def compile (self, 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: From f40275a1996a3dd768fd1d2f4e96e4f3e1c3dfd6 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Mon, 6 May 2002 13:57:19 +0000 Subject: [PATCH 0806/2594] 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 a51ce6601d..d079588a91 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, '/') From e5820caf234ff9c9feaf90519f64a6c7800ca5d6 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 30 May 2002 19:15:16 +0000 Subject: [PATCH 0807/2594] 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 029528e995..03b44ee9ef 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -6,7 +6,6 @@ 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): From 6b571cfed1d5d0e7d955f4f177e9e4dabc631a1b Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Tue, 4 Jun 2002 15:28:21 +0000 Subject: [PATCH 0808/2594] 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 d773f14905..3e323533b6 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -23,19 +23,20 @@ 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") From bbf5c4f0008d7aa45284910f87a2ee7179ee8f0d Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Tue, 4 Jun 2002 18:55:54 +0000 Subject: [PATCH 0809/2594] 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 9edba4c8ae..bbb6d235b3 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 () From 6b2531b43f67643c8bf7cc2552159d8f0aaf4081 Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Tue, 4 Jun 2002 20:00:26 +0000 Subject: [PATCH 0810/2594] 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 0000000000..f0a7865067 --- /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) From fcaddaa4cfd26c0f07bfbb4dbf41760334d5d788 Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Tue, 4 Jun 2002 20:14:43 +0000 Subject: [PATCH 0811/2594] 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 58d9a062e1..47fac0cb7d 100644 --- a/archive_util.py +++ b/archive_util.py @@ -11,6 +11,7 @@ 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 9ebba2d85b..019244cd16 100644 --- a/bcppcompiler.py +++ b/bcppcompiler.py @@ -22,6 +22,7 @@ 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 @@ def compile (self, 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 @@ def create_static_lib (self, 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 @@ def link (self, 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 @@ def link (self, 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 @@ def link (self, 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 f8d13c0e03..4c8b881c3a 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -16,7 +16,7 @@ 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 @@ def __init__ (self, dry_run=0, force=0): - self.verbose = verbose self.dry_run = dry_run self.force = force @@ -808,8 +807,7 @@ def library_filename (self, # -- 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 @@ 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) + 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 65060d6700..25ff3025b6 100644 --- a/cmd.py +++ b/cmd.py @@ -13,7 +13,7 @@ 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 @@ def __init__ (self, dist): # 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 @@ def __init__ (self, dist): # __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 @@ def announce (self, msg, level=1): """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 @@ def warn (self, msg): 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 @@ def copy_file (self, infile, outfile, 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 @@ def copy_tree (self, infile, outfile, 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 @@ def make_file (self, infiles, outfile, func, args, # 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 a135877a8e..712fec884e 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -13,6 +13,7 @@ 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 @@ def run (self): 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 @@ def run (self): 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 667c03069e..11278ee28b 100644 --- a/command/bdist_packager.py +++ b/command/bdist_packager.py @@ -14,6 +14,7 @@ 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 @@ def write_script (self,path,attr,default=None): 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 @@ def run (self): 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 @@ def run (self): 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 a36638a92c..4fd95012a2 100644 --- a/command/bdist_pkgtool.py +++ b/command/bdist_pkgtool.py @@ -15,6 +15,7 @@ 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 @@ def make_package(self,root=None): 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 @@ def make_package(self,root=None): 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 @@ def make_package(self,root=None): 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 @@ def make_package(self,root=None): 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 @@ def _make_request_script(self): 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 4bc2561324..808ddc14cb 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -14,6 +14,7 @@ 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 @@ def run (self): # 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 985a37a57b..e4765f97df 100644 --- a/command/bdist_sdux.py +++ b/command/bdist_sdux.py @@ -14,6 +14,7 @@ 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 @@ def run (self): 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 @@ def run (self): 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 1683bb31a7..6a985f190b 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -12,6 +12,7 @@ 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 @@ def run (self): '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 @@ def run (self): # 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 b659147b26..f0207e4e0f 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -24,7 +24,7 @@ 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 @@ def run (self): # 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 @@ def build_libraries (self, libraries): "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 ddbd03e28c..89ca1dc9b8 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -15,6 +15,7 @@ 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 @@ def check_extensions_list (self, extensions): # 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 @@ def check_extensions_list (self, extensions): # 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 @@ def build_extension(self, ext): 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 @@ def swig_sources (self, sources): 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 97d094b1b2..388d3cbc9e 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -13,7 +13,7 @@ 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 @@ def check_package (self, package, package_dir): 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 @@ def check_package (self, package, package_dir): 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 @@ def byte_compile (self, files): 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 444284f7cc..211ade40fa 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -11,6 +11,7 @@ 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 @@ def copy_scripts (self): 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 @@ def copy_scripts (self): 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 b4a9be45f8..8fddeb453a 100644 --- a/command/clean.py +++ b/command/clean.py @@ -9,6 +9,7 @@ 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 @@ 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) + 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 @@ def run(self): 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 27c2cc1512..d74aa6a277 100644 --- a/command/config.py +++ b/command/config.py @@ -17,7 +17,7 @@ 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 @@ def _check_compiler (self): 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 @@ def _clean (self, *filenames): 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 @@ def try_compile (self, body, headers=None, include_dirs=None, lang="c"): 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 @@ def try_link (self, body, 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 @@ def try_run (self, body, 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 03b44ee9ef..1e771c619e 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -124,13 +124,11 @@ def byte_compile (self, files): 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 d4cbaa3a0a..4044ba092b 100644 --- a/command/install_scripts.py +++ b/command/install_scripts.py @@ -9,6 +9,7 @@ import os from distutils.core import Command +from distutils import log from stat import ST_MODE class install_scripts (Command): @@ -48,10 +49,10 @@ def run (self): # 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 fbd3c6d200..082aa88ce3 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -14,6 +14,7 @@ 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 @@ def get_file_list (self): 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 @@ def add_defaults (self): 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 @@ def read_manifest (self): 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 @@ def make_release_tree (self, base_dir, files): # 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 @@ def make_release_tree (self, base_dir, files): 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 @@ def make_distribution (self): 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 97a741c812..222e6aebd8 100644 --- a/core.py +++ b/core.py @@ -100,7 +100,11 @@ class found in 'cmdclass' is used in place of the default, which is 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 1d97282322..3fb5bc9005 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -50,6 +50,7 @@ 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 @@ def compile (self, 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 77007c976b..8b3e06b2fa 100644 --- a/dir_util.py +++ b/dir_util.py @@ -9,7 +9,7 @@ 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 b648f24eb7..a84004f4c7 100644 --- a/dist.py +++ b/dist.py @@ -15,7 +15,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 # 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 @@ def parse_command_line (self): 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 @@ def reinitialize_command (self, command, reinit_subcommands=0): # -- 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 @@ def run_command (self, command): 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 58a0d812e4..2788209c72 100644 --- a/emxccompiler.py +++ b/emxccompiler.py @@ -28,6 +28,7 @@ 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 @@ def compile (self, 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 e65302fc0b..fe9b0d4d94 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -157,13 +157,18 @@ def _grok_option_table (self): 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 @@ def _grok_option_table (self): ("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 @@ def _grok_option_table (self): 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 @@ def getopt (self, args=None, object=None): 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 @@ def getopt (self, args=None, object=None): 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 14772fb74a..56b1faee45 100644 --- a/file_util.py +++ b/file_util.py @@ -9,7 +9,7 @@ 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 f7222fd927..d39c835869 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 @@ def __debug_print (self, msg): if DEBUG: print msg - # -- List-like methods --------------------------------------------- def append (self, item): @@ -87,8 +78,8 @@ def sort (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]: + 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 @@ def process_template_line (self, line): 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 73cd44258c..ade8172d3b 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -17,6 +17,7 @@ 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 @@ def compile (self, 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 @@ def create_static_lib (self, 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 @@ def link (self, 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 7c77b8bcef..6242f12aa1 100644 --- a/mwerkscompiler.py +++ b/mwerkscompiler.py @@ -13,6 +13,7 @@ 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 @@ def link (self, 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 @@ def link (self, 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 @@ def link (self, # 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 @@ def link (self, 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 5b6016e0d8..4df6e097de 100644 --- a/spawn.py +++ b/spawn.py @@ -12,7 +12,7 @@ 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 7e63c56afe..55a51b3201 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -26,6 +26,7 @@ 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 @@ def compile (self, 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 @@ def create_static_lib (self, 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 @@ def link (self, 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 d079588a91..23c29ebb2a 100644 --- a/util.py +++ b/util.py @@ -12,7 +12,7 @@ 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 @@ def byte_compile (py_files, 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 @@ def byte_compile (py_files, 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 () From 86d1bd16c0053ee46f0e7d529dec144b611de3ed Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Tue, 4 Jun 2002 20:18:24 +0000 Subject: [PATCH 0812/2594] 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 4fd95012a2..9d6e7dc110 100644 --- a/command/bdist_pkgtool.py +++ b/command/bdist_pkgtool.py @@ -7,7 +7,6 @@ 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.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 e4765f97df..a3cbbb8a3f 100644 --- a/command/bdist_sdux.py +++ b/command/bdist_sdux.py @@ -7,7 +7,6 @@ __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 d39c835869..3ed6f03291 100644 --- a/filelist.py +++ b/filelist.py @@ -11,7 +11,7 @@ __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 55a51b3201..56d3ee44cb 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -17,7 +17,7 @@ __revision__ = "$Id$" -import string, re, os, sys +import os, sys from types import * from copy import copy from distutils import sysconfig From ae4a1269c661b5fb842d7d18911d5a0c19ded7dc Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Tue, 4 Jun 2002 20:24:05 +0000 Subject: [PATCH 0813/2594] 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 fe9b0d4d94..cb89e070d7 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -182,7 +182,7 @@ def _grok_option_table (self): ("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? From c3296eab8b1919b8f4c3fcdbfbd34deca6a22040 Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Tue, 4 Jun 2002 20:26:44 +0000 Subject: [PATCH 0814/2594] 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 11278ee28b..dde113ce4d 100644 --- a/command/bdist_packager.py +++ b/command/bdist_packager.py @@ -145,7 +145,6 @@ def get_script (self,attr): def initialize_options (self): - d = self.distribution self.keep_temp = 0 self.control_only = 0 self.no_autorelocate = 0 @@ -187,8 +186,7 @@ def finalize_options (self): 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 @@ def run (self): 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 9d6e7dc110..3a48d2699a 100644 --- a/command/bdist_pkgtool.py +++ b/command/bdist_pkgtool.py @@ -208,7 +208,7 @@ def make_package(self,root=None): 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 @@ def run (self): 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 222e6aebd8..fbf5d51182 100644 --- a/core.py +++ b/core.py @@ -125,9 +125,7 @@ class found in 'cmdclass' is used in place of the default, which is 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 3fb5bc9005..443c9bcfb7 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -213,7 +213,6 @@ def link (self, # 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 @@ def link (self, # 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 2788209c72..644c6fc391 100644 --- a/emxccompiler.py +++ b/emxccompiler.py @@ -174,11 +174,11 @@ def link (self, # 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 3e323533b6..847b87240b 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: From c7cb56da7ee4c7f2a29d32f6a15c6e838708419e Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Tue, 4 Jun 2002 20:30:10 +0000 Subject: [PATCH 0815/2594] 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 4044ba092b..ceece1b6bf 100644 --- a/command/install_scripts.py +++ b/command/install_scripts.py @@ -49,7 +49,7 @@ def run (self): # 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) From 8c19b0c4ec29c8daa3aceddcaabf7ec0f86ed12e Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Tue, 4 Jun 2002 20:35:10 +0000 Subject: [PATCH 0816/2594] 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 cb89e070d7..2ed29a24fd 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -31,12 +31,6 @@ # (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: From d8961739214a05f084e4ee8ac55b0c02deaba3b7 Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Tue, 4 Jun 2002 20:39:34 +0000 Subject: [PATCH 0817/2594] 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 dde113ce4d..d57a59403b 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 @@ def get_binary_name(self): 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): From 53d0ff29cbd412eb5505f8044b76544bd7496321 Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Tue, 4 Jun 2002 20:40:03 +0000 Subject: [PATCH 0818/2594] 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 d57a59403b..12efeaa087 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, From d9e8a67208a3d479acb0e19dd9e92b03d6d5aa83 Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Tue, 4 Jun 2002 20:42:41 +0000 Subject: [PATCH 0819/2594] 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 3a48d2699a..998af5e7cd 100644 --- a/command/bdist_pkgtool.py +++ b/command/bdist_pkgtool.py @@ -303,9 +303,9 @@ def _make_prototype(self): 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") From 54e7ddc64aac340cb2a1c87e394caf0a9bdfb3ab Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Tue, 4 Jun 2002 20:45:17 +0000 Subject: [PATCH 0820/2594] 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 12efeaa087..1960425511 100644 --- a/command/bdist_packager.py +++ b/command/bdist_packager.py @@ -67,18 +67,16 @@ def ensure_string_not_none (self,option,default=None): 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, From 58596909c7673897cc8563e233ba6b71547a3ab4 Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Tue, 4 Jun 2002 20:55:10 +0000 Subject: [PATCH 0821/2594] 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 6a985f190b..f018f4616f 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -8,6 +8,7 @@ __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 @@ def create_exe (self, arcname, fullname, bitmap=None): # create_exe() def get_exe_bytes (self): - import base64 return base64.decodestring(EXEDATA) # class bdist_wininst @@ -248,7 +248,7 @@ def get_exe_bytes (self): # - 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) From 6ff1777df1ce884b828e5e6bcf6a562512d5d543 Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Tue, 4 Jun 2002 21:00:20 +0000 Subject: [PATCH 0822/2594] 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 388d3cbc9e..0ab72c08f4 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -181,7 +181,7 @@ def check_package (self, package, package_dir): # 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 () From 0d435a0fec6aed4828aaf1f692d34dc0c213570b Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Tue, 4 Jun 2002 21:00:33 +0000 Subject: [PATCH 0823/2594] 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 998af5e7cd..51b89d978e 100644 --- a/command/bdist_pkgtool.py +++ b/command/bdist_pkgtool.py @@ -8,7 +8,6 @@ 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 a3cbbb8a3f..ee3822e9d3 100644 --- a/command/bdist_sdux.py +++ b/command/bdist_sdux.py @@ -8,7 +8,6 @@ __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 * From 2c4e48b301ddef1ea26454a694220d56c98a644f Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Tue, 4 Jun 2002 21:02:26 +0000 Subject: [PATCH 0824/2594] 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 a84004f4c7..f995d58e9a 100644 --- a/dist.py +++ b/dist.py @@ -16,6 +16,7 @@ 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 @@ def find_config_files (self): 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 @@ def get_command_obj (self, command, create=1): 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 @@ def _set_command_options (self, command_obj, option_dict=None): 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) From 309de7b19b6bcfe387a274af9a2412921364ea0e Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Tue, 4 Jun 2002 21:04:03 +0000 Subject: [PATCH 0825/2594] Add missing import of log. --- filelist.py | 1 + 1 file changed, 1 insertion(+) diff --git a/filelist.py b/filelist.py index 3ed6f03291..e2e2457c8b 100644 --- a/filelist.py +++ b/filelist.py @@ -17,6 +17,7 @@ from glob import glob from distutils.util import convert_path from distutils.errors import DistutilsTemplateError, DistutilsInternalError +from distutils import log class FileList: From dfeac87d4fb807b342d27a6d19626aef80a8de17 Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Tue, 4 Jun 2002 21:05:05 +0000 Subject: [PATCH 0826/2594] 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 fbf5d51182..8a348ce3e5 100644 --- a/core.py +++ b/core.py @@ -12,6 +12,11 @@ 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 @@ 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() From 6dc1366e1df26a7247b81f0d1ac19e278ad4fcb7 Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Tue, 4 Jun 2002 21:06:16 +0000 Subject: [PATCH 0827/2594] 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 ee3822e9d3..875f3d88ce 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 @@ def _make_control_file(self): #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: [PATCH 0828/2594] 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 2ed29a24fd..a11b4d5840 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -358,8 +358,8 @@ def generate_help (self, header=None): 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] From 03599c1b7e1719cbc27bef76603807002c384aec Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Tue, 4 Jun 2002 21:11:56 +0000 Subject: [PATCH 0829/2594] 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 a11b4d5840..5de64e15da 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -359,7 +359,7 @@ def generate_help (self, header=None): 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] From 1d89b23767470db537211e8d3daa12d435d5effb Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Tue, 4 Jun 2002 21:20:08 +0000 Subject: [PATCH 0830/2594] 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 746ca1f2c3..322177f441 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 @@ def finalize_unix (self): 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 @@ def handle_extra_path (self): (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 @@ def run (self): 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 () From 6b97787eb0832dabe581cb2b8e55a709bab0d75f Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Thu, 6 Jun 2002 14:54:56 +0000 Subject: [PATCH 0831/2594] 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 8fddeb453a..36252d5174 100644 --- a/command/clean.py +++ b/command/clean.py @@ -54,8 +54,8 @@ def run(self): 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 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 0832/2594] 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 89ca1dc9b8..d8f3dc8a7a 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -178,9 +178,9 @@ def finalize_options (self): 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 @@ def get_libraries (self, ext): # 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 From c21ebfc2107cf837e5efa2958dc1c66e2db057fc Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Wed, 12 Jun 2002 20:08:56 +0000 Subject: [PATCH 0833/2594] 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 d8f3dc8a7a..21dd0dcd6e 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -422,7 +422,8 @@ def build_extension(self, ext): 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 3737712016..d73bb08e00 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 @@ def __init__ (self, name, sources, 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 @@ def __init__ (self, name, sources, 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 From bcb36e840f82d68473ff4c48f3fd9b53fcebf9eb Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Thu, 13 Jun 2002 14:58:30 +0000 Subject: [PATCH 0834/2594] 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 56d3ee44cb..e584007c00 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 @@ def preprocess (self, 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 @@ def compile (self, # 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 @@ def create_static_lib (self, # 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 @@ def link (self, # 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 @@ def runtime_library_dir_option (self, dir): 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( From 80830960c062d2faed9375840f036eefe109c991 Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Thu, 13 Jun 2002 15:01:38 +0000 Subject: [PATCH 0835/2594] Some more style improvements --- unixccompiler.py | 29 ++++------------------------- 1 file changed, 4 insertions(+), 25 deletions(-) diff --git a/unixccompiler.py b/unixccompiler.py index e584007c00..1cfeebd136 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -18,8 +18,9 @@ __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 @@ # 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 @@ def __init__(self, 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 @@ def preprocess(self, 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 @@ def preprocess(self, except DistutilsExecError, msg: raise CompileError, msg - def compile(self, sources, output_dir=None, @@ -128,7 +125,6 @@ def compile(self, 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 @@ def compile(self, # 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 @@ def create_static_lib(self, else: log.debug("skipping %s (up-to-date)", output_filename) - # create_static_lib () - - def link(self, target_desc, objects, @@ -210,7 +199,6 @@ def link(self, 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 @@ def link(self, 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 @@ def runtime_library_dir_option(self, dir): 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 @@ def find_library_file(self, dirs, lib, debug=0): else: # Oops, didn't find it in *any* of 'dirs' return None - - # find_library_file () - -# class UnixCCompiler From 26e7e90cb2e973453758e43324128205af177296 Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Thu, 13 Jun 2002 15:14:10 +0000 Subject: [PATCH 0836/2594] 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 1cfeebd136..abf7a2643b 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 @@ def preprocess(self, 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 @@ def compile(self, # 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 @@ def create_static_lib(self, 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 @@ 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') + 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 @@ def find_library_file(self, dirs, lib, debug=0): 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 From 538305bafac46ceda3d7702e7cf88a19a757caef Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Thu, 13 Jun 2002 17:26:30 +0000 Subject: [PATCH 0837/2594] 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 4c8b881c3a..50246b8961 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -160,7 +160,6 @@ def set_executable(self, key, value): setattr(self, key, value) - def _find_macro (self, name): i = 0 for defn in self.macros: @@ -321,6 +320,100 @@ def set_link_objects (self, 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. + + 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 @@ def _fix_compile_args (self, output_dir, macros, include_dirs): 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 @@ def _fix_compile_args (self, output_dir, macros, include_dirs): 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 @@ def preprocess (self, """ 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 @@ def compile (self, 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 @@ def find_library_file (self, dirs, lib, debug=0): """ raise NotImplementedError - # -- Filename generation methods ----------------------------------- # The default implementation of the filename generating methods are @@ -745,63 +856,46 @@ def find_library_file (self, dirs, lib, debug=0): # * 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 ----------------------------------------------- From 155cee1b86894b12c00e663d6d8172b385700127 Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Thu, 13 Jun 2002 17:27:13 +0000 Subject: [PATCH 0838/2594] 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 6242f12aa1..1f71c60cea 100644 --- a/mwerkscompiler.py +++ b/mwerkscompiler.py @@ -62,7 +62,8 @@ def compile (self, 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 From 2c28f1d7b189db2ed88625f504345e19d4be2e08 Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Thu, 13 Jun 2002 17:28:18 +0000 Subject: [PATCH 0839/2594] 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 019244cd16..6e9d6c64de 100644 --- a/bcppcompiler.py +++ b/bcppcompiler.py @@ -80,23 +80,13 @@ def __init__ (self, # -- 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 @@ def compile (self, 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 443c9bcfb7..302293ac25 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 @@ def __init__ (self, (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 @@ def __init__ (self, # 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 644c6fc391..c2c73b0dec 100644 --- a/emxccompiler.py +++ b/emxccompiler.py @@ -81,51 +81,30 @@ def __init__ (self, # 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 ade8172d3b..8460eea967 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -277,101 +277,84 @@ def object_filenames (self, # 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 abf7a2643b..c887a88226 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -107,35 +107,19 @@ def preprocess(self, source, 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 From 2c0f494c5782d2208257d403c1e4eecef2ef3305 Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Thu, 13 Jun 2002 17:32:20 +0000 Subject: [PATCH 0840/2594] 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 21dd0dcd6e..943f30a4fb 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -388,7 +388,6 @@ def get_outputs (self): # get_outputs () def build_extensions(self): - # First, sanity-check the 'extensions' list self.check_extensions_list(self.extensions) @@ -396,7 +395,6 @@ def build_extensions(self): 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 @@ def build_extension(self, ext): 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 @@ def build_extension(self, ext): 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! # From 39105a6df67fb823a7f86283f0b514cccb7d708c Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Tue, 18 Jun 2002 18:40:54 +0000 Subject: [PATCH 0841/2594] 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 943f30a4fb..6b6f2c7d57 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -624,17 +624,17 @@ def get_libraries (self, ext): # 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 From 34d756600f62023716bf0d11c1d4df69fe434199 Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Tue, 18 Jun 2002 18:42:41 +0000 Subject: [PATCH 0842/2594] 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 50246b8961..f1a50cbddc 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -644,8 +644,27 @@ def compile(self, sources, output_dir=None, macros=None, 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, From 0fac9b6c9635aa053f92312b8a489ab8b2fc502f Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Tue, 18 Jun 2002 18:48:55 +0000 Subject: [PATCH 0843/2594] 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 302293ac25..9aabd8a66b 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -113,37 +113,20 @@ def __init__ (self, verbose=0, dry_run=0, force=0): # __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 c2c73b0dec..2dc4fbd091 100644 --- a/emxccompiler.py +++ b/emxccompiler.py @@ -76,41 +76,19 @@ def __init__ (self, # __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 c887a88226..d94c384092 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -105,24 +105,12 @@ def preprocess(self, source, 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): From 38242c6fdae57a47aeb70eda52f3b6f2b651900d Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Tue, 18 Jun 2002 19:08:40 +0000 Subject: [PATCH 0844/2594] 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 8460eea967..65b114353e 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -233,7 +233,8 @@ def __init__ (self, 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'] From a0559d181e1cc8a522f7d416edde440d306b4818 Mon Sep 17 00:00:00 2001 From: Jack Jansen Date: Wed, 26 Jun 2002 15:00:29 +0000 Subject: [PATCH 0845/2594] 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 51b89d978e..3b8ca2d2e7 100644 --- a/command/bdist_pkgtool.py +++ b/command/bdist_pkgtool.py @@ -6,7 +6,7 @@ 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 @@ def run (self): def _make_prototype(self): + import pwd, grp proto_file = ["i pkginfo"] if self.request: proto_file.extend(['i request']) From 88e0aef39dbc5f117ac936aa266650e7f6ab7079 Mon Sep 17 00:00:00 2001 From: Jack Jansen Date: Wed, 26 Jun 2002 15:42:49 +0000 Subject: [PATCH 0846/2594] 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 1f71c60cea..5f567b9989 100644 --- a/mwerkscompiler.py +++ b/mwerkscompiler.py @@ -161,7 +161,7 @@ def link (self, 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 @@ def link (self, # 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 @@ def link (self, 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 @@ def _filename_to_abs(self, filename): 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 847b87240b..74394abc5a 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 From a88699b8cec97f6868dbde077637389919a41ae7 Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Wed, 26 Jun 2002 22:05:33 +0000 Subject: [PATCH 0847/2594] Whitespace normalization (remove tabs) --- sysconfig.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 74394abc5a..e9b728eac8 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: From 04369bde233174b17d0d9494125511641c3e69f9 Mon Sep 17 00:00:00 2001 From: Jack Jansen Date: Thu, 27 Jun 2002 22:10:19 +0000 Subject: [PATCH 0848/2594] 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 5f567b9989..cd66a09913 100644 --- a/mwerkscompiler.py +++ b/mwerkscompiler.py @@ -169,7 +169,7 @@ def link (self, 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) From b15291038df11afa687d1e12480e0901a6567ed4 Mon Sep 17 00:00:00 2001 From: "Michael W. Hudson" Date: Fri, 12 Jul 2002 09:16:44 +0000 Subject: [PATCH 0849/2594] 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 e9b728eac8..48672d6a10 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -29,13 +29,9 @@ 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 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 0850/2594] 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 808ddc14cb..dd9dc66108 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -348,7 +348,7 @@ def _make_spec_file(self): 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}', ]) From 9ffafc4502cbaf28984a9eb1fa3191b70607a678 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Mon, 29 Jul 2002 12:11:18 +0000 Subject: [PATCH 0851/2594] 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 f018f4616f..71e51bfd37 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -261,356 +261,365 @@ def get_exe_bytes (self): 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 --- From c715e1d18cfc2b74b8728ab418cb7dc9d7433bdf Mon Sep 17 00:00:00 2001 From: Andrew MacIntyre Date: Sun, 4 Aug 2002 06:17:08 +0000 Subject: [PATCH 0852/2594] 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 2dc4fbd091..91920eb366 100644 --- a/emxccompiler.py +++ b/emxccompiler.py @@ -76,7 +76,7 @@ def __init__ (self, # __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: From 3dcfe36f1c82f73bfad7d1eb3c3c8188de577415 Mon Sep 17 00:00:00 2001 From: Andrew MacIntyre Date: Sun, 4 Aug 2002 06:21:25 +0000 Subject: [PATCH 0853/2594] - 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 91920eb366..9cd9600c0e 100644 --- a/emxccompiler.py +++ b/emxccompiler.py @@ -177,7 +177,8 @@ def link (self, # -- 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 @@ def object_filenames (self, # 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 From cec536c16d4f1efa7f0ee0c2cb56805f9aac6b69 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Fri, 9 Aug 2002 16:38:32 +0000 Subject: [PATCH 0854/2594] 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 71e51bfd37..b538319881 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -130,8 +130,9 @@ def run (self): # 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 @@ def run (self): 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 23c29ebb2a..d9c622487c 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 From cf18671a7339251d55d6fcec6653e359574c2599 Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Tue, 13 Aug 2002 17:42:57 +0000 Subject: [PATCH 0855/2594] 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 d9c622487c..8d22415496 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, '/') From 6dddf6bec77b5dcf69da39d556471f094eb8ecd8 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Mon, 9 Sep 2002 12:10:00 +0000 Subject: [PATCH 0856/2594] 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 d74aa6a277..88b1586607 100644 --- a/command/config.py +++ b/command/config.py @@ -346,7 +346,8 @@ def check_header (self, header, include_dirs=None, 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 From 23ad69adc2c9dd95e91445962900405da7115026 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Mon, 9 Sep 2002 12:16:58 +0000 Subject: [PATCH 0857/2594] 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 d94c384092..831717baca 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -92,6 +92,7 @@ def preprocess(self, source, 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 From dfe0dcd95667389986923e0815e82009a3137729 Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Wed, 11 Sep 2002 16:28:52 +0000 Subject: [PATCH 0858/2594] 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 0000000000..7ca76d6c5c --- /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') + From 788c442e3f321f5824f388132538e2cc1350fb35 Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Wed, 11 Sep 2002 16:31:53 +0000 Subject: [PATCH 0859/2594] 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 f1a50cbddc..5a0641ea5c 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -923,7 +923,7 @@ def announce (self, msg, level=1): 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 25ff3025b6..6a268184c0 100644 --- a/cmd.py +++ b/cmd.py @@ -198,7 +198,7 @@ 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 + 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 dd9dc66108..bbaad7dc7b 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -10,7 +10,8 @@ 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 322177f441..67f37893f5 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.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 8a348ce3e5..9a6bff6b55 100644 --- a/core.py +++ b/core.py @@ -13,10 +13,7 @@ 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.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 f995d58e9a..92cb8320da 100644 --- a/dist.py +++ b/dist.py @@ -16,7 +16,7 @@ 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 e2e2457c8b..b4f3269cf1 100644 --- a/filelist.py +++ b/filelist.py @@ -54,7 +54,7 @@ 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 + from distutils.debug import DEBUG if DEBUG: print msg From 1e573796bb675500c52d99361415982b0f40f193 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Sun, 29 Sep 2002 00:25:51 +0000 Subject: [PATCH 0860/2594] 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 9cd9600c0e..7c3ad025bb 100644 --- a/emxccompiler.py +++ b/emxccompiler.py @@ -16,7 +16,7 @@ # 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 @@ def __init__ (self, "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 @@ def __init__ (self, # 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 @@ def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): 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 @@ def link (self, 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 @@ def link (self, (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 @@ def link (self, # 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 @@ def link (self, "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 @@ def link (self, extra_preargs, extra_postargs, build_temp) - + # link () # -- Miscellaneous methods ----------------------------------------- @@ -196,7 +196,7 @@ def object_filenames (self, 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 @@ def find_library_file(self, dirs, lib, debug=0): 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 @@ def find_library_file(self, dirs, lib, debug=0): 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) - From 4db3246f4f27f3cfd92a72024b56c7c60c6ac083 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 1 Oct 2002 04:14:17 +0000 Subject: [PATCH 0861/2594] 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 5a0641ea5c..43dfa731d2 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -876,7 +876,8 @@ def find_library_file (self, dirs, lib, debug=0): # 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) From 1d52f144d021e9b66b6610b2dfc1c662182f7248 Mon Sep 17 00:00:00 2001 From: Skip Montanaro Date: Tue, 1 Oct 2002 17:39:59 +0000 Subject: [PATCH 0862/2594] 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 43dfa731d2..60d1caeed1 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -82,6 +82,7 @@ def __init__ (self, 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 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: [PATCH 0863/2594] 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 8143627559..e70429c807 100644 --- a/command/__init__.py +++ b/command/__init__.py @@ -21,8 +21,9 @@ '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 f61611eb8d..454c9df119 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 1960425511..0000000000 --- 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 3b8ca2d2e7..0000000000 --- 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 875f3d88ce..0000000000 --- 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 0864/2594] 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 bbaad7dc7b..86a40947b4 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -282,6 +282,9 @@ def run (self): # 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: From 6ed654260a646f4c8823e335597316c2a29038e5 Mon Sep 17 00:00:00 2001 From: Skip Montanaro Date: Wed, 9 Oct 2002 21:37:18 +0000 Subject: [PATCH 0865/2594] 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 831717baca..692e3eb477 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -196,7 +196,10 @@ def runtime_library_dir_option(self, dir): # 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 From 41f9cf2f3074f7626544b810e8995139bb842be9 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Tue, 15 Oct 2002 14:51:58 +0000 Subject: [PATCH 0866/2594] 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 b538319881..71e51bfd37 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -130,9 +130,8 @@ def run (self): # 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 @@ def run (self): 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) From a3f707d5f06386feabf8d3767faca67ef4a580cd Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Tue, 15 Oct 2002 19:45:25 +0000 Subject: [PATCH 0867/2594] 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 71e51bfd37..a3526a7a04 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -264,7 +264,7 @@ def get_exe_bytes (self): AAAA8AAAAA4fug4AtAnNIbgBTM0hVGhpcyBwcm9ncmFtIGNhbm5vdCBiZSBydW4gaW4gRE9TIG1v ZGUuDQ0KJAAAAAAAAAAtOHsRaVkVQmlZFUJpWRVCEkUZQmpZFUIGRh9CYlkVQupFG0JrWRVCBkYR QmtZFUJpWRVCZlkVQmlZFELjWRVCC0YGQmRZFUJveh9Ca1kVQq5fE0JoWRVCUmljaGlZFUIAAAAA -AAAAAAAAAAAAAAAAUEUAAEwBAwAQIUU9AAAAAAAAAADgAA8BCwEGAABQAAAAEAAAALAAAJAFAQAA +AAAAAAAAAAAAAAAAUEUAAEwBAwDeb6w9AAAAAAAAAADgAA8BCwEGAABQAAAAEAAAALAAAGAGAQAA wAAAABABAAAAQAAAEAAAAAIAAAQAAAAAAAAABAAAAAAAAAAAIAEAAAQAAAAAAAACAAAAAAAQAAAQ AAAAABAAABAAAAAAAAAQAAAAAAAAAAAAAAAwEQEA5AEAAAAQAQAwAQAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA @@ -277,7 +277,7 @@ def get_exe_bytes (self): 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 @@ def get_exe_bytes (self): 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 From 4d708a479b7ca9f8464af6059bc6afa01ad2bdf7 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Wed, 16 Oct 2002 17:51:38 +0000 Subject: [PATCH 0868/2594] 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 a3526a7a04..d996bee35a 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -264,7 +264,7 @@ def get_exe_bytes (self): AAAA8AAAAA4fug4AtAnNIbgBTM0hVGhpcyBwcm9ncmFtIGNhbm5vdCBiZSBydW4gaW4gRE9TIG1v ZGUuDQ0KJAAAAAAAAAAtOHsRaVkVQmlZFUJpWRVCEkUZQmpZFUIGRh9CYlkVQupFG0JrWRVCBkYR QmtZFUJpWRVCZlkVQmlZFELjWRVCC0YGQmRZFUJveh9Ca1kVQq5fE0JoWRVCUmljaGlZFUIAAAAA -AAAAAAAAAAAAAAAAUEUAAEwBAwDeb6w9AAAAAAAAAADgAA8BCwEGAABQAAAAEAAAALAAAGAGAQAA +AAAAAAAAAAAAAAAAUEUAAEwBAwCepq09AAAAAAAAAADgAA8BCwEGAABQAAAAEAAAALAAAIAGAQAA wAAAABABAAAAQAAAEAAAAAIAAAQAAAAAAAAABAAAAAAAAAAAIAEAAAQAAAAAAAACAAAAAAAQAAAQ AAAAABAAABAAAAAAAAAQAAAAAAAAAAAAAAAwEQEA5AEAAAAQAQAwAQAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA @@ -277,7 +277,7 @@ def get_exe_bytes (self): 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 @@ def get_exe_bytes (self): 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 @@ -614,7 +614,7 @@ def get_exe_bytes (self): AAAAAABLRVJORUwzMi5ETEwAQURWQVBJMzIuZGxsAENPTUNUTDMyLmRsbABHREkzMi5kbGwATVNW Q1JULmRsbABvbGUzMi5kbGwAU0hFTEwzMi5kbGwAVVNFUjMyLmRsbAAATG9hZExpYnJhcnlBAABH ZXRQcm9jQWRkcmVzcwAARXhpdFByb2Nlc3MAAABSZWdDbG9zZUtleQAAAFByb3BlcnR5U2hlZXRB -AABUZXh0T3V0QQAAZXhpdAAAQ29Jbml0aWFsaXplAABTSEdldFNwZWNpYWxGb2xkZXJQYXRoQQAA +AABUZXh0T3V0QQAAZnJlZQAAQ29Jbml0aWFsaXplAABTSEdldFNwZWNpYWxGb2xkZXJQYXRoQQAA AEdldERDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA From c3c5a89898505fe9b1efe3310fc6a666212bd148 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 31 Oct 2002 13:22:41 +0000 Subject: [PATCH 0869/2594] 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 92cb8320da..c71cb36a94 100644 --- a/dist.py +++ b/dist.py @@ -12,6 +12,12 @@ 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 @@ def __init__ (self, attrs=None): 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() From 00c4a7c3a8e95dcb877acdf8d92421cc853c69cb Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 31 Oct 2002 13:39:33 +0000 Subject: [PATCH 0870/2594] Catch only ImportError --- dist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist.py b/dist.py index c71cb36a94..dbeeb8b1dc 100644 --- a/dist.py +++ b/dist.py @@ -15,7 +15,7 @@ try: import warnings -except: +except ImportError: warnings = None from distutils.errors import * From 763affe91fa9f53831b26e94b818e34d73c5a2cd Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Thu, 31 Oct 2002 14:26:37 +0000 Subject: [PATCH 0871/2594] 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 6b6f2c7d57..11ab59528a 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -635,6 +635,8 @@ def get_libraries (self, ext): # 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 From 42f8752d3cfc16950f11ddc0c462ec9c06cfd822 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Mon, 4 Nov 2002 13:33:07 +0000 Subject: [PATCH 0872/2594] [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 86a40947b4..597b26c6ad 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, From a5639c31c94c1fe14c1685072399798fa4ed1a82 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Mon, 4 Nov 2002 13:45:15 +0000 Subject: [PATCH 0873/2594] 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 9a6bff6b55..d180eb8cb6 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 [] From 1bd24104b8ac0fb4707a24204bfde7b5e30c93d4 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Mon, 4 Nov 2002 14:27:43 +0000 Subject: [PATCH 0874/2594] [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 f0a7865067..6aeb7c9aac 100644 --- a/log.py +++ b/log.py @@ -9,6 +9,8 @@ ERROR = 4 FATAL = 5 +import sys + class Log: def __init__(self, threshold=WARN): @@ -17,6 +19,7 @@ def __init__(self, threshold=WARN): 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) From 17cca1e870697be532a44be00137f801faad424d Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Mon, 4 Nov 2002 19:50:03 +0000 Subject: [PATCH 0875/2594] [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 11ab59528a..934b4576e6 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -451,14 +451,6 @@ def build_extension(self, ext): 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 @@ def build_extension(self, ext): 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), From 4765756222ebf75cbba020854d4550f7e471f2f5 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Mon, 4 Nov 2002 19:53:24 +0000 Subject: [PATCH 0876/2594] [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 48672d6a10..e879fa149a 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, From 8c157b7b9e0b11cd6274e6fdbb476e142d61951f Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Tue, 5 Nov 2002 10:06:19 +0000 Subject: [PATCH 0877/2594] 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 d996bee35a..029a026b62 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 @@ def finalize_options (self): 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 @@ def get_exe_bytes (self): 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 -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 +6I1EAipF3I2F2P6bafShezdgC+7dgLwF1w9cMseY4VkYaLATHehPFz4bMyvtoGX4hoQFtrRhexFS +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 +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 --- From 47474c30d9a5ba83990c5370f74eba40d166d84c Mon Sep 17 00:00:00 2001 From: Gustavo Niemeyer Date: Tue, 5 Nov 2002 16:12:02 +0000 Subject: [PATCH 0878/2594] 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 6e9d6c64de..abe302a804 100644 --- a/bcppcompiler.py +++ b/bcppcompiler.py @@ -146,8 +146,7 @@ def create_static_lib (self, 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 @@ def create_static_lib (self, 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 @@ def link (self, 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 60d1caeed1..317e21efb4 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 @@ def _need_link (self, objects, output_file): # _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 @@ def create_static_lib (self, 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 @@ def create_static_lib (self, 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 @@ def link (self, 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 @@ def link (self, 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 @@ def link_shared_lib (self, 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 @@ def link_shared_object (self, 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 @@ def link_executable (self, 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 934b4576e6..4bfc20c9d4 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -477,6 +477,9 @@ def build_extension(self, ext): 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 @@ def build_extension(self, ext): 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 88b1586607..9ebe0d9191 100644 --- a/command/config.py +++ b/command/config.py @@ -148,7 +148,8 @@ def _link (self, body, 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 9aabd8a66b..a046ee21c8 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -140,7 +140,8 @@ def link (self, 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 @@ def link (self, debug, extra_preargs, extra_postargs, - build_temp) + build_temp, + target_lang) # link () diff --git a/emxccompiler.py b/emxccompiler.py index 7c3ad025bb..624c0fecbd 100644 --- a/emxccompiler.py +++ b/emxccompiler.py @@ -102,7 +102,8 @@ def link (self, 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 @@ def link (self, debug, extra_preargs, extra_postargs, - build_temp) + build_temp, + target_lang) # link () diff --git a/extension.py b/extension.py index d73bb08e00..7fbeb4e1f1 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 @@ def __init__ (self, name, sources, 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 @@ def __init__ (self, name, sources, 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 65b114353e..a2459ad032 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -367,8 +367,7 @@ def create_static_lib (self, 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 @@ def create_static_lib (self, 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 @@ def link (self, 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 cd66a09913..8f62bf7d87 100644 --- a/mwerkscompiler.py +++ b/mwerkscompiler.py @@ -84,7 +84,8 @@ def link (self, 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 e879fa149a..cc571888db 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 692e3eb477..2f4546e1a7 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 @@ def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): 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 @@ 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): + 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 @@ def link(self, target_desc, objects, 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: From bc09b3f743da2691427c0a23f5b5aadfe90c7847 Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Tue, 5 Nov 2002 20:11:08 +0000 Subject: [PATCH 0879/2594] 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 cc571888db..222648aa74 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -14,7 +14,6 @@ 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" From 152918876946cf1aba35f1a4e4e7a9ff53f37acc Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Tue, 5 Nov 2002 20:27:17 +0000 Subject: [PATCH 0880/2594] 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 317e21efb4..8898f51f59 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -589,7 +589,7 @@ 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 type(sources) is not ListType: sources = [sources] lang = None index = len(self.language_order) From 5211b8478b5bd08e9237dbf35840eee043efb320 Mon Sep 17 00:00:00 2001 From: Gustavo Niemeyer Date: Wed, 6 Nov 2002 18:44:26 +0000 Subject: [PATCH 0881/2594] 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 597b26c6ad..0dad1ac474 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -129,6 +129,7 @@ def initialize_options (self): 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 @@ def finalize_package_data (self): 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 @@ def _make_spec_file(self): "--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), From 013ee1e07785651f7b0bc75fdd3944b6409e7a31 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Thu, 7 Nov 2002 16:41:38 +0000 Subject: [PATCH 0882/2594] 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 d180eb8cb6..001e74be47 100644 --- a/core.py +++ b/core.py @@ -87,7 +87,7 @@ class found in 'cmdclass' is used in place of the default, which is 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:] From 5ebe4f2073b0cfd36cbbd01e7c3bd18ec7a1f773 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Thu, 7 Nov 2002 16:46:19 +0000 Subject: [PATCH 0883/2594] 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 029a026b62..97a045824d 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -265,11 +265,11 @@ def get_exe_bytes (self): EXEDATA = """\ TVqQAAMAAAAEAAAA//8AALgAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAA8AAAAA4fug4AtAnNIbgBTM0hVGhpcyBwcm9ncmFtIGNhbm5vdCBiZSBydW4gaW4gRE9TIG1v -ZGUuDQ0KJAAAAAAAAAAtOHsRaVkVQmlZFUJpWRVCEkUZQmpZFUIGRh9CYlkVQupFG0JrWRVCBkYR -QmtZFUJpWRVCZlkVQmlZFELjWRVCC0YGQmRZFUJveh9Ca1kVQq5fE0JoWRVCUmljaGlZFUIAAAAA -AAAAAAAAAAAAAAAAUEUAAEwBAwAvl8c9AAAAAAAAAADgAA8BCwEGAABQAAAAEAAAALAAAMAGAQAA +ZGUuDQ0KJAAAAAAAAAAjSEomZykkdWcpJHVnKSR1HDUodWQpJHUINi51bCkkdeQ1KnVlKSR1CDYg +dWUpJHVnKSR1aCkkdWcpJXXuKSR1BTY3dWwpJHVhCi51ZSkkdaAvInVmKSR1UmljaGcpJHUAAAAA +AAAAAAAAAAAAAAAAUEUAAEwBAwCllso9AAAAAAAAAADgAA8BCwEGAABQAAAAEAAAALAAAAAHAQAA wAAAABABAAAAQAAAEAAAAAIAAAQAAAAAAAAABAAAAAAAAAAAIAEAAAQAAAAAAAACAAAAAAAQAAAQ -AAAAABAAABAAAAAAAAAQAAAAAAAAAAAAAAAwEQEA5AEAAAAQAQAwAQAAAAAAAAAAAAAAAAAAAAAA +AAAAABAAABAAAAAAAAAQAAAAAAAAAAAAAAAwEQEAoAEAAAAQAQAwAQAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABVUFgwAAAAAACwAAAAEAAAAAAAAAAEAAAA AAAAAAAAAAAAAACAAADgVVBYMQAAAAAAUAAAAMAAAABKAAAABAAAAAAAAAAAAAAAAAAAQAAA4C5y @@ -280,332 +280,332 @@ def get_exe_bytes (self): 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 @@ -618,16 +618,16 @@ def get_exe_bytes (self): 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 From da858ca9d519e45c814af5f7cf45afc598d30059 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Fri, 8 Nov 2002 15:11:42 +0000 Subject: [PATCH 0884/2594] 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 0dad1ac474..88be5e8bf7 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -434,7 +434,7 @@ def _make_spec_file(self): ] 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: From b309ef158743a718b302cf7566e03aa79cd600d7 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Fri, 8 Nov 2002 16:18:24 +0000 Subject: [PATCH 0885/2594] [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 001e74be47..51961454f6 100644 --- a/core.py +++ b/core.py @@ -145,9 +145,7 @@ class found in 'cmdclass' is used in place of the default, which is else: raise SystemExit, error - except (DistutilsExecError, - DistutilsFileError, - DistutilsOptionError, + except (DistutilsError, CCompilerError), msg: if DEBUG: raise From 8541483a41b056cfb0e5e2bf707f66c3afb1d0e6 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Wed, 13 Nov 2002 13:26:59 +0000 Subject: [PATCH 0886/2594] Update file --- README | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/README b/README index f1db3c6c40..45c7ca8ca9 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) From 02d558c61ba43389e4aba498833b2dfd6e55c26d Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Wed, 13 Nov 2002 17:03:05 +0000 Subject: [PATCH 0887/2594] 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 222648aa74..3d369b00de 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -14,6 +14,7 @@ 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) From 87b5c0ec3c8aded916170321554f653bafe57030 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Wed, 13 Nov 2002 20:54:21 +0000 Subject: [PATCH 0888/2594] 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 7fbeb4e1f1..9dc316a1a5 100644 --- a/extension.py +++ b/extension.py @@ -10,6 +10,10 @@ 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 @@ def __init__ (self, name, sources, 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 @@ def __init__ (self, name, sources, 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 From 649fdd11b8ff8d08957390b343eca0dbc5a9e3a5 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 14 Nov 2002 01:29:00 +0000 Subject: [PATCH 0889/2594] [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 8898f51f59..8aca058b0f 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -366,7 +366,7 @@ def _setup_compile(self, outdir, macros, incdirs, sources, depends, 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 @@ def _prep_compile(self, sources, output_dir, depends=None): 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) From d47c1f5e8455cc049fc8c4ae2c00795e98c3abb7 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 14 Nov 2002 01:43:00 +0000 Subject: [PATCH 0890/2594] [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 3d369b00de..b12b98deee 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -35,6 +35,14 @@ 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: From fb4cdac4953c41880107d487afc61c4b7ca937b2 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 14 Nov 2002 01:44:35 +0000 Subject: [PATCH 0891/2594] [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 97a045824d..dcc390e6d6 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -70,7 +70,7 @@ def finalize_options (self): 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 4bfc20c9d4..250e539340 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -12,7 +12,7 @@ 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 @@ def finalize_options (self): 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 From fdfa7f054ccb881256c4696ad9dadbb5f0f9aa1c Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 14 Nov 2002 01:58:48 +0000 Subject: [PATCH 0892/2594] 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 bec3464511..963d83377c 100644 --- a/errors.py +++ b/errors.py @@ -5,11 +5,9 @@ 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): From 4461f74828aed6a205722c0d9e697f8425771f8c Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 14 Nov 2002 02:25:42 +0000 Subject: [PATCH 0893/2594] 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 47fac0cb7d..98c1e55950 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 8aca058b0f..3084947d58 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 6a268184c0..b35eb07855 100644 --- a/cmd.py +++ b/cmd.py @@ -4,9 +4,6 @@ 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 454c9df119..e0648f3bdd 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 712fec884e..7562b70812 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -4,8 +4,6 @@ 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 88be5e8bf7..a7bc45f481 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 dcc390e6d6..c5cfd6d31b 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 6bc92a43b8..0643948c92 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 f0207e4e0f..fe921fb88b 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -4,9 +4,6 @@ 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 250e539340..0b3ef14933 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -4,8 +4,6 @@ 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 0ab72c08f4..f61dc17a18 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 211ade40fa..e4d6099bd5 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 9ebe0d9191..abfa138530 100644 --- a/command/config.py +++ b/command/config.py @@ -9,8 +9,6 @@ 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 67f37893f5..9adda4688a 100644 --- a/command/install.py +++ b/command/install.py @@ -4,8 +4,6 @@ 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 495d150c55..957ed239b1 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 1e771c619e..5da1c7aea7 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 082aa88ce3..91807e6a3f 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 51961454f6..5be9e4ec29 100644 --- a/core.py +++ b/core.py @@ -6,8 +6,6 @@ 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 a046ee21c8..93f8803847 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -41,8 +41,6 @@ # 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 bbb6d235b3..f49665483a 100644 --- a/dep_util.py +++ b/dep_util.py @@ -4,8 +4,6 @@ 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 8b3e06b2fa..d407e9ac5b 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 dbeeb8b1dc..3a690696bb 100644 --- a/dist.py +++ b/dist.py @@ -4,9 +4,6 @@ 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 624c0fecbd..76bdbae506 100644 --- a/emxccompiler.py +++ b/emxccompiler.py @@ -19,8 +19,6 @@ # # * 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 9dc316a1a5..5b197fa58d 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 5de64e15da..f78b0a6854 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -8,8 +8,6 @@ * 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 56b1faee45..c2fa086f05 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 b4f3269cf1..4b5d47d02f 100644 --- a/filelist.py +++ b/filelist.py @@ -4,11 +4,6 @@ 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 a2459ad032..c2bd77de44 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -4,7 +4,7 @@ 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 4df6e097de..f94817d498 100644 --- a/spawn.py +++ b/spawn.py @@ -6,8 +6,6 @@ executable name. """ -# created 1999/07/24, Greg Ward - __revision__ = "$Id$" import sys, os, string diff --git a/sysconfig.py b/sysconfig.py index b12b98deee..aa3636f609 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -7,7 +7,6 @@ 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 7086b1af56..67efd65e36 100644 --- a/text_file.py +++ b/text_file.py @@ -4,8 +4,6 @@ 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 2f4546e1a7..603dfe90cc 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -13,8 +13,6 @@ * 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 8d22415496..9de6077fc6 100644 --- a/util.py +++ b/util.py @@ -4,8 +4,6 @@ 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 02502dac95..71a5614719 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$ # From 58e414fbe6bab66065fc5b5a0705980f578f4796 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Tue, 19 Nov 2002 13:12:28 +0000 Subject: [PATCH 0894/2594] 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 1055aa39c1..7873d297b3 100644 --- a/__init__.py +++ b/__init__.py @@ -8,6 +8,8 @@ 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 98c1e55950..d1dc909520 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 abe302a804..cfbe04ac01 100644 --- a/bcppcompiler.py +++ b/bcppcompiler.py @@ -11,6 +11,8 @@ # 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 3084947d58..edb9f7542f 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 b35eb07855..1165f95124 100644 --- a/cmd.py +++ b/cmd.py @@ -4,6 +4,8 @@ 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 e70429c807..fc6117166b 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 e0648f3bdd..7c606ffc4d 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 7562b70812..d1cce55b20 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -4,6 +4,8 @@ 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 a7bc45f481..237cc70d25 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 c5cfd6d31b..9c9fd10918 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 0643948c92..78231541ec 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 fe921fb88b..ef03ed7269 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -4,6 +4,8 @@ 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 0b3ef14933..0c37768179 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -4,6 +4,8 @@ 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 f61dc17a18..258d6d4ca0 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 e4d6099bd5..b7c11d472b 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 36252d5174..41b22777bc 100644 --- a/command/clean.py +++ b/command/clean.py @@ -4,6 +4,8 @@ # 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 abfa138530..b6f5ad1dc5 100644 --- a/command/config.py +++ b/command/config.py @@ -9,6 +9,8 @@ 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 9adda4688a..5d5bdaa77e 100644 --- a/command/install.py +++ b/command/install.py @@ -4,6 +4,8 @@ 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 d0091ce238..5c1f18a9f2 100644 --- a/command/install_data.py +++ b/command/install_data.py @@ -5,6 +5,8 @@ # 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 957ed239b1..3a37d309f9 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 5da1c7aea7..daf3e010fd 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 ceece1b6bf..6572e650d4 100644 --- a/command/install_scripts.py +++ b/command/install_scripts.py @@ -5,6 +5,8 @@ # 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 91807e6a3f..c0b7dd45d9 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 5be9e4ec29..9ab419ef4c 100644 --- a/core.py +++ b/core.py @@ -6,6 +6,8 @@ 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 93f8803847..18af388c3c 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -41,6 +41,8 @@ # 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 7ca76d6c5c..e195ebdcdf 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 f49665483a..0746633d23 100644 --- a/dep_util.py +++ b/dep_util.py @@ -4,6 +4,8 @@ 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 d407e9ac5b..ca9fa9dc7f 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 3a690696bb..faeb7b10b3 100644 --- a/dist.py +++ b/dist.py @@ -4,6 +4,8 @@ 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 963d83377c..94e83fb557 100644 --- a/errors.py +++ b/errors.py @@ -8,6 +8,8 @@ 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 f78b0a6854..a4a4e7979e 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -8,6 +8,8 @@ * 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 c2fa086f05..e230ce587e 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 4b5d47d02f..bfa53d2133 100644 --- a/filelist.py +++ b/filelist.py @@ -4,6 +4,8 @@ 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 6aeb7c9aac..0442033d66 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 c2bd77de44..65a50cc79a 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 8f62bf7d87..d546de1f25 100644 --- a/mwerkscompiler.py +++ b/mwerkscompiler.py @@ -4,6 +4,10 @@ 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 f94817d498..6e0423b099 100644 --- a/spawn.py +++ b/spawn.py @@ -6,6 +6,8 @@ executable name. """ +# This module should be kept compatible with Python 1.5.2. + __revision__ = "$Id$" import sys, os, string From 6ff384d0805ec8da74e606380d9526d73cac0c8b Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Wed, 20 Nov 2002 16:10:29 +0000 Subject: [PATCH 0895/2594] Add missing import --- extension.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extension.py b/extension.py index 5b197fa58d..e4209449e4 100644 --- a/extension.py +++ b/extension.py @@ -5,7 +5,7 @@ __revision__ = "$Id$" -import os, string +import os, string, sys from types import * try: From 66ffcc6481adb327ae0c529f439bed2c418e97ef Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 21 Nov 2002 18:33:28 +0000 Subject: [PATCH 0896/2594] 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 d1dc909520..d5b3096617 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", 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 0897/2594] 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 603dfe90cc..2a6b1beeea 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -201,7 +201,7 @@ def runtime_library_dir_option(self, dir): 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 From 2fe3abe410be345020e460b3be5860c4d60a2223 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 21 Nov 2002 20:41:07 +0000 Subject: [PATCH 0898/2594] Reflow comment --- spawn.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spawn.py b/spawn.py index 6e0423b099..4857ce5e63 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 From 90b1c1a1b6dfb3d5ed0ef835e62d94ea2c45f4fa Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Fri, 22 Nov 2002 20:57:20 +0000 Subject: [PATCH 0899/2594] 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 9c9fd10918..7609fabeef 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -13,6 +13,7 @@ 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): From fc4c1ada98f887c3423daee9c803753513fba0ba Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Fri, 22 Nov 2002 21:08:34 +0000 Subject: [PATCH 0900/2594] (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 7609fabeef..5acca11a62 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -8,7 +8,6 @@ __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 @@ def create_exe (self, arcname, fullname, bitmap=None): # 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 0000000000000000000000000000000000000000..6290a9361e36d244122f6af3feb506efe6d255c8 GIT binary patch literal 20992 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 evH<`GG2EY+UcbN*O{|Knd=fqw(hGF_bj literal 0 HcmV?d00001 From 7e77495f1012854570db4870dcd839ce9ece7854 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Tue, 26 Nov 2002 17:42:48 +0000 Subject: [PATCH 0901/2594] 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 ca9fa9dc7f..bd1ea0f243 100644 --- a/dir_util.py +++ b/dir_util.py @@ -6,7 +6,7 @@ __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 + From 56507a08e5a566801e5506f80325a67198e86493 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Tue, 26 Nov 2002 17:45:19 +0000 Subject: [PATCH 0902/2594] 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 d1cce55b20..8ee3a5c5f7 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -11,7 +11,7 @@ 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 @@ def initialize_options (self): self.keep_temp = 0 self.dist_dir = None self.skip_build = 0 - + self.relative = 0 + # initialize_options() @@ -97,9 +101,24 @@ def run (self): 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) From 6f8ae17986bf88dde5c0f158e4a49b1f9465736b Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Tue, 26 Nov 2002 21:28:23 +0000 Subject: [PATCH 0903/2594] 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 5c1f18a9f2..2fa0da29fe 100644 --- a/command/install_data.py +++ b/command/install_data.py @@ -48,7 +48,7 @@ def finalize_options (self): 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: From 6efa84626efe12980cd1ec6c649b9c41bff2d6a4 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Wed, 27 Nov 2002 13:45:26 +0000 Subject: [PATCH 0904/2594] [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 e4209449e4..94b56e6ce4 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/ From 54d6451d9991f43b2f593fee76fe238632027264 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Fri, 29 Nov 2002 19:45:58 +0000 Subject: [PATCH 0905/2594] 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 6572e650d4..abe10457b6 100644 --- a/command/install_scripts.py +++ b/command/install_scripts.py @@ -53,7 +53,7 @@ def run (self): 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) 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: [PATCH 0906/2594] 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 9de6077fc6..17fc320aa6 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 From fb9fc102e11fdf1851470aa6b5dd11b0e2737b6f Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Sun, 29 Dec 2002 17:00:57 +0000 Subject: [PATCH 0907/2594] 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 edb9f7542f..bfcf1279f1 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -15,6 +15,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.sysconfig import python_build from distutils.util import split_quoted, execute from distutils import log @@ -366,7 +367,9 @@ def _setup_compile(self, outdir, macros, incdirs, sources, depends, 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 @@ def _prep_compile(self, sources, output_dir, depends=None): 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) From 0f17b9ba75fc0d3d166a058387ca8a7cd2d97a63 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Fri, 3 Jan 2003 15:24:36 +0000 Subject: [PATCH 0908/2594] [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 faeb7b10b3..f15c945c74 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 @@ def handle_display_options (self, option_order): 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 @@ def __init__ (self): 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 @@ def write_pkg_info (self, base_dir): 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 @@ def get_keywords(self): def get_platforms(self): return self.platforms or ["UNKNOWN"] + def get_classifiers(self): + return self.classifiers or [] + # class DistributionMetadata From 68218cefc84e5b95d3cbf4cadccb1742b83623d5 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Fri, 3 Jan 2003 15:29:28 +0000 Subject: [PATCH 0909/2594] [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 0000000000..6920d1d1c9 --- /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 + From bfd055388d345fe1f58cd04f7272f6f7d3963f15 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Mon, 6 Jan 2003 13:28:12 +0000 Subject: [PATCH 0910/2594] 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 17fc320aa6..dc3183b691 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 -- From fc7885b993648b7587683e0e14adecb21011556b Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Fri, 24 Jan 2003 14:56:52 +0000 Subject: [PATCH 0911/2594] 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 b7c11d472b..c7bd4a9d9b 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -7,6 +7,7 @@ __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 @@ def copy_scripts (self): 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 @@ def copy_scripts (self): 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 From caa7bc3bd88e1b1c50f4f03bda217b0fc6063f2b Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Mon, 27 Jan 2003 16:30:36 +0000 Subject: [PATCH 0912/2594] 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 9ab419ef4c..29fbc5cdf9 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 94b56e6ce4..e69f3e93e0 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, From 04937364e507dbe9f02d00d0cd79908dbfc9afb7 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 29 Jan 2003 16:58:31 +0000 Subject: [PATCH 0913/2594] 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 c7bd4a9d9b..f61ad37d03 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -114,9 +114,12 @@ def copy_scripts (self): 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 () From c7d6889f2f4e4f3a0562798e3641d67c09951f35 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Fri, 31 Jan 2003 20:40:15 +0000 Subject: [PATCH 0914/2594] 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 65a50cc79a..e07a6d5a0c 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -310,7 +310,7 @@ def compile(self, sources, 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 From 90ceb5eaaf84551cf39ba48550debf99f9081e7b Mon Sep 17 00:00:00 2001 From: Just van Rossum Date: Mon, 3 Feb 2003 11:43:54 +0000 Subject: [PATCH 0915/2594] 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 b6f5ad1dc5..3bd537a6e8 100644 --- a/command/config.py +++ b/command/config.py @@ -151,7 +151,8 @@ def _link (self, body, 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) From 3ea65a4bba9d3ce1086bc983c16c5f0feea8055b Mon Sep 17 00:00:00 2001 From: Jack Jansen Date: Mon, 10 Feb 2003 14:02:33 +0000 Subject: [PATCH 0916/2594] 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 aa3636f609..67353a8d64 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'] From a65727b5db3ecf4546e808fcc72eb14fbea9cf42 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Tue, 18 Feb 2003 01:28:51 +0000 Subject: [PATCH 0917/2594] [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 3bd537a6e8..f18c79ff43 100644 --- a/command/config.py +++ b/command/config.py @@ -17,6 +17,7 @@ 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 @@ def _check_compiler (self): 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: From dae4f7a4a339196ac3fa85ede474b603659e3169 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Tue, 18 Feb 2003 21:28:20 +0000 Subject: [PATCH 0918/2594] 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 6920d1d1c9..aac700c480 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', From 7fa7f24d329793d1d40b4fbdb0d56cb284e4c907 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Wed, 19 Feb 2003 13:49:35 +0000 Subject: [PATCH 0919/2594] [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 aac700c480..328d8d0ae8 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 @@ def build_post_data(self, action): '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 @@ def post_to_server(self, data, auth=None): 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 From fedab9d677951d29d7e11684ab6978e31cea0a68 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Wed, 19 Feb 2003 14:16:01 +0000 Subject: [PATCH 0920/2594] [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 29fbc5cdf9..a463272c2f 100644 --- a/core.py +++ b/core.py @@ -47,7 +47,7 @@ def gen_usage (script_name): '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 f15c945c74..08e2a4f7d8 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 @@ def __init__ (self): 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 @@ def write_pkg_info (self, base_dir): 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 @@ def get_platforms(self): def get_classifiers(self): return self.classifiers or [] + def get_download_url(self): + return self.download_url or "UNKNOWN" + # class DistributionMetadata From 3001d212f66a2cb547d0d3306680e7ab09c87f6e Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Wed, 19 Feb 2003 14:27:21 +0000 Subject: [PATCH 0921/2594] 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 328d8d0ae8..29b76cbfd9 100644 --- a/command/register.py +++ b/command/register.py @@ -233,6 +233,7 @@ def build_post_data(self, action): 'keywords': meta.get_keywords(), 'platform': meta.get_platforms(), 'classifiers': meta.get_classifiers(), + 'download_url': meta.get_download_url(), } return data From 39a95207b183654cd92b4048ed910491ffe1a48a Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Thu, 20 Feb 2003 02:09:30 +0000 Subject: [PATCH 0922/2594] 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 0442033d66..01420da9af 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) From 72cbcb8b55ce0bd038577685f074418aa61fdea6 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Thu, 20 Feb 2003 02:10:08 +0000 Subject: [PATCH 0923/2594] 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 1165f95124..7e7a4cd5ff 100644 --- a/cmd.py +++ b/cmd.py @@ -191,7 +191,7 @@ def announce (self, msg, level=1): """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 From 5daaf7cd967da08a031cb40e3b668a173b4673a1 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Wed, 26 Feb 2003 18:52:07 +0000 Subject: [PATCH 0924/2594] [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 bfcf1279f1..46fb743db0 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -932,6 +932,8 @@ def object_filenames(self, source_filenames, strip_dir=0, 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) From cbc89d16f32d66f6b51a0a94cd6a0a00824bdbff Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Fri, 28 Feb 2003 22:03:04 +0000 Subject: [PATCH 0925/2594] [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 258d6d4ca0..6c007c6987 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -86,25 +86,11 @@ def run (self): # 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 @@ def find_all_modules (self): (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) From 06f82fdc6015683f349612125cd2e44fea44092b Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Mon, 3 Mar 2003 18:26:01 +0000 Subject: [PATCH 0926/2594] Improve description --- command/register.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command/register.py b/command/register.py index 29b76cbfd9..e4a379939b 100644 --- a/command/register.py +++ b/command/register.py @@ -15,7 +15,7 @@ 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' From 9c00844cfbe8e4f33255f6174c54fe45c266fce8 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Mon, 3 Mar 2003 18:37:16 +0000 Subject: [PATCH 0927/2594] [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 fc6117166b..870005dca7 100644 --- a/command/__init__.py +++ b/command/__init__.py @@ -19,6 +19,7 @@ 'install_scripts', 'install_data', 'sdist', + 'register', 'bdist', 'bdist_dumb', 'bdist_rpm', From 9766b20bc6d839174db2fb53881c140f110583dd Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Mon, 3 Mar 2003 20:07:27 +0000 Subject: [PATCH 0928/2594] [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 08e2a4f7d8..d313e7d116 100644 --- a/dist.py +++ b/dist.py @@ -205,6 +205,15 @@ def __init__ (self, attrs=None): 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): From ad325698f9280631620c2d7aad6619b67ab8d899 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Wed, 9 Apr 2003 12:35:51 +0000 Subject: [PATCH 0929/2594] 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 e4a379939b..8e347ce6dc 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 @@ def finalize_options(self): def run(self): self.check_metadata() - if self.verify: + if self.dry_run: self.verify_metadata() elif self.list_classifiers: self.classifiers() From 1942fa31ea1b33d7fcccfed31d6cd2ebd46c7f60 Mon Sep 17 00:00:00 2001 From: Jason Tishler Date: Wed, 9 Apr 2003 16:03:57 +0000 Subject: [PATCH 0930/2594] 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 18af388c3c..e86dc81533 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -33,12 +33,6 @@ # * 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 @@ def __init__ (self, verbose=0, dry_run=0, force=0): 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 @@ def __init__ (self, 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) From 972516d3ad5fba97791d87ba95d126f3d6bcc5a2 Mon Sep 17 00:00:00 2001 From: Jason Tishler Date: Wed, 9 Apr 2003 20:13:59 +0000 Subject: [PATCH 0931/2594] 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 e86dc81533..794dcdb59c 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: From ac175fdb7176adf51768ab5237c81f7ab421c50d Mon Sep 17 00:00:00 2001 From: Jason Tishler Date: Mon, 14 Apr 2003 12:51:26 +0000 Subject: [PATCH 0932/2594] 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 794dcdb59c..94b8b86b6d 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -33,7 +33,17 @@ # * 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 @@ def __init__ (self, verbose=0, dry_run=0, force=0): 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 @@ def __init__ (self, verbose=0, dry_run=0, force=0): 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 @@ def __init__ (self, 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 @@ def __init__ (self, 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') From 8e2d2f9fefd61c49a54298e45d4c10be078a5696 Mon Sep 17 00:00:00 2001 From: Jason Tishler Date: Fri, 18 Apr 2003 17:27:47 +0000 Subject: [PATCH 0933/2594] 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 2a6b1beeea..e444917fb6 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, From d19626f7466f8166af24c6cd5e3aca722395b835 Mon Sep 17 00:00:00 2001 From: Skip Montanaro Date: Thu, 24 Apr 2003 19:49:23 +0000 Subject: [PATCH 0934/2594] 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 46fb743db0..751ec0694b 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -883,6 +883,51 @@ def library_option (self, lib): """ 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 From 70a92cdaba12a893f3c1d19f8c714029e6054081 Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Fri, 9 May 2003 16:06:42 +0000 Subject: [PATCH 0935/2594] 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 e07a6d5a0c..b4c890d030 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 @@ __revision__ = "$Id$" import sys, os, string -from types import * from distutils.errors import \ DistutilsExecError, DistutilsPlatformError, \ CompileError, LibError, LinkError @@ -48,126 +48,116 @@ 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 @@ def find_library_file (self, dirs, lib, debug=0): # 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, ';') + From c4ab88656cb41c9c7182805868af9ec6800f9874 Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Fri, 9 May 2003 16:55:28 +0000 Subject: [PATCH 0936/2594] Replace line somehow deleted before last checkin. --- msvccompiler.py | 1 + 1 file changed, 1 insertion(+) diff --git a/msvccompiler.py b/msvccompiler.py index b4c890d030..4d7159f0f9 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -523,6 +523,7 @@ def get_msvc_paths(self, path, platform='x86'): % (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) 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: [PATCH 0937/2594] 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 4d7159f0f9..eeac0ee67a 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: 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 0938/2594] 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 e444917fb6..02db910ae6 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -203,8 +203,10 @@ def runtime_library_dir_option(self, dir): 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 From 2e51b32d4660c3e6a8e90ce8e8fa7bb6bed786dc Mon Sep 17 00:00:00 2001 From: Jack Jansen Date: Sun, 1 Jun 2003 19:27:40 +0000 Subject: [PATCH 0939/2594] 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 02db910ae6..11ecb9f6ae 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -203,9 +203,9 @@ def runtime_library_dir_option(self, dir): 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 From 3f5044ac5626bd886b51c905d16a1fcb1aaa6464 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Thu, 12 Jun 2003 17:23:58 +0000 Subject: [PATCH 0940/2594] 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 5acca11a62..3c4c893920 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -100,7 +100,7 @@ def run (self): 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 From 59a7a49f88a89af3e6e168da54dcd45ffb374aed Mon Sep 17 00:00:00 2001 From: Gustavo Niemeyer Date: Fri, 27 Jun 2003 19:33:38 +0000 Subject: [PATCH 0941/2594] 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 f61ad37d03..8de9cd3f6d 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -15,7 +15,7 @@ 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 @@ def copy_scripts (self): (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")), From a17a0b4ac049c984145ad23ccbbe301b98dc6564 Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Thu, 17 Jul 2003 14:41:07 +0000 Subject: [PATCH 0942/2594] 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 eeac0ee67a..5f3d8de872 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -45,6 +45,10 @@ 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 @@ def set_macro(self, macro, path, key): 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 @@ def get_msvc_paths(self, path, platform='x86'): 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 @@ def get_msvc_paths(self, path, platform='x86'): 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], ";") 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: [PATCH 0943/2594] 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 7e7a4cd5ff..6e44221eb5 100644 --- a/cmd.py +++ b/cmd.py @@ -148,7 +148,7 @@ 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 + 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()'. 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 0944/2594] Patch #812378: Normalize white space. --- sysconfig.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 67353a8d64..46d1acbb04 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'] From 2c9c51ede8cea8c78f14fcc723c5c0a3cbf7b5cf Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Fri, 28 Nov 2003 19:42:56 +0000 Subject: [PATCH 0945/2594] 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 5f3d8de872..27fb658b5b 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -547,6 +547,16 @@ def get_msvc_paths(self, path, platform='x86'): 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): From 2a97217f7fae82ae141e9dc1a426146dca203b04 Mon Sep 17 00:00:00 2001 From: Andrew MacIntyre Date: Tue, 2 Dec 2003 12:17:59 +0000 Subject: [PATCH 0946/2594] 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 76bdbae506..f52e63232d 100644 --- a/emxccompiler.py +++ b/emxccompiler.py @@ -63,8 +63,8 @@ def __init__ (self, # 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') From 21a1ad1c19eeab489dc344c59f0cd027bda50dd7 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Fri, 5 Dec 2003 20:12:23 +0000 Subject: [PATCH 0947/2594] 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 cfbe04ac01..b0360a248e 100644 --- a/bcppcompiler.py +++ b/bcppcompiler.py @@ -96,7 +96,11 @@ def compile(self, sources, 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 751ec0694b..ebd93c27eb 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -691,7 +691,11 @@ def compile(self, sources, output_dir=None, macros=None, 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 27fb658b5b..1441ea04c3 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -291,7 +291,11 @@ def compile(self, sources, 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 From affdbb99128310385c45cd2562afcb2f0009431f Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Sun, 18 Jan 2004 20:29:55 +0000 Subject: [PATCH 0948/2594] 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 0c37768179..7fff422268 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -171,7 +171,8 @@ def finalize_options (self): # 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 From cdd1c7641bdef9ac123a2879c88c2f4d28a9a9fe Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Sun, 18 Jan 2004 20:39:35 +0000 Subject: [PATCH 0949/2594] 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 7fff422268..0c37768179 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -171,8 +171,7 @@ def finalize_options (self): # 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 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: [PATCH 0950/2594] 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 6e44221eb5..fef49390da 100644 --- a/cmd.py +++ b/cmd.py @@ -253,8 +253,8 @@ def ensure_string_list (self, option): 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 a463272c2f..eb419721e4 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 bd1ea0f243..e479b62415 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 d313e7d116..f63ea97331 100644 --- a/dist.py +++ b/dist.py @@ -525,9 +525,9 @@ def _parse_command_opts (self, parser, args): 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 a4a4e7979e..512bc9b665 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -162,7 +162,7 @@ def _grok_option_table (self): 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 dc3183b691..8c3c8df979 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 @@ def byte_compile (py_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 @@ def byte_compile (py_files, 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) From bc3373b4d89d718b8760d3c8a4afd18316fec86c Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Tue, 17 Feb 2004 22:35:19 +0000 Subject: [PATCH 0951/2594] 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 f63ea97331..586e6bb145 100644 --- a/dist.py +++ b/dist.py @@ -346,9 +346,7 @@ def parse_config_files (self, filenames=None): 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 From 0d7a3db04fd813544f939ce1d9bbcf135f1a6f35 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Fri, 20 Feb 2004 14:43:21 +0000 Subject: [PATCH 0952/2594] 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 3c4c893920..76b1762ede 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 @@ def initialize_options (self): self.title = None self.skip_build = 0 self.install_script = None + self.pre_install_script = None # initialize_options() @@ -69,11 +74,12 @@ def finalize_options (self): 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 @@ def run (self): # 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 @@ def get_inidata (self): 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 @@ def create_exe (self, arcname, fullname, bitmap=None): 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: [PATCH 0953/2594] 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 6290a9361e36d244122f6af3feb506efe6d255c8..bea2bed4c14ba91dacafda27a890db281143dc4b 100755 GIT binary patch delta 18883 zcmXtfXH*m4*L6Y)fg~hAfY3un5_+%FdkHP{UJM;m00j+QAfPCq(iB9op+5wrNC%}T zC1C#Fo^YwsRm-S8D=l2fqv@AYCYDUrjkmt&q|6yrKz9sK5e`V0}>i_ss%Zo=SQFhI;N^o70&DcRybruO3(CTG|@IndHXQ?=QFlRsp^!cnLnG)p_oK z3)H$Xq-NQwhM}{;o327fvvNa0`-`YC!}z&pJuDf>JsKr6L=_QMywRp~o;7H7HI zf?$X?i}%NZn-B@R24&kZAx|M)TG-*!o16PIIU+g#x}XhMCCTFwb^e{a8@CWLDG&TQ z_Y~*($2$^YN(TwJU=7bi+$uk)-;&GcXQQyuYv+=gv=_cVhYL{9C&JtCjoIW@$OP*8 zqDQ5-DwbT!Eo>mD+PCb4-x@2Jg~|X83m~lJz~zD}R#V2#{&=h;n3YpEeSikn0rfik zF<$v@NZ;(|F(l^}7;2)Mx5(!P2P0X0dOkp&yL+5v^H^%+;|J(Y3HSSG0{2ti!L`=_h*me*Zn1DtVaW?IfzoAC37Cn}pTZhAdvZ zI#FNyo$+r~SM{)6=&~ABL)bKWR}>g~BCQu@k@7E6AnB7(s`}C-`L?dhV53KDv!FWo zFIMVS&>ms3eFk*K9TlG*o6 z0}^i(=y|RcKHj>BGstt@a|*!0`=`2@upmy+7v9r^*_Z`>N1_1i_G&EOjy6C4j8L+> z_Q==Z;58anXpImo5mbFmcZ()yA-ZLn+omUDD!5IP5!!|?zRQ*5!xg@2`tpu@$%1{1 z@fW-TS6i0LkXg>WwZCNO0H545JcPiq#{Ek+-?O^hUp-$cxaRvaquVALkX% z2NYFF31V-gMg+GgOmIL;I1S|C3v_g3kzT5!onhZIEW-3&=sPozvPz&#_zxg8m7tn> zMNgt$cR2vS&V62XXuD0*sndW12Oh>OZP0i^hxWqqJ1}HFcMZA%D5WK`_{xiDHj1G# z{#7o$zH2mWK*=voC}L+rHTw2RZA3p-m(A!}KyRGGO!Vv9M3aFb;NfwFeQThXHVYb| z9Z^u^SFc6fzsz2oPJermKU_(W92-JhiQN%iTczgExH7$QvH5pcWj=)uGfE>X=O^|p z$nK(Rf6#OdHM-Jw~+#goNWvSZ~8{Fd5yz11!kNi9ht9IX(P8`;*(=u}Gg^=xTOXr*YY&)lTx ze|h|g?++_WX1F97do{a4g+;WDtyK8}bUNN-tr5&hAL@h1{ao$JHjr5=Vb(41abI`oVCoF{@;p#kN*6BvX2!&1$S5ZJ$d=Lm+6l45vX879fZ`A=ZA;l3O z^2zz@47oLeZ9*+iUrXRoRl_iTLO>Y9qYKR})oV9w)|&MoPHrulN1=dFVMBpY@`n`t z0c$nWSP6Du@y6Ib&**)OpXIQ?m%*DiB#S22O{d$wbfWJ1{sZ?eSR|_m|FW6;Xeof`=#G8Xn(l6fk{b#X&LfcXoS9{ zpN|zDp#d=P=tZ;lzqaoFGp?W>M8fvy+NAyy2N{fiJ0kLx!iG#Dd!8l7stBTX#L?-6 zqqTVz?)v(7rC$z!gKkaB9`xMSwu$TCAV@{|& zyz7>&Q!zfWDsH)ZWN(b8ybsy@QoB22@3e1PQe1|@d6x6grsexRk2zp0sP6eb#5qV) z6E|-Yg7XG5U6OVm+Ju3laQ1E|*P^3&3i=TQ2B*~dp%bbpM@A8O?Vnp-{_9xrC{zjx2pL&uG{-VgRwu!NH{cQ-t{_=9O`$@(Er2KJsg zIe+RsN4zH8#9vLd~{j4gS~EeKknhSasCsD7*C=|w#x(R>|N`n zA@I|Gf5ILWPvYdpUOS8j2QR|MxrI2`J-%;o4A5FvZ3YN`buF44 zm9}rYx!ooIYepaQcK%Lz;qtEqZn2Uwvyj1XR-KOT ze6rxr4~SSc)4k8>-ge+Ef6otNp$c|yaN@-W5Y85oT~|M7@6ocnn%PX0R!uh8R_|Lh82 z?~hTJ0&+s zrisz{J6}O=$N6kXHs>qjlvxG`yeW~Z@RaMuJeifkTMI)7K$Br>drM~QoVRO&Az5vH z;Y!JZw-3>oq&+_1h6y*}`LF70oKn2Mxj)Sm<~}G-@_4BG+mRc|ASI7y?35fjkoq5U zT(^DgBM(NWSST-4PIh*ngjek67$!`^D+!%5p~4&$0@$ z<;x5We1Wnj?L{6G-6~V_-uCDKJhF_L3bEFmqnQMrX)%nV8G!d_+6PYo!bB^S;$^(~ zwWx?-cErafRMAU@4|uY{%avu+e2UkpYW=?V{1#?!3ajnux0Tj^xR9cWLp&X`PC(5pXa@P6RU^nw!GWogm*TaAhkX5sdIQjazz_qm`}uc`PfO{ zE(3D6nqD3%?M@1IrY(H#3_4>qx8&&s3B{57i$xa=plXc6LP?t9A+6%2NE28(Q%=n` zi<=}vAP`SQPe~%A__GLe1`%v4^2|7JAw8YR-d(!jH1qcfGPF{C#qe!yN+skxwz6jV z-fX8~2Y>CxIXcblV4R;D$6=8#YbLAQmo>0=$(qhh)+_nZ-y@1#+rd6AfcatTJjTiq z8i>FU#%&@odC1uCMhk-YQJ}NA+5?72EUrMEeRXhBzYIfk>uJ_x>AcPb zM0oO+l7}Nplz;^I(<0d22H9R6fhOh)XSWAZd0@@n@Qu8(-(rQFPm(2-CQS5tpXYP9 z4Y~c+*U(&gw>rL8%p~PH<@?7^dHH<;nhR1GMkR9_BhiK{x|3b*G!1Yrwxe}TS=)?o zlvl}qoYir@ZjO%QVlQMz5CcB(*srX+ok9oA|^eOGpH zZ~Ho-uwito47yfOofD>0I&ck^QkoPUp7k}JG92X;M$Yxko)>vOdf!9$AMNC2^AUT- z_w_z<0<6jknV5qd*{MouC!3HyAZKP$3pHfsd(kc_CTeOg|23t4j;ewxy|}E&x>Yr} zR`obEzM-b$x6t0w={vG6S*mO5(qXZ77v20kMcFr(djqx#Ml z&dTs5Q}&%C!?Fovl2$k5)N0jpwb1$f$k1!#`f|RuWtFp3Jddai48=d4gN**uC$b%9AO8Bx!8iQXECfLB^ zZmIBMVxd&Veeqo-VPOlXtS{6*q(pYk=`Te`z)R&>o9i#(FFrB}*WeGto-7r7=#Q?c zk+`xgHIzcY{wrKV@DeoZYA$IGLYHVHsoUh>QmT~*?1q2o*?Z#z+t5}Fhw@PP%l@Z^ zE7s?e&t0=1*{Hq78MaGuGlD@oe8Z$c((7QDf4`i_-IXhFWdMeg)KL%xJDtY@L?~wa z6b}Kw8P{dBh1XwkYEBCx+bfz+gfuiJvi(!i5=Oy)m~5atQK!jI%fa3y(|aj7K3jcy z_c+;Nmm{HtJ!@+!{q<}r=(6(VSLK0mBt&Cd-$Kiv)zyPG5XrlT+S<4t^^U zwDld?E!<_;?x=>TN+XFQyh<=emY(DmYKn zO0OkPcMgr0NMJqFrdDWr`ofR>r|+?@jp1}Igc1`zb_zj2LkPmoq|u30UQsfpTvAla zvipUo5&8PrO9c#BL}^Y#h`)}-dh*6fP>|{eOj!v&H(qx5mj7n*ZFOmR_77aX*SnsQ zUz81VkWOygcr!X67UAc*nh@|neHh2P!Sm7h$=TOl1fRhtYY~?WXn*l;su!YyZjh7! zc#I<_B_RC|c*M^<@6ByGq-m)fVUN3zgCXAdU~fpT_5eMoxiq>JAIAj zOo4ltKND1Zo_Fqr($9y`*(Y|j+glGG+63Dm9twC#&o$(8=L&WJV5xeYkh9^Rl>h)m za{v1;6}(YGGgov4O;j6qCVhlh#Rns9mz>f$h5N<8b4B8Ac&o%63LEloM?DjNQ($Ai z7ws#*F~hF-mD4*{ElFdPbnm(|q;LdMb9KGZE0>XvE2TeWX}y>J13iPf4c?OU&U_e5 zj0jIlozVZTzC5%aE7Hb48E6sd?%EPWbmqI+6z3N?x_3iy0*ek$nNnS|M;b^9?r|H9 zU5#RNxqjG^;SHWLk*S=2YY*xwdsTlb~86j3RX^p;g+k7vEALiC6OL?<{RQ95= z61mCu8_#xXqAW|A0ceG`A{nNcfEz7l7R_KL9|E89+z(AVWQhy-v9qJOLATGb1RBDW zlB~h1=S95M4u3?`5EQzuVJ{SEzN!iZvD+!SteQJGgf)f39FGbd#v>U$c1ux*Ea>{g zz14Z>DkO`;+|a?VI56Uh5?4&J^gT1#4|$hTL8kA!NTwpmKl&#@GpT|d3$ls;h9C&Q z7|%9aE=#FW)gxQVDFJjudTva%7y^lpr>q^n4li$aIMaQTmq$)}!^f`_fy$Do{&x&D)M{5TgwJYQ>3@TG$BH+PtGO?NyvYe+qIJ!uNDw`Lfl+Yx)yXRU}`!%l7i ztzz6YXC`cMfKBmD2sf3w=SzzGNWD$EZVrkY14c4AeTYItF>zSH{Cpx!dVe*krBbrlgp}tFM>|YlJ44SZjUrIDwm{F2QiGAt!IR=`1rd zDf70KUY|uL&4$}N7N*ykCgDELTzgxde-tMUJ!t2WWdIf=H@^a%Ew?04GA_&uK3^B! z6!;(Zz{M73m|nIjD?ji#SXW32%v*Rf!R(4>T+IN`|3YVrzO4$85(%iu{QyfJ(W&X! zw+XbBWAW{&1aO$AF|>f^(K_#lE2wtp2m!pxSb=}X$vrC1UPPdm=x^m zkS|8~orA@AEY=ZP=hUWqI1Qb%Uqu^-#pwPFADPGymGj9GF-HtHXdK)>bze-)!`j(2 zWn}ft*5u7?lMQWEm45XOw+IWIHg?0=8K88oE+k9NqxkyS3(4PvN&Sf8_QMmT-99A*)PXPOBW^M-y@b4 zCcU`nvalFv+__^BTLn7PiA|FQx!GJhp_Lfpda&%>`59SQNMj?7RhD61Jwrwztk^nv zZ#I+3ipfNJh#>sU~-~jn88+^$~*KUn1 zNhFQZqVY9u4Z8f3XRk6@yMD;Fu%n?i75_9YV;4{${96mt7zh+KNQ7wFx>cARoXbzI ze*RIm@>_-MS=k2OS-sJSy_|VCr<()nq9IB>YgV1Gt^9S*&rjq~$r+refeS{eGO>dO zPHqgX!5w#J8{Iy-nMR?(fr4zDE5+l60-o z-tK0uN9BV#9=$(`Adjj@NlvoqtBVgV+9qi zU&&na4oXJ$cF&~`0(N!|FXgMg)9t3IFfE02U(r^wi5Ql~!^77wEb}zNX?IR=hnV(- zHCvf!C?tk)95>X8v(nOI0K=LF6lBF30X_MDm*wWNU^-e%^L%BD=pzqfNjd#ocBh!= zeXM!pNtlR$EYa~@>7e|qB(e_xaPf8hk4s-ljsz?4oSZG$$pIphCw00Wd=b=%JgiER z#A>lmKVsM2ooMujv-=qb;c{9Tynal|HNNsN@ z#&yYLr|TU&Qao_5l|*MJV3;J#+w8Ll!`+?D_}bm6K0^eM_8XSu1x7JQqKAz;eodUC z3)$9a;;18H_k-X&;_kbYuLeO%4@$-D$}JUA+- znWtAjgvClB8>!L$%uc|FsR&_mby8GBW)37oGg;fOXKMSAf?318p7IY07RB<>t;FHUW2Sv>n>afVy^nWxS(_^?^u*BYR6LfwO*1Qi!rErE;0NAlTrOh?(Z4QteML_Mo|v#IB|FMeCWaMQm`yTXk>vl9d`; zgr;fJd)+ik<7}u-<6|OL2$P>;sreB-AjJUffL>)=YO|M$s7Ug`;It6UFM&PkD!S^H zN+#(c92_cN7yDEEH6~3zFNjSW!YYDUUi*&Rxy9^jt*jI-iwKD&``kdk+OQ2Jx<>W^ zL7K?M^etXX3-ug!ZlX4&fv`BDM`8hY2VcQ+yyInquc5PLAMq!OiP`#LqwJM@8S7WS zzlnza^l8%e`kWddAU+)Et}%Is36`)2BDjDW)9aTNh5qJG7^~QufDEUr!>*boJloJP z=X+UNJ*A;y3OZFe9b|5#|C$h`h;@4jV)Wb{V9wD(nHEickT#<>Q7GNB3rljX=>#1v znjbJ7lw`Vk;|-ao+L&&K*%o7r=|jH?07Q3-!kXuPi2n)NU>ak*i5y+`leB99H?P`B zPj|CE{|6{75UT5V0JsUzLc$fpdTD5@RAzL_`l?sK6Z++SL8bxoEr*F>>*aPZRXjx} z-!i?!@9B9u$Z(Z2o!^Y@xek7~n^*H6v)jmyKE*_F;4RMItsQ^1{)drmo6V}3t^YX; z+;9~`!W7jE>LRE4q_a8wdRblGWSP5<%<%3S`9xyx-wrlL@q7!yg>3CGc&as@inBS$ zk}Dq76(u~56VS1lpbIcPR^4RR@`HPzsz&kShR!fq+2^C`0o#&z#}_1ap+S4J>V($^ z_8BQqPa=UJsr6E%GC=wCVo%!IQ{&mY`dZF@T5GE}Wut`8FidVmM04Ay37(@u!qG}3 z-F^#~h}Rtvc08t32)@g56+&PYIq+^znE|EkvFRZ)lt_K6|Lz0Q{(`7_9+tELfXmkU=a*|d1#$|Q^pIep)NvFqKLGfN< zc}O)!4m~Fsx3V}Nu?aqJF()oZgsD@iXrc69TOZ5Zudm*YI6SVq0`+hOF^iu>Lwzxr zvblWFuV107%R^qTaa02IkLhHHpcY8Y|BS;=n{%;@w@HRF0hBQ=Cfuir`R3DN3*}qK z$eo>e=)niAb!M0D%Ux03tJB_EHga2w*&qrnnyDVlw=LHvv^9Id$0?(d+lDY=syjlL z5}0ln@Ba8xQ#wY#(3LvNY{N4({fq*>%YA3SbpANzUmWQ1nq96vqJKaQ)I?fC6*__*wd^EIPoUkYaCP!qiX!`s^FU& z*X(m(bLJ(Pw5mTG@9sz)o~#@GvMky!az*r=aD1QnJY#scbOD<3rvlRUB2I%lLf5jO zYZ;<;pRePbXkI#urT>QD%~PLR{1DJh4s(vSx6m(rCa@~dD(v1LbLT~)^gI}fJ*}Go zjy>zp=tPZn(Q2BC|M20b!`+egHtQSDRWxdvo7!HtgM>4q#71n~`Vh~U<8kOa7(Rpz z;=J&ehVFIn>^+*}mLp^9Rav)j!_ewq*fPX1w35F)U|61^`th7@YdMPt zDvha2(p{$NPSzG&*r|nBe?7PaQ)mF8i`wM2Ev6B$e?}Uzl$Xp8Eqg5^mJMwfi9QRL zE@0lbb)DD+4a-UNO*Lvy>U10Y75-b>=w>c&PGZ%3l!c+z$#5K3^AHV`!HE8zkn|&M znd^;bw&dPYZN~!qT_;?3g=%txv!#5Fy-eJI(k1pS{%cuEk`EJ+_gLcXc;z#Jr(duPrlX?(ufTgdOFxW0lp>sUib51lCYo*e?g(Ss?ehN<-x)|o^vJo1bP8cig z2{&mi){Q7JjGQ1q_$)^6)08~jl#SI8Do8>YEeO|1M%}vFCxuGdCS8@q7`{%frl#Kl z^CC97!o&paq2kwk30sd7eIwfINZ_H7cndrH+w6VYI)p9jDoBh$ByCv^UiJMdE-ITb zvQX{P;P%m!0L37!-gG|Hws8F6eg?yZV(0l!;0khZ(qvLj7Jz8GY`$!*^0is}ayKrhQjfvRv~81zv2?xpTQibE9V+~QsB4?2$trju zEA{U{40f-}wT|ItY#%0crDx;(MEGC)h@2(po4t*1gG`I2I~yl)m!T{A zndqn#2F%NN?WOwrlmwJ}Z;e4#ZeAa0fI+JN&$*UrBDY8!wK2pJx}Nvc<&EHoMURVw zZ`eAIWvS)AaT>z@b>=en#pYl3^>DEr_Li_wY;c({t-NNqH;lOKNP1{M3HDLZksa2w zwedg2xNi?OxD21e5grR&y-?}xze(IEvgNeU@Q)_>J7d;ty1VV=NycmWvPl;K94^=b zn3%rDWuhhNRNX*>CBEojrKYd`*&r+85BqPmpK-*J?SPce2|g&kFF=3jV+vButqiT(hyY{WWJN zPxR1u{rQhefa@KswFWTdRdtcQW6T0qNHU^skmQC+V-do;MKImUBJ*=4GtRq7s`}J2 z+=5Q_BA%KzI>f$mPGC;iCddwE=u&8}`+F^A{SGrFCmVbxk528!`H5*Z3m=`*w6hC4 zdoDpAval_RZ0@`=yx8deH~zI$cycpVI`fyS&%xaHl>J(swjK(k35K7XM#=x%>C3%4 zvfQ;^^nUk4*GI7A1Uu1$h1c!^%!EUmafb9A^!vw84(+A!Zd;Z^;)ZVpD3%f9FDxp^ zSGkRE(DT4z!-Y6r8nx6H4icY;=bSw_-Kd{qmPJGZvMf2D29z!ru>cM&v93zSg%9km z;Kov#e~rm{;h15H^pO!nx>~rBy=0SYc?C9c!8uK20YtSYm}6`x&(5{8p)UM^68)!A z4lbI)g)48Cb;`0ghv~d$XH?ot%4VDU{aFa{6=lAB=kTT{TM;z&fj6Z;ACXOdooSAI zdWKl(gh@=axEIx$(@Xb26yIW}s$4HK)ME#&mSm1p5^l1O!M6TZ(y#}Xi7ahCYEJ}N z`?`KxAt@~SNS_)@exT(Gktp?-nAe5_lTdX*a?Px4dlLBdba=}c10&0kV>aG1zTmdO zxAH*p1ECXZrnysoCk{jDSIYPy@!>ytQ+eli{ZXWyZxLt^b`;)Mn0V&`PZz&I?qXR= z^kq7ak7~}y{u6hLM>}0;nkX;l@&rEZ_L3Etw7J&heO1DcF06wfUTK?%wInlaeg;#Y zW(%d40)1GE^i+<{|DRc)*P(Bbz}`EnU;D*g!)Tl!>~2;*A1eqyg*lVH)trxod4K^H zP3lVs&L=GL))m#(95_!F1Zc3qFbjgXtokd^lKN4~ymhjwc+v6g&ADloCPtf8_t0|uqrrZFViU4mUnmBv3T&%%fd=ho}Cyac121(3%gf-Z-T zH%EpMNXu;2i}rZK_wLGecsRq8r8sj$;=S6eavaAm| zjn-PMJeO~@oa5B>rL($Y=dFT!I=}pRFOo0_09X;PuA!!{3pE3BVc)XY9f8bqkBsNL z7oMbDg0S{kV1){Z1ZkjFK|F!Xkk`BH{qIo1 z(A>$>T#6h-$t}s$8wtTUN9R7A^vKHOHEw_OR|%kltTvsE#mnH^YL&-FAwySa9$=$F zTGIJE&dGI~`Hm+8$erP6d+QOew|MivQUQ0DIPYm^Cv*2_9N>=Tb!P8thM%p-J`I|7 zPSs^JDitA`-0rO;N#Nx@@zy&r9R1&6?t3Vm^NwQnQ;3u5D9x)@d;FCP$lcp+RL#ym zm>d~P`wE*=OGrBUsF4Nj85poi&&thbUGF%z4@~OE(@yr&4|LJ-LJs}*)z&^hPn)`H}P`a=9 zlK%^hTXh;_E97qVId?cNwnK=IzB%j8&zy~7)pvMXLX+UhEquV#ALk@!UCl*i3tfGj zdzy9BIGa-}S5T$y?_U$$Das2bb9Q%H1CcxVXcYhN&)@7qyxXth%zQ2JK96?6X>9<< z7=Ygpq=&jx%)-IQq_gD^ZnOQgr??jt@ zWOI?r-&m077n2Oiyx{Q=G;0j$js3{+mwlQ8psjdkzeR*0z^E(;u@>*NJo1S^OVYsb_O~8s4l}JmO8Dq=iQ`i_h% zJ>8X|1p0jb{i^2Jzx=3iP)Mjwp&($HRPhtJ8XIH4wG#j_O6viD-U4! z;vj}~vKk_9z8lwzpYur0d(`<|axZT;p~zc3c`v5*4?16_o_(FNzwseiGG>#|Xd>$d zxX8Ewp^@c?KR4L`Codg3<3Mz{0VvIfB~GmxdsK;Bdh4d<)JSwBn^NOz(`*ah)VMma z%-oYo7S0$oXEBoqj7?3G79`lpT58>C&hp~pR9y^4Lgzw?m0Qy&?RM_Ztjk! z=?K75{)WeU^$?$O$EE?6d>(G2O7#wH7_x4TeJ0ptXOVq+9d0XM6xQ%lf{9!)IG`a! zBo!s>>ysoxotKMv={^Q zGKJ5E0Q&pBenBXg{d0=R7Z*q7a4bkXG7{4Lr$A-H@7l8nVjlP4ttziOrj5OQE9)5;Hl<|Bac@sJ_LVnL zf+?tKM=YESpD*iIl;4R{jE9e#iBJ|j?z%Rbszt(wLU)m=WJFQCqqNZ#Q_LlV8uX!zT z2ZefdRjmN0R$j+R305Wmi-z*sir?z?P^HhWeBSx*oc{YDGcg9`1C!v=s)-^0$&~m8 zTIuMmG}NU{{VJ*Y$LNJ_;D4#y_Br#D>cNa69CkErYRlqRLBBXCf0*zM-Jv)!{7{Za z*5K68Ur`HlJNmA*LYyqhD(}C)>*1>;jvQYgCr?_)lo=&JL=*e1L*3LnRNL3F_?36P&aEqXn4#<4KZQ<=%Lyp+ma;0wL7MKrSHQoDK_;px z5=L?*+IzBbG!k#Nk-wX$rjnV&y#h!DE*%WE1M@tX|78@U@Yw@W4~ly^@Q z8HIkAI(i3X9QyW9ifC@$K#GI7c>9wA%*}(zek8pIAV+<O>BA%xk=p9kRR= zk;qCrG0LM>lZHGPO1TFvE`m%Ps)g9aAG|uzv8x@l)w+V z6<7rjy%FO&Ux|%Y;!QRXr|X!*SeK^%(9%;^`H96YC>__0Fs;JGR$!fef+E!!@O7sA&k zRqf%T5F+vR!gQACLhO^JShSE|qJ+yZ2ux6X*A4oh^t?xA!c3#TU&|5zD2c{Hy3w4R zT-9OEjbJENHfTw8SP44>4n}~&N~+nzkSrWP)a%2;j)EC=EJJ&4?eI`rU0y{=Nd;#Y zK#9N^1xJQZa2C#qn{B&(5%G+~cq(mZckYx|6*u31f=7ACsHg;9Rrfd}F>wx}rd2_@ zN7Z^h0r^!VP2zwc7Ldu21BL*uN|ma`IviH-&v8*VSNTthl^^Xx*(e~TE371R!Y~B{ zWC4L0(4)?37y^yOO|eImC>i>H8mbS~8jA7%xQg!zs2+ALv4@V$3>STg4l;S$g zfhaXYV-YwI(4L+nN41l>4gx^T0#cx3ho4WHP(lJ|A*NwLv>3`6oU9b}M7{svVUk@y zEGaA`-XYXHEHZ=#Utidkh!&0wi$2B2Kp(El0A86z2jlo@bV@W8Cly?q4xu{pVYo#) zJI`B1htX89P-b}kV>a(}6AgDaQ%Vvm4J8;ng^2Voa0e(pTp9qH7MK>4RIfhg1rfO6 ztU>|^wV}E&{FzmTBIM4h+<>pf4MSbSj8r6DAcf+Xm8hO zDlH%~^3$PAziLznl^Sp=#4j)=0T9fSc>#v<3tH?6{T#Kg;m-k(qxn%|rNi{5*W&j5 zLSrI>iGHzyWPrE;oIH7$7h^yRqSGV_Rx}+M|01P2?9A+4c>%Uzf!#mxC5@G=;~y4Dmn&7r3cL! zgmsf~x+m$;5xnuyILrs!OGZ#ACMN^lq5k0LcH;u#0x|;vv!8JD#syhgq&O#=n=t&` z$(h9SISC;{swxV>l|He^QFHl6I9hno(-@RC4kzO!gOkUpH9R^OA(E(+AeG=?=LU~J zt_2s*aLb;+`EO3IPe@n=<=Dmq1zZso^ym{cqMV{f`BQ}i*9gXyrb?1Jp=I#3j7UP- z3!&!W4{-oTI_ivaG9~oC-m2IThHTKw_GE)MIss88F~wHX(X?sc_z8O|APE%aFX$U& z5tC^db`~$#HE$y+r>vxu8$3T7PL>eYsiCs-Da4)usC0>RsiwDKo5}~<%nU>Y9pW70 zDbF6oe$mG{O`MM6?Ti}8lo3ft811b-qy@1Hs;m#K4Q&PM%Nz#K#mr+WKNp>T=!KDJDt}9`&@LfvkelKWlFpA4ltY zt{#a~dVwoC6Ehqe@s#LuB9>mH(D=-$5@5_9cZzoY!Vvu;K8o%sVUnfs^i5kNPr5Lf zXM-O5h$pbii>b$}5}e3*3Izk(QN*?{&veP<_rv&bvpE<~RiYi#n^YaFQ?W}rxsD}LTZ z;S^I?inL4V&CIwCrc+t(Mu1D@ZK0-|ukGtg-Mp<-IO_$jt}(8VWXrCzUo!ln;QaM& zT&pCe`|v`0i~`$#BkuvzF_ZjhcuIO3mtv5YSmjgs^s{4U=IT5W6(qV0wYVBy>g-0J z%9mG7Z;YxJ6b-p>hBUh%jktEfHE`XZ%vPFzau*{%zSR_C6^l+VE6&*m*sUlArco0cD@^|`s?hI59Tlm{Lkx>s1DdENyEZKE8nZ#pxgMtr&Ea6*(oKw7U3EFFCT ziewklZZL|G;fxeJg%I<<^i{i6%6rp@+VXz*VL6?;%;n&`NRoz;y%3}|zYx05)2Q0e zNEqInH|ji*%pq-Ktm{cn^KZK7LRa)NRti?O6k?j7Ow4)xx_iKlJn<+p#ps14)T^5k z#OEByI4QVjW#$L9iwUNa)sR=yk+R|$PDS%0Dq6B0AqgSkIC*$b#yW;Y{vSbs=hRj{ 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 R9U;Ea972x7`nLFR@IS?K`al2x From ab3840c7ff78c37c20f8c36242eba1b3329778e2 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Fri, 20 Feb 2004 18:26:55 +0000 Subject: [PATCH 0954/2594] 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 bea2bed4c14ba91dacafda27a890db281143dc4b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21504 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 Date: Fri, 20 Feb 2004 18:33:38 +0000 Subject: [PATCH 0955/2594] 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 0000000000000000000000000000000000000000..c40cbcb5a0adbbc15edd7005be0deb672f9d08c3 GIT binary patch literal 21504 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 Date: Fri, 20 Feb 2004 19:38:50 +0000 Subject: [PATCH 0956/2594] 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 76b1762ede..324ce31a91 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -249,6 +249,9 @@ def create_exe (self, arcname, fullname, bitmap=None): 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: [PATCH 0957/2594] 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 4857ce5e63..67391e8826 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" % \ From 7fcc46c767cdadaa18b2b1111864b92ec4439bd9 Mon Sep 17 00:00:00 2001 From: Anthony Baxter Date: Mon, 22 Mar 2004 22:22:05 +0000 Subject: [PATCH 0958/2594] 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 870005dca7..3a9a53ee85 100644 --- a/command/__init__.py +++ b/command/__init__.py @@ -24,6 +24,7 @@ '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 0000000000..729002c725 --- /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 5d5bdaa77e..7fb46a74bb 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 @@ def initialize_options (self): # '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 @@ def run (self): 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 eb419721e4..fba463c10b 100644 --- a/core.py +++ b/core.py @@ -47,7 +47,8 @@ def gen_usage (script_name): '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 586e6bb145..2795b7bc83 100644 --- a/dist.py +++ b/dist.py @@ -214,6 +214,51 @@ def __init__ (self, attrs=None): 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 @@ def __init__ (self): 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 @@ def write_pkg_info (self, base_dir): 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 @@ def get_classifiers(self): 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 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: [PATCH 0959/2594] 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 8c3c8df979..12775d6fc4 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 = [] From 74d0b875f5ca2d37ade9f60df8ab09b90db35b57 Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Thu, 25 Mar 2004 22:04:52 +0000 Subject: [PATCH 0960/2594] 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 8de9cd3f6d..165a009ded 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -41,6 +41,8 @@ def finalize_options (self): ('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 c0b7dd45d9..0a29addba6 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -304,6 +304,10 @@ def add_defaults (self): 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 () From 802e2d1d699ab8830e703ab229c31a7e2749f369 Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Tue, 11 May 2004 18:13:10 +0000 Subject: [PATCH 0961/2594] 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 0c37768179..83364c7463 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -171,9 +171,10 @@ def finalize_options (self): # 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 @@ def get_libraries (self, ext): # 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' From 56255b44272116bc022a3c8b872a6b5441e1177f Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Tue, 11 May 2004 18:18:35 +0000 Subject: [PATCH 0962/2594] 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 83364c7463..0c37768179 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -171,10 +171,9 @@ def finalize_options (self): # 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 @@ def get_libraries (self, ext): # 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' 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: [PATCH 0963/2594] 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 bfa53d2133..0872e96636 100644 --- a/filelist.py +++ b/filelist.py @@ -166,7 +166,7 @@ def process_template_line (self, line): (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) From ffd7d4b009e36c735ec8e7bead8c45c56049f144 Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Mon, 31 May 2004 19:27:59 +0000 Subject: [PATCH 0964/2594] 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 0a29addba6..7021496549 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -347,14 +347,14 @@ def prune_file_list (self): * 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): From 48fa26d6758e83a5680f95d8795ee6428d752a02 Mon Sep 17 00:00:00 2001 From: Jack Jansen Date: Thu, 3 Jun 2004 12:41:45 +0000 Subject: [PATCH 0965/2594] 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 46d1acbb04..c912a15cdc 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. From 10fc69e6cf3fed9c530548ec7a8dbc4536ad4576 Mon Sep 17 00:00:00 2001 From: Hye-Shik Chang Date: Sat, 5 Jun 2004 18:37:53 +0000 Subject: [PATCH 0966/2594] 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 94b8b86b6d..0101bae5b9 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -108,6 +108,7 @@ def __init__ (self, verbose=0, dry_run=0, force=0): # 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 @@ def __init__ (self, 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, From 2dc3968b1f396bca0d8732826103427ffa8f3fe8 Mon Sep 17 00:00:00 2001 From: Anthony Baxter Date: Fri, 11 Jun 2004 17:16:46 +0000 Subject: [PATCH 0967/2594] 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 237cc70d25..4be9999497 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -313,10 +313,15 @@ def run (self): 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() From f94afed8695dece39c63c0122579a9e7f823d9e0 Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Fri, 11 Jun 2004 21:50:33 +0000 Subject: [PATCH 0968/2594] 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 6c007c6987..329b55a9d5 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -37,6 +37,7 @@ 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 @@ -51,6 +52,8 @@ def finalize_options (self): # 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 @@ def run (self): 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 @@ def get_outputs (self, include_bytecode=1): 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 2795b7bc83..7d0a7bad3c 100644 --- a/dist.py +++ b/dist.py @@ -158,6 +158,7 @@ def __init__ (self, attrs=None): # 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 From c32a93c26b45cb63b89156e60d7ab2218feec3de Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Tue, 15 Jun 2004 15:49:46 +0000 Subject: [PATCH 0969/2594] 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 0000000000..7bdb912463 --- /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 0000000000..f9a95ea504 --- /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) From 05b7bce87c1ab146c6912af1520c1d988fa43959 Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Tue, 15 Jun 2004 16:55:46 +0000 Subject: [PATCH 0970/2594] 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 f9a95ea504..824f733601 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 @@ 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 @@ def test_default_settings(self): 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(): From 8191b39bcc348a1b1f89d7ab3a00e6b2afcf5e94 Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Thu, 17 Jun 2004 20:14:50 +0000 Subject: [PATCH 0971/2594] 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 0000000000..cef985d79a --- /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 824f733601..0a11abf6bd 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 @@ def write_script(name, text): 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 @@ def write_script(name, text): 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) From 7e402b740c8846c7b50ce89e2ac4013f7048b35c Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Thu, 17 Jun 2004 20:16:19 +0000 Subject: [PATCH 0972/2594] 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 329b55a9d5..cfbab262cc 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -53,11 +53,11 @@ def finalize_options (self): 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 0000000000..6cdce2c776 --- /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) From 08d85503d984dae19579aaeeca76e8c4b5322d63 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Fri, 18 Jun 2004 18:30:27 +0000 Subject: [PATCH 0973/2594] 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 c40cbcb5a0adbbc15edd7005be0deb672f9d08c3..8ab173b662012ef4eed68d00f4704b761c182bae 100644 GIT binary patch literal 61440 zcmeFad0bQ1^FMq;0t5vU6%-XUYP2Y*SVWg@#z-HLbSQ z+Sa;M+hP~1)&=VZf`VJc4REQ&*0w>gVx@{!&GVjn17erY_w#&zzvua@@S5Cv&Y3f3 z&di)SbLQSa?D$oTm|++Rz-D8Zd|c^A#ee?i4=b8mb^WFlv)koNw|r5|nQrmy%nZf6 z^tm(Ar_5GNn=)t4T)kpyiXz=GM=^7bB7BTSF?;Uxlzwh*EtGcBZANY{*LoM28lmsN z)20NJFAeJl#HuJxzVJ?_3+@6rk8R3wcl}5BCca+ zPGhN0;|$ah3=<=gFooSOs2c047@4T0$cbSlqh^&*dnW}~Iba@@Rl+^WBp>=|yfTbP zXhWR?-vT7+M<4vP>B6@bZ(x{BbXUbN8|D~UK3=<_V@V5l}=~EW!(UO&j58`XG z{fbNDj{>#*(x*?+Phptff=~;ZyaCX)*^dgf{RA||+8Yf!01EpRm&P9z!xZ#O&q$w! z3VyCtxKRMg0L^|>!YIvZ{=NNI0{=?jUkUsxfqy0NuLS;;z`qjsR|5Y^;9m*+D}n#7 zB%n#qa(bn^>3*Ura)TGU4->F?Y$EL2 zdbj5=k2_6>G;li6xJeU}UoJ}EYK^t78f_vsq!ifx_Ya_FL6AXea4w0E;?T`#*(@Sv zkZQFWo59sqVQjFOBOc34xm4{@j9$hDv7wXAps*R_z_7Nr*=%6RR3OU{gF?+tV}n}n zq89k6=^tcJ$Z~G+G+lKLwgg3zsUS(;PZ&q%@cb}_o;*5n1=4IhW?cP=y{|L%O{(6A zB9QbgfS?oeb(V;FwawsVGkDrO3cw(j`IxF20fIb;+8~3w!3FpExUYfy4enZ;QHztyZgZ(AYe707qkvklHG^2x;(fLwmv_DX`wagK`oS z5QTriq=_$|5QH6Q%#=$tT(VS~C^M}A@47%sM5&HZTQfBb!$r92MCOQ6F2Y?k(HwD_ zi|{l@RO$lRJdh*2yJ~atMXRzuP&2)Z#a^ZYy{pNl@1tf{5o4VQN20dg^JAE#;Nwa9 zwrF%yvrCB^3FZQ3?gK|{9e@-KFL(iAdUJ%D4*vIp401yolAys=W4$((VbE14nus}{ zo(H)cZ=6wftv8emez^p>M$5%GJhDu1xRq#5uJQ?U^(i!3+i?xzv)RGwXh$b=rd4K| zh5U&z#hpntE@{EY-k6MHjSClBi~d}ul?!j($11dQxq>S(b< zX6i?s3;Tg7b8M~nr0j^ci#fJlc4V}IFL;R)YQ^VdN5*)WaZ@NelGssP#zn}*306zE zLl<+zHH)e8sm-RgcE^kw@8!bmvJN^{plyP_tk*r9@2YU3cvBs;Fn<058nOle>& zIgUih9B~txHwH5Lq2`D>EDSE9&gN%5gidO@HPqYuY#!lQ*~VfyEs2v*IT!KR7SUiV zkOCjQ(A!WdJ2Il9S{MbLuv#YYeOw!vR6)v(_me=qk=vvs{V1p@m5X#$B;Cl$okZ>BVjGDif-zh zYzdxVp31fe_n-)O*~(bVoL`5SI~Utv8(3=bDz%oXu}U)}m{cR9J5VD#GN~gb%*ED= zOMNPZ7IW-lwe>wTf^OqP^nDt8Wud`d+aDGzRNJ*N5p79HptnMr^>BjTG3#M}gM%ts zW1qHNZd`H|%(c1zZ2A|}h_C) z@YpuU<`D&};vH^p<8kST?5AmHTKXdz*{yuvftZ=lms|z>7NjP5;t4FcD2u595`|I0 z?}H(5+0I@i2#%Kv#ymmvRyFZbvmN5iuJy&*)%xGpnhmvPL!H@h(`=|W8y=et4K|O! zbQKePJhP>m_1Xg$Py2=?;wC(Ho!x8K;%Wt415~M5Ik1}PNlEq_$#;T`)@=Z{A}yPX ztIZ>%iFEC5kj@jRVSj#v13XRjLF*wLjr^oIHHvyt`e0#Xi9!~9@f(-Tg7x>0{zEO$B z_G(sv#^$PoI%-B(Oc$|USqWyU*4be%s9m@b+M2C^Bp3%Y8y81Qhpf|rKCJ3_ z)wt6VIzdZ_ERb~VixFVgesp0&2-|KqJhkjv%KB?`qL(cZa#$P#_l3?Z)uPuz*e^NU zVtJG-M+7qx9XPWTvMmZ!WGNJC)_UhXRpVNapn#3bK6>axE9& z4K50+Td3fXA?6kS82&0)JB}wK0x}8X> z6ol_zpbd(U9wv$F1JssSsX7kP5mo`y8q)EYfN5(R6@xY6kpqI51yF1r(BVg^2S*fdF>T6D@1Ite&BIlGGIJYW;=f5MV6CpZu$I3zZu;Lg5XZ>g-6sOkeWICCK>l{v^xxm841%DQWvevrH zj?+R%QLXI)&naSW(2oHE_!HPuf}=?j{R>_`aa7J=YWeig$*M(f%y6_U=N(KZJJVGz zg1wu*Kmmb%rZmy|DQ-7cVw-?<>3PFX2MTZfh<_!Zii;E%wY3zeB@$&7))9172k#Sz znS&8mG;;xRd_wm^lbW?+uS>L*vzv}XKTqrh6?Dq4U$jhu5^CV?+_M)-18HS<&^ch; z&7);T0_O*{dANhDe*w+PIHU}GGWy}hA;d6a2-3okf%zB)#MN!4Rvp8sTUer6iOb@~ z>Lf5$4>gH#3w0o?w|Ow2XIH))>aDY@x1MMO@Os69*NM0WTRE5Tm_z(suVZ}5d?Fr? zRdJaA@qRuCxL0lq~c%BYb-fR1sRii zUz3@Tf#fK?P%Bsn7~t(@sZ!1u!gvC`8wn_y2)OphUic>r8f| z6Wc9GYr6mgB^%=BkZb3ZPOjlfcf)9%=r4#|XX5Axg?EYU9Y_~664|=On*vPE`YVo1 ziEKeRZhyor_Mqy8ApN()!t6ZR_v`~7pzdmo^-J_hO48Wr^D;mzMVKq>m@4p1z8X%S zm-l!z-)y!wvwk4N7Q?LB-IzYwylfsTfe&7c4GxB`wgAGg1@I_afx@#ZELPy4gWTqk ziw-q%ej^uV{v(30EpKYU}Ue*!9Ad-fHVlxHA^JXDyYqpP+AzI!}9C zejE)+Nwx(N)Oo3``>05_Nou;K5o&PQg8wmR)(Q+-Ow_1>J8$C8 zkZg%SxNE9|3e?~YQ6m({vt$So$g4GM7AC-ZA8$y?9AUNN3)_1VANjrC9EGRi4l1BX zljdjK7?Rm1oYM}xOyq+!!lYvp0*HKx99~O=nL>H4xNq`W*D+_oQO)m5E=V5>fAtn_ zVAZ>oSlkRBKe08?DJr~CA!xR-wN_|Q@C~#8t!8KR7LBVO4JvjltW^rj{z|3h9PjhE zgGx^j_@|wW<+m=f)Pwm=9_iV}1&8o#+sXq5OH5&>sy+enVa7BKP9+^O0J72d9YULy z%=9JNn%2FY&*zM@nj9zh17YeVNe*~2ilJsu;I?`B| zp%N~_x|A9>Q6rv;ov?jYSjAjKl{c%lQ$kuzEWNtLUHt{Y`Ljwg3sj6%9Ow!s_uWB?%~!Z^^2G zYCVKb&q!=S$vDJXq+*x@A!t5CE>f>z6+{)n;niEN;RV|mTqjuLq2We1=T7;r)kHK& za8Z+Ces&i1;l?3>m7uK;W+nfTq5~-M`BQys9RIA4An3to5HUK+Cc{~fEyG!CE(N!+ z7z$s>9n!E8VeJ@u@{&o~LMTGKhy*DYK<99Zp4c7xX!$+?VYu#9@aULf%!aJwWS{Ut1cxS}L@M=)j{*XsQ4OcaG=SbYGT3D#DVW zc;P{1rvhV@tFd4j86Hv#__ScVU`sJqNR^x>%HC?*J4!w9vHL*Y5}?s8#G~2K&NP z;r)}K*lJ3ZV-JvOtvAV_%@OsDHfq?yZaHOE6W-M0Lv}Bbu$N8vDBB^++BC?p- z%M?fRIfeh#l3NF=ScniaSLa_4T&WMnK~_H~D560>ASmLo-Z!{T@6E3%emNyt6%Y?C zD#ZMl6lKyK;3u)FN}jDM{>bS596x~vFb>wWg08#^kkTE=s+?^Qm>_RpZQ_T$DBQvY zu*Je{9eD$DL~ZbOSwhw^h?PZEFfo)P6?#hSi>{fd~~#3;2M&GpX!IP zUl1A0gWXCuTlmaf0%bYJ_?qq?BYmCKFz`??8zC~+UrN9q_kH`;kSH0T5 zK+QTKJXNa^=TQg5Ygh$R!3TBH(NL&mUHP8Pr_AF~aAwS`6B)ehy;+heTMrw2#)_P! zd;<_5lIHxdH-Xk99<{Yi5IHnY6>-96cj<6cNu==X!o#yUOC;O^zISt$?EM5*_?~Kk zpG`!>E*d0^ct#9Tb`?*dQ9UaKdgO}Kc!U|S9=3=@A%q`zIEQ*;vC3ws6|V}YqQW?XXm z3k3#Ss?4+w1)NKQI#YNd%W*^Ta27Pg9jE-{Qy5!dXDZ8be#1SUKTMx?koD8RIG1c-4fxy((v0%YoQ3?dLHK3_NtG0Thz~>3UeQ=S!lf^<^o z-9h*^53+DL4?N(bJ&beXcd*X<4pyi-OsgnPi!B1#C}3eDbyus|P3ZdEifr25AaEX& zo}dussy}104zDt_!+2by(K=V*Q*FDzKRXhp1z&_`W7fV6v^CP8iu%sImYOA2-e8F0bnM@+9s#(aN zX#DTP-Fv9D&(b*1zHo913Lx__xLeiOD4x;f;~}k<&$98jeDXsy?oX*Ui9PSZqcl58 zQ?B({RBUj}mmhA7eV^a>8e`w3XtzhatZ++Vu00N>P0+sc(RRo0hx{gK-veu(-vdts z#bV>jr;RW6u`K5+{-!~eL(lnx@NhThL)-*kmgTJBE9zu9E2*MTmXpI*)aqa5l3jx@ z>-B*x_4C;2;0H!bN)ukDFnIB5)1PzB}56%ItSz@a8R~C;Lomgg)*_(QI4D6WCDau2D>n_0rZK7co@=R zs;AZ4Y?Bdc8n+jIJ=)hS9&b(Lk18~7lKn*jJ>J^TwM_Cauw)kd-@o^{B@<86>OTLR z>pUGkTj%;Y-?YkXSb6XBBa9SVa7jdgA0%eZET)B4iY&6(aE%M_1-iNX4(Qva-Fw$QStyU^0`tYwbS@@-#gp*ZI;#$P!QwKVuT zJi{?ZyhE9gh%YP=D>42m1=`Gp-Dbl^zBp{XI^$alq zKJ7U0e!$p>2L&7*9%{kKYFN2|Xdk<88pcNMpW$mS3AJ137>vp~s2FKednKJ;buCy* zk!si~zNw;#HU%*nwvx>7G(O-I__^^<`B>T!H0(j4i!g^%;7z?{6KGlQ(IFwJaj!fL zem0X>a2hfdysUSI39?j2d2j@5RE|N}zaYDqL`H!&UO3U33KI2mxlB1{kn&H)Q;jx< z`BU(C-7tDZlt`8J&~NFetcU)~x@0|^qj$}Em~IGG9d~kztnueB$DLY4ju}()0v;6U zr1S_x%C8RBkQN3*BMX}ci}M?86*iB7=*F98<$*LPMU6+x zl=|~H4Te%NZhI{|8TG6RAINe*fqxqqBkxepKi+eG^C!dL$5}!85aln@SBo1$u2-m z9KMWMVk^P>Z#bA;mE~|?EKHZIK~+PN$(E$g%zA+tp=*JInE!qLWhZf#f?TftsWKQmIhl!gR{MX8-YT9RIRC;hYO4O7@)JY1^l#C4%K4dpdUkG=FgiWE4 zu!&0qX^e%%K|!oW^B=-p-gtq@hr7W1OSsG14W*;Fy5M{IL6qUg%a4Y~TI<^Tl<$%c zfpupY{vl3&-rpXcEF6d(@Lp33%&u)RF@?W=h40SRMx~>BBW}(i^&%ijLxW?j=Gca%U5pqOlQlw`$fea|G{F(d za*lu-u>E%oo3sdG{qOUBmmEJbfNu_k7$mSrP-!5Zi{*HwA|3)@<R%>3if#g;C03M-%&_YJ%JR#zhQ&N-8tU?0dE=0a(2>0BOX_8!?ij2 zTCC?r^7@X=TP`+hF~~JmC;Px3*&^y|Qkr8HG{=-`tkdymrDk|@j4%F#@suzW%`w(B z$8gqI12I~XF`_|Yi>;HH&SC%ezYp8zjg-75b{t1y#GPu8x3s@g^vX370;K>I@DaiQ zlmao-v*8$_DME3t!JB2ZjqUZUqIsiSrxIFNSE{z#zjfvVdMT3Mz36>Nn;$-1@ycqu z7idivX%i3gc`XW4v~)hFD21gbLR#x?NIhu_Jr{!N46WIDM^$zusjJOsW9j)4OA#nb zX&m7}5*{C;iGO^gceW0yhsbosA%w&jx^qKrf;`?!re%X%A{G1)0sC1$A;~4x4CCL7 zp9h2ET|`%v-b?4ivKSabs0F&wMp<{I^s37}`r+YpWJYZ40gBtz~Z!Leu{ks>!={Z3s!jT}5+W~wHTx$iQX)ac3 zmNt+6NRV8D6is-sC&qrKS^lH?G`AEAcsJ_MkUkd?F=WtektwN0mO2QnScs8J=VDS^CN( zikjACY#!^uE$nW#hSH4Gz~&w0#ko#sh(~r(t+vgVs5LqV>y@*#J$Yd# zM4uScBKym~@depg%atI$)L8_X@qt?`UKGYVRbT^e_?Yqb{b`Vd7HaSi5@R%)_gj}q zJ9Z;H-Ry^pS4!}_>YP4^8}1ccsDIHM4zd0>CNVKVlW}KO{;+6!IeoT z=HnrTZBh{zqm&1qHAi}7I-3_tA6!PXbguA`WVrF6pII%<6dP~Ij1Q$t+Z!LsUTvc_ zM|qhS`dVGl)mSW_3S_JE z&|rKb&v?OPljS@?LAKY4J9D9GjPaP1$cf7$(aaOY0O^;}JI_HMnf<6MPCbU9{9BM=1u&QeL9+ zd}k@%kgNnH*ifNWLN6#8t7P~ZgYmW`xNyEZmVkg7>X}8TUjkL&T{)or6YuTmBv7GuZn&}!>V1T< zCjVtDu`pH(n!I<7*fNsg=P&!G`IF^*XCL>UX7s4bpJycDN8!6k^b=^T9yH9j|7IAo zCh#9+x9U$LahFQO@YOIL`Tb)Oy_O3LgolDDz@S1vA9K)mCi~!`-PPJSYczF3h?9= z(&#>54^3QQCs#Ylay|nx7e-(1@#M}JZ#ZALVJl0o7;p zvHOFs(D--L;j#EBT_zv&;9O6#fWm4y;;Kd?kdP5LfdJ_tP*4I4n3ND5$gdMGS&k7a zD)=l`=~=8BFYu`dF17FYUbLn5BkmwNe3{j|DQEzim$32E3q&z|tHP%a{I~*_%jVO;;OW4aO&}1f88k;qNBds!?ag8(j-;Ic68qS3=IC+8DymRX z_#rcgP2j@D84D$awgb6Oq=<0uhvvC@9c2A(rz}2uQ4@dl?nVkNU#YmB^C3?D9WDX0rylur! zG}aOpqmCHS zb%`XO*}NYq7=0HMgAWyU!_*;iCS$B@@0s9gvapO^M6(heuaPDy1)`C_ush)NE2-!{ zoEAEym}(A>X1TD*Dw4E0Y;tuJRBT{D(-P_sY~{)N2=T+Ke@j?EYouKPQbZX7Bpb=0 zl!k0yOjs)K6^sR9TAbKKXmNfHnWYYC4Q7wgK4OSLbFej|Z==1)o{je`+N0$W$s_Ux zTmcPvUAT1w&j4&Rgfl+`o=zGIoI$HM38VyC{>u`u(*e?RbdZAY$zXjyp6+2^i0^PqCO}@|CF8`UIIduJ3sk(<#B5-HOCZUZB`mHZqRdMs8-Ar#+1w&t`xQLa zbhlen9xSLv25XBUyprLbR3W&p|75Q)7kKM&YD+X5rxRJ%!lOR3R(B!?QY%3UxAN*# z_#ZB8I+=c0l-=+-%{WV#S{=tlPbgt%$B(mwYheBk){!9gy!khVfqF9c4nP(>veD+0 zo|3&(sc?XD(_e-pV=yak958Xg2~%$NGb8@j_<@@5Shac9Kx-yc3A}htEvJKmAI=9{ zm>&)ckR#Tc6ei(Kdf;u8kD{x;!lHe)fM^wza$(_|Bv-cgbnqD%jC~1J1FzBC|HBfJ zVChjm8U%e86z5>4+=uz+A-(h!u4pD>hwCW?f}a@ojVlqi82^G5(^Fv^5EnMGf(k!` zs6QPg#u~^1cMQH$&D-xW;p|e{Yy{$r9|g-r`69-M#$>ZG zf|o#^r@bH#{Z+KU!0v@x_`o6-xXR&I9Q;2Wo!VnU)w2w1bT! zrG}#pClPa)szglglnW!9j=;+4NfWT3`L8P{OciNgHCMT4RpS(*1Hrwxl!D4=p2HHc zJ5h{Q+cE4ajCxLmVV12*VDbAy*Jq=l*PonWRFgCOizffK8YE8s2Mw;oNy09G|BVJQ z!hfT|Jl^?2Gs4+TMVk4H_76d`q@d^!UPm5HIvUxiqiP`j*E$NZ>nOyoBXVP~ChgCfdw~g%Y7w)}RL^x@ zcmvJ|hM9)r0WuPW+{wafPxzoPcVs(-uviKu)=UV*3eJ%aJ+* zqp0wWv;JPgrw!rb+LnlMVx#vc_%x)zryh7(2{&4fWMK$4W?wE+>T`?Jy8Ap79}`!Y zktWuXQeh(EvltON4Zxev19-h8>RWSKSDb`>D#og`WPu}bp*U=y>9#r2*B}9HXF8`z3?0D#KxoR8 zhb%2IjU>OVQMdz9!Qb;z=kIF5R7_BWia7}w6scm~2S`TX@dBV6FfqhgGJ;qfYF79ci3#rzJi0mf-nOaW+I z0yu$2CqT3G;%Q12OYtgVAQwr0P-6f$T8Wf=A5P_qQVSu6dk%pBH`h(jv-DqQJ`oI2&~kqd5Wqj9MnP5k=?)L^U)p_iXft>zGdNVo~g z_)(Ua)_-uU;FjjOaw<7j>T_zeCCASOA&bAoiW|s9DB)bfxma3%QCx&Cmpl*^-pCj8 z3o;@KB`+>E93{e3aIsA>f2^9z^ov46pnV6#G0_vg!ky|ujDN^76tca;MZ>vM7pja! zQtp(vfiGXEA|!DG)xqn5XGM>*!mf#qa@pR33wL}jLvF+KbmiYn4bvJ*)A9#Ljnh)` z(^CCqTFSpnD^IRJYM(pi5nKtrkOgd-1^5>x^7nJ&$K$Fok)M1jF*lCpCKpr19Z(JD zj{6iM`azv+IejnJ#~PfbjFP=hyA9fXF(t2&$Y1xtjknJ+F|UkZtIW6>67!*hPCT7& zR)mT|BV%)&4EJj?f{$frfC+qYsZSc8D2T`IKFyBJ=ii>;Hx2@};zy!uh!1fS55g+W z`V6hc>)GHLo&u?7t)zDDTYf){$Jbxy54NbElH9N5fl zhd@4_uQA>dgK5Yw-tZkNDYY06L!4+whp&nk%~!7&DT>T(RTBkS9ka)G^g0~sp1)u! z@y2Sg@gW@v;}=|oM{u`e2$Y;ws6feytSdNP>6Z}~tXB?{y;g_^M>P@Xt;YDC*n%ih zDPbu0!&z&5sR5M~M%8#C0Cp*fU|OQBrVY^5NEK1&i#i^^sL(35xE@0@eQW(Pcd9X- z1(B#84VK{|v&a%DGKadNv!BmVV}YYNRD%0~KF1v?XJQVOn=}0;O*AJDBqtmqxFKWh zLjMBjgyNYH(hVS7!>w25U?AMbB;YHKD{&7`4;cc+0Vd4vV3(I{pKDmEEj&hLiHT9E zf{&&D%B@ts1ANXUpHG0rC|%LPJ=M509B(x{MHw4J8NcD766)qV;nS5v;9+n{#uuT$ z)xsj}VOp|4*M~EPm*^Uar^s}TVho+>I*KuLq-zXgXm333XnZQ0UsfZJtZ_3Qcc_sX zkBjlep5v0>Z}E4E^h!DZD2zK=<7Ced?~4f!7oYbzx3Is;*dUvK$8Z-wipSFr1&_S?(R>CL&{&jG{V-GM=@>$Fn5L z3tWlO%6x+j)TR%P(zm1FLb?nl0R>0l3iI}@3ChpS#2*uI05g#0!2Ht260m&fYp6AC zxg35BYNkp$KjGdF)$4G}(V>UL!2BD zcNyoB8*ZYkY+OH-^wTSqshks?s{5SEjZwCi?JEsFndy2Qiy1QwSGK9?yR55hA7S{N zt6JeI$IZZ0P9g`gTtMKFCzGU+TLp8TKne6|GWt{86W+}!ECWrL10#N9aovwVNRGAf zt?9fu$Z#JWJT0Sa_DPwJ>06LLJ7+Z=GdzMTXlaSE^*95!S6$=af16l_`J~!n;-g?& z@lg!stu%DOYW9-lRFRlgDp%2^T(-{vnhJ$1Xsx@;mb*|YMh~xs71ZPRV9J1o%X02v zYc^dq&<0N4hXfo52E8oqbe8kUd+sbHCma6^ZK zr*mjaw_<>B%*fk59dQ}y#3&f92&B9XZroA3W-jr3;(PbJdYy7zS8=UYV7=vruNFZq z(6&!$A$%G13_D%rP(1>?Vi6GfilcpsOt(o>Dm=VuZC8Lk8RIMXeAiVz*(gl*OmL~5 ze3d&l*#9aR@}w?!$a0E1#wQ`v%(XQ!aH(&$5J|p$DCNCfdV}7hrUkGtm~1ON50LF` zCq5Y|ei&u=oh!tH-Mh&6Ke)@49HATTr2qK`4@_6NN3z%3qE9kc@-m*xOln!!6p-RA z8SXdJ{Rd_GUi|Z=`-Nh?m;d<{t^Lcge*g)F^IDS&--4&N{zajq4c{A|DiRW9uXUo; z{j(+*4E>!D zo+;ruHn>XvOMlUWpoL4EFRurh!ll0`;~5hqhG$HWK%Q9#sqq*QC^G)47$Y;?!hzTL zRFWRWl_P2HFM7t6AdjWWHeARd(k~c$@{Fp$(G&lWMvjcXN=9)dhC`)9|4hRt zd6c*z^fD-(-AmKmv#aU8FMXIqe0@eOLeXNAh`Yf_Ih90BUv1FgrTz^#u+E#IVy17# z3S!jL%{}RAtYz`iyE(-Q=a~E$F9Ul7Vw5DK+qKJJ#pqT=7ksAym7LqnB5de*SIv(v zRq>_GzEohg+ytVvH9TKX=N(58MY*zR7|iCOBy!dtF%;{%11w1KVf_}x&DW?uef#De zeA}oJ1xv(wyz;*aunEUaFLY!j)Pq?d$r zH~dCbl4ezk`M;Ty%PbLFL9T3`in$+ACemZ4kGNRVDr=}>ekI}7{H|*;PK_@Z*gWV9 zA8Kn>1^q$Ib?D2WUR*j#m-6;h_R6S)+t&5(<~(1P~Uww~^|*m^4P$zqqM z4-sRf=d(bCj*DzPLwJlBLdOqU{{8t^0{=?jUkUsxfqy0NuLS;;!2hoj81yN_^a8j8 z4t|1v(FWKKa6q{Z*D3)0)NW>&rvUm{g}Qlw$$)r36d(}b4Oj-CAHOXO69O0wm;jgu zSO!=N*abKWCxK0J619Aat0b2nF0jB|1 z0Ef4MS6mwaEzpO4Zsjq|HNbhmNk9Q0ACL!F4|ofZ3s?q_pua1iBfu9B0*C@w0TTde zfTe&Hfc1dxfHJ@}zz%>Ga+?g$0rY@nfLy>^fc1b+06PHR07?Pp0oMWa2TeZ}=GV1zZ7~1{45x0M-L?0gC}Tzyv@% zAPVpzpckMcKniGp+e0T_jHJ^DN1UJGc)s4$hb&i=Tc4;X~@(-1rv>ye1 z;&%$58_FNyDgtB!LQ(z!?Zi(KpaaTp+4a8&7Q(-E#_Zj*MA1e(1XiyyZ%Y0Jy8AxSHd>}!chJh{Yn2X z10ZLYwRZh4K{*)Za=ZR#p$xlr*^4XL69rn24@*Z4Co+|*O zQ2rhLN&o+}{?FL;p9-8_!2c3glJ{~zB+9qZpY%Tw&;{iW?fPGa@=%n&v+I8j%KcD2 zglj9n8-UR$KlvZle-Y>tzf%C+fWHM-5g;27it>+WCw`Iu9Z-JzPwW4JUH>!D-W%;- z<4XLR0V7eqhyJ9`e_H>i?D|guPEX)}jw{K_1c*Sn2K~vNk^!Aj{=lyPOq7S9eATZ1 z*(mo#`2enN01F@%%VdRe{a|ST;TKv{t;YdfHwhSP_{90{Mn%Pcy8O)u{G1lQ6z8e=*V<%6iGZBLz&Kw zB9+`x%y>GA9NZndF2qXYV9qkjkL@7dbXnfg0MwS896BU|pN4;M-K;)sramQoN?QNM(Wv#$G&2+#DTq{>>Ywi;v^MscIY)YG<(XNMU7LMoBoX5j777j&P~f`qD}1C@fwG1YRk~iO;4Gwn3^_s+N?$# zkZ)=i8uW|iVS;SRl<6tyifQT76tib$%$}m3#xjOEv*yfQFh^l;OHG?HquCgZa&PR} zD0e}=3uexlK6im4W9H(NCOXsR&YqW^l94fU?i|JJ6g@k4I^xw#6%zso2KWIK0CxZb zs9UOHDgo3+dY(ORihkzQv=qhslyo9ObD3&LO$Fa6>FIORsXTK|25M$bo0&34k3nY7 zO<%_9%%izp=SrC8#90jWu`FMOd%r@Ws5`(BC$v!auP{Jt|Ga}Q=}02iBzJ= z&Bt#XvvDjMr5%3Twh2PHjRpFO@*7`Xw@SW1?*Kgun-s@02+xg9U z-MoO0r&VuRGkwF0xmR0l&A+y&OZBCy+{@?R^@-W*a%kz!WzK!`e_7r6$g1h`eVu<( z?9fHMvj5)o(8D>I7q4`zx%u7fxIGu^OH3Dx+I4Ad3-V@7U;J?DliCw0E5=L^xnE9m znl^2S?9uOTQcI~~PVhwe%$S>@lP{&eGUKCjH^m_%E=r=GnB30K+SB6WM|pb!($?)7 zUVh@hRn^0TU;LO>`J!RM_Yr5^%C992IrsS&ewTZ_#9WWdimNDXH>K>zp8DP0dK7=H zeQ(V-XJsEB%HNRh;4RH{oV4q*>v3gu%bl+;o!h^43_I@g&Ql79^iAEnD)icXr&q3u z4l2&C>m$E($86g9O4mJmoj$&KWTXGZeCtOioEHv!i2qKO=d#vwU8@@h+;p$@9Wq<` zZrb$Kq6yP}u8*5FYwDCVuS&lZ(`aVuZ@1Str7ihb)Tyvu`dVVK?DylBcciGR_qDl| zf0$dEyZ`a`oxhtpvhNj-drL2@Oo_Sp$G9oGV!w^s^OBx9xa9}G0~bR-{(e-&n#v)w zi_dNSqrUutu6kzJt;aa4a6>hOYG z-tzErh3ko)d)UOUO>-447gP6dygB9DRu8XFTzBGf@9yi$cI?ipC}}_B>!fVA-Ti_m z9NPC~+BXFXWB%>EUmtnj^@qLU_s{Rt2aULN>#GIlKlJEzRbp&+Eu&4#X+P!!Pk(2q zZnj(Ac-`WbXItI;(B|^?3)`6%J2yCIhQBSVJHJKxI{TZb%hRJyGm{giJ~%ZaCF_+z zX&ruXnl)#Br;AUkd@h*BFTNr_J?gs|bHCsJ@Zp`qZ-gD**Z$HQJ0@GVl$S4i``qT@ zqm_YUfBSyS-5CeY%}6}>$#+hBhD8tB6?MhOtum%li?8mF5)YcZSQ1)vXY%(6-_OXq z^TwP3iw@6>T>15sQyq<|2REIc+jGzl?ASF6Tu+Nfv^=oCokREjy&Mw`2Y++SyXB!> zZ;jvWBhh_5;Zs}1(E(@6_HY|6`*z-b{pB4;r+?7vw`sNOx9AKl-k$BS&B2n>9XrQ(rR$k?hqGVqwg3K&1v}<07_rau=ke0qdH9o9Cxe|*C$to`wrxmxbLRG` z4Hs;)rbL`gYq#)+7Mp%O?^Y3Sl#D+5wK!->uNj{{Xg9emcErq)R~O6~I!bqL`?c}q zms4AQuc`~K9IV-XaNDsB2d=+*cGrk%+a5o!-wqvGarB#$es6!R`DDv(rQ@KoZ{BvQ zIMX}vdi;?YmwWVEoceYCs42w`o!I1eeCGBUe7NQA&2P9Cb^6|MyycDqf+~gy9nh)6 zYf}fkSfuIeBRQ5fJ)(X==JHR+YfILKj*+goTsrc=(Q_-TcOIR(T>qo%m_1*_F5AB6 zjlRYWXT8?F9@pmRD0z>|nhW#l7rqsGXG%iF_I~a^?-^XNXPwvD9HV2>x=ycoZeu>U z`l#=Y_fHMlmYlrcXRow0+xNplFK%cx{>(?poHajsY+R{`-1X^{1=|jPa>IJ}m$G|S zrSW=E%C2KqKHHw0T(dDU-23#bxig>4o4SN(BtWBYF`Ijt65-_|qm&aM#KqI(&KqpZ5j zPRBw+`dlC9IA&2@__C?hulF5u^tf02RnZEMcT@*jS&Y^mpB%j$c*O14rofo`1Kr;I zbZv`o*IL@TFsCO(4XB9Vraqc;QgdTLeDZ;BJkmC8DIaFZeYe%x)#`N>*IREpU2^`> zjoP(IkPc4c0m@`KPGLq47LB<1JvKQHa+ zaw>6pVA<#`+gFQz*to*8+v!jJCqFsZ=c^maFMll?w!5#f&BnA{F3UexMxD6jp(*Pf zx$F;iLCO#J7Y*ybFG@K`-^rzOK%X{}-wtY`zqUk;op(KD(SSS4CO^IR^Djq^K6$!y zb=jpMn@*j6G-!qKfb;727seml8I=T0Bf?M1Urei1eA!rV^D4gLB=-ncR7dg>y)C^|wq-pTzw5oB zE*p2f8kqC>4yBjxT@UAg7bE+0$yhMJdH0QZQNNeXiW_8%T)bdc^0d(vk50*czH$Ea zp3@I4ITcl-+N}MmWe>~V(s`RcG>0C@iA?Sxc1{cISUk*QfT>k0@8Rpl%n)yjUsmvF zQ9|~qsWDxT1!hmI@3HQ~Pg{L`ZLP;=otXHa#t#_t_`Ru9D=IaMzHL9|#z^h5N9r|w zE0$dJIz6@R3QOOy2i6W;Z9UMn?D8h3+I!0;et9%y#GKVpY5g~84AwzyUfSmD(nA-o zl=`F$YkhOkr)xisI{0R4r`20u=(FNb)xlH0ZMKxvOuhc3pvRq`FCV&b(EV}Qr*6ZH zD|#*4wR+foW!bz2k5e;)BYz&gc)^pG_smMURzH5(vB55yU*`u#J=nS3CBJTCo6Y{G zhpk9|qI{dy|6tuw(VUAp;-tfo4%hNroNJDra@w`jA~Cfa zNpPM1u%nCS?Mi8jz8hK$88M;dye+-FMx40Wxyyo&y7uloxyu(V`*-?a%I%IvzyI9x z_dPQ^l>Qdb#7dCb8{@%|Dk9*2)-n$p}>3aPepUBCN zd%x0RsCT{BD}9URf9?Bz>F@o%{Ah6h0?YgJ9{e)ymF-DgGT!sQmhRQ_?FBw#;^wE{ zZLg2`;F4kCHS@ADd!t`%Cw0s0Hudb%t@^A*Z+98K@Z=`R;wLAMFWJ%WVA|{9%VuAH z6ganf)uTCS%lFKBxpslhVe9}lAn@Ky>8oE&9p*86TI%OMQ{oD%Cts}iB<06L)2DwU z?w)FySv5m(sv@fWW{;7hyK15r8CH*aJnj0JQ!ZYk-`Y1m=Hu_y#jbn5IP$AHrxEu@ zt0D@OuZ4%dcqTMsoJ`f{=aIuZujIni?A`bo4*m&44;m6(%J*qk7BpxMeD>1VAKaI! zFD*M9XASyc{P!Bg%gb-4ChdHE>$ridYm&u(^qw$2V(!G*{lA=))hXb{zE>Vp{d#ui z50^jA_;JG5AwSQ${PdR>XXO9n=(hM~;HX}AWQv==YBzjTJEeZ|?VCUJw_eD-UAfe}BH) z@8A6tJ@{Is>#T}9soTo;R^GXoy{qpf$=0c5{@-por7M zt{fk!@+>*6O+5NX$eN;$Ub|4Z#@f0d@2l8T8;`vSO`Ryc+q&fmy>s~Ku-VJcbi7b- zw(ow2Z&ho*TD{=$>{b1S`@Ge`yZY_*LqA#jdHnQuf4kLv&9SVicN$6#Zan+tvJGq7 z2Cm<{=+TGDxqH^NbzJb?r~?B&$UJxNeZK&+#d%Wn%857J-k8aqeY5t%tQA)u4Ch`O zDKYQqaXdG7`uoNst;W6f^NucA=dN7Kjz9f&&a_YBOfPx1f88zp((+AZKWuyV8%5r? zlGM*0&e{6;(uFnKquccUs?8U3zwCMZ%P%~$?|$09#{ZM~35Knq!}fh#+`D1Zy|9-y zfA(bQmJi=O{83p(@t$8gIPE_CscNtJ%xn8xub$Zzyi@k|RN2U#2`StTwtK~aNl_l( zINjIe4_Lo?|Lzsn4;rhy4qcfz{>bft>kiZUPnmHrpDT6w?ANdMv`Kt->YFdtT_1dH z(yX5Q11mnf=P8yHS1ox{n`|xZ#KUb4R}Vw)FRs z%uiL9JPRJr9$mIEz-M0%%Q4Tj8#le$=YPoAvSs)~LK8~H0*a@>3T z&W^2Ha$UVT?zo!$YQ>D8)eF!2)OVfpw)jD>;M7&`8*_*CUwwPr+r8r_hwmQX+~E3s zUTl|d&ffi`_WQSh*O%n84&hgTDRiI}#2)38|&ONWt3I4%*lWtEu_fx`-W$F=^I@H?Ix&=>liG2OaFV!!WzV^l&^Db_?VR?7S2HpFIJ~`xEStvR( zXwL30H7-(*_eZC9w)Ot@hVgsXz}Ey z+s~$da3bSm_q$sYN38X{vn}Y$C#NU2j&S-lcK$T;-ES?u!mn9F8>YuC>i=+Hm$yT& z?ChmEQvQBT_3?*auiPBba%!u(4$d=UHXn}rAmr+x7jgpLduy%wX}^B&?T_#E__}pN zn^%%oE@*dS;J5t-ZTo!Wiq;W74-+f0jR)qhuKKa<{RuBWNva;XZfL^YZceYIg-;oJ z@w0Ea<7c-GUKSYoYI$<6qB&oi=Pxb)^_vIe6*Et@-PLNr z=My#`-5+#(nJDY&w)H=~=b>ylXw#rMab9nH+AekZ(O0ev^}RmVcjW#H@6=^WT-N-Y zzjMh*w=G@z?d%ja^n6&~q&a^S-M?D>$?6$#UAOHxz4%*K{X0|kr`~mPyiV(XfwSM* zUt3M_ea|KC8|T@@9p>rk->jbYalnS@Yqqqyn)~9SYx!HNE>(AV_x$Btm%TAQ%XTh3 z^h4!z#-%Eipt-`%XKzqlvPc)?WCHf^1D`mDTy zCsQ9TUXgO5)?GAV%rvL8%a3G3rdgzJzX#7zl*Y`IPrNia^yWu1UP%uT-#iyBxj5py zo9W5NE%wX`*pv5Y_^x$nSO1^(z5}YMCC1+gF&uz>n3U+G8yY#yy`^)2gsP^v7W|y~L88*(Q*nWq92^w;y$bZI> zLZL@`Yh*q|2X5bo0_A~16G=BTU=_I z+;)0ov)o3}!)%v|WviZuLXD!L{NG#2gkiI3`&)0rkgu%aidU9KTc^%y^#0g~<|l&a z-NmO`9(c}Mn4k0a*Msw>h286L-~6_%_ixvxz58-~H0_fm3?7p0e`#|b(p=BAe>n2~ zR_?G{D~h&UIT3yB=KC*h(&v!_y|O|ul0Ulc6&-l_`Q)AJ|DcVSe#$}xy^xmw@JGf-?({m^Rtat zmM1vfDmdYH|82mP-0(MPc|qaM*$wY`WjT(z)vWjJU5zbAJJuUn!L2`K()~&0$G3&w zALAfjT<$5&k3TQm`&*tQvAwn8YHC2lL&pnEV^`!h8Bwnb$Kv3a1~z?99vYgTb+}27 z-upt9h3<{4UrCZ3jY8Llic}-Z(U1pbAG-gotlh#(r*+mT!lUB~MOdhuuWwhOGTX`qL@%)-6u@fd# z$z)d7xz6`5hL~bFvlalwvG-=Tx>x`24U$V-b)bHY}BAD|apZEh>E)EC2+ZH?j6DbP^ja&D`8}LCVAa=@&Zp%KJ8H zXSdQ_`3Eo)78Rh`!G86^`A!4sDOw!llzLoKGE=xWKV;%~IpsJ$Zp~+tTU5Llcun zbxF=>Y?57h)-3b({ZVV}hW&rR}=m$m-s~WYjV>(Sg4( zGwkYCDP$EOLF zic-&Bc9Igu(&;CSQ(^iq@_9#h;bgsgY?)E8#Xf0ic+Zqnn@1-zbH0_Ne7^ba!jQ|) zzD=Ex5tX+(E96^NiplWpNlw%ISasVy(AfM<$6lWOj2&XtUdqya{!xV=rg$bgiY8>m zp06zOy;}OD`Da)FcuIHJu&q zm+YDlm{D&`eC5&6VdrPgbUN87x$FChdri~V|7pJR?&CmY`PI)Mq$n(G^`&_JE$>pl zHoVG&u~Um|SA<=#x^BB@L8}2Pa|1f%K3m!_wYYF%!n>Cv=z^UxucdM=Keg|WmX6Q5 zniyv^>J}K;!kO;9GCls$l02`iTb5c%4ln4{{q6a@2QLa2Z9Mv|B&_&JdDqr?rH7lP z7k;%_km&5ZG%G<_*zKV7y!j{BC!Ryce~X=RmyXIlnGo{6jBhgNqnA@mj!EX8jV)7N zFL1ule|fiW;n9v0+XN3<_p4&y?coy2q<1fO_-_}aa<;xz+4@>+}bL}->vt)eXhc(vP53_ z`}3&mIkL#um*XQG0yxrlP6NYsO>ZQMHg^{G-Y%K+{FpzTR&-q~KKz%YOT9zk52TB{ ziq2Es<{R6L;&<{Lxl|kPS0eiAYh`kNbjX2aKFRmCdY)L5Jfh+2T^=sJ74EaT9phf? z=kC%xSncZlq?g;m-#P@oiQtXt&j}CG<_#Zv;HC-x)6w4ohWwe!o7VcFe}2M^kcWex zji>nc$4Oqz3Enby&x99C2ZZ+gQg5QdBSf&XZ*=KE%NY+oPCoE(&*iee)LV<6{IWOn z>G$HO$2sBs9!+W1pxkDpQ`sY``O8fk#=e^LUihr%}%WQqx-4by%J8hb@46;8hhdR zieJ7QyEbdn(H75EocGza?c9=+DQC`3J$1H8i#HeCjWrkNM7v!+daeB>&OzIyyH>jV z9{n|TS#MXn<o17%y&CI&sP6h0+Bx*1ufn`ti>E`rfnVo$8vMxJEp0 zRVAjR?Sr_j#-P?T6&^_MG!*(yE$K)1ui8y5LCCK4dG(5O;okL#9f_;ar z9wZ(KnRq)tdB7{&|L5d|(|?+rS=cgrSJ{%VRY8MFUiY((65L&1YjHu77b zIQ&3d1PcHoZyw2c8+q&AgZr`lgGFXzdF_Hyo^7?StTan$?YiUBPo(VslzEi7e z`xLdK(UcKqQ~T{2-gfbs@r{Ru zXJ^etg?)KW(p4`7O=pjL{czahRdeY%bFbVR@?hWPC4M2X{d+k7{m5)iKyd6|F0XDi z`{aKl;&ty&8@*PgoCxV+-|OLqAiwF}ii*=88ZE!!lKyM-*Q0F*rWE=v>UyAe%F4DU zyZ4%&dg$ATb{sA>&FuY>W!t=-eO%i*cv#nM&cO~He&0WT1=7jj0aQC_cc8p>lJmrS@TWmJrSpc^-uRK z&Pg5Zu1Nmy^;*^MWfES(*xQ3FO%HbW+26pW$ClmYr>vUJ+dT8m?)HPji>CL!8n<-0 zywm%kHXrLHuK8WEw6F8Lp38$@?Q@?L*z}XJLr0%&_tV~7KJ&%7o)E0xvE$Yv5`II-j`})q$J9S$T%aMZ+)WLHTY1JFzIR7mBoUR{Vxhk76(kpem*Dyc@`JK0>Hfc z4u9K=#xCh{^RsYGnU<5Sk~ z(wq6ByY4oN#Gkm&KKG6H=PvI!fN!(s_KP()kL>+?>Uk57jlZ>fZ5p<5wAA>Qwf^}@ z-5u|&T()|ko!xhyP2j=R8=jSX{cXbJmIF#HVly1=F11+Ty}h3E6OY`)nbcnomd1<^ zt!U-_eB8G;V}>WoW;N`5eYnv5)WS=DCJf5Bd~-V2Nq*dB!nBzAdrzDzyE(4IO-1R( zJ&oVB?)Yfbw)mN?&P$87$C_pxEZ7)olyGYBx{U8Lx|y$j?3n7^=uwI-HQKvJH1ZT==cq$YjFzhY)sx(F8dn z&4Sx+f_>z$6*(_Ok2hlrLcsvDH^^(1279Jq%WAR#-WGrj06eh)%qtzr0hm{+zPwM{ z5PAf#pIvo(?CbW}|2Ov7^O2ikj9e<0YNW6y4K_-_J{>oO8*EV0(B5)pe;Pjj>!$EF zz){HP{{8I4G8ytlJe5L8t2FWa2%6S#;gknra@TnFi!Xds&t zM<7Khm4^eCC32G~)U=mS4jZD73{%4hfzoIi36Q%&?GBB^u|P#NygjC8P^SR~C3Dek z&KP-g4a$w`$zVGd?8F0l!*O0{wumbh<2`0@BE@L5LMR?B4O7AXEx>qWHl`>zfbcXLz;`;RLn}WP@q=D8yPB+b$?I2)WZT zT0`STaM1v!7Sb4K98^XEKyNt+X2_3lKCqN^ugb4CMoxADVfvfE+jK=!W9qJLS zh@lNNpe4q{IDD+0A{+~(qm>4^XdR3v)I>ljqdWq9J*u@V--zXp7i^Jmg}YP%4KFke(QiAFtNX(LOY=Nd)*!kt-c8m9rR0z;#Iw**8UsxorJbGt@H3t~wrjX&ClnppI;-|%4cT<{{Tc0|Ex9aNJKx%h~vpnt33{91MX9>&p;e2;<(^Twxf(7%o7# zNUV>OY+S^3AIs(Gs)-J8z|n{zfG?{!u&Gp}j7MlS4yo}*-y=9xE`q(g`w1T2#G8*p z;F{oM7=hs&IV2PfTse+Iq`+W~gi%UVY9xqJ%h?D>J6b4%jk|Dq&qz=WK8EwjG5Luc z33gd1o1<2XgmMX-=;Ek|{ld9Ajzol=FG3SA7C03#N1~yj#|Yy{)SzxCh$DfHLyr77 z5{Zn+M1U~~td)^KstAJ#hF zvN)`Xp)(j$qmaP~Gb4dgL^c_lL&<6;2)j@Pg6J@TNUIV=3*lJE7S3c)c@0X3Rxk|@ z;7G!0jT#+g+6-(HK^~<*AP}m;;UH9)J_cO!;g59-2qfJix`qUFoK%CnkRVv3#QqD6 z0C{r=5G_Fhp(t7`lcQiL0Z2rH*MTC{s5#URI$A)EhW)q#0SH&Ch6s2z*ddJ2ddMk` z)T%WCiC79Xh-7L28vOnXB3h{tM38P#)gPe)NFV~! zQ7L2+&Wxjqg8s#B?>M&3DO6VOGVKB!z2tLDd-#ti?#nSME%*YwGLLtfjSP< zaiER^|BE=l4121_^i&UPbx+*<8gn=n%HZO`Jv)#40nPx}qcAXFr&Q2Is^n_Q8YV7q z_J%nkV+dqncmmtc5StvMfjptOGH@mBbwT)Ft50K}8ulrJ=Op+AvvCHS!FLB3#>db9 z3Eu)T_5r*m@U(&_oXSK^;R&Y`(aS6s{EaPi9iDCBnFmi}cBNeF>YKVn0 zLwR*5JMSCI9C`}HD+aJO0HeA8Bu_Lmw-tmb;*eN3*B6M$+#kzO0@62#A{ZYD)Jj?e zD-;5WLI!8V1Td?KKuivSIsyi5BCQ6w@x9$g3q1XN17THzgn0peBLcWSd{}uPaae!w z@$(hv!7Q8~$PEY_!voqi@bq97?(V@K73jxv_YUxI3-k*ZR}25gWHuj{$8+Zf!otc^*ua&p#;k}EWnFh(eay+5#U zg7{uAtK>5R@Fe_aCfe!<7|*k_C{MtK(`p(89N)uhN0d$~RCjB2cgmJI-$vQTS;rxm z7zHU*QLyw-?Gt+RG(^DdqBY(yD8&mS`1sq;(z&W|ShA37)U^{KuREMhWWCwFDIORuS??~a(e{2G~e5OzHgTskrw!|O=Xytfn zMCtIVlPQ>us5Kx53Y(nTxp8A2UbmvgdGN6$xub))q-#8j+QCWG^i#K}PODLAef zuh7EbP&k1Ji-0OS%1tJPBc;BuLO`?*CNNUDmZspKsvAC3E5lGc+oE9RM1i=qO15lL z7iB!0{ghjat;NiIjYr}X>)@a-u_N6Hg0iqR=-eIhRZuJ{3nJq9<#?>52PLu@O?R%2=VA7~DS=p(_LO0)KSbfustk0fhAscF;(? zDx#|^Q!!uy%pZs7{WMddQ0CzVhjT-%)uG-Dqp?Nk>s-wXHPxfKa=NzR4!qj;|4=5@ zbC@E^s#OaM1JqYxX9rCAd76l%;q_9SOG)HGKuGUfwDKuZWz7Z>%|(c9K{Y{~6Z9VX zDJV-t+t!d4;~P)_EK9`Wnz4z{Bv>&v)T%JN-bZ9wBfmldDrrY~OR!FY0O6P~l%k?z zU_}}$pa|yH;WUMhPXj8^TOAEXDr#DT%}(8i5)pc?;N*20{S)ntB{o6ymKQJZ1LKK? zwNOtu3k|vfij09WUaP_)eIO*MJu6a1yR8A0G1{_)F5of!l)i9?BR(%_<05faRWS9Q!#igl&mJpgz-dcScVfn3C4iz`&E|-tC^%kD9*4fc3c3(;d((; zmGP{X<%44v(M(^}t5+||oAE}hso<3;7+F}4>uVj|*BSy|L#%x$z#}^FL#v@|ef{xb zB5nYDDisV>tY8~OM`L@95XQhTMSoIXD8lb1C~~A;-55F?;$5SKq6n!Rf1^QX!VDY_ zkMUp`=;2j=)<3Ws2mPsB3d19@U1P?fV3h_N1Q-SHur#>vA9|;1E5|(0x4}@qAqD8+ zbQM@ROOsLZAy7T&7s1T+!<=A-P3)>FrPhcQFib`UaT>2F_J=VA*dE0vLngiKLKy%V zXzCm&kVic7QUd%Qbgs~c!C(Yx!+6>sID!3e4S6!dE#k{`_Rf%De7(NiM0QGjfwoLL zLD{h2q1s?yPy*}g@R*3yjrc=sSY&~g@Tw+#Rx8 z12hs(N!UIezLmkm@t_9#WnG+gkofx$;3Pm=5(Sx`!X5HRHKf8&Q?h?}7KQFrtB1@kIYnf|yjfdi?~~zwWBzKph9_I8eud zIu6uvppFA|9H`?!9S7<-P{)D)lmi^{-kL&aMgu4UzPvo6W=Td!4gh%XPzzok;cuY= za0YDPL&@-jX)oFhPmF(tBtfA1G9sY|)p1?G2$|G}@8IjA)lUZgzVnl)VRU%6!i4zQ&dt?f^Octi4Gq=jFOU)kgKaJ8a#L~a&T}!J$v>T6Wg7a1FPfI&e1rdlc;hOL?oNSoyOTc(cqB4N%8Upum z0r<{!9gpGP%gRX@LjY7GUj=1@^KCUl$b8pUv({$#4*%TY6u`fi5x5PxBOKS)v(jZG zWc%Xpz~wrG_=9fZJC_?`01qLlW+Q9lt1vT%s#j3vX$)U(VZ`#IyyM$i)&CVeqv=zJMvwplMpzf3-2cy%jhk%yJq{q5a3@$f7jVV z8II|GI7q_qz!El;A2tO>)0ro}>wd&W*f`eXn)w*bFDql7Y#R;QN!pC#HSN7jR}%BY zclO8fm5Do`TNxeF>!yMI zRnucjWoR+l%HH+5StB1~f!O510)P&o#ZtmlEufvO>}up=EU>B)j6H;r6R|pM1;j>1 z_*d&@jeLv+f(~Ne!qU@O9j1qUz(3I=nm^9pVLWMNO?oXHs&%s#J;wI1@`J2?XdmMZ z7~w%iou3DZd4K&3R?f)jM}C6c>vZ#{^e`VSFdvET>*Z&@3)kUi^uUMcY(0Q$)`Ix5 zN|&1Y@?*XVV)2Zvo8vID#_g=OdqzKh%*R+fV+SE%{b2qz?4Gd)+%$4k^M$c^tncu{ z)}MBS3G3`0!>h^Z`KdE?wztJPuIqXAVI4ICIp{(EDpXz1e}%0_`hT9!2>uf)mKMHT z&`~dDBskS5i$M4Y-Yg<5J|s;3(Frp0f$z3c@Dsx5QB6WLycd-sIEZ6-@qUGGzkh^X zqtEIgpfC#s!f0Dcas?uOaX+0={)`1PsRfsH(!FZ^MfN zWo(8rVEoMD;MCgkfmFEuTk>gIEg#%c8T>?~{Yv1t2I|Xjzouk_kRoMM7`|yiQZ$19 z?_zMxXf%^|1kFR)@E*-xXQw{m?Ki3%~>f&l-jQRmzI7H4f zkSDZMPOL#exQ_Lj0?o#Mx~sMx{5S2i0(4hrr~fxw3Il0`le%JyVJzgIbOYO^H+Th| z?P5#EK0|MrShg`x8VxBrs}w=07%Lp06bZq9pa5OdmxDR;Pc$i_hZ>@vxC}MSZn4ME zdk0r26Z?tUUgO_+gMan_SV}WM7uTZ-kn0Q3qiCRxZAk`C0r)Ewv?847vl<}%LH_W5 zy%y9#Cv5f!|I32dNCoKLOQM^zfTxCD(opwm(=f2de_;!?^$W+R~Yf_*t1~I|8G1q?n87vir!+N`v0QcxRs3m?m_%F!{?b8TzA!RppFA|9H`?! b9S7<-P{)Bf4%Bg=jstZZsN=x@haC7ncVh9! literal 21504 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 Date: Fri, 18 Jun 2004 20:39:11 +0000 Subject: [PATCH 0974/2594] 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 cfbab262cc..3079bfcdef 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -104,6 +104,8 @@ def run (self): 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) From b48d7f0f6502934c6ee3cc6d3d2d2ec6a602c651 Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Fri, 18 Jun 2004 21:28:28 +0000 Subject: [PATCH 0975/2594] fix typo --- cmd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd.py b/cmd.py index fef49390da..9362410487 100644 --- a/cmd.py +++ b/cmd.py @@ -68,7 +68,7 @@ def __init__ (self, dist): # 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" From b20a6ccc211060644ff3e6f89428420fa59f5a5d Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Mon, 21 Jun 2004 16:15:22 +0000 Subject: [PATCH 0976/2594] 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 0000000000..0cfef4e5cf --- /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) From 08c14ecdbb73fbb9036506e74a2bdf742ee5b13a Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Fri, 25 Jun 2004 19:04:21 +0000 Subject: [PATCH 0977/2594] 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 6cdce2c776..757d757c86 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -48,3 +48,6 @@ def test_package_data(self): 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 0cfef4e5cf..7ef71cc120 100644 --- a/tests/test_build_scripts.py +++ b/tests/test_build_scripts.py @@ -72,3 +72,6 @@ def write_script(self, dir, name, text): 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 0a11abf6bd..2e86dcd8a9 100644 --- a/tests/test_install_scripts.py +++ b/tests/test_install_scripts.py @@ -70,6 +70,8 @@ def write_script(name, text): self.assert_(name in installed) - def test_suite(): return unittest.makeSuite(InstallScriptsTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") From 1bf1bfa6924e28ef13c43dd27d35ca94b988b36b Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Fri, 25 Jun 2004 23:02:59 +0000 Subject: [PATCH 0978/2594] 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 7fb46a74bb..3c36ede622 100644 --- a/command/install.py +++ b/command/install.py @@ -242,19 +242,15 @@ def finalize_options (self): ("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 @@ def finalize_unix (self): 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 0000000000..c834b91b38 --- /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") From a6850a7120f88d68c4e2f8413120dce52e8b19c5 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Fri, 2 Jul 2004 08:02:40 +0000 Subject: [PATCH 0979/2594] 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 8ab173b662012ef4eed68d00f4704b761c182bae..efa3bc4f1f8a2632e71009b24317e5417f2a0522 100644 GIT binary patch delta 14456 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 z%0D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 XeFj6*(m%X58ll_0O*eIXG4;OyH@wYg delta 14669 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 Date: Tue, 6 Jul 2004 19:23:27 +0000 Subject: [PATCH 0980/2594] 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 324ce31a91..7c593adcaa 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 @@ def create_exe (self, arcname, fullname, bitmap=None): 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 From 89f251ec91fbc1a66a06a5aa903f6faa2d44d047 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Wed, 14 Jul 2004 15:22:05 +0000 Subject: [PATCH 0981/2594] 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 efa3bc4f1f8a2632e71009b24317e5417f2a0522..7e93de88ebb447bc758757638161718fb4d62883 100644 GIT binary patch delta 16266 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 O*=>HP<(BSG=ln01UY@=H diff --git a/command/wininst-7.1.exe b/command/wininst-7.1.exe index 7be9396b8a9c39abaddb789180bfaa6752b8d9a3..40f80035e4be9a80d6abd461a1afc0eeb14d18c9 100644 GIT binary patch delta 11920 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 Date: Fri, 16 Jul 2004 18:14:37 +0000 Subject: [PATCH 0982/2594] 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 7873d297b3..2e9c90c7c7 100644 --- a/__init__.py +++ b/__init__.py @@ -12,4 +12,4 @@ __revision__ = "$Id$" -__version__ = "1.0.3" +__version__ = "1.0.4" From 3d2ade66dfe5efa2a561cd804d43c35974ed210a Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Sun, 18 Jul 2004 06:16:08 +0000 Subject: [PATCH 0983/2594] 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 d5b3096617..55babe6fc6 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 b0360a248e..f995e36711 100644 --- a/bcppcompiler.py +++ b/bcppcompiler.py @@ -85,7 +85,7 @@ def __init__ (self, 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 ebd93c27eb..a3b1ffa4d5 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -685,7 +685,7 @@ def compile(self, sources, output_dir=None, macros=None, # 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 @@ def compile(self, sources, output_dir=None, macros=None, 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 9362410487..be8e826fdb 100644 --- a/cmd.py +++ b/cmd.py @@ -79,7 +79,7 @@ def __init__ (self, dist): # 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 @@ def __init__ (self, dist): # 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 7c606ffc4d..4eca9c3426 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 8ee3a5c5f7..3db332d5bd 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -53,7 +53,7 @@ def initialize_options (self): 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 7c593adcaa..d91c08936c 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -115,7 +115,7 @@ def run (self): # 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 0c37768179..04cd742cef 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -173,7 +173,7 @@ def finalize_options (self): 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 @@ def get_libraries (self, ext): # 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 165a009ded..e0fcc23e13 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -94,7 +94,7 @@ def copy_scripts (self): 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 3c36ede622..175f785214 100644 --- a/command/install.py +++ b/command/install.py @@ -537,7 +537,7 @@ def run (self): 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 8e347ce6dc..8104ce0656 100644 --- a/command/register.py +++ b/command/register.py @@ -286,4 +286,3 @@ def post_to_server(self, data, auth=None): if self.show_response: print '-'*75, data, '-'*75 return result - diff --git a/core.py b/core.py index fba463c10b..fef291f656 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 0101bae5b9..a962007601 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -75,7 +75,7 @@ def __init__ (self, verbose=0, dry_run=0, force=0): (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 e195ebdcdf..2a87eb5d28 100644 --- a/debug.py +++ b/debug.py @@ -7,4 +7,3 @@ # 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 e479b62415..77f64c4104 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 7d0a7bad3c..b4dd0b9170 100644 --- a/dist.py +++ b/dist.py @@ -231,7 +231,7 @@ def __init__ (self, attrs=None): 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 @@ 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 '') + 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 e230ce587e..e2b1c51558 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 0872e96636..566d87ab28 100644 --- a/filelist.py +++ b/filelist.py @@ -167,7 +167,7 @@ def process_template_line (self, line): 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 01420da9af..024e7c28de 100644 --- a/log.py +++ b/log.py @@ -28,16 +28,16 @@ def log(self, 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) diff --git a/msvccompiler.py b/msvccompiler.py index 1441ea04c3..168881ad2d 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -117,7 +117,7 @@ def set_macro(self, macro, path, key): 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 @@ def find_exe(self, exe): 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 @@ def set_path_env_var(self, name): p = self.get_msvc_paths(name) if p: os.environ[name] = string.join(p, ';') - diff --git a/spawn.py b/spawn.py index 67391e8826..a89b88cebf 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 c912a15cdc..0a4e14cc37 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 11ecb9f6ae..24cbcb53e5 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -206,7 +206,7 @@ def runtime_library_dir_option(self, dir): 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 @@ 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, shared_f) dylib = os.path.join(dir, dylib_f) @@ -232,6 +232,6 @@ def find_library_file(self, dirs, lib, debug=0): 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 12775d6fc4..387e9bdc93 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. From eb85239d5cdcf684b6f19ef7cce2cc969accdf6c Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Mon, 19 Jul 2004 09:45:46 +0000 Subject: [PATCH 0984/2594] 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 d91c08936c..79c95ae2ae 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -253,8 +253,14 @@ def create_exe (self, arcname, fullname, bitmap=None): # 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: [PATCH 0985/2594] 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 7e93de88ebb447bc758757638161718fb4d62883..ed288d6a260ab8e0b5e8a9b79ab781433399e94c 100644 GIT binary patch delta 2835 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 Q{?YJ&%%TcsP5r(2-#D6bD*ylh diff --git a/command/wininst-7.1.exe b/command/wininst-7.1.exe index 40f80035e4be9a80d6abd461a1afc0eeb14d18c9..a800678ea7dca567eb518e427a9b267deed5b9c4 100644 GIT binary patch delta 15767 zcmeHte_T}8weOi>#EFiX!7z%7I!VTaL=%Z4iHO)y8A1%`(UBRZQIQCGm;zEb$7lo( zWT21nNT2O7y{6YRT5W0@+h>Y3+-Mae5$*HS+W3n`Z|X~J@3BK)NW!%wr8)1r_8A15 z^tFAT&-?4W`Fz%#{bTL5*Is+Awbx$fG+a+;xSp`1LH^AyZ_&Sg^62P6X!-ET(XXKX zo&T&I{StdF8U1bcyqDEeKKv@HFL-wrd$xRN;HBWH0SFVhyYn7K z@esg8&$f-`ZDV+UVL%bx$<4QdV!cw~PF3pM$qn;4u{yjmpgW7c>gAN)Q1h*jSlsXc z7u21*tI^UO`LixP7^eCz)SEk`dz$|!+Jl1Qkh;Q=mf|?%<-#NMOOa07>BN<}k`gu* z6|Y|PjJne?FTVwo>(0X%7wJ!sCh3w(mmIoe)5hz%juFir_ND|A$HiWc^z-6fyzbly z+}O-a$z^Z)0}YmJVY6Q73N?4AlagGMj~Z>l3e0-*)i}aPLI~Y_MJ-BtXk@Fej|ZqP zB+WO^JEXpq^nf|-kot$Dbh&Z`$0J&iqSkPsMF3LXA{l5hUBb*1?a!RV^%(-+yMpNweZl>UTYHfHfpk4TKxYxd@^ zm_e&7nOhgxGK#Z*eGRIyyOaXC2LzOQP02}mxOH|?LVv=ijq~}ZO=f%ZT%a_5iGMo5 zVsCzwMxT5XTty3d5`Jz!5ttG!=(C?#;$oH5gu((taX1G2y)8b0idM ztvTi?QQx)J=I+mQo!7=eLFBZySgcSG@6BXc-$e$>|+^$jS#UOip=5eD&8l=}2^-PucnIvu1@KZjl@z5(6W zAL+0HAEPjOrF6!A;>n4;F%3pR+e)GHU~7;~F9bbUUJCMq?1n<(V(ip9CD+E|@9sew z8?kve_%NqUQ03%_@-E`X$TMWzGpQ+cHBQ%Bt%S$;}S%*PT5e2UiplABvo|KH2!0 zh_1TdqEk4>#_sGM919V8I*M0AZkE5efEDJTH2sh%)J1m^M__YYyh)hCWXD8{FL~*EpIm|T`p-bEp^X#FsCUX9Kus~ z^0&t(W6;e5hJmye_AeJYfS_1;J;|Wmb6EYw_z4rwL7K5@G9u|3^)5?Ax0M$T99B&! zlg!%>t5Z^@m@5ve^HU}T^GNi=^XGtq*OQn9@*#{p0P00f>UMMB(_&xwDLvBBWIVy&=8ZMc$kenUZQ*Ho9 znPd~F1!NjPjl5N-BhTJ+19Hy8^F?}!Ql9uso;W4X-uxe^?{!00Xvr30uh53@IdD+uf2{YD3csc zH#&sfm;k-Q)*E{^&V`7B$R@NH2z8hnTNtJ@K-Qg`P-Qy*xX?rh+J<}(h7m~9fS}!n zX<{$kL8tmoI;NttrvhQC?rZ`jU3A-&UfrpI$7tf-88C!UG}jV}W>9hUkaRsAflQzE ziQ^C!VvyIgnXq z{K6*T7|u&nz(5W-QnI}ye7}ArZ1P8a$+~kM>c<=22D4i4VhD~8gzaIwUzt1y9r^`9 z*Ja%>V;&6w?OYsbRCpA`t6$KiWO`n~1hatv1`YlOu(;yYo{neEzUAPc_U)X5Yi>%x95rf#`#yOOx|#Y1^~=Scj@7XTao{y>vd$sHm=HEY zeXB`ZNve!4P6&+mbP(r}>~!$Li3=my#1toG2B0Fqbkh{Qd#N|fDg3C`yt@<5FIyhA zKnt9CB&vNhKo{q`u|YQ=4W>Mi9tSqDH@=B87@utSU^rou(yw1u3I`O#e|=$Y`uXX; z0rkE6?_ZDxJaX3LKHxb5eAoi6&%cg^VS5{(4VX~rZN4SlMQ+3CLe9hyOka{YUX~GlfovNHRPIkd( zXg|%x$^;i81MP<>!pGli0*9ZYK^lvlPZ3@kTyP6TD@Tb4?~jh5-PZ(A2!;H@2Scli zkJ=U=hbY5sOR4WzNCG>-jNWWQ{s%H>}p21K=pfFYk<>>%N z4&Au}17Z$#Y8op!k4tDMG<>Y)#^J2=x8}HAeqK9-eiYOA`Pv>7Bb~_&b(Zlf#UwPQ z#DmC2gT`|fw59S|4OOWa;)UM9<*C1aXogIh3Vo0)VN=W;P6#r`fDIYu7I1fjQ)}!b z)i*p_V-E!pxYPMPS9xIqXXgAuJ5(jQ3I{lBpKKEfHPX{@^ARBW{eB$$ew_ZhyAS}t zg~1);j^N~~?d-&D9zYwsVRpZoIc-Y$zvO^h$k^We1GFm{q<$ESqkRD3ny`Z@ee_W=({O7#tCJyTt)-(6)$Kw+&^(8>kzyQsg&bfl!~rZrBHW7Tm26gXGbW zh6cl}5BByL9WK^EO0FFmiP5H_n;StTjs>%znB((QV!vX3LpkP70fG1#A`khG_!%Ow zLxWUgd;4vhq$7j$)duBAHVhkQhOh}30&#`P%?}|orHQRW@mjMVgCxX9vgs_bH(x^E zZEK4&%9KYYt0$|y*+#<;GTxMIQY+7`=pz(21p-Xj3cE(kh6w?SV`nN2Lf2QY)iCW0;B*&nbappuB$M>#oP!%(Ui=ab`5Waw*83b95 zNle1!L|eef!TyOBU6)qD@JY*I_LBh#2jLi-4X&g_WXV?$~r zgKm2i

S`h5}fqpr_J}(i$p7eO46njUqw;VV+SOKyijq?9(qJHN%2Y$%2c=;;i?? zMVXRSoS!N6iJw*XoffBi&SoNPUeoE7Is1zzqgK7pN&y>PEwp5;4Z46> zx3FJ19ZQIufknfL8+&-D1`B*)zn)vcDEzYLEXXA)Udw{mE&CfM5&0|G^o3+yCEtjx|0>~tbQx=Ve2j|1Tsrj&ty)Mcf>281)H0}<0}5@hb%>5 z95zEl^v8#9K09rdjTS^aO}P0xjIANgjR>92GYFin+lcGBd;{d7^~=z^rR?TqTy+Kc z4CZIS0$dPqv7aQ=8OFUgetK-2kD-z^eizo(qlzOfUezxfVOfNBIOx%X@GrZe4$7Ez z`p-Cw|Hw)2(b6{&l>2!kOZ3ZosVmaq;{7r?*2~60mkSr7^hP?}S$~r55#|72E5bn+ z!p7YEJvIw01hn@_Bcr~)P+Tb)DJA0&BZvceh_-$?v%W7BvNyeqAxEts=hxb@S-fmP z1ruZIW#>VR44ZM-96$l=S@kO{5Z+Jirh+xbMhkrqy{tWl<`7B2xr^vx zWrKOeWy}7LOh&-sU`PK3zbO%or_^!1C6W%>rh8Cktz8rcK~YmoH|LCwX4 zYts>TV_B3=2&M|q;-)vwbw2Cz0>e;j!Ik<$+N-^S1A?ojtT83Nx&2`}U+ z_fc9*@%8MZ)Y9J`um3Z+Emj;`Z$$}8FDpjv*qoVPaLpqPmVBClU*ZI)S;Y!fq) zDeR-H2Zc?@1)-`;?!-Tj;z4=Mm$A3@*>4WHSliQmk;-Yxx9L!-Ig_bwmzzFD z1y57J?eg_qM@w`SymM=F59u|#z-etyaM&c)a1jW+_O06u0i@wk zVc9USpLI%{)Y?jz%h(wt93y<)NvaGN;`icZ&u*0ChuP%#VRoDyW&xZQ$p0A8Tr$rK z2Ql~#CEeSuo_KV;y!)@)1+&c2(c;vI_GA|p$Lkl^djw+{a6ot`q9ydxiHJ9T;yf}w zV@+W*AJA4oEMSnXYAJmDIQ0q@!@0M`Pso@&jv)y7=#6xEGU>uRSERjAU zPll|XgGu#&QeGG|j598oQiY_A>|#YH7|z9p{TV$#qdmZbmyNgu*`+JO0PKEn3+bR8 z&@X76SW7V}Qfw2uxp;2D%{%$P(3J)IZQPJDGe~2kWR1rG!QZCKf+1WINV^y=T^c~q zNOZ}!GhLK3`r=I2>a#-;tu&%N^>{pP{i`EyFl=o>e>_+fjUq)c@er1dzxbI)2i+O@ zO(3)LF@F5@+&=z%bo!mmEWnZ1PDftBfW7Izu|mJS=~Y(fGXj{bUmIEK%z|Ef({}V^ zowPSKF^nEjR#w@vel3b~MvK)#H5h{0(!LpQi)AU5ZM7epFXW;3wu0luPb^oMji%3q zmN-#^TKAvGQ6atQ!1;;u10g}bUf2i4qAiF0AVL@!HX~C&uapPhhSIH?<>=x^h@4@Z zAvl3?OQ(Nc*o8S;oBPOShn*ot%i!t3tT1k=cz3dz-85+85X%EbdPD@A;n5*aM{9kj z=hnxswbu7VrQX+GQzo|nD{Om>b?r!qO54@?`45`)N$RWfr>3S&fx2Es3`wclXnkjw z?_4DW+2!m0_&)W2%ulhcdXx|-Dj5iqW#EMI ziU=owUr0S6Gm`PiA6ZFjGYK{}{*uwUk8X@;9DrDn*^4*Qg)clxpe&wW=wV$~ZWE`# zz%SfDtI&-<7$aL8dmki_^Ysg7j3I)|R`K7#9lwuq^V-K0mLnx)98A~1&|VS@?jX$_ z&xj+TUq#eL>0bm$RHw|ir=zixghm$>yyg!ycLc=^O1(potn8iTx`=MxSkmyxN@>Zq zJd?NKlk~d94WD?nPLUfvsS<4spVUZM-qVR=a$_F$IGs2ucj?mDgLv~0wo-ooJaa#` zd84EegtoLL5ifdjm&Qi4+@0%PimT5{u>?$pZzsShanVk>7fny+5FFv6r(~1f1fU2w zW1tV$+>_w@q#JTnx(?q(`P)F$r$wb6!*dHrFp!_to%tBY6sr5^ChQ9zmHH|9<@CZ2 zt;59Q140Gbg?#)WDF7K7IZik$yzKn|R^X*!gQ-9pUd3HevMDXj2Ljd(io)nqB|B=kAPwl-1 zPIDu3Oj{lHQB%F+xFze%xM7iK{ zs71?^+<@RZMP@djwO~9;^fo<>uAtxFONdp0`@fO|%La(z)A*H_& zRYA^_Ec*w7E2um(wtmD(u<0O+ux`gz!J)_si1Em(Ww)F`xN}~n|6*|>42ep@09oc8 zu_riYg4JeDhg3s2q2mX1JEeuShtSIMHIaI2LbSd&inFu6Ct5#%l>8{HPaG2r3aM`) za=AGV-cUgrEd@1Q-e}QoJZ$Wfco%RiXpLQEQHQ=UzNAoA7jiUKiTZm^qW@?(EuJ^& z4rToYsn2U@4c^>^ZHAh(Hfyv7IvcQteA*}@N81KQ!fC}uKd-I9ymAQd97**_^PTqN zO&R}zvBTAW+@Sg!T|+h?CM=0{A~vPT;PmK$=`?IsNGa%zT9gG@1^v;2-nvN<$RN^{ z7DO1uV=ARP)O;0==(k`839G-!Z*w8SB}mFcw>u+7!pqs}FTolty3`A5E7;l`VYhG& z0uWkA3dTtg=})vb^KjRZG7T4~Uao5dximC1;1w6F`Rm_;u#^mWCrE_Nxb!@OjFg@p z)ItvpT1_!w`1+MO@x`_Jth}&^%>jjXWSx?pj*FV|4ZJorK2o~Rm<+=kZcfBM+a7#1 zrCpY3F{U>$siGYPt`#oBs(>44*|9KFh42w&%};9TKg&uSfm$6A#Et+KizU z!%)SE!->or)+0HB+8;hOh6j7(>LfJW6}6#~7M2pF#(_E8Xrt&)<2 z?J(sy+c%@wV1Zt82o%`~l#DRmX7C#AJ@d5UNbPqYL#p9OHX(7+efYWp>WQx`$v6?9 zP$eCqWqLphlXOF|FR@QqH=%<(y*Sk?riK%RCd>>upXm6;OdcO{+M7S( zklvWTB`|xeI950VHkWI)za}nkzKfo$x9r=`Q^cq&v1a{T9HU&G9-Kudz27p9|6ZP2^p`8NQwY^U%IZKP8~pi_cEX388fx z9VDdO>}8y1euZhQws3CBW`)% z10RM&Q;ZK>Ntd0%eoQi`%HQgy<;At_+_0azVZrt!E`*bMYg*=3w=}XV*X;_m*%nSW zn_Wd}X3iz^!8+C9d&s=2PR;W9%nfzw3%;E32kY=c&;_@D>-2Te0nN?t@}`v5wf(#A zGqah;GjFEJyt7EXd+}t)ni?cd7v)|;Tl2h{wfG71ygF4}TwzYFYkPljk;#1h zd39cX7CkoS7mnqKdI|=_318dNc0J!~zNcdgG|R56aOcza)m;lyI<}~urB9jnZ&9}{ zU25LGrR|qXFPO~LTiW(7+hsBrZBgg@zh<7ZrR}u;l*#PcqAG=J%+@V!e=Jl@^3@zK z_fW2vdlT@$lU{Bcpbt=#=jBcV9(~Hoy$*0L_HqjW1%T;Kd%14|T=`z^6~N8{q@sBo z1vQrMY#JjqD+-UU0#zHVWB#xE7vNFkh zziTUo<+#(=;2B-vo)ODz-&2n-SLmf6UNO#Yzh2*VVf6xwdCyjL+}bpG&)0A+9eGDd zesRwWxZYh-y6Hva$ykaqU2)wPli)sIgz}t=QGYXa$8HH0AqMSeZ2N033#i{(0JP?ID*%;zMdDrvy=Xi*7 z@U}98@qqE3w>_s6`8-ODlR**C^6f{id6L=Ul>>hk4ciQkF*A^Lkm(B&v6hcGb5EQL$`~)gAiTnL+E{HKtL=4Ah1)j|h}sS~N^Lr5WlrW! zAx&r#&X<$-N(N3yqgYC?9l~K|#h(@fvLK>gdlIF+bBLU;yVci?^wVrpr~2gjv_wMY z4yn@mka_GC_5Av6PR5Xws88#!Mjvr00SPc&_{* zCcs2RfC|71fNug00FD4U0oMRMfGUhx2|#*?n+tdtkOHs)>OkvL;Lxx10LOg|be{)Q z0hR!A0FVC=C*^%8H~?nAM?V0kfL{RK0{jH9AMkC!Hvuhx=K&RfQoxr1Ucf9s8o&h@ z2e=z>1k&pO{2Xu*@E(9Zvit}z07wK=jsrNUC!&xBm<5;*$OG_zF9UXfsVY1-0=5Fa z2KYK)7vQ^q9|8^oIsoSY7Xj}9G{8rI&j9ojz6Xf`J_U@z79E3h&H%bg@OJ~y4@g93 z3Lp(I7my2B4!8~w0d;_FfNuj10FD9910Dv!6u^%`gMKYo-EP1}0LQicI1(Q9z#2I8 z6#zdV4=@KX1CRo+0(x<1`s>@~fBpCO%54J90|5OHvg)f~EhT`#`d*a37q7F%_}|4> zhH7)M^=Z`{XW28zbp>4$sJ@A&_3S^sZa)+%V*|2@lUd$nz`N&W2bl$jrJxiw{F`Q^{nl+le-%R-%N=l8&0@eA(W9srZ#*QlV`<8oVJz82(q4vK~l;q(S)s&TpWjRkR zEEg+FsyTIO`_##2P0P#HFRIv3TlRF>X6}8eEv+uALdCaff!f+WEh$EA%cQcJ&4H2% z3CmDVwqMOiOsExW)>gu#lc-d)R;b}#HLo+)!c8+5Z>+6~SGeDpD@!UWHmv2;q<} zIcQ!7iE~^+LT&BZlB#t$*~~Remhg?{b!&+ij(d%;N=mt(nAeGA6&2he^SauyvafL8 zGq2kK{(*6BwF1Zeh%i~(B2#hshG*5~Z$3VA95E$ssKD3RrsC?g;^yiy&WG+= zqsVRMii<0sEnX|t6jzpP;@ZCPW~IsXsj1jlR5=_KE0;>uoWopA-W~CpZl7yn*LPJTKxo9?xTVn(;h{XEL69@Z16DA7^~5i6(9n>URyR z3#hv$csUDo;#Y+FOU@zuoS!6nxnB)ym;;1&Qq(CYYuW~CMAKb-;c{R8QwzCT^&8^! zaqFb2wPN{(Dp%D8(N(gsq`ac!*@`mtxHz$`S6pLK*KEq4v|OqZ%PY%VzM2}SGuDer zt!w#)%91Me!OfMb<@sIep67E^4X|@_wOY~GuBN@PN6l|^E#Nm}bg{p@vbv(oFT$4A z<5<~ToLgR1Qc?bV8I@rve9eaSH6@ixN-CjT)b>eJOTF9>$AT&y6Pm;~Cw zsaH1@sB5-%ep9|*eOi7@?U&c64YFvTR*LmN0BRbEvqK3cu`Axy||CTQfC MF>QCB|7hy}0w!Zz4*&oF delta 15673 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 From ccb3f9c92225e9af8441defe2003ddf96d2b9cd2 Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Wed, 21 Jul 2004 18:53:06 +0000 Subject: [PATCH 0986/2594] 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 757d757c86..6f7276866d 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -34,15 +34,21 @@ def test_package_data(self): 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) From c8ecef628841d8f77400d5227236538dd227eab1 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Fri, 23 Jul 2004 19:44:29 +0000 Subject: [PATCH 0987/2594] 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 79c95ae2ae..506a4a26ba 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -176,36 +176,38 @@ def get_inidata (self): 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 @@ def create_exe (self, arcname, fullname, bitmap=None): 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: From 255d561a68712ed1f40f673cbb1c428815a5febd Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Fri, 23 Jul 2004 19:47:32 +0000 Subject: [PATCH 0988/2594] 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 2e9c90c7c7..2592a6c559 100644 --- a/__init__.py +++ b/__init__.py @@ -12,4 +12,4 @@ __revision__ = "$Id$" -__version__ = "1.0.4" +__version__ = "2.4.0" From c1e60ed922d5a869202a3aea3f5c9e7587d063f1 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Fri, 23 Jul 2004 19:58:28 +0000 Subject: [PATCH 0989/2594] 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 506a4a26ba..20bd61389a 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -224,15 +224,7 @@ def create_exe (self, arcname, fullname, bitmap=None): 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 @@ def create_exe (self, arcname, fullname, bitmap=None): # 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 From 372525bfe68c909c7d7a8b5f92bdfc31ef361149 Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Wed, 28 Jul 2004 14:55:10 +0000 Subject: [PATCH 0990/2594] 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 daf3e010fd..c234117adc 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -7,6 +7,11 @@ 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 @@ def _mutate_outputs (self, has_any, build_cmd, cmd_option, output_dir): 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: From ab63210e40d4c242c91f03b4ae363e2cdf7a6341 Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Mon, 2 Aug 2004 17:58:51 +0000 Subject: [PATCH 0991/2594] - 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 512bc9b665..6c1134fc40 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 @@ def _grok_option_table (self): 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: 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 0992/2594] 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 a962007601..acd393d593 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -122,6 +122,17 @@ def __init__ (self, verbose=0, dry_run=0, force=0): "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 @@ def __init__ (self, # 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 From 1e8e19184f43761f8645b35e3ceb1bc95edde5b5 Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Tue, 3 Aug 2004 16:37:40 +0000 Subject: [PATCH 0993/2594] 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 b4dd0b9170..53846e937f 100644 --- a/dist.py +++ b/dist.py @@ -141,6 +141,14 @@ def __init__ (self, attrs=None): # 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 @@ def parse_config_files (self, filenames=None): 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 @@ def parse_command_line (self): # 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 @@ def parse_command_line (self): # 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 @@ def parse_command_line (self): # 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 @@ def _parse_command_opts (self, parser, 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 @@ -627,7 +648,11 @@ def _show_help (self, 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 @@ def get_command_list (self): # -- 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 @@ def get_command_class (self, command): 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 0000000000..695f6d8192 --- /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) From 5f46c973a65daae1e9351c98edd0c4d7e531526b Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Tue, 3 Aug 2004 18:53:07 +0000 Subject: [PATCH 0994/2594] 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 024e7c28de..bf26302fcc 100644 --- a/log.py +++ b/log.py @@ -50,7 +50,10 @@ def fatal(self, msg, *args): 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 cef985d79a..475ceee598 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 6f7276866d..78e4c55ed4 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -9,7 +9,9 @@ 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 7ef71cc120..bf25b38222 100644 --- a/tests/test_build_scripts.py +++ b/tests/test_build_scripts.py @@ -9,7 +9,9 @@ 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 2e86dcd8a9..fffa6ef2c3 100644 --- a/tests/test_install_scripts.py +++ b/tests/test_install_scripts.py @@ -9,7 +9,9 @@ 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() From 4e7fa463934326cdeebc21acd417260cb1dd5da2 Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Wed, 4 Aug 2004 02:36:18 +0000 Subject: [PATCH 0995/2594] Whitespace normalization. --- cygwinccompiler.py | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/cygwinccompiler.py b/cygwinccompiler.py index acd393d593..31e8e3492f 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -122,17 +122,17 @@ def __init__ (self, verbose=0, dry_run=0, force=0): "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 @@ def __init__ (self, # 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__ () From bf70c77f2d4b915ed174f2484197be059d589916 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Tue, 17 Aug 2004 10:15:07 +0000 Subject: [PATCH 0996/2594] 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 20bd61389a..33e15561bb 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -283,6 +283,7 @@ def get_installer_filename(self, fullname): else: installer_name = os.path.join(self.dist_dir, "%s.win32.exe" % fullname) + return installer_name # get_installer_filename() def get_exe_bytes (self): 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 0997/2594] 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 78231541ec..e6b3991f15 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 @@ def initialize_options (self): self.compiler = None self.debug = None self.force = 0 + self.executable = None def finalize_options (self): @@ -93,6 +96,8 @@ def finalize_options (self): 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 e0fcc23e13..fb73719f57 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 @@ 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')) + ('force', 'force'), + ('executable', 'executable')) self.scripts = self.distribution.scripts def get_source_files(self): @@ -95,7 +98,7 @@ def copy_scripts (self): 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" % 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 0998/2594] 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 4be9999497..43181ea018 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -361,7 +361,7 @@ def _make_spec_file(self): 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 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 0999/2594] 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 43181ea018..5c8a7570b3 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -332,8 +332,8 @@ def _make_spec_file(self): # 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(), ] 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: [PATCH 1000/2594] 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 bf25b38222..666ca44c1d 100644 --- a/tests/test_build_scripts.py +++ b/tests/test_build_scripts.py @@ -39,11 +39,13 @@ def test_build(self): 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) 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 1001/2594] 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 a3b1ffa4d5..e5b9d7cc11 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 24cbcb53e5..56998c3507 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -205,6 +205,8 @@ def runtime_library_dir_option(self, dir): 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: 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 1002/2594] 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 5c8a7570b3..11fd9f184c 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'] 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 1003/2594] 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 11fd9f184c..559fcb9f73 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 @@ def initialize_options (self): self.use_rpm_opt_flags = 1 self.rpm3_mode = 1 + self.force_arch = None + # initialize_options() @@ -250,6 +255,7 @@ def finalize_package_data (self): self.ensure_string_list('build_requires') self.ensure_string_list('obsoletes') + self.ensure_string('force_arch') # finalize_package_data () @@ -389,9 +395,12 @@ def _make_spec_file(self): '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', From d3c88e64e1b45b0c08f081423c191de75c823b05 Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Sun, 12 Sep 2004 03:49:31 +0000 Subject: [PATCH 1004/2594] 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 559fcb9f73..22eccb9325 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, From 6b853a9c0d4926ef6c628436cdec012241c7912b Mon Sep 17 00:00:00 2001 From: Sean Reifschneider Date: Fri, 17 Sep 2004 08:23:22 +0000 Subject: [PATCH 1005/2594] 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 22eccb9325..18546d2611 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -326,7 +326,7 @@ def run (self): 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) From f9ace2e1ee023402f1c4ba9df06df33a2d5b52ac Mon Sep 17 00:00:00 2001 From: Sean Reifschneider Date: Fri, 17 Sep 2004 08:34:12 +0000 Subject: [PATCH 1006/2594] 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 18546d2611..7f1b440e05 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 @@ def initialize_options (self): 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 @@ def _make_spec_file(self): 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', From de08fcf3db1b9779a9430c600354bed93dadd90b Mon Sep 17 00:00:00 2001 From: Anthony Baxter Date: Wed, 13 Oct 2004 12:35:28 +0000 Subject: [PATCH 1007/2594] 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 3a9a53ee85..870005dca7 100644 --- a/command/__init__.py +++ b/command/__init__.py @@ -24,7 +24,6 @@ '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 729002c725..0000000000 --- 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 175f785214..2aaf010562 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 @@ def initialize_options (self): # '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 @@ def run (self): 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 fef291f656..6867534967 100644 --- a/core.py +++ b/core.py @@ -47,8 +47,7 @@ def gen_usage (script_name): '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 53846e937f..a23a773c57 100644 --- a/dist.py +++ b/dist.py @@ -223,51 +223,6 @@ def __init__ (self, attrs=None): 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 @@ def __init__ (self, attrs=None): 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 @@ def __init__ (self): 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 @@ def write_pkg_info (self, base_dir): 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 @@ def get_classifiers(self): 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 From f790936afe6035720d8afde494b9b17d408032c7 Mon Sep 17 00:00:00 2001 From: Anthony Baxter Date: Wed, 13 Oct 2004 13:22:34 +0000 Subject: [PATCH 1008/2594] oops. how did _that_ happen? --- dist.py | 1 + 1 file changed, 1 insertion(+) diff --git a/dist.py b/dist.py index a23a773c57..1a39022fd8 100644 --- a/dist.py +++ b/dist.py @@ -230,6 +230,7 @@ def __init__ (self, attrs=None): 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) From 51311882da9cc8f976e8c669794f9bbcc1860792 Mon Sep 17 00:00:00 2001 From: Anthony Baxter Date: Wed, 13 Oct 2004 15:54:17 +0000 Subject: [PATCH 1009/2594] 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 0a4e14cc37..8986dc9857 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: From 27585e07f272206ef70fe911c5c0f05c78fa9336 Mon Sep 17 00:00:00 2001 From: Anthony Baxter Date: Thu, 14 Oct 2004 10:02:08 +0000 Subject: [PATCH 1010/2594] 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 04cd742cef..07614c6a0d 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 @@ def initialize_options (self): 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 @@ def finalize_options (self): 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 @@ def build_extension(self, ext): # 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 @@ def build_extension(self, ext): 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 @@ def swig_sources (self, sources): # 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 @@ def swig_sources (self, sources): 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 6867534967..8c82801051 100644 --- a/core.py +++ b/core.py @@ -54,7 +54,7 @@ def gen_usage (script_name): '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 e69f3e93e0..440d128cdc 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 @@ def __init__ (self, name, sources, 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 @@ def __init__ (self, name, sources, 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 From dea703ad7656eb0d194d854734483045e0a0be56 Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Wed, 27 Oct 2004 21:54:33 +0000 Subject: [PATCH 1011/2594] 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 33e15561bb..f4bab62ae3 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -116,20 +116,21 @@ def run (self): 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. From d0a2baffd16b3af0caca15a0b5ec9c3449a6abcd Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Wed, 10 Nov 2004 09:01:41 +0000 Subject: [PATCH 1012/2594] 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 168881ad2d..ab92801c2b 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -237,9 +237,14 @@ def __init__ (self, verbose=0, dry_run=0, force=0): '/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'] 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: [PATCH 1013/2594] 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 2592a6c559..a1dbb4b5ef 100644 --- a/__init__.py +++ b/__init__.py @@ -8,7 +8,7 @@ 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 55babe6fc6..6aa5e635d7 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 f995e36711..ca524a5b88 100644 --- a/bcppcompiler.py +++ b/bcppcompiler.py @@ -11,7 +11,7 @@ # 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 e5b9d7cc11..6dad757a6a 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 be8e826fdb..3cd5858920 100644 --- a/cmd.py +++ b/cmd.py @@ -4,7 +4,7 @@ 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 870005dca7..0888c2712b 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 4eca9c3426..23c25a55a8 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 3db332d5bd..7f498c8396 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -4,7 +4,7 @@ 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 7f1b440e05..8eaaff3246 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 f4bab62ae3..9b45cf35e8 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 e6b3991f15..9ae0a292a3 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 ef03ed7269..69d8c75166 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -4,7 +4,7 @@ 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 07614c6a0d..4191c76cef 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -4,7 +4,7 @@ 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 3079bfcdef..621bcb4af3 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 fb73719f57..bda4480ca5 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 41b22777bc..1844ffefd3 100644 --- a/command/clean.py +++ b/command/clean.py @@ -4,7 +4,7 @@ # 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 f18c79ff43..520c1b0c76 100644 --- a/command/config.py +++ b/command/config.py @@ -9,7 +9,7 @@ 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 2aaf010562..fdbec35872 100644 --- a/command/install.py +++ b/command/install.py @@ -4,7 +4,7 @@ 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 2fa0da29fe..1069830fb3 100644 --- a/command/install_data.py +++ b/command/install_data.py @@ -5,7 +5,7 @@ # 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 3a37d309f9..2bd1b04367 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 c234117adc..22d0ab37a7 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 abe10457b6..fe93ef5af2 100644 --- a/command/install_scripts.py +++ b/command/install_scripts.py @@ -5,7 +5,7 @@ # 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 7021496549..fe6c913913 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 8c82801051..eba94559d9 100644 --- a/core.py +++ b/core.py @@ -6,7 +6,7 @@ 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 31e8e3492f..4fd23e6dc6 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -45,7 +45,7 @@ # * 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 2a87eb5d28..b67139c7d4 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 0746633d23..c139c852e4 100644 --- a/dep_util.py +++ b/dep_util.py @@ -4,7 +4,7 @@ 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 77f64c4104..7f1450373b 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 1a39022fd8..9eb2aa424c 100644 --- a/dist.py +++ b/dist.py @@ -4,7 +4,7 @@ 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 94e83fb557..e72221bdba 100644 --- a/errors.py +++ b/errors.py @@ -8,7 +8,7 @@ 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 6c1134fc40..218ed73f98 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -8,7 +8,7 @@ * 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 e2b1c51558..37b152ed8a 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 566d87ab28..43f9aaaf5b 100644 --- a/filelist.py +++ b/filelist.py @@ -4,7 +4,7 @@ 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 bf26302fcc..cf3ee136e0 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 ab92801c2b..dd9d8928ad 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -8,7 +8,7 @@ # 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 d546de1f25..0de123d9e9 100644 --- a/mwerkscompiler.py +++ b/mwerkscompiler.py @@ -4,7 +4,7 @@ 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 a89b88cebf..e5654ff009 100644 --- a/spawn.py +++ b/spawn.py @@ -6,7 +6,7 @@ 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$" From 0d815d149d55b3ec5930550e0facf64838f03cb7 Mon Sep 17 00:00:00 2001 From: Fredrik Lundh Date: Wed, 24 Nov 2004 22:31:11 +0000 Subject: [PATCH 1014/2594] 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 dd9d8928ad..ccb62a8813 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -124,10 +124,15 @@ def load_macros(self, version): 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: From 69251931fb71629f37d7daa3e3765ee802513e74 Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Sun, 28 Nov 2004 01:10:01 +0000 Subject: [PATCH 1015/2594] Whitespace normalization. --- msvccompiler.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/msvccompiler.py b/msvccompiler.py index ccb62a8813..89a75b3486 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -124,12 +124,12 @@ def load_macros(self, version): 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.") From 0713ced9996da5971124d3912d21af46ec2c21ad Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Wed, 1 Dec 2004 19:43:34 +0000 Subject: [PATCH 1016/2594] 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 ed288d6a260ab8e0b5e8a9b79ab781433399e94c..4683659e41dbce722c3ff1160de9954a03c331fa 100644 GIT binary patch delta 3904 zcmY*adr(tX8b2pS!HR-F1SJY0D39M z?{U7z@0{B^v7mQi!O_il=2%(PUVBHwSJ@G32j)goRMtQS>|c$_8PLG|aZIW_Q+c|q zmo)GvnCoKl2U20qMBM{vFn=7AIj|1qr|#P@kOT8}TPsg(j;)PPXAKC|VI?}X<~1D( z{U%T(UV;}Pv=@M*LGU}W9ACq|{#!U~*_%< zOK!uT`z`BYab8{}#C}#9XV6Zwuv9iSiYov#n~h>D6+TCWs7Z{uBS}W_Q*tWher)mo znDVs(w~#M3tn6_}g>Fz8Hk^?Pm0vT6zjO{VmsAlR24agEHq5fa{ikVtL51Zp_>@#? zH-q{Eg0dhmsR>F$f!oTV)V}bLg}4K@)Jf2RbanS97n_Er`lcd`&87m$WRg^*HZ2zO zWN%thY`P>#L(_euDWyO8V))JPK=De+6KOTeB4BA0Pn97wrc4PZlWBDjdr|uOIQOh1 zfsexv(@;|;m+&(XySRh+(pl^vAE&ohM7+x&lXR884K7SWEQAKKFNNI6T92ux^;jTVvJ-JJY0uuVW%>fR6Bi&#guz|YV}{!|0U6viLsOojDWktb z?Bc0S^yf>rPCDlndYiQcs|6j9D zEgx*`3ov{g2=7H^UoctC7m?I_5B>u=p8pMgg!C0O;LpjI1*wZRzYoD`$u9+ISVJ~w z(;!M#tBQ*PxLNv2Dh&UcK`&}Y>665x_eqy_rFt5?H%r%oGCRvB0fUUZ2q=GpyrGT5 zm&qsEoA`V3T47yL7dT=EeKUi*W+l~Sh_($18rRIb>A5WYNre-XfiiXF%RK%aG)+(P zH+I)tqQ;VH6wHSEpZgi`(I6tHT}Ug>pQQ-Aw$jz%{*%z}_s>wk1ZUmd^UahE$@LD8i($~_C zTXsJvKTc=kqYx6+)N}y=FBwQnFZybS`$gK5ar%;o(dof}e92=A&yFyN zzR|ZX>zlCT?v1k`s;ga770mE$e^L*o05FD`Nm+WgP)Js$pnDs2%uvazmpbrAzuU&WROZ~nM+}pVY6Lq zCaZLgl1o98<6r_`qQ-&%%d*fN)@WCVSFT7>->4dT0HQiTP2l_x)F0Rk-@t$_W94Zu z2O5JlD|U|4bP57{C3iQA78;(RLdw(g8%uJnq}BcCfgj) z(3bYU;BJ2uKN0q(`*8Mh91C9@_yy#DRQ)Y^=Gk=%vv>yS{2x3!sW2K*m8;2VbJ_%( z#bJAdLue@=RUXbFa5YY^U~#qrmIYYNJRO&VE6C5bIvl75u^yM3^9Y?rp5p`^LJA^1 zpWgGZ!pI4A7O!CNcw8RYgX~jp^Kh+#%hL&(OYs&?sJ1ycogjE@`+NfD1@tPex44^a z+<}HRDno@R7!BQk#BFo7gH{5na=5(QR*TaLIu3doRcN$zaA*(G_qe=z$bfDeU3Uem z=z;Ts(#~_OLnDeNXUDIO4qxE#3LXdNgagZ|E#%s&vsyiz*Nf1HxXxv<*4g%XEOI(G zalOwW*eW~WT(iq$vvO6u#e*(`TC2rT?cq4|I;wVgIxIpxIIy&H;Px#<1tBMBJw2q} zW%W5inSq80={W}{a5M`Y(vqGR<9bSh!Pxdgju2BDh>TD|xiT8XcFyaAym=_N6`=#P zHmt|y^4NsVDu;!J{u6?8s(c;~=ah4z`P`X@&Ds8j+6Fgs9*`bY?UY+divflZV#>X+ zX_WU{I%sMeAdn8%er|rtqp2pu4Os6%10m8;bU zp~pflwq z%jv7o&$Om6e}e0w5W#a1+DUx`H{0PdAO+or+#vKM(%X8xZi~>$-}Q+eS2TJoPOk&{ z9Rd-$fmx1w=$_7c8aXu83-|4W%+26NhmI+l+*UB13&5^0@~#7OJ9Fq31osB0_xBi+ zh*N5tF+{zFeRdir`TRj9EDIj z#SBhgM@Z+9U+aLz07W3Mdp3J05`0lYR{JQT=kBAI3?Ln{`ZvAwm;$r@w_nbVib=I* ar&@Q)Z=u7%S_B>;|M%zaTl~Vstp5XW)y}g3 delta 3847 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 TKJ?Amg$rWfIt|Hh;hg^gbD-Z? diff --git a/command/wininst-7.1.exe b/command/wininst-7.1.exe index a800678ea7dca567eb518e427a9b267deed5b9c4..b8028f448bdb7598c6f88aead5c6074c3a6a7e19 100644 GIT binary patch delta 14899 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 delta 14881 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 V#=;>aX&n@A`0(a?&wW1e{{X&s-p&93 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: [PATCH 1017/2594] 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 22d0ab37a7..08ff543449 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -9,8 +9,10 @@ # 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): From abc818a08898ada7a31b2466a7bf56848798fe86 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Wed, 22 Dec 2004 17:24:36 +0000 Subject: [PATCH 1018/2594] 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 4683659e41dbce722c3ff1160de9954a03c331fa..6ee282353046c2c28240398af0611d81a1fd5dd6 100644 GIT binary patch delta 14670 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 delta 14779 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 G$NvH-Cc66o diff --git a/command/wininst-7.1.exe b/command/wininst-7.1.exe index b8028f448bdb7598c6f88aead5c6074c3a6a7e19..6ebef04653050ef00e6506cb16c9446f69966bef 100644 GIT binary patch delta 2154 zcma)7eQZlnAij-H^HV5z8)C+;X+#0QTl4FUE0`U zZ4+YTq^kE8snUglP(OkMQc;11KB&T$mH?g&Q>JcXIPOTZ(t-5YLdzz|qKDVfRwpJIi(X(iFb0<)-XbT}X^gRe z7<2Qb%oJ}UC}T)=b@feonKCUeirg1me&Fj7x9oB>p6?9t=DhBG2q9^c=B{8BLP;Gj zz!@jQt7lgt*cjqVdvy&#O=%A(qas7FpJidDz=@6mz;0zGBD zhcD8TPt3uGY5ECQN#}Anh+mXl+aLhAsiY^J1HmUfqpOwbIabUyH7j0zImZ7~_RZak z@eXCtyrWou#D$QUy`%T(bz2kOO6OJV!*P11qP*tNGGLvXw+ZSV7)9g8NQZ9L)=&Y+ z%+k@YMw~D#1OjZQ4rHZ*xGWlDsS~w9ZdFNHmh{8GQTY~>(GrbHLXh*y3H9oAny#(t`aje7Frz6+ zD{5c1p5;kVLJ%?7bw8*_QBJcIC}_0?*-p4ic@*j3dDI#z3L;5@a6sP%w>Q)Sw+BD|2V zfYMoJ-Wmwa$dst4>Om3+i0TjF>r3d&NUgoWn5h0|oVK`%@lWXUt{U!-WAuQlxZ><= zuy2o?*WR1JiwH@usK@^VtvYV(84Bl0!fxnaVqSmZ|W^T0U+Zpn{yW*o}OI6 zTLQ|O<^>#Q*{r8b<9ex$Gsa6fKcTSnO(t_Wk2Hwx{Z3QV2wXb@3aHF1Ms z*My~1jRz1Fbr=&h4ndwI{-73VHqZ%C;x<%7jWKUJap;_ln_SOrRkX>ud*|HqbI*6b zbJzBDO54{dN3v+jimcaQCY?ni;Gq4|r;3&#O-Z9uAtz&=^hOEPW#rNz_%iaP6E28k z%%_{-t&C5m9bH98Bs6e^b6M|P1^Z3ev;~ftD(EfngK4ewa|0~T+-quVAcSu-bhyU< z@nvv1Gn*E{G;>x?8t%3opU{s7pKqG9v4jWUbbG(2%yG9y0_cM?x%0~u;NI_o2G&V#4;;2=p94R`AX&bi|0a1C>FY%3#)L}BZW&cAE? zCpGU2whj|TNp0`MX!kp-F`--UT}DWc!6KuACkbPk-Quv$1GxGmACVCn-p>Q$-25Hp zPxKQQ?{JOq{;+E#Qd>%8Zf+4F@nEbl#Qh81UcGe{X2qC@!Nvn4KH;(lUC@%!(`_h4 zMA}gG>IK-2mzy6#1>5{_ipoTj>1b7M6S$Zk7l~cN%KCMuwFNoPP`Xz;l{-jtMlT@_ zvFuP3Pv@`a4;Z{yqAOWKe+16vUqMQoxqdcX55HXRq&6s>J)M?mb+hxSG*+YCW4Vda zF75R>dnwJ+ZZ7De#=D$^c$2s{0xztg*FsIvlXN?rFUl=nw+OjZYbliVmG~8mWwx~# zuDjizj3aukUhpBE0k#{ys&BAkY^!mlC$&8+ z5wf$@m_`XnZ7)bF#DGd@A1dXe4r{ER;npP7+MFcDgx??N?e>P9BiJY0Q9QG(KN1>3 z(y2`Y;sm`mLHLB>NUJa$zG@H|Wc@ifRO~R%5=`5zyg$ajtP3rze&fiUu;> zZirT7JHaI$e-eguGvrb-Uu9nwT%IPSBz# zUj0fTA#1t~S@=8-;^U#)euC$mGw8GMma|-%dl4=ijimBjF?t4@+ry~%7rzIgglHqhN7d$7=Jg)zB-mdUaiw! zXf&qMt#Ei_F3p6q8=t4|YWJ!es1)6#^=~Sg2vMo&=+@)abKEK;8&Sj$|5x!p@tbbC gs@LGROZ(>V$@KIB1Q!P|@oT=HktXffi510v0gQ=V1^@s6 From 9c20e5e7816e7e2f1a132fe8a7458745355141b1 Mon Sep 17 00:00:00 2001 From: Jack Jansen Date: Sun, 26 Dec 2004 23:07:48 +0000 Subject: [PATCH 1019/2594] 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 8986dc9857..d4eb368e9f 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)) From 4cca0bf734f15ffd714bcb39f781c6e3d768312f Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Thu, 20 Jan 2005 19:14:17 +0000 Subject: [PATCH 1020/2594] 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 fdbec35872..7723761117 100644 --- a/command/install.py +++ b/command/install.py @@ -352,8 +352,13 @@ def dump_dirs (self, msg): 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) From 3145cad407f875f453bb252f7aea93597c6e6777 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Thu, 3 Feb 2005 20:48:26 +0000 Subject: [PATCH 1021/2594] 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 6ee282353046c2c28240398af0611d81a1fd5dd6..bd715250e780b37e94996e33820070987bcc77fd 100644 GIT binary patch delta 15247 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 bP-%azR!KZtF@ko|0KpnNw)M^npUwE+0zYU( delta 14785 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 HuO|N=)K@Bl diff --git a/command/wininst-7.1.exe b/command/wininst-7.1.exe index 6ebef04653050ef00e6506cb16c9446f69966bef..ee35713940177c0eb8e3cf0435d7a6a9287a27fc 100644 GIT binary patch delta 15856 zcmeHueRvbsmG?*%Ac%t zkt4Y;%0yk#A^XxS-9n%#X=xs(Cd3IYnAq6RWxJ##;3hmNPs*DeDrf69Ug~t6%=W%$E`4!xHn?PB}$qh&G@fRDi0dGw>!Si zaR)j4UE{dY$;xZS9^@>)%!Tvp7(Y7Vb=IY8R86H1S?u z*h^?<(iju3b}ZyLe}0!=oPy4YyikwMfJwU&?o4*x#28*RJi>tmesO_5U~{u_U~-!A z*jnYS$rm&K;NiGfQ_mNg#;e!xjA@~^F;`GuCr;5jgl3@R)i#fEVv2K841;*t-KPcn zwJgn+`Ay|dQ;LoEd6Y*}Ubuh%8jjQIOi}mX5Y7-I0_ixNIsQG_>USqG>0A>_8fRXe1q@Z1}9w+1%YDk_$r!W9wc+GYZ z^}{qO<{tcW6pH=?lAukvM)c=ozE5~eS6rOmAJ2DcryQ@LAvmZ#pxLs%iB>wRd%|7j zM;gBn(N+H&Ex2J&xWFdw>lvC%=i6Sk;e3Z|`rM9T0gjI~4bV9TfSxD7YbijglP^b_jRIcnB9)1c1vU#xWabY`p;O> z;1$lH)NGrF=cZuNjiXRW9C)l`V2#j&0kWCbCh53)!NnVU^nNXPje1t|=rZ%d1$4!0 zFi#6!MZ0oa>UilWwUy=td&N}N(aR=e>Jy%$&iuS!pEy$sUT6J%tiMq2ucrQ)dBJ}1 zZr0h)Ivwbo#tTL09DNwzXZ(>^)4QK*nqa~%`GBSc7ph*bW;=&7mfZuf7pOB}Si{hy z0?i^`3#vleEtr(Esdq>M_~C4iJ1~ov&Dv@+*4H028}*HnR6mXog1UM&i7K%+hB<<8 zEALZC)X$Tslb{3#G}|B~1>%#?Bz^!dR7PuH>hEI z$pWEBU5R4X_S1Jt=# zCjQ-9@YLLj$548{!y7Pbwg@n3q9a-E(`?yzj*dN)3GMPnmzn(09D3X^!d+*(H_vVU z+9~3zn8U}4&HQRO2S_(J3B0hiW0T$?_Cxk<&*@}ih-T|l>Sm|kxkqo&2L-G`8>1_S zBQCP@cD5@;b7x6Bn0@0o_Tlly-0ePyB2cVFqudiPMAt)aOHm0-_^$ttpE=J97l3B9 z{ueL^ZAOl2AfS+hyTC=v_h?rF26(6x&9(^jQ=zjUR=v0!jN`olOTglhXXTAmKZc4Fahk01Yq$~HLLA3y)Lm4i&Sa#oI0BbXg- zNM31)?8AN<8@u$(wTk;#@+i!n^X;B*%OEeaP3UOk7g z-6n?%k_smFL7#i{qb)?YoKu8f2#Vgt3o0CdPrEE|-B6j&6P1IQ%X9-hWzO~uaTt)4 z#vSHd(wgZ3BV=1in>Su!3u?9%K=+o`QJ87iP|c$DMhh$98ho)T|ZUFow#eh;eT;|@F8 z?CjYH-HLCQ>$d9+axAR^$sf+T*@ZgGc>_IJmD(%#wO@O7jdKkO?*u2F|>oX2dEn) z5>w?_Bu;@O990d>hgZ9Vi6mM9{1f&hC<_m0(LzT`aAm-toJyaVLj&YlM9FCie|*C4 zz#Gi<34KEkc9;(J1w?^9!H%#)efE*>+%r8_$BZ*g6}~&P>pc|poTTx*CpwYNRV~6i zzu%KUYb&Z%R^MZj$SA^?f)cCvb)F%KCXfhwNqHO)W+9>rO`2xg$BddaJYWpY(Q;^n zmcxdK7{aI`rV@{=KxReUjtOxq^OHK!&C7_6#N4m5$*KeXSRRNKI_w@^-HQQuOy|`Q zp2D3ep?cG_b>bwnr6xkyavjH0CbXsT>Kdxjqn}SOi?;Xhy_pipAQ&Aq1q^XxAjy|F z5Z%D&hf=m-J`(ImAaGg_j>hI4JC8B70eHSpmHjHtQ|n&Q#bIozhqn^>|>UiO>a% z(%=Rm8FcC%Q10{Rf73cJ;dbCJ z+rXM=XuweV`1y`jlNBaGDFoROM>3v=O~QxR3RwY8+&-VogIEk(@D?5r-5z&9h9Hh?5uXny1s$Emaqnr zDW>Qi)vy!>v2_k6uk(u&nW}iykC!mDSFfWC9i&dKM4j>(WJDc5j?@dcFH0m73+O5- z>H4fL!4O!5)I%mr!M#-Hh5t0US#d@vmlJJ5DH!1^K}eWGgVa6f!YzsDtI)3{zTKD;0TIu6YLnO9_OUdIQ}nigbNBgf$$_*JnY{Ihpl0Ts1?GRAdbBH{vIF|^MrSShV~#N@L~NC zghn8Q!Zh6!C*h8yE@tRp!0jCOyy$&toj{AEqJX z%fr3N9z@x6<)o-c2)&61kWh_M^2t4T+(=E7W4J7=&>s;Z2#fT`K|E&akNw(Z=gmA8 zy#`IVsXUhdp|~_JXciyJ3l4~RdBH(3n=VfG<#&l0&hvSQvp0eY%L`|sX060b0UljN z)D&zDvLN?KTFHRi6;BGEgO)?H8+&=k28)!T)6Sc|D7fi74|HQ?muYeQrsa(@xGKm6 zWNLZuNz!Acik<`;uXuk(3hpf#ZUBZ9nURc0CGtF0h60f{r$9t`=t0}~{n#A%2=Bb^ zK}Y`Hp&HC#p(B$=mf+>zli;MnhQRHOVAdWOEfM4dPChtOYV4&5f($TORDrx=tZ6^p z4+jWyw|kK6y>>-!#Jc-&esT_F2*`~g9KQ?+6?FZGxsN40-Z@4V@-tR;+iN;XcXo91 zEHKe*X_yazFiAL;%oyuVxUVS>W4WMNZ{t-Hs)R+yfItUH2HwKZ-|aT?vwKc%s0l1XQ>X2IDtYm}|f>P;4Uig*(%se_vxLz3RsW zuwOCLL!drAK!XlqP*PV($dQZdj91OUeHbhw4aDB#w+ZIA;DpsVXeNzB*n5Dm(uPTW z{eBi(ul|eb_}8?t9{YlhLL22iXM28+n2#%?_&}a}K)gTCJt$`8_lfDkMhW`HRL!T_ ziSJN2g!(ud$^sf;J)v;jBMQgtL&_}#rJdC6gNaIJ9(whq$tGOYAIVZ&7$!())gNIV z1nH>$+()J&>h8_|&=PqTguqHzB8}`}K&&TA@f2B#TFS$0fqqvB2QX2)BoFxWzYrf5 z_7WOwMA$ku8_vG~4IyzHs%jhjCoso~`+ci@0h7;n%JfsL! zm=hit6HE(OeZq4Xd`rdx5#RTL$|vj~G`Dn}q6k7RxCrU;P@*8#be)2>&26sfFlDeU ztV)WDl}Hf|J9?C&40~lZP$haPCPQy0dgWP^2hrPk7#twSZJkWFkWN%3TEd-0u--z- zsHTLOYPt?(*DaM-Ft;hc$hcnuIJ?f=*W=hS%f&a|~%>pS=3VJhEx2E(EW%aXw%OaFWnQxG3wz>9%oZU})<{*!0mf zB_`^$u$c*hB7`Kdf23dtJ@_q%48stkYjk|XXsoT}41$3Y=_-ppG+R45!ksR5YbMh+ zf`|@JC>)K9S2OZxH+I*lY-%61B@F)W??xj3z>dKCXSPD_46$@Vh+FAit^Pb0>mr}x zwM1Tl+zlmEBFps(2fu1*I!+Sl)bd#1Wr^H_>ZxluB)N+syBVm43aHLWmdHfZko=)2 zTHsFMhr&5*INH$H?e6dY87?^u1KOonQ!fcGyFaQAfQUqciUGVjeYnBPtI4Ra^c(1h z^306IXzFU_goKMUUbB%X!%j#(_!#EQCXyGEbK0o=jO#RM#cCfPbth|=^>8qS=Y@7m zzC}uh9gmM}C`KZ54bsTwFOL+A5+ZNRPQgY&Kq`u;Tf)l>eP1v;QFotu^pEh7EWE=- zs1IjDy#XWdRd+%YK`~t&Q~AWYE+I5}JIRSSFg}&_3mehNJSN+&-_Kfaaio&I>tRoN zd3ZykJ?UkE{3W};k@O;qhn%C89l?K4)*9O(pNGU8Tu?f~(zGb?nYpcg~vuu$-N zD1_+cl3)dGf-31}O)(#GMZ^=;26=d#tZ#e=yCykr!tmR8uP}T;f^g{9vAi6LVuL9v z50^2596=Ve$IFszY%EJh7)rmCX`{TAMA;BFhHg682MtA1j`D4Z@Hsf9H49>?2g$j& zIKiPHIfDLMBV!7O6Y(-R#Do_l*T;M#bC~20B~0r@>nMMCKCc)~a4b$cuXxZB`JB9A z@qi`L&mQ_MkvJame`SgMhBfr**P8q@mdGzzL$4)r9uIObIsf;T$Xj^Kz1+pD|6b$1Q_3&q&*GW;VRD^Nb-Y8` zz^*zZ%zl=j4EKs2uP<<$-`O5(=ycxv^vAJ={%El8$3K>5?SZ)uB>$Lo?TrSXQ>+i) zZPaEec@NKVrO$>iUyi!_DYqPL=XCmlj67i`3WKVw>hNU!81CDeG&sqE9>a3+4daL9!YYXG)`+?hIose3ZD z=@?3QkKh;?SOOJ9T3P)F%fsyGV*1B_QdRk&A6N}GZN4qr$W;Xvlz4b6%V$nE& zLq=LM(MXrjzyyJklO923T~~&YQ$XPnu8~x{iytk~!(M6$WY9fAr#?lH*e?DB)bWQo z#8&F36zL<$Mptdkb`=C2BE}%(ZEAm990%bmr8de`Be0@6W%r%!jh)0aQa{<9>}_iI ziQD7`s~j{xYmS^3E5n*`dwJ-yb-|U-78zWj&$8+t3w`F?JzENWwpC0HeO4RHcXf@O zxGe5uMc3E~%T}$5-;K8?0W-a$=+UKd(S+a_8n$ZBSP0&-Rq-)?HqfWb@Lr{ggr(8giWy$q2PtZ6*Vn%Y7A9KS^cHvt3C@(9tk6tN>`J0fK7E85 z;4fh#6X=9JXi78?;zaN0U?@-+b06K{MJ{&teasIcO&ARx?RLKl`VjxPkHY6RLjaLF zWVD!&ar7ux?k_MLRBQ|9n6V(eD*2{ja856M0dtlH)_ZVG8t^x1eoG{bmeAeYBY_FX zdN^;guO5;@c0~1f^*$E)R#>haire7FnE_NPVzak-{v>+5NX-MK`p#Sgc1;p2WgDcHPH_ z0~it7oVJ$5dG!tlw_co%%M&;z3_zeD6QdOXr_T7SW@muMe*Jpg*h$C7tSY6#)pYbh z8=P9W!JLE(Kost}p*Pwvh*uL6a9iN;U6oHrdmE>GV-f5vf*c_gHEp{|{S7|aqO%jn zV~7cj@ec%!^+6SBHzW#(Fz{)WPf*XG|70LNkN)}$Oxh(#9j$nzWtBjmYhTH-~z_z`hmysS&EkBQ%mpBPs0)^qXF&#IXAJnnA^+? zB3lF6V$JUeE>EzU5+S@gJ25jjS6>U$LpNsNSLl&2E5NDau2Lj%qYZ6p{6kHnJLOzd zZu}=WF?yBFqgPqzQ~%F@(O|!5*4(`}=vBV{Wk`O99w&zfk}Q$esM$Su(nvplyGt{Z2rZO4g|oOZWx3cKHII(#h?jDS z8D03QXNB>%c+ZoXvDliiI617&jb&S%zl=`9$5qj3jd}d?F;M(JfV9Skc6qZWi<5usSoVd{D4=}w zK7E3ZB_%en*F(YWX|S zrWcn?P9^NAn2@rU2*#$|ofbtb`y(6W2Novn7<+59TwkzFm@ z_)7-ko?R`4YxWzAHM^7#J>N3&yILyOb{LGgyOa#?Mx%XKOU$bnq?2wJH~tY9_ab0y z5o8(rxmaeTL%yA6Hu$>0kQhGU}-ub*VRGj}(p6*{tZpp33{ zWXAJaTN^+zy;((&nB6+i&{9{n*ko+ot(@4DF13CO@$89T$|*0jK7$MCdvXs(S{n<=P;gexWHeyD^Q^N&mM-Fn zQ8L5=+P>w)Rc8v@oZK^t71RF)Cq_tcf(@f4f`^Qvud+dsFJiF-4{o7D?s2~Nm7~xP z;ypQ&9R+RTy|0|d2o=PL5h{o!IstN;ehz2DXO#CV<}9F7)VE#lm@F^)866~+|9oVw zzV7GFMOat4yE2`mXLY5|AS$m^J~CPTXZXAiAYjf9qU*1DkTg(?Pi~OX$e)BKx?nN2 z@WetYkOd`!HPMCS4Bm&^Qdn&fs~xgZg*&_m1J%7Snra46=FQ?dkOfT^nge9Xk_o3u zA)1if3>Rp|j~d5dVJ-UANAPqgm(aO;mc-mWc$1TjTFiYBm*@+Cb1-zyijw;8<;*166^zC=|Z0SgIVh0pj<55Z&#*p89FRcz6+T6ocAOMzF|!z^i~$fOi2O0S^3#F@nlIPRB#bAY!1#{fqFFTR3w)^$Am0bs-ew*%$^ z76O(4c))f*1n_OZtAMuvU4V;#tALLIPXI!|Q49DFJPRm40{40WHv!yHj++2T0n7%Z z0~P`nN%&a?SPR$y2mnMtJ>Xk_y?_@1F9Ci8_$lBmKqufAfXjeufZqac0*qLA0w4u2 z8;}lI2zUsv65s`F1km5nAK(hAf=&GZFV3wH@BrX0Kq?>^Fo;1a;9Wo$-~`}(j6aBS zA7D427ElRT3n&CE1ULXzfC)f<$H9e{0OyDsiNB5GxR&MNiV1h^fWN8+d;_o%zyk^a zSpYl04ERPv%jMX|cSuT=i~Avf{%{l23IZ-}F<_|Pi07t6oi!$YdcQwXtAyO7*Kl0g z!I8EL*iG+;Wt;RJKTefYlD6Fcj*d%VN z-KqGG=P04q)5qo;Jr!FwE2$?Qn{P1k+v>#Pin_Y;Ckd4YU1DMNlaGoOTh`+LGA`yW z8=tDHsjb>7RxVb4ed5k3_X2HoMR{FC;ZiP{h$&pEymn&F#7XQ~pUEjVUeB9eOt_W? zYwNbva&H<|Y%AZaR~9SfCzehq@wnGG7cSUbU9IH4Q98-VEv>C67b^-MT~Z}(DX-y_ ze|Tfgtn-F76;Cd$-d0z!ykaN!5!F`JRBT1Xy<@TRuW!to6sNY;!HU|Q-ty`owxAeV zKgb!IR43MM+5&~2L{GJwgj()pW2L?iZl1Ahd)?MVh5NN}OL=wmwoRO}`ps!Gj~Oe$ zZH`Mys;k>nzO@n_-B4B&6f3rFS7yF>=lmZSD{(qoxfhLSyREeF{e&{=gq+y&T1^f3Ds;Z$9uvnTv>vY3x+ZUBJTo2h7^E-i|%u}_c zrnZ+$J=ovZ#1Mp;R`IeRCTOeN4_UP6sv5JjQUJ2zZ&B3gR zynk)cs+C2niroeC>Xd?~p1O_S398C?QdQ;G!Jo8rK3!^1?DdhBo_f2%_UKl~V0Cpl z+hHX(SFw2kr$o0ud^^9>v$KwtU0nO*c20Szexb7Q8Nnh}iPf|`Iv{VHQn#a68EEKK z+IO#2_DUO-R7teV+l;+~ThPY`?I1&w8jQ7^@^omS((_D>V%)KSOGXc$GsIHqGlBQfYe0zuMI86*+n(UBP@3WP+=af(XdOkxsn zuru^BJx*$ROlw=4tHrdWv5g7V5Vd9$0(w8*dubAtL~r6tZQr3o8g4JOq@_9UyY?A= zSo>Z-eV@s|aT&faUUz4qE`uf6u#Yh&Zpq{gdBdzR@eFXXKZc3ggF;Gd4$TSj2- z8^tXl)W3G@nU*H@&2RZS`z~U2!yC`Bdf9ig*>~?78l3^(Z-TDjyLl~PeE;eA!WJXG z+h1Os=>J^Hi>QC~^=A|Ho%Eg8vTn=c0u9b3es~wh73mCI<4?~mm(z8r`cF5@59vI& z48F#32Z-h>$Cb{IPwQ7ts6k2p!h$(d7K>FoL^LgZM7~{r$GwlPMj4G9pZMZ4^5f(< z=i*J3VkHobv+*kiu+X<{r~Yfxdiqjb4Si zy(GDexm|%-IRTq!Q3EC+Ehc|DJwv~By*z(LY9%^FU1$CO$w$iGSCTV0NRS%pOl^vZ zrY2Rqix>6~+qpzz;FVu3;W%GzmruMEjnjCc9*us3dMVUta$E-uuPi9wpaP$`SR>dJ zllRX^)t`6E$7l4U&v0{Gw5j(CRpk{9WpryiIpGrO8^v3-2H^-;^2(KU^08YT(|@`S z%Okl4)WD#cp;`xOqc%{#!-^VMzVGN%WD?+ONsYe<= z-xw%ze#6-Kc}D$%jh{Pq-q!f}Hqq4hd2Qf+XV=7O1@Wcq(=~BQ!J0MkJN(9I@$gz+ zwTe$VX`aib1SSyKn&yd+t{`q`zNNsq2FCLJimr)V`MHjRLI)X-Xyk*cbq6~6RMBow zt^HWY(8A73PH4V`XgW%y{$2e>9n_b%33V=TpHL(^In{a|q~JJ9$fRo4rVj|&+6`7d zN9H%2`^>jyF!~BU9VBIhbSQAJgY7jgOj#%6~!r%Hg7{)*HIIgRKVKws) z_R}yR@4U_&oI=ZQ8AkvaH5K_Wo` zGj31dV8whd2_OGgVm44WGO73*^3co$rlMLWm)q4al@}TV^1@kmuNVNu*hK72F>|;8 zdZK9x&97lRok!S*uWBtE?aQN6Un{mY8I3VoGPF);1qrWC*LbG|ALP}88fa39}*0a#14+^35CPo%e=XQw`+2BS9GFE{S zF;^^S(9T$b6A@^XTBC5DRI4c)mI(U_^_xg(QP&W2MZ%#QHJc0z(Vy#}T+d)OA?R zI*WpE1f9Wu0K>?O(q;#3%-TdU)AVRlD@;f@OeD&yMPLyyYB*lmk7}sXlBlW|N2ZtJ z_DET@5xmlnAU=h!dQsR&U#SrimEB31w9oKU| zintVWI1GEr2@H$DoWP*?Y))WE4CDj`#2T`buVQVU5C%yb?@2?sU--m4K|#%{mJAD5 zsnEi9Ovx|=_fKpggE>`$-Le7OMJ*ZP)qA}`wPY~VnH=h}tPcDZ{;`ENisb5Vp)D-A zlos0c&`pvf=B%I%$JX?QNe`3MkiMV3Y|_2jJZPrpwIXIZGU;)uz??_tuiQBQbGaXw z!)Ivn#lzzr+gt=J1OsLUh(;*8 zTQR^;)BqiWP&Ukj^eGKNnb>rVj6<^29XI;*rGKS5(|CIsek!QxF8%2d-6a%8x=SIX zW;-UW2sKIrj#zH5n49Ao7Vph*4T?*0Tti}d?tqvkG`<7rAPxz*37Z{qF|nFdcvHQ4 z5#m9pq|jNFEhYA*J6PiJInBhNSqUe>ZTK5oB%-B~^UbX@Dq&X_i zz+pG#xQw1tR1~u^cs^QeTuaW27r5zAne`+jrFyi9gLX0WVK#_~R?`}jFV0B|wrUu^ zML3FKM+mcGm!JgZI&nC%t-7n9hVgi#E(;vKNuv}92fpnjqRR-S7MjD8uv~@seh->0 z6rU~>A1M?U6q=i+0Dn+`=b$ri9~!|XC=6lPzFnx9-*j1|tRdpmkbL^81?R7TXnJJB zTm-dO(2qm(9Y?PbiLFO5S6+!>Gx>ad2hifr5C+CgY5Z}}*NAo|3gQZJ1~>~>K#GZs zY}v)Ysn(+q27#kbTrc!eyK-5JBz=LEQiI74CDMX>r2#D@icCCM-9f=Yq`(l#qGEh* zV#3-Woi_~Ph*da)nsV17yu;oRNG^Yp$FCDmXk^ROj(K5UUvu2V2pfj~lC)GL(O_Xbz`!C`&>Ea@fVbL!mRuX7Yfk7%ZE-Em(BRc7eMLP{FXi4nA z-loewbNJinnh4txjT<2|7JRE4rj>yKqfJ2;>8aLIt!2F<#`C=Tl!15q^~#ms!T->` z11c4Zfb}M}5VWYA%?J1TNjj_rg~L*@1&1FRAPy;4wO#=hM_t@1STGcf33XnvV?S|$+^j^3Q5?!d=kd3Uh?a3A%l-` zqJHWCEY_o(Tgh=g-s3m>&2DMlGBl{~32?hw7@Li{M|pXre17iiukfghj%~qk73G-O zxic0HXM1~^h2xY4aD#+W-lL#}iFdntaR?%?y0ZFq`GtE^f*I%rYj1?zVjC5K3g=YR z)qCPFWacA*p-35QHyu=)G5J{@;Q;VLBdK(pmtsz?_cMfCB6ZWEGWbdyN0+2L(gcrv zFkQXG`z`o|O@T`m$oUdCY&i|>(f~G|YPF+?j}4y+scSS$v5=AnG$p4Hbr#buB169p z2gOCHWLP>Yxds>YEOHI|Ik{xsmx7tZL)+i&bWvB9uR!3}V0zdb{uWfQtHET!Mlm=2 z1Qlqg!RS>c!#WPqexmDYGI_vSkT5q0J5$Kn;QYadS5{$3a9Y`LP{RifVCJ#0nM+xN zPcCJ_ht|dm3ban4RSbJa_)DU~^^)%4QqfXT9$zT`By~=Zu~)5EF$!*I+FI`Fr!W}4 zXOI_Wb9&Bk*)23agCz)<*z%-e*|4!_!`@AV_|f9@{V=k?U^jdgCJDP{bmpdEZOVDf z+q*0(E(aY6B3WHv3jW^7F~@6edIgkH7RA_6*M2_QG)!T2DTc;o&@e?%gpjUbLY*nJ zOiV_1w27MNuu`p>fPr$s1o1}B>D>SFb-Vf}03$Aoy83w!#8v~aLLDbdw>yFcNIQNi zyN-Yr3(%R6v7cVdFVeo{G$3FuOzG-Lcf_Rfv{u{Wu!g&34~ zgzD`E{mzN){it;WOsE?5tkN){{UqVqO^6JX&0m35!}es9L(0Q$L~iEruk{>v_Zd?6 zQM(Nd(%rDPASW)u(QSpnfIbcAx4%Gnb8fe}i9_MELMQ$o(5vr1(LZ6kB0IcCP1mDd zIMH(nx@4MQ47}K5%PQe8`Uk)+{e(XHFS^yEkQyXnl8W-=i4pfjL;A=J$|l` z7rvfoY0RSmAgntYw%dRd8lNQ_VYf4|2R0R0Z69E4ioT&73m3>6>1rG8nmyL_zOlaX z3GD_nU@Zp8*uaDqL7GSV#h(NzYp{_#>U$XU>XY)m`FHZPg`{~j)Z28M^$MRsW1#Q~ z10#h;u>+M?@-RIu3VsPiL{WWV2zxVAvu#!ue}ekBBhzTXm5reH`P>Q4Y+6WLAbJ(P zkWvl;uJ!s2Ua$9*;S6xlNiue9Znu~f8k!JD^;^7x4a7Gj4dBfF8LYj6k=WeOB<1x$ zaqd&(eecvF>}%wh2A?CNyA*9=HM;R&B)TTH2p`C0={9}mb8;|!=EGs2G$LNjqL3Iz zEM2*Tb}$&hggOi1DTG7gP>B>o(=}*VTPP%to|Au-zGSP=2s-dY3&tFm`xy04p@x24 zAT>cEkRAkK9B-I`KoP*`7<4byG99HL8=V%fq~9;^y8G5(5p_nc#hSvrx;PhmAX5_p zU2ha3FYnd$Q8;m znh2OiSrIAg(3AJT)tq*&eVew0myfti>P2`11a-+gQlaR4gV`QzmV54*72NUHctIb8 zYXmI?juGv z;Z4}>oiOV7%-|?N+8BY)$FN%3VSLshi;RyUE++cfV8mm^ivmk7P(VUX0oQa32XT`j z9so6_l9?R|H5oSVrFV3b>#@f;7@vyLvII=)6en@_H$zO$r+YyW~M5dx4h%s)-R0(yC`2~M|IYP5moJt>Mk`J&@ z)CUC5&LKC&f0_uqI}&K^cD;`?l-nFB!v%W>s`cFgv?Qv@<=#j`&O!2T|M&pYq z!?K<7r`dDv&&E7&z+!FLB2xg#92RAckQ++Go@;TTp=HyA3;v&k%NrA}_8%4wV#Jp+ z6!D$lpxET>dQNDJA5B=C@`Ozi0y)_Pk`kz@)S8;q18JD}hOS|QGKyM?`tPGnjWSxaYM3kBH{a(jD#NA^b9hW;#_N5u7w z!5gRppk|4E^(pKg@;$j7$mF?DT|C42{9N>Vh zhnRr!5ail1yCS9sET=i#f`yUhk)>-S!9tu=T9H@I-h;*gJe!zL88UeFJzsn(Di0@W zv?m6d!&$^R(Svq4jG$ytbq(e;4Em(v{)pj)`gx)5DN^_8t9pJdp1Uv>O&G$O!+jY3 z2JwP7%GXqjcn|7wTxR}*jxyvI{*Zy&4f2PBYM@`Wu4l8c`0YYIW-f3veNEw^p^4_E zC3?KFlnr)IplBTBMt1Lslnt1l-;XULWzluRuiqdQW2XJVsG(WSh#;}0^e!IRT9riS zx+Y~OrWth^aW-M8;H%)Ub2~JApje>bo>yK<^bE|^Mn(6=KDz3$wefR(l{k77Kh8=l zK~oYM{1*HH0T`-@{ns~4hXos_e%v^LrCufSXX@q?jjBuOJ4^awS-jj1bND!Fp`j#m z_;o_NhWlQ@cf8EPI9}qM@hyC!B}mf=SiH(-f6|8eq3RlnSB)oKHjYUE`JtT8RW-h1 zJVn}HQTzu{*v3;NkEfXARr)ZRPjwAQLDxX8YtX#A78=sloh-i*R)ti~1d=70_5#A} zMDFtBHL~-ZGW=}=8KO9c&Y^&ULr92WU&Go5n0+M2tqJtL@o0&3Y`gQW$wy(ZOibB*r#*Ohq{e(wI`2u;TtBWKCvi^9o>tauKiBKW9mW3nqw-zi%miSW(oIKh)I@B@mSMX$GFY(~RE-8Pn zqfWW@$;}#)p|_|)B$MG}jVayo_rmf@(KTt9pF z5on=-umPbZLq}TKyXsMU3TvQCk8%uWAr6tgT5w5F;q;;Bq%khIM`&!~zf9zL|mt3a21oLv?nwrkYbDbY#?oe2%5u`FvVD44~-YxR8!!@!Jb9+K0Tw*+ohygM&j z4XUcS>6d66P4pxrO{93W(7W$7>Yhg_4|j=lm`xISbPr26l|=3)v+tfjcR2)XA-;m{b9268b9XyPba;Ou3(4{Hk1f`nAIgQzzXcd~F2)yxLRI`XmdG{lD zkWe1`%OP_xOpiN)m>>~O-{!;!jUDux?)6?o1vcep?~@30O`Iz5AXPrPG>%931+WO^ z4Wbh#<_ei3ylg0p-?A6AWYy%$l$k<|E}H#ZQ1Q@X3^M9;*Gotx=7;hI65I@bhug^y z$GGWO?tX+>8SY&b<-0n`;RJ@e7JjN?^As{0Z9PZlULEcmz6&UwAa} z^FFfmKPBGO#QAmRbd^-NP4D@hQ*hZ6tVJIg)LFmQD&na1u3$D_KD zitkM4e7M%^Ov~A64@~MVu-klXyH_sK>usg-TdoWGy-|5j!I$-oQTarHOD{xa{i^&~ zd!u-eB;S0!>#AtMxmeWgOeu=Cm96?hug}K!p?h@ty`}Q=tLIq?AyxOvqRCK1_sYUT zNj|aqKD|9E53a7(8=`HG6_x7rXC?V$aV~v6FD{v(><0G~bVxAn+}-x*!%qFJt-#04 z(t3N5RBuo2UXjweTW;eY)bHLcf5ETOi@V#_tbI?XFWuc{ckk2box9~z>%XqI?QSb5 z>CowoyXC3gNA&|z+g7iv3trB5a$hcRa>oI8ta5VS01N_33!PjSVDW=a?sb6mAt$#2 zPz+eK+R1$nU@LNR{|wk$?Bwcsc&~L%?iIkF-A=AFmp}E*(d}q6KJuHSB{od3o0|(wNlH9jDzfw4%Tt^*>y%cT zIJw)!O+{OL2K2&1;h5G7QQ=I5Q<-Fh5|^mi-mXP<9# zKf2tYZ~mtI?M-RH=I~Vd%qy{(o@Unc#4$%gJ$rMd|Zf>XV)i;gW*NSp#xo{p3}btli-yE zmw!+O;&7uFV=_n|I^MZShJ*y~Na;)l^mn}D=#YXvC^1P!R6x@=AG_j6W|Nb8$EjlW z-_gXF2o9)WTu11TNp!Osf>I9#OX%PRIiz04g`1W_TZju%I$H{w#D$yIV@wKC#F!MM z5={UpRa=L5LZ{{9o98d4Rn(?kZ=WG8`vol|hX2c0Uv1pKIF?~tdD0VUWIaos@am4p zktbHoXnzep>@p0@aXC_U)r~iQ`I9Hgg9f-BUufD6I5IUft)4!3LnZ$;tsY(v;i}cd zYCBl%poJ=2iwHtxkB;M%MPQjT&v^zu)g<(6A)lH|JQccQ3Z@v`rxCxDIEV!i?aC^Y z4vNIi)w=?JOn_IG8q}h$9$fU!1J1&+r-BFG0V1x>+>QpL->Ccs*6kAv#E&C>bZ14b z*Z!oe+p=`c9=Q7Ju2!8m5r3(4IG*I6Qc5qz2(Xmbd*S*AB7s;=*|oqVc;Pg##vd)< zcf4fT?SZL?&&#fw_nwXvU&XRu?2ISnuWiZ9$!2uRNRZ-T6*(L`hU`!mW)gm(jqrI) zVS$P2!XY|qNQ*-K2l8iI?AEiS?|l%u+jUJrc~=8Ia<%_Xd5zy_?WNZBTI+z;IzX+9 zbt&@q{d4p-zx<|uwV7i>4THD3aInVNc3S`M6N3NtCjMIi;1j^T?HqRpAQ_+s{2sWU z0qEz8H#n{zKtE}~Jp_0Juo+Mb_!?jj;C=x8_>XejPQdek9|GP0bOAmD{0^Z1ImY=p zZU%RukPX-fz!ku60KW_3*APID4kkb{AQf;2 zU_B(f7vBc}g@AQ{a)1D+1=It+0oV(83GfrZF~BLnSpfa4IL2{3fUAIhzt>L z1=w&Azdix{7VrgN28eD4EC$2?Jm4`vEuazbEx;kb8vqN|S`T<0{pd%)MCt(^0Dju; zdn2HuBa7op*13uvTp{0cYPQ_omNvHzICtTyD$!k2_4t;`>Voano2qL0N>NxYf7~{^ z&2scnoo=K2&RY(-`S@YE@YJ#ewZ!I`ih?cMDyz3VTZQtvz_tqamaR3_RRvqBt2lYr zsauzW;=yfOM6e_p>fx%bt>A07Y!h$j;TC~1p6B zZrNI8L*MPS^46cPkq@0Iow^>VI@`MKTPwH8xo;QCy+5xuy0>o@pRTN}l6&4tmK)z( zFXtcIhd(L#;MgiT_07Vz%oC+Ld4H@>?m6j{_nykoLn*iX;&z?f{B~y&#FJOYO61|# z4$y2eZ{IH3R#!c9zilC@hm%968)Ud_@Hu?QG+vVo=LvrUSVH$)E)PP9b zB34()OU~4_?Wh%|+);>ge;I&+{0w|;fMfs%7(me03piibHnHJ13xaI|!Uh2SAm&%g z;7uz5BlUwQznrKuWa8J!BV)BW*k5UoqkkN(5ynsoTus7R5OpeyCEcyJQwB;A|PuJCuN7}rcbL+8_^Q)hDP^{Xz z9{)dZIhPB}woS!Vb#;|bR4wPYyTGZssC@w6}Cc z+bcJ%4AjRt9UzwRyC)SGF!TPX%GgQfOAM3YPR#$I-oa4Ty-~2fB z$APQgtm%VWsjJws{V|SvS-*KZILY5^{nD*J&~M&c9jFsH?x)0q;mcZQEt*KYAKwaR zF`Y`aj|;WjLVd+kb=wlmxTK`Iy2mTGZRX^UTeBv2>$lX^)l@!S)mHqvHw?1n$l+iQoGCff&cyd*;(HX|R=|NoeZ)Hv{cZr! z)A#SuQ@@!25wuhB-GuKPeAnYUn?r<@iGl@)RD4tLHQ+l5-@#fZHw)h@_~K;Y&f}Ym zZw%iffZSPHrVuD@AL{qd9IH2@zG=3TGf*RbcA)-T?pXUK)Q_^dNh2r)!a7^-dc77m XbL~ghw)T_P=zpW$)^<1W- 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 1022/2594] 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 1844ffefd3..02189c531a 100644 --- a/command/clean.py +++ b/command/clean.py @@ -15,7 +15,7 @@ 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 9eb2aa424c..4f4bae5218 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 @@ def _show_help (self, 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: 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 1023/2594] 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 d4eb368e9f..aae0f27cb5 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -34,7 +34,7 @@ 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 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 1024/2594] 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 89a75b3486..016139321c 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -211,6 +211,9 @@ def __init__ (self, verbose=0, dry_run=0, force=0): 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 @@ 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) @@ -381,6 +385,7 @@ def create_static_lib (self, 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 @@ def link (self, 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) From ec12da619b7a0be1135564e31e6a9b41d6134437 Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Sat, 5 Mar 2005 05:28:45 +0000 Subject: [PATCH 1025/2594] 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 016139321c..d2ea008e82 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -293,7 +293,7 @@ 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() + 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) From 891c7ebce5c207c73bfcf79ecd51d58ee8fab395 Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Sat, 5 Mar 2005 05:32:14 +0000 Subject: [PATCH 1026/2594] 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 d2ea008e82..8106df6177 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -385,7 +385,7 @@ def create_static_lib (self, 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 @@ def link (self, 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) From 8992c0187b1cbc5bf26aa8c686421d0b81e55e4c Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Sat, 12 Mar 2005 19:05:58 +0000 Subject: [PATCH 1027/2594] 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 8106df6177..b94d35f15f 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -255,6 +255,7 @@ def initialize(self): ] self.ldflags_static = [ '/nologo'] + self.initialized = True # -- Worker methods ------------------------------------------------ From 5a3290c36b8087fc73356362cccb51d523b16167 Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Sun, 20 Mar 2005 22:17:02 +0000 Subject: [PATCH 1028/2594] 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 0000000000..44cb41ee38 --- /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 0000000000..5126ebf548 --- /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) From 1690ef36ec0968c381d0cd02c92863441416daf0 Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Sun, 20 Mar 2005 22:19:47 +0000 Subject: [PATCH 1029/2594] 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 8104ce0656..6e9a8d4297 100644 --- a/command/register.py +++ b/command/register.py @@ -231,7 +231,13 @@ def build_post_data(self, action): '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 eba94559d9..c9c6f037a7 100644 --- a/core.py +++ b/core.py @@ -47,7 +47,9 @@ def gen_usage (script_name): '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 4f4bae5218..c5dd5cbf78 100644 --- a/dist.py +++ b/dist.py @@ -106,6 +106,12 @@ class Distribution: "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 @@ def __init__ (self, attrs=None): # 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 @@ def __init__ (self, attrs=None): # 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 @@ def handle_display_options (self, option_order): 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 @@ def __init__ (self): 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 @@ def get_classifiers(self): 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 695f6d8192..7675fbfa93 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -4,6 +4,7 @@ import distutils.dist import os import shutil +import StringIO import sys import tempfile import unittest @@ -96,5 +97,93 @@ def test_command_packages_configfile(self): 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 From b9835f36c86e8b46e92de5b8c6fcd88525bcac21 Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Mon, 21 Mar 2005 06:36:32 +0000 Subject: [PATCH 1030/2594] - 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 c5dd5cbf78..f015874bc4 100644 --- a/dist.py +++ b/dist.py @@ -1181,11 +1181,7 @@ 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]) + distutils.versionpredicate.split_provision(v) self.provides = value def get_obsoletes(self): diff --git a/versionpredicate.py b/versionpredicate.py index 5126ebf548..8922b137fd 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_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 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: [PATCH 1031/2594] 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 7f498c8396..59435516fa 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -117,8 +117,9 @@ def run (self): 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 8eaaff3246..09bfa43fd0 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -297,12 +297,14 @@ def run (self): # 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 @@ def run (self): 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 9b45cf35e8..a0335feec7 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -162,6 +162,8 @@ def run (self): 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 fe6c913913..8b88f22f83 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -449,6 +449,7 @@ def make_distribution (self): 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 0000000000..d14f177697 --- /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 f015874bc4..c7ec3830fa 100644 --- a/dist.py +++ b/dist.py @@ -177,6 +177,11 @@ def __init__ (self, attrs=None): # 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. 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: [PATCH 1032/2594] 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 d14f177697..aea2e4b9c4 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 + From a2e09746ac9aee2d9002e43e37fd3b8c71379e9f Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Tue, 22 Mar 2005 05:43:18 +0000 Subject: [PATCH 1033/2594] fix Python style guide conformance --- versionpredicate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/versionpredicate.py b/versionpredicate.py index 8922b137fd..62d89f8b00 100644 --- a/versionpredicate.py +++ b/versionpredicate.py @@ -117,7 +117,7 @@ def __init__(self, versionPredicateStr): raise ValueError("empty parenthesized list in %r" % versionPredicateStr) else: - self.pred=[] + self.pred = [] def __str__(self): if self.pred: 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: [PATCH 1034/2594] 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 aea2e4b9c4..ad0e230462 100644 --- a/command/upload.py +++ b/command/upload.py @@ -4,9 +4,10 @@ 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 @@ def run(self): 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 @@ def upload_file(self, command, filename): 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 @@ def upload_file(self, command, filename): 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 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: [PATCH 1035/2594] 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 ad0e230462..a62da78a02 100644 --- a/command/upload.py +++ b/command/upload.py @@ -89,6 +89,8 @@ def upload_file(self, command, filename): 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: 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: [PATCH 1036/2594] 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 a0335feec7..5a83226b78 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -163,7 +163,7 @@ def run (self): # 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) 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: [PATCH 1037/2594] 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 a62da78a02..6a4e3b3332 100644 --- a/command/upload.py +++ b/command/upload.py @@ -67,7 +67,7 @@ def run(self): 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 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: [PATCH 1038/2594] 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 59435516fa..ccba00955a 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -13,6 +13,7 @@ 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 @@ def run (self): # 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 09bfa43fd0..c7b94e8133 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -15,6 +15,7 @@ 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 @@ def run (self): 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 @@ def run (self): 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 5a83226b78..49afca0472 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -162,7 +162,11 @@ def run (self): 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 8b88f22f83..3dfe6f21a7 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -449,7 +449,7 @@ def make_distribution (self): 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 6a4e3b3332..266e9b1e87 100644 --- a/command/upload.py +++ b/command/upload.py @@ -4,7 +4,6 @@ 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 @@ def finalize_options(self): 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 @@ def upload_file(self, command, filename): '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 @@ def upload_file(self, command, filename): 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 c7ec3830fa..ff49886d97 100644 --- a/dist.py +++ b/dist.py @@ -177,9 +177,15 @@ def __init__ (self, attrs=None): # 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 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: [PATCH 1039/2594] 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 c7b94e8133..7968d14ca7 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -347,7 +347,7 @@ def run (self): 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) From 17df9acf1e48891c158caa9db83cb972377f81b6 Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Thu, 24 Mar 2005 07:00:05 +0000 Subject: [PATCH 1040/2594] 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 7968d14ca7..98621af9fd 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -348,31 +348,30 @@ def run (self): 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 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: [PATCH 1041/2594] Add missing socket import --- command/upload.py | 1 + 1 file changed, 1 insertion(+) diff --git a/command/upload.py b/command/upload.py index 266e9b1e87..d1d5ec601a 100644 --- a/command/upload.py +++ b/command/upload.py @@ -8,6 +8,7 @@ from distutils import log from md5 import md5 import os +import socket import platform import ConfigParser import httplib From 31e173cb4da568d9a71645bfd46d416b6d0d406b Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Mon, 28 Mar 2005 01:05:48 +0000 Subject: [PATCH 1042/2594] 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 98621af9fd..4bc00c3ac4 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -361,12 +361,12 @@ def run (self): 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() From 462bfbfa899143d92476cc8b1e2e86df2b4b7432 Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Mon, 28 Mar 2005 01:08:02 +0000 Subject: [PATCH 1043/2594] 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 d1d5ec601a..7b08336c82 100644 --- a/command/upload.py +++ b/command/upload.py @@ -133,7 +133,7 @@ def upload_file(self, command, pyversion, filename): 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 @@ def upload_file(self, command, pyversion, filename): 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 @@ def upload_file(self, command, pyversion, filename): 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 7675fbfa93..4d2a7cdf1a 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -175,7 +175,7 @@ def test_obsoletes_illegal(self): {"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 44cb41ee38..8a60dbe806 100644 --- a/tests/test_versionpredicate.py +++ b/tests/test_versionpredicate.py @@ -6,4 +6,4 @@ import doctest def test_suite(): - return doctest.DocTestSuite(distutils.versionpredicate) + return doctest.DocTestSuite(distutils.versionpredicate) diff --git a/versionpredicate.py b/versionpredicate.py index 62d89f8b00..ba8b6c021b 100644 --- a/versionpredicate.py +++ b/versionpredicate.py @@ -101,10 +101,10 @@ def __init__(self, versionPredicateStr): 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 @@ def __init__(self, versionPredicateStr): 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 = [] 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: [PATCH 1044/2594] 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 6e9a8d4297..dec9aa2bf2 100644 --- a/command/register.py +++ b/command/register.py @@ -254,7 +254,7 @@ def post_to_server(self, data, auth=None): 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 @@ def post_to_server(self, data, auth=None): # 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) From 7edd1145871db2e4b444114c99e326829edbbc12 Mon Sep 17 00:00:00 2001 From: Anthony Baxter Date: Fri, 15 Apr 2005 06:17:20 +0000 Subject: [PATCH 1045/2594] 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 4bc00c3ac4..738e3f7269 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -298,7 +298,7 @@ def run (self): # 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'] From 1133a2e0542e3f3e82544ec98095d3799a5921cd Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Sun, 24 Apr 2005 22:26:38 +0000 Subject: [PATCH 1046/2594] 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 aae0f27cb5..1bd62097f0 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, 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: [PATCH 1047/2594] 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 1bd62097f0..0be5b6b7cb 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: From 3b756201e5bd88f1dac1206c49864e5a286a3497 Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Wed, 18 May 2005 02:18:09 +0000 Subject: [PATCH 1048/2594] Whitespace normalization. --- sysconfig.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sysconfig.py b/sysconfig.py index 0be5b6b7cb..9bdbb16a8c 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'): From 7c683cbb4a9a15e15bc522c90e018c1fb5707418 Mon Sep 17 00:00:00 2001 From: "Phillip J. Eby" Date: Thu, 7 Jul 2005 15:36:20 +0000 Subject: [PATCH 1049/2594] 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 7b08336c82..3b5a0fc9d9 100644 --- a/command/upload.py +++ b/command/upload.py @@ -71,7 +71,7 @@ def upload_file(self, command, pyversion, filename): 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', 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 1050/2594] 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 b94d35f15f..85d515b20d 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -269,6 +269,8 @@ def object_filenames (self, 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 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 1051/2594] 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 7f1450373b..2248b607cf 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,) From 326aece3c808d1f4a0cec3f37fcca0d5ce4f1223 Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Fri, 26 Aug 2005 15:20:46 +0000 Subject: [PATCH 1052/2594] 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 2248b607cf..43994db3ff 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,) 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 1053/2594] 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 3b5a0fc9d9..62767a348e 100644 --- a/command/upload.py +++ b/command/upload.py @@ -70,17 +70,41 @@ def upload_file(self, command, pyversion, filename): 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': 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: [PATCH 1054/2594] 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 85d515b20d..b65e5286a2 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -214,21 +214,31 @@ def __init__ (self, verbose=0, dry_run=0, force=0): 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: 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: [PATCH 1055/2594] Detect Win64 builds. --- msvccompiler.py | 49 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/msvccompiler.py b/msvccompiler.py index b65e5286a2..aefcf98709 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 @@ def initialize(self): 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 @@ def initialize(self): 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: From a1548e541bd1b4b0e54ff449520e393bbfd00a39 Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Mon, 20 Feb 2006 21:42:18 +0000 Subject: [PATCH 1056/2594] Whitespace normalization. --- msvccompiler.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/msvccompiler.py b/msvccompiler.py index aefcf98709..f88f36526c 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 @@ def __init__ (self, verbose=0, dry_run=0, force=0): 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 @@ def initialize(self): 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: 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: [PATCH 1057/2594] 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 0000000000..6c0982d49f --- /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 From c24cffd76bbddf35362ec7ab850b25bea009dbf5 Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Thu, 9 Mar 2006 01:15:05 +0000 Subject: [PATCH 1058/2594] 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 6c0982d49f..f05d66cb5d 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -216,10 +216,10 @@ def run (self): # 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 @@ def run (self): 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 @@ def add_files(self): 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 @@ def add_ui(self): 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) From 9e5258e007776422cab25de64ea538f0056f438f Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 15 Mar 2006 04:33:54 +0000 Subject: [PATCH 1059/2594] 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 9bdbb16a8c..dc603be8b7 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -16,7 +16,7 @@ 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) From 68fb7a659320c41ca8949058210394de916a400f Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 15 Mar 2006 04:58:47 +0000 Subject: [PATCH 1060/2594] 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 9bdbb16a8c..dc603be8b7 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -16,7 +16,7 @@ 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) From 0a9342f5549e6078a83335bee511bed4dcd43480 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 15 Mar 2006 23:08:13 +0000 Subject: [PATCH 1061/2594] 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 dc603be8b7..0feb14aa2b 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -16,7 +16,7 @@ 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) From 79b3a97173af4724fe2c4b437a12ce760960f718 Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Fri, 17 Mar 2006 06:49:51 +0000 Subject: [PATCH 1062/2594] 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 dec9aa2bf2..f8912621c8 100644 --- a/command/register.py +++ b/command/register.py @@ -13,6 +13,11 @@ 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") From 6811c9d7ad4341ea3769490502cd805ce400eb09 Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Fri, 17 Mar 2006 08:00:19 +0000 Subject: [PATCH 1063/2594] 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 6aa5e635d7..b725a14b41 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 4191c76cef..6ea5d57998 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -613,8 +613,8 @@ def get_ext_filename (self, 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 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 621bcb4af3..3b7ec62c59 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -154,7 +154,7 @@ def get_package_dir (self, package): 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 @@ def get_package_dir (self, package): 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 @@ def get_package_dir (self, package): tail.insert(0, pdir) if tail: - return apply(os.path.join, tail) + return os.path.join(*tail) else: return '' @@ -335,7 +335,7 @@ def get_source_files (self): 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 43994db3ff..a4aff58e3d 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 43f9aaaf5b..4bbdd1f00f 100644 --- a/filelist.py +++ b/filelist.py @@ -69,7 +69,7 @@ def sort (self): 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 387e9bdc93..889bf13603 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): From be411d30ce0fadb76610cae16903da8a1f34f200 Mon Sep 17 00:00:00 2001 From: "Phillip J. Eby" Date: Mon, 27 Mar 2006 21:55:21 +0000 Subject: [PATCH 1064/2594] 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 7723761117..453151d08b 100644 --- a/command/install.py +++ b/command/install.py @@ -601,6 +601,7 @@ def has_data (self): ('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 0000000000..4e472d7c75 --- /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('-','_') From 9e248daf97b68077781fbfde44293dcaa0104172 Mon Sep 17 00:00:00 2001 From: "Phillip J. Eby" Date: Thu, 30 Mar 2006 02:12:14 +0000 Subject: [PATCH 1065/2594] 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 62767a348e..6f4ce81f79 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 @@ def initialize_options(self): 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 @@ def run(self): 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 From c363bcdfa7ebe6448597a5dc7a323ee6887083e8 Mon Sep 17 00:00:00 2001 From: Anthony Baxter Date: Thu, 30 Mar 2006 12:59:11 +0000 Subject: [PATCH 1066/2594] 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 4e472d7c75..c31ac29668 100644 --- a/command/install_egg_info.py +++ b/command/install_egg_info.py @@ -26,7 +26,7 @@ def finalize_options(self): 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 @@ def run(self): f = open(target, 'w') self.distribution.metadata.write_pkg_file(f) f.close() - + def get_outputs(self): return self.outputs From 723767238ee601ade352660d96ceb887a9437417 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 1 Apr 2006 07:46:54 +0000 Subject: [PATCH 1067/2594] 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 cf3ee136e0..95d4c1c5a2 100644 --- a/log.py +++ b/log.py @@ -20,7 +20,12 @@ def __init__(self, threshold=WARN): 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): 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 1068/2594] 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 4191c76cef..fbb74768ba 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -185,7 +185,9 @@ def finalize_options (self): # 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 @@ def get_libraries (self, ext): # 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 0feb14aa2b..eafd49e7af 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. From ea42db5cb8c6b047ed510fb937a4110b102b92ea Mon Sep 17 00:00:00 2001 From: Armin Rigo Date: Mon, 17 Apr 2006 09:22:35 +0000 Subject: [PATCH 1069/2594] 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 eafd49e7af..8d66cc2e7e 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -31,7 +31,7 @@ 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': From 8e8c8486409ae4fe7a424a541307265b07d82753 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Mon, 17 Apr 2006 14:43:30 +0000 Subject: [PATCH 1070/2594] 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 8d66cc2e7e..72aa511700 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) From c4c2a4987d1138c0ae217701f994abf29cdd7148 Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Fri, 21 Apr 2006 10:40:58 +0000 Subject: [PATCH 1071/2594] 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 6ea5d57998..5771252123 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -185,7 +185,9 @@ def finalize_options (self): # 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 @@ def get_libraries (self, ext): # 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 7723761117..453151d08b 100644 --- a/command/install.py +++ b/command/install.py @@ -601,6 +601,7 @@ def has_data (self): ('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 0000000000..c31ac29668 --- /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 62767a348e..6f4ce81f79 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 @@ def initialize_options(self): 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 @@ def run(self): 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 cf3ee136e0..95d4c1c5a2 100644 --- a/log.py +++ b/log.py @@ -20,7 +20,12 @@ def __init__(self, threshold=WARN): 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 dc603be8b7..49536f0d3f 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -31,7 +31,7 @@ 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) From aca28c3c53d3a26bed0130d101ca283bd517f2b9 Mon Sep 17 00:00:00 2001 From: Trent Mick Date: Tue, 25 Apr 2006 00:34:50 +0000 Subject: [PATCH 1072/2594] 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 f88f36526c..d24d0ac6e0 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -618,7 +618,7 @@ def get_msvc_paths(self, path, platform='x86'): "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): From a3c755c1adfd523cfd16d54e90c9ef1bb50f9915 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Fri, 28 Apr 2006 16:58:52 +0000 Subject: [PATCH 1073/2594] 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 387e9bdc93..061092b673 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 -- From 9115c7ba3b080c10b08610e08a1e3bcec03352e7 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Sat, 29 Apr 2006 11:31:35 +0000 Subject: [PATCH 1074/2594] 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 72aa511700..2a18d2beb0 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 From 91bcdf7042985faabfa0efe6fb435a7d55dca67d Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 30 Apr 2006 08:57:35 +0000 Subject: [PATCH 1075/2594] 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 6f4ce81f79..4a9ed398a0 100644 --- a/command/upload.py +++ b/command/upload.py @@ -6,7 +6,7 @@ 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 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: [PATCH 1076/2594] 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 f05d66cb5d..75db8773f1 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.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 @@ def add_ui(self): ("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 @@ def add_ui(self): 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 @@ def add_ui(self): 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") From eef192824d14409f513b9f2acb842aa6ed2b8f5c Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Tue, 23 May 2006 11:47:16 +0000 Subject: [PATCH 1077/2594] 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 fbb74768ba..00f8a6b9ae 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -689,6 +689,11 @@ def get_libraries (self, ext): # 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'): From 0856fd358badca3ac456fd79ba1442dfce7118f6 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Tue, 23 May 2006 12:01:11 +0000 Subject: [PATCH 1078/2594] 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 2a18d2beb0..2ba3c4db5d 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 56998c3507..e612cfc2ec 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -42,6 +42,48 @@ # 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 @@ def preprocess(self, source, 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 @@ def link(self, target_desc, objects, 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 061092b673..623c41e280 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) From 57e5a01e0a790b43902fc19615d1a81ffa888c7a Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Tue, 23 May 2006 21:54:23 +0000 Subject: [PATCH 1079/2594] Whitespace normalization. --- unixccompiler.py | 2 +- util.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/unixccompiler.py b/unixccompiler.py index e612cfc2ec..324819d4a5 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -220,7 +220,7 @@ def link(self, target_desc, objects, # 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 623c41e280..cfcc6a951e 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: From e6ecfaefb58476548380b845c21cffc4a797fcc2 Mon Sep 17 00:00:00 2001 From: Bob Ippolito Date: Fri, 26 May 2006 14:07:23 +0000 Subject: [PATCH 1080/2594] 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 6dad757a6a..1349abeb65 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -15,7 +15,6 @@ 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 @@ def _setup_compile(self, outdir, macros, incdirs, sources, depends, # 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 @@ def _prep_compile(self, sources, output_dir, depends=None): 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: From 9cc8b6b8399a7a3b3ce1c8fc308f3f01a39ef975 Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Sat, 27 May 2006 19:21:47 +0000 Subject: [PATCH 1081/2594] 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 6dad757a6a..1349abeb65 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -15,7 +15,6 @@ 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 @@ def _setup_compile(self, outdir, macros, incdirs, sources, depends, # 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 @@ def _prep_compile(self, sources, output_dir, depends=None): 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 f05d66cb5d..75db8773f1 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.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 @@ def add_ui(self): ("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 @@ def add_ui(self): 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 @@ def add_ui(self): 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 5771252123..9626710b0c 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -689,6 +689,11 @@ def get_libraries (self, ext): # 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 6f4ce81f79..4a9ed398a0 100644 --- a/command/upload.py +++ b/command/upload.py @@ -6,7 +6,7 @@ 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 f88f36526c..d24d0ac6e0 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -618,7 +618,7 @@ def get_msvc_paths(self, path, platform='x86'): "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 49536f0d3f..e1397a1988 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 56998c3507..324819d4a5 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -42,6 +42,48 @@ # 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 @@ def preprocess(self, source, 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 @@ def link(self, target_desc, objects, 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 889bf13603..1265f4c4e3 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) From 5baff4ddf909616e95ebd3eb3febcc6ebfa471d9 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Tue, 27 Jun 2006 10:08:25 +0000 Subject: [PATCH 1082/2594] 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 2ba3c4db5d..c3b1e4ecfb 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 324819d4a5..6cd14f7728 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 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: [PATCH 1083/2594] 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 738e3f7269..5b09965867 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -467,7 +467,8 @@ def _make_spec_file(self): # 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 @@ def _make_spec_file(self): ('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), 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: [PATCH 1084/2594] 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 d24d0ac6e0..d725905bd0 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -237,7 +237,7 @@ def __init__ (self, verbose=0, dry_run=0, force=0): 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" 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: [PATCH 1085/2594] 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 d725905bd0..c96d527478 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -131,7 +131,7 @@ def load_macros(self, version): 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" From 50345b356d829f38ac236b497f5eca14852db45e Mon Sep 17 00:00:00 2001 From: "Phillip J. Eby" Date: Mon, 10 Jul 2006 19:18:35 +0000 Subject: [PATCH 1086/2594] 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 4a9ed398a0..67ba080427 100644 --- a/command/upload.py +++ b/command/upload.py @@ -185,7 +185,7 @@ def upload_file(self, command, pyversion, filename): 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() 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: [PATCH 1087/2594] 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 a1dbb4b5ef..8a28531c8c 100644 --- a/__init__.py +++ b/__init__.py @@ -12,4 +12,4 @@ __revision__ = "$Id$" -__version__ = "2.4.0" +__version__ = "2.5.0" 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: [PATCH 1088/2594] 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 8a28531c8c..9c60e54690 100644 --- a/__init__.py +++ b/__init__.py @@ -12,4 +12,6 @@ __revision__ = "$Id$" -__version__ = "2.5.0" +import sys +__version__ = "%d.%d.%d" % sys.version_info[:3] +del sys 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: [PATCH 1089/2594] 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 c96d527478..0d72837ed3 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -131,8 +131,10 @@ def load_macros(self, version): 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: From e11eb794d3d6a0845f8d742935a884c82f9eaaf0 Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Fri, 11 Aug 2006 14:57:12 +0000 Subject: [PATCH 1090/2594] 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 a1dbb4b5ef..9c60e54690 100644 --- a/__init__.py +++ b/__init__.py @@ -12,4 +12,6 @@ __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 738e3f7269..5b09965867 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -467,7 +467,8 @@ def _make_spec_file(self): # 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 @@ def _make_spec_file(self): ('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 4a9ed398a0..67ba080427 100644 --- a/command/upload.py +++ b/command/upload.py @@ -185,7 +185,7 @@ def upload_file(self, command, pyversion, filename): 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 d24d0ac6e0..0d72837ed3 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -131,8 +131,10 @@ def load_macros(self, version): 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 @@ def __init__ (self, verbose=0, dry_run=0, force=0): 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 e1397a1988..76fe256c00 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 324819d4a5..6cd14f7728 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 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: [PATCH 1091/2594] 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 9c60e54690..21d34c767b 100644 --- a/__init__.py +++ b/__init__.py @@ -12,6 +12,12 @@ __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" From 61e2384194c8c759b122464be8f82c562301a108 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Fri, 18 Aug 2006 22:13:04 +0000 Subject: [PATCH 1092/2594] 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 b725a14b41..059d5447f6 100644 --- a/archive_util.py +++ b/archive_util.py @@ -124,7 +124,7 @@ def visit (z, dirname, names): 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 1349abeb65..0ed9a40a35 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -159,7 +159,7 @@ class (via the 'executables' class attribute), but most will have: # 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 9626710b0c..cd67544b7a 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -341,7 +341,7 @@ def check_extensions_list (self, extensions): # 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 c9c6f037a7..85a28fe795 100644 --- a/core.py +++ b/core.py @@ -101,9 +101,9 @@ class found in 'cmdclass' is used in place of the default, which is 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 @@ class found in 'cmdclass' is used in place of the default, which is 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 ff49886d97..d098cb9713 100644 --- a/dist.py +++ b/dist.py @@ -239,7 +239,7 @@ def __init__ (self, attrs=None): 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 @@ def find_config_files (self): 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 @@ def parse_config_files (self, filenames=None): # 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 @@ def _set_command_options (self, command_obj, option_dict=None): 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 218ed73f98..31cf0c5c3b 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -97,7 +97,7 @@ def set_option_table (self, 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): + if long_option in self.option_index: raise DistutilsGetoptError, \ "option conflict: already an option '%s'" % long_option else: @@ -109,7 +109,7 @@ def add_option (self, long_option, short_option=None, help_string=None): 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 @@ def get_attr_name (self, long_option): 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 76fe256c00..0ea4bb7334 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 67efd65e36..ff2878de1b 100644 --- a/text_file.py +++ b/text_file.py @@ -89,7 +89,7 @@ def __init__ (self, filename=None, file=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 in options: setattr (self, opt, options[opt]) else: @@ -97,7 +97,7 @@ def __init__ (self, filename=None, file=None, **options): # 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 1265f4c4e3..1bcda93fe0 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] From cacb980ab0e5e233a16ebdcea087b89b2f97f79a Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Sun, 20 Aug 2006 16:25:10 +0000 Subject: [PATCH 1093/2594] 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 a4aff58e3d..92f49346f7 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 0d72837ed3..9ec3508c76 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -239,7 +239,7 @@ def __init__ (self, verbose=0, dry_run=0, force=0): 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" From 75682ad15d892d2159454279c881771f44e2500f Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Mon, 21 Aug 2006 19:07:27 +0000 Subject: [PATCH 1094/2594] 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 9c60e54690..21d34c767b 100644 --- a/__init__.py +++ b/__init__.py @@ -12,6 +12,12 @@ __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" From 6c4f3892adad5878f43e5246f8794c3de5b685e1 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Thu, 24 Aug 2006 00:41:19 +0000 Subject: [PATCH 1095/2594] 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 71a5614719..2cd36365e7 100644 --- a/version.py +++ b/version.py @@ -32,7 +32,8 @@ 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 @@ def __init__ (self, vstring=None): 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 From f4e988990abc4e2e3408af5bf07bb08203347252 Mon Sep 17 00:00:00 2001 From: Alex Martelli Date: Thu, 24 Aug 2006 02:58:11 +0000 Subject: [PATCH 1096/2594] 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 0ea4bb7334..96923bdca4 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"): 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: [PATCH 1097/2594] 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 0000000000000000000000000000000000000000..7403bfabf5cc10c13ef2b6a2ea276b4f6d26ff37 GIT binary patch literal 61440 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 literal 0 HcmV?d00001 From a731ab145f5bca79f452269c2dfc52ab0ad6f6de Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Fri, 6 Oct 2006 13:18:26 +0000 Subject: [PATCH 1098/2594] [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 dec9aa2bf2..5fcc8d2764 100644 --- a/command/register.py +++ b/command/register.py @@ -251,7 +251,7 @@ def post_to_server(self, data, auth=None): 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") From 942bf846fffbdd0aa873115756281bd91d155dea Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Sun, 8 Oct 2006 17:49:52 +0000 Subject: [PATCH 1099/2594] 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 c3b1e4ecfb..0cfafab99b 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) From ab8b0e572d34555fd27810253833db03dd2efb0b Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Sun, 8 Oct 2006 17:50:26 +0000 Subject: [PATCH 1100/2594] Backport of r52233 --- sysconfig.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sysconfig.py b/sysconfig.py index c3b1e4ecfb..bf52a27474 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) From e3d14a32164b47b216074647f6e3a05ede126198 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Sun, 8 Oct 2006 17:51:46 +0000 Subject: [PATCH 1101/2594] 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 6cd14f7728..d1fd1d9511 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): From 364819cca89dfd6c2bfe1c7cdac42073baa01ba3 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Sun, 8 Oct 2006 17:52:37 +0000 Subject: [PATCH 1102/2594] Backport of r52236 --- unixccompiler.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/unixccompiler.py b/unixccompiler.py index 6cd14f7728..d1fd1d9511 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): From 299c49adb264eb716796a042441fa013b094e2c2 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Mon, 9 Oct 2006 17:13:26 +0000 Subject: [PATCH 1103/2594] [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 dec9aa2bf2..5fcc8d2764 100644 --- a/command/register.py +++ b/command/register.py @@ -251,7 +251,7 @@ def post_to_server(self, data, auth=None): 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") From 3624acc8638f40e03e87655ec596501dece3042f Mon Sep 17 00:00:00 2001 From: Tim Peters Date: Wed, 18 Oct 2006 05:09:12 +0000 Subject: [PATCH 1104/2594] Whitespace normalization. --- unixccompiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unixccompiler.py b/unixccompiler.py index d1fd1d9511..75e8a5316e 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 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 1105/2594] - 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 5b09965867..03ef070c87 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -337,37 +337,47 @@ def run (self): 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): 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 1106/2594] 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 5b09965867..03ef070c87 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -337,37 +337,47 @@ def run (self): 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): From 5a83e9120fb4a858150b3ddfe7d7a9c7b7e9f7d6 Mon Sep 17 00:00:00 2001 From: "Phillip J. Eby" Date: Fri, 10 Nov 2006 00:33:36 +0000 Subject: [PATCH 1107/2594] 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 c31ac29668..c8880310df 100644 --- a/command/install_egg_info.py +++ b/command/install_egg_info.py @@ -35,6 +35,9 @@ def run(self): 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') From cff70f8bd97d2c1072fcead0aab6a6ea16b8f473 Mon Sep 17 00:00:00 2001 From: "Phillip J. Eby" Date: Fri, 10 Nov 2006 17:13:29 +0000 Subject: [PATCH 1108/2594] 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 c31ac29668..c8880310df 100644 --- a/command/install_egg_info.py +++ b/command/install_egg_info.py @@ -35,6 +35,9 @@ def run(self): 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') 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 1109/2594] 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 03ef070c87..6f0e0d881c 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -391,6 +391,7 @@ def _make_spec_file(self): 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 @@ def _make_spec_file(self): # 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 @@ def _make_spec_file(self): # 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 " 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 1110/2594] 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 03ef070c87..6f0e0d881c 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -391,6 +391,7 @@ def _make_spec_file(self): 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 @@ def _make_spec_file(self): # 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 @@ def _make_spec_file(self): # 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 " From 6188d82d9f938c426b361a467b459d7a0ff21db5 Mon Sep 17 00:00:00 2001 From: Matthias Klose Date: Sun, 3 Dec 2006 17:13:54 +0000 Subject: [PATCH 1111/2594] - 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 00f8a6b9ae..2413829369 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -186,7 +186,7 @@ def finalize_options (self): # 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 From f2107aba5992adb79db1e79607b500541270f89b Mon Sep 17 00:00:00 2001 From: Matthias Klose Date: Sun, 3 Dec 2006 17:16:41 +0000 Subject: [PATCH 1112/2594] - 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 00f8a6b9ae..2413829369 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -186,7 +186,7 @@ def finalize_options (self): # 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 From 6c3e3687e30a200343363312dc6ee61e0dce9d8b Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Wed, 13 Dec 2006 04:49:30 +0000 Subject: [PATCH 1113/2594] 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 5b09965867..6f0e0d881c 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -337,37 +337,47 @@ def run (self): 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 @@ def _make_spec_file(self): 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 @@ def _make_spec_file(self): # 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 @@ def _make_spec_file(self): # 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 cd67544b7a..f79eee3b74 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -186,7 +186,7 @@ def finalize_options (self): # 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 c31ac29668..c8880310df 100644 --- a/command/install_egg_info.py +++ b/command/install_egg_info.py @@ -35,6 +35,9 @@ def run(self): 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 f8912621c8..3177476b3b 100644 --- a/command/register.py +++ b/command/register.py @@ -256,7 +256,7 @@ def post_to_server(self, data, auth=None): 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 0000000000000000000000000000000000000000..7403bfabf5cc10c13ef2b6a2ea276b4f6d26ff37 GIT binary patch literal 61440 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 literal 0 HcmV?d00001 diff --git a/sysconfig.py b/sysconfig.py index 96923bdca4..8989d92b4e 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 6cd14f7728..75e8a5316e 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): From ba04afa355a9b33a8dc4e52e11de3b11cae13afd Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 10 Jan 2007 16:19:56 +0000 Subject: [PATCH 1114/2594] 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 ca524a5b88..6968cb8f6f 100644 --- a/bcppcompiler.py +++ b/bcppcompiler.py @@ -115,7 +115,7 @@ def compile(self, sources, # 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 @@ def compile(self, sources, 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 @@ def create_static_lib (self, 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 @@ def link (self, 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 @@ def preprocess (self, 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 3177476b3b..cb9525ab0e 100644 --- a/command/register.py +++ b/command/register.py @@ -284,11 +284,11 @@ def post_to_server(self, data, auth=None): 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 3dfe6f21a7..eb2db505dd 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -333,7 +333,7 @@ def read_template (self): 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 67ba080427..7f96a475b6 100644 --- a/command/upload.py +++ b/command/upload.py @@ -184,7 +184,7 @@ def upload_file(self, command, pyversion, filename): 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 85a28fe795..4dc8eb01aa 100644 --- a/core.py +++ b/core.py @@ -110,7 +110,7 @@ class found in 'cmdclass' is used in place of the default, which is # (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 @@ class found in 'cmdclass' is used in place of the default, which is # 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 @@ class found in 'cmdclass' is used in place of the default, which is 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 @@ class found in 'cmdclass' is used in place of the default, which is raise SystemExit, error except (DistutilsError, - CCompilerError), msg: + CCompilerError) as msg: if DEBUG: raise else: diff --git a/cygwinccompiler.py b/cygwinccompiler.py index 4fd23e6dc6..44b5850834 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -142,13 +142,13 @@ def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): # 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 92f49346f7..398f242974 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 d098cb9713..d21c5e2f1b 100644 --- a/dist.py +++ b/dist.py @@ -398,7 +398,7 @@ def parse_config_files (self, filenames=None): 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 @@ def _parse_command_opts (self, parser, args): # 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 @@ def _set_command_options (self, command_obj, option_dict=None): 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 f52e63232d..8aef2b7a9d 100644 --- a/emxccompiler.py +++ b/emxccompiler.py @@ -79,13 +79,13 @@ def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): # 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 31cf0c5c3b..62a24e8524 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -256,7 +256,7 @@ def getopt (self, args=None, object=None): 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 37b152ed8a..c225ad3172 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 9ec3508c76..968a4ffd46 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -129,7 +129,7 @@ def load_macros(self, version): 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 @@ def compile(self, sources, 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 @@ def compile(self, sources, self.spawn ([self.rc] + ["/fo" + obj] + [rc_file]) - except DistutilsExecError, msg: + except DistutilsExecError as msg: raise CompileError, msg continue else: @@ -414,7 +414,7 @@ def compile(self, sources, 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 @@ def create_static_lib (self, 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 @@ def link (self, 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 e5654ff009..6b07f52ebe 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 8989d92b4e..9de7234683 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 75e8a5316e..0795f1206d 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -162,7 +162,7 @@ def preprocess(self, source, 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 @@ def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): 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 @@ def create_static_lib(self, objects, output_libname, 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 @@ def link(self, target_desc, objects, 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 1bcda93fe0..16d8bcba4d 100644 --- a/util.py +++ b/util.py @@ -229,7 +229,7 @@ def _subst (match, local_vars=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 () From 4e0a58cc6113aa5ab73196de13b16d500ff46fe6 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Fri, 9 Feb 2007 05:37:30 +0000 Subject: [PATCH 1115/2594] 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 6968cb8f6f..b6a3bf62ce 100644 --- a/bcppcompiler.py +++ b/bcppcompiler.py @@ -392,7 +392,7 @@ def preprocess (self, 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 0ed9a40a35..25d90c8a9a 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -1026,7 +1026,7 @@ def announce (self, msg, level=1): 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 3cd5858920..be0a3e23f6 100644 --- a/cmd.py +++ b/cmd.py @@ -163,14 +163,14 @@ 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 + 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 @@ def debug_print (self, msg): """ from distutils.debug import DEBUG if DEBUG: - print msg + print(msg) sys.stdout.flush() @@ -475,4 +475,4 @@ def get_outputs (self): if __name__ == "__main__": - print "ok" + print("ok") diff --git a/command/bdist_rpm.py b/command/bdist_rpm.py index 6f0e0d881c..bbcf2927dc 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -267,11 +267,11 @@ def finalize_package_data (self): 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 520c1b0c76..42465693be 100644 --- a/command/config.py +++ b/command/config.py @@ -359,9 +359,9 @@ def check_header (self, header, include_dirs=None, 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 453151d08b..fd5d6d1f39 100644 --- a/command/install.py +++ b/command/install.py @@ -292,7 +292,7 @@ def finalize_options (self): 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 @@ def finalize_options (self): 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 @@ def dump_dirs (self, msg): 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 cb9525ab0e..48070ee83b 100644 --- a/command/register.py +++ b/command/register.py @@ -86,14 +86,14 @@ def classifiers(self): ''' 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 @@ def send_metadata(self): 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 @@ def send_metadata(self): # 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 @@ def send_metadata(self): # 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 @@ def send_metadata(self): 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 @@ def post_to_server(self, data, auth=None): 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 7f96a475b6..438ae99a9c 100644 --- a/command/upload.py +++ b/command/upload.py @@ -196,4 +196,4 @@ def upload_file(self, command, pyversion, filename): 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 4dc8eb01aa..6242775c11 100644 --- a/core.py +++ b/core.py @@ -125,7 +125,7 @@ class found in 'cmdclass' is used in place of the default, which is 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 @@ class found in 'cmdclass' is used in place of the default, which is 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 d21c5e2f1b..ca7a2f9952 100644 --- a/dist.py +++ b/dist.py @@ -290,22 +290,22 @@ def dump_option_dicts (self, header=None, commands=None, indent=""): 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 @@ def parse_config_files (self, filenames=None): 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 @@ def _show_help (self, 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 @@ def _show_help (self, 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 @@ def handle_display_options (self, option_order): # 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 @@ def handle_display_options (self, option_order): 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 @@ def print_command_list (self, commands, header, max_length): 'print_commands()'. """ - print header + ":" + print(header + ":") for cmd in commands: klass = self.cmdclass.get(cmd) @@ -723,7 +723,7 @@ def print_command_list (self, commands, header, max_length): 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 @@ def print_commands (self): "Standard commands", max_length) if extra_commands: - print + print() self.print_command_list(extra_commands, "Extra commands", max_length) @@ -862,8 +862,8 @@ def get_command_obj (self, command, create=1): 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 @@ def _set_command_options (self, command_obj, option_dict=None): 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 62a24e8524..646f8e1ee6 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -497,6 +497,6 @@ def __init__ (self, options=[]): 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 4bbdd1f00f..e4a83d6484 100644 --- a/filelist.py +++ b/filelist.py @@ -53,7 +53,7 @@ def debug_print (self, msg): """ from distutils.debug import DEBUG if DEBUG: - print msg + print(msg) # -- List-like methods --------------------------------------------- diff --git a/log.py b/log.py index 95d4c1c5a2..e4959d6492 100644 --- a/log.py +++ b/log.py @@ -23,9 +23,9 @@ def _log(self, level, msg, args): 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 0de123d9e9..9db1a39b17 100644 --- a/mwerkscompiler.py +++ b/mwerkscompiler.py @@ -160,9 +160,9 @@ def link (self, 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 6b07f52ebe..c75931c3ab 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 4d2a7cdf1a..8d4b07037c 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -74,8 +74,8 @@ 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" + 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 ff2878de1b..10ee1be783 100644 --- a/text_file.py +++ b/text_file.py @@ -342,13 +342,13 @@ 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) + 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" 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: [PATCH 1116/2594] 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 2413829369..542b77a7ad 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -185,9 +185,7 @@ def finalize_options (self): # 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 @@ def finalize_options (self): # 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 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: [PATCH 1117/2594] 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 2413829369..542b77a7ad 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -185,9 +185,7 @@ def finalize_options (self): # 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 @@ def finalize_options (self): # 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 From a8bf57767e4e8ba7c99e9a51f30861707124c642 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Fri, 9 Feb 2007 20:13:25 +0000 Subject: [PATCH 1118/2594] 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 ba8b6c021b..434b34f184 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 From 0eaf9a8276f4587601ba9514243c1f06e4511cb8 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Sun, 11 Feb 2007 06:12:03 +0000 Subject: [PATCH 1119/2594] - 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 9de7234683..220b03310c 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: From a67f7d4700f890ac5822401b756dc0b20a6eb421 Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Fri, 23 Feb 2007 15:07:44 +0000 Subject: [PATCH 1120/2594] 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 f79eee3b74..bbe514626d 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -185,9 +185,7 @@ def finalize_options (self): # 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 @@ def finalize_options (self): # 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 From 4b5b20c7db13586c89f2de8110f9b3af5a8e69be Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 13 Mar 2007 10:19:22 +0000 Subject: [PATCH 1121/2594] 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 542b77a7ad..82474de823 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -186,7 +186,7 @@ def finalize_options (self): # 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 @@ def finalize_options (self): # 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: From e936d6d24f1cb257cb2a66a70c32c89f65306d0a Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 13 Mar 2007 10:19:35 +0000 Subject: [PATCH 1122/2594] 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 542b77a7ad..82474de823 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -186,7 +186,7 @@ def finalize_options (self): # 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 @@ def finalize_options (self): # 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: 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: [PATCH 1123/2594] 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 21d34c767b..86ad44fe3a 100644 --- a/__init__.py +++ b/__init__.py @@ -20,4 +20,4 @@ # 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" 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: [PATCH 1124/2594] 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 21d34c767b..86ad44fe3a 100644 --- a/__init__.py +++ b/__init__.py @@ -20,4 +20,4 @@ # 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" From e49345974375c27eddb6aa6a576e5b30688e3511 Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Sun, 1 Apr 2007 18:24:22 +0000 Subject: [PATCH 1125/2594] 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 0d72837ed3..29a9020956 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 @@ def initialize(self): 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 From 1085c6753feacd1c88d97e59031217679ef25e6a Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Sun, 1 Apr 2007 18:29:47 +0000 Subject: [PATCH 1126/2594] 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 0d72837ed3..29a9020956 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 @@ def initialize(self): 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 From 57e9dc4a5d61c5572467c3fe69e816c357647a6f Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Tue, 17 Apr 2007 08:48:32 +0000 Subject: [PATCH 1127/2594] 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 be0a3e23f6..ebd99310ea 100644 --- a/cmd.py +++ b/cmd.py @@ -8,7 +8,7 @@ __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 @@ def dump_options (self, header=None, indent=""): 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 @@ def make_file (self, infiles, outfile, func, args, """ 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 23c25a55a8..d6897d2d09 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -7,7 +7,7 @@ __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 75db8773f1..f31bdedac6 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 bbcf2927dc..ef2bdfd808 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -7,7 +7,7 @@ __revision__ = "$Id$" -import sys, os, string +import sys, os import glob from types import * from distutils.core import Command @@ -354,7 +354,7 @@ def run (self): 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 @@ def _make_spec_file(self): '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 @@ def _make_spec_file(self): 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 @@ def _make_spec_file(self): '', '%' + 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 @@ def _make_spec_file(self): ]) 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 @@ def _format_changelog(self, changelog): 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 49afca0472..21465a82ff 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -7,7 +7,7 @@ __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 @@ def run (self): # 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 @@ def get_inidata (self): # 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 @@ def escape(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") + return "\n".join(lines) # get_inidata() diff --git a/command/build_clib.py b/command/build_clib.py index 69d8c75166..07f5817986 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -18,7 +18,7 @@ # 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 @@ def finalize_options (self): 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 bbe514626d..d0cd16293b 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -8,7 +8,7 @@ __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 @@ def finalize_options (self): 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 @@ def finalize_options (self): 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 @@ def finalize_options (self): # 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 @@ def finalize_options (self): # 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 @@ def finalize_options (self): # 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 @@ def build_extension(self, ext): # 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 @@ def get_ext_filename (self, ext_name): """ 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 @@ def get_export_symbols (self, ext): 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 3b7ec62c59..990824bdff 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -6,7 +6,7 @@ __revision__ = "$Id$" -import sys, string, os +import sys, os from types import * from glob import glob @@ -150,7 +150,7 @@ def get_package_dir (self, package): 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 @@ def get_package_dir (self, package): 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 @@ def find_modules (self): # - 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 @@ 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: @@ -362,7 +362,7 @@ def get_outputs (self, include_bytecode=1): 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 42465693be..3374db9e66 100644 --- a/command/config.py +++ b/command/config.py @@ -13,7 +13,7 @@ __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 @@ 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) + self.include_dirs = self.include_dirs.split(os.pathsep) if self.libraries is None: self.libraries = [] @@ -84,7 +84,7 @@ def finalize_options (self): 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 @@ def _clean (self, *filenames): 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 @@ def check_func (self, func, 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 fd5d6d1f39..c03a90e82c 100644 --- a/command/install.py +++ b/command/install.py @@ -8,7 +8,7 @@ __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 @@ def finalize_options (self): # $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 @@ def dump_dirs (self, msg): 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 @@ def handle_extra_path (self): 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 08ff543449..65d25f7af2 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 48070ee83b..33567981bb 100644 --- a/command/register.py +++ b/command/register.py @@ -7,7 +7,7 @@ __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 @@ def check_metadata(self): 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 eb2db505dd..8e1c066023 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -6,7 +6,7 @@ __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 @@ def check_metadata (self): 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 @@ def add_defaults (self): 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 44b5850834..fae6848165 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 ca7a2f9952..7ed1b5cf2c 100644 --- a/dist.py +++ b/dist.py @@ -8,7 +8,7 @@ __revision__ = "$Id$" -import sys, os, string, re +import sys, os, re from types import * from copy import copy @@ -304,7 +304,7 @@ def dump_option_dicts (self, header=None, commands=None, indent=""): 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 @@ def parse_config_files (self, filenames=None): 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 @@ def finalize_options (self): 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 @@ def handle_display_options (self, option_order): 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 @@ 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 "", ",") + 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 @@ def write_pkg_file (self, file): 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 8aef2b7a9d..f4b90dcd00 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 440d128cdc..0fcbcc141a 100644 --- a/extension.py +++ b/extension.py @@ -5,7 +5,7 @@ __revision__ = "$Id$" -import os, string, sys +import os, sys from types import * try: @@ -128,7 +128,7 @@ def __init__ (self, name, sources, 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 646f8e1ee6..317a3a4860 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -115,7 +115,7 @@ 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) + return long_option.translate(longopt_xlate) def _check_alias_dict (self, aliases, what): @@ -253,7 +253,7 @@ def getopt (self, args=None, object=None): 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 @@ def __init__ (self, options=[]): 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 e4a83d6484..bc668cfad3 100644 --- a/filelist.py +++ b/filelist.py @@ -8,7 +8,7 @@ __revision__ = "$Id$" -import os, string, re +import os, re import fnmatch from types import * from glob import glob @@ -84,7 +84,7 @@ def remove_duplicates (self): # -- "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 @@ def process_template_line (self, line): # 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 @@ def process_template_line (self, line): 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 @@ def process_template_line (self, line): 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 968a4ffd46..ca1feaa088 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -12,7 +12,7 @@ __revision__ = "$Id$" -import sys, os, string +import sys, os from distutils.errors import \ DistutilsExecError, DistutilsPlatformError, \ CompileError, LibError, LinkError @@ -148,7 +148,7 @@ def load_macros(self, version): 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 @@ def initialize(self): # 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 @@ def find_exe(self, exe): 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 @@ def get_msvc_paths(self, path, platform='x86'): 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 @@ def set_path_env_var(self, name): 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 9db1a39b17..028ea8250d 100644 --- a/mwerkscompiler.py +++ b/mwerkscompiler.py @@ -8,7 +8,7 @@ __revision__ = "$Id$" -import sys, os, string +import sys, os from types import * from distutils.errors import \ DistutilsExecError, DistutilsPlatformError, \ @@ -213,11 +213,11 @@ def _filename_to_abs(self, filename): 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 c75931c3ab..c70c10bf7c 100644 --- a/spawn.py +++ b/spawn.py @@ -10,7 +10,7 @@ __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 220b03310c..ea2e059778 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -13,7 +13,6 @@ 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 10ee1be783..c23a31dd9c 100644 --- a/text_file.py +++ b/text_file.py @@ -7,7 +7,7 @@ __revision__ = "$Id$" from types import * -import sys, os, string +import sys, os class TextFile: @@ -142,7 +142,7 @@ def gen_error (self, msg, line=None): 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 @@ def readline (self): # 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 @@ def readline (self): # # 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 @@ def readline (self): 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 @@ def readline (self): # 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 @@ def unreadline (self, line): 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 @@ def unreadline (self, line): 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 16d8bcba4d..6f15ce8b5e 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 @@ def byte_compile (py_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 2cd36365e7..2db6b18f1e 100644 --- a/version.py +++ b/version.py @@ -26,8 +26,7 @@ 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 @@ def parse (self, vstring): 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 @@ def parse (self, vstring): 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 @@ def __str__ (self): 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 @@ def __repr__ (self): def __cmp__ (self, other): - if isinstance(other, StringType): + if isinstance(other, str): other = LooseVersion(other) return cmp(self.version, other.version) From 44168b43a0a197dc0295f0e80734880cecf658ea Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 24 Apr 2007 15:27:13 +0000 Subject: [PATCH 1128/2594] 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 82474de823..12d4083743 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -533,7 +533,8 @@ def swig_sources (self, sources, extension): 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' From 092ed8384228a0ca24fd297dde6243b6751a46a0 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 24 Apr 2007 15:27:25 +0000 Subject: [PATCH 1129/2594] 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 82474de823..12d4083743 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -533,7 +533,8 @@ def swig_sources (self, sources, extension): 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' From 0086340118b6b5d4243cb84b3e4c6df726a064da Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Wed, 25 Apr 2007 06:42:41 +0000 Subject: [PATCH 1130/2594] Whitespace normalization --- unixccompiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unixccompiler.py b/unixccompiler.py index d1fd1d9511..75e8a5316e 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 From 078fadd293abe8b159cb6185a3ded2e87210c5f8 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Fri, 27 Apr 2007 19:54:29 +0000 Subject: [PATCH 1131/2594] 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 21d34c767b..86ad44fe3a 100644 --- a/__init__.py +++ b/__init__.py @@ -20,4 +20,4 @@ # 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 d0cd16293b..2832d5717b 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -186,7 +186,7 @@ def finalize_options (self): # 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 @@ def finalize_options (self): # 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 @@ def swig_sources (self, sources, extension): 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 ca1feaa088..07c76f1838 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 @@ def initialize(self): 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": From 3bf5ce40cfaf14aa42593b4c4e5c28338c31633a Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Fri, 27 Apr 2007 23:53:51 +0000 Subject: [PATCH 1132/2594] 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 25d90c8a9a..50905c1d44 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -168,7 +168,7 @@ class (via the 'executables' class attribute), but most will have: # 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 @@ def _check_macro_definitions (self, 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): + (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 @@ def _setup_compile(self, outdir, macros, incdirs, sources, depends, """ 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 @@ def _fix_compile_args (self, output_dir, macros, include_dirs): """ 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 @@ def _fix_object_args (self, objects, output_dir): 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 ebd99310ea..ea3799af16 100644 --- a/cmd.py +++ b/cmd.py @@ -222,7 +222,7 @@ def _ensure_stringlike (self, option, what, default=None): 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 @@ def ensure_string_list (self, option): 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 @@ def make_file (self, infiles, outfile, func, args, # 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 07f5817986..5434e86c86 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -92,7 +92,7 @@ def finalize_options (self): 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 @@ def check_library_list (self, libraries): 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 2832d5717b..0236a26835 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -137,7 +137,7 @@ def finalize_options (self): 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 @@ def finalize_options (self): 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 @@ def finalize_options (self): 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 @@ def check_extensions_list (self, extensions): ("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 990824bdff..52534bdb47 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -361,7 +361,7 @@ def get_outputs (self, include_bytecode=1): 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 3374db9e66..04cfcde736 100644 --- a/command/config.py +++ b/command/config.py @@ -73,17 +73,17 @@ def initialize_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: + 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 @@ def search_cpp (self, pattern, body=None, 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 c03a90e82c..cc92e96368 100644 --- a/command/install.py +++ b/command/install.py @@ -463,7 +463,7 @@ def handle_extra_path (self): 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 1069830fb3..a95194426b 100644 --- a/command/install_data.py +++ b/command/install_data.py @@ -10,7 +10,6 @@ __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 @@ def finalize_options (self): 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 398f242974..c6f014b260 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 7ed1b5cf2c..89390fb2c5 100644 --- a/dist.py +++ b/dist.py @@ -598,13 +598,13 @@ def finalize_options (self): 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 @@ def _set_command_options (self, command_obj, option_dict=None): 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 0fcbcc141a..7fe846b8a8 100644 --- a/extension.py +++ b/extension.py @@ -103,9 +103,9 @@ def __init__ (self, name, sources, 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 317a3a4860..82e1f4d1dc 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -166,13 +166,13 @@ def _grok_option_table (self): 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 bc668cfad3..a0523531c7 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 0795f1206d..a42ab5ed42 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -16,7 +16,7 @@ __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 @@ def link(self, target_desc, objects, 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) From 74aa5c74dd3dda2d8f3fef3c83107f3e006d724c Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 2 May 2007 19:09:54 +0000 Subject: [PATCH 1133/2594] 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 21465a82ff..5074a1a675 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -247,11 +247,11 @@ def create_exe (self, arcname, fullname, bitmap=None): # 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 5434e86c86..bdf98bf3f2 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -147,7 +147,7 @@ def check_library_list (self, libraries): 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 33567981bb..2ddabadf88 100644 --- a/command/register.py +++ b/command/register.py @@ -259,7 +259,7 @@ def post_to_server(self, data, auth=None): 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") From b6944798d4c6fe91c594c4e64ef13820f0c1b266 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Mon, 7 May 2007 22:24:25 +0000 Subject: [PATCH 1134/2594] 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 cc92e96368..a6543ba6a2 100644 --- a/command/install.py +++ b/command/install.py @@ -517,7 +517,7 @@ def run (self): 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), From 0a68cc8e653759fd3913f2bcbc85eeffbb02f194 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 22 May 2007 18:11:13 +0000 Subject: [PATCH 1135/2594] 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 89390fb2c5..e89d942f15 100644 --- a/dist.py +++ b/dist.py @@ -569,7 +569,7 @@ def _parse_command_opts (self, parser, args): #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( From 846a6d17bc1356201efb984e23caafff578982f4 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Fri, 25 May 2007 18:39:29 +0000 Subject: [PATCH 1136/2594] 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 ea2e059778..51f23a24a5 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -11,6 +11,7 @@ __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 c23a31dd9c..9d4d42b4c6 100644 --- a/text_file.py +++ b/text_file.py @@ -7,7 +7,7 @@ __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 @@ def open (self, filename): '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 From 6d3419303b176f1986ec06a1421452f227a839c1 Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Fri, 1 Jun 2007 07:29:12 +0000 Subject: [PATCH 1137/2594] 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 621bcb4af3..b9f39bae79 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -114,7 +114,9 @@ def get_data_files (self): 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 78e4c55ed4..54a4ed80fd 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 @@ def test_package_data(self): 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) From 4951f23221c37e1b9a5b595397529ec8c301f705 Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Sat, 2 Jun 2007 18:53:07 +0000 Subject: [PATCH 1138/2594] 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 621bcb4af3..b9f39bae79 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -114,7 +114,9 @@ def get_data_files (self): 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 78e4c55ed4..54a4ed80fd 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 @@ def test_package_data(self): 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) From b56a8f9ed0bfc43878362c0d08e7292b146f9b81 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Thu, 7 Jun 2007 23:15:56 +0000 Subject: [PATCH 1139/2594] 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 ea3799af16..8d77e7fb2b 100644 --- a/cmd.py +++ b/cmd.py @@ -9,7 +9,6 @@ __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 @@ def ensure_string_list (self, option): 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 @@ def make_file (self, infiles, outfile, func, args, # 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 e89d942f15..c01724d83a 100644 --- a/dist.py +++ b/dist.py @@ -9,7 +9,6 @@ __revision__ = "$Id$" import sys, os, re -from types import * from copy import copy try: @@ -527,7 +526,7 @@ def _parse_command_opts (self, parser, args): # 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 @@ def _parse_command_opts (self, parser, args): # 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 @@ def _parse_command_opts (self, parser, args): 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 @@ def _show_help (self, 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 7fe846b8a8..43b0d3fd00 100644 --- a/extension.py +++ b/extension.py @@ -6,7 +6,6 @@ __revision__ = "$Id$" import os, sys -from types import * try: import warnings @@ -104,7 +103,7 @@ def __init__ (self, name, sources, **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 9d4d42b4c6..3f6a220dcc 100644 --- a/text_file.py +++ b/text_file.py @@ -6,7 +6,6 @@ __revision__ = "$Id$" -from types import * import sys, os, io @@ -137,7 +136,7 @@ def gen_error (self, msg, line=None): 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 @@ def readline (self): 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 @@ def readline (self): 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 a42ab5ed42..d07ae1e358 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -16,7 +16,6 @@ __revision__ = "$Id$" import os, sys -from types import NoneType from copy import copy from distutils import sysconfig @@ -212,7 +211,7 @@ def link(self, target_desc, objects, 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) From d9a8d08e4670a883b259554331b4372e8ae18f12 Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Mon, 11 Jun 2007 05:28:45 +0000 Subject: [PATCH 1140/2594] 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 0de123d9e9..343c6cecae 100644 --- a/mwerkscompiler.py +++ b/mwerkscompiler.py @@ -18,7 +18,6 @@ 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 @@ def link (self, # 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'] 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: [PATCH 1141/2594] 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 5074a1a675..55d5d7e1b1 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -246,13 +246,8 @@ def create_exe (self, arcname, fullname, bitmap=None): 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" From 4beaf762f12da3cb21e32d646c5f01c8bdb7e9ca Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 13 Jun 2007 18:07:49 +0000 Subject: [PATCH 1142/2594] 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 50905c1d44..d4f4adea19 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -1040,7 +1040,7 @@ def spawn (self, cmd): 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 8d77e7fb2b..b2c952c38c 100644 --- a/cmd.py +++ b/cmd.py @@ -356,7 +356,7 @@ 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=0o777): dir_util.mkpath(name, mode, dry_run=self.dry_run) diff --git a/command/build_py.py b/command/build_py.py index 52534bdb47..8f5609084c 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -114,7 +114,9 @@ def get_data_files (self): 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 bda4480ca5..511b82f999 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -119,8 +119,8 @@ def copy_scripts (self): 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 fe93ef5af2..da2da358ba 100644 --- a/command/install_scripts.py +++ b/command/install_scripts.py @@ -53,7 +53,7 @@ def run (self): 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 2ddabadf88..53f4293e17 100644 --- a/command/register.py +++ b/command/register.py @@ -183,7 +183,7 @@ def send_metadata(self): 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 c6f014b260..0cfca2e8ff 100644 --- a/dir_util.py +++ b/dir_util.py @@ -18,7 +18,7 @@ # 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 028ea8250d..662046ae22 100644 --- a/mwerkscompiler.py +++ b/mwerkscompiler.py @@ -18,7 +18,6 @@ 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 @@ def link (self, # 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 475ceee598..91e704cfcc 100644 --- a/tests/support.py +++ b/tests/support.py @@ -9,12 +9,12 @@ 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 78e4c55ed4..54a4ed80fd 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 @@ def test_package_data(self): 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) From a16a76a14958dc71e70971b0fc346e34491f734e Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Sat, 30 Jun 2007 05:01:58 +0000 Subject: [PATCH 1143/2594] 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 0cfca2e8ff..a6c4416a6f 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 () From 3abb62b2c71e85682cb45612ce358f174a71b545 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 3 Jul 2007 08:25:58 +0000 Subject: [PATCH 1144/2594] 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 c01724d83a..8f614765ef 100644 --- a/dist.py +++ b/dist.py @@ -112,8 +112,7 @@ class Distribution: ('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 @@ def get_command_packages (self): 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 51f23a24a5..346707fa90 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 2db6b18f1e..de20e21f70 100644 --- a/version.py +++ b/version.py @@ -148,7 +148,7 @@ def parse (self, vstring): 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)) From 8f2fa921c711ac7d3ff775023a9e85061fddd202 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Mon, 16 Jul 2007 23:10:57 +0000 Subject: [PATCH 1145/2594] 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 f31bdedac6..012d06ea69 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 From 804a12d456aa7253cd54ce0e183277f4459f7d7d Mon Sep 17 00:00:00 2001 From: Collin Winter Date: Tue, 17 Jul 2007 00:38:21 +0000 Subject: [PATCH 1146/2594] 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 6242775c11..bd9b8542cd 100644 --- a/core.py +++ b/core.py @@ -112,10 +112,10 @@ class found in 'cmdclass' is used in place of the default, which is _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 From 555f239ef6a7c03122bb5b73a3b229fd08634adc Mon Sep 17 00:00:00 2001 From: Collin Winter Date: Tue, 17 Jul 2007 00:39:32 +0000 Subject: [PATCH 1147/2594] 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 a0523531c7..cc48e48e58 100644 --- a/filelist.py +++ b/filelist.py @@ -65,8 +65,7 @@ def extend (self, items): 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 de20e21f70..96b6552197 100644 --- a/version.py +++ b/version.py @@ -306,11 +306,11 @@ def parse (self, vstring): # 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 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: [PATCH 1148/2594] 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 5fcc8d2764..200e61e240 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 67ba080427..7461e5f453 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', 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: [PATCH 1149/2594] 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 5fcc8d2764..200e61e240 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 67ba080427..7461e5f453 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', From 8c45e5f857293b59b834acf6fbaa8428ae6dca70 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Mon, 6 Aug 2007 23:33:07 +0000 Subject: [PATCH 1150/2594] 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 53f4293e17..10a903e33d 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 438ae99a9c..d1cf87a871 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', From 913423ef2fd98c50b414f552f21604a699430a67 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Thu, 9 Aug 2007 01:03:29 +0000 Subject: [PATCH 1151/2594] 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 10a903e33d..91081ddb1b 100644 --- a/command/register.py +++ b/command/register.py @@ -8,7 +8,7 @@ __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 @@ def post_to_server(self, data, auth=None): 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 d1cf87a871..1ca2fb9052 100644 --- a/command/upload.py +++ b/command/upload.py @@ -14,7 +14,6 @@ import httplib import base64 import urlparse -import cStringIO as StringIO class upload(Command): @@ -135,7 +134,7 @@ def upload_file(self, command, pyversion, filename): 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 54a4ed80fd..75b6624f5a 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 @@ def test_empty_package_dir (self): 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 8d4b07037c..23506b5dae 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -4,7 +4,7 @@ import distutils.dist import os import shutil -import StringIO +import io import sys import tempfile import unittest @@ -177,7 +177,7 @@ def test_obsoletes_illegal(self): "obsoletes": ["my.pkg (splat)"]}) def format_metadata(self, dist): - sio = StringIO.StringIO() + sio = io.StringIO() dist.metadata.write_pkg_file(sio) return sio.getvalue() From 3a44c01ba5f3f9c0b34705d05e934cbae24d7d60 Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Sun, 12 Aug 2007 00:43:29 +0000 Subject: [PATCH 1152/2594] Kill execfile(), use exec() instead --- core.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core.py b/core.py index bd9b8542cd..d60998240f 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 From c2b4b26b086f52d2e029ebcbd5170f5092b39526 Mon Sep 17 00:00:00 2001 From: Skip Montanaro Date: Thu, 16 Aug 2007 14:35:24 +0000 Subject: [PATCH 1153/2594] 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 65d25f7af2..4efaf9c671 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -9,10 +9,7 @@ # 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): From 59cd99d0b267019cd9811bc03ec379827b8f8410 Mon Sep 17 00:00:00 2001 From: Skip Montanaro Date: Fri, 17 Aug 2007 12:57:41 +0000 Subject: [PATCH 1154/2594] Remove support for BeOS --- sysconfig.py | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 346707fa90..c40c2e7425 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 From e9d9c100389e6d04f060f58eeb5e75921e82eb59 Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Tue, 21 Aug 2007 01:04:47 +0000 Subject: [PATCH 1155/2594] [ 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 cfcc6a951e..8979634e4b 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. From 8654a69ef372ec3dc466d9fe80f4991d43d1f6cc Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Tue, 21 Aug 2007 01:05:16 +0000 Subject: [PATCH 1156/2594] [ 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 75db8773f1..a1c0df4bc5 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -633,7 +633,8 @@ def add_ui(self): 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 From 04cd9c5fbf2d2bbdd8621c82fa07ac5347a45bf9 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Fri, 24 Aug 2007 16:32:05 +0000 Subject: [PATCH 1157/2594] 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 012d06ea69..5225bed1b2 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -633,7 +633,8 @@ def add_ui(self): 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 6f15ce8b5e..9aa857052a 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. From 767edf21398f72156adcea6a418eba9150d06a20 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 29 Aug 2007 13:18:47 +0000 Subject: [PATCH 1158/2594] 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 a6c4416a6f..7dc1205c49 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 From aabf8bf21c444cfae978e92e626ba7c0274a9f10 Mon Sep 17 00:00:00 2001 From: Collin Winter Date: Thu, 30 Aug 2007 03:52:21 +0000 Subject: [PATCH 1159/2594] 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 86ad44fe3a..f71c3adb14 100644 --- a/__init__.py +++ b/__init__.py @@ -8,8 +8,6 @@ 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 059d5447f6..f3f65c6a0c 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 b6a3bf62ce..1ab76c5981 100644 --- a/bcppcompiler.py +++ b/bcppcompiler.py @@ -11,8 +11,6 @@ # 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 @@ def compile(self, sources, 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 @@ def compile(self, sources, [input_opt, output_opt] + extra_postargs + [src]) except DistutilsExecError as msg: - raise CompileError, msg + raise CompileError(msg) return objects @@ -165,7 +163,7 @@ def create_static_lib (self, 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 @@ def link (self, 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 @@ def object_filenames (self, # 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 @@ def preprocess (self, 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 d4f4adea19..c33e5ab440 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 @@ def __init__ (self, 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 (via the 'executables' class attribute), but most will have: # 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 @@ def set_executable(self, key, 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 - + 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 @@ def define_macro (self, name, value=None): 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 @@ def undefine_macro (self, name): 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 @@ def set_include_dirs (self, dirs): 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 @@ def add_library (self, libname): 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 @@ def _setup_compile(self, outdir, macros, incdirs, sources, depends, 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 @@ def _get_cc_args(self, pp_opts, debug, before): 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 @@ def _fix_compile_args (self, output_dir, macros, include_dirs): 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 @@ def _prep_compile(self, sources, output_dir, depends=None): 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 @@ def _fix_lib_args (self, libraries, library_dirs, runtime_library_dirs): """ 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 @@ def _need_link (self, objects, output_file): 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 @@ def detect_language (self, sources): 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 @@ def compile(self, sources, output_dir=None, macros=None, 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 @@ def compile(self, sources, output_dir=None, macros=None, 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 @@ def create_static_lib (self, 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 @@ def link (self, # 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 @@ def link_shared_lib (self, 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 @@ def link_shared_object (self, 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 @@ def link_executable (self, # 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 @@ def object_filenames(self, source_filenames, strip_dir=0, output_dir=''): 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 @@ def object_filenames(self, source_filenames, strip_dir=0, 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) + 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 @@ def library_filename(self, libname, lib_type='static', # or 'shared' # -- 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 @@ def mkpath (self, name, mode=0o777): ) 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 b2c952c38c..66940f7799 100644 --- a/cmd.py +++ b/cmd.py @@ -4,8 +4,6 @@ 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 @@ def __init__ (self, dist): 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 @@ def __init__ (self, dist): # 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 @@ def __getattr__ (self, attr): 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 @@ def ensure_finalized (self): # 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 @@ def initialize_options (self): 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 @@ def finalize_options (self): 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 @@ def dump_options (self, header=None, indent=""): 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 @@ def run (self): 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 @@ def debug_print (self, msg): sys.stdout.flush() - # -- Option validation methods ------------------------------------- # (these are very handy in writing the 'finalize_options()' method) # @@ -216,23 +208,23 @@ def debug_print (self, msg): # 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 @@ def ensure_string_list (self, option): 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 @@ def ensure_dirname (self, option): # -- 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 @@ def set_undefined_options (self, src_cmd, *option_pairs): '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 @@ def get_finalized_command (self, command, create=1): # 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 @@ def get_sub_commands (self): # -- 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 @@ def make_file (self, infiles, outfile, func, args, 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 @@ def make_file (self, infiles, outfile, func, args, 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 0888c2712b..add83f8740 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 d6897d2d09..69c1b28ff2 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 @@ def finalize_options (self): 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 @@ def run (self): 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 ccba00955a..f89961769c 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -4,8 +4,6 @@ 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.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 @@ def initialize_options (self): 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 @@ def finalize_options (self): 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 @@ def run (self): 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 @@ def run (self): 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 5225bed1b2..d313a5082f 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -81,7 +81,7 @@ def xbutton(self, name, title, next, xpos): 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 @@ def initialize_options (self): 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 @@ def finalize_options (self): 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 @@ def add_files(self): 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 ef2bdfd808..72f74f9c53 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.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 @@ def initialize_options (self): 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 @@ def finalize_options (self): 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 @@ def finalize_options (self): 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 @@ def finalize_package_data (self): 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 @@ def run (self): 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 @@ def run (self): 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 @@ def run (self): 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 @@ def _make_spec_file(self): '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 @@ def _make_spec_file(self): 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 @@ def _format_changelog(self, changelog): 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 55d5d7e1b1..249b74c1bb 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.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 @@ def initialize_options (self): 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 @@ def finalize_options (self): 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 @@ def finalize_options (self): 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 @@ def run (self): 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 @@ def escape(s): 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 @@ def create_exe (self, arcname, fullname, bitmap=None): 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 @@ def get_installer_filename(self, fullname): 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 @@ def get_exe_bytes (self): # 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 9ae0a292a3..1f2ce06205 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.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 @@ def initialize_options (self): 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 @@ def finalize_options (self): 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 @@ def run (self): # -- 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 @@ def has_scripts (self): ('build_ext', has_ext_modules), ('build_scripts', has_scripts), ] - -# class build diff --git a/command/build_clib.py b/command/build_clib.py index bdf98bf3f2..5f95207ca9 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -4,8 +4,6 @@ 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 @@ # 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 @@ def initialize_options (self): 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 @@ def finalize_options (self): # 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 @@ def run (self): 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 @@ def get_library_names (self): 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 @@ def build_libraries (self, libraries): 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 0236a26835..a439c49ac6 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -4,12 +4,9 @@ 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 @@ def initialize_options (self): 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 @@ def finalize_options (self): 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 @@ def finalize_options (self): 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 @@ def run (self): # 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 @@ def check_extensions_list (self, extensions): 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 @@ def check_extensions_list (self, extensions): 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 @@ def check_extensions_list (self, extensions): 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 @@ def get_outputs (self): 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 @@ def build_extensions(self): 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 @@ def build_extension(self, ext): 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 @@ def swig_sources (self, sources, extension): 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 @@ def find_swig (self): 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 @@ def get_ext_filename (self, ext_name): 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 @@ def get_libraries (self, ext): # 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 @@ def get_libraries (self, ext): return ext.libraries + [pythonlib] else: return ext.libraries - -# class build_ext diff --git a/command/build_py.py b/command/build_py.py index 8f5609084c..454424f333 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 @@ def initialize_options (self): 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 @@ def finalize_options (self): # 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 @@ def run (self): 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 @@ def get_data_files (self): 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 @@ def find_data_files (self, package, src_dir): 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 @@ def build_package_data (self): 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 @@ def get_package_dir (self, package): 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 @@ def check_package (self, package, package_dir): # __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 @@ def find_package_modules (self, package, package_dir): 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 @@ def find_modules (self): ".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 @@ def find_modules (self): # 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 @@ def find_modules (self): 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 @@ def find_all_modules (self): 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 @@ def get_outputs (self, include_bytecode=1): 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 @@ def build_module (self, module, module_file, package): 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 @@ def build_packages (self): 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 @@ def byte_compile (self, files): # 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 511b82f999..176e6e1073 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 @@ # 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 @@ def finalize_options (self): 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 @@ def copy_scripts (self): 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 @@ def copy_scripts (self): 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 @@ def copy_scripts (self): 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 02189c531a..ae1d22c376 100644 --- a/command/clean.py +++ b/command/clean.py @@ -4,8 +4,6 @@ # 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.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 @@ def run(self): log.info("removing '%s'", self.build_base) except OSError: pass - -# class clean diff --git a/command/command_template b/command/command_template index 50bbab7b6e..6106819db8 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 04cfcde736..a601234218 100644 --- a/command/config.py +++ b/command/config.py @@ -9,21 +9,17 @@ 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 @@ def initialize_options (self): # 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 @@ def finalize_options (self): 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 @@ def _check_compiler (self): 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 @@ def _gen_temp_sourcefile (self, body, headers, lang): 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 @@ def _compile (self, body, headers, include_dirs, lang): 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 @@ def _link (self, body, 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 @@ def _clean (self, *filenames): # 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 @@ def try_cpp (self, body=None, headers=None, include_dirs=None, lang="c"): """ 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 @@ def search_cpp (self, pattern, body=None, 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 @@ def try_compile (self, body, headers=None, include_dirs=None, lang="c"): 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 @@ def try_link (self, body, 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 @@ def try_run (self, body, 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 @@ def try_run (self, body, # (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 @@ def check_func (self, func, calls it. 'libraries' and 'library_dirs' are used when linking. """ - self._check_compiler() body = [] if decl: @@ -327,10 +311,8 @@ def check_func (self, func, 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 @@ def check_lib (self, library, library_dirs=None, 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 @@ def check_header (self, header, include_dirs=None, 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 a6543ba6a2..29cda1ebb7 100644 --- a/command/install.py +++ b/command/install.py @@ -4,12 +4,9 @@ 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 @@ def initialize_options (self): # 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 @@ def finalize_options (self): 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 @@ def finalize_options (self): # 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 @@ def dump_dirs (self, msg): 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 @@ def finalize_unix (self): 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 @@ def finalize_unix (self): 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 @@ def finalize_unix (self): 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 @@ def finalize_other (self): # Windows and Mac OS for now 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 @@ def select_scheme (self, name): 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 @@ def _expand_attrs (self, attrs): 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 @@ def expand_dirs (self): '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 @@ def handle_extra_path (self): 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 @@ def handle_extra_path (self): 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 @@ def change_roots (self, *names): # -- 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 @@ def run (self): "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 @@ def create_path_file (self): # -- 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 @@ def get_outputs (self): 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 @@ def get_inputs (self): # -- 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 @@ def has_data (self): ('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 a95194426b..2fbac63de0 100644 --- a/command/install_data.py +++ b/command/install_data.py @@ -5,15 +5,13 @@ # 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 @@ def initialize_options (self): 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 @@ def run (self): (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 2bd1b04367..7114eaf068 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 @@ def run (self): (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 4efaf9c671..ac620fccb9 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 @@ # 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 @@ def initialize_options (self): 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 @@ def finalize_options (self): ) 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 @@ def run (self): 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 @@ def install (self): 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 @@ def byte_compile (self, files): # -- 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 @@ def _mutate_outputs (self, has_any, build_cmd, cmd_option, output_dir): 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 @@ def _bytecode_filenames (self, py_filenames): # -- 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 @@ def get_outputs (self): 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 @@ def get_inputs (self): inputs.extend(build_ext.get_outputs()) return inputs - -# class install_lib diff --git a/command/install_scripts.py b/command/install_scripts.py index da2da358ba..ea8d5aa654 100644 --- a/command/install_scripts.py +++ b/command/install_scripts.py @@ -5,8 +5,6 @@ # contributed by Bastian Kleineidam -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import os @@ -14,7 +12,8 @@ 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 @@ def finalize_options (self): ('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 @@ def run (self): 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 8e1c066023..b1c76486ed 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 @@ def initialize_options (self): 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 @@ def finalize_options (self): 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 @@ def run (self): # 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 @@ def check_metadata (self): "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 @@ def get_file_list (self): # 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 @@ def get_file_list (self): 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 @@ def add_defaults (self): 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 @@ def add_defaults (self): 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 @@ def read_template (self): 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 @@ def prune_file_list (self): 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 @@ def write_manifest (self): (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 @@ def read_manifest (self): 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 @@ def make_release_tree (self, base_dir, files): 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 @@ def make_distribution (self): 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 1ca2fb9052..b49acd123a 100644 --- a/command/upload.py +++ b/command/upload.py @@ -170,7 +170,7 @@ def upload_file(self, command, pyversion, filename): 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 d60998240f..0490e6378b 100644 --- a/core.py +++ b/core.py @@ -6,12 +6,9 @@ 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 @@ class found in 'cmdclass' is used in place of the default, which is _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 @@ class found in 'cmdclass' is used in place of the default, which is 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 @@ class found in 'cmdclass' is used in place of the default, which is 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 @@ class found in 'cmdclass' is used in place of the default, which is 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 fae6848165..bec72ca3f1 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -45,8 +45,6 @@ # * 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 @@ def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): 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 @@ def object_filenames (self, # 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 b67139c7d4..2886744402 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 c139c852e4..a9d589a256 100644 --- a/dep_util.py +++ b/dep_util.py @@ -4,8 +4,6 @@ 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 7dc1205c49..30e352db42 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 8f614765ef..974ee5163b 100644 --- a/dist.py +++ b/dist.py @@ -4,8 +4,6 @@ being built/installed/distributed. """ -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import sys, os, re @@ -264,8 +262,6 @@ def __init__ (self, attrs=None): self.finalize_options() - # __init__ () - def get_option_dict (self, command): """Get the option dictionary for a given command. If that @@ -305,8 +301,6 @@ def dump_option_dicts (self, header=None, commands=None, indent=""): for line in out.split("\n"): print(indent + " " + line) - # dump_option_dicts () - # -- Config file finding/parsing methods --------------------------- @@ -353,8 +347,6 @@ def find_config_files (self): return files - # find_config_files () - def parse_config_files (self, filenames=None): @@ -397,9 +389,7 @@ def parse_config_files (self, filenames=None): 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 @@ def parse_command_line (self): # 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 @@ def _parse_command_opts (self, parser, 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 + 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 @@ def _parse_command_opts (self, parser, args): 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 @@ def _parse_command_opts (self, parser, args): 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 @@ def _show_help (self, 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 @@ def handle_display_options (self, option_order): 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 @@ def print_command_list (self, commands, header, max_length): 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 @@ def print_commands (self): descriptions come from the command class attribute 'description'. """ - import distutils.command std_commands = distutils.command.__all__ is_std = {} @@ -760,8 +737,6 @@ def print_commands (self): "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 @@ def get_command_list (self): """ # 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 @@ def get_command_class (self, 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) + 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 @@ def _set_command_options (self, command_obj, option_dict=None): 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 @@ def write_pkg_info (self, base_dir): 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 @@ def set_obsoletes(self, value): 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 f4b90dcd00..d9ee82d58a 100644 --- a/emxccompiler.py +++ b/emxccompiler.py @@ -80,13 +80,13 @@ def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): 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 @@ def object_filenames (self, # 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 e72221bdba..963d83377c 100644 --- a/errors.py +++ b/errors.py @@ -8,8 +8,6 @@ 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 43b0d3fd00..7f5954e45c 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 @@ def __init__ (self, name, sources, # 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 82e1f4d1dc..5434334e79 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -8,12 +8,9 @@ * 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 @@ def __init__ (self, option_table=None): # 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 @@ def set_negative_aliases (self, negative_alias): 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 @@ def _grok_option_table (self): 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 @@ def _grok_option_table (self): 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 @@ def getopt (self, args=None, object=None): 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 @@ def getopt (self, args=None, object=None): 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 @@ def getopt (self, args=None, object=None): 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 @@ def generate_help (self, header=None): 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 c225ad3172..e29e90e6d7 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 @@ '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 cc48e48e58..8f801073d5 100644 --- a/filelist.py +++ b/filelist.py @@ -4,20 +4,16 @@ 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 @@ def debug_print (self, msg): # -- 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 @@ def sort (self): # -- 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 @@ def remove_duplicates (self): # -- "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 @@ def _parse_template_line (self, line): 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 @@ def process_template_line (self, line): 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 @@ def process_template_line (self, line): (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 @@ def process_template_line (self, line): 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 @@ def include_pattern (self, pattern, 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 @@ def include_pattern (self, pattern, 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 @@ def exclude_pattern (self, pattern, '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 @@ def exclude_pattern (self, pattern, 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 e4959d6492..97319a07aa 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 07c76f1838..f5b67049ef 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -8,8 +8,6 @@ # 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 @@ 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 @@ 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 @@ 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 @@ def load_macros(self, version): 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 @@ def initialize(self): 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 @@ def initialize(self): # -- 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 @@ def object_filenames (self, 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 @@ def compile(self, sources, 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 @@ def compile(self, sources, # 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 @@ def link (self, 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 @@ def find_library_file (self, dirs, lib, debug=0): # 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 @@ def find_exe(self, exe): 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 @@ def get_msvc_paths(self, path, platform='x86'): 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 662046ae22..25d48ae866 100644 --- a/mwerkscompiler.py +++ b/mwerkscompiler.py @@ -4,12 +4,9 @@ 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 @@ def link (self, # 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 c70c10bf7c..0aee2bc3eb 100644 --- a/spawn.py +++ b/spawn.py @@ -6,19 +6,13 @@ 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 c40c2e7425..70a279938c 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -27,11 +27,7 @@ # 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 3f6a220dcc..db054fd270 100644 --- a/text_file.py +++ b/text_file.py @@ -10,7 +10,6 @@ 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 @@ def __init__ (self, filename=None, file=None, **options): # '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 @@ def warn (self, msg, line=None): 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 @@ def readline (self): 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 @@ def readline (self): 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 @@ def readline (self): # 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 @@ def readline (self): # # 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 @@ def readline (self): # 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 @@ def unreadline (self, line): 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 @@ def unreadline (self, line): # "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 @@ def test_input (count, description, file, expected_result): 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 d07ae1e358..91d0dffd76 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 @@ def preprocess(self, source, 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 @@ def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): 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 @@ def create_static_lib(self, objects, output_libname, 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 @@ def link(self, target_desc, objects, 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 @@ def link(self, target_desc, objects, 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 @@ def link(self, target_desc, objects, 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 9aa857052a..22a8ba23e8 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 (match, local_vars=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 @@ def byte_compile (py_files, 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 96b6552197..f71b2f6ce3 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) From bf9ae3584f023df6f04f88e17cfe987e004de033 Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Thu, 30 Aug 2007 05:35:41 +0000 Subject: [PATCH 1160/2594] 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 22a8ba23e8..e0ae2e5ea2 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 From b7375849d526bedd70683539901013baaf9edcea Mon Sep 17 00:00:00 2001 From: Collin Winter Date: Thu, 30 Aug 2007 18:46:25 +0000 Subject: [PATCH 1161/2594] 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 f5b67049ef..d239057bac 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -442,7 +442,7 @@ def create_static_lib(self, 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: From 70ba60503c5d76dd223ae450b64f757ade0b9b2c Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Fri, 31 Aug 2007 10:37:15 +0000 Subject: [PATCH 1162/2594] 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 29cda1ebb7..ffd68db269 100644 --- a/command/install.py +++ b/command/install.py @@ -348,11 +348,10 @@ def dump_dirs(self, msg): 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 5434334e79..15cbdd7162 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -26,7 +26,7 @@ # 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 @@ 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) + 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: From d597eb5b512b06572e8d5857ed0846c9f431cc6d Mon Sep 17 00:00:00 2001 From: Collin Winter Date: Sat, 1 Sep 2007 20:37:22 +0000 Subject: [PATCH 1163/2594] 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 ffd68db269..f1ebcbf052 100644 --- a/command/install.py +++ b/command/install.py @@ -347,7 +347,7 @@ def dump_dirs(self, msg): 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 91081ddb1b..d123d327de 100644 --- a/command/register.py +++ b/command/register.py @@ -125,7 +125,7 @@ def send_metadata(self): # 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 @@ def send_metadata(self): 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 b49acd123a..34b6692d04 100644 --- a/command/upload.py +++ b/command/upload.py @@ -45,7 +45,7 @@ def finalize_options(self): 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) From 69af9b586f1c9446c371b23a1c5d6e6a745f848e Mon Sep 17 00:00:00 2001 From: Skip Montanaro Date: Sat, 8 Sep 2007 00:34:17 +0000 Subject: [PATCH 1164/2594] 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 c139c852e4..2c6d7927f0 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 From be6286d3fa93348073baeaa554fe14dea328e7ce Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Sat, 8 Sep 2007 17:39:28 +0000 Subject: [PATCH 1165/2594] 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 a9d589a256..07b3549c6f 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 From a68bb215508d8be88a6391cc760e2a203e1b526b Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Wed, 12 Sep 2007 19:29:28 +0000 Subject: [PATCH 1166/2594] #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 bda4480ca5..b4810c3eac 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -104,7 +104,8 @@ def copy_scripts (self): 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() From 02aa5e870e7910a62c992c30eaced5ef448d43fa Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Wed, 19 Sep 2007 03:06:30 +0000 Subject: [PATCH 1167/2594] 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 176e6e1073..227bb9c7a1 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -102,7 +102,8 @@ def copy_scripts(self): 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() From 4cb4040d3e7687c6e5ec99a6a52217ca9091db9a Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 25 Sep 2007 21:48:09 +0000 Subject: [PATCH 1168/2594] 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 15cbdd7162..b3231c3de7 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: From 27cd6610b468d4cc03dea2c22d74aa3c281d3a66 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Mon, 15 Oct 2007 01:27:53 +0000 Subject: [PATCH 1169/2594] 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 974ee5163b..631df48cd0 100644 --- a/dist.py +++ b/dist.py @@ -280,8 +280,7 @@ 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() + commands = sorted(self.command_options.keys()) if header is not None: print(indent + header) From d593487053de01cd756914a5cb5cc6b058c99c03 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Tue, 16 Oct 2007 18:12:55 +0000 Subject: [PATCH 1170/2594] 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 c33e5ab440..f4edb7c6a6 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -154,7 +154,7 @@ class (via the 'executables' class attribute), but most will have: 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 @@ def _check_macro_definitions(self, definitions): 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 @@ def _setup_compile(self, outdir, macros, incdirs, sources, depends, """ 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 @@ def _fix_compile_args(self, output_dir, macros, include_dirs): """ 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 @@ def _fix_object_args(self, objects, output_dir): 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 66940f7799..bd560a661c 100644 --- a/cmd.py +++ b/cmd.py @@ -213,7 +213,7 @@ def _ensure_stringlike(self, option, what, default=None): 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 @@ def ensure_string_list(self, option): 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 @@ def make_file(self, infiles, outfile, func, args, # 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 5f95207ca9..34f4983689 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -86,7 +86,7 @@ def finalize_options(self): 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 @@ def check_library_list(self, libraries): 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 a439c49ac6..ddf8f723f8 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -133,7 +133,7 @@ def finalize_options(self): 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 @@ def finalize_options(self): 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 @@ def finalize_options(self): 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 @@ def check_extensions_list(self, extensions): "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 454424f333..63ced4b470 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -325,7 +325,7 @@ def get_outputs(self, include_bytecode=1): 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 a601234218..34f91884d8 100644 --- a/command/config.py +++ b/command/config.py @@ -69,17 +69,17 @@ def initialize_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): + 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 @@ def search_cpp(self, pattern, body=None, headers=None, 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 f1ebcbf052..b768663c69 100644 --- a/command/install.py +++ b/command/install.py @@ -449,7 +449,7 @@ def handle_extra_path(self): 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 2fbac63de0..06a70b4ad4 100644 --- a/command/install_data.py +++ b/command/install_data.py @@ -45,7 +45,7 @@ def finalize_options(self): 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 30e352db42..1f0d49c25f 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 631df48cd0..ade2ab795c 100644 --- a/dist.py +++ b/dist.py @@ -580,13 +580,13 @@ def finalize_options (self): 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 @@ def _set_command_options (self, command_obj, option_dict=None): 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 7f5954e45c..b271816844 100644 --- a/extension.py +++ b/extension.py @@ -102,9 +102,9 @@ def __init__(self, name, sources, 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 b3231c3de7..72441fb43d 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -154,12 +154,12 @@ def _grok_option_table(self): 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 8f801073d5..6506c30e6b 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 91d0dffd76..ee975e15fa 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -211,7 +211,7 @@ def link(self, target_desc, objects, 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) From 7fba7d60bbce7a9e20d887716e9b87b9496180f5 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Sat, 17 Nov 2007 11:46:54 +0000 Subject: [PATCH 1171/2594] 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 d239057bac..6bf969f9d2 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 From 200d0eac23788226d0326f42258ced7ebf2431b2 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Thu, 22 Nov 2007 10:14:26 +0000 Subject: [PATCH 1172/2594] 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 cfcc6a951e..731ee988b5 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 From 83cd6eeec1a6e2141517e7944b38efb712536537 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Mon, 3 Dec 2007 13:47:29 +0000 Subject: [PATCH 1173/2594] 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 12d4083743..ecfa177d76 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -17,6 +17,10 @@ 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 @@ def finalize_options (self): # 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 4fd23e6dc6..4ac11eb4b1 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -56,6 +56,29 @@ 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 @@ def __init__ (self, verbose=0, dry_run=0, force=0): 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 @@ def __init__ (self, 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 0000000000..a6cff2c04e --- /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 29a9020956..fa123462ec 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -650,3 +650,11 @@ def set_path_env_var(self, name): 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 From c9618bff1c953979a37bc8a27bd2b4cd435b07b9 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Mon, 3 Dec 2007 19:47:54 +0000 Subject: [PATCH 1174/2594] 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 0000000000..a6cff2c04e --- /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 From 57ce0a030801c3b95c04c514bad06f556d5a9933 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Mon, 3 Dec 2007 19:53:57 +0000 Subject: [PATCH 1175/2594] 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 a6cff2c04e..0000000000 --- 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 From 04bca8195e91cd3520b869da61781cb5649d4164 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Mon, 3 Dec 2007 21:02:03 +0000 Subject: [PATCH 1176/2594] 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 ddf8f723f8..6d42278e40 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -14,6 +14,10 @@ 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 @@ def finalize_options(self): # 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 bec72ca3f1..488752300b 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -54,6 +54,29 @@ 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 @@ def __init__ (self, verbose=0, dry_run=0, force=0): 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 @@ def __init__ (self, 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 0000000000..a6cff2c04e --- /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 6bf969f9d2..3b4e9c9d2d 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -632,3 +632,11 @@ def set_path_env_var(self, name): 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 From c03780a3a80420dab6fb7ffa8471e135a7dec9a2 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Wed, 5 Dec 2007 20:10:38 +0000 Subject: [PATCH 1177/2594] 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 a6cff2c04e..828d7fbf7a 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 From 7ea9ff425c9efeff99694a5e4f8d1123f76133d1 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Wed, 5 Dec 2007 20:18:38 +0000 Subject: [PATCH 1178/2594] 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 a6cff2c04e..828d7fbf7a 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 From ae50fdd2c427c6dbdb7990bfae890534240d5169 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Thu, 6 Dec 2007 13:15:13 +0000 Subject: [PATCH 1179/2594] 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 0cfafab99b..2ea7c78b83 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -22,16 +22,17 @@ 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 0000000000..8337b0d25f --- /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 From d1f35e69fb4d6a4e274369771d5d9574e45ecc59 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Thu, 6 Dec 2007 13:55:01 +0000 Subject: [PATCH 1180/2594] 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 8337b0d25f..ef7c38bf78 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -15,7 +15,8 @@ def test_get_config_h_filename(self): 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): From 115bfed7f4bf7f00580c68cdc64e49e36434e7bb Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Sat, 8 Dec 2007 15:33:56 +0000 Subject: [PATCH 1181/2594] 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 70a279938c..191f3d1d18 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -22,13 +22,17 @@ 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 From 329322869d65d079b3bdb40ecba5ecee0bb98d27 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Sat, 8 Dec 2007 15:34:59 +0000 Subject: [PATCH 1182/2594] 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 0000000000..ef7c38bf78 --- /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 From ed6b063cc4b7549d790cc4f83dcb1bf5fd4f93f6 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Thu, 13 Dec 2007 19:23:16 +0000 Subject: [PATCH 1183/2594] 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 2ea7c78b83..aead1a19ba 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -32,7 +32,7 @@ # 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(): From ebbd3f714cc3fb8da8fbc8d2f4daf42fffbef1b9 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Fri, 14 Dec 2007 01:24:44 +0000 Subject: [PATCH 1184/2594] 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 191f3d1d18..c450cd5159 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -32,7 +32,7 @@ # 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, From 4e9153cd047c24fc7c7c595885444a4a1e599b8b Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Fri, 14 Dec 2007 23:42:36 +0000 Subject: [PATCH 1185/2594] 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 aead1a19ba..32b165ffd1 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -31,8 +31,10 @@ # 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 ef7c38bf78..770b7c376f 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -15,7 +15,7 @@ def test_get_config_h_filename(self): 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? From 0f89abbb94e9b4c48d3a5356c564b31862602227 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Sat, 15 Dec 2007 01:27:15 +0000 Subject: [PATCH 1186/2594] 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 c450cd5159..ba89c3b864 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -31,8 +31,10 @@ # 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 ef7c38bf78..770b7c376f 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -15,7 +15,7 @@ def test_get_config_h_filename(self): 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? From c8533b41bab447f51d5e375f239163fcaafdd633 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Mon, 31 Dec 2007 14:47:07 +0000 Subject: [PATCH 1187/2594] Added wininst-9.0.exe executable for VS 2008 Integrated bdist_wininst into PCBuild9 directory --- command/bdist_wininst.py | 2 +- command/{wininst-6.exe => wininst-6.0.exe} | Bin command/{wininst-8.exe => wininst-8.0.exe} | Bin command/wininst-9.0.exe | Bin 0 -> 65536 bytes 4 files changed, 1 insertion(+), 1 deletion(-) rename command/{wininst-6.exe => wininst-6.0.exe} (100%) rename command/{wininst-8.exe => wininst-8.0.exe} (100%) create mode 100644 command/wininst-9.0.exe diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 49afca0472..b0691fb823 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -323,6 +323,6 @@ def get_exe_bytes (self): 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.exe b/command/wininst-6.0.exe similarity index 100% rename from command/wininst-6.exe rename to command/wininst-6.0.exe diff --git a/command/wininst-8.exe b/command/wininst-8.0.exe similarity index 100% rename from command/wininst-8.exe rename to command/wininst-8.0.exe diff --git a/command/wininst-9.0.exe b/command/wininst-9.0.exe new file mode 100644 index 0000000000000000000000000000000000000000..5e0144c92b58ea750211c540d15667e0b1d1a356 GIT binary patch literal 65536 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 Date: Mon, 31 Dec 2007 16:14:33 +0000 Subject: [PATCH 1188/2594] 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.exe => wininst-6.0.exe} | Bin command/{wininst-8.exe => wininst-8.0.exe} | Bin command/wininst-9.0.exe | Bin 0 -> 65536 bytes 4 files changed, 1 insertion(+), 1 deletion(-) rename command/{wininst-6.exe => wininst-6.0.exe} (100%) rename command/{wininst-8.exe => wininst-8.0.exe} (100%) create mode 100644 command/wininst-9.0.exe diff --git a/command/bdist_wininst.py b/command/bdist_wininst.py index 249b74c1bb..2d75a38f74 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -304,5 +304,5 @@ def get_exe_bytes(self): 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.exe b/command/wininst-6.0.exe similarity index 100% rename from command/wininst-6.exe rename to command/wininst-6.0.exe diff --git a/command/wininst-8.exe b/command/wininst-8.0.exe similarity index 100% rename from command/wininst-8.exe rename to command/wininst-8.0.exe diff --git a/command/wininst-9.0.exe b/command/wininst-9.0.exe new file mode 100644 index 0000000000000000000000000000000000000000..5e0144c92b58ea750211c540d15667e0b1d1a356 GIT binary patch literal 65536 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 Date: Tue, 1 Jan 2008 14:37:32 +0000 Subject: [PATCH 1189/2594] 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 32b165ffd1..3cd647becc 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -27,6 +27,10 @@ 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 From fdbce281befb4d8d7bf3c90e9a37f33164da71f7 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Tue, 1 Jan 2008 14:42:15 +0000 Subject: [PATCH 1190/2594] 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 ba89c3b864..4d790ccbe5 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -27,6 +27,10 @@ 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 From 30e73c78342c8397efc90d0c9dfbd9f1a7aebf8c Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 2 Jan 2008 18:59:36 +0000 Subject: [PATCH 1191/2594] 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 bda4480ca5..d26fe94b7e 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -111,7 +111,8 @@ def copy_scripts (self): if f: f.close() else: - f.close() + if f: + f.close() self.copy_file(script, outfile) if os.name == 'posix': From 667a1ea083db9823c816fd690d6d61ca0d931710 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 2 Jan 2008 19:00:46 +0000 Subject: [PATCH 1192/2594] 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 b4810c3eac..6af363b36d 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -112,7 +112,8 @@ def copy_scripts (self): if f: f.close() else: - f.close() + if f: + f.close() self.copy_file(script, outfile) if os.name == 'posix': From 2f672dbe9907466088c0283b1ad4bc23b23107b9 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Thu, 3 Jan 2008 23:01:04 +0000 Subject: [PATCH 1193/2594] 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 227bb9c7a1..7b44264cc2 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -110,7 +110,8 @@ def copy_scripts(self): if f: f.close() else: - f.close() + if f: + f.close() self.copy_file(script, outfile) if os.name == 'posix': From 8b891e185114ca8306b56c80aef8866034f179c8 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 6 Jan 2008 21:13:42 +0000 Subject: [PATCH 1194/2594] 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 e29e90e6d7..69190601fb 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 0aee2bc3eb..4c536d28af 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" From 60d10b8328e3c77e3e722bb4015477c9ac7854dd Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 6 Jan 2008 21:41:49 +0000 Subject: [PATCH 1195/2594] Missed one because of indirection. --- util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util.py b/util.py index e0ae2e5ea2..917f1d0b93 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 From c39e9fa5952a01de7527da5fc1ec0d04451e300e Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Mon, 21 Jan 2008 17:42:40 +0000 Subject: [PATCH 1196/2594] #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 9ae0a292a3..bca031f730 100644 --- a/command/build.py +++ b/command/build.py @@ -69,6 +69,12 @@ 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 -- From 32ff6ab5969de182ade4a74e0159e28e301d79d7 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Mon, 21 Jan 2008 20:36:10 +0000 Subject: [PATCH 1197/2594] 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 1f2ce06205..4fe95b0cfb 100644 --- a/command/build.py +++ b/command/build.py @@ -66,6 +66,12 @@ def initialize_options(self): 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 -- 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: [PATCH 1198/2594] 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 86ad44fe3a..8c954c2238 100644 --- a/__init__.py +++ b/__init__.py @@ -20,4 +20,4 @@ # 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" From 2f0340a9ac492d58a9220fe9eef0daccdf997b56 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Sun, 3 Feb 2008 14:34:18 +0000 Subject: [PATCH 1199/2594] 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 ecfa177d76..29d5668c61 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -182,13 +182,13 @@ def finalize_options (self): 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 From e5260241bc5e4e91531a72a9efd5b0f46ad8704e Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Sun, 3 Feb 2008 16:51:08 +0000 Subject: [PATCH 1200/2594] 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 f71c3adb14..0c3d939dc3 100644 --- a/__init__.py +++ b/__init__.py @@ -18,4 +18,4 @@ # 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 6d42278e40..ff84bca7a0 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -178,13 +178,13 @@ def finalize_options(self): 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 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: [PATCH 1201/2594] 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 3cd647becc..1aaaa28fbf 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -37,8 +37,12 @@ # 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(): From 71947cc1efed4a4f2ce1980ac465d70ea2542f29 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Wed, 6 Feb 2008 14:31:34 +0000 Subject: [PATCH 1202/2594] 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 4d790ccbe5..39216a532f 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -37,8 +37,12 @@ # 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, From b1165a0f3caf19626c96f1b397bd52a9d1c080f4 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 21 Feb 2008 14:23:38 +0000 Subject: [PATCH 1203/2594] 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 3dfe6f21a7..a06b1953b4 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -383,6 +383,7 @@ def read_manifest (self): if line[-1] == '\n': line = line[0:-1] self.filelist.append(line) + manifest.close() # read_manifest () From 36bf11e144f03aaf5cce1d8cd8efcdc1e3a53b35 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Thu, 21 Feb 2008 18:18:37 +0000 Subject: [PATCH 1204/2594] 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 6aa5e635d7..a9c6a5b488 100644 --- a/archive_util.py +++ b/archive_util.py @@ -124,7 +124,7 @@ def visit (z, dirname, names): 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 1349abeb65..0ed9a40a35 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -159,7 +159,7 @@ class (via the 'executables' class attribute), but most will have: # 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 29d5668c61..3042fe0223 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -362,7 +362,7 @@ def check_extensions_list (self, extensions): # 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 453151d08b..c62121023d 100644 --- a/command/install.py +++ b/command/install.py @@ -352,7 +352,7 @@ def dump_dirs (self, msg): 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 200e61e240..d1a9100de4 100644 --- a/command/register.py +++ b/command/register.py @@ -120,7 +120,7 @@ def send_metadata(self): # 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 @@ def send_metadata(self): 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 7461e5f453..301a159590 100644 --- a/command/upload.py +++ b/command/upload.py @@ -46,7 +46,7 @@ def finalize_options(self): 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 c9c6f037a7..c40dd0a7aa 100644 --- a/core.py +++ b/core.py @@ -101,9 +101,9 @@ class found in 'cmdclass' is used in place of the default, which is 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 @@ class found in 'cmdclass' is used in place of the default, which is 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 43994db3ff..77f253255f 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 ff49886d97..d098cb9713 100644 --- a/dist.py +++ b/dist.py @@ -239,7 +239,7 @@ def __init__ (self, attrs=None): 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 @@ def find_config_files (self): 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 @@ def parse_config_files (self, filenames=None): # 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 @@ def _set_command_options (self, command_obj, option_dict=None): 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 218ed73f98..31cf0c5c3b 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -97,7 +97,7 @@ def set_option_table (self, 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): + if long_option in self.option_index: raise DistutilsGetoptError, \ "option conflict: already an option '%s'" % long_option else: @@ -109,7 +109,7 @@ def add_option (self, long_option, short_option=None, help_string=None): 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 @@ def get_attr_name (self, long_option): 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 fa123462ec..f3acb53477 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -252,7 +252,7 @@ def __init__ (self, verbose=0, dry_run=0, force=0): 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 1aaaa28fbf..506cf385b6 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 67efd65e36..ff2878de1b 100644 --- a/text_file.py +++ b/text_file.py @@ -89,7 +89,7 @@ def __init__ (self, filename=None, file=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 in options: setattr (self, opt, options[opt]) else: @@ -97,7 +97,7 @@ def __init__ (self, filename=None, file=None, **options): # 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 8979634e4b..deb9a0a0f1 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] From a5c15c1aba945408f59b87959d31be1b74a2cb2a Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Fri, 22 Feb 2008 16:37:40 +0000 Subject: [PATCH 1205/2594] 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 f4edb7c6a6..1e09123afc 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -147,11 +147,11 @@ class (via the 'executables' class attribute), but most will have: # 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 b1c76486ed..8289925785 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -357,6 +357,7 @@ def read_manifest(self): 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 From c3981d56adcbfdd1e06f1b4fabc554e470ce4646 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Sat, 23 Feb 2008 17:40:11 +0000 Subject: [PATCH 1206/2594] 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 ca524a5b88..d4fc53366f 100644 --- a/bcppcompiler.py +++ b/bcppcompiler.py @@ -16,7 +16,7 @@ __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 23c25a55a8..d6897d2d09 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -7,7 +7,7 @@ __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 ccba00955a..f740c468e7 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -11,7 +11,7 @@ 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 a1c0df4bc5..a4014525b7 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 6f0e0d881c..9aa7030049 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -8,7 +8,6 @@ __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 b9f39bae79..be6d2c5b4d 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -6,7 +6,7 @@ __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 6af363b36d..104be0b349 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -6,7 +6,7 @@ __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 c62121023d..0d39b91cc3 100644 --- a/command/install.py +++ b/command/install.py @@ -17,7 +17,6 @@ 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 2bd1b04367..4895240a4c 100644 --- a/command/install_headers.py +++ b/command/install_headers.py @@ -7,7 +7,6 @@ __revision__ = "$Id$" -import os from distutils.core import Command diff --git a/command/install_lib.py b/command/install_lib.py index 08ff543449..81107a85cb 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 d1a9100de4..5c588effd3 100644 --- a/command/register.py +++ b/command/register.py @@ -7,7 +7,7 @@ __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 a06b1953b4..9b37f78982 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -6,7 +6,7 @@ __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 43f9aaaf5b..6d27cce64f 100644 --- a/filelist.py +++ b/filelist.py @@ -11,7 +11,6 @@ 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 4d2a7cdf1a..5ae79332c0 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 770b7c376f..d56f7e9bc5 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 75e8a5316e..dc759ee8f5 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -17,7 +17,6 @@ import os, sys from types import StringType, NoneType -from copy import copy from distutils import sysconfig from distutils.dep_util import newer From 6b8ac4a8066f127f3e2009e760eceda2354e3db1 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Sat, 23 Feb 2008 18:30:17 +0000 Subject: [PATCH 1207/2594] 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 1ab76c5981..c5e5cd2571 100644 --- a/bcppcompiler.py +++ b/bcppcompiler.py @@ -14,7 +14,7 @@ __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 f89961769c..2d39922672 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -9,7 +9,7 @@ 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 72f74f9c53..83efb8e043 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -6,7 +6,6 @@ __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 7b44264cc2..3ac5b0c0be 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -4,7 +4,7 @@ __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 b768663c69..e4ee680271 100644 --- a/command/install.py +++ b/command/install.py @@ -14,7 +14,6 @@ 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 7114eaf068..346daaad02 100644 --- a/command/install_headers.py +++ b/command/install_headers.py @@ -5,7 +5,6 @@ __revision__ = "$Id$" -import os from distutils.core import Command diff --git a/command/register.py b/command/register.py index d123d327de..40d9f20d1a 100644 --- a/command/register.py +++ b/command/register.py @@ -7,7 +7,7 @@ __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 6506c30e6b..8eab0a95bf 100644 --- a/filelist.py +++ b/filelist.py @@ -8,7 +8,6 @@ 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 23506b5dae..91acf45805 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 770b7c376f..d56f7e9bc5 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 ee975e15fa..25a042d075 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -16,7 +16,6 @@ __revision__ = "$Id$" import os, sys -from copy import copy from distutils import sysconfig from distutils.dep_util import newer From 7eaf2c9799ea499603b40dbf7324c8adb60d8fad Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Thu, 6 Mar 2008 06:47:18 +0000 Subject: [PATCH 1208/2594] #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 9b37f78982..961256c3ac 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -347,14 +347,14 @@ def prune_file_list (self): * 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): 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: [PATCH 1209/2594] 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 3dfe6f21a7..b724a3b7d4 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -347,14 +347,14 @@ def prune_file_list (self): * 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): From b41f4b8c277041837cda7062923b4cde662d5ebb Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Sun, 16 Mar 2008 00:07:10 +0000 Subject: [PATCH 1210/2594] 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 8289925785..b29fc64eca 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -325,14 +325,14 @@ def prune_file_list(self): * 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 From 7382bd51a39f785fee596182768da1b8deecf6c7 Mon Sep 17 00:00:00 2001 From: Trent Nelson Date: Wed, 19 Mar 2008 06:28:24 +0000 Subject: [PATCH 1211/2594] 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 506cf385b6..27a770b194 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -23,7 +23,8 @@ 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 "\\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 From 227cb3d385365b0716558d21804dbcf8d36b86ba Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Wed, 19 Mar 2008 21:50:51 +0000 Subject: [PATCH 1212/2594] 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 39216a532f..d2b2c9a347 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -23,7 +23,8 @@ 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 "\\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 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: [PATCH 1213/2594] 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 63ced4b470..f39ffb878e 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -383,3 +383,27 @@ def byte_compile(self, files): 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 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: [PATCH 1214/2594] 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 f39ffb878e..09f6d2331e 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -386,9 +386,17 @@ def byte_compile(self, files): 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 Options: 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]: From 3405aa3e09e63da9af1a4ca90c4ae1c5bf9dcc7d Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Wed, 2 Apr 2008 23:33:27 +0000 Subject: [PATCH 1215/2594] 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 0c3d939dc3..b583e397e2 100644 --- a/__init__.py +++ b/__init__.py @@ -18,4 +18,7 @@ # 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-- From b2c84945a0906325790271d2d8108fbe81483b70 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Thu, 3 Apr 2008 04:10:02 +0000 Subject: [PATCH 1216/2594] Updating for 2.6a2 --- __init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 8c954c2238..d0dd03a3c5 100644 --- a/__init__.py +++ b/__init__.py @@ -20,4 +20,7 @@ # 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-- From fb68e5c8dd08660e12a945ace7487a7b2b4daff1 Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Fri, 4 Apr 2008 05:41:30 +0000 Subject: [PATCH 1217/2594] - 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 c40dd0a7aa..640d6b5955 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 0000000000..6de58d9284 --- /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") From 2e06d2e918a1f2d6e7c8f87853dee6f04e86d6d9 Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Fri, 4 Apr 2008 11:31:14 +0000 Subject: [PATCH 1218/2594] 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 640d6b5955..32b9026999 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 6de58d9284..4356c21295 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 @@ 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(): From 5ad3f56a92652c3996fb97ab7d923ca7e3647fe5 Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Fri, 4 Apr 2008 11:38:51 +0000 Subject: [PATCH 1219/2594] 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 4356c21295..55d2d5df7e 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -69,7 +69,6 @@ def test_run_setup_uses_current_dir(self): 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) From dc94cc0477c3ef464ffc9cd41fac155898e72402 Mon Sep 17 00:00:00 2001 From: Neal Norwitz Date: Sat, 5 Apr 2008 04:47:45 +0000 Subject: [PATCH 1220/2594] 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 0490e6378b..a4c5e18033 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 0000000000..8e274dd40d --- /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") From bbe02c7a83e5792122e71937f50787a18245f27b Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Sat, 5 Apr 2008 23:39:15 +0000 Subject: [PATCH 1221/2594] 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 d56f7e9bc5..aa1187e77b 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -19,7 +19,27 @@ def test_get_python_lib(self): # 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) From 6b17a395e92863f9bb6e93e067d487ed42c0de9b Mon Sep 17 00:00:00 2001 From: Thomas Wouters Date: Mon, 7 Apr 2008 00:25:59 +0000 Subject: [PATCH 1222/2594] 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 d56f7e9bc5..aa1187e77b 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -19,7 +19,27 @@ def test_get_python_lib(self): # 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) From 9351875b842427a4cf795d32ecec02dbd992cff7 Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Mon, 7 Apr 2008 01:53:39 +0000 Subject: [PATCH 1223/2594] 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 d6897d2d09..ca3da74c06 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -97,7 +97,10 @@ def initialize_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() + 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 @@ def finalize_options (self): # 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 a4014525b7..f94d9579d2 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -9,11 +9,11 @@ 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 @@ def finalize_options (self): 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 @@ def run (self): 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 @@ def add_ui(self): 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 b0691fb823..02542afbd3 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 @@ def finalize_options (self): " 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 @@ def run (self): 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 @@ def run (self): 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 @@ def get_installer_filename(self, fullname): # 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 @@ def get_exe_bytes (self): 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 @@ def get_exe_bytes (self): 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 bca031f730..7462e9348f 100644 --- a/command/build.py +++ b/command/build.py @@ -8,6 +8,7 @@ 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 @@ def initialize_options (self): 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 3042fe0223..bf5ad7e00b 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -15,6 +15,7 @@ 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 @@ def finalize_options (self): ('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 @@ def finalize_options (self): # 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 @@ def finalize_options (self): # 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 @@ def run (self): 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 0d39b91cc3..33a1212b0a 100644 --- a/command/install.py +++ b/command/install.py @@ -16,6 +16,7 @@ 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 @@ def run (self): # 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 0000000000000000000000000000000000000000..c99ede4b3fcebe39b1a1e33283307e12c42b7b2e GIT binary patch literal 76288 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 INt;an1b{yP$^ZZW literal 0 HcmV?d00001 diff --git a/msvc9compiler.py b/msvc9compiler.py index 828d7fbf7a..8b1cf9a910 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -22,6 +22,7 @@ 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 @@ 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 @@ def initialize(self): 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 f3acb53477..30a9fc6f22 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -656,5 +656,5 @@ def set_path_env_var(self, name): 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 deb9a0a0f1..69d90cffb8 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 From f39783d33eeae201072044971535a43415e3e78f Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Wed, 9 Apr 2008 08:37:03 +0000 Subject: [PATCH 1224/2594] 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 69c1b28ff2..e3b047c66f 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -91,7 +91,10 @@ def initialize_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() + 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 d313a5082f..aab89cd609 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -9,11 +9,11 @@ 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 @@ def finalize_options(self): 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 @@ def run(self): 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 @@ def add_ui(self): 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 2d75a38f74..bf8d022ae8 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 @@ def finalize_options(self): " 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 @@ def run(self): 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 @@ def run(self): 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 @@ def get_installer_filename(self, fullname): # 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 @@ def get_exe_bytes(self): 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 @@ def get_exe_bytes(self): 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 4fe95b0cfb..9c2667cfd2 100644 --- a/command/build.py +++ b/command/build.py @@ -6,6 +6,7 @@ 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 @@ def initialize_options(self): 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 ff84bca7a0..e01121986c 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -12,6 +12,7 @@ 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 @@ def finalize_options(self): ('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 @@ def finalize_options(self): # 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 @@ def finalize_options(self): # 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 @@ def run(self): 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 e4ee680271..50884c3337 100644 --- a/command/install.py +++ b/command/install.py @@ -13,6 +13,7 @@ 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 @@ def run(self): # 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 0000000000000000000000000000000000000000..c99ede4b3fcebe39b1a1e33283307e12c42b7b2e GIT binary patch literal 76288 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 INt;an1b{yP$^ZZW literal 0 HcmV?d00001 diff --git a/msvc9compiler.py b/msvc9compiler.py index 828d7fbf7a..8b1cf9a910 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -22,6 +22,7 @@ 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 @@ 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 @@ def initialize(self): 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 3b4e9c9d2d..71146dcfe3 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -638,5 +638,5 @@ def set_path_env_var(self, name): 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 917f1d0b93..72039a7e6a 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 From 6d3e6470c91a0dd98c587edd9ef353906a981a17 Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Fri, 2 May 2008 12:48:15 +0000 Subject: [PATCH 1225/2594] #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 02542afbd3..7c43e7459e 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 @@ def initialize_options (self): self.skip_build = 0 self.install_script = None self.pre_install_script = None + self.user_access_control = None # initialize_options() @@ -220,6 +225,8 @@ def escape(s): 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 bd715250e780b37e94996e33820070987bcc77fd..10c981993ba2c2e3a977db8ded201a46d529749d 100644 GIT binary patch delta 19227 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 J=*&lB{};woC0qai delta 17718 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 diff --git a/command/wininst-7.1.exe b/command/wininst-7.1.exe index ee35713940177c0eb8e3cf0435d7a6a9287a27fc..6779aa8d4c1b0d5fb37255f1eea211d92ea6942a 100644 GIT binary patch delta 24325 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 Bkf#6u diff --git a/command/wininst-9.0-amd64.exe b/command/wininst-9.0-amd64.exe index c99ede4b3fcebe39b1a1e33283307e12c42b7b2e..b4cb062c391d230dbf4a7fc3a16536aa3dbc5b42 100644 GIT binary patch delta 19961 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 IFg5&t0jk>d#Q*>R delta 18603 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 diff --git a/command/wininst-9.0.exe b/command/wininst-9.0.exe index 5e0144c92b58ea750211c540d15667e0b1d1a356..0d04a6678b94b8ecbdde10257396e8c0e85c038c 100644 GIT binary patch delta 14220 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 CX=EAz diff --git a/msvc9compiler.py b/msvc9compiler.py index 8b1cf9a910..c8d52c4237 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -594,14 +594,25 @@ def link(self, # 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 @@ def link(self, 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) From 2da7eca35531226358da395c9cd1aacaa81620e9 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Sun, 4 May 2008 22:42:01 +0000 Subject: [PATCH 1226/2594] 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 bf8d022ae8..3f0e09bc4c 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 @@ def initialize_options(self): 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 @@ def escape(s): 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 bd715250e780b37e94996e33820070987bcc77fd..10c981993ba2c2e3a977db8ded201a46d529749d 100644 GIT binary patch delta 19227 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 J=*&lB{};woC0qai delta 17718 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 diff --git a/command/wininst-7.1.exe b/command/wininst-7.1.exe index ee35713940177c0eb8e3cf0435d7a6a9287a27fc..6779aa8d4c1b0d5fb37255f1eea211d92ea6942a 100644 GIT binary patch delta 24325 zcmeHve|%KcweOi9gaHCGXaWHuj5ug&f<`B?iIdoYWI`kf9++f8%8w9D@GuPtP0mCV z@FdPm%XpYuya(^~_S$!~(3abJKUWLI_@k1Lm;|nE`JoV%w$aP&J263Hr4VTJyzkm) zh6J_t_P#&fKMy~9_OG?qUVE*z*Is+=6I%yNtpldrc?R1P`2|vLd)t{EZ+{eur-l>X z2|a@P*0+8hl34z|&~}y&u=-~wo@e!+T=|6M4JUNC&yWWI_uVTOLtByWc>BZ95F7V= zr~&yWzkZ&Lf8yi|Y}_Zm{srYXgjQ{OK%mLF*gxLGais<`_v&x1EK@QJsmAhpWx2sO zqx%JpJ4i4C9Jl&97 zt->Des8Uj9+NEFUZNjUdVDOn3z+?baRSo_Vsu(Cvr9mdv7WG`a(KFmIJv(M-A z8@*cc(*W{DJQnSY?6J6d0<(z-TBNYMNjX0=Q#xM7ak3{d;^~t;N$!Z}4cU|8&k=0R zZZ6{Kk9zw2A3HDdk#%ntsg-vEbVq8iJ2l)PCWbZfR$dsT+2#?LnOASl;y70izOFt4+XSL<>C6f{=<yo%=XcFO!cvMIDENW^DOp>*ck9QupJQ*2|gO z?`^&8d~|;6S0-Zg)?@^rE3VS=N&40spL%!Od_!R zb|!#5MMLJDGm6~OeK1u2Tn!7k{%F^;`;U-K6fGDO!k{WmbeOf|92&pKamEe0_Y#P{ zi!9hh!Tf!wbAhi5wtyS<@Hq4g2xkkUBC6Tl{nV)T09BQ<<7k&KfMy_n6JUF3q*sc1 z#`ssxqS=ehp(URKtZqGN-&Mx(Cw~dTq2KwO7q!5cmi4FY9W+lK=!AW!5^pLBem1q~ z9QuSlMn@iK*XbZ@(m{qm&id0Y|2zu8hvgFtjL(W-s!!0QorW4${OT@P<|w_e zk!p>}mT1N3D!~pAUYn`+4+oTIX3xC~OdZ++5G`<4hE!IjqSwL;`>B0(ZeW;cx)vB_ zof&%Pu7EN!dx0UK*iup@lF9@zVEIGK1T#?=eiN$3`!(zi9}dhnAQIU#DqH-9=#$j7 z3e%+kV;=1vA@vK0u$M+%Jz@fz+5#HKD_ZgvEEMu{NDL(~5r7VEoa+xV77|zep0EZ^gpdtbSMQSs5-z=-%TsiT-1 zP{l@}1EaN5@~BJttV+xm4iYl0VoZ3B3VYdZtQf;`{0V!>M7-7qX|2Id)G9`K?Jj^` z2zDn0du%0v4`k2CUfP4QXLv6e2ic?UC4&M>zBdEyV4Mjja&_!*4-V%|WQf#H3&nc+ zD34$xofZ!2MOyoQy-3W7afH~w_(a@`^9Ed)z#He$+%j-s3dEXQHfjxVtN?agBEH~w z4}FM&>uqc34>+OGGHea~mRdTkp|==_Y7M=Cf*i28KDLHlMKNcFHFOa9O6#T z?KJ3=dxuQHK23@f)Hb{u?1KO9eAf%&c_Mx~@m9`mr=@nfS03KJ6_fL9cEa+HT2x;0>Ec&Ob;bbhvr#jNXv_e7xcGRZ3!gM3iIbYj&id43cFC`8nW_;K$21>_-Xf8q-dZ`Apu2svrNMLWbcKJicgQjsf)VL=RN2 zJ1zXKcsIlARhxhqkWsS>R@YBZ&j1Q7rmh}y4os$3^<2_4FqK>eaA#_VAY6F%x z7?l?Aw%8h)43QNh|2{ZeEIw2$ezRD-vDg}xrlRB7zJBr(tf8mS3p_$MrX6fU#rmSh zCTFk3l=6|3IUhr`1U*v;7Y+bp<(c4jEYmt?vuRQK}u4^Zh`(x;Z@+i3cyJ~ z4O(eYgRK}EBSpN9$k-21bjEGOZD@m@VOh*ADi`)qzk0faV-0}pWh&$&`O&#xgA22 zM}yd@bkeQx(kYM2=q?g5T{hwF_Fr>1C2c(u+=0WM2BZ>@nJKSCmr|5KE^eiXkrcUS z4IPHTe6H6GMOqrnKi-c*AlC=)6;qd!EJmKCUYVcj=c2_kR+^5l@|(%lW)T5^OWaF? zwB*$QW|#f8VRCPyrAFKiZ9d!(E9D)wpt>+BW!ao3BeTJ|$ZY+#7?|I7&o*Dyb>ejb z_pqB=rA~t3h4_At)w_=Dm<+G}O%&x77S}Oxx_mm_XHGv6NhX5Qi_CJFCB4YvjTq$5 z(jPT5*DSsd!|zsEL;rxGQQbjhosyQk7DU6F*zARFjX@T%Zcwjxlsh{R2+&&0yw`73 zogc=oPhhE7iq=YKNC$KROjNhs3+&q`8z2kv^5rrc4qkMC9OA8hb;j{Jlq{UZOfY)I zn1fdx*f!+SGuAWbEfwaz7~HFLYC}m;&pAv|imG1XQSuuqvF*-bcDO=;-&QL(&bz^x zgN{Ku_+Xi%o(}9uNGuPHa=+gYSqJTP)6l6^|8(iHkOl5Kk1F4um$Zzikn;OOUKvGT^eIp5$>xjJ_t zrmJ3$g&jR&Lu6^VJ zpE$u|AI#Lw@O~S9uqkle1UO&eg)V2HUmn56(~>h$B<)5v9`^_i+la{{I+IfgI-7A9 zHr#K(iE&zn-_VECApLl{XUxwj6*t@@*$IcfzuPGcr(P)HaLG{d_%v4804vW-IC`qr4=qxDnmSiv=(Xe&%z}%jF3YxI z^6_DNaJ!hp89BHD0{;kYghVEuREQ1xiZ<+G0>nT4Hu5x-EO4P0HVcacQU+%YkLgR! zXV%_hLr}rh6$4VV8xy7T9^KAb!~Y6M{pu0*JPn^=3Rfr0fxBFKwbi4oT)RlT0!NoW!CqMl*i2eJ{9(UO(N7iLW}gVxwW zl%osn1ysj=)+1pneRLu5n<1gJPB4*ON7yVBIt@%=23H?}VCx<_L!Y&EC@PgAcO1ZdMn%!DRkJx>w=>QUHZ9VUieO|r!}7@0I}X^$YDpLz;7e5 zGgpx`JLE`3tFGFZdjsYMb8K87KR(ocb*Sa)(66&yp^qJ!)cz*;4_S1RMga*4LB4^u zfZqfvx4_--X{Ru0it>*O((a>8A>TkV9j04&rSK6L27pRoBwpwM(dyczSe@?ru0#=j z(O?L)egWqUdrCI{3Jvi_rjxi;E5NI&yfMUVQE)d!M~d)xj4y6bvr}1PpC`F7PGA@5 z=X5PfMHmTZ5vB`{8UqXT>vrs}{WL%rMR5k&fYqkEogD6}HFuwe%@b}|a3D8Nn9Dx+l=R#y7Wm6esRt?fh` z7?Om|>FO1;gQJrIseT*eu^szrVxR-2>0f}cQs_qyB+WsH7{~;ke-2#pzDBlG(*5Rh zWM;YniHk)v%xc?liemuX4fc`~%g%%30Xq9{IE!CaKc)Zj!XAu`4I~4Df&C1U98A5P z*Rlx>4p+LA2|J14?7rJrQ?R=j7DN#BHr5o2orKRp7P?4%g0Nlr&x|`Z3%ouVdN9v~ zg|T1{S)t&LVM8Dl^Ad?#j<_4oHEZHup$QfV+< zPdFfl;h%@SV%CWOr;Ksq-;S@f2;ny{-0yG;bU zB}7Wsvsg1Nxr*J-+R5^(%k!~7xEMZb=tt1@xQ`K9qF3m;W(_}&3Z8tjDK2dd-H2Li zz8ic+B?%RHePrP(g{K~co{-}JPDTPFy`BrZ&;plC(oRLg!$dBbE?#{-4@=Y=S^>eU z`^OsyF|Yn38e+sFGAhoCMrrs;W^n|8#>DSlHVUcoSk_+*G9+sAanMs#Gl}Pg)fjw@ z(m(r@a{Tt$Qt{{81)~HD3|R6wF`We&9QzpE6sCX&s=5a$Qi0b`F`u@w-s?B=2tZ67 zV#gXzsz3@K3of{Yl!?SAf_4f{49%*$3NQ-c##UEVr3Weg_NaxRM@~KMBWO zIhMK54H|J#;q_Qnz|s@Sr#cyy{g^r~z*q!>R;}j33{!`C!=Zi3BX@kxYg~U6ZsRrY8e+20r!$HIuHlZo z&%@jV5Ps~^1}dWUj-748Do6WdHv-DL_n?S}Xx+A~3hvl5si`!6rWXk07DPRcn9rxX z0}}iJkElC-xTty@u?TV8@tdIqL>PNuq*WW89l_VP#=;t{n2hXzegraGoJ!B5px^=- zmr=WPGvey4!hpVjF=|8@(I)hx0Uh-GA+du5ZzF|x_Q}L=BHjh}=h8{4$Iv&>#`0Ch z9uW4C@qrVK7bcJt4YPVQ{0!tGti|Ne7dnz+EvxkwlDwQv2Z&HgEp*XG)SZn@KI}#l zLT=+tf z`ClMsZ$-)fF}cR4{Z=hmL=|?6MJwq*k5kz6H&aM^ z=1wN5X*6CVsCGFs|>y2MQ|IR%CrG0mnA3 zk%0@aNg+&By}s*j7=vKRPJK{&k_`uo=Pr)kfNFue(!WMK=zyaQF*`L^FM{op;axom z&GEYZ>v%ZyDG+PF>-VvqN6^z_;tIUNGw?hjYaK~y7SK^-%^Lb9Ix8wGdkYtGtcQFT* z!oEFfP&&tqeLnQGH83kay>N{&j~V;_E1O=STXcD}lG${SP{<<1%A`D-6ztCIDNJ>6 zt=kMrsdu1fVo%M_lDeNuUXgxoRkjY>w`Lv~`o{$o{mak_@+^&+B|QxWi(q z^iPefyAWB2=!%LPe6={s>lHfM+;~f7S_cG!dnFwGJ{iH$SJ8p+m)ZgZEFMbG`>%x# z09e?^0I`$ER=K(m3H4Y4QvkdR9YZJlY5LV0JrU1v;5uPD;ZT3}2Nv~n0t2=o94m_B z;Dlp)U@gg#J_@pi_Cgr)4W!;W48G|Y(M!C#(}}(jM9QtNnKOCqRMpTlR93`lv=2sh zhO!BBY(#9gS6t9M7jk!8z!wL@5%Uw zbez0!6C87J%lR&|CFGeQKJ(O8F1DD%Fp!=+pdA^NbGFO@yqS2#NSw?qy9I zS~?Me{~W|`3Bs;JNcyVsCW1_^R0lC#mF6L3b9pXUcQt`Qy4+i!`DAKwBbr3CBxz{D zDII&9IQpNY=eg_PbCM#8`1B1?dKCwfpn})XCf&D48cJr((x}iqvEAl(3(wR3XTuR$ zV_Q{Siq|5%($I%hZNoeS#9ko~0diifjNp#%JUV2S-R33m=tkvya`db(cG3fjj|CQA z88X2v#2!J<_T&+_nXwO zV~w~0=`jn2g(1=Iy7sA6dEu5+31QXvi?i_Gede@31{Gruje<=t!YQH43_Z877mwwx z--!iyZ5Qv!4O|d&>6JZ;UfFNQNt=!rXxs|P$+JEwbw=>`$)5Hskb$}pa)hP9^9UY6 zd)Omr>}h-@^FhwS8D_ymdQ4{~DfU?fmVN8{Fj?atW}ugOmtQ$h3XJ&;>c{VWab&xh z%=N>!VJn9mi6|qYNAbMhVUMtQp4hUOVsl~{0(pl}0J{%5$i}k=GJzzD20I$`sByfP zJ$D~;q_7T&Zh2}K4mz^-O+XU^!!bh-Z(l?+bRrfFlwWn&faA+U)B?p(a7Nh#6%vYR zHASQu*U*uu$Lu7S3gJzFFx5b1@H~_kE2f3WZcF|hFsS0<{2XM~@CmX!@t9nU&{!;7 z4^|KUJB`k#V3M~&evT~?4-P&S#H)xbrbqtXNp$B09Q2xpi$NC{Kzxfd9xX8EXgExl zTKV^tCF;2hSi`)OwN>JD<}O#NZ=j{Bs!~0MyrRdP!l_GCO{-L=4l~`MQ|cC=zb1ON z1rX#DJv+wq2A_lK%#dDDAt!NeD?TPn77l_DUid3oHi4;!e+yd?%qMbjEq@af3(v$L zgoXuf%+P&=eklr4iR0g{PB}#+kW(~G*bY$jrqdHqJOLEJ{H!iFrgRA#03gwHLMciU z_h#(HzW8kl`Qftw(TWcVee`IBrw6**#)uLNgZY^;Y?;9ERmlF_KaR}69ytivQo^ettJGwvlne!qd5YZhMe)hAU`4iC?3G9mQ*sc89 zd5h7yNV()(;68r~_;H#&9(C}M9o@a2H#T$h($pW#*Lz0ktxE4{(0g9Gje4LZxJxO% ztU_t`t+0iNoom@|AnGRP?*v6vUaOhc*5j{I$ z}@3f#w$|BB}l!DA$gzO~$J_D)a=e-GvarzkCANeD>WveQY2x?|Ok-O!X%SPlE)kWHs6u>cC`S z3z~)|38l!eD}VkDGu>6V$YSffam_!u++lVeb)68)6a%;P)fjG9%kEBHlE7 zMCcXkdMKlzc|7ktn3$vyx8QEx#e=HVhG8Bl<(+36*j zuD(dABdH}pX1VOm#8~AQg$tKu;obH``iPF>7{gH=Dae!y($+@`GDv~C74o<8d~#-`ywxlheT!91z)!icGHm}^s_lIU(?$c3oJ&_lfOX52O;-3&sS7bsH+@cX+etL2wRrb zgR}rY*|J=c!mm`b9k{Qc7tUX#-eK#dn*t}lJ~(zAvI2X!qqhLt5Jq5fuR9Yuq+tSs zHS`=ol{##4=OPWo&H+p3pvBbbJo(ZBR4;uTK4}f_jKiF0ec#%8#UfsZ09$FVJ05 ze9%ztNCoWb&JQe?P6ppMLNBqH$db(9u_=-3X+>De9W;?X^#G6&-oe>uBgWnm)+B4F z0kyxv*gQ+5z$V&=Fc3vc7wL(v)tZ^e`Ib5_|ZlXz5ff!2tulEGe*E z>^6#vXyTm+n4lfmA_#!VL`s7kURVfv81c>y=lNcf5zHH!6#UfQynrPCYhax~Ad|YC zq~7$NKA9=91RBWZ`FeaFY?0%gY6>KS>2B%#z~-liy|kUBj<4-3cigHgK+Zzx+L__p za6<{>N>_gE)L*7_ItfYg@wWF#8zT7lxx|iDM(}qgUtBvDT@ES=9bc0RQdaS*^x}=< z6wsPz{bo|065GYs&0-7DvN-zBXyMo{fI?2-#a|>SQs5TBmZUEsVIup}B-#dFAYo{R zzN*Q+OV|eJHNmpRNQ&uyiQDxXXl^RZ6U)hJFs1j}!zaaJd6g}E61zS}%$L1(`H>{K z*)6Y1N&hUGmFxOri#h#_d<>eCBjInmQcPFm6BCUY^eh#B;gv*vve$O8$0*(axu%lP zd@I%O*g*@d4V0Z)p*gbGoqpN$F+Q{+n?tt8>y}I0WVxQS;!G%TgM6;nlz?BxSc^6E z49ZOZML;-@>MXV%O1fhSEsL!m^k;~R<&~+ir7Xleu9f!YxtKkN2!z0L9xrrc>@?5R z3fv$j2R#|2{8Zl7S^ox>39@ z_^HIKkTvwj>HiJAPHDr%6Q9|2UbQ;Ulhs*;vCi|%-jUH3XMr=Fm!|~ZGr&K9_QhNW z`i27@2(z59n!2f!Vj9$Ing+TCX8;x19au(dsQNF^#B2{~S<~BOW!m23Kvd`ZHq$ZZ z$;U;15HJ1zI2_GNa$#OVGlc^-xn zYNSLmGbf}hc-iz&Gfv_CIE6b^W3LHoXLy6>je}Rf8)_^l?SL28TTng)oxKHo$1bX^ zCfm?!!YOifz|q(M6SmgX#)D|Y3hA(T8e;1;%|oXIyhJ`tvxflUD}xlMy*WKv>tL4e zP4s@*iWpc-8pq|nODAk(0SdiI=(VSR42sBprNPqzz(fOOXO;t9 zgi3;?8vFrtBKYZy=9@_CK1Aiq);VKbatqoz4F(d)B>8xxWCP`pCp-vq@cjjmlC^Tl zhNYdVIe30X+vWKHjCgBl$=2_F3XaRif|uu8zxziNdkX{!8AL%=UM>+_C2O14=$4RF ziP2O27wCy=l2@?4m{j48x?y}C@9R`e!Sg02(f1~AjFft}0PT_pL*4Wri& zTGNSK5EHx()5s;|F~>_TDP4ZkaXIk`>!G(?Z$Ty8EqNrbB|sXJ7l6q%H60lrQbY!Q z;yB2h#y->yLfoAnNVbi^E4JoU)yJ1BkYqIcGFN{bopeU`Wn&wJIMJ9yCQ}LKohVa7|yUr!Om%&>g4n*xOZfb zgiRmG+iIa@rfnNcCtPRr9U(o19%fQ;*2|i_jhMM@3-V?6Mz#ro4{5W?-VJg|t?5{9 ziC}%~Ly#GC&BI28wfIua1b9q2$xcF1p_>M^la3bp%T>}&ksJkb@+I=}?WBy~2RbZ> z%%hp=k5iq1KmcG^TMM;$>uD!=`K$Y2HNO&i+smO7*Opp73T;@0Z^8L%W=z6tbO9O0 zPNl6e_%%W%p`_dHpq9||U>j@~{rO7MI)Vl{w13hfJbC%JX46q`TNTNSY&G+dlAOr5 z^3?5+FJQ~{E1{gJUj}hA40c?P14M=i<@okQQNrFl^#PXU?i{q4 zPJkLY=?PRbaB(1n)g4>>}zo zleobLS>1irFFFn(50;`a_`WsbE$%gu)s;(%q3IrMsYSGk_qqt=J;v> zeqMh3SITew_)TgL?t0ck5(_B&Ph`mJM*(E`l_I=>46J&|^j%rEFryY$sD0$sO3A&7cG2`)>_)XhMXfo(p zmPXSoy*Gv|H2@7|m%Ij;EZvF?K7b+uFrk-_)8NOw?wvE)SPct|0o_BL6l=0dp|`O1 zt1dr%@%)8j20^BYPA2%64pgA2)UX_919mTSATuN0RM_IZUBwogsx4ZB*PjVZ{>b_$y0x6{O;QL!Uyx6uRrqf_>KS)Z!V5 z;-C~{ML-YnJUX->_sGgbT2SO+b9Lm7$V%IK92b#-oOSI`FpfbY+G9Ac9Il*`|G6P6 zZ5Z*e8PEt~;>7$`USLMhm9pawoHUuPB{ngQxKEW;s^bG?&g#Wdrd(u6|3LO7;V%8U z=}e@^67eO;C8=_O#q=AXC`lEw@qZGz1M&%XREjh4zo0-kA-xyzkGb#~hpe&>@tTzM zGYHDO4VM?!HVR1vgcc}JZmgYUV6qtmQU9$DV@ek&G%D_%)n_eQr)hn#sGlzen1$(#=K65xw`hxtEcqtafv4NF`NnZVQs{n{Mt3 zl6eij6GQqJ;5dk6`7iujK~i>B-Dt>CepKrDQXTB?Q&W9)rk0Nc}LEH$T#aucaSyd<>*Eyt3w%30Uqi| zwr(h8zg?Wt>*45}8losD4=3b!R9Uc|mvXAtN7m(095}CwJp}}Zx66ARq@d0Yge(dX zIWBaN0WCx-Mk@8R?|6y`+~WCqp93nx=}7Quh?ih21nQ2fUA~y zOR&Qn9F^ubot2+v>jm%MZOH^>9>l(Hvc6dEA@{$$9-`TY$u~yK%UL5BU0xq8FRypI zx|)v5y+M_G8CF0OUB%)oP>|KS8(M)~+JT&m_E2s+hvH%*OaR1|)$WehdJAuYmkzEC zT@R+yEYy#&8RWU3oQdYA6=}ol>>THLd=RD?qP#|)75Yd?$kBva<=X2EaKA$Apy zBYLm$n+(S(MHrft z=yENl^5NIT1Z7Lj!g;0OPI)N@0vv()E?j4rt>449x{PC1<1nMtcMWGYP0X`O4j=>N`@ zM1McFzFMC}>I(kZ*6hR|l~1!CN-*^mnJ$+E{&X!mxz~B}HNtc0B0d6=j{w=U&aSpU zKMxnk`q(iL+y3wW+OYo8e^$sJbe^9kYi&c+)xL%_!~}zi6AS-Sr9(ag{#Q(t8)Lc2 zbXj5z=2*j|Se_8eC&zM2ET0m~r^fPWu{<%BPmkp@V!4%a(`EecD)1f4Lv+`qhaa|- zkLBLOC?KSSK;WfIW%3nkxCjvI=y9zJYXV=ak3E5c+=cMWS1jTGY6a4NYYD+2TYMEF zX#AYCu2~cl6kF3p`HI}TJ}&ud9&g`x179S|e+*x|q-?FlA4EN&{6lS;#Ql>SqBngyC8;#%Yk>alZZq8e$Z@E@vH<=BkKmda{dP8{Rh`ecJx*Sr}yLI1}!-KEM@rfS~SYjJC1-j zd;*!mYOk`|vq@Cp_Pz-~^$A!UH3KMfZ`j?5&nyy!oJNdKwh>O%fDR$C15TL*Kk6`m zg|iJPQ3USUc0%XrUxGgk$Dc$UL@nw$jt?~7K&^ux;}h>;z-q}O_#LXqTNs#zPYBlySod^B$_5}m%zQy<+qA&&HRah&$#;)2V2)@h z8`JP1`6A5P1*VQt$09Rz%ddv0R4!yU7dT%o%D*x=CYuwqWyy!2nI=)R)rLo`4Z@ZOF;KKzan}J4pY4v=3)-o z=^dnVNFO4NA{nJuI4%)s7SepAG^9I`@{o#p2DQhA$=z{c|lX1a{crF zG}(#1wH$Zg>pu?|=mNv>6{|d@_byd#e{GJ^ejsgLGuphx%j!h$mbwQv)zlX?*4NfG z@in5bMEU)JIR|V9*BX?*mzF4zmv5X|+Su@5bx~8}W?$o`T3&48l%KpDRf=D`XH9j{ zriPmOO^?)Z{KKmP4b|RFo43^0dBvtp4G&_{hrJIsS9|NkB~1^u5D^bo-`lWB#B?-* z>Yk>iY97EM^#PD?YJ9M%X7h@g&2`G8mmBBU(-zf!4UjcRz%OpvTxZ9a#wKO+FYZ(J z9a%lC98JykRgIf#8Wh*zG6css<;*WOTfB`M#fNIZj^i&SDXlM-D}{%iGH}Ye_8yiJ?N!`PD+ZT}_I3?Kmg0lOi93|r=`++xKTy5|!t*NyKKuLAY z1BAZ%fyM^0sj=SPQd1uw)-^V6VIvzF8>q_F1jI(lH#RmsKn-6qR_S`JMaez#tpj2c zKSdeXmZZG8EmgU&ZTRMn@8QOcL|8pI7eXs*Nob~^W z|Nl4s|IdtnEyo{l{cwq4`bS(*Q(ax@ru&;}njTiR?OmXJf8U)qJ!F8nFKes~)Ylc) zG}PADE#bJ|Q=6}@sd-algJmR&VtlM0U|Bdz%&V`nS+A>(Lng?mF zTY;&*uBN%Juz*XXc?%1aQ$Na8%s+A~H~)C)^p^~!jWx9efu`ohrX|Wxezd@9F!GJf z;#CaFw|{igT{GE8eIAZ0qgnMK_&XZUD(9WI)z;U;kLGX^!af(?yL6Mdxn_&f^yAga z*&okeFl1O&_u$GvUDLxpvZNsED76&SH#XP7AS<(fvdH>AwcNL*t^p0il(L^x%u6s# zwC`AMv8S|DpG|r8C*NH%%hW73iH(^0Ipan!AFMTQuBoqYd;kunp?V7rg@zVxwxN0> z&JPg>zj33^Ij+D^y{YkjPWj&cZ%p|&8rz7Om38}Prza532R6ed&7@M(128*_jMXj8 z4Y8q|$<*BZKuyC&PWk=*>?ys*P0h_)Y96RNQ1-8z4M}5$YK*GI@pXljfN7KR&QDXP z+Kkl@(w2sr%?CP-{y; z9FjETbCIWrZi$js40!sNhx)wy@%}W_%k!<{4XJ3jqP+e>v&8`L6@PHQa$x2A|2|zg NZtOiU;7~9IpMSRdjHmM{a)+0etQp17Yt1o4BK*b_HX6oOKW~t61e@T**SyhzOBVMccGsC z4Jjv$<@0l#EWeZ0U)b8n>L2gCpXK*$)yBPm{AP?hz0;j@2lCsWx+Uijo7b3=jlBK) zPB#B&WOD-EM#{(JEMEPHK#PCt=9FaK%W)+-Blk*FKVM1JrRv{!RJl**pWyfb$L-+o zcY)(7Z&F^;7mr%caiacPH%}XTmsqn=MAuDAl$-Ro-h1^vl+np~5;?NLKMz{(TD4MK ziH4#(@YsuFr+nX9{Zr$4U%6iv9oF#G3b9eHbEL|f9oDA%IdOepsXuP3{F;Z8`>MiM ztHkQ2Te*t3?YZ@6=??uR9=t6;^=+t!+k%tAmql}hVA-pbS)6k$dp%rWRQyb+Bl%d; z(!!t|&{bA1U$*2(x!fD-P4@XKxwg8j3Z7+a?`VGP9@t{2(w8VqS>Xdjq zj_KhxbBjU8aj};|{k%AakJ}DlZ0zowpxxYZi6#e=1NwNmy(-+MOrL0<@w(O~lmUgF zA14NkrU6wwKTs+s&a8eB0OZDjsP_s#n6QuCXw3;)gJV$MK}%r!L90Jt^rPWg7w06N{RAogg_7mdh$fZ&*0LYE1TrlyDXy!yizv0v7 z`}ty>-WjBkTp; zc=Z6ftEv)%ao7i}3upd#Ljvey*K=EVCW-oA);K=$^0iU74hxCs2?(0(9f*7TLv1FE$1KcV8tgJ3S}={*mccBD?X6}VKR@$gh3b^z`TomfK#U`o2@e>;W}wa3Zd{ePzW`qaH5SQjW-|4AIoEG zASqs+0xYQ>_gZtjR`au^*do&nY*H@mk7ss8Q@hmdi=j;oMqodPP!n3rhpMMxXL9Kk zLswe2))Ofm;MLXW1c5Ds7>jodPUF#&XuTWasx>F*ei!-0~cL|R1oCmauxn1#t zj+f979Ejf=w`D+pVC<^I+e60!rUlKPiRjz>JM;=C+2rTC2Pb3yuD0ssAe`~K9isvq z$lMR2LV)5n!U$+iPBdwxW0b+7#|d8R5HhjXUW=X(aAdC*o;$>5OkqH=S zg}9CSMo3l_NDhHqw1`BK$P&dILAa4_RuawQB$~w7V1L{;0M0^OG60MhR?+ALH!vzo zYTb)9>PEC`mYOpX5|JF~ z@A+2%(Qp9{bguL2OK5^fNr4QlLwFtwpm)T6V_!{RA>w>W7Pe>uHe(B5VStVXDQ??{ zDwF(sg%%p1K3odGP=(+e3{Yodnb>!3fV2KaIJ(N?NBjX(+|~j}anX@1_r-0QC`XcZ zrb8R5B8A4PNEQ{B4^!9G=Fjn((hd?`#VkHrYT_5cH-Kbv6G01G8#d`5BtPWS_N-!^ zWtZqk0a|H&I8%rScC%%zbuZZ$@uPS(?loK>j^a`o5d;cK{SU53&w3c zn9=hx_UZFSy=@f`RWz2NQSOG1u?#v{iArGXcd9;lSC+lH-Rh98{o{vv8c+0%+LIL*|y}fO~UevIV+m4!?*BJi$g& zh58c|cV@%tft@0gvw93+!Up#%7f_LTknw64hISeq9tbVy*e5}?yEQf!pkK}^!V^M6 zeK2%_3gcZ7KPzyZP@n6G$_`9sdV-#6&^&e<&dlbGrfkxnsR2FYd_9RZk(aF+x0L{_ ztBtl5%F6(voH##}2aF|-3+DJCOc8$TZ~Vhg8U!Y;w{CLASLT zwIjh~R84wTX&lwsMs2OhP!M(bgB&NfIIJk+vJp}rsxY^F1hnLr9d@+IcM^N=Jj~4n zP19hLZU&Pu-if|LpP^jq?lgy!QD|4ml<6QCJ=ZZG@}iVFr)2hlnQWksZMFimb^;== zp1~kUf=|GVrs3v;XjZm6XGldj4~3`EiM})P5lCKUQyxJB9=>Rc!%iY-e+8hRF@Q)? z)#6A+t0q>&RBGPruyDbA2H^HV0QMmO=RwCW0EmrhebI>y5c_%R2PmSlJcFbyU`WWH zKYkiBOBDWgXBNqyJcHIdL@rE)@F6II0Tn{uV8ISEqJA5ffC|A5*FoL7K>7FEr%E&v z=e_7i5xz6H*)1lNUZ)AXFEXCksTN^kRh2IRm+dSuYinYb2gs1+gvE^%I4t77@C;}q z(JgEvt#UxXg%(YAQgL1U8ZIF?gF^lK;H-ES4T)#5F(Sq=JO_zu0W=FPlvp8VVW+9K zjlG40*vPr61BbTUA1!d$eZ0C2{U|zlwF#wAhqY<5amrG0BHB{mb>4x|lv-s9aF2z3YwjS~){IP7L(h znH|L80kqK-nNuFPb*A(<0t9r*nZrLtyPQR$h`BiN2jI#H+n9i!)mloRrNSz$1@{7& z6%CSYIX2F3|SR)|4< z(cnabX6grfd$kTb>mbQjlTDbdtL%|B08C<7!YBD6nAltFM3E0TQUKwvoht%6NJvzk zZH1(0!Z>`26gaHIXUIm35C|&?=SI&*pq1RhHX?gWdG~<8;+=VPgqXu;&^M&U2}Ey@ zqMm&;`$dKtPGW4%bv1G)xmA8Y6T0%wQJGLBjN;{E65 zuyC!jFnej0IG$a#eCi+OGlfH!ptO?}k(B9q+sA7)r4%CG9AOPU!;pqX7#l6IdxXq~Z z7&O=c0);D;gjiN{2T49Gz)mO7gm{tbN5+d*HEMokXbvMu)h-%Br!xzF$yszGB*#Z+ zdKCqrP)9jJOJ{`@REl^_D3)kNH~>PfRvbVv9YqNvuEftGqQjikpb;07h3?;q`8h$8 zcz;f?U(Cq~4v3jK!7Jk3?qgz_>v#^_>J`Ak{O$ILDPC@hpC(%n98j&;8)N~laCyIc zEM^FG#aSGvxtE7%Fuxco?YdeKfq8Zv2ec&l0grKB?5g>dcH9i)JTjWR|8>%1=CSz_ zd^zX;B`LVSe5e5+tTIqmj=S#|vmF~@`lbxH9rtY;tiiYyIMR9C*MIyG3vOtq&@hnr zy(5_q!~HM0sZw(<9sXoA$vg^lvPHw&>E72*fIGbdWTWHfv_>qu9h{Q0=&Fv>i5$JN zkVQe$gsAt$#39wiutJQ@!me@QJ#=|SH_v>ExGfd)!FrcOuX+=>aSkG-++T zYDATwXb|9a!tYwNNFyxWN3#i?yaV)9ceWyXedB9Dt<4ibOcp`f_&L@ZYO}M7b~28F zt&#gf9S*njS#Xk2f*I-*?70d&?1_V~ki^sqm5viSv8`2bqDg`oC`4ef3|8P1u;m$h z2OCZgEjD7^_-Sk)2ST8^@j@ys=enPH<5CJLl^41!aTc$$hRicem)q zT~NF?$J;O7lj9u_)7^cdQ&=HEvzSskSVNIqJ?tcOt8-Ylm?f~@fKck| znk6t&&MZN%wlt~#d96rB;p7lPLaSDUArPcJTDgymLd4ta{;j!XD-eM_Ft;?bLch3~ zY{KJY6Y6tWiai4O?d2n;qNY@xy~7D_6o7{V?JNgZ7O67kWG zz47sTF|I#!%mORk{Fy{|jm^Ku9c34V)(|7zMFBY3+q;XRSR;dYZ11VyMJg1v!iC^V zMMczm5!WARpg0RfaNLeQt>i#yXh`h6#4V?}Hx=rRi;IVYY7ZQ6c(^M~8RS)2( z1mtMxm5N0b0b@nQLE|s5meQPNlb%KA4WS2kWk0~w3SlEnyP@kK`37>{DM*u#Zv4^k zMe?{-VIY(yQ6oJC#w7QjK3xT7@o$4^JYy(HJ{I;h?Y zBoYlO2Jq_Cp$0#%CZobaY{1{$DNc_@Y3d?&9to#teB4H&43i;M;J9Ju+ITw98ndW^k9d>gXEh(t%JV`SCf^{XLuXJ0nNy5}zZ#^Gdp|!?&`WT)G24ob0)Z4{ z5wC>z7}~yIf$%~`-SY{KOM00}H=I83hI#{f-mg9ZO$5et^Go3q>w1Kyk=sd5cz?0U ztY27xPIh>*?N(K>)*H^$JZ;xQhw9m(4UIfh&kA&OvU?dxF9LChD_RhtXv>%_HSNyp z{Uf4oKouFU`91JeP74++KgdmT5d^pzjn>a z$s*@8XjOTf55ut8%^x=nNSj2mqFp@z@^e8liZFe7kZk$!VXrtisxqX;;!|!-bDNej3@H3}$z}#}3j=s`< zbITQ0xMFUJq2T_Fx#f4Pp-;Q5xZAb&XE~+4=9c5=k%P(ZkIYibYba+=Ft_{yIR)`} z66EeGya28W&SL!ZLkNVW*us$D<5d%TlOYt5#<5GR7B6R%#o=0^z6423M0U#u0tURj zU5+>m(fS?I5q2+{D>q8#qYFkEjyVR@?_~m~D;0jcs`aoD(1j-_8jksoabufS=-_+w zSJ9=R{0c_o5|Un1Rcv%Xk0^$&GFo$B_|-05`dN%HaY$uphZIh9#1QDo3(o=ZX!y!! zkHRR7IHafvQkXEtOzsn=0Pld7v-=MTdlidAhI&Q3<`0aka#+cZHok-RrY#r)5oVPzwyjUb2$VGo_|$YsKWI zYxP05=UCGC!kCL?$CAbtKKyX(HoPVYnCM+YwCq6R_anhvz_}{X9HEn+e@=U%-H3GK$D7^$1Rk{E~vcg@7VN z!@KE-rAsJ+gizAW*p$|YgJ=i&X>Hp2r$Dbn%K@#0&ILJ(PE}zqwXo}mHuunOYQO;r zlbL`f=(ikFX1iufQSYARe#A$gyqg_v2^CGH+p={lg7W2WR00pBi&|a%z6Xo&eQ9`RcW~*^t=z;iCzH9C ziQoxyG08il_})L{VzP0}!IZwt!_>kG6fL5+Xkr#ljT9c=d->J$s}!WNkWVRcaXysEnq~4hOeP zoQgXZ2qg5AVYtDcCaGa$RWs8-(kk*2mm`cx>|%D8!sMzGeb5S5Jk)41;GPh{somHc zX&k`2im}+FIKHAnNIeXu_T<7K%M#*>DX7Lb7}ek1p|MIFgE2-l#@-V+)(0a%$P??~ zrr;yH3PEj0|LXx~A_hc4fLL%hS}S_CgVWHLSlFk!y~K7Rw>a5>yPxRfrsx#Ft`OvM}QMIIPe zBkU!hsf2ikCEyUcKmehIsGymFP=As+%;OLV)u^}}_Hmt~$QGiZiM)5-d)TnnEYMIx{gd zI9po`)0=vx;mO#8kIzP*Cs(8A0GcE<_S<-zuAZ||>-jS%F>;kvBUhPQp+55$8eAos z;@;jKdX=wzB;r0|X1+E)0yVe1M9toTo}YdW7C!=(I4!XXjyY^5dqh{zQlY-}skY1y z}FK2rP2V4=HgT0zmd9)tEO3idnrt?LO!*bE*VTljx z;u(mTCgUqd$8C_Csw4i2gDn0;E{mTgk;fv?dmO*^vSt@Knqdi_-%J#qJxtr#ixGTJ z1Z)%NwU9vWr$C_??fnn0l5{Y0ERI%Behjh(LU}ZZ3(l7FiXg!FoR!oUcTTEg!2iuq z14P`a*aPf?+=in}jZGV6pnYx(WZDchTcMY<~6>a`_>X`-C`NKAYw@ro9!h(FZ4Kg+{s5 zB7eqQ%(OyNM3?p?-a@hXI?U92<`(VCEP7{*a1=0$BNz(oA(v}>6qsXglRK~`$l0xR zl)2h?o`-4YeF42X_SbaOrNV$?P6=8Ov$Z`-j0&36vkoZ!=FvLPkhy!M_~;a$E&&gG zS`$gpE+i!6Heol`V8XH*w&p+2(e@H@cyh#_T;?$1b<4fkDN2qM*&{`cNKr@|LbU@G4)xPR(} zDmV>KI&+?M1V?ukI=FuU^9!BAp6SY!f(4^fBOBAz(gMIU@)p9`A#|CzC4oepl=h7$eXBYxF(--p8_)2tp;B9 zIozijMj76U_#DcUg)^o7lORl$sU#ggqhe8V__(=c2a5Q5IXF%(GNK;dMs@eNU{a?c z8AxK3?8_*ZAItR185QzIBM^1UMHbl*>Q6=4j<}9n_~kPv z5$TLO!W@_($@%1qj?>`w>_JcHx~1W^a2$~vRLuL6-qwCCvqk&ahc2n!&IdNp|mrU&!Ooqd*w_lxw>L(wC-!_Mvs2_0y z`Bc*f=B7_gq7}6g2&|>klhUyn-{^G%5(J`MHNwZWgf5uzS$OEO(cQkmDwiaN&P$Q@ z(%65;SPgeE6|>45m4x4XGRFX5`s7sTf*w*A>KYR<5Zb`EiNIqyg4H=MWjleXMH^A? zb~#S;JA~@rpr+ApQxd#`{IY;^Wq)xcDM7w%pIb^k(`kSyg9KTw9yN-7 zIX@dV2jT#8z;f8%d#@hVi!(?@?2wDI)A|j4XWgtTqx#WG@3`jAZg9R0&uA$_@7DYiDhkDCC8vu98X1f~U;tjRaM%Z6+59_ed| zqcTDE$h-_tz0)ub%lBM_sU+cN#w_QCWrtRU{Ex6^Cz{{quqw3Rg2m9~YTLTXaZ~&I zCMbfTD{7obx{lRh8s=qaI!4zql1E%y%`H)^mq05k1?|IT1Try%7QR*FN10n*z+gGw zC_#==s0`C=2>I)Vh^9v;-ntZeUnd_9T`-iw ze~%O!A8&wx7h&MrFX=lC*gJz7x~%Ux1=>(!e!<3i*#FM_k}>G*%rDybb=-;ad6N20 z!%ZV*{2(#I04*B=vlWeyF|to(;7q4sIywQDkf&t=)IYc{aDmzz?nu~$pQBUTk96bF z!iKR;{f3OuCvVLmDQNGp!21{!giI2IBtRfmWNUnY6cZncvzfM}BBBMoa=!^>eG}S~ z3PkPhuEa^6N(by+gdT_|;m3B)A$I`dQZ<)=zLF_%q@dTH)(3>F80<8VBakvur|tAx zJ#;$>7IeEO2#E+?pP;er1S(&~wh4o53%xJRYQHq9xt+eKl@CXXD?=Bk!6U<~Fh}y2 z%K4R{c4PY`W9agPyqOpjDJ~;QnxFX)I}UAHI@A2j?@{W^Ux6pM01FSBwZdIo)=(9? zZfLkIy2`;!@dICui!>xd`76w?7`^=v0@v}aC)zI=LYEDUneUITjg;gIfI2V2AlLr{ zl09)55LE^qW<^M5V?fW;#Cn{B)=f4s5q7uNApYg zMV%b1k;DZiMe_w*iPR$C4xNG7#E+?9<1$5i;&4qNj;BCO69RW-6Y3_^hqevp0o$&W z^OqXhbFh=py;w#rE*bP4iwlZwIV>kPoA)1e9|aRV8*<6~rH_$@IE2yU1a^Qpnn{LEz^Aia>*eBquH7 z0nPkMNq#M}4xu&!=_n2q($Sw0{cH0k0^Or@s02&U?^YY`926S!@Pfl0ASp>98_2}? z9BuOSr9<@b1FbQdtzn2^$Y{X2ok-FNPJclW7#xX>x&aXG`V0`d#opue5YX3!^v4O~y(7?|5Db%7FvcDy@kULoJNQ4g`%aW7KTX9 zUquR#Uz8Ak*;g6O=lMl)@lvUiBM)Fvw7>@?(5nvu+Koyu=o;=Op`VJunsE-rlj>`l zVV?A0aQzYxvjqEc;zEWDH!@2kBZ7N@F9Xoh7xR(FGSxPZ}Dk|$*g zF}47WxT8e!J)H)!ta3g{wigOChqe$!_ZJl?-%|(!0etJaLE zod){AR4(QdBNALJA}&fpY|M}Dc3>DgEM^TahiRWPi$z0b@eADi=b0#JTqIon4i}%r z6(d1{FL3c6U?NudGA2r&=i(k_hru1~Ubmi@^8y_v8h43D#kq`4wjnk>ILM|R4E%@L zluK-qz^B{6X#!&JE)uc=@F2vRn6 z0z@`}L8BdzlVk=IR~gjNmW_e9h@so#@E4;x||& zJ&h;|X6okQEPx-+e2er*af*nWm_@>c7QN1J8;#|~acNx$Qv4fSk=Phk43VEce8HeA z_Ph+j9;l`R045#|&YZ)p4JX>)hgE`f*5TTKkVU(EqP-^t#wg#4P@(25XM>Ni71|vJ zmrThpm5CACBYkquK$=^gO>CFlU8w!Q;`iDF0~I~Oxii5>Yvpl&#L z5c~q6eHnlkyeZ^PY0P44;w*CW7STzVq9J{eLH%<~S-9j*DP1l!rCpN!*0gJeqlR-4 zgirm%FtFe7CP0B-S@@9m9!8%EaiZ)`#{D<#++tq+6}BhJVBZVyb#Y50 zUKM8Gk|j@~pUt_Bhc0+RpN_-l_YXF|--KNl+70=c;meW2^x$M8fRCoTnK3vOm}Sxx zea~k~^3pld>s}8xW`T$ME>cnvBHBprAZ0)3;eL!XVWEfn7Sf-QW*6g!bV%b$Jlxlj zxKiA)ki>`Z!;nQD?kheI7eRWj3_?0a=tjo}`$FP$7+;$$_J4*u|vB(i?76r5dSI=vhnUa^}% z8yfPvS!%Ad1a*;@*3w-kPjuy;DxuhJwxFO*N z+2M*HRU$3jFg0=dm|scGuZ|3MO1j28>I;C+-A;#~*jMO<(O)qD;OcnOSSX#{k$|*Jamxp|~!}$H^LF zGzo<>Qf7B}g5&a%$SVu2qw{MBv_q2uJA>op!-;*JA^2zU``Ycw!5X_c7wp@n?^!@EBoCnNa?M@3TqU)3rSv=o zLM9>;@z#LE6=epWRkU|8^uGfoS75iv@|#*%to=g^en@PpW+Rs$@d4Wuyg_)n55UT| zHUDL=4XYoi$7^a7(z-+a7W1<|AdF5SlptUFacR4fFoBI>R-V z>l3*lksA~Fs6?KW$VVq~Qz9Re$j2t~afv)Rk&jR06B4=EaP1+(8-{E63By8j_;K9( z;PXyEjL5uyvf)Ib;aXAf&vHBB{vX9D?k{&9qJn(F_4X&9gx`}tH9s>6bC$`SOAxQC z9@oD7h|(jZ>06&vCa-o%txtOZ=xy2h?bfFd)How|gI|{RSc;tf{Z>f6TxE2fVQ&~* z)^O`q3^|R!pK3Ah|1FKcY=mCS``ct=m;pE{ze}FQjR1}|e*ly?m+{*S!Vyd%A_ItY zJ^|&Q#DO5(h{iAu^1H61U%(-O!BIJ#;eh$Bqpl;ebP6Sg$sh@6`{w=UT~@X^xqHM; zrv4pD45Q!z8AeP54H-sXB!eWM!eR*;+<=GN?K<-XR%nbkBd0S~&?e4&fgi(I5Fv)K zAd+YZ$f+71j)vNm`yQKh7jaSB_GZUTa_%pQky!rv@LX-(>#kg^t330VlceXR$8u&b z{Tkko&RM~p0(;i6=X&-O@l=xQ=Gg+Gp#B@)y?X*yZ+m(xf>Nc2MH4PwhW^hOFfq1g8H-P!J zWP+*EmnTBHWO-Unbu?1 zdhFD5o1+qqSinTEa8A%e$EnpCc6cFuQs5p_9#~gwF2IQR96NhDzvkKZlKw-Z^w3W@ z&V-bL^b}G((h8(>q+Fy=e+)qQTlhnKl#WDyBHErq+JW>U(gCCsNavASkm&EgF0>(i zfb<#CgxwrB8|hx8g-FYh*6)Tt@iYqGMcRWw2ava5k?lzTfpie*9i$JCo_~Sk`jDG2 zE(PhkNFwT6kZ(q+Me-vRAY~)%0jyh*)8D&4<+!(zx{zK&dJ$XH74vVi;pP_Gy1DiXJc z(=hN_Z3^l_4!h3LuF{n~|PI+J^L9q#q)^fbpOv zq^n4JEIby;iZm0+i8L4Kex!$x{75U1=x@)D5Dis9rYlH(@T~ypUZh)*Qjn671~5oP zdI#wk(tf0OF@6W~=a8O6sz<6tDnlwjnv3K>vLG3e={sVwaTF z9_}Yd^oJ8Qo)_?N^NCVtUrhNQ?foC^{r{r9 zt2n-X-TIn+pS+x>Q|9lVIsQYgu)e0IWc63;SJpqFd~3gL{ARA8rmjY;DO_DwGf%00 zB}>`Af6K&H{oo(LBtgf$lL|lil%l2m}&%NSIa_fCHYgZ}htqbqe>G^dHVrfl7 z!^%f%=Hb&|bcqFZk3J~YtSQ5PLOPE-tAD&(~B~+77wD{J+R?V*uHV7QIpx{Am4tHF)xaQIPx^)dT z#WhcGA5iV#^)+iz@ot=_y#MMPbW25 zYqfoGbM)048rCK%+<)uWtgNeB_Xv!!u6n51O!L-0vi=F~MSU&kkK^Ca(D2C0wYBgd zbk*yFV$IqOO6ltlOw89+uU_|6j{A2S2hOck&b^+MGeuWj+aQvOolKC#b#-|4jCqfU zPpq%uyqMFV6}cz4>gqLLt$rj}U%h7KMsA<&z#5(VQ=r0juo~wCE3XQ!=Pdea@M-

Z(D?RiJq_Ef42o`Y2^%{qqvt=$9sJ8(Cs1zPJx|y~D%pPUO!g z^5>9mL263W6VurEI<(Pv%KzSYiYJ$090EKNSAYUV`ZAGEMQ%lIK|b&c50`@cBJ$D5 z-$71qHx3}zBYzRO75NV2+mQMvYyWc!nz)Uqj~P)HP`6L@a7OCHUnT0_vC(*nKjE(c z4R5gqt)7kg+scgghJC3GqHYXdzSvvx;C!w@c}Yy0TpL{bh`4&)TKn2{qJ8Ctm8r@pC!0dr>j$<8y}v&IJj0^y{5+Qt*?iyW5cL4*cY!`vvRHS%_r6O0@JH~tJkcrtMQ4@v_~QPPgEDKUW-He>ort{ z9>EAaTEBA5Lo3&S!_@ZR+STG}HbObm>{jYF%u~i}d~6c!X(by0WU6^mT;*pQZc#qi zuyx;ao3Lcx=9YbX8gJGq-xsGSb&ubl?0cet7MNH6=mt)yZ=9=~4+TMV<$)(k_c^2# zno*z(G^Hw@Ceb`+6}AOZK+o>8&uLI@Yko;7X<4tF-&i?{mexVq#*g1O;nbgI{a@Q7 Bkf#6u diff --git a/command/wininst-9.0-amd64.exe b/command/wininst-9.0-amd64.exe index c99ede4b3fcebe39b1a1e33283307e12c42b7b2e..b4cb062c391d230dbf4a7fc3a16536aa3dbc5b42 100644 GIT binary patch delta 19961 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 IFg5&t0jk>d#Q*>R delta 18603 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 diff --git a/command/wininst-9.0.exe b/command/wininst-9.0.exe index 5e0144c92b58ea750211c540d15667e0b1d1a356..0d04a6678b94b8ecbdde10257396e8c0e85c038c 100644 GIT binary patch delta 14220 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 CX=EAz diff --git a/msvc9compiler.py b/msvc9compiler.py index 8b1cf9a910..c8d52c4237 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -594,14 +594,25 @@ def link(self, # 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 @@ def link(self, 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) From e2aef69588355bb9ecd378a778b3d50f00ca14c0 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Tue, 6 May 2008 22:41:46 +0000 Subject: [PATCH 1227/2594] 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 33a1212b0a..44c76926ea 100644 --- a/command/install.py +++ b/command/install.py @@ -18,6 +18,9 @@ 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 @@ '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 @@ '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 @@ def initialize_options (self): 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 @@ def initialize_options (self): 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 @@ def finalize_options (self): 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 @@ def finalize_options (self): '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 @@ def finalize_options (self): 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 @@ def finalize_options (self): # 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 @@ def finalize_unix (self): "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 @@ def finalize_unix (self): 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 @@ def _expand_attrs (self, attrs): 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 @@ def change_roots (self, *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.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 ------------------------------------- From dd58a30c84365eaa82aa6175dbc52a26ee63bb7f Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Tue, 6 May 2008 23:45:46 +0000 Subject: [PATCH 1228/2594] 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 50884c3337..0a902cee89 100644 --- a/command/install.py +++ b/command/install.py @@ -15,6 +15,9 @@ 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 @@ '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 @@ '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 @@ def initialize_options(self): 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 @@ def initialize_options(self): 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 @@ def finalize_options(self): 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 @@ def finalize_options(self): '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 @@ def finalize_options(self): 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 @@ def finalize_options(self): # 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 @@ def finalize_unix(self): "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 @@ def finalize_unix(self): 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 @@ def _expand_attrs(self, attrs): 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 @@ def change_roots(self, *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.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 ------------------------------------- From af488a5062e1af5b85b7c25c2a58087cb9905a86 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Thu, 8 May 2008 11:54:13 +0000 Subject: [PATCH 1229/2594] Bumping versions for 3.0a5 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index b583e397e2..3618c4f727 100644 --- a/__init__.py +++ b/__init__.py @@ -20,5 +20,5 @@ # #--start constants-- -__version__ = "3.0a4" +__version__ = "3.0a5" #--end constants-- From a59a6118c2edd586a14b636043fc27f6509a62f2 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Thu, 8 May 2008 13:16:19 +0000 Subject: [PATCH 1230/2594] Bump to 2.6a3 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index d0dd03a3c5..e3e32f51bd 100644 --- a/__init__.py +++ b/__init__.py @@ -22,5 +22,5 @@ # #--start constants-- -__version__ = "2.6a2" +__version__ = "2.6a3" #--end constants-- From 8a2d33caa7898053d9c911a313660fc39a412bbe Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Thu, 8 May 2008 22:09:54 +0000 Subject: [PATCH 1231/2594] 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 a9c6a5b488..264e66faf2 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 From 2e52da55e1a498e632f64468c4babe7f2b542850 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Thu, 8 May 2008 22:27:58 +0000 Subject: [PATCH 1232/2594] 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 f3f65c6a0c..9444ff012f 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 From 78cb86ae70da077547532ef552ebdaee33ac0a72 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Fri, 9 May 2008 12:19:09 +0000 Subject: [PATCH 1233/2594] 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 bf5ad7e00b..beb3319469 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -10,6 +10,7 @@ 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 @@ def initialize_options (self): 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 @@ def finalize_options (self): 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 () From 22b54825ad9cfd0d15ebecb4b31b7886f6391833 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Sat, 10 May 2008 19:51:55 +0000 Subject: [PATCH 1234/2594] #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 5c588effd3..a9ab361a17 100644 --- a/command/register.py +++ b/command/register.py @@ -8,37 +8,29 @@ __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 @@ def check_metadata(self): "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 @@ def verify_metadata(self): (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 @@ def send_metadata(self): 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 @@ def send_metadata(self): 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 @@ def send_metadata(self): # 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 @@ def build_post_data(self, action): 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 301a159590..daf681128d 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 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 32b9026999..de9ce7d7ff 100644 --- a/core.py +++ b/core.py @@ -20,6 +20,7 @@ # 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 d098cb9713..0b13c1e6c1 100644 --- a/dist.py +++ b/dist.py @@ -343,10 +343,9 @@ def find_config_files (self): 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 5ae79332c0..2857263730 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -55,6 +55,7 @@ def test_command_packages_unspecified(self): 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 @@ def test_command_packages_cmdline(self): 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 @@ def format_metadata(self, dist): 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") From a189b6be0df13ba6b77292b59ea501774804b422 Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Sat, 10 May 2008 20:52:01 +0000 Subject: [PATCH 1235/2594] 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 a9ab361a17..5c588effd3 100644 --- a/command/register.py +++ b/command/register.py @@ -8,29 +8,37 @@ __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 @@ def check_metadata(self): "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 @@ def verify_metadata(self): (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 @@ def send_metadata(self): 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 @@ def send_metadata(self): 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 @@ def send_metadata(self): # 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 @@ def build_post_data(self, action): 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 daf681128d..301a159590 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 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 de9ce7d7ff..32b9026999 100644 --- a/core.py +++ b/core.py @@ -20,7 +20,6 @@ # 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 0b13c1e6c1..d098cb9713 100644 --- a/dist.py +++ b/dist.py @@ -343,9 +343,10 @@ def find_config_files (self): 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 2857263730..5ae79332c0 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -55,7 +55,6 @@ def test_command_packages_unspecified(self): 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 @@ def test_command_packages_cmdline(self): 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 @@ def format_metadata(self, dist): 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") From a76f1b7559518a73f906030946db7d12ea21fddc Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Sat, 10 May 2008 22:12:38 +0000 Subject: [PATCH 1236/2594] #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 0000000000..cf547698a9 --- /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 From 650146240e3815b6cee42887e1919baf4077de40 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Sun, 11 May 2008 14:00:00 +0000 Subject: [PATCH 1237/2594] #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 5c588effd3..a9ab361a17 100644 --- a/command/register.py +++ b/command/register.py @@ -8,37 +8,29 @@ __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 @@ def check_metadata(self): "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 @@ def verify_metadata(self): (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 @@ def send_metadata(self): 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 @@ def send_metadata(self): 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 @@ def send_metadata(self): # 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 @@ def build_post_data(self, action): 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 301a159590..daf681128d 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 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 32b9026999..de9ce7d7ff 100644 --- a/core.py +++ b/core.py @@ -20,6 +20,7 @@ # 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 d098cb9713..0b13c1e6c1 100644 --- a/dist.py +++ b/dist.py @@ -343,10 +343,9 @@ def find_config_files (self): 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 0000000000..bfce3e3e72 --- /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 5ae79332c0..804c8a4686 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -55,6 +55,7 @@ def test_command_packages_unspecified(self): 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 @@ def format_metadata(self, dist): 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 0000000000..b05ab1f78b --- /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") From 9dd1c5afa5e07e7da296484f3508464e9622a5bf Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Sun, 11 May 2008 19:15:52 +0000 Subject: [PATCH 1238/2594] 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 804c8a4686..05342af829 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -215,7 +215,8 @@ def test_custom_pydistutils(self): # 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: From 34cbcc09ce17524bcef6b344b93a8968f2c34582 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Sun, 11 May 2008 20:08:33 +0000 Subject: [PATCH 1239/2594] 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 05342af829..8f1288e9f6 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -213,7 +213,7 @@ def test_custom_pydistutils(self): # 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)) From 2a4ac571e1458d6871960e7fc687eaba5b6b778d Mon Sep 17 00:00:00 2001 From: Alexandre Vassalotti Date: Wed, 14 May 2008 22:44:22 +0000 Subject: [PATCH 1240/2594] 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 daf681128d..ecc06f05ec 100644 --- a/command/upload.py +++ b/command/upload.py @@ -10,7 +10,7 @@ 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 cf547698a9..e9ba40260c 100644 --- a/config.py +++ b/config.py @@ -5,7 +5,7 @@ """ 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 0b13c1e6c1..e4ab3363d8 100644 --- a/dist.py +++ b/dist.py @@ -359,7 +359,7 @@ def find_config_files (self): def parse_config_files (self, filenames=None): - from ConfigParser import ConfigParser + from configparser import ConfigParser if filenames is None: filenames = self.find_config_files() From 9285f7c07f1c115cfbf61b15b00925f1ea4be940 Mon Sep 17 00:00:00 2001 From: Alexandre Vassalotti Date: Wed, 14 May 2008 22:59:42 +0000 Subject: [PATCH 1241/2594] 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 34b6692d04..23999ae9ca 100644 --- a/command/upload.py +++ b/command/upload.py @@ -10,7 +10,7 @@ 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 ade2ab795c..847eb902cf 100644 --- a/dist.py +++ b/dist.py @@ -349,7 +349,7 @@ def find_config_files (self): def parse_config_files (self, filenames=None): - from ConfigParser import ConfigParser + from configparser import ConfigParser if filenames is None: filenames = self.find_config_files() From 04c09e98e06c0b9467e8f22c52d2fe052fd505e1 Mon Sep 17 00:00:00 2001 From: Alexandre Vassalotti Date: Thu, 15 May 2008 00:33:57 +0000 Subject: [PATCH 1242/2594] 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 ecc06f05ec..daf681128d 100644 --- a/command/upload.py +++ b/command/upload.py @@ -10,7 +10,7 @@ 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 e9ba40260c..cf547698a9 100644 --- a/config.py +++ b/config.py @@ -5,7 +5,7 @@ """ 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 e4ab3363d8..0b13c1e6c1 100644 --- a/dist.py +++ b/dist.py @@ -359,7 +359,7 @@ def find_config_files (self): def parse_config_files (self, filenames=None): - from configparser import ConfigParser + from ConfigParser import ConfigParser if filenames is None: filenames = self.find_config_files() From 7af87d550e429290a39a66350c57fd30141080f1 Mon Sep 17 00:00:00 2001 From: Alexandre Vassalotti Date: Thu, 15 May 2008 02:14:05 +0000 Subject: [PATCH 1243/2594] 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 0000000000..e9ba40260c --- /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 From 1ea453fe09439490fd90dff0675db494b5b82b86 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 15 May 2008 20:06:51 +0000 Subject: [PATCH 1244/2594] 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 cf547698a9..e9ba40260c 100644 --- a/config.py +++ b/config.py @@ -5,7 +5,7 @@ """ import os import sys -from ConfigParser import ConfigParser +from configparser import ConfigParser from distutils.core import Command From f543720ad42bf00329d322a6ae72f69a9e3d90b4 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Thu, 15 May 2008 20:07:39 +0000 Subject: [PATCH 1245/2594] 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 e9ba40260c..edba47f579 100644 --- a/config.py +++ b/config.py @@ -7,7 +7,7 @@ import sys from configparser import ConfigParser -from distutils.core import Command +from distutils.cmd import Command DEFAULT_PYPIRC = """\ [pypirc] From c926aa0be33963fb9fe9c776acf77df7a25770c1 Mon Sep 17 00:00:00 2001 From: Alexandre Vassalotti Date: Thu, 15 May 2008 20:13:54 +0000 Subject: [PATCH 1246/2594] 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 40d9f20d1a..b6a36f5bfe 100644 --- a/command/register.py +++ b/command/register.py @@ -8,7 +8,7 @@ __revision__ = "$Id$" import os, string, urllib2, getpass, urlparse -import io, ConfigParser +import io, configparser from distutils.core import Command from distutils.errors import * From a2b4da6548ce557e87cbc6a40bf312fb308d4d4b Mon Sep 17 00:00:00 2001 From: Alexandre Vassalotti Date: Thu, 15 May 2008 20:30:56 +0000 Subject: [PATCH 1247/2594] 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 edba47f579..f1117beed1 100644 --- a/config.py +++ b/config.py @@ -5,7 +5,7 @@ """ import os import sys -from configparser import ConfigParser +from ConfigParser import ConfigParser from distutils.cmd import Command From 67495c271aba3864b15d2e9f2fab5cb7c775edc0 Mon Sep 17 00:00:00 2001 From: Alexandre Vassalotti Date: Thu, 15 May 2008 22:09:29 +0000 Subject: [PATCH 1248/2594] 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 e01121986c..73cc00ba0f 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -7,6 +7,7 @@ __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 @@ def initialize_options(self): 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 @@ def finalize_options(self): 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 From f71e81f1f9cf18ae21fe3e17c4ac17e83b3ef403 Mon Sep 17 00:00:00 2001 From: Alexandre Vassalotti Date: Fri, 16 May 2008 00:03:33 +0000 Subject: [PATCH 1249/2594] 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 b6a36f5bfe..89cb2d410c 100644 --- a/command/register.py +++ b/command/register.py @@ -8,42 +8,34 @@ __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 @@ def check_metadata(self): "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 @@ def verify_metadata(self): (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 @@ def send_metadata(self): 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 @@ def send_metadata(self): 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 @@ def send_metadata(self): # 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 @@ def build_post_data(self, action): 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 23999ae9ca..603336c954 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 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 e9ba40260c..a625aec5e6 100644 --- a/config.py +++ b/config.py @@ -49,7 +49,7 @@ def _store_pypirc(self, username, password): finally: f.close() try: - os.chmod(rc, 0600) + os.chmod(rc, 0o600) except OSError: # should do something better here pass @@ -58,7 +58,7 @@ 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 + 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 a4c5e18033..6e4892039e 100644 --- a/core.py +++ b/core.py @@ -17,6 +17,7 @@ # 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 847eb902cf..ddde909fbd 100644 --- a/dist.py +++ b/dist.py @@ -334,10 +334,9 @@ def find_config_files (self): 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 0000000000..016ba4cc70 --- /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 91acf45805..81459ace53 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -55,6 +55,7 @@ def test_command_packages_unspecified(self): 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 @@ def format_metadata(self, dist): 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 0000000000..b05ab1f78b --- /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") From 17a42fcbc36865bdf006384e396f968312103464 Mon Sep 17 00:00:00 2001 From: Alexandre Vassalotti Date: Fri, 16 May 2008 00:41:41 +0000 Subject: [PATCH 1250/2594] 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 81459ace53..dd0773508e 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -213,9 +213,10 @@ def test_custom_pydistutils(self): # 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: From 372161eb00ab6befb04dd3a2a85d38238efd5b0e Mon Sep 17 00:00:00 2001 From: Alexandre Vassalotti Date: Fri, 16 May 2008 02:06:59 +0000 Subject: [PATCH 1251/2594] 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 daf681128d..92c4bf204e 100644 --- a/command/upload.py +++ b/command/upload.py @@ -10,11 +10,16 @@ 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 f1117beed1..35a21ec211 100644 --- a/config.py +++ b/config.py @@ -5,7 +5,11 @@ """ 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 0b13c1e6c1..6299919fdf 100644 --- a/dist.py +++ b/dist.py @@ -358,8 +358,11 @@ def find_config_files (self): 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() From fbd52989678335fca9cbf0007209bd04513036eb Mon Sep 17 00:00:00 2001 From: Alexandre Vassalotti Date: Fri, 16 May 2008 04:39:54 +0000 Subject: [PATCH 1252/2594] 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 a625aec5e6..6ffccaa095 100644 --- a/config.py +++ b/config.py @@ -7,7 +7,7 @@ import sys from configparser import ConfigParser -from distutils.core import Command +from distutils.cmd import Command DEFAULT_PYPIRC = """\ [pypirc] From 9c132229971073925b69493eb88596556c7a658c Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 18 May 2008 11:52:36 +0000 Subject: [PATCH 1253/2594] 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 0000000000..b5c2aa2339 --- /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()) From 0c96212a691e860e15ae057f59b5dab114d90c90 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Tue, 20 May 2008 21:35:26 +0000 Subject: [PATCH 1254/2594] #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 8e274dd40d..170d76751e 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -5,7 +5,7 @@ import os import shutil import sys -import test.test_support +import test.support import unittest @@ -39,13 +39,13 @@ def tearDown(self): 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 @@ def test_run_setup_uses_current_dir(self): 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 dd0773508e..f1b11c1745 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -7,7 +7,7 @@ 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 aa1187e77b..c6ab9aa5d5 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -4,7 +4,7 @@ import os import unittest -from test.test_support import TESTFN +from test.support import TESTFN class SysconfigTestCase(unittest.TestCase): 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: [PATCH 1255/2594] 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 35a21ec211..e07f8ac998 100644 --- a/config.py +++ b/config.py @@ -62,7 +62,7 @@ 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 + self.announce('Using PyPI login from %s' % rc) repository = self.repository or self.DEFAULT_REPOSITORY realm = self.realm or self.DEFAULT_REALM From f21c3f4abd01a3a0e8d94b71aa43e817ccebca98 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 25 May 2008 07:25:25 +0000 Subject: [PATCH 1256/2594] 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 92c4bf204e..8805d41da0 100644 --- a/command/upload.py +++ b/command/upload.py @@ -14,11 +14,7 @@ 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 e07f8ac998..e3a4c57e33 100644 --- a/config.py +++ b/config.py @@ -5,11 +5,7 @@ """ 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 6299919fdf..0a21380973 100644 --- a/dist.py +++ b/dist.py @@ -358,11 +358,7 @@ def find_config_files (self): 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() From 281cb13d4994a8d61b79b5d0fbc26eb3e2b29d23 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 25 May 2008 07:45:51 +0000 Subject: [PATCH 1257/2594] #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 c8d52c4237..fdb74aeabf 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -24,17 +24,17 @@ 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 71146dcfe3..1cd0f91d5f 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -20,15 +20,15 @@ _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: 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 From b923b203a06f4768e4793c212c10ab2594784536 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 25 May 2008 18:19:30 +0000 Subject: [PATCH 1258/2594] 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 0000000000..96c0a2395b --- /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()) From 46e1390e226138292126a2ffb6a8e960ff570e85 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Mon, 26 May 2008 11:42:40 +0000 Subject: [PATCH 1259/2594] 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 b5c2aa2339..a658f1aa1c 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -28,6 +28,10 @@ def test_build_ext(self): 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 From 882263289e030cef4305d310473f9d80401caa56 Mon Sep 17 00:00:00 2001 From: Thomas Heller Date: Mon, 26 May 2008 11:51:44 +0000 Subject: [PATCH 1260/2594] 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 96c0a2395b..20c10745cb 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -28,6 +28,10 @@ def test_build_ext(self): 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 From 208bfafeaaa2900a5bacd8534cc7b8f3bab0b947 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Mon, 26 May 2008 16:32:26 +0000 Subject: [PATCH 1261/2594] 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 603336c954..2cad2c7bd5 100644 --- a/command/upload.py +++ b/command/upload.py @@ -11,7 +11,7 @@ import socket import platform import configparser -import httplib +import http.client import base64 import urlparse @@ -151,9 +151,9 @@ def upload_file(self, command, pyversion, filename): 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) From 52ba3bb9b2d0a33e082e139a2a2e48154dbf60e2 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Mon, 26 May 2008 17:01:57 +0000 Subject: [PATCH 1262/2594] 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 6ffccaa095..0ecfe0cc34 100644 --- a/config.py +++ b/config.py @@ -58,7 +58,7 @@ 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) + self.announce('Using PyPI login from %s' % rc) repository = self.repository or self.DEFAULT_REPOSITORY realm = self.realm or self.DEFAULT_REALM From 92a963ecf18661f11b3e8eea815b6a1bcb69cba3 Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Wed, 28 May 2008 01:54:55 +0000 Subject: [PATCH 1263/2594] 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 7c43e7459e..f18e318cb9 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -79,6 +79,12 @@ def initialize_options (self): 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: From 4416152bdd4d21c96ac792ecefe9db9239460e73 Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Sat, 31 May 2008 05:11:07 +0000 Subject: [PATCH 1264/2594] 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 10c981993ba2c2e3a977db8ded201a46d529749d..f57c855a613e2de2b00fff1df431b8d08d171077 100644 GIT binary patch delta 9158 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 diff --git a/command/wininst-7.1.exe b/command/wininst-7.1.exe index 6779aa8d4c1b0d5fb37255f1eea211d92ea6942a..1433bc1ad3775ec9277e13ca8bdca3a5dbc23643 100644 GIT binary patch delta 16824 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±-*x5J>Sp~(&OLVN&)(&WvEXW=6dYZA+CWV!u`l;IADKvZ_arYiG6GIMsy zIYcE*LU#=rIt$TM$Z-=)?bsNNcqlO-3nJ>3l}PPhPvr3Lm|SNcZW>FFi#oe;raucf z4J&H|jZkMw#MznE(TKo}@@p&ruV5g09MRhgD!AoPg7U5^JO1@^tZK%L56{tsO2pru zS$f4k0efhFkoXx0%D=6e37m=5bJnI39dCzJp-BFoP~#PjAr3P{jyhdEtV*X88_0L4 zbBCyWu7S#Pm~i~#)wyZN3M4V1;UoF+YMb>C$#xG2cR8<|M!KmHFZpowm#l46Q>4{& zYc<_eGtX9nLUdpvS{#=^4;`nJY1|>adsaxPl^>`nxD^QM+*I~l)Z}R0f1>Kv)^`1) zx`3mNhX`}=NNfQ-0oV?Ny?7U)BLVOf;2FR{zz+a>-ojtR z@a_XBfINT?ZAt(IfTe)D0f_)3U>9gz0}lOqak24JKqufR;1FQnn*ok{0g20i-vT}Z zOhPAf01E;40}22e0Cj*M;Az1307n5Q0cQbyfDZu;XkQKZ2)(^M{I1M-g zcnR<(@OR-I1c-nQfI`3uz!HEBFcn|`3;_2EfPS6m^Dy9_+Bmts?&a2F;mS#Or(%0c z2FwCj032WtF3M#zURyU;FhPf!1p5jsW_>#aByf99%hIB)=c&@5b}2F#b4v zcr>>ZdXXB$)Q>596%3Z3tmOxzc=-^3VJDP(6wkwTqi_ijca0WuB1sZu?B*=q{_7|k zZh{2I5bQx&EeO`W7DvMCX-tO_eD;oM|Fa(dXFW2t`2VyXTVqF7>EvZc=S=<`x1z41 zqG0Qzb>($CG_&^adky~efeV*xrmsdO3bT#Y?WA1RfN9{=W@OJ z$LnkBwpNQEs#An^o08QP36^F;1lS|YW-qG^)`9+TdT$|*OhIpc~tIwYiZ_mUD=j; zv0Q|;rsiTzCH~_A&_vZdXCJrpLg14p4A z2RD3Ddk@Cn{divn^u_aB{73kmC?kA&|2_O^X!k1c=(MyK@0obF;5`F-7*~me1qDTT zC*hrqH$FS(((s;+cOu?;ybX9K;yrZ2!6|@$pQe5Ejv}rb`QKVc^XPsGGtsk zl=5xmTPw>St*nqQi8EU*{`ER}_x6HWh5l-BYgI*xv#t)RjL{%dpHf&;RbDNBZAX<{ z`NX1x_4vclx1QK48=uURd4eZq%F~{3w=UXQqLVE%Y&+5V+>_{Lpds9PsPPV+{B3c%T>1F@CfAO7BAQ$G*fvhCYg{a!ermnEEA(S| z#f}xNwm=d!$&-hJ$+9CTn&)oDm>>nLq5D$i*30vnUXu&LwerR7C6h^xBR?G!>|>LB vYxlWjpTM1)G*S*n>CbT8bffvcVN5N}Z~S-pGdNIV@(!J@^{#jSea?RZ&ax3O delta 16530 zcmeHue|%KcweOk900RVPzzhTk5@gU+z(yvage2YpG9gI76Nbz%LI_cUhiOPcI0wNd zOmHS%riY=>gT1tuzJl<2Y303#4aKO40|^QAQj78{Dr(wGeJ4)T_)Ia{)bqY;p9u-- ztDk=EAMc+#`K&qntiATyYp=cb+H0>psjoNHcO>l2F)!k*6l3j< zg(~s<^tEHG{hl|TXYD?H?YHz?5L&kWF@ZYg;ve3{afJpGck&NkEbPa1a2CTz!>|$D z8en$o*yR;?S^?F{BZkQaPI=OB{~ayQbKG&N@8Gx zprjhjvwv0!G;vuIf8x>qNd@-)nzdzO8ONPk0xVi`JUx}w#t9=jl2METtjbfyslz)^ zN(EeO{GY~Uqm+w=KA%|XZ5_&KwtdyWv)6( zE6EvlU65VL{yBnmlamX(+9R%W{!bh?`EbeQeAPP(q;)C5mXwC5nAo6+_whn6v7Sg| zCSJWKgX6MWp{P4iIf@rgsdMtHe7a4eeiQnve|Pc=ex9>GSC*UbD4h=)v+N zE)p6-L}8aIyUf8koR}SU^~kOsEnU46EqG1pW^bA;W24e@r^B#ONlUUDf9+M4CjB(Y z;>8?oGKF19amMOr9_6n|6EizJaZ#*Hl<)Lcp99_p8G-%oR|NbwkFwY{B|!F5V(n~% zP>yJ)@;6N@mb;2PUiowR|HR6EXbznhOM{wm^CR`2uaXL#KQz~Wo?g4C{&UB+yXrr$ z6cg(|uaV|DTZfLykIiC_)}bTvAAUG?kKY_w(pQ9`*P{#QC})H;gvcJ=F;sT-<;P4r z?#OpO9Dr&5YbgwO_4($5N6wI`ie@wlp^;A$?Iz7OhuTlGUvpw8hlr#Z5^nD@8!#=o zhw;uy-xI8o6V`hXS_Z>$La&I|%$9aSs;ywv>v zOi{Dl04-z%50fJr^Y-(%eFMYTyB^0)P3qG!{!$yIesUlutj8x#%@2M#qUHwbgmX-e z9LTQA0p5=2>Xql4<&3{Pi50Nm?AE%GypXq988OZtn6nuj4UEIy5VeHzp{EE&DR9rDKZ8fG;;Gq$TWyoj zsPhO119J7r=16hxG9eWtyf#|r-38Tl?$GNssYAx}6!EAs^TGvG#j4Rxldc1={Eu|vF5RF30? z0#pv3R46mHP%zQ>8ZN_ddB3Jfvs8~qvt7Up%dTG8>^DT7p}J)dIvH$ou)r zjo|uInExFH=aC9m8oEPnz|?W&MNPx$z&f}Kim zZYU<{#Xb~Gda)P9ZioSbZWKFMG2rS#`GSsCwX2Ss+3$v3H!PuNFo-Ov&l1|n3O$xk zkQI8xZDdMY$=Jj!NfoPvC|YZm&79PCRj^?BkC-r z<;=_>XRt!rX`mR+IeLb%LaqwO^&*Y^h+ZV+#5Dpn1aw6)?ZuS?EyR^Wx^bM(S^_N$ z$HZqX>9vG7R)9J_BXun4wuC-HA^S~B=zWIhvV`6t#A!?D3PY%t&;=A^$(;R(C3F(S zId@n>$MGz}q6rj`2~p_a^GTBr?!%iW777j^h4R-XY%EJ(< zC*bwKEXhLWfRMZDBPnaCJ+Y!xf7qhcmed6paSC9BWmaA8sk=-q(3DFidMp#&z z`nNtBr5Qt%dmErK(WqU8oN`+%A$U&fmuGv|ZfMKx@jBjiLw25IA4RH_Gn;6nr=3Nw z)^34td^kY8Gqne!sfekdFwFj0C#SyhS51?Ye`aR||ABp)G}sMAQwl5% zBY;16uAQO>OK2Y|!A597x1HPZVtK)3l`~hNOSvxTbcYqOhJXW5|H{1zVy|E{1qLvk zFQHk#$hy6$QL-Rk=K<9C(}j~@ev4qx&emCv`nV|K7!u@*0 z#IlZ?$^#3=1~5l*ZxCyiV!Neo+PHBx*-4X4hY!TO?YA9O$Z)R(>#%=QgA^h%F+1MR zX1fovU8BQEvfR3ocEw;W*XD#t%@5{YY)3)L@*sl6NS2f4y8{QQTINUixk$ksMF|&| z`Ay_@Ge`gsCLW?znr#KBR}aLvkwW7K)8|<|)8{366Mq7vwtU!lCMtpn5MrT zwvj;7^G)&+^YnbPJ8Y1@oW9M(BC7b3Y;SnmX9@iTY~mqlymA#}MxeUE4NK~S=Hs;x zUawg0h~ikNZ8q_4zfpC3L=Gu{gBesR76R>sLC~UtI@KR60{7Zs2FwM8`tlMhwqR61 z98xZwx@fI9V>aPBx{A9$)sAIGVLekl+kaCKj}$<8UWXP-inwl|k3ziaCB9}1g}+`e zUeWtiY(ejo{&?af#~hG!(iR9G9dSjm05M}ZK+5fYL%0OC>coqGgzu-HeJ*4`yN>e; zKQU=x0i=?1^YG2PwQGKZ8>ceR;{8;|8O*3!TflKX-s89UEpUZ7sL(D6aM`prF;u*J z)D;EFa}&q^6OWf$ednRO($yH)`7>4y18YjNa-5nDZjeyUT?1OjO;4Mv9eXd==DOZ{ zw^BbRIbcUKf#d3NFvglG1QmR2#MS=t7RbzF_YIf81*d^(9mYP+BUAt{6yi%8T!1^>VjA0mXC_$++<- zm0NGcNLEsEx31(AqRwR6g){dXuytIO5kQ>7zA*jbbXT9BQ!*w^4WtqeeKj{xDn;$d z=eS^9;!;cK$56rcx3JCpyk2k-w_}33GZdGbr*VqU}kG%OkB85+P08Q*>v4>T7d# z4FN{XiMYCW52QpkL$evp)yntpo>gjsbc01FS4zjCxYVILz`t}gLlG%fydGE=dFX)R zH$jbQr=e#Yi|Tb5DDfo?XD-=Pt%N7d9=#W15h_S`q8+QzRHIy+bXOn`QGT4?va7~HW@73{n&`QXjgmO?_>Mr(pEulZ7mzifsUB~UIsE}up(TLNr*V-`Y zAWsAK)h|(Am)&M*;7~ZN(iZ&}H0t~W&ExhbS_8M)pGUj$aysnA%?83ac(G6tnK3>J zzYSw^3s7VI0D7^Grr1f4QH7c-aHe^7t2idui)@ykE8vBv<2CiU#2>=CBO!aL0ghkb ze=vA41Dyg|-I&q$8uSh2SUD-Tzin!~Ehm`2scCFT(`9HGu}uqE4|q^OBTpjp_9w&* z-lJWD14*t?o|t?uPZlpvqMlx*leAa(7#ahGSLp69L?N}hYCeXiN4-TTBJAl5LDsJ` z{b=&I+5F4Y#vL9-hiBCSdY{i7=gdZhSW~no3qOo&#tC_LY*eySCj^{mD6r%3QQcfp zkUqlpgARkeMrpEsXpWzl_k)DV)b9_iU~ARm&X|>@26U01m`5TEZmXta?&RJ`o;}sg zt6tQjn8rV-E<_OFW2kr%q~QjpL!VWTAk->%&%8H4gNJ*a2(#t&Zb5?{ z2MzI-R|?j38Ke|QKqLaGPna{P7g&$bafT!z`_cuC+fW+FZfS#8-+PdzKiSDg0!%-= z-+=y8h!-7PxB;0=!1WJ#uuO3rYjc}GH?U&+l{08MF0+N)KOGnKWsT_waOhjhoYPDZ zL!Xv;9FikP3PlH7oXok>nQ(v+m4w5CnepnKY&5AD&A?zz?F9{pzil)D-HPHZ5l;-# z?ckw6TeK&{BZ2K*wE19XAoFEbl&M=$AKT05n-@fwe59d=jB$_^>6m`|u?5Jc*jWNc z$cE@@mVc%p``GEMiS`Bcp@OB%sN#6zFbq_3Q+SG9;HG4P7KI`88fWn}Z?hhX88iG3M2O0U|Ct(wpMMNQ9afF>fRY)TaOBWu?3*k70c^{cvA@{=t<<$9u|KCh@$m z0*!B51IQ34*7UJ~g0EHxMjS0DeN7=K`ZMDJONt9l2@Hg!sd_hVgcKy`o;?KtI9XQ@<|o*j8n_4P_Ija1A3+}>k(9Iw?LeS{-J{U1 zggcfzTpy+5I}x{QN3v*V){oU|Xfb-cILrEJeI3S%;SqG>stPj2-y;nNFk8YZbPgM% z4K9vZp<~Ee142a#_OUBjBIM}N|)U+mW3*5p1 z#5v(r_9Qg}>?mz#2|a?!Vz0L?&u%}0@rP^eX`o0&|7?$8$!%Hf_tm_<#}t3t27|>+ zsc%L{x3IJkeQm$d&vtavMfwwGv)GvO&8=!MaD#0nxiH^)X>5P@!fjhcd@K1+?HI-S zej)dI*^U7b^4Mk(I6#`FD;!YlM;RMMYhH?-tFN`&xe{?a{PbSv3k}j>DT7u8Sp`$H z8p}F(KU1n)GhJA6$x{&noWGTs>KJJ@}zRS0HTKQ#&cx zHbQPY-C^X5uz8{{8Ze}_ggVJT{=da*sKMa?+H$(jfSu5JJ>ta5@>h;n@d(nPumbWgrQu|&bwjUne5t+H6v$U1OvZurMv_~ z?TVOoXz94*^r{`R`j=K2k-2WjbK=8{xXiqegnr<@&~tXPj+d4Qbfn|eU&dQX6WfLa zs8_VF@1zqxn;bvaS%w`>ed0q+qbuPARQRpD&{PkL&@HgDkB&2}pTofR_an{ns$pC` zL4BfGbE%!@NPX-&AiLQTdI@jA-ULhNc|yDTI)B9>*DzS<-Gi8k69(688B`-#z3Rtb z=-oW4xq4&egDKYzrbKEl>vK(u%^ys`526T2AbE#KQU{ad3?h++dDTvI=hIwdY}u|J z%g!1oNT2u3uzYg6I1mmYS&}qhu(!uALC*gN*?Cq8qFXXVaRO~nl8W6)P_e9e_&+@) zNs8Ix_!h$Vqpg0YaE#VJYmW3CTdC@N+*si{s1zBm?Oi-3h~0J|73H|;4I|ayI3F{~ zPSZR@hrMzx1&x*$PSZ_}hvhV1jwQe?&_(Pd%;`0c=M^IkB*hFv2(UCkgP1JSInTR{_i4Qn2u|3oIvT+ zZ+RwgI*c1wcEM=IB&h2!k%$LzHG`YjR(2B`&pj8hIORIRN|wVM_@$5jBMprEzCXcb z4gAalY*Prz4d;q<&ZrQW#kqqv-mC+3(G8un`cs*P+WqX z;t3R>@8foI^0;77H=YHIt%qjwOqEwr)>yfvw0h>4(_8Ge3`#PK0 z>+!^-e9~?Ok8e(o3oIX=XSWI}W+-mHlnp26mx$dfQO_O*;46g7AYrzFm(CAiy%;eK zM0T2UAAm;{XXa8a+|uwmxt{*?U|i7Ka^ju;i(2PWO3Pg=A7z8YWrv5IZHmJ)>6Zp= zL+E@BIv6&sH#!Z0;`?Orz+k%p;#E38%kOy?t2buA8|Eah@`jof&!$2eXXPcb9Sq)jKIq(=1H7TkDv0{gyT?z z8^Mc~No4A)@4#6Eb4gsJ04_sf;Xs^1sGRIX4=rctn=&q5?Ek6i@Jl2D9l=KmwIEfe zULDM04_E|qGn$>~GFw;!f&g_V6rwbEamIf(Q1B+D9`V}*(c zIc|of?nqtFbI(2JPXsb7?TpmzRz_u{4hJR9n(dZp%Iu8EDIeYowOnQQS5ZD(*V5*? zuz{n?uJ%Z-vN0n$@x}Xz0G5Gcm-0-;Y&yrX1C4$`wEI4|L*((b%nL~V$+^2x{R#ym z#jxCD{$*O^ZXqv(yE-~H;%>{&`%q`|HN5?<0+Yx&`V`MkLNvH=_{ST%SvPIq^Q>7;EB>H$hN~D~2P$2|Mt0TU5)k zp=xUf!iz(x(2=?xkp^YR^(NA;*4EI0XL!yAb8Y{XugC>(|=`?u~zZ=+{N)aUF}U`q#S=?Sz< z1?pd*^r!kBIKaJ;FzN{i8mkEAQZ46|?v`^45xC(80dox7Y`D|M3;h5ihroEA0l8RD?ra!OT&a}pkhOWLOif8fAbb|-aZ^`v3M>hm-3ES zttB{8ZA;7AW|xMw<=fL6WmlHbkfEGi_-A9{kCdYY(~Z5mls5|&8#{I>_bhUaOZ*XT zu`eT2d9$TMw93ovg>CbbTXr=*wdngM}${avz>#jiTSS1svE7F)${GP#hG1B-K&oc&NQ$*Rh;%VbP zpnb0yjP()a#v^NtLZmT&4Vd{5Tsk1M*vTy}baF$MIJpCW zgr!a{9gq!}@GxcsFq(IAKL+eAf)1BsYCTTwCBX0%PHsD3vCqj>13Zs7xi0{wl}IW8 zegPiG0e@cVOyIs)s(4D)8F5*3s${$|w6XEcl0PRrurC75aTB39S&hxWAbAY13CLLT zkyLSbn~NKXy4V=xocK0pGRFFGnaen}em9!s+0&zVhL_uC#Il+=6lWT-U1L+pXmaju z^sX%#V&wKHC)TG0xSt}lIq`;^^bGe5l4MupcDm|29ZQyH{G8i^QTj}dE7LS|->y8Q}+v)li~IGCPc9GcNYzIgfrFbQ0b zbNL5lAP%>SF(8BdmgDT#WQa>}R-VCRKzqkoN3$Hbh!T^eUj;OL%ZclbBsMs?eUK`~ ze~TstL~uY2gBn7IOro!=As}BwXK@|eCWqYaxbn5B&?n-GJcCUI4dTkz=3_t#QpA81 zq!L{KIYpm`v%%BKTi>62Kh2^(>{|O6Ip-CcNOb?Vfwp?Te{|12tTiACkzAvzkauG0gz?m)_;qGuo*&PmDa?Ie3Um_#q!q01mt_ZT=%odK3vlXjnmBTf|N zRH41iN<38qDul!+R;C$WY8Qxw>y;=KpmeZ_*kPle=W53l&2hX%To-XRy#P1|M`{L- zU`uM))so#@XZD-b|IHzs6HLU9BYr;z7u*M^)ykAg$A)SrchhymAP&Wk;Eu);{3BM- zFB1?as!v|QcF`S{qFE(3q+#&fU7*!$-T00b=iDug#5tp+!?O2ucu5E54_)`5%7@Lt zlpmI^=XbJXfeXKHmxqU!T;_|^?>8suq)x_hZ&nu#iMnL4GR z+O!#X6-dXz^>)QlWw$w>#}M{mNNuhgO(<`#!$a9wb+1xYWw!BDyIil0>a|g-{Vx7b zy!>_MN`bshl=G9vJ%)$6bDr*XBaRs$*lPXe9>`~+|quoOU_$`c&-6yV2zUjj}5S^;kXJ^~nD;JC3bATN-H z!h?W9z#3F+!7~S)@ql%J&48VNp8?js!f{9Ni~=qKW&oC=&3r%x;4Z)&fIi^60c*hP zT|DXY>`Cwd>;^mq5CIh@1044#3P%Ai0e%O#3itr<1z-d^Oa|Nsco0ws@BlUgo&xLy z>;pUt_%)yaZGMC2Ts+eOX@I)`t6Nl1Iz;C0P+D# z0V@D&00N*Ium$iGU?<=wfM)?m0Ve=az-xeZz@Gpg0lokjAw(izEPy_KbWWeZNvP-; z;8}njb2pae575&DNdcDut$-%LF9CZ2PXjgs)&e|$`GDDgdjMkqUxL?7zz2Z00OtVo z*@sS#0-la{zM!UD2|jhOarbG_KpP~-7cX-aE}F0W;`iefYjc`$6Q?X~o-l5mRQZ^= zzN#{{vPw)X+g!H3qU_O%a<1|5<`RRFdVZer=ku>BO}~F|WoiEU%Cd^}PnL81)@4#< zseAo~>WXr=ShK$Jad6-2-nyyOT`tb6d3-bVwzYIo<$7_w-h$p;H8rI?h()RcAzxGV zcum=crDYq+m6G#6uz;tyenWXG=&EWsPHApgqMUqV#mMDAZc1HNwV|w1asOcnPJWy+ z{p<$C+BQMC+UimEolRFRoW+lKlm}WDDq?Fv)h07n@Ca0zrOywqMnP7~*H@I!Lj_4o2L4;8amwwy8XC0hef42R;?_1VTE$?g}Wxl49m(NFOte@wtC7c zBoga9VHQ+WZ7PSmQ$BlPn&nS~dAPc~5(w9pdCJVD&*qIy*d*47RbYM8xDLwWxc7`3 z$|@?V9z%d+D6N*n^2*KJI78{WO=6jdoy)jR*9^D7P`bYAQBILxT;x;@rJE|_cnfi? ze5`saHz+Sj*a@{-P&-(at3!W8#wek0Uk4E%7favM@ZS>S`EMN|3Y2Qu5a{`{pc#fyL zn%+%L%BpZ3cqZd{4$onDHsLuA&jWa3cj9*AnS^HuPZ5whZeUkfh4;y02i^GUQuZ8S^YcfQ>CK=tN3lsh^u&Hp3EnMW`- m>}yU8G7(k$)855#CK53I>A?TLJu>Ls`3cuJ^^fn|^?w0O%ix3n diff --git a/command/wininst-9.0-amd64.exe b/command/wininst-9.0-amd64.exe index b4cb062c391d230dbf4a7fc3a16536aa3dbc5b42..9dedfcdc2398df99f9540120dba9421452795948 100644 GIT binary patch delta 11202 zcmeHNdstP~wx4Um7JO_WWeF(A7B_}sN#XfQ?YDz+8YKeWD>Xg@wf+a0> zK4zUfWv2G-^LVsf2Gmgu12rnElgH3wW@+1?vhpbTn)f&6nxx))zWc}hyZwD@{Kgz} zj5)@b@3o-xXlUv2&`nxm(U)$6soxpan{~9cXKWP&FIhs*8lI@#Y}>q!Ptt~Td#S6& z-lB>EjkQDp^E4J7$~S5YwB@|L?MAJM-(s8CAw1NoV(s`7wiK;CV8~(kXr_Bvybo^VG+W=oz{`CGg=%$yrhaLm+G^f; zz!t5A?-=0JE(SguP!eLB>fvM3W{-Le`531C)GvUvy+%d3YYil&S(%+R#!5odYMUo{ z{PqDT8GgO>K4|$pF1OHE7BS}W&vK<2{wXducMOX5B^NNZvbLngFg>nRq5nLGu_`oz z<|}2RMp|RgI}YXLd&z5^vApH5thP_P>b;gEaN%sobvd6xLFn@f7)#rqu}4M|UAENa zvOmewas}7PGVrn@mlFXzT*z2itJ60C8lljj_)>GtTA|;M8d!g~1i}P#L~2fT zB-%~eFZB0NMVXTyifJkT*Px+VCf5f$eP!qxkH6e?oa+2~3z_>XU1#7_=$+?E;pf*0 zH{T_NZ<5g5XPAeDJ{zKI8hKn+eJJ!%*UKli$!}KjGg^Py*7UZDZq&93%aLH-oiAhZ zxQabyUD{{C5Bc%IPR+@+A=$nqvl+{%^G>A!&i7CLS?JRt5&9TV|Ktb+hBl;R0BB7q zwM4R5l*lGe=vNe*FZ7FdT2inIw(1=@;Z|H4Xp-EBd#)jWk`JF-K4Ox)*Dh!ijF6F%FsC9=25_w+wofwc5OKLf>pzy|MT@t7b^0 zjnHGDZzb>68WLH?}G$4y>(pfC4 z4$DlpwpC?;L1IF`oocBQ)>;yz`d~BfKeUIh0^Mchx*M@zy2dQJF6#CL|0CCQ%b+dS z3d`z^`>(V5T;_%T5jumu%=ITZr2jkDt}|B48j9T;YlymtT^)s9h={RYoRM>H8?{lk z*EzG!J=rL0an{_5G%&&bgI&tCcFXvhlb1;P&tg$cFlgu)$vrISmuD5m2 zQ2NJV)@8_^mED%*plY1LHoVjXI3f+&X6y^fe3p_h2y`M+euY@F}8e}{EstvTt@d?>RZG7L~ceO zgO_#BgYgs|8Bw1b=Vql#U2aB;tN|@x;xSA-d0rrTEH@JBLwYwz-n8JQiojYp*% z!$tgL#!V4BtrdyhB+dD}S7y}A)sR(VS75)s;%$#kUx;$^hlXiHqA(2wp>IdFhDl>w zLK=NZNT`LW6Iw^F8LMviQ+aLTdz9uu;_T>!zdS=Rir=W*95^eaGW==H3 zC*s41T@gb6FDWkh(I~8=BOuScYEdZmvChV(wNq#Rso*mWarD@WKcXC;4ho4G(IbC^42=Rf?ZSZ24(<3pb7h+D2G(R3`?v-7<@&_*s(hbnASmbgFeGZyxc0hCp z2e;7kP(*L;K!GM!UQV?eUgD@TH=-)2|62%hS?S7ydpcXDbDKNc7m03F+e9fk2^&ZD zcNhazp%yi;4#jOn-Mcx0}BL^nDwFqIHh^>k{( zu?0#-uca=h)Iv41ur!~OwKpAw&$M&K${!-io$C?h`6E4!lzr35V~#}ZdNjI^Qce~1 zLLb4ua>pf}MAnu_?@mq71w+pM;OB>hfWJ5%pA#J&g*w@Y>G%yJUekgIkv<=n(^LD2 zFUZO9`DvdqzZ81Z6lz9)UY4$GW}!d&qdXJn-Wg|@xLL~UU9ja4^7T9ZiXjzxf`G@ey~(&4xF=7YHa%xb6>asw zdqzPnH;^RW|F&_reTvLHX1(kWxnK$~E1RR`*mx?inOXzXP3W&eGg#jwguJhw-iZZ= z8L>w&UUOUgZA+*fzx~4ZkoxuBa;nK2ZmO)nN~j5<%cYBxmhLI6#H)S}PT}2id--(K z<;d}~RzXcMUM7Uz4&u`ea#`VMy3AOEJslL25_Kogp@UGniGPxt;Cuf#?MjWoT_=UeXox7E2XCg{ zcnQ?;ENIa2wVYoM;4s}~kNhb{VB^)@x znf27BCfnSNP5~ce%u8=2@1b5r(btON0okVAM1@)gkyPP#{0V~>?DQdJ6*OjMG`bVSDUKbbx_^)D?|@r9?mdnGapB-hu=0jrSr37gI2Ih+b?{_myS;8 zejh||TP|No3Y0}U=8=cLJ^BvK#v_d0zG*qIlZz-Su>##Ij2`*GBoV?H$f z%XUCv2o%tSoV#yYmE%x}nog+kSydHwFTxc1Iw<&;)!-m6hK~yS3`nsv^so#X=aOIQ zfol@J2PVj?^*5?ROt77T-4L1kN!_n61Hgc8nnE9DX&-<#6gz&4rO}qjXiE=)VuYQc zSWZIkXsK_o)St&JwS4^UAMKyNi!{F;OU?#ALMr~OBal_M!zkcx;0%6wS_pbmSOr1w zQjiKIzF^AtLY(&Pfvn0d$7?Vhi7SC`0eQ=!;x-PVRsYyNt*b6$F>t}Dhn?8f+og3qr(2xImA~l zSEys*dCE$c`8zh!fUn%|Z71|^#!zsMr>;uIKyv-!o6cLt#`*9apsXdGJYx`Zs;EQB zcNsN+!9O8G`1dz(={bl9D(qjwJstm1xN6DX)E`33=DyP}B1==JB#=X zI#iHhGPQ-x#f|L{*T}G-5BD5j^CL!C)<;ldB?*{AEl8ydAC0*28V81P(3}`&zM(Arv!LM(Dl-z+`3>KH#OO5JC z!*Ll1^*kD)-Nj2MCTKZQQeNEu@nTfU@AK~q` zA!mMj_-V*lBqv2hPCn}2dMNPZoda#!dcJ2)SFI@U#hf{z8ZMbd$+o%&_?)5)?SB4T z(LQZupy=*3n(gRfer{f&HYYISo_I|g!ylf%Q(F)iaPKKiE9Bi4uC?u0z+YcDSzF34 zFZ9{=-Wyo9=q=55YIb1I{pFfw@an~t+H-tV@i6T#fzslsnwG*3EtzcFT^LAt;2pa* zE%41ltF>OcupfE+S?2*cW&1?)O;t7eFqlCA;uiKG>>;FWZqr1_GLl{8b*nCKX+N=l=D)s;x<=ab_9tSFNH-_zv(m?e4A<0Ki#-3CuA zjnDjgtGwVH0$@2!e%0FU0oaj-NpK_rax3D8QVAy~PK6k>P+?ExkCqO!y&A>;RvPPj z>3y6cu?B8|VY{$P<9)B1E}?ikh(e*sv_A@kvR196gWu>G+=eb}9&Xy_L5STtrNN#L zj_lwz7E1=Z=><3mzKsnN`(sp%a%un|Fj2U)1J(qUp{`|n@fZU-mqVY3j5z) zM#x!}psXaEHtn65XJuK>NkJzZjwqV;3Ldv2PV@3%D|#l6mlpk`LVL2Hnb}yDmELAT zWz*h=`&K0Bt;IrkL(*#Nj6!=dyds{H#k_jObiI;l=zVIUQRsLV1q{u@JgID)FXkoc z)A}xy2V&G4i8CD^5meYu;j#_*4q)2-uVaujoK#h)H0_U2(H3k#hXjY9`;o^MN(}Gi zjzp9(su-!VtmTl3lJ>IoD_!NWY`wS9QOtiTb9T;>F@)WKsba<_^Z4@7w&06=UU@&? zTc|PcjhrqiIQYU5OAd)l`?CZU_KrA)4b%F21&W;@ZLpt!$M&JDw!wZBoSX!v{f^CK zkn5Tcg_ zDFP=n`I2XFXFh!8Y~KrzVRlfGBB=>kiKL^T)+bH8funoh`#TwDlMjTHZj`l(l8*8= znh06*vTD2vu;2+BV{4%Oq|}c5UE6ecrQ`}6k-b}5rc=GS&%@&$i|HOCmF+puqi@k} zykT0B8~KFCGQwv*0=YkH3SawJYIvGe=#N70M3M9g6h+c&lD;A7R!O%>x;>J2e!LRz z3C}$~?ABHUAd)UfdP!0Wsj(4LP(C7QT4&O5gBp0Tiopt|QBU zaj+vb(Ijc3q{n16lMb3Be^gRSeq8dubP7zV@YsACdeS6iY^%a930B#r@QjilQ~0sM z8ih|Nl#PHKh_&pi6t>AHD7_&H`&jt&Zms>WP$iqKaFN0h3ftNk(!*+dtil=3+@NXE zz9&_2i^81>4=6mQ@QlKCaaJ=?3X>JO6`ob0hjzF0ViYDT`}L}vZK01n+}l!|tI(q` zQ(>aQ&I->aTKdNoHY(hvaGkYupN?|t@X$OUul>C&!LkjmQ ztWtQh!dDcwb^MiHs>f6S$qEM&!as8>fuV4c!gVU+*A%{`aF@b*g&P&NC`TWwa+4|_ zRJd2+lWKOZ2i{3hZc&1*3U?~3S9nlilfn}UTNIvC80xe#?W{14=d4cfWvkLCg?S2R zC@fM~tnd+qNc+Phq{nj};!a(8rD|!S@Q!D(s;8BSvAeLbt*R3a2S7 zQh2|@2NjkotW@}{!tE0Ade`v*I>n}QmMlG z70yz)K(&^w%Ka7gRM6E2w7s&D zxwW;ux;lzhgMsduwNelrO8?stcx`RoJ$5=R7xT-l?`lQ7`nL?d$i!-ZZfuVb)?UkJ zwp7N7c>4hR2fDCRoNM*qLL{8s^X{S-WD=+;aghwEPRHsb3_k!IDmu zv_#UUFAjzLql*bQP&=hKs$*kqEKEy*{pE`Z<4Ib8V_(BJhR_jhwK^mD8uBW&d&qL_ z@8wuo-cDs1`&V8il2N)1)9YR@$ zLE)^!z;?y2MUkQPJB5upZxdtT`c>I1jO=#EYGba-S}|Sb<9b$mjcqB;mif@jzbeaS zE*gFaw_jsC0sW3*f|$>Uht8?J|h_`2cHZqz_T8_MDUxyuLoWp&Dd+;nZeis4`chlF9Pl# zk25_|^RY%S2PZHVd4ndg%S1Q_9|fF-XAt;&VB;hN2cB@w6r3L53xIQ{jUy9JEqKDIc#=sUxExP0c>DrMPaM(s Naa-a$;W1jP{@;-hzHI;i delta 10853 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 b*}!e%=FMDW;|Z-f2=yp7Xb From dcd2eb88e377b96a037acf6da4729aafe95607d5 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Thu, 5 Jun 2008 12:58:24 +0000 Subject: [PATCH 1265/2594] 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 27a770b194..9993fba1ab 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 dc759ee8f5..6144efc070 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 69d90cffb8..262a9b8b04 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' From ff2fd5ca8a081c349f525318bbe8e07c8f1fcc3f Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 10 Jun 2008 16:57:31 +0000 Subject: [PATCH 1266/2594] 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 3f0e09bc4c..ae7d4fde2e 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -75,6 +75,12 @@ def initialize_options(self): 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: From a7d9be62dc156189214df813236f2ff56a66016a Mon Sep 17 00:00:00 2001 From: Amaury Forgeot d'Arc Date: Wed, 11 Jun 2008 17:46:10 +0000 Subject: [PATCH 1267/2594] 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 73cc00ba0f..c0eef62815 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -646,10 +646,10 @@ def get_ext_filename(self, ext_name): 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 From b06cbddbcbe9c30374cca3e0ecc4d96b61f39d7e Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Wed, 18 Jun 2008 20:49:58 +0000 Subject: [PATCH 1268/2594] 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 89cb2d410c..6d5c459924 100644 --- a/command/register.py +++ b/command/register.py @@ -7,8 +7,9 @@ __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 @@ def _set_config(self): 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 @@ def send_metadata(self): 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 @@ def post_to_server(self, data, auth=None): '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 2cad2c7bd5..5049f03e93 100644 --- a/command/upload.py +++ b/command/upload.py @@ -13,7 +13,7 @@ import configparser import http.client import base64 -import urlparse +import urllib.parse class upload(PyPIRCCommand): @@ -145,10 +145,11 @@ def upload_file(self, command, pyversion, filename): 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) From 6f6285e128b5c81398538d25ece8a9af988bfb4a Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Thu, 19 Jun 2008 00:35:43 +0000 Subject: [PATCH 1269/2594] Bump to 3.0b1 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 3618c4f727..54fc2369d0 100644 --- a/__init__.py +++ b/__init__.py @@ -20,5 +20,5 @@ # #--start constants-- -__version__ = "3.0a5" +__version__ = "3.0b1" #--end constants-- From 07491092fe0ca819ef1c40ec6fb32710d4f2b872 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Thu, 19 Jun 2008 01:48:07 +0000 Subject: [PATCH 1270/2594] Bumping to 2.6b1 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index e3e32f51bd..a192357896 100644 --- a/__init__.py +++ b/__init__.py @@ -22,5 +22,5 @@ # #--start constants-- -__version__ = "2.6a3" +__version__ = "2.6b1" #--end constants-- From e518d13e52d41fc198d3c3f5fc42275b2ca2c614 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Wed, 16 Jul 2008 02:02:25 +0000 Subject: [PATCH 1271/2594] 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 10c981993ba2c2e3a977db8ded201a46d529749d..f57c855a613e2de2b00fff1df431b8d08d171077 100644 GIT binary patch delta 9158 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 diff --git a/command/wininst-7.1.exe b/command/wininst-7.1.exe index 6779aa8d4c1b0d5fb37255f1eea211d92ea6942a..1433bc1ad3775ec9277e13ca8bdca3a5dbc23643 100644 GIT binary patch delta 16824 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±-*x5J>Sp~(&OLVN&)(&WvEXW=6dYZA+CWV!u`l;IADKvZ_arYiG6GIMsy zIYcE*LU#=rIt$TM$Z-=)?bsNNcqlO-3nJ>3l}PPhPvr3Lm|SNcZW>FFi#oe;raucf z4J&H|jZkMw#MznE(TKo}@@p&ruV5g09MRhgD!AoPg7U5^JO1@^tZK%L56{tsO2pru zS$f4k0efhFkoXx0%D=6e37m=5bJnI39dCzJp-BFoP~#PjAr3P{jyhdEtV*X88_0L4 zbBCyWu7S#Pm~i~#)wyZN3M4V1;UoF+YMb>C$#xG2cR8<|M!KmHFZpowm#l46Q>4{& zYc<_eGtX9nLUdpvS{#=^4;`nJY1|>adsaxPl^>`nxD^QM+*I~l)Z}R0f1>Kv)^`1) zx`3mNhX`}=NNfQ-0oV?Ny?7U)BLVOf;2FR{zz+a>-ojtR z@a_XBfINT?ZAt(IfTe)D0f_)3U>9gz0}lOqak24JKqufR;1FQnn*ok{0g20i-vT}Z zOhPAf01E;40}22e0Cj*M;Az1307n5Q0cQbyfDZu;XkQKZ2)(^M{I1M-g zcnR<(@OR-I1c-nQfI`3uz!HEBFcn|`3;_2EfPS6m^Dy9_+Bmts?&a2F;mS#Or(%0c z2FwCj032WtF3M#zURyU;FhPf!1p5jsW_>#aByf99%hIB)=c&@5b}2F#b4v zcr>>ZdXXB$)Q>596%3Z3tmOxzc=-^3VJDP(6wkwTqi_ijca0WuB1sZu?B*=q{_7|k zZh{2I5bQx&EeO`W7DvMCX-tO_eD;oM|Fa(dXFW2t`2VyXTVqF7>EvZc=S=<`x1z41 zqG0Qzb>($CG_&^adky~efeV*xrmsdO3bT#Y?WA1RfN9{=W@OJ z$LnkBwpNQEs#An^o08QP36^F;1lS|YW-qG^)`9+TdT$|*OhIpc~tIwYiZ_mUD=j; zv0Q|;rsiTzCH~_A&_vZdXCJrpLg14p4A z2RD3Ddk@Cn{divn^u_aB{73kmC?kA&|2_O^X!k1c=(MyK@0obF;5`F-7*~me1qDTT zC*hrqH$FS(((s;+cOu?;ybX9K;yrZ2!6|@$pQe5Ejv}rb`QKVc^XPsGGtsk zl=5xmTPw>St*nqQi8EU*{`ER}_x6HWh5l-BYgI*xv#t)RjL{%dpHf&;RbDNBZAX<{ z`NX1x_4vclx1QK48=uURd4eZq%F~{3w=UXQqLVE%Y&+5V+>_{Lpds9PsPPV+{B3c%T>1F@CfAO7BAQ$G*fvhCYg{a!ermnEEA(S| z#f}xNwm=d!$&-hJ$+9CTn&)oDm>>nLq5D$i*30vnUXu&LwerR7C6h^xBR?G!>|>LB vYxlWjpTM1)G*S*n>CbT8bffvcVN5N}Z~S-pGdNIV@(!J@^{#jSea?RZ&ax3O delta 16530 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 diff --git a/command/wininst-9.0-amd64.exe b/command/wininst-9.0-amd64.exe index b4cb062c391d230dbf4a7fc3a16536aa3dbc5b42..9dedfcdc2398df99f9540120dba9421452795948 100644 GIT binary patch delta 11202 zcmeHNdstP~wx4Um7JO_WWeF(A7B_}sN#XfQ?YDz+8YKeWD>Xg@wf+a0> zK4zUfWv2G-^LVsf2Gmgu12rnElgH3wW@+1?vhpbTn)f&6nxx))zWc}hyZwD@{Kgz} zj5)@b@3o-xXlUv2&`nxm(U)$6soxpan{~9cXKWP&FIhs*8lI@#Y}>q!Ptt~Td#S6& z-lB>EjkQDp^E4J7$~S5YwB@|L?MAJM-(s8CAw1NoV(s`7wiK;CV8~(kXr_Bvybo^VG+W=oz{`CGg=%$yrhaLm+G^f; zz!t5A?-=0JE(SguP!eLB>fvM3W{-Le`531C)GvUvy+%d3YYil&S(%+R#!5odYMUo{ z{PqDT8GgO>K4|$pF1OHE7BS}W&vK<2{wXducMOX5B^NNZvbLngFg>nRq5nLGu_`oz z<|}2RMp|RgI}YXLd&z5^vApH5thP_P>b;gEaN%sobvd6xLFn@f7)#rqu}4M|UAENa zvOmewas}7PGVrn@mlFXzT*z2itJ60C8lljj_)>GtTA|;M8d!g~1i}P#L~2fT zB-%~eFZB0NMVXTyifJkT*Px+VCf5f$eP!qxkH6e?oa+2~3z_>XU1#7_=$+?E;pf*0 zH{T_NZ<5g5XPAeDJ{zKI8hKn+eJJ!%*UKli$!}KjGg^Py*7UZDZq&93%aLH-oiAhZ zxQabyUD{{C5Bc%IPR+@+A=$nqvl+{%^G>A!&i7CLS?JRt5&9TV|Ktb+hBl;R0BB7q zwM4R5l*lGe=vNe*FZ7FdT2inIw(1=@;Z|H4Xp-EBd#)jWk`JF-K4Ox)*Dh!ijF6F%FsC9=25_w+wofwc5OKLf>pzy|MT@t7b^0 zjnHGDZzb>68WLH?}G$4y>(pfC4 z4$DlpwpC?;L1IF`oocBQ)>;yz`d~BfKeUIh0^Mchx*M@zy2dQJF6#CL|0CCQ%b+dS z3d`z^`>(V5T;_%T5jumu%=ITZr2jkDt}|B48j9T;YlymtT^)s9h={RYoRM>H8?{lk z*EzG!J=rL0an{_5G%&&bgI&tCcFXvhlb1;P&tg$cFlgu)$vrISmuD5m2 zQ2NJV)@8_^mED%*plY1LHoVjXI3f+&X6y^fe3p_h2y`M+euY@F}8e}{EstvTt@d?>RZG7L~ceO zgO_#BgYgs|8Bw1b=Vql#U2aB;tN|@x;xSA-d0rrTEH@JBLwYwz-n8JQiojYp*% z!$tgL#!V4BtrdyhB+dD}S7y}A)sR(VS75)s;%$#kUx;$^hlXiHqA(2wp>IdFhDl>w zLK=NZNT`LW6Iw^F8LMviQ+aLTdz9uu;_T>!zdS=Rir=W*95^eaGW==H3 zC*s41T@gb6FDWkh(I~8=BOuScYEdZmvChV(wNq#Rso*mWarD@WKcXC;4ho4G(IbC^42=Rf?ZSZ24(<3pb7h+D2G(R3`?v-7<@&_*s(hbnASmbgFeGZyxc0hCp z2e;7kP(*L;K!GM!UQV?eUgD@TH=-)2|62%hS?S7ydpcXDbDKNc7m03F+e9fk2^&ZD zcNhazp%yi;4#jOn-Mcx0}BL^nDwFqIHh^>k{( zu?0#-uca=h)Iv41ur!~OwKpAw&$M&K${!-io$C?h`6E4!lzr35V~#}ZdNjI^Qce~1 zLLb4ua>pf}MAnu_?@mq71w+pM;OB>hfWJ5%pA#J&g*w@Y>G%yJUekgIkv<=n(^LD2 zFUZO9`DvdqzZ81Z6lz9)UY4$GW}!d&qdXJn-Wg|@xLL~UU9ja4^7T9ZiXjzxf`G@ey~(&4xF=7YHa%xb6>asw zdqzPnH;^RW|F&_reTvLHX1(kWxnK$~E1RR`*mx?inOXzXP3W&eGg#jwguJhw-iZZ= z8L>w&UUOUgZA+*fzx~4ZkoxuBa;nK2ZmO)nN~j5<%cYBxmhLI6#H)S}PT}2id--(K z<;d}~RzXcMUM7Uz4&u`ea#`VMy3AOEJslL25_Kogp@UGniGPxt;Cuf#?MjWoT_=UeXox7E2XCg{ zcnQ?;ENIa2wVYoM;4s}~kNhb{VB^)@x znf27BCfnSNP5~ce%u8=2@1b5r(btON0okVAM1@)gkyPP#{0V~>?DQdJ6*OjMG`bVSDUKbbx_^)D?|@r9?mdnGapB-hu=0jrSr37gI2Ih+b?{_myS;8 zejh||TP|No3Y0}U=8=cLJ^BvK#v_d0zG*qIlZz-Su>##Ij2`*GBoV?H$f z%XUCv2o%tSoV#yYmE%x}nog+kSydHwFTxc1Iw<&;)!-m6hK~yS3`nsv^so#X=aOIQ zfol@J2PVj?^*5?ROt77T-4L1kN!_n61Hgc8nnE9DX&-<#6gz&4rO}qjXiE=)VuYQc zSWZIkXsK_o)St&JwS4^UAMKyNi!{F;OU?#ALMr~OBal_M!zkcx;0%6wS_pbmSOr1w zQjiKIzF^AtLY(&Pfvn0d$7?Vhi7SC`0eQ=!;x-PVRsYyNt*b6$F>t}Dhn?8f+og3qr(2xImA~l zSEys*dCE$c`8zh!fUn%|Z71|^#!zsMr>;uIKyv-!o6cLt#`*9apsXdGJYx`Zs;EQB zcNsN+!9O8G`1dz(={bl9D(qjwJstm1xN6DX)E`33=DyP}B1==JB#=X zI#iHhGPQ-x#f|L{*T}G-5BD5j^CL!C)<;ldB?*{AEl8ydAC0*28V81P(3}`&zM(Arv!LM(Dl-z+`3>KH#OO5JC z!*Ll1^*kD)-Nj2MCTKZQQeNEu@nTfU@AK~q` zA!mMj_-V*lBqv2hPCn}2dMNPZoda#!dcJ2)SFI@U#hf{z8ZMbd$+o%&_?)5)?SB4T z(LQZupy=*3n(gRfer{f&HYYISo_I|g!ylf%Q(F)iaPKKiE9Bi4uC?u0z+YcDSzF34 zFZ9{=-Wyo9=q=55YIb1I{pFfw@an~t+H-tV@i6T#fzslsnwG*3EtzcFT^LAt;2pa* zE%41ltF>OcupfE+S?2*cW&1?)O;t7eFqlCA;uiKG>>;FWZqr1_GLl{8b*nCKX+N=l=D)s;x<=ab_9tSFNH-_zv(m?e4A<0Ki#-3CuA zjnDjgtGwVH0$@2!e%0FU0oaj-NpK_rax3D8QVAy~PK6k>P+?ExkCqO!y&A>;RvPPj z>3y6cu?B8|VY{$P<9)B1E}?ikh(e*sv_A@kvR196gWu>G+=eb}9&Xy_L5STtrNN#L zj_lwz7E1=Z=><3mzKsnN`(sp%a%un|Fj2U)1J(qUp{`|n@fZU-mqVY3j5z) zM#x!}psXaEHtn65XJuK>NkJzZjwqV;3Ldv2PV@3%D|#l6mlpk`LVL2Hnb}yDmELAT zWz*h=`&K0Bt;IrkL(*#Nj6!=dyds{H#k_jObiI;l=zVIUQRsLV1q{u@JgID)FXkoc z)A}xy2V&G4i8CD^5meYu;j#_*4q)2-uVaujoK#h)H0_U2(H3k#hXjY9`;o^MN(}Gi zjzp9(su-!VtmTl3lJ>IoD_!NWY`wS9QOtiTb9T;>F@)WKsba<_^Z4@7w&06=UU@&? zTc|PcjhrqiIQYU5OAd)l`?CZU_KrA)4b%F21&W;@ZLpt!$M&JDw!wZBoSX!v{f^CK zkn5Tcg_ zDFP=n`I2XFXFh!8Y~KrzVRlfGBB=>kiKL^T)+bH8funoh`#TwDlMjTHZj`l(l8*8= znh06*vTD2vu;2+BV{4%Oq|}c5UE6ecrQ`}6k-b}5rc=GS&%@&$i|HOCmF+puqi@k} zykT0B8~KFCGQwv*0=YkH3SawJYIvGe=#N70M3M9g6h+c&lD;A7R!O%>x;>J2e!LRz z3C}$~?ABHUAd)UfdP!0Wsj(4LP(C7QT4&O5gBp0Tiopt|QBU zaj+vb(Ijc3q{n16lMb3Be^gRSeq8dubP7zV@YsACdeS6iY^%a930B#r@QjilQ~0sM z8ih|Nl#PHKh_&pi6t>AHD7_&H`&jt&Zms>WP$iqKaFN0h3ftNk(!*+dtil=3+@NXE zz9&_2i^81>4=6mQ@QlKCaaJ=?3X>JO6`ob0hjzF0ViYDT`}L}vZK01n+}l!|tI(q` zQ(>aQ&I->aTKdNoHY(hvaGkYupN?|t@X$OUul>C&!LkjmQ ztWtQh!dDcwb^MiHs>f6S$qEM&!as8>fuV4c!gVU+*A%{`aF@b*g&P&NC`TWwa+4|_ zRJd2+lWKOZ2i{3hZc&1*3U?~3S9nlilfn}UTNIvC80xe#?W{14=d4cfWvkLCg?S2R zC@fM~tnd+qNc+Phq{nj};!a(8rD|!S@Q!D(s;8BSvAeLbt*R3a2S7 zQh2|@2NjkotW@}{!tE0Ade`v*I>n}QmMlG z70yz)K(&^w%Ka7gRM6E2w7s&D zxwW;ux;lzhgMsduwNelrO8?stcx`RoJ$5=R7xT-l?`lQ7`nL?d$i!-ZZfuVb)?UkJ zwp7N7c>4hR2fDCRoNM*qLL{8s^X{S-WD=+;aghwEPRHsb3_k!IDmu zv_#UUFAjzLql*bQP&=hKs$*kqEKEy*{pE`Z<4Ib8V_(BJhR_jhwK^mD8uBW&d&qL_ z@8wuo-cDs1`&V8il2N)1)9YR@$ zLE)^!z;?y2MUkQPJB5upZxdtT`c>I1jO=#EYGba-S}|Sb<9b$mjcqB;mif@jzbeaS zE*gFaw_jsC0sW3*f|$>Uht8?J|h_`2cHZqz_T8_MDUxyuLoWp&Dd+;nZeis4`chlF9Pl# zk25_|^RY%S2PZHVd4ndg%S1Q_9|fF-XAt;&VB;hN2cB@w6r3L53xIQ{jUy9JEqKDIc#=sUxExP0c>DrMPaM(s Naa-a$;W1jP{@;-hzHI;i delta 10853 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 b*}!e%=FMDW;|Z-f2=yp7Xb From bd559affbcf751d932274da766e8e762c23e1f87 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Wed, 16 Jul 2008 02:17:56 +0000 Subject: [PATCH 1272/2594] 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 d2b2c9a347..3a120dd584 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 25a042d075..87ce9217fd 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 72039a7e6a..76798b9506 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' From cc23416383cd1931421722ea974a8b8fcc273d10 Mon Sep 17 00:00:00 2001 From: Jesse Noller Date: Wed, 16 Jul 2008 13:24:06 +0000 Subject: [PATCH 1273/2594] 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 6144efc070..045368a925 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: From 859dfb6ac16f97bbbd9632c03ac655ad5e3e5d59 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Fri, 18 Jul 2008 02:28:44 +0000 Subject: [PATCH 1274/2594] Bumping to 3.0b2 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 54fc2369d0..48bb00560b 100644 --- a/__init__.py +++ b/__init__.py @@ -20,5 +20,5 @@ # #--start constants-- -__version__ = "3.0b1" +__version__ = "3.0b2" #--end constants-- From 5656e21cbe602ccf223815c23bad6f8411d039d0 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Fri, 18 Jul 2008 03:20:07 +0000 Subject: [PATCH 1275/2594] Bumping to 2.6b2 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index a192357896..06ec0d4bf8 100644 --- a/__init__.py +++ b/__init__.py @@ -22,5 +22,5 @@ # #--start constants-- -__version__ = "2.6b1" +__version__ = "2.6b2" #--end constants-- From e6c78afba556f44c4d87f6e5392a7ffba98a45c9 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Wed, 23 Jul 2008 16:10:53 +0000 Subject: [PATCH 1276/2594] 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 87ce9217fd..d65ab321b6 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: From a52ff5544a4ddb9aa9b5e94543f45cf7ad7f348e Mon Sep 17 00:00:00 2001 From: Amaury Forgeot d'Arc Date: Sat, 26 Jul 2008 20:09:45 +0000 Subject: [PATCH 1277/2594] 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 bd560a661c..c6572caa5f 100644 --- a/cmd.py +++ b/cmd.py @@ -158,7 +158,7 @@ def dump_options(self, header=None, indent=""): 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 c0eef62815..2db53f134d 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -244,7 +244,7 @@ def finalize_options(self): 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 ddde909fbd..8bcb88c31b 100644 --- a/dist.py +++ b/dist.py @@ -864,7 +864,8 @@ def _set_command_options (self, command_obj, option_dict=None): 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 8eab0a95bf..a80c71e8c7 100644 --- a/filelist.py +++ b/filelist.py @@ -85,13 +85,13 @@ def _parse_template_line(self, line): 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 25d48ae866..130cd6147b 100644 --- a/mwerkscompiler.py +++ b/mwerkscompiler.py @@ -104,10 +104,10 @@ def link (self, # 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 db054fd270..266466c1eb 100644 --- a/text_file.py +++ b/text_file.py @@ -292,7 +292,7 @@ def unreadline(self, line): 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 @@ def test_input(count, description, file, expected_result): 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) From 4c8a00dd7b5c30ef7f88deaf6e76be5815cee1fb Mon Sep 17 00:00:00 2001 From: Hirokazu Yamamoto Date: Thu, 14 Aug 2008 05:50:43 +0000 Subject: [PATCH 1278/2594] 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 beb3319469..8cf7888117 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -209,9 +209,12 @@ def finalize_options (self): 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 From dd8e87105079fbf52d866825dc56482a40732b99 Mon Sep 17 00:00:00 2001 From: Hirokazu Yamamoto Date: Thu, 14 Aug 2008 07:35:13 +0000 Subject: [PATCH 1279/2594] 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 2db53f134d..7ef5becc6f 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -205,9 +205,12 @@ def finalize_options(self): 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 From bc2dcae88fe834e1fe0de12f0b01df8763fa7d38 Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Sun, 17 Aug 2008 04:16:04 +0000 Subject: [PATCH 1280/2594] 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 8cf7888117..1461409f60 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -679,7 +679,7 @@ def get_ext_filename (self, ext_name): 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 be6d2c5b4d..3bf1267328 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -169,7 +169,7 @@ def get_package_dir (self, package): 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 @@ def get_source_files (self): 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 de9ce7d7ff..a0e44ea631 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 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: [PATCH 1281/2594] 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 a0e44ea631..62a3389b53 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 From 07f55be128098cc6915ebd7671a1a13078291e9e Mon Sep 17 00:00:00 2001 From: Amaury Forgeot d'Arc Date: Mon, 18 Aug 2008 19:23:47 +0000 Subject: [PATCH 1282/2594] #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 4ac11eb4b1..94a7bd96ee 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: From 478e78efb66d39ece39152d4d41f3842f147a48b Mon Sep 17 00:00:00 2001 From: Amaury Forgeot d'Arc Date: Mon, 18 Aug 2008 19:33:42 +0000 Subject: [PATCH 1283/2594] 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 4fd23e6dc6..6d1ba16be6 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: From 9b46d3fc54dc19f29a4d7503e8c06dcfbebd7b5a Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Tue, 19 Aug 2008 17:56:33 +0000 Subject: [PATCH 1284/2594] #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 488752300b..da2c74a2b1 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 d9ee82d58a..62a4c5b4e8 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 3a120dd584..b17743a865 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 76798b9506..b87dfbe065 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 f71b2f6ce3..907f71c313 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 434b34f184..b0dd9f45bf 100644 --- a/versionpredicate.py +++ b/versionpredicate.py @@ -5,7 +5,8 @@ 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: From 677cbea5d0294da0039c6645d0c5a8abce8efcd0 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Tue, 19 Aug 2008 18:57:56 +0000 Subject: [PATCH 1285/2594] 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 da2c74a2b1..ea4c7971fd 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: From ce2fde27c45c1041550de710a9dce751742ed47d Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Thu, 21 Aug 2008 01:15:08 +0000 Subject: [PATCH 1286/2594] Bump to 2.6b3. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 06ec0d4bf8..74d0a47d47 100644 --- a/__init__.py +++ b/__init__.py @@ -22,5 +22,5 @@ # #--start constants-- -__version__ = "2.6b2" +__version__ = "2.6b3" #--end constants-- From 82e68f4b495194b137f661c4ace674a857ab9a24 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Thu, 21 Aug 2008 02:39:51 +0000 Subject: [PATCH 1287/2594] Bumping to 3.0b3 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 48bb00560b..9af2d9208e 100644 --- a/__init__.py +++ b/__init__.py @@ -20,5 +20,5 @@ # #--start constants-- -__version__ = "3.0b2" +__version__ = "3.0b3" #--end constants-- From 031df9f3aba5b6fac4773f05107ad5e7c6d830c4 Mon Sep 17 00:00:00 2001 From: Amaury Forgeot d'Arc Date: Tue, 2 Sep 2008 23:19:56 +0000 Subject: [PATCH 1288/2594] 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 c8d52c4237..0b27428d05 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()))) From acd8349dbdd9b886e8e78d43238c32f7858c0c20 Mon Sep 17 00:00:00 2001 From: Amaury Forgeot d'Arc Date: Tue, 2 Sep 2008 23:22:56 +0000 Subject: [PATCH 1289/2594] 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 fdb74aeabf..465013dc33 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()))) 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: [PATCH 1290/2594] 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 0a21380973..9ad94fbeb8 100644 --- a/dist.py +++ b/dist.py @@ -23,6 +23,9 @@ 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 @@ def write_pkg_file (self, file): 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 @@ def write_pkg_file (self, file): 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 8f1288e9f6..6f5fe9c757 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 @@ def test_command_packages_configfile(self): 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): From a69032e129711e6b10e786a753ba6bde427272fc Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Thu, 4 Sep 2008 21:32:09 +0000 Subject: [PATCH 1291/2594] 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 ae7d4fde2e..e997e8f094 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -260,13 +260,18 @@ def create_exe(self, arcname, fullname, bitmap=None): 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 From a97c6df7180f7bd58a5fe5415023ed396a1e7d58 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Fri, 12 Sep 2008 23:25:57 +0000 Subject: [PATCH 1292/2594] Bumping to 2.6rc1 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 74d0a47d47..b622f7d217 100644 --- a/__init__.py +++ b/__init__.py @@ -22,5 +22,5 @@ # #--start constants-- -__version__ = "2.6b3" +__version__ = "2.6rc1" #--end constants-- From d85f8e8c6760feba91c06d78299f7f3ccd06cf63 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Thu, 18 Sep 2008 03:00:28 +0000 Subject: [PATCH 1293/2594] bumping to 3.0rc1 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 9af2d9208e..2bc0ab040f 100644 --- a/__init__.py +++ b/__init__.py @@ -20,5 +20,5 @@ # #--start constants-- -__version__ = "3.0b3" +__version__ = "3.0rc1" #--end constants-- From 3c15d77130e62f3ce3799cd5e27aa1efea293169 Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Thu, 18 Sep 2008 03:51:46 +0000 Subject: [PATCH 1294/2594] 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 0b27428d05..804f1751ce 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -357,9 +357,10 @@ def initialize(self, plat_name=None): 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, " From b4b479dd9b72baf9837af27ee2d31adf0dda6f7e Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Thu, 18 Sep 2008 04:33:43 +0000 Subject: [PATCH 1295/2594] Bumping to 2.6rc2 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index b622f7d217..fb8043c370 100644 --- a/__init__.py +++ b/__init__.py @@ -22,5 +22,5 @@ # #--start constants-- -__version__ = "2.6rc1" +__version__ = "2.6rc2" #--end constants-- From 2bf49fbe89baac64f6d5487e888385e9797e6164 Mon Sep 17 00:00:00 2001 From: Hirokazu Yamamoto Date: Sun, 21 Sep 2008 20:48:41 +0000 Subject: [PATCH 1296/2594] 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 a658f1aa1c..31b8b48185 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -62,8 +62,8 @@ 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) + # 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: From 2e2fdd07fefbe699f2978e48c0ea70e51a85eb30 Mon Sep 17 00:00:00 2001 From: Hirokazu Yamamoto Date: Sun, 21 Sep 2008 20:52:42 +0000 Subject: [PATCH 1297/2594] 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 20c10745cb..552a3d1440 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -62,8 +62,8 @@ 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) + # 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: From 2ffea9b40a52bc8b1f8ee1595969d86d9d87ced7 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Wed, 1 Oct 2008 21:46:40 +0000 Subject: [PATCH 1298/2594] Bumping to 2.6 final. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index fb8043c370..7315a37271 100644 --- a/__init__.py +++ b/__init__.py @@ -22,5 +22,5 @@ # #--start constants-- -__version__ = "2.6rc2" +__version__ = "2.6" #--end constants-- From 9dda0b585406f106ef19ec45b300f89677d0197e Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Sun, 5 Oct 2008 09:00:28 +0000 Subject: [PATCH 1299/2594] 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 69190601fb..b46b0da678 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() 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: [PATCH 1300/2594] 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 09f6d2331e..77284894ec 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -384,6 +384,18 @@ def byte_compile(self, files): 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 @@ def run(self): 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)) From f4beb1d3745aee6654e115b5da14424ff4933a9c Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Thu, 6 Nov 2008 03:29:32 +0000 Subject: [PATCH 1301/2594] Bumping to 3.0rc2. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 2bc0ab040f..5e4972fd2b 100644 --- a/__init__.py +++ b/__init__.py @@ -20,5 +20,5 @@ # #--start constants-- -__version__ = "3.0rc1" +__version__ = "3.0rc2" #--end constants-- From f5f5810be1fccf5d38fe34126b0422079ffd3934 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 8 Nov 2008 15:15:57 +0000 Subject: [PATCH 1302/2594] #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 0a902cee89..b5c9554d61 100644 --- a/command/install.py +++ b/command/install.py @@ -546,7 +546,7 @@ def create_home_path(self): 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) 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: [PATCH 1303/2594] 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 6d5c459924..045cbb6381 100644 --- a/command/register.py +++ b/command/register.py @@ -15,11 +15,6 @@ 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 @@ def send_metadata(self): 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 @@ def send_metadata(self): 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 @@ def send_metadata(self): 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 @@ def send_metadata(self): 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 @@ def send_metadata(self): 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 @@ def send_metadata(self): 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 @@ def post_to_server(self, data, auth=None): 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 @@ def post_to_server(self, data, auth=None): 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 = { From 2dd637745910d84b0c7342622a823ed0c3a2cd62 Mon Sep 17 00:00:00 2001 From: Amaury Forgeot d'Arc Date: Thu, 20 Nov 2008 23:53:46 +0000 Subject: [PATCH 1304/2594] #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 5049f03e93..7ba7f5888a 100644 --- a/command/upload.py +++ b/command/upload.py @@ -7,11 +7,11 @@ 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 @@ def upload_file(self, command, pyversion, filename): 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 @@ def upload_file(self, command, pyversion, filename): 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) From a122c2880d7b97fac4345692b4d9fedd58bec7b5 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Fri, 21 Nov 2008 01:18:21 +0000 Subject: [PATCH 1305/2594] Bump to 3.0rc3 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 5e4972fd2b..2cf9d4d304 100644 --- a/__init__.py +++ b/__init__.py @@ -20,5 +20,5 @@ # #--start constants-- -__version__ = "3.0rc2" +__version__ = "3.0rc3" #--end constants-- From c6338dae14e511f967554aaceb5ade0c698327a3 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Tue, 25 Nov 2008 21:21:32 +0000 Subject: [PATCH 1306/2594] 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 552a3d1440..527529777d 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -11,6 +11,10 @@ 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 @@ def setUp(self): 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 @@ def test_build_ext(self): finally: sys.stdout = old_stdout + if ALREADY_TESTED: + return + else: + ALREADY_TESTED = True + import xx for attr in ('error', 'foo', 'new', 'roj'): From 3b615290cad279b0375edf204b8c673c0353f27a Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Fri, 28 Nov 2008 11:02:32 +0000 Subject: [PATCH 1307/2594] 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 804f1751ce..4f72e78096 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -316,7 +316,7 @@ def __init__(self, verbose=0, dry_run=0, force=0): 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 From 6850072391c86bfcfd01c6b0d65762f9d9c45d40 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Fri, 28 Nov 2008 11:03:48 +0000 Subject: [PATCH 1308/2594] 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 804f1751ce..4f72e78096 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -316,7 +316,7 @@ def __init__(self, verbose=0, dry_run=0, force=0): 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 From e32c222776d9dff73e9ba246c7cb654764157bb9 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Fri, 28 Nov 2008 11:05:17 +0000 Subject: [PATCH 1309/2594] 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 465013dc33..eced0524b8 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -316,7 +316,7 @@ def __init__(self, verbose=0, dry_run=0, force=0): 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 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: [PATCH 1310/2594] 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 77284894ec..99d85be96c 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -9,7 +9,7 @@ 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 @@ def byte_compile(self, files): 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 @@ def run(self): 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 3ac5b0c0be..dc04a9fcdb 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -9,7 +9,7 @@ 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 @@ def copy_scripts(self): """ self.mkpath(self.build_dir) outfiles = [] + updated_files = [] for script in self.scripts: adjust = False script = convert_path(script) @@ -92,6 +93,7 @@ def copy_scripts(self): 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 @@ def copy_scripts(self): else: if f: f.close() + updated_files.append(outfile) self.copy_file(script, outfile) if os.name == 'posix': @@ -125,3 +128,13 @@ def copy_scripts(self): 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 b87dfbe065..28337fa9a0 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) From e55ca00608e5c34e302a4ad0fc367fa55fd5cadb Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Wed, 3 Dec 2008 16:46:14 +0000 Subject: [PATCH 1311/2594] Prep for Python 3.1! --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 2cf9d4d304..34fc008a58 100644 --- a/__init__.py +++ b/__init__.py @@ -20,5 +20,5 @@ # #--start constants-- -__version__ = "3.0rc3" +__version__ = "3.1a0" #--end constants-- From 78ad69e50bbf4f0948621c9805a65936b5bcee2e Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Thu, 4 Dec 2008 02:59:51 +0000 Subject: [PATCH 1312/2594] Prep for 2.6.1 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 7315a37271..8f7eb8427d 100644 --- a/__init__.py +++ b/__init__.py @@ -22,5 +22,5 @@ # #--start constants-- -__version__ = "2.6" +__version__ = "2.6.1" #--end constants-- From 30a3850d75b9050b79a65a4da14c58401e495443 Mon Sep 17 00:00:00 2001 From: Amaury Forgeot d'Arc Date: Thu, 11 Dec 2008 00:03:42 +0000 Subject: [PATCH 1313/2594] #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 0ed9a40a35..87d6e27396 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -1041,7 +1041,7 @@ 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, self.dry_run) + mkpath (name, mode, dry_run=self.dry_run) # class CCompiler From 73b7309d54071194b72b68c783eded336f96d9fb Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sun, 21 Dec 2008 00:06:59 +0000 Subject: [PATCH 1314/2594] 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 1e09123afc..f5d1587d72 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -984,8 +984,8 @@ def spawn(self, cmd): 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 From 058256f5d77a12162cd5ffd6d5249a53b7f44add Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sun, 21 Dec 2008 17:01:26 +0000 Subject: [PATCH 1315/2594] 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 0ed9a40a35..87d6e27396 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -1041,7 +1041,7 @@ 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, self.dry_run) + mkpath (name, mode, dry_run=self.dry_run) # class CCompiler 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: [PATCH 1316/2594] 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 a9ab361a17..bf7be96096 100644 --- a/command/register.py +++ b/command/register.py @@ -141,12 +141,14 @@ def send_metadata(self): # 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 @@ def send_metadata(self): # 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 e3a4c57e33..4186c9b1d8 100644 --- a/config.py +++ b/config.py @@ -10,8 +10,8 @@ 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 bfce3e3e72..cae7689884 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -5,6 +5,8 @@ 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 @@ 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 @@ def initialize_options(self): finalize_options = initialize_options self._cmd = command + self.old_threshold = set_threshold(WARN) def tearDown(self): """Removes the patch.""" @@ -62,6 +76,7 @@ def tearDown(self): 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 @@ def test_server_registration(self): ('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 0000000000..3a3a3b739b --- /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") 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: [PATCH 1317/2594] 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 a9ab361a17..bf7be96096 100644 --- a/command/register.py +++ b/command/register.py @@ -141,12 +141,14 @@ def send_metadata(self): # 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 @@ def send_metadata(self): # 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 e3a4c57e33..4186c9b1d8 100644 --- a/config.py +++ b/config.py @@ -10,8 +10,8 @@ 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 bfce3e3e72..cae7689884 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -5,6 +5,8 @@ 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 @@ 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 @@ def initialize_options(self): finalize_options = initialize_options self._cmd = command + self.old_threshold = set_threshold(WARN) def tearDown(self): """Removes the patch.""" @@ -62,6 +76,7 @@ def tearDown(self): 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 @@ def test_server_registration(self): ('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 0000000000..3a3a3b739b --- /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") From a89c58deedd83e10acf51ce48c0a8ee9b753cc98 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 27 Dec 2008 16:00:54 +0000 Subject: [PATCH 1318/2594] 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 045cbb6381..bd9b8c0143 100644 --- a/command/register.py +++ b/command/register.py @@ -143,13 +143,14 @@ def send_metadata(self): # 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 @@ def send_metadata(self): # 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 0ecfe0cc34..73f326047a 100644 --- a/config.py +++ b/config.py @@ -10,8 +10,8 @@ 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 016ba4cc70..bdc9b2b539 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -5,6 +5,8 @@ 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 @@ 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 @@ def initialize_options(self): finalize_options = initialize_options self._cmd = command + self.old_threshold = set_threshold(WARN) def tearDown(self): """Removes the patch.""" @@ -62,6 +76,7 @@ def tearDown(self): 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 @@ def test_server_registration(self): ('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 0000000000..3a3a3b739b --- /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") From 326cd6e433ffd81a314773b3b24db40a0a1cd09e Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 27 Dec 2008 17:00:44 +0000 Subject: [PATCH 1319/2594] 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 bd9b8c0143..30e9a37e6a 100644 --- a/command/register.py +++ b/command/register.py @@ -150,7 +150,7 @@ def send_metadata(self): 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 3a3a3b739b..021b3ea884 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -60,7 +60,7 @@ def test_create_pypirc(self): # 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 @@ def __init__(self): 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' From d2c6eebccda5f6a0d0b252729142548561441e0f Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Sun, 28 Dec 2008 19:40:56 +0000 Subject: [PATCH 1320/2594] 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 262a9b8b04..48cc17f624 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 From be477751d0c5f8a5fb565a0e871a2ad8fb915d45 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Sun, 28 Dec 2008 19:42:55 +0000 Subject: [PATCH 1321/2594] Backport of r67988 --- util.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/util.py b/util.py index 262a9b8b04..48cc17f624 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 From 99059691e42d8d443fbb3746eb7b6695226dc166 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Sun, 28 Dec 2008 19:50:40 +0000 Subject: [PATCH 1322/2594] 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 48cc17f624..e9d29ff49c 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. 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: [PATCH 1323/2594] 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 9ad94fbeb8..c15ca9770d 100644 --- a/dist.py +++ b/dist.py @@ -235,7 +235,7 @@ def __init__ (self, attrs=None): # 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 6f5fe9c757..bf59c41844 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -8,6 +8,7 @@ import StringIO import sys import unittest +import warnings from test.test_support import TESTFN @@ -131,6 +132,29 @@ def test_write_pkg_file(self): 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): 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: [PATCH 1324/2594] 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 9ad94fbeb8..c15ca9770d 100644 --- a/dist.py +++ b/dist.py @@ -235,7 +235,7 @@ def __init__ (self, attrs=None): # 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 6f5fe9c757..bf59c41844 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -8,6 +8,7 @@ import StringIO import sys import unittest +import warnings from test.test_support import TESTFN @@ -131,6 +132,29 @@ def test_write_pkg_file(self): 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): 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: [PATCH 1325/2594] 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 8bcb88c31b..7903c2a783 100644 --- a/dist.py +++ b/dist.py @@ -228,7 +228,7 @@ def __init__ (self, attrs=None): # 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 f1b11c1745..8e7ef5d33c 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -6,6 +6,7 @@ import io import sys import unittest +import warnings from test.support import TESTFN @@ -96,6 +97,29 @@ def test_command_packages_configfile(self): 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): From de38e65a4ea05f1b0924676bc9fb2ecccd182f40 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Tue, 30 Dec 2008 17:56:45 +0000 Subject: [PATCH 1326/2594] 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 28337fa9a0..042306e913 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. 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: [PATCH 1327/2594] 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 4f72e78096..68b7775830 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 0000000000..1659cea510 --- /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") 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: [PATCH 1328/2594] 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 4f72e78096..68b7775830 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 0000000000..1659cea510 --- /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") 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: [PATCH 1329/2594] 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 eced0524b8..aae0637394 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 0000000000..1659cea510 --- /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") From b7024702a738bd1e31391b391cc073b97d94c008 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Thu, 1 Jan 2009 15:46:10 +0000 Subject: [PATCH 1330/2594] 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 48cc17f624..e9d29ff49c 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. 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: [PATCH 1331/2594] 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 961256c3ac..e10e25b37d 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -7,6 +7,7 @@ __revision__ = "$Id$" import os, string +import sys from types import * from glob import glob from distutils.core import Command @@ -354,8 +355,13 @@ def prune_file_list (self): 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 0000000000..7cc04cfc0c --- /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") 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: [PATCH 1332/2594] 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 961256c3ac..e10e25b37d 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -7,6 +7,7 @@ __revision__ = "$Id$" import os, string +import sys from types import * from glob import glob from distutils.core import Command @@ -354,8 +355,13 @@ def prune_file_list (self): 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 0000000000..7cc04cfc0c --- /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") 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: [PATCH 1333/2594] 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 e10e25b37d..27883fd68b 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -358,8 +358,13 @@ def prune_file_list (self): # 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) 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: [PATCH 1334/2594] 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 e10e25b37d..27883fd68b 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -358,8 +358,13 @@ def prune_file_list (self): # 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) From ecf83b1f901da44a7408bda6aba7d408b4f8155a Mon Sep 17 00:00:00 2001 From: Hirokazu Yamamoto Date: Wed, 7 Jan 2009 09:42:28 +0000 Subject: [PATCH 1335/2594] 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 1659cea510..0c8bd6e042 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -13,6 +13,10 @@ def test_no_compiler(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 + return from distutils.msvc9compiler import query_vcvarsall def _find_vcvarsall(version): return None From 18a095cf8cedcd8e615a6dd6d71634b2fe35bb70 Mon Sep 17 00:00:00 2001 From: Hirokazu Yamamoto Date: Wed, 7 Jan 2009 09:53:35 +0000 Subject: [PATCH 1336/2594] 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 1659cea510..0c8bd6e042 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -13,6 +13,10 @@ def test_no_compiler(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 + return from distutils.msvc9compiler import query_vcvarsall def _find_vcvarsall(version): return None From 7da96ed6b364ce84ab7b0f2c563fda2779f641ec Mon Sep 17 00:00:00 2001 From: Hirokazu Yamamoto Date: Wed, 7 Jan 2009 10:11:17 +0000 Subject: [PATCH 1337/2594] 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 1659cea510..0c8bd6e042 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -13,6 +13,10 @@ def test_no_compiler(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 + return from distutils.msvc9compiler import query_vcvarsall def _find_vcvarsall(version): return None 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: [PATCH 1338/2594] 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 bf7be96096..40661d8776 100644 --- a/command/register.py +++ b/command/register.py @@ -173,19 +173,23 @@ def send_metadata(self): 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 8805d41da0..e30347e507 100644 --- a/command/upload.py +++ b/command/upload.py @@ -50,6 +50,11 @@ def finalize_options(self): 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 4186c9b1d8..9166199ea9 100644 --- a/config.py +++ b/config.py @@ -82,12 +82,12 @@ def _read_pypirc(self): 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 c15ca9770d..18cc910900 100644 --- a/dist.py +++ b/dist.py @@ -206,6 +206,7 @@ def __init__ (self, attrs=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 diff --git a/tests/test_register.py b/tests/test_register.py index 3a3a3b739b..b3543f50f8 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.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 @@ def __call__(self, prompt=''): 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 @@ def test_create_pypirc(self): # 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 @@ def _no_way(prompt=''): 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 b05ab1f78b..3f8ca6d6ad 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -9,6 +9,17 @@ 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 @@ def test_finalize_options(self): ('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) 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: [PATCH 1339/2594] 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 30e9a37e6a..c271b18c4c 100644 --- a/command/register.py +++ b/command/register.py @@ -174,19 +174,23 @@ def send_metadata(self): 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 7ba7f5888a..020e860a6d 100644 --- a/command/upload.py +++ b/command/upload.py @@ -48,6 +48,11 @@ def finalize_options(self): 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 73f326047a..5b625f3f7d 100644 --- a/config.py +++ b/config.py @@ -82,12 +82,12 @@ def _read_pypirc(self): 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 7903c2a783..6c4b4afbd1 100644 --- a/dist.py +++ b/dist.py @@ -199,6 +199,7 @@ def __init__ (self, attrs=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 diff --git a/tests/test_register.py b/tests/test_register.py index 021b3ea884..8826e90fe0 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.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 @@ def __call__(self, prompt=''): 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 @@ def test_create_pypirc(self): # 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 @@ def _no_way(prompt=''): 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 b05ab1f78b..3f8ca6d6ad 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -9,6 +9,17 @@ 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 @@ def test_finalize_options(self): ('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) From d5b7603836bc49a68327aaa343590cb4e051cdf4 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Fri, 9 Jan 2009 04:11:44 +0000 Subject: [PATCH 1340/2594] 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 b29fc64eca..96fb7fa1a7 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -4,7 +4,10 @@ __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 @@ def prune_file_list(self): 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 0000000000..7cc04cfc0c --- /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") From 5a672e9f309ce3c1bf808e839c00369b7e75a25d Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Tue, 20 Jan 2009 07:24:44 +0000 Subject: [PATCH 1341/2594] 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 7403bfabf5cc10c13ef2b6a2ea276b4f6d26ff37..7407032d4b12d53cb4dd8f63b8f600e95eb8f1a8 100644 GIT binary patch delta 26694 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 ?54Rd_BV2_Q-ivUr!R>_m0ImV< zINUk7AK-q0y9##$uFp5r}+^Whf5<-!%ht%KVL_cYuVxN5k!;P$|M z3U?OHD#E)2_dA>#ln#L#1s4Vv3HJcpLb!amb#Ucy^t*(yre7k4D;jPf#(gK;D{y6S z>)`U?7Q@Yf)5A@G3xc}~_&4C%zv32f-yrfS+(Ec^;a-H>2=^phA>6}oMmYLKf&&BL zg5kP<IGWONq=RIQ=PdyN?%M^>H0py zYkX|hs+EsqB^9n(k+v!?+h{K2cHg=k)* zX`7N`wiF;tT9%ielO1ec6SF|$_T-%*i@{@XZrE|Q3VLbB8B!$cUw9RU)({qV% zbh#XjFbpmTP7T-T)^jazloz4AZ2R`Zle%rO+|s{(yXf-^SEio7bh1geWeDe<`{l60 z$`?%;Qx(lRXCzmBRCDJrZSFq(?R%SbyZ3N!aV!7$QMlh z_ha(}Q+pYi z5@-EtJ+6BhD(S~3h9>L7zAK)R@!Kig z(=n)zzwe8;cee#;*S$Dn`_N}U9n$a*`<;|;?S*iMSAkG3IK&I)w&i^({Ro0%WtQKO z+?evh+Yz&OZl5&tw~_aiy{5T%BQvVc99Qv6A!n`~D_i1vv24^|2hH5)7xL}V^ZydL zz2|3G?@cEAa4dG{RM$H`h zlB7SWd+kZ&*Q@_>Vc5=h-*u=B`#$`5eFQz@xtAa6tbMm+~T{EAXRS`b<<+NQ9Uin)`*7!#$Ce42S%lz7-j|_id z;>FMpf1N&D`}#%Y((+f@cD`X+9v1)HN!{MlG6%}9UUH2Xc}ll!AmGQ|Zk}&#O)nU6 zpjqb_hJ-^$?yYV-c4EV8&AN&WYzQ`--FspDGq0W0ZTtlJPyh5nP2@8}H}rySd5?|y zq^eoB<9_5HeRUa-oj6dwpWsXI6xLbfA*>mQK?)of0>Wm$3AZxw)WO_u)e{YqD>Ax8Tmdn+=$jWf+2Qc3Q8uOfb&3VPSB* zO&s{^-CSG675$(dgzMZ>D$D3!vCzl`uIvA`dDr*$6S}_*=7Qfl^Vf&<2cMs{c=HL} z3#TBY4~qAx*6nZ0oODw6!9DUqH9tDK^Hf)}?u~si?D{$?PaJb>*1>(Re&6`Gb=>1Q zcYe&R!9wzS6bLxe^l-u(X^;Hn8_fF=^1Q!zuiIzi)+XJC2W2?h3xBy6JZ8mnn^!;E ztb2C2++cNrV*18;Cv_ix0QiZdS-U1~UGqmJ+Dk-xRy4TGtAeFoF!yfli8qEe>0bB; z3Y<>-tNy|J;x2r2LRY&U1mz|DQa#|~Pt(5w2h1`@Y_XUA-SPbhZAlsOr>1r4z2NtI zEp%2|>DGNyKV#gfCf(abXrO+oE#!?~pKfhllvw$p`QOJUY#m_Y2&}=);u}5DC&gn-8^|EZFzn4z}~Q%t5ElbTbj~24{T*i zZ}o=*|MIh;N%!o7=t1Mo*+=|D>-0lSx_vPa8lOSu?|66iqP{hdfdCMI%|>hOcyJOn zpM4L=`L-K_R_Go(p?fY8=bAd_Tu0sDPGAR8X*qUqART;Fo0h@tl*>?|cHg9c?~$ z*NTVkiM;EMUhuQ_uP;t2LjDVK{#$dy_~c%@<9ip2e$0ucOgE*S80Q_E{cSu)_rjEcJ!xO>C4+?J%?@Y3;4x%?Tqyuzyk zoEL1xiphR%IY>$!5M3DV+%~OraQMp0{*mk(7s~3+|6Cy>cEewI!H2zIw-;P@D{M{r zrsA>6uwy56)z>&%R_QbEs~cZ_e)VD;BsP))u`B&V%spRkyr)_BG;E`D_icIcxEK7H z7ktbM{)ZR*=`FBz%ZtgkQj|2^3#M;P=;vlWmvo05_Q2s@u(;2!?fJ*B_TzDHr&^sc z!>jznn4+MdyN>JL`4?osnz$zZF0Ts4dBLGxaD*3(2m0*imV=~8a@a%QL@)T+*((M{ zKXO9%JcQE9@dNfGc~N+~SAobQ!<>(mK?qwh1cwfP{_fTO``7k@AOHOF#Oc`>qBt6& z=oJ;c8ld{C7oK@@XcD&PF7z0%w&T+mG80bV-v)Emx46A|ztJaC^m8+xOVWG6^lyIX z=Vm^a6z>IBH>kCPSD(7%zcFhnM_hhX~Lo_=oTb4dfcVR`-IlIC?Mco0IpV51jI&kE@0WdpcvTOroz!x z;V%|^$G6&*`TlU_vyZtRe}A}j+!J~(9!~uHX5lwrU=6czT;LZs(}G~#TZpu-(>K#< zU?z;)z;Rc4mDR)K7zsRgue1wfhD6$xUS*ZA4n}U^?&Nqh*8NLF;to`JwpYUkVeDP%$gN3c|vx6zH7-x3D!*CB6Hz#zL4YY^@Y88c>(OrF%&DxG22X69_l zto&8Wm%&!PBIn*njyvO%<%wDd$Gj>ZlTVdfm;*C&xbKQ(`M}21^~oxLxqIbmZl5A6 z7nX7Jy^(nlleu&M8sID0y7g!sP|#BwSkL6mpZ4 zS$Rb(9)38V7{#4a$+*6#bNPzw{FR&*`786xKwZJfA4&e7{L5*99eh`a2Q zWwopXIgfHLpc*g^h$0h z^?voLM{}~)EHf9a!p+Te#sRuL$t}pjrO7zu3?d#a?>>HDh$8;)kRZr@-f|4%b{5aZ zMb8cvV~Jb>YLF#xnoky!)p4MWWb+HxnA&f5@2bll@T^;v5r|t5x(V^~g!oQ~!CfVK zt^-blqx|k4;eU=a!l&>*;=2;+Vy#0^Y(KC_|JsRgIKnD~qrzDuIIiPDr*c#;#vTIJme}cyMuEhVMxMFgq?ur z_JXSc|GYcC?wn?UPQj_6Kwj{awT)14)ry;N@JI!x0zKXNA)^)CL%m?z7zIcFs<%78 i@^%HcVE2mVR$brwCf_&VzI!LT7JcvB{nPhD{Qei&3FGzv delta 17844 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 Ty(jg%1=9?n*VSo;gt-3&JwIim From 135be1455bb46863549fadf72cb99b0c30c9bece Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Tue, 20 Jan 2009 21:25:32 +0000 Subject: [PATCH 1342/2594] 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 7407032d4b12d53cb4dd8f63b8f600e95eb8f1a8..7403bfabf5cc10c13ef2b6a2ea276b4f6d26ff37 100644 GIT binary patch delta 17844 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 Ty(jg%1=9?n*VSo;gt-3&JwIim delta 26694 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 ?54Rd_BV2_Q-ivUr!R>_m0ImV< zINUk7AK-q0y9##$uFp5r}+^Whf5<-!%ht%KVL_cYuVxN5k!;P$|M z3U?OHD#E)2_dA>#ln#L#1s4Vv3HJcpLb!amb#Ucy^t*(yre7k4D;jPf#(gK;D{y6S z>)`U?7Q@Yf)5A@G3xc}~_&4C%zv32f-yrfS+(Ec^;a-H>2=^phA>6}oMmYLKf&&BL zg5kP<IGWONq=RIQ=PdyN?%M^>H0py zYkX|hs+EsqB^9n(k+v!?+h{K2cHg=k)* zX`7N`wiF;tT9%ielO1ec6SF|$_T-%*i@{@XZrE|Q3VLbB8B!$cUw9RU)({qV% zbh#XjFbpmTP7T-T)^jazloz4AZ2R`Zle%rO+|s{(yXf-^SEio7bh1geWeDe<`{l60 z$`?%;Qx(lRXCzmBRCDJrZSFq(?R%SbyZ3N!aV!7$QMlh z_ha(}Q+pYi z5@-EtJ+6BhD(S~3h9>L7zAK)R@!Kig z(=n)zzwe8;cee#;*S$Dn`_N}U9n$a*`<;|;?S*iMSAkG3IK&I)w&i^({Ro0%WtQKO z+?evh+Yz&OZl5&tw~_aiy{5T%BQvVc99Qv6A!n`~D_i1vv24^|2hH5)7xL}V^ZydL zz2|3G?@cEAa4dG{RM$H`h zlB7SWd+kZ&*Q@_>Vc5=h-*u=B`#$`5eFQz@xtAa6tbMm+~T{EAXRS`b<<+NQ9Uin)`*7!#$Ce42S%lz7-j|_id z;>FMpf1N&D`}#%Y((+f@cD`X+9v1)HN!{MlG6%}9UUH2Xc}ll!AmGQ|Zk}&#O)nU6 zpjqb_hJ-^$?yYV-c4EV8&AN&WYzQ`--FspDGq0W0ZTtlJPyh5nP2@8}H}rySd5?|y zq^eoB<9_5HeRUa-oj6dwpWsXI6xLbfA*>mQK?)of0>Wm$3AZxw)WO_u)e{YqD>Ax8Tmdn+=$jWf+2Qc3Q8uOfb&3VPSB* zO&s{^-CSG675$(dgzMZ>D$D3!vCzl`uIvA`dDr*$6S}_*=7Qfl^Vf&<2cMs{c=HL} z3#TBY4~qAx*6nZ0oODw6!9DUqH9tDK^Hf)}?u~si?D{$?PaJb>*1>(Re&6`Gb=>1Q zcYe&R!9wzS6bLxe^l-u(X^;Hn8_fF=^1Q!zuiIzi)+XJC2W2?h3xBy6JZ8mnn^!;E ztb2C2++cNrV*18;Cv_ix0QiZdS-U1~UGqmJ+Dk-xRy4TGtAeFoF!yfli8qEe>0bB; z3Y<>-tNy|J;x2r2LRY&U1mz|DQa#|~Pt(5w2h1`@Y_XUA-SPbhZAlsOr>1r4z2NtI zEp%2|>DGNyKV#gfCf(abXrO+oE#!?~pKfhllvw$p`QOJUY#m_Y2&}=);u}5DC&gn-8^|EZFzn4z}~Q%t5ElbTbj~24{T*i zZ}o=*|MIh;N%!o7=t1Mo*+=|D>-0lSx_vPa8lOSu?|66iqP{hdfdCMI%|>hOcyJOn zpM4L=`L-K_R_Go(p?fY8=bAd_Tu0sDPGAR8X*qUqART;Fo0h@tl*>?|cHg9c?~$ z*NTVkiM;EMUhuQ_uP;t2LjDVK{#$dy_~c%@<9ip2e$0ucOgE*S80Q_E{cSu)_rjEcJ!xO>C4+?J%?@Y3;4x%?Tqyuzyk zoEL1xiphR%IY>$!5M3DV+%~OraQMp0{*mk(7s~3+|6Cy>cEewI!H2zIw-;P@D{M{r zrsA>6uwy56)z>&%R_QbEs~cZ_e)VD;BsP))u`B&V%spRkyr)_BG;E`D_icIcxEK7H z7ktbM{)ZR*=`FBz%ZtgkQj|2^3#M;P=;vlWmvo05_Q2s@u(;2!?fJ*B_TzDHr&^sc z!>jznn4+MdyN>JL`4?osnz$zZF0Ts4dBLGxaD*3(2m0*imV=~8a@a%QL@)T+*((M{ zKXO9%JcQE9@dNfGc~N+~SAobQ!<>(mK?qwh1cwfP{_fTO``7k@AOHOF#Oc`>qBt6& z=oJ;c8ld{C7oK@@XcD&PF7z0%w&T+mG80bV-v)Emx46A|ztJaC^m8+xOVWG6^lyIX z=Vm^a6z>IBH>kCPSD(7%zcFhnM_hhX~Lo_=oTb4dfcVR`-IlIC?Mco0IpV51jI&kE@0WdpcvTOroz!x z;V%|^$G6&*`TlU_vyZtRe}A}j+!J~(9!~uHX5lwrU=6czT;LZs(}G~#TZpu-(>K#< zU?z;)z;Rc4mDR)K7zsRgue1wfhD6$xUS*ZA4n}U^?&Nqh*8NLF;to`JwpYUkVeDP%$gN3c|vx6zH7-x3D!*CB6Hz#zL4YY^@Y88c>(OrF%&DxG22X69_l zto&8Wm%&!PBIn*njyvO%<%wDd$Gj>ZlTVdfm;*C&xbKQ(`M}21^~oxLxqIbmZl5A6 z7nX7Jy^(nlleu&M8sID0y7g!sP|#BwSkL6mpZ4 zS$Rb(9)38V7{#4a$+*6#bNPzw{FR&*`786xKwZJfA4&e7{L5*99eh`a2Q zWwopXIgfHLpc*g^h$0h z^?voLM{}~)EHf9a!p+Te#sRuL$t}pjrO7zu3?d#a?>>HDh$8;)kRZr@-f|4%b{5aZ zMb8cvV~Jb>YLF#xnoky!)p4MWWb+HxnA&f5@2bll@T^;v5r|t5x(V^~g!oQ~!CfVK zt^-blqx|k4;eU=a!l&>*;=2;+Vy#0^Y(KC_|JsRgIKnD~qrzDuIIiPDr*c#;#vTIJme}cyMuEhVMxMFgq?ur z_JXSc|GYcC?wn?UPQj_6Kwj{awT)14)ry;N@JI!x0zKXNA)^)CL%m?z7zIcFs<%78 i@^%HcVE2mVR$brwCf_&VzI!LT7JcvB{nPhD{Qei&3FGzv 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: [PATCH 1343/2594] 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 87d6e27396..aa92a9f174 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -1063,7 +1063,6 @@ def mkpath (self, name, mode=0777): # OS name mappings ('posix', 'unix'), ('nt', 'msvc'), - ('mac', 'mwerks'), ) @@ -1103,8 +1102,6 @@ def get_default_compiler(osname=None, platform=None): "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 343c6cecae..0000000000 --- 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 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: [PATCH 1344/2594] 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 f5d1587d72..cad663a348 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -1004,7 +1004,6 @@ def mkpath (self, name, mode=0o777): # OS name mappings ('posix', 'unix'), ('nt', 'msvc'), - ('mac', 'mwerks'), ) @@ -1042,8 +1041,6 @@ def get_default_compiler(osname=None, platform=None): "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 130cd6147b..0000000000 --- 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 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: [PATCH 1345/2594] 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 7315a37271..57fcf97602 100644 --- a/__init__.py +++ b/__init__.py @@ -8,8 +8,6 @@ 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 264e66faf2..b50563e317 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 d4fc53366f..5a0fa727cd 100644 --- a/bcppcompiler.py +++ b/bcppcompiler.py @@ -11,8 +11,6 @@ # 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 aa92a9f174..a56a03880e 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 3cd5858920..267cf1802e 100644 --- a/cmd.py +++ b/cmd.py @@ -4,8 +4,6 @@ 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 0888c2712b..add83f8740 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 ca3da74c06..3df5ef4c42 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 f740c468e7..7324c6c2fa 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -4,8 +4,6 @@ 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 9aa7030049..a48e0f186f 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 f18e318cb9..ad6eee8ea8 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 7462e9348f..84e050204e 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 69d8c75166..fe921fb88b 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -4,8 +4,6 @@ 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 1461409f60..fbf2a0b217 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -4,8 +4,6 @@ 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 3bf1267328..d9c15749d0 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 104be0b349..48e06aa562 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 02189c531a..ba03d1fff6 100644 --- a/command/clean.py +++ b/command/clean.py @@ -4,8 +4,6 @@ # 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 520c1b0c76..6b358cf250 100644 --- a/command/config.py +++ b/command/config.py @@ -9,8 +9,6 @@ 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 44c76926ea..adc9daf11a 100644 --- a/command/install.py +++ b/command/install.py @@ -4,8 +4,6 @@ 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 1069830fb3..cb11371023 100644 --- a/command/install_data.py +++ b/command/install_data.py @@ -5,8 +5,6 @@ # 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 4895240a4c..c25b771246 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 81107a85cb..fed7e5363d 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 fe93ef5af2..29cd9e7a0e 100644 --- a/command/install_scripts.py +++ b/command/install_scripts.py @@ -5,8 +5,6 @@ # 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 27883fd68b..6ce8c43ddc 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 62a3389b53..0e85ca6f06 100644 --- a/core.py +++ b/core.py @@ -6,8 +6,6 @@ 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 94a7bd96ee..3ac1dceb3e 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -45,8 +45,6 @@ # * 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 b67139c7d4..2886744402 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 2c6d7927f0..39eecfb248 100644 --- a/dep_util.py +++ b/dep_util.py @@ -4,8 +4,6 @@ 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 77f253255f..54f5c68e28 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 18cc910900..3dd776ccd2 100644 --- a/dist.py +++ b/dist.py @@ -4,8 +4,6 @@ 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 e72221bdba..963d83377c 100644 --- a/errors.py +++ b/errors.py @@ -8,8 +8,6 @@ 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 31cf0c5c3b..e19319ae9d 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -8,8 +8,6 @@ * 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 37b152ed8a..3af6344f11 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 6d27cce64f..51a1d57e13 100644 --- a/filelist.py +++ b/filelist.py @@ -4,8 +4,6 @@ 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 95d4c1c5a2..fcaa545a79 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 30a9fc6f22..d38afb7223 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -8,8 +8,6 @@ # 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 e5654ff009..a6d3426390 100644 --- a/spawn.py +++ b/spawn.py @@ -6,8 +6,6 @@ executable name. """ -# This module should be kept compatible with Python 2.1. - __revision__ = "$Id$" import sys, os, string 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: [PATCH 1346/2594] 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 fed7e5363d..4c62c7133a 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 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: [PATCH 1347/2594] 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 6ce8c43ddc..e8b6bce9c9 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -456,6 +456,10 @@ def make_distribution (self): 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 7cc04cfc0c..96ec381ec6 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -4,10 +4,13 @@ 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 @@ def tearDown(self): 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 @@ 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) + self._init_tmp_pkg() # creating VCS directories with some files in them os.mkdir(join(TEMP_PKG, 'somecode', '.svn')) @@ -68,8 +76,6 @@ def test_prune_file_list(self): 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 @@ def test_prune_file_list(self): # 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) 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: [PATCH 1348/2594] 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 27883fd68b..a366d1eaf6 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -458,6 +458,10 @@ def make_distribution (self): 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 7cc04cfc0c..96ec381ec6 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -4,10 +4,13 @@ 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 @@ def tearDown(self): 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 @@ 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) + self._init_tmp_pkg() # creating VCS directories with some files in them os.mkdir(join(TEMP_PKG, 'somecode', '.svn')) @@ -68,8 +76,6 @@ def test_prune_file_list(self): 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 @@ def test_prune_file_list(self): # 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) 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: [PATCH 1349/2594] 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 96fb7fa1a7..054fe191d4 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -428,6 +428,10 @@ def make_distribution(self): 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 7cc04cfc0c..96ec381ec6 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -4,10 +4,13 @@ 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 @@ def tearDown(self): 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 @@ 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) + self._init_tmp_pkg() # creating VCS directories with some files in them os.mkdir(join(TEMP_PKG, 'somecode', '.svn')) @@ -68,8 +76,6 @@ def test_prune_file_list(self): 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 @@ def test_prune_file_list(self): # 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) From 52141aae7f073fe917c841ddbb4531a9beb04ddc Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Tue, 27 Jan 2009 18:17:45 +0000 Subject: [PATCH 1350/2594] 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 907f71c313..77814377a5 100644 --- a/version.py +++ b/version.py @@ -21,7 +21,7 @@ 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 @@ def __repr__ (self): 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 @@ def __ge__(self, other): # (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 @@ def __str__ (self): 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 @@ def __repr__ (self): return "LooseVersion ('%s')" % str(self) - def __cmp__ (self, other): + def _cmp (self, other): if isinstance(other, str): other = LooseVersion(other) From 8e32f7ad504aad7f9ed2603c282f0e9831c716ba Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Thu, 29 Jan 2009 12:13:31 +0000 Subject: [PATCH 1351/2594] 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 9dedfcdc2398df99f9540120dba9421452795948..11d8011c717c3a1c54afbe00e002fab6d37766c6 100644 GIT binary patch literal 223744 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 IUt9dY0OTE8cmMzZ diff --git a/command/wininst-9.0.exe b/command/wininst-9.0.exe index 9102ecdb51fb3af90b850165af47b79c7c330036..dadb31d8938b9b147830382495eee2453c171648 100644 GIT binary patch literal 196096 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 r^A+wA7Ib9KOZFnF6;^o&F%T6)Q%lh`woQgW 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 delta 21049 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 P|H}h@WxVLsyx{)=Bb7Hx From 4b0e22f7614b2454d2a1b0c1abc13e4cdad49707 Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Thu, 29 Jan 2009 12:31:51 +0000 Subject: [PATCH 1352/2594] 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 9dedfcdc2398df99f9540120dba9421452795948..11d8011c717c3a1c54afbe00e002fab6d37766c6 100644 GIT binary patch literal 223744 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 IUt9dY0OTE8cmMzZ diff --git a/command/wininst-9.0.exe b/command/wininst-9.0.exe index 9102ecdb51fb3af90b850165af47b79c7c330036..dadb31d8938b9b147830382495eee2453c171648 100644 GIT binary patch literal 196096 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 r^A+wA7Ib9KOZFnF6;^o&F%T6)Q%lh`woQgW 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 delta 21049 zcmch94_s8&mH(YTkWn#1BBLl^P{ELD3_6g2f*F)iOw^1b14Ly+8SA07##x@ZB1jE#E`_cHfytwKvS9+gTKRXo;o}N3+5UQHzU7y ze)QoWkJ;fC9^bn9%$@mP2G2_74HytZnKKz;9>5pQ+a z8C$6{va8T)OIE>x&Xy}6~2 zu@?w4S;q$A_Rg3Vb!j(aiF;=xOx~_AW}~W+pO3LlJy1e}I@3KvTTx8?(pczl>@S(I zP4nt2%EfZV3TS*EU^}3mh3?W%y=QJB(hP*?5q(VQ>pvS~;d%89^;=Lh5QhMSvB{KS z&wKTc>i)@i+e$yOMRIjYT(P_(CAQGFn2B|s4NlckvCGEf!P3&EtD>v;bTm^f?LL(B z`v0g(w|iKzYT1u`Q&^hR^gGd9EDZ0qg*?Wj`TMB!cj81|!aPyxJO1vN)6pB2N%DHv zMr+ts)>R}A_y=MNoyBtQA)hVOX|%=-pp}2fAWc)HWL1h)r5I&WNHtC|pu5o2sCx}I zzb&*rD&4CP5j%&2_A3sD14LX#K3j^D#vy$P>3+~I#W_7jCkl?B0E1Au+^fDzq?@y) zWHAf<3u%5}&vrVU9(|!|iNSOW19p@8u533gIxS5hJfcI010JSPl#F>;L*t`U^%pmV z=0{tq_MswR?+x0|c>Z9$>IiIndzq5Gl!3fNe|KV2Sd3{>#cw!-9W+n^p&1>@uksly zEnRe4oP^4W4nd&aji3#kUj>ds8E^svt>4hxISJLav);Od`H0InT>6fiogPxrrn8wj2 z(Dy8wSi^mfp({02M7f%}9UC^iD~AoWokwEG+=ry)I?akdzt^s@%5KcC#>W&>;NY;8c4dsE;&f_ zTVF3z9$yc^{7DOPXS?K#&-bGv4|4|XjSdf55N+tsOnL0$L3C4E+8?W|M5?l`2 zI~>B1E^J4gQz-c@@${J1lgT0MN2~sjZ10dw!J_uHg6RUW@)is;!6DRr%NF{6O#EU8 zkoronY`{~cMj7I*s3qvFPISm}J9XlPwfKF=^R3OtbHO z%uO(I(hfO1sw%ZOu};IJ&Ft;6$)gJv9a@Vq;?YMgHe!p=G0Gjz_t0Y#lMP3qn7=o1 zninw>;s`B^k z9#*4Sz9uC9BYF(%^!3;qi4W=p6l0)fh@6~667TPhEmq%x5^4G&Lw%+XqXe7U2{%(f zS{MX14)ubZX-KA{=H?yu6-3TSf_%R@phMlMz_2w{qGcw7*U zP%2=*g5-WJIgDhpmK;J-)RKcpR%poqBpv@~3mrEvT9A(-^(}d*bkUb$u8>6tYSFNe zPKkqjrxXoiHlFB%v~^D?EH89NY+?clzib~kNb)M%m4mdSW&7oW)zk7qn_}a<(D=kb z`|x$*uL&EPsrL)QN{Rz^A|p#n5}k9kB+(h6esoTJqLF!AMtx)+ih99%Hha~8xp5rW zmAz`n+{k!h&}(k|0*udIb;aEH2@;FmGdF(33lwwXFR9>^x$zxdaM|2=4hdN@E&7AG z@ns~lZZH=EQ-qKP!qh~k;BWP4D`)Gy3HmrCnU+Wl;p`>pQS_p$+n=oKt ztFg{QV3BI+*o_uN19Fe@4jl*FhER)!g7z02g+2Bm16rw;7T!%P1bCjj^GtAgl=6cj zkaVTkiDNY%TK<(=lDv>-pvm^3(*E%teP085gV_>vA90nfmsbs2UsuMV(a27@&iA2F zzHcWEb6!SxO_CB>0pfMFwgOlTdC(eO)GIE^whxPUXWOrc^Rq)cCXKt9nJ}lKVF*l* z(l;CmO`74|H9%%4X*p)?4m75a_LLh__8}@Pau$1_K?C_I@v5bSsLv$c$-RLMDTx>* z_XV0bIQc3I;Cw7p-&HU8!^sXx`MZ-}N*jM`_QjV7kVDsN&>mlk9EIMp(QwlwCk$e1*koOE;~_m(#F z2np(2)E8_|W^O!;xpa15z+jzjkIB|}R=Os%Ba0#8y>m{>Lon%RTA7Csdi1j5VMXeN zA}l`T@m~=r$iZj`gZWuH(A`M*gZ}O`*s=HWzU2#Z&hIfb$LOj5t3~KLF=$-uA>?)- z8%`IUHfCX~I+dkYRTZM?L#e;po7g`=XaOU7tD$vCobv{S8N=Ls_Nv+X8vvBMDz4uSF50}{>|b_z8K>P$|S|HXGh{Xls^a67e zE<)^p32Kb2JCakfqYE&8|910>_ElWD+# zrS#dqh*1e)f3FH1F&lVF?G>zce**7s7OWE9VcZQJ5(~pPNNr`x&m5twDKpb?tzq(z zA8N>G4fn4uq@xr@M+dDdT{vK!_Kv-tg!W9C=8XYCI8;22 zpY0lm!_L)o365WqR5>&&e!iEK`L4x}IWuk*Z&`b?5z|!O83qITE}>%4Yvz^>_Qw{JJVwsQ)Yk}qtbHJ3=wGRM98o3o6wu}sxx3e(qs1- z7-KNjI#X~pmRbT*Gss|G+*WY43<%Ob_!TgoKyHkHeLpThXdJM&<0A6PFzu;;)DhTt zNVN#Ss>BwY;uHo^E}Tc8j?r4GbR=j$Ds)pDObFNz0a(ZH%DG@}$dxd4b~OsIvklk` z`A8xXsU%3`)9u;%`g&#a%Fy=`62@(yicD(wT0-vBO~}#4gX-=y((GSbsm(qVpE%3= zC`9D|?J2tQ#F6@{$5!Gx3_ILGBOzXg*rAZY-(yVaxQUF?;orlWkKHp$u$E;BL+5N4dTIdP)|3X#i7MXiIWm}fw^fSnvh!t`Z=^UX{qCB#=sb2Z_u8D z%fNtegiio=IAz;~9aK~8p%s{S7ib2+-=b4>Rokl9_jKP_$JP88%4fMX-W>~I*o;zOyvDlR8 zR=g8u;_Q&`w#j8?EbA$S&d$$K-N%sS!i3e}N8zX}6{+vOQiruKVX!eJ5%t1(%=7vlL1{cnHEAQu2_16lOUm`- zYE6?zn>K%4)BdQ=btnf1^Qjtr5bNY55=xK3V`f4f#I;C$3xh&JL(e78Ud|;om&Q6l zH&CbWd&~z@#O~G-9aQ`bUHqOQTeR`p*EB4A3FHbend4j9eTOQ7;fJ zBkCm<*gjz6R-jRvJT3{f{K~1dQLKDjOxCk|_7a7^pK%wzZO)jv$N)-1u9+!bk_SI- z%|?%|Uk~LJVV0&aB3rs8B_1NB*^<_Vx zm#ji^JE<1()X7OALY{w13mrln8b?}2VOGFA&oRk*)5;xcCUv8}CD1iVFcGzp5`T9A z6o_!8<$6)XDPV7>Me>45K|w9Z!^MY`R?yz+fTJ#uaSKcnINnG~nf9 zCk}s$repj&n|0Dusuzf(Lg$#lAzA{R)gzt>FYzZLVMjDFv^n{^d|5RcIr`TRV{H?DbSRFvPPnbvl|>5vmTK$-sU<@6%m>+f81?HCyn;TP+%}VlN$;^#Ykrv{gga#v{!Wq~|9<&(bmj^aBYf9f5-qTF_wz(y0 zznL+*41W@Qm68waS*}~&7?y@kFtj*)xRd(CqXBNs{7Oz(p=5KsSDXHC44)lZ)Fmba zgVcBp*O`Ly1mE-8jVrbgs z2wDuGd4YH4t9XJQ)+RZTD|T}V3J?YCEx{%tj{7+RcAqlkzcg2exR6#`tMup{@*B!s zef(%86>Wjrm%4|i#D>L<+#X@v4f$}Bog{d-f2FpxlpGz$Fj3wDjt)7OM1toc9v5}g z{h@g_$RsAsDEAf$^adET?;mwV6w+0Y7{mPx{_b>C!W(OV29a7%=P$_I502ts0f|By zXyJ=ZyXpehO*D1o6WrclNv-d1AUh+Z-aeThLD{(^G}5igJs-lm!25(+r@o5ee}#A< zM~60DmD1#<7eLvr?WQc4N758-cF`FRoOQqfZ!}anNTSSdPumxx!B0AnrOU((PZTR9 zgEibG6hOAn(ulbqJf=DP74;JLK9`Ja4f}A!CgIoUJb~;JGW#{Flg$3mrL<`)G;>z! z!KGlt>a%H%x?ko-48~mp``Ps^XCDqW(e235!z0?F5Ez)FOYfNFm9MvHyrB(O#bl>w z$4)yYK!TP57Wjvfy>lT)JJ6}HNt+p2%`nnnCzYT;tK-ib{M6)r#AH4c97smlgwQ0j zJ+YzR-jV7}H;Jy`k59oE^(g=G5k{Go2V&sb;Rk)~)Ob2qLq*p89 z5|&94ES#f~fcp)epp`_Nkhn6(20Ls>4BGq)q<`GYE*Q7_=a zY7voz+7*0+=C#!Etfd-}Ds{`5mnZ?TK7*?vFGDjqNIcPWe&-^Rx68oIHcxb|XEnm#Z$wqc~=rJjw2C>w$!NHsV5QFQtyw%qF(kL#B8Du1Xo z;2KZ24Q?R7Pn@YeXhiN%@pvl=5|J3pwwsGIt#AJVFb;Q+7!Zrc-UQf};$)|AnX~ss z)X}>Ny{2durWc;(zsEd0x!0#r|5r@I`)Bho-8hfo|G#WuTi-Kk56Vb56aG92_K%X>X99NkaFQtD)qh{Wyt1}+zM)}slkxfHR@;DU1NuG!Z z?i-?@7=nPcRk5}`{O#`<2#w9N(ZQY97pPeDlIzBX0+T%WQU^?iJ+~~JEX6neLX6$B zMz_$ZlVbAnmMw#+_L22$e^kO*|9k49H_VMi=&G=&xVR@jB_*vD2hWa_H1v`@_VlA? z0r}1sKnWcSC+YAjE~Jkn#vFZK?36LWq@8^l7C-sE?%<<)4zwTf7(RUQZusis9UeW3 z<8iF1zv0Ik^bPY#lY~D%xgy6J(ROSX)}NnQ?;!iG&TYIBa#f|#`tQjaS$~*$hj8c@%1^(5$Qt$-N?{1W7=TGj zo)CL`kG*3&IFcPa1_W_3*lPPH5`E|&>|u=97ch|EG#HCd=AeeVhhu=2hD_gQSZyH% zDYXGRnwUTu1J(^bADQTM3Nfg1_Wc}Ko;2-FippkGMmziRQA$?0{SfCG7;u^M2+M13 z{AZXja2?S0zzgqvfuh!agIv@K(N< zI!^=AKJybl07vAcGw4CKdzS5c-YB97)JRLc1&sC2k0c?y*D@7ks)RT?HvwbAJGD{T6=E7ZT~*cttaVD9Fz`m zx4&f8=t3i(w{JWm#Q={pn9}qoFvSz=5VFw64U$R{QZ;sz6h32m(emhB&@Ypm$|7uM*TAnxV@hJd_l z=22X~B~qL}rq~Yt*@SeI5MdD!Qa;DK8{S#UaMz>#jGNvstaR!hK~B42r9nP-4U}L- z$ZQIIIe+RDeB}a1G_yQ_ir!p*7YAkfzgo{9a zN{k?cM%E{C6#~(yFpc;(F9_>Gw$QlD`QCMChn1w8O5e+n5IK`d3h|nQ8xQwb2JR#Q z=!v465fET+M(}~~FeC&|%_wL`t5@jaEO(97#=cG!%{GX)%746me{T2luk8pi^ipPw zmr#`FKEk$;f2JJ%m}{5D6pVF)7x6bh(C&5e(19F02Kc*8uPERP_SrOF785N(2BuvI zdels+SCT$JmwtYw!{z@i&@LoLFoftCSkKHDT;WwO_v7%-Yl@ZI-yp#yqho?F43SY< ze=)X`C((&NS41-=E)*^SfyNWgAu(30FKC|*!RU& zqRx)}KRO$FXS6e&GHFa_uJw4LYe_N1GFG+`HbBQbvH^_9l4F#ZED=GmYw*k}*KH0#sz>O*AwgUN8y8IV$qwN*if`vl&KDbrs>WEh@kE0Y- zu`efMJ(O|hq2_o~l*JV}3Wei9rMIuwITF1ed6mmpUk%IS5a<=DP&lTcMTWhz1;gUS zv4fYsqm>?{6-Qy~4TW`7IG$g>3kBcUUZ`*E#X?~_N`-0!@K}I0EH%HW3O+O~6gG@) z^J+Q>^o%bm6biIrPxMI}Qu628L7b@k<0rH=#5$$_ca)GGQhHF**8vWDqE2Rw!A(-u zYq~JS;e51rvfB4s`CTRhoVU*T6ygL_%2t zk3QTw5eI1OE*m>QYlhx$f-a!LZ{+!g@;S&uKx1R^iTZ<{kBrCpKh`w4PeJE3^A z3%o#^)2aN+`!v)+a<$^xm|1 zFX-*xlAXJr5Tv97gP>T3^`(3K7J1?TGU|MXviLJJ=+EV!d52NP?>xyiG077xBw{$s zL_sm#lf#m!_~ani)E`}Z@*HBt*!%p43%{c`*q*Y5yt%V^d?Yu+Yx`##oAsVG?rGc8 z7_#?Ne>|np+pJSo2=oD1$CC|T$gkn`OMd@UziHMf+1M0mWYij=INpjhHG0!(e2O=U zMx*nmK8}@pXT2%MJw+|3^;bvo{_hR-wIg|BF`@S(Q_=|h3$yZXH6>g5%)qISV+ns` za(_0nuN{s?)7Ymm^SuOO9C7|@m>3f8y?r(@j+W)mSvPio(5ZWG<4gLV_ZAv#hi##3 z+dW>}4>g|mT4SEJ{p-jWNI!>#I&Y163W&^lGZyMiTB!GEkn^-wXg8~u#DE&1n2Q88 zI%CBv4>cl@`01%Wh{UWja!l}RNAl9J|IJXNandG2lO7B zWB;qR(9J6Z{fxxW_7!&|TuorGWbx%{%$!~^NnRdPIHwCA?PMswOb8ucaX>$OURdG%&=Q*4(cjm=yZqnvmh*33zVT&Nsv(eq?PbkH5cm zwIO`M_Wa=513JSYRP0-~+R%*nx9jHf{M4c)hB_dW746j-hy$NG3v~uCC{$1)8NjjU z!zC3uLkXJP{@@CO4X6zdX6d}=i*4+)60AX~jTs-bF&{v`-o`orIU8*3Z9wrx8~YL9 z1Aqe9U1nn+0yUWB9<0?FKky zeDH-27}Motp>J$>cIs;;Fnw8CVA;oGDzRJZpOXlMOL?{nOE*;j&u& z-009&*KGPE>G^NFmg>?rg|Uuyr2|p_J@oaaM~^2FJ%W>$uncpq1$JIJnH;t~uPfiA z_ZFbs|C3E;yE^y5itX|gm2{D#8cu_kgj2viBkQQZWd3*9;dB<#LDB_x6E1~5EuA{o zhXW^-%qul@9}oBQu$_l7z*19>@HF@Dre5Z0g+g!Y0M8uaAsxJGs)>h29x@&d^V%UE zUg6;&4?B2R!ov+bBuh_Ct>B@+!)hLqqZxlY!o%%6+`&Vyk4JREtEtUAY~djpYierU zZVGKY%;8}N59xNGrtatAJ|4F6u!V=sJZ$8lkB2*WxSfY05A%6g?d6fc!wMd5;^77! zmhf;L59varrqYKYYH9%wZJfj$9%k{-!NUw5=JT+ChbwuQ%tOXQBM%dOUg|EMXX1Dm z%flERns}JO2Rq8cV>~>EjIUb(p;oCeU z2cDWrPBAt05)Uu)P(fI6ARaVz3AcU|Dxw}9u>%a&96V}g^kn1>N9D1DloUECI8MO} zRAU)JW8B*gb|cpm-sv@YOv-LBw6x1pBBb@9x0su7?fD?E@p51z{!VT`_U3u4SC4%d zjvEz`4g+h$%XP++i7*uLw>Pjw-v$UpPPnKCMF0JD-~cBbs2-tz4h?Acr8?XAQs)o=>h2y{$eco zk(G^MG1|ZLGLx3#XV$f~QOlboB^}}WJzEc5&KMte9gbE_V=%sF#^?wV0qk%WPae-AA9cvT| zv8J?3b}O>|;tieBErFq}KbLsHq-gv-4;D1CI0tC_{z>q8P}@jhr^*;pyqI@a=c)%8 ztIS5y#zcBWJ&@x?GU%g%U0Du0di>b@VnO__YZJh5F#)k2Wf%ONjp*d&ZLeb@%|l4` z-|XKm-n{Kq$}7m5pE@OoEE4*?Hb1J2`=x&q>B?|H343gM8=$lFSQ!%B5a0Kcc9(mM35YHhx z+qV2+UA8w5wbt;)*NjYfYdbZkp1>|>w<}m9HI>wkCr)sXg7N7YR1~cjd?!?U0nZ1Q z>63{V{tlXgveN#CpqPdY7}W0`j-Znl<+7Sd%Z)Z@bSY@R1poZy8hkKnk~2vhP~lKc zqFum#QI+sVNSa_sm*^Ebj*CiUV<=>iVuLAngPtyo=BLrR_`0GyWi|Mueau-}uo1=1 z&UjFN0zq53OP4gRGnujoC_61hO{JZvrZPy1nrh@BZF4o1w1B4c+uB{a_Gxu!Cs-hD z6$co84*!4&ekWR~wzZpd-%SV8;&{V&-cUoOcVmzXFHWF3B{m3{b1Bj)bj#JOw*N@d z-RezjK+kkeD%n_k_?0(ysi`Eod{X|MCMHf%r9@SV#~(RI{!ZG$G2{3Q1;z}d&vf)f zwxZ)TF;G)@j>1p*7!-hsWwflx=oJf$|6!pat)|Z1J&hv12&uF~wLu4B`OJ3I>5{WZ z0{b`N`F{xFa}**XOT$)rwmwGUGN@W!h{T;d4%!EScid-dd%I3Iga57C7Z(DV)JvHQ zFWhSCI?i$j594{5$irkFa^Xsy%hTyR%-|uN3u-EzN@^;dT52ktaB3&5)k^tk> z<{>CR+Dk;KsYIfhN(8H^GzB#^i+8$`hdDg7@i3o<1w1T4IP$yr2mGl$VC|=jEdith zrUPyUe2Ma(0_ZpXcZ>}UXzAU5fKR9r6;41t;0eI@07n7a0QB4T31fQz-vRs^-~{03 zfDZvz0povTOE+7LC16YT;#ef_tM?4MibFi})@F8FTa1{{!8UCdNz%;;2z+AxH zfPBDezy?4ipa!rF@HpUGfL4I_I|vQ|jsac;x^AR6!`a6Scm2sjU*-wPP?2|&lE zNm>DgrJ`*#^Wiodvpj0sJHs+%i_(p*~PuG@npWgPO?>_xd`t6QWHkJ<{?{^mBG(aN21Q-h2*d+k!Ui|)J`Wcg7 zJik|eEY?KlC7q*fzGw7tumILb^)Zt?U_}jp4CU#F7i_b&-TM>$RKvMPZEfG} z(ogk1_Lz-52@pt?Mt-v){AoVM_KzuR2lq=*wj;9r2{j8VoU0=eCVX?g!fE%o9)_4T#& zPMh>!hE?Tt&MNmK4^%Fzs&=zSBdC-?O;j=UIMjLq2+^+`8J)@{e@OZy;j(n4a@kp0TKA zYkA$mF+=~zSluAjSG#L4V6q;K9rd+a@+vCo-3<+l{XxI7w!C6x)#mzgPXDwv@6SO0 zO5L)0w>whI{sg2g<<(Tgmg|<))^9BrSD|_NLvD2XHc>?M6;^%I%}Qv9b?&WoRI<8! zD`7l1QsyWZ1>~<%i&eE1QgvkRD3b`s>8@|6s;#kmx8rVNSnF<(Fn0k=u0SJVkE6b- zw!TVybZK=tQU9;h(Nd|t-d)3|MN>LDrfj6`1S8sNnUNmYz6j%p9cTvxZ)Vr#QuOe3X?hM#y23I8MCLFQ0nz74998zw4K@_#Myc*azTn(-| zNp#n2W6$Yal?`IKcy~sXbpg9*tgNfAsu3%_@Y}dtRkfR4l~N5FEQ+e<6ypr0&cMq7 zW2NY>u4W$_D;wPIhuQnaN;((W8^+2lB## zX4F44)Ygk^AxPGy`;M{lA-CAT9@GZhTF3Taa^>|8ZNqI$OJRzu3Y|;WW~a}qz@#i* zovVCn!$U6j_A0RZ`#?euTg$6z(5ivW)w#B4H9K?~N+olZY^fs&7TxtMRqv{5*t&Uh zH5a;bMh*8*%vHW+YejVpGorqxN(Aaw7Ii)CaJ=mv<@xq$Kds%tHQlm@;>yCyWS<#);>%c=3b%{a~ItTdiN+2 z%O>?uD3;fYQr(K0%Gx}3r*YlL%pzMD#wXOD(DwS9`YBcmZF%H4nKT-RhH8nd@|*e< z4f)^|vC08W2>rpP9}^vMcnqaTbZjSP?C2RN0FHi+)6 zq_*=KNR3d69m3|pwy)X(3H~-uS4eg2J3Pe;SvI(Z&6kA5%rv<8K25?((7rqa#Q=?3 zQ@$0JG7I6jQc*H9a--I{^@f5@!!MdX*59HlnxSK+@uXEXG~XN@>(E`#C*P^V2EU$9xl4z`>w4ZdU&rWQ zDHv(LZ-I_E^)J@+>!n-0$;mktZY);?HtVB{Cyr0XZ?r_rYiMw9-CR9VfhwF}ykrF$ zh&a=dX(#8-e59(zx?pB978{vWRa@$78)_@XdCsNKZ%fy%o0+^#J0x>wX3tBXw_twy zf(7%kW+vBpwR5btJ`dU$+IEX5)uV^)S$CsS+FV_=<$m|0>##v*7~x;eXi zzI%R_+q!w?;+(s_O3ug(?i!iYjZ?@OE$5TXxvTBBf6^y-9eMfrD^@QrDTyThm-F7` P|H}h@WxVLsyx{)=Bb7Hx From 7036aee66775cdb74a5c8342e6d08c8df75d4048 Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Thu, 29 Jan 2009 12:36:50 +0000 Subject: [PATCH 1353/2594] 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 9dedfcdc2398df99f9540120dba9421452795948..11d8011c717c3a1c54afbe00e002fab6d37766c6 100644 GIT binary patch literal 223744 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 IUt9dY0OTE8cmMzZ diff --git a/command/wininst-9.0.exe b/command/wininst-9.0.exe index 9102ecdb51fb3af90b850165af47b79c7c330036..dadb31d8938b9b147830382495eee2453c171648 100644 GIT binary patch literal 196096 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 r^A+wA7Ib9KOZFnF6;^o&F%T6)Q%lh`woQgW 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 delta 21049 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 P|H}h@WxVLsyx{)=Bb7Hx From f7bf90fda5ccf81484f5bb114cd0115c62eb1c18 Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Thu, 29 Jan 2009 13:08:01 +0000 Subject: [PATCH 1354/2594] 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 11d8011c717c3a1c54afbe00e002fab6d37766c6..94fbd4341b2a140fb0aa38ed1e10ff69600d4911 100644 GIT binary patch delta 39360 zcmagH34Be*`#*kXl1oUg8aTqS2M zB^NAf(&;VJ+HTp7Gu4M?e6g36LPpnhR|Rad?l|E0|2e(xfPm@iss*fG zcNp-{j4#)X1H6#YeO;b_u_FIMM!bLnrxZ=*b(@U8f*h0SsikI9&90-OeRm0Y3o4`WfV!4zKHTLz0UKiJWRKm0lw~G+OE7q zdeh0~wGknH?d?y`vjgol95wkOGExblVV$cEB03YOLx5udpZLo(nKIiyHQqiRxF1I# z$V8JtfYHjcG^EM)=7^#dQwzX!Wj$@#2yL7Eks4mUoAw~eDdPL0=it^Go;R6n@&#L2 zm3^*FK7p1rDSZMp$*S|7R@@=!43L`CQF+0$6E#mzwtBAjj~P!<KaQI6+w=#(~D)ubq8YJ((;HkOw?j^U64A&zDy< zT1e<8^OWx1K{V%iWrTNl;|~^^OvjELtNm@E;`E+NTP{`}diP4Od6;ZvhwWx{(PGZ2 zqiXxp#N>?eo(6W>T;}OK-+3T>)c#|~Y_fT@O_}8r={Ts<+T?rc52px z$&Y{kI8Q!=Nx6GETRv=akI<41gNq|DUp2C7^Ej;bY1U zF!&m4b7gbp2BQcl8r$TPs;k6gsyl0wtJ(OsNTB1G%~crfHO#j7Mgid?HpjI=casj- zT&2;O9OKN$>dpr*e^cDU=C;IRRy657h%Rq47-N0w85D3!k07#|v}lBfsg?^eFlT_V z$u*j^m{YsV54w3kNYEkVZ4-G%HHVag5@l7^(zU#hGvgeHO!ogIU9!0|8n@*&;AjO&zK?_FFL~liCbdrZj0oPmEF4HHq(DJ&ZS9+%T!C_AAa( z@StE9BH)JQt5?7qw`JvSYDzkjE$=sGY`d7T`97#SQLSms^#3A%%>3|4yuG6ZyRbrlaijR48~#N9ER2sa&om zYtjxKA5l+wRSS^b(Q$ualF@D+*T63hRDzoIO?UHo{~A59rR}$m;(Qh~HQIT^hl}`0 zEbNIz9wv^8eY?Edy=xbS#t0zU}~N#=A(hYyUf2%;0r3x75~may0xQv_+M9y)L25C}f9p}(-f-6xvs_-8& zUrn;=xYx0ia^>TBoS%;hL~XAMltbTJYIWQMoeV96YtlK6lk>gMaw_nGCwqZ|T1m+d zvX*Xy2;`;Rww7{%1IpK0x>TUBgO>2Smxwlt92 z5bJ8SmU3mw_urH1Vh=2hTYN(fSI;fsVkeCeov60CZR6B8A&Tx^p0&++WwOQNDXvw` z(V(cj0o|z!jW#5R_g_8!NYnD%@vAz6^Jl*<{Lf$d7W$eq7WsU;Zgi z{w=TFHJj|sH@}AJ;J-AjjwV&*x;$Ihocq0Uoi|DG_u29su$9HlA(sKA-ac~e`(coEC%i*aPmEOZvyV9yOq^} zQDGDFdDjE-pxpRavgK;NMs=HMV7_uHFu;2gNOI+4nv{~S+z#wDY(Cdp^7oo_kGJlY z{)05~g%JOwqr!9)IHpNwbX4qK!X!8ANEcLIlv~#FXO;^4Q z8Wyym1z*L&U^s;yLNOFm>lq)Yp>pYs443UK06Q~VeBOXW0TLz zCq@5S>M3MuQZpWRTkL%?FU{}P&r31(mYsdYz+N1}0k$i#=f1)@#Z&6g+nVOA(Q3^^ zxezRMD$hlRe9oAXkC1}=8WXY^DJ%kO>2}_ASYO!i-J$mfD-j`5^AL1ER(J48a{jGZ zv(+B%?07LZkCXu+9UL2Y2Vu>In{tcJ3G>(H@pZO*P?)|>UYHxh^2scZgNHo3npFEO zAw#O?Y?xr!#Vx(D6{%wc7RK^cl*r{)a{(e*@&Ue82##W_cxMW?7G8O4qNQH`zL zMc{m};eVwL5b|r;GEyj%qhjkB99@vL6XVAsYK*DW&7lGucDMmh1ubHeUx_a9RK986 zhPs0lbBk#5i_*45r|=&jC{QNW(hoRe-O@l*SNkI3asw!>b%n~J7C~)SU>@@@%Hh01 z*)pvVEz8xF6iH2b2AQ?xqUkL*rK*MGP{0?cx(i~z5@umd*3JY$aRFI%^HE1{yOetHrH`V?+Mj1z{?Gre@_ zi8Tok5(grzZJdcH*D^~*N!<*X-COYu3vE&)P*_Mq8&K^6ZP+kezP6%Z1PF}LCf1HsHv3c_Pz zot#}*4hfo6QXd7E(WRs_+(_h81~Kccyc!;oa?$@EEmp;cA=f$|Snb}LTyap4_BfNj$ znI_!@dvu0~p@1P}ice%~pPLDwYIhZfyg|*B?vWikPeQRu6P7{JVVi4vbO|3gcF1{} z6oV>u!h}V-GFb& zmpivKRG@KeKrK*Anlu`Co_`z_?rh52Ktk5*vPTQBQ#71@PFpk$C*Hm%~LJ(;^ zIRd-qP?3W}1L}o(;@Ze#ys|nfG4Lu#c!{r5<)U~Y^q})VCJy46 zgLY)1w5qnRXH37QN_KR-;|h*4`DK^vEijNuf+d}`V>TB7zV#OJ6ZKzWrqPi&S!PWo3?uYY{%q z4#3v6&RRU*w<{Vb4%MVG)R(mr#|tpIiZf>>XfRm@s;y zud*m6kSZApi?O#&7TsGJZNiwKN+1QihvU%kI_Ob;Wa5*hN%2`qa;r$|>2%>faP0I2 zz50tXu~j0SpRR0d)jW1OFDoCd+=Lu%V-!`{TpxIeY6ozc!~w8ClUk)K-?oZR{4I^w zV=digoH%Wo^dnIJ=+64(J9f0b*^N_HWd=I*b0GJSppTj8<9croZ~&tg9H`#Qk6OmD z`2$p!bVpSlDbFRM~cwKhANgQhMKb9M%r6pnhNcx;yrOp`g8c!Pfk+?e0bB zFPqDvHb%8h-BskgPZvS2n(e4&f3c7A0YzdG5(^t5@)(r+ao(LC?Q3({ql0WNSi)ID zvW8|2+fjh^p&s{cTQ8qU7G-SgfWfa1K;>!G_Tc}KPu<;9*^QdMd@Ka(gnUku-b6aj zoh;|`f$kIRl56tiLuxLJTKSx=O1deYZQ9UL-INY(S~-$XVue{kfy~rudnb&zkr?<& z;R-=fobSOVelX_e7DKe5kv55f-{#>+o%UOCHZL8C(l2oH1Y*UBMd>Zv?02sdh&nJq zY6A-J*AF0TrvoAk*+G4EoQ_9$fvhjNBGAPi#e0%NWcLy577m>UB#cuqSZBQy0Lx zbl#)Ik??J#g95&d`+WpOKW=fjEt^xg2~dl5!O1hmb7ORc+5Zj5@p7{hnX+U?IoYzm zkMeT6T)I6&`MF(Z>XWX7#l^J9K{?C_XQ_`Sbw?`O-JjGUo|LAHjg$PNfzmZmCWM!3 z=ElR7<#8!VC%w=M{h&|+A;duedq+qE-Zk@4zCka(FJ#l5@eWehYQ}gecj7uZZY2vx zOK;~rb8#z8N=)UWkbcKt^d$%-2(IH?h77paxVI8E2NK}Tc2Ge$K$0t;1>*!ZIRIx^ zRN)Z($cvr{CUcW{#!B?cEsd*3tG~)XIAB6dhUN4kgj(G?l9K?Tj`}Ay05LT6Ko}*c zEe}fBj2TnYSMX6JWMWb_F^HGpni0_%Y6J8GxM8A_f9I_~=d)W%uScsVTtE~_n`1hni?XFF`i)6}U!1$>C(c49 z+$?=&h*Fn&D8F?G3cJsX7}nW>=$YB_3D~08aAv{)d#Q&KlMp`PQpfti1a}eaJ%c^> zzXmfNcrG3Pj>0XuIgu+5gOM#e(UE@!Mb!KS81-qb5Nl-b8{Qn{DWw*#hywAkJeD`YzHG6Q2nKuQL|&84wKVtz1it z3e-)vBQ{rW&01kzBE~F@N>D;OMmic|VeoMU@iTY z=gWdv(fUdCEN<|`rnU?VP(AWDPMC0jng{T8 zg~tBhoBA}+Ix{-;L<_|MYT^Y=t8cAi9=~$9fqX1*Xu?W90iqW+;DmKm*XBtRvP~yt zMv^o-1i8AR7V`-)Fb&WRFFud(G6dzybHa1wYusEq25SlH(egJ$T$7$l1RvqfV>nTY} ze5a_U-@+b8?Uh_lC1}z+?UnJJngxXyU|5R}ypNjpLo6@wX_C#2iL$a&3!2_R+0vm(-m&p;_MX*2}37p`LW(Dg~3`)I*emsEXFRxGaTcL!;*L2 z3wLw?MHTja-pvHzZHhz=O1~wLSY!HDBykNp6InJ{x1sssidCLT?&5Jvqsn{9VUA%K z8@HRT@@Cx9`=WxC(YMg78dWNAYo5eyb_(1~j+1L}3Q}Jk%lrRN4ZTFpQFZBmqpb#d zhJoe{puLzN13w&i2#I?b6&azFoauXC1_(WUjy|U zY0u_ayzBNL+!I)Ld#i42S6J=0h01yblTm+c(vN7BsThZv{(s92d9qw#TjfYf%k~d@ ziouJ!*30#r5j^{z=jaVz;};a}zX|(;ANJKBF7b1q zT~c#V5tdLNp|7w31VbYE1~8oK|A>rZ~tYOW^X+NcgE%u%g**QkAf^PY!2Pdiv= zGfWcrf571zu)WonmCoH-Hu!``=PIMR#X4GvMHZE%9jHsxq)V~&=NkPeiDD+u*1n3f zx^_^Lc7fa`5A%iNv*24z%Yly-<}o*1%5K{EG|q-at=|tVM^$wept=C1N%N5ePyM*X z4YLrA{Y=i$=3r?xveXs0_0#Qv0?aqp$n%uY?%n;ng1PnQHol3-<}v=t%|40vYXb@4QTZ4rRJBM*L?Ei z9KN8Q<3|#{vuf}wOl_?^>fU@n&ObP*2d{$Lu|nF_3M@yo_ib0SCp_n(!MvgqdV}ep z7WKm128nSedZ@KBIWv@6vz29;nU0%cHR8&BdNi+E3Kc3$_qC7EQ7;M-py1@~rK48Y zqg0%Ab<{e}OMYj56%I!oHLD(lf)=2XaAj;+=jWlDJ-u<~Ygc+@#n3j%$}?GgybeQU zYSP$fWlxsm@c#xwb6W-oTvQi6zyq<|9_O}~e1t2gOmw$Q+dL5aAh728+HCT6e{^E;%3w@LS2V%O3mY@vkk(Grm^Cf>nC>nSmm&ZVe z0(Bna7!$6~ES&HsL8-obS;+Y3bhwt6pG>D)8Y0qQI<>-t(SJU;D#uC?&?T$W8ZL|2=G9tpyVs)Kw2T3ik}IFp8?v(2RXFAa7y_nplFGjPCyy z$vJO11y=zwT>mz4o@cl%6)n-k7}3N9?(PaOZcJ*_Hlh*8Uo;wlR%y?#jBH zSTbDsGADtwR({P19Qk!9>Vr0zDNb&%)6Bu&@pXVx1>fT)+hExjOcN5~&q;X(P}%%7 z1SbebiQm3Q2~Ia1bfT0d{nA1i+pC?WvIWY33`*Qwsjyz*A#PxBUMLlw@^MCiu2}M+ z%IRJsXk-f|u6Ly4yf?~;b(a1sV#Ye-+Eq6HAP%YVy9BdC?OX++g;;Sa#iAJtW{LL! zdHh28DEA3#(t5PIn{VJkAA*5Thj5q7A@v|6z$QzpP^Vr297VBkT}e$*&670@;UnS7 z6osdR@xs-rOD{Z76tY#=1UMDR27E;ItQK@@OvnNaqZwF zyiX{52X&$IH~A^cEq%w&Ve&!v&C$L2I`s-rgYbi3Jm9Mz$$L5-IEfiH-iP(o$qRMz z49KtE#y{<&{$VJmNfDgVogBhR+!<*dm8jgLM&I#=5AKY+iOSgASjUG*ne54+dP~P| z=SN|=2G$vRRt=%cbDKACoLicuXF(M~U?ATZPQM_mgaJ6!qX-VrjyNO_i8Wwp*MT#y zSajAhhR+Us9SyJt>6pO+6C_acgT-QbVy!%|B<6q=? z9jOHJL0L*rKn?r~-Eo;$@)I(wJAWL7LPW`Z=)YW3`!WPkmd5KPUTZFxz8Rh6?yhi* zww&icVR$>B<6ty4RHwp-shf?f>UkhD6$i@}pGF=g=Y6uM31-<7arN$RAnA`TJO)Q9 z3( z?k`X3JI{lD7O#BNFO0qvUw*t_I}bX!ZTW)%Grj!V!-Y}ji}h{c5q>Z8V^;a1kr9+e zWGZhI22wAZQd#IE`;~5^qk~?H!2S4dIKs$7eQk2zu6Xs4+wPRQto-@Wvq`{>-q@hd zVSBlI&~z120mW?I(Yu_EjiBUmdGz>nO6T=dCKR=%fjt$uD2?vxq0|&r(~DWk+NW|! ztMW@vXsYFgJqUGJomrZOz=&KpZJCi=6yEDoQC;vofMViuc>KbZJQ_f8tL{FtE zZKo}wL8;|irfnxQwL|&1XG#fuC0_aYS(*M8r_7nrmEKG)|7gZwLLHqHk68n0vrgs1 z0LU<9%bZY>QeHD>EcLqxg_yO&7Q;D zeF3uY(55ba2gPoTD<*!>$4$q!_T^Rc{zFNiGWCTB>Ccsxn zzYypsYl=76Fl=gXKqYE1vwtB7pAzArS{~ma!Yv|vMTDzFSR%sNB791OLq%v4VX6qD zMHnDLFA+Xad7U>z_#Hxr7IR7@s0i~!!~H~9Ai{wn94x}2A{;Klks>S<;TRE)6X66A z>I0Z4;*&)sbsUo}!uBEz7ondBJw^DJpoG)- z#0xD*AEoYvHjYez>L|i!5eA4b{AXUBp9nog_}5Pyql)kw5grraV-ao;;Ybk{ig2O` z=ZSFY6ZrNbo+-j5lHl@m(c~BrPDWUMYGHW;djHe%veGM9ZkLsb%ga*tRzi`aowf(a zB{dL?GIQ6R)Ku)H&Yuavb9rD_Xt@IvDLuXHc%0qSS6(j2EKPTo$GC1#Z}|ksaj3Pt zS)sI8nd*^qg0kZYG|0Pe1y(P`+JEqf-AbU*%IhmzwD1CT(iu(iy~y8?7YC|uL9d7h z1&b~y7gmPTW5<;TE2HRp$CZ}uVEXn2CDq-Ajz6wE?T%`mew^AfkN7TYw52*{trdjX=VJnNx{*A!Y-a3JyG6#y8Qcf z&yZF>;>h|#OWU+F!*f0SYrxWstzcnOL^>g2lTSmFU%$q}dyw|!Z?11jtQTLUrv18a zMm9gXqwHgmjyteAOP2GwGk=B(`F0WQF7V=pOXwFovq;a}U}QGj5rY2K&BN))Elop( zHkUuKIrk;loO``&&N>2@hHRb+>bjq}?eCT*fGEdp^T@?Tj$88jf!|(M+4kNuJHKz4 zs}ya>?lOHBln_4M_z5mC7@fafL#;Ub)Ykr?Y5!dJ~A`W&IPl{SM{m{>#=sUU~xe z^&+L>z(uQfJ&yPJw}s`K5B_SVT~;frj|Tac;VFo*MrNd#yS%7W9L=G_H!6>h2GZ^u z%L9*@Nvn_kSL9|qZ;0GPM0AmB@evofxbkO@mk{rMj8}@sy%|ioeye3#KNwQ-knr;GK5u(Tv7d~gAFNkiK3PS#I+f9b8spP=Cdn6$tf6q&py2{RXe2;B870BpIC; z>nD?-CeDmWymEU>kd663$vhoMK2V0A&g!*f3H(gf(nk>bYV)%^fmrtPm?A{WE?LV? z^3^L_4s1c|t5bOVmVVygj@-?9=H@NRmD9s~IZ22hxquQ~wY&a7}CoEQWoC&2f6y@tP+oYsPVr}s6nDmA0%wQ$TYhShX^KXe~Mk%%; zOK(IV@t!S}Z@%i=vcrqSv}Q;CNRw$|ig`;IXeQW-%$E^?!MiC;$v!(Fs54S~__*M| zTc0Kzf)Obh^Bl_Vv$3?FLs8FmmEzW-pDuIGdl-ZrDjo6LieH`WP%z)0|I#Y*_^d0RpNBYC042Y7F}+2gW|U!vrHolPIE z!W6ucma~e$LgjDMk`UwPQTWfp5rvTXlwB(Ja@(l7y_(m%LPouT_4VFyD^o9o1xGIi z2dg{s;qV8c>iazEGFuiaZ(InVf0igaFQm}QlJYwj-XP7t0)w)gC$(%>M*27$*jyPg z0Fako|585vCYo+=DCfV4qH`R|!*9ar0EZHKDT>B9lpdGj20aEFbJI@Pebj`lc;e(9mlQ2> zA`zIL=CSjXzrPP_oTVqFF7uR_A0nn5 zdQQ}4t^`CII|YzF*0Gk?0H16F{#qn)j_K`>wiY3`IEuG5Pp1!^&FS$$V>Um9XY+fJ z+I5&%PBQE{W$O<7b!<2#7X7y}}l=W3Sx~uyj&Fs$;uk5e8ZQLU}GuF=K z%cspweuPs$>uEBrab_$OIB)ek{_TW$=G^iIKgLsm z8+|-bxd-t1K&2}_H2Za+vPN}8em?+)33S$F10FZvkb%I%AuxKNl2|*K&KRUDs|}-r z1}X2=Mh0{kgh}*-Lh*M6sY@gIF-8g;q+F@(+4u~~i7a2`)9bOy;OhZ&#Xx2H^;9}= zpz{9pUbOK*<<|8dgEtgndEHgBJWlO>;4AOjtG4twg>LLDEWcTInb3~?m18$Ygbl-? z(C`4wH1l{pv(N18hX9yg+&3R^On{rhvK5hmVV6aKV8%Uy-3$d76h z_+2&*xt8Qyj5N#BW#%ea&H@woH_!57FZ94=Zi!r*ym&YgP~%NAMJiTLz12^oGWAp_ zQu1OSQEF{J<@keS`XEd3e3(hSdYAWlxPa0X=ad6~&!r*fl=R1~>CLms(~rxVOvR_C z!o7ykO+2gIdmQ1N22|2t;Ctt@O0@P&v!ez{xax+V23pncE9Gr`2zIm6OKZtOI`9nh zCFB20Z%fRb~?^NxV!sCIvZ#vPWoO4_O+Qz zq0z@#j|QX@{q886*MPL50ms|_9C-LWI|VN-XB-HGFTIq zB@ptms;nlwTTr#>sAyD{QTdJSDTikOb!grmjG;W0>o7Bh>-{uj#Tk!WiFv7{;ppBD49 zpuwGwZ0P~K^S)-cXwooXotA{I>{V|P$$7rlo3x|8iR@Qz(v!Z>g(dlr6kip+l`SK| z7ygcZ!brBjheUMgfVB83q1#AEfqWF!asb|W+<{T6NvRy??CYhuJn9;%R~@(oyu{b| zzTxbw57|i_$JlBMNyHNyw$DPkd4Gs21bYa?sAnhU-GpTJSPTU3WHdBZv6X-C&HFG7 zSXd_f+&m8mw5qN_d-caF&I6$Nc*hT6?j|JMt3T2%b8s^I45^lP;kg!lZU8ho9L8@S zj;dckG&PA1uej5MbR<5-efX;8=c&RZ*3p-=Z}j0%F6idbeb^LV5|Q*gk~_HMh$XJy zq*-E63J-)ir86dwS0QrwIyE=y!@l$-&8cS+`^J}K(|6ji@TMflx(+Ke>q~!_0$4G3 zt6A@+q)nS*jx-#rhs;xXO`7BwD9kwiUVL3Ont*pVmu+lHV%tW8TY9S(rfL@EM}~(kIz-TnZ#ZY;N%dPudR>n31)KK(rudPr zbiiPC+>eCO+X?KZAL&C^wq_~KASkXrY;-dcN~iW=3!0G-+OH4W(2PU{wn1NO*Wt6e z`iJoRHLiajcBUE0pg(nIP5ntP`f7JJ!k@&@x!o&D{E3wWJPonO$9dLudkxFZmXb*|cDiN`K2^?*)^-^vyhWCm0Oy&S%X-2%{ZS*!B=Cx0_wr{SXpPk9MtS z-W-hat;7n;k^j!JuB*1CC2#M-Qd^LjfvLderu}Ix!|PJ;;-)<%FC{*?3)>5iUD8Ed z2(cI#RB~elEuOL@XvFJbeXFSinl>wlat@?%({h=;j~|8wjq?vZ2m1)nEqr! z9S^bObuFly1H%2uEGmr5q6NupeHfWX18mHje8g21@ZqTNMS}~c{-WTv; zgm8Jt=Eq>S%W@B)QdJnl{KH8|$Udaziap;GrL*0edD-l=&@CCv7EZ>8^yc}ITwl0y zp3Lu%!S;uf=^=YDH^|2meh-oVno<5g=`1&b%n9lAPyXC&UcNrRMS8{g2=W$<90p$4 z@yO8f4Pxp$lf+rQNhM#>*dp-4U>JKjnon6K`#PHJOgq*RCy2k2a6$U5^93(b9Ahgw zRQb9U3-gf8?Nb81)dxxyQQWh5t7q_99?B&i=&vH!(HK$?m4yoMG5hloT>m_@$z5z^ zf7zQm9bUvPiD#Shy0dPrNNd`^JDUX9@C?TJMLSDLb)V^L*kD)VbY zMp>|<&1oat?v@`5*z;}3d{V&fv>~JEt~55NEr})l*@CvD1znKFHnk-MNY=F_u67$z ziRrJN7&LA#`ub|p8l>xNxz1x+PT60@-47uv&lLq_wWlB3+>T_ofL&4>4&|JacEk9k zm}=ZwMuW3s`!TH@iSY&nJ}E!tG>7zK9pcDAAKw(%met(fIkfBgc=9yKV-w@aIXWez zBBMPSO6lHY_EG|A*O;qW`IyU6lFN=HkaRz!ZDx}+S9^?0P;7LMDN7YEYP=xm1 zwLR8(@3B1!A5-DbQn%|dM7bu*d9|f&?|pOrw5s9x9;Js%mf_^vI8WXOlA2vC zpbLbeWLZUK7g9ky{%%59Hsk@sgS<*Cn4yJJ@`~`I0_JbKvLii7953HT_PGidFJ6Ldjo?hSY|@e6t1sDv zQMRIMMT>0Gnk0hDR`*iyC$H!?)G-j6w2w1gHQA^3k^R-bdIUM`1gCdUZve1q9?$!m>1{~N9* zw`E2`#c#P}Z9{tCuZkrD$kN8tevvgUB=0x6jw=ihycJrptB@pk(Gk}wZazhV8xwEV za0cn&6ZMzybT!6G|BDToK_We@!oCrTrtdp z*MDQ57n3E_)4*5VVq@l#<8;bzEdDtX7w7SB+qUd>_z49cScM<|CN!DIg;LSl#z+ol(wWxwX21Zx9BGCQ0)>Vuttu}(zMz|{P!8$=INBOKb|Ay z!EpCloiEih`H$SakHO@O*Ks|%XC8UMla9a4(if7aXr~`5-dspF5c=jwmhJ!#r;cRv z93+Ac8p&SfFmoi^?Z7(t=qnanLPj?A;o|pzS6p-#;uZObm6njs%^q^0_5U4$R~RJZ z_2NSY2X(p+&v1F(heWS^$fU(2s*@n~xvx(47KEdb&UHpVG}T3;zW#ddJ$)_w=SPUS z9Gd#DV)|lI*?|1cS}zB?2kl{fmy=S9-)>rJOwK(}E zo?xLbl9q|SJbxxe5{QV}c)lRBIUiH}nhx&Pf0_pb-oYFPRUc5*9%s+INSenU;|Pd} zr5q7;H>cviA%J3dZhu1Es25v*oPGKt`7`UiV}e<)0|@s!Hr<)E+y=+S^Rk||?!WR@ z>3PrTc{7i(pB2&pYMuqIBN>fu9TnBp9%ZA~kyTA*m1EXU!xfnzo2Qhs`|C&)%`9hO zFOlMii$`#?FMdGm->+AVA2X{(?<An!9#I*YX2O2-77$fZPi=g z@9`_cRJ9M(x!`2XNtU>tL?lq;;)%z}f$%5gf?mCh!#gN+xGeO+VN{cLfnOKG#Pm7N zmaK;d;@#KSh4o|*ZF7RfGxC7u*RVDlV7B|#uxB@rVChdB%g}L)4xKmfaBJW(_6oA- zm1C@81DQzsA7?E$lAwTV#8hln_Z0G0Lt~MG`Z;`>a<`u@PB!>p$2nWHM%(Tyat z$wgcW@cXDL6tGmX;7w$5i?=uqO04DYk8uKyQTV-q&Dk0*^K;*`S2vOHu+~7SKi?MF zAw0X5K-l=jK|E5*>PG?0Qcb)n?r$OoN#`qi%UwpGTkec1e7#(M3dOUHL*{K<*m0kL z`vY`hF{trFbbb}=&MU;#WZ37R){Z7sWxGOqf6nAr;gUJGpY45>`1iPbh?ls)Pi#Kb z=8sS>Dl*mKlftJ_mu%S#431FN>=Q1F!sGXeZpGxLeap?bibE{$H88$y4V(TNNp1cs zN@b-T;9p~(Q2!L`?GE^$Nzx&<_cang&4<{<*RUBKJIF#d<9Pb%LDqRQq%m+ko3xpP z_B5@>Qkcs<^{^SzMDO5*#;dmBG*ov2+ zfA4;k=L%hpnErJFjy?Fc$r+RUO@%M^3>x3D+k>hVfGzxb`d1$Zr}KQUZh z!GDs-?vo>*%XY;-yB+Lru1S&bFr)NXBr*Nj*6j1RHyN;<<$gpIT2R5h`-r?t-~XH~ z{TS-u;b-iFk4ZoJ!DsB@$3&)KpRwhiz(08Ob9VF-@*z#$&gOqgKBJ4uS;AHYQ;(LBceOJBk=vDogeZGxEbo#qB&V_O{KVoj%iq7q~d$}?`{TLnVtV!dx@s3&E zKvM76iEK#b$IQE&B&S*>uRyk0{pL80-9dD(Kdtcsj- z>{uph%{eM+4M!ax>zE`R6AR4SIwnHL;AX(GNyk`qOjlsm=$OBchKz9e05@W);nW7QgC) zODa&Zh#Jx{F;QT}r-%4@Q~E9jwLndTt5+|Gi~V=-6Em+t({0gTn{0_;r8UG&r%h+g z4&#?w+rG>S55wL1PBe2JhTmxEGR44HaG!(0y;`SF}{MT-_8u@-|ZzCVe_NY!GTbwId)H#ytTZ4sC zmnfU709}^KMQp)2(!O!X)6g)-WV7GX72D5|5rnRGR#?9#{~u!MVkjC%#;3^ z%W@x+0=g@gz5E#LQF1GGJtprr2(p8}&NoGSS8VcKn>Yf;T(h$m%yg8``Z2;}fqTL} zmi=I+Z?-D|hF|sb+tFs4v{w+=8(|^%U3;w6!HtQpo^XvWeKLoA(ts`qn!_8|C>nqx zqyCKDd=A?^j|8w04e1{GVP6*FMW1ct58o#|g2!i7to5QvlujPW4mGCk0T)ML-#%z9 z<0ft0+qenC)A!m^aT|1FGS+6Jwvc3f4=8YET+e`JnTR7trhOvBhYO`Ayz9=4mr#sa%lL~MoW#%~_w_Gy#w_I3Y0{+O?5;OWY&3nO zr~{XDi9R&6S%9d+-fSY=Cz*9ktIdrvD#rWJctVG!u{9RDJ@xfeFrebFwZhY8tvDLB zw+>zGnTI-y(`B=kgvU9EW7zz~20jtN2MxeMV`e5-ENVhOCG?p-6$!rdG@(bcn1>(T zPnQm4HGXtZVB$b@1!eFVFeW3-R^$y2QQRci zU_!=MXhAfWkZ~2cA@nKFs8M{DuQlMjxrLw1fy*oO?B5!9s5pOa*;ih98!~$;j8=!_ zeDyb1#C_#bpz!A3RhW>eC)n1Om~_zn+>*8o=9w$<5>m2KYwQD{Yx9>kHps%lY4hGj zb}G*fMRss~Hoh6q_GP&z-_P>+CVP2yLaN?4|GEH&0R9unvQMI6tggytCE+y2r^}hY zHIM74{uWx_8crGSMV|;7m9@F2;2z}vvL1UOWkGa;y%SplzEsR3zMQl4V7XDWMdRmA{;l=V`#d9xuH!YCqiJ;f zwM^0HCiw0IrSP#?*~btt-fwH^+d|aLYfjYnoehnqts;2iXt~jf(&(L_&|4O$6oV_T zM$=5H9cNdfX+Vo#j{mJqbhX28yVCr$U#v?$#!{?{G$@U7TlDBMB{$W6eH`^UUI|Y(yK{iB_bt)oth+8k5Q*+S0Bx zEuD>OOJgZXeXp!7ZQvaUZ6zL`w{OPQB+yiPx(z#)0Fn6GkNuKBOKDS2_Dmv;pnr8_ zYZ7TTecywfPo$|4>pF?)6c0L`3xjZU7OqS8kRUwq!Y4kRSYk)|79HM^o$p9T)BA}m zIfodv0o#*G zgQ&SRyO>I&k}lu{X^wo1|1N8`>nVIi1)YNlXwz1(rE!*gHV4KssSCzWz5ypopR{5f z(x^l?v|_!|=#TXIwk$54y6K!)wkw^!OOLi@MH#d;eX})NnSnJxTeG-snEu~evAk~d z9=#P^(W^WCk(W~0f~D3L-A2?C8isG2hsf6NUr_N`xdPr*OX2^?=orcH^FSSFn+WzyPui3I7|!LT z6}!-r76vYlfDtUdwQ~=HEr*{*gRP@Pu)f)Jo$uvvF8lZduZGL{FX8OBY}$tUw`3tX z5DC9(;@fEe8`qmo4C=_2 z)&KEZmvm&`^`_@ZV#Q}R>P2W&5W`QkrqQ1R*`r+ga!?0eYAINVeID!4xKj9}1N)>8 z-RhVgf`u0ItO#d{aJC5TBAhG2c_MsXgbPHtNQ5OKTq43!5iS+sauKc&;Yty%65$#V zz9_z9z!gMfj!&-xlG!B79$jTSWMw2tO9#ry|@Y!tEmbT!j3) z0WGFdgkOkow+O33Ah=r0K9M*e!lA(&lPSWEB8(PcK=Ar}8sO*^#Iw7Ku&oGNh_HzW zO(Og~kkkAq!t)}m5n-hWKNR6>B3vWFB_gzoaH0r@i?ELf(?!@`gyGFZ1AZdmDZ*7j zEFquXrfF}msDAWm=@CYu?uUZJJM&wJ!=(7El)c&ynrT=F>(HNev_t`&%fGcfsZO5D z-uRXTvaF)+c?^laknYX1B*63*5n}B@)~g3UYBM4 z1Lj#k&sx%mY2moMqF-Q@?QO??DxfV%J7yk0BT2i8*a37VvE12)= z)1&cgxWB>8lGS8li-%H)EgVWGk|_4$P^^uZivGiBUrNH+%Ohw%fa@dZ;l|?n&;N*e zvm5($7;Rp0VI++u%2j&RF_C{8$o~xJYrwe%3^U+h1NJarM+5$f?-if?`=*Vat}$SJ z-adnFn*ra|p~JMvK&&v}^9G!3z*7dSuc%i$y@A07e9C}!1C|)@MFYNTz|ReM#DL!j z=rEbC8;HjS^o!FQXl1}u1LhfUf&u3maFqeK81OZG+|K`28nCNT?_?ugXuv!U@gp^+ z&IY2D0sRa}4R{0JjPt*540ynR+YI=+0oNF?gl!#7109)0vXudS40s3Mhl}bAxXpl@ z47l8Yt&Dz$7|_RnkJ{?xuN&~J0c#BSyxwhx>2m||z5zEFu*86~4fvD+`x&sC0b>p5 zZ$M82&NDhV(SSn@*v)_+iw*LfralH@qyeWIaIOJM4fvt~Uoqe|1MW89Sp)LV==t9*13offfWc{z ziC}#P{8u4Q{;9^|_||~i;`NFOjMd%4fN=&4Hz5CZUHlpFcca{o20UZHDg&M{>VGpH zzv00D))*N}4EU@8Cm67w0aFdw)_{=)3@~5=1Ku$j5S1f1V93@s170woiH#UfTZUeE z+r+OUOyM3xz%SP8FyURmwGqvv*a9jt&W_gM7w@2evX`_BsQ(YKD2k87ztTt4RC>MQ z!Famf(|bbkl<&&~n9;$=AXpK3JTL)2rMz$%Z>&!^ZL=?!IG(d!-8U(f&IjM0Jr z!Iy5=@!|i2e{@2}`~DBU#W5Z4#r7Vhfvo8zIe^hkvW!mS&Ts zQ=G{{+L{{7#IL=tHVGaEljwXr3nVMAj%=8|IpJYQv2G-$Z1-PP)hKUZ9VB&*=si`4pCmP_> z8ywxxb+C;^MLy(@LI*uWq^Ou=Xq?zxIYm7jCBs6)qQoN^85SBQDjMQ_zgyXQ z`}5xWXFkuz_g%AR@4aTtn%R5S40}|sw3}}p-&OgruICK2KWBqf+Ws1Y?PdEy*vnq_ za;ZFSZ`Z_I?aBm0nWJ0urT+F-anyPuwGO7%fnBw>zd$B$Y<3vN5c7D8K2qv`twkRL z%l*#r6qzBC{<4~<4x~h^r4RFAq7g!AzR|jnZM|MVO{e-@g{>) z+mzz(I(arN++A1Q=jhS>e7Y&0M9)f(w1M&N0ZMhvAHH3wejK*f#GCtn(HHR~vrnu3 zlH+|$4tU-oP4VW=R(-N#dIbmlwWK57yxht$7WLkIL7y{1URU0E%0Z=)hK5dz9FLhRF6x6^(vBH*DkzlTC(*ymwYB>2jcK1YD`RCL8U;;Tp zmQv#*r_D2M`dY_pjQ^1(`HA7Y1oQbzde{S9t>=D0DSuLMYboum9Y|}-b`f^Fm1i)| zEK*M0F6kbQ*G*&Ev!ph`^t-H|mR9U}MfY|*KIghpd&t=Y^ZqOP2zh+L6@8rJ@qW6d zJ|lXf8P&%aW`1x*ALe-QRCi5XB7+mnpRVYBj#cC8tf}!NC{dRlq9XCXkeEa>uw5VF zn2()@av@LLnr|rXM_x@dpKjNeOJ&Y5zrE5|Z|+;hT-BojrE%o5q}D@Izmc7{o}V;G zQzJb!b?{dCZ62FM`Anku_Emk1<9$bXY3g30l5W;73|~rolVrsXH}`bvJ?|ahqy_}_ zPy+({rgg2*gPd(?BNJ~u&thXX-qPQUe_mkboG)FhInm6 zdRk1Vx$?Tc(%yL2^>LnE^Rz~3>cDJG-JN`Mq0Q*ax|cbrQ};9%cIty1uk|In-G?9H ze1d&GbKMPnu(_jCAML2@dOkmnlLfb4XdeD6S(|L0@4TaHg+HpPD6&1-^uBR(ZLYnc zFR)ka#vNVk$<><5B?po>`59xn%RbI`uvyT{7~xoM|CeZFax8h%TI2SHx0Z8RHcrx= z*5kjQ?;P`7 zxHaE}EoOmk+}>@i$=6gZnUZ3j*Ns3&oemXH{Wn?tDQ2Ky++L-&w`lHdYy&CgGQ$Xz z)+sOy569}T?VuZwSE%!$9p+)oSy-fMC5ItfE5N z$o|hDQF`U;bU(mzoetVngaZXcH z$>kJtrL(bL-ptR%SR{PO#Te>n$W#4wHFAKi1}~P4b&z?)#Te{(aId?rz9WH)&1){k zxPe`3{=A2-u91fp)A`MhU5!CKy$0O;X2I((>UqcOck*Ez;H|3!UjNQLX*|>ZOrBkA zzTeZB=XlF6@8Tu>_>+Of=Fnco?LF%~qjfc#oLX$o>}5Rdn7^6dd6J(V4qRd$>t#&0 zH+rbKrZ0^?-qlzl%e&Rpu(y z=$=VFT5@y$A$N*gNcoo~=5x$vEgg01xPXpvnW2X0G+nvfb$ePRU(-vOR_lLzn%q+; zEnQdDcRaU=X{pQ?f5)_J`3ldp$~&IBlWFlhxA^9;hAVH&co7pLp3*lC_xfWP<&R;M zTZh1Z45R!pjPl1Y%KzIiO8Hv@jRD#~FJsnI40wb+lb#auxeX8E#UwKWWH@%h0!{u~P>=5dT3oA$! zUITj!<3JR5hP!+@Fvg2v{74RlapADLxc7Odu6&{9$7l@h1PA#m6^DDm3na6XTR*fR zTRAB==^mv@@MQQksl@ByihFruJRO#i7F;;`K8`bSe^}0uz3UBrjvGEWo?~G= z1kMWNHm-Oyd@P7VK0FTY53(v{p-@I)=)W^2gonejiPTP}!!IT&RfN~V1)LSH!sFoI zLzSw-ZSWG|9s;TzW=~P79T%P>&KwQ4!ptzaD?E3fhttD3aK zwoCBzHT0Y3S@*i3a$uht(KNGcDs!_k}M z+Z;;?SC9-m9Udh)cpY55S*ct+4|;D=Dj)ZOk;H}zmy=374Q?mZcoF=R)Z#VJ#k!Rx zqu>hTNFy%n|2Nhd?hfC7gQdi6@F(JK7^(#>FQDJxY4DwG^bfog-c`g>Wc`$!gE1}8CEmxG7G_emjcgLC%MpYRxXi8SNR@6w@0ze}yV(OfXGlv?8!Sh|mn zgWKTVzq4-f(Dzy2eTBcZFj}4L%l5Cv|uPTud6Q z6jG5l4$|y+3G91_odb8?a)Y}`0A2z=CBb+NY$c(%uy-|^rOXclNi-e==aN`F7G{um zT)2g#;`z|}G5@7esDfw7DqL>M()&~PShyP;PqOhKxRq?c^Wm_=Y{$4Se1R0=a*eb8 znNr2L6I?<{@l<$**zjifa1C2Wch34mB8N#e57fZGBXmMM2$qw2+y)1K&ennZz^$Yi z&xbciE3Up!>N(PmXTmc?F}T_c|6I#%7`MP<#0{^5^N+IrJt@Qfd9$G{8{i#tYiaT)cs9H-TBixpW;;wd=B zd2t!15ayW```2f>w`ibJW71bz%Q6a1Fo|V z@5L?_){+2R7~e^g;=->PcZ|d9p}(f7Rk)B7l`0z-Mi3hw3y(26S&6$d0zZS);t}vo zQjhc7LTX?S&8nIycydYDM&j(s9u1ld$NS=ia56*g@pvRGCAqk8U@uJ-;hyjvQX$jf z=HC2n4W0+<`)aBQ7oO_Rh+1#@7BmL35V+GoD?f3-ID7|bq#DbT?@jm>m5FP|44bfBv9ttfa$4WuOMsjiC zS0o>Agx!2uN<0YuC$Zs~@Cd2Io1n*VmKgVh#iR}w`j6mBB3!tAw5HneN;uVDQ*M1( zU}&x4KVJ&M^s$->z=c1M2)r4(-K(i++!yAMSX|h597}<_!A&F!&x5}Au>g1*l%H!T z6Nj4u8TFNU;D~>tMfiwEZ z#)T~;9T&dFnT#x%2i8yIjd5Y^11wNKIyhWAiS-{sA!m}No(bg~5S{^lBsq99{9uZv z^6(0HffVCy@WC)TBpw1^B{qBm>@$_sfd@gaD3%iUfpdsce?AxBsoC@)ywQpnoYM%W z-~>mLNZcPjOk(gz_#}zL)8OkQ8PA2~#Dd#kD(6KqaN&2P2ycS-%wZMaW1;V(R1Oy= z5v%(EUKF_=L+{1aTs9#Ri5J1cBo42EzmjCU4fdGF8{^I}lBDCpnk6*5%nuV&X)Qb% zt|c}+2ks}8cv&jj{~-$16sn=?Qq~VH?2$p!;m)v%IJwhFVZ&Wl#*5)OQjE942`{tC@QiFuOAwno`~MQ8o>cOHu*WM@2zQ1-q!v$ubJj8+ z9s_rhX1o}ty{f5JnFk(UN9P*In?d*Wv?v|}>qscx2q$h}%fj>F%hrwbWD42v1Coqa zzzf8Jx51UK(Y$y%TuU`OinCJP^4N@UXE==bc+gp3EE$ChH;@2ac#wqP(OcMslW05_mXkEx29J{s zcs=YOdAQJXE9>7zA$BXLR`Xc}xIdgm>hM%}gEZk0Z_p8mEB7rBrV)2s7*a^(aN#@K zs4Q-SC%5zAgg3%%oTkdh%ivj3g}3Zr`@eT5Z$N=dY*Ykk!G#}_HoOX6+@OBTp#A}GhC9Iri3JaVe zcr~U9?E5L}6L*8x4$~^!{G$VI{)~Qu z*9mJl+lLGNKc~`oGQ3Knz1jbB!H_y|jOOBjO4v@)ap9UTsVSZXJ?p3_E_|2d;-&By zDaGqx{&BWx+~+GzXMO*Y{5(6@=+jtdWw61*CQ{J^G&hggw+kSYq5 z@WnIiaPVyS;g9SAWjcJQnU=vV@ULgt3b>6)7Q9G&@iutoCn}CNL-S`A0MCPgE$kWb zAh?~x;YINAbF6<0g?MBQ*??!k9V8DghW!1UD#UBy1yYQ+!NKQgc03MRT4{D%m`SQ} z;cuiC?|{)4SQuPrl2$zL0^9#t3hfkH;Idy?GH&Byfy=M6WOy2^>102T3kTfb#c+3+ zLSpe$c!pT;HW!^@OXHM)Zy*$zJdIflH9o|0C|CUa>Jc0_$Fs!eepur zcaW}va5uQdlhIH-2NrS~)*|!3NKU(E;lk%g8J-Cr;0`c#cnGW^4R}3_7^15tT$pC% zzg7xqa5?8{+r{BR&e?hnqt{GvaT+1t@A#cE;)=J!4RdtmgA2cVl*-{v z@G7z39q_dnM#%A8_|Lgi4zGrr=TQkf5AvJLstOlcN6)8sQt*eXV`*MIW`V9Y5$BOS z0rDsE$_;mdTz9HQ;lgJZvA*#Pxb$(B9#4l(@hrW}4?Rd39sr*s{tUZi!Y#zrVE->7 zSL+2ChMhHdGA?W)`M9uyuz5xr*k~CbXr_hJ^E-CaNO}Jd$G=)U)l@40DLI%5D;p?OX z7oH++>=}iDOE{T?3m+mjT$n)0*mQ-fNHANFa6QSwg*!Wjz_2cVg^}f0>`L*Wrt3SK0Rn+ZapTRUY*P>+Ftl U=2140JLbA~)O+#J&@vmQoE8rA25Gbv>@)QG=?s zx^JqD_L8=QkOZ+WwY8=7X>WuURXfk`J@Y(KpU?OIdi}j#nKN_d%$e=XnKNhRxfY)_ zEk0#h)tws1>Qu|*Th&!BZd%*$_exN+wR@00o8rCpl7LOu9tOPp$<4L<1Z=amTEIWn z9ssO*@vXIafM-(z*JcaoD)P^!m;@X&EpIC4y*k*tT~4OKFlnjLuywIhO~~8CKOBnZr=a| zX{gd0Fhm(geVc#mhbU?>v;j;~meRncsN3L))UdLxv?p29Xh_?6^9=^8eAZf8Q83pk zA3-gu6hDy~WM}a`wXjp%2_RLeo$^Yfc=~vP@>QdCp8gXkirlb*z+CzkxT#W0WwbGz zv{#lGQ^^NPg>m$Nk-5~6ZO;x%%eGs>(yaC&2so!K;1S6zyC)8E-0Tio=*Rmt9Ui(N(T*5 za+^tMW;=H8h;U;}DTKK(Ih_F)jHQZ%UR{ZMQa#t_spu-P&iq)ItxMqGte zUZM3muGYD7uCk(e5M2?eY;B(8Zx~I%aDE$<`bIR}R0U=hxk8op=O{*V6n%cY5@YT_ z^Kz8&X5T(9zJ)H^O_95)A?}1luC&@jHP%srjXT(!M&)&bRaLdh**#m{R5u*0L^F zbZj}+t3}4hU+%Bed4!RZO7j*$fnP&apiOV0E2`9QJRjT@VcW2>t?2IIIZD44kv_x6 z^7>J-XMa9|g^l7W>N}{?g5k=_7Oji+4Hq2zK_nkj(FO1*-e;}Eke&-cTn+CDlqOUj zT&M(h**GQ~nVL|&!|RjJW!sI_ytG|%|%K$oKkTSDnFwGjItZo@ZUmu~A zwd~a5&R|fX=b|O(d4%$J%l1W|@UrqbRocO`?a~sFK7@s&N*`#bVHzr%Yk@TyDz1SF zs3HyJ(LjZQGc?qHFyd%G6bcuEsj4(Y!~dY+v*k-_YbjmBt!cnHRjK1(WqYf>-J|$~ z{fL%WlPe2G^C1&7HR>6}Z$qN2@=+`=I>yDoQ5~A{sgVBx>b0>-vS)&iH*&IV;aPow zw;7j@=Axc$>yX}8v3d3@n#jrH_FHY4IauXjIScHwZT-A0woELKn^s#tS5>+)Pz*~Z z^0MS3AoftDV;as!$MK>1K||d^t3Z~`Rp_T0D!Bo5NtKv}>c&w;;KoO^M8iFXt`H@` zY!*k!{an$eGeC=hAIovY3q1_xk|?g9N>y`75F-6bubWG}MC6LOgl~w}(mUo7qrf2Y zi0c=wX#Pf|A1`%PU@jDyOU@&YHLr#@K#BcDOD9AbZRTQ;Lk33g2CoBVsb7@Uz0ct7r86bl0S z$%ixLsw^2|S>vo19X!e>y9D(0l5-)(2SYY;olrR#uzrj9K&=&Z?&J+prHP^chKMNM$NlXhEIzLwq_zd@6=Fiua*h!BGF*z4^BJP0m z8>O3Xhaxv{5)*@Q=h8p8FEylp&u1oG#&!A+a!GxAZiA?SNlgUTZUG_tk4bH_kdGn7tZ_F|n>apLM1`nkM=ko#! z#^0a>lmQebBI)uzuC~QkLT#^B?)k;g#ePb=HYr83vUtO|sR#9+lPy;|2Z%ibW}qr% zBeTBIGD}#F$W^6EbS_)2h9Fs^&jIHS#dp$~B1F{m?O?H%f8;w2tcRTRo#+T-%C^dU0q%0z3n z@(!bUL0EpNPgJ#TlRz_!6BN;vp<*);rc+J&&KQw*5qb6V!8A^58@G<6k3g6$mx%hD zFVEwZYL%CFL?P!RJ|up*+}i1UqLi*zADb z6}*D+Xem!OBRgo7Yu~acmPJHp|H0qZ4eV{JdviX3JjO(GkIMnwcT+kXjCb; zk8&_Dlz!)>{1w=rUKptK2ck z+A5MCi^XGgIpiGhJ-5&gXJMbsvc-p~Qc^Ffja+?eXe}gA(bgQSni}UJC|_Ot_}&Kb z>o5s$$-Z19)5oQcO&>FU+}P1desBxZYka)05MrQ@t;)(^pP{pWfi-ss*4os{w82A% zapUZ)m?GnUq^EkSLSX8OAD9c=RLLEM>LPe;n4^5CO{8}si8e!HJ?X7H3J#&MIZ9AS zVw_h7A8YL7$5g2m;^i705SX)OJX6;pDIX;A^<~5v_1v_Keoh_JH=W=nX#xBv`dp*4CRj-kX8-(RUaS?TpRGc zIS$DBw&_dedvpW;LVs1VfnEB}FdPpsauvMC`5;!Mmx0%2Fh=jBRY{L%OVe5?&qoCK z-9v-YclsFkc%#%|anOpp0}Cuwc`u@%UAk!SiZBC)0ont?(Qwqd3w_9s7<^7tsdG;y zy={p3Cm3K@C`CIxK<_-M%x>E`bD@Td&jvAP!!`C|Kw(?tbB^+%@{JlIJFmiO`^*&y zRT|1Akst0||wm8Cd?V;cJ^sgW^si@TB^=_5Hn z5qCkpmt0xs#tWt4IM!X+92rRax+}XQLju~m<5=4n6Ox(Le7xERGM3*Cz!P$cFcv9Jju&D@o=s6h9f?Ha_RM{{L*)WAk7tS$!S za)xi0T|KDbhbLlkkI1L72$9aVC$L_qp?WW-Z!zYspIqh4f>kJ=7K`)M9*Rf1C_27} zlH4w$s4Ge=HwL3XT2ggEJX$P?hHGbU6jh~bshp|ig3!eaYkadP=*+~KFZr)R3ojjl z(yxiV@R`zEQQB%>D~|H@=#-QI3XD$xK>E&(5F^YB`2EG{c!U>7|ADVCP4r;h>e9C< zRcRjWRWwJV#0i9NTw_$}c_1?F{hs`nsE!2^)%=MyUH+FgLaKCDL-K`Qexm_d3FLBV zKXA*WkIF#-ROt)^G~1ps z9*nH=GA?}FFt?dHHdQW02hx;O<#Du^TM4Mt3T{0|D1q&Ribjf3rcp2>(df1S7^xM# zxy5zZIdwK{xqGxQ1c&Ty$igAps8Ngph1G8QESB%5*CCsnJZb{B-1IoREk-_ z`a?!sMIHB5sgfB93}6MQpz=YIC7)8Ikpi0#(n$=fECww*EFY7`t<%vf&?vh!qXF$a zF9T78J$oun=!^K6y)}xH0O9QYG?s!Gs%qR&EE`&qpKzU#%XueFnJ6gsKIfZL1R@>? z6nNZGv|<-FH0Kg6$5)U_3Q#aGozsDp!=x9|`Ba;d0BqcPgvR5V4ow{mLcXfsQ-*cw zQS@el7^q-uV2PslSNUFqB`2u)C}D34NaE9(9X0~T8_@3pBST_ASdQ2)C-mekd&+(d z%J-A^IX|5&{5qe(yo1Qid()i1D84ZPG$>O^it#VH94}fF zfPGGD(OK*;x*ec3XFTv2qaEm*728(+)7rr-5WIoPz ziEn9qZt|VwHX!n(&qhast85BGgE1-DauxT$4#KI1pKiLmC`$c-olKO{gdp2ifO_G6 z4RZd6bI}c?1!4IX`3P)8-bUC!?`9~SI|mj05!cW^|88Q$e-_+?oul>dIp78Rrya9P z8#oc{rAV~x1GMdF|L(zfBRZ+Asx%=LFvOfM4CkHWF|VgF6Jij%Bl$`zQK#Si+MOD-e6 zZKa*Hg9LdGmV-xO$DX zTw3f>IH;U4c?Lr{#c7oRB_dEzs#ZF%SAytM9YkKx>*@%pfNI_Z&?g`5YHz$f?Hiwx!!)8>y`QTR+TvCt00*ZtT)k1PU=3^)uw@_qi61@tT#VPs8 zyo=oHJqYuZhZI;oa>rK5Wjsxl662MGgwRn}I`QRLSkCoZj4FMBn{RyKZ0o;G+JFgQrX zKoHT?oBXoGE=9oRj5`2+kr*~1#X;cO9?GNmUQkgdP}IQa;FZP*=THc8Q2H~0#Ddi9 zMv2Q}KC<{qS$H_G~$T&;K^Y%aG&|l;na?bp3w3m(^uA_Mcs4qN7$EN|$9kw-74TkUVC;FC; zJvQ!ybFDI~YY_E|SCpq0or^A1Sqoe$P*9-=OvxPN|qFD@0Kw0r68?9FpRf?HaKT$rCx^R{7k9OfE8s_=~6V09(EQeDT#3{?!EWl z4IH8@N%SE}%A1L8B>zm&KywL~Z>^C@4@Dz~f(KfTy|-9Mg)ts>*)!E02KK)ic@3uv z6liEQHd*ZnAGJ^dlapv!iZVJmu>a2&R24mzoSi`q`5h!Q3Y0W~ zIB+FB0yh~2!Hats_^j1Jx-i$$Gjsi;Dz%1Oa&zM9K&2+RmwRJxB!$~z#Y4rnd;6m1 z+9X2@^X0o3JcLTB`<4r)Jlhq<#2*-9VMhP`VL?+ggP%j{;cK&cAi+;T&ie~F@eQs~ zIl-%(;M1MZWVo&v$g}Q=t%v8g;{8BAoZ16^QB}`6Tp5bx2M26bIrj!ThbZT|M|XGx zeSprl#-&F00ao>>ddR%X?EGC|3;(j_VYslq(w5C=l;iBCbWUkY49duq5b3Me!H?he zjfIcEf*UPdcf6sjO7ZRfDa>UIP+u{M(j##?2tylr$D3pNi|;iymtvoTJYBa|8{6Kc za1h{RYWE+}N%=h`Gj0}&aJ6C2)y_Fxt#(+Z7@=HPQFg1x-yas5P;l!VJoZ4rdXw|6 zGA=cwsk>gTn_^3iYP=UlzO$*aBQ+}N6&^jWDI>o2;2jrxJ^UjWrc9pu{nOk+KWE&1 z?65dUj*e9PdiXcK4O`h+tfck`Z0z9CaY|l~$fCAlnS`dR`|3O6)yiYO5LwY-+|mJ5BGq#F#DpaX2LNEbCx%w^m+lGte3H2YEK-NfmOW;s2*YuosT4j z)0bPwaGk;DoyG^#h|#Q$Qp{-*$yLJBdU&Q`92<^RTo22}$tGo9nm>IpMqz3G5s|zB zram#)ih^-6Ew6__U`1Q(J-y&{7r7l8nUwQsenmxSx!8tO9QU8+S!iesuCyR1RISdI zGjaIqn};U$drB$w6m;08gbX{&u&7YqK7Pi6)fQwB6=vrh(JJf?YR(5F?}9|XnX3}b zPzut0=-mwE_4Kr&f5f`N)o(#KZ-x{u6p!YCo}i&N3KF2;`0THtSOdzbO34~(6CXeM z5WmlrA~n?f1{4ZPK*b%fCYm<6xo9>=CNf#?vIr!79P(!y z&Gc$}7elr263l1b8+ewH2La=TC-)YL4f{!$m<2c|!j!Kptz1Qw;iK9swU&sYa?~~% zHafR84`#`aZf%WI;Z^RrY(el7t`9cCqUKk4Yn-Z@Tx~9yjCy3L02Runo(P@)0RaTc z*U!bUOY?g=t;t6VLesh>76hQrO<|77XT&~qMl{6qmoJK~Ii;$JDUXWUx!{=55==o_ z4bxjE{T!q?cQ0wpkB`R5s3_a+Kj25+mQ&dHvc)NJKqYTeA-^V@grd$@syWY31k2qR z4#ZKufE%u1|FRyawZd2MEHeHLLt9@7-FS}O6pUGNwy)*gLRYE_TJa0l8t2M~9D@VF z>OczC8K|aUEFiC9D5_WvMtbZ23gshi+5r+|tp2x#Gd#m?+6@!jdGHYUDKGZ&@oW!< znyug9(Y>0gZ0Z$B(vV1{L$FAhI2cZ`w})Z4pL z8;*zaNNXS!E?{KWUq+2U=F9*ej+_dYpT9DtceH7*Kgs|D-E90;dAoNIeZybb)4Mg5 z0+e&TN78owO1w3sXqOwv#8R;Qg_w3w5RI0N2l+!OdxuIfi2e(>&H%$macG0kPr)dN zE9`7}Kcqo()NMw!yZBzpwJhp3z0ivLIjWq8F|}4%nhTy919)E|;SZ90z~)&FV|l~% z!n3@1;cDktP06b7PnwruhMrG3#?Xys-Q@Z%zP$v8c|Q*HXx`4fQWVd!Cxk#)v8QLmDbao6n*Po&y6h=xtTLrfWYKn{3fVHq$I$Dj&l0=EglAi`Q) zK?;J<%E4$r`i{n2$icrwF&B2JC6K%tM1SclEXFHG^_>HoST~$;x!cXvA|J?>&s)r8 zW3!)kp~pKa2L=StuR5014TyH3CDCQA2j{zb_JLhj?}3FI#UuRA!jMt6ada@HDe1}& zW4&B)$%THV=dy~8#7p^m>}0ZA88t2p?=p+VwI;=7>&DF{tzAJ>cN%-f-Tj735Cmvd z<0)%dKyEN4=gQJ1cBgc$MOiedEse7%A5BW8KW8XcCRNjadMaN$mqkL#?mm}EXg0RY zDS7m0TG@M3+7P-kRoOH38+tyqY~Hm05PGt^GJg6h8r)sEIejp_maO!ev53YemsQQ! zPUx_hvIR3s2;I?9q4_d(>!`e*-;FlyT6Q3R2%&E#D1oyF(M}0va{vMdyK%SZ==K)d z=$LvhIitNb8lIObs|!xjr=1C9R|>|{mZza6(|1_I`N-p4&JpK6W#~)GNuqM;r9O1! z?y~mtR+5m8;y{=(1t1-7LFzl~px9b*f%6757%mZxbSk?z{}W1DDN7dw=eGEZ7%F$b zwZz>J*1Y2wr2gFQ+4HN5HF~dA7T$?;^OwiugLO;bg2&U9gLO_={%ZJ2CqcMagy*mF zc&!L`iEx_;wWetjts?P`2-k?PM1*riI8}tBM3^nYR1wCAu;(9~ zL%awhMCdKTW+Hs_JJ0=7gh2vRF5(MBxafD%1KEp3VxtIGG-LpuFXDYfxJgXMQc>kR z5x$DBwzllXqO!)+=j*Z$N`G;oi+@&@+Delye+MZ^K3>pTUR(p|DK(BLr-njT=lpy~ zrw#A3j6HUOA~D0Y01t9AJmh79%+$HOEOYra>h@VR$Z-HQ?cJetwHHfQ;57@n@8f)dT2nr_1K7wiEJ;LX_4By_Na&vr~$q#M0`M zO0}|(-Z-iBTAS$p#YqAuRH39wE+-Xv?NEPiQKTPKtyZ%py&E+`7bQ<`VW`=2Ny>s}xc7jPW@HPAZ!#E2{x&l(Kr z*sn2!Ef5)kh*ic()p+$hOUob~%l5Js#C(4qCm_VQ(&8#^=uX z4H)u0C(NGX$}JoJ?|EjPmbqTfY_h`_?X91O^9~LGU}&{@5^M3^7;Et!S8H)Sf%`!= zJ`d{p|M1f+JPjbqv|C-WaD9Sc=vRw_5Pr4tXAUE8>|H4Ry+Z2D@VY23GXfB%4Hn=?D~{kF{Q z>)nK&2vlmza_Nk~vX0y5HZ^x;{Fst=uCOIK=eJBL`{A5_nR>AUBg*|Fvsx;KlJxV@*%Cs#j%bG)i} zRh~1~uX?8J(Itvn`IA|F{TbYEip4?AP#wBn@i~@6KbfHnJr?4h;=+x&zZf!MJ@`OM{D6U7p-Ly#DQk~~ z(@Lg%cdUXQU!W{J-it0-pzJ>$JY)-6FP|&$hA31b?i}`=@ISe7EB9-TC@tq}N&1l2 zYB#+E;QSL$G9c8WknV(B=Xt#RfxD=$AW6wy&t|S5-pz|srf}v3fgl^VMaezkMYbsO zPNetRf_@S0m2tIkB~L*7Ez81-5iPx7E0QKc9Rc)vpp_n(E1b&)sZPXP6p6dUsleZ^rx?_Rqmer zS{gi6tPTEgh_*0_QylY^U8mZ$j1R(dn?!4#X$m5c_^=@5!KuE1y%l0uwWHrCgJE)_ z@n9fmCR+21j}d`^Tpp-QIz7>Q7*e}=zu<#hmnofDsH(%m2s;nkZx&9aDDM>%}qMKe)Uj4CM(bRcp zGwuVRNU%mL;4?S=18S_A{yfKK+&!14O2Srg0-Lb{nbpR1KsIe;sHirsLX5W#1@@2U z1#sX)e2*T_$NQ001jiNw%85r&&SnQR`+5=Z)}D6L+Yhl-O4E=kPB{;5hQekV;jick zUUJys9Orcj0ZihBmh9uL73QmH$t%hWxO8r{N+~AADtibsLj~nWo6Gf98+Ib z&f(_${QNS{^Y0SBzcE1R#Ya_(EoICEoak&RnE;TN=J%9i7sBX{dCHv&p;Vct_+1R5 zv*sx&7ei_GJZ0j=_JadZHY@o^fx9#2E4=iwC78rKn1+d&ibUJ+9>ZMH9z$DgtOHU= zfT=5D(+_mF;lmA#z+_8e!gOG;-%GPzQZ8TgY&jZ9j^4o0VY2bvY3pVZ{2qBXGv&2q`-b9aQ%m?l?s%3(UP>jcziq|y_8_nL zY2KDr%7V+i+V&p}@1`-EhyJMu>u2#%&#~Afi^%DT(0RWxYq5cNt@*WaN&wO_yaUrJ zoHz-()a)>fi7?tsO@N-?%%w&(-5&)e?#VR^nn%~D#(Jb|raB(_Ul^ri{5ose2qX&K zlM`|Q%_Z+*U=sZ=&Ej$H;W0Z*hj_watP()_4gfEHO`icmICF3mw@|EMit#A6#2<|E{ZX2P@ zzA`JLGg9WaD&@-h&ytPtJh7>={wBX$#ocUi%2zM))f2Uo9|N7=WZ-MS;*_@p&dvEV zfBtV=^zY)GEW)$M+Z7FudL*cL4=MPqMUW6(tsg~cU>wyGgui>H;gtPqHL=x z3L%4Gmq2Ab9;CtRIxOUPI1CmJRt8=jLSG-Ed~`K{&KaUqUkzzJUV`srY0MST*TJy@x!??u}URy?j<@!2sJtLv^a z-R0Px2Ojd?J?v1O*~KM^`8N4fUr$bgruSaF(%DBsAJ?M*qW5jWU!ROyxZ zWqE%cBDB|FCFW-5BsVm?z+x@EP|)T^v5Q)WixFdWKd#*10=FjDz{as`g65Ttmj`l1 z_Sy~%BT^>^DsSHG>wS762DkX(lfv+7V-@0jLE<6dT;<8lfPhU;5xel_q5f3y!$*al zR>*l!wQ=!8CFxco&B{~?Z?&U8EmR)eIzj);RgV1~ zMPC_E_VDkcM4F8=b5?R~L38JA22Vf}yqg-IrTp?w8#)hPSp4JDwR{|wRE8^TL=kbf z8MpDsj}R*Ebok@R6Seq(aiNbj@1WUX>FY5$-Bx1#c)Zf@&WP^S+0<~O*q|1+z^+%0 z1h2{HbVp5AoBt;w*M;mfafvz^1KTp1zY~i<8^&6d%XfNpT*yVvb0;=O)p-+Pwedxu z_~5|4jIS;~$FovBM-rT)SC=Wk%HReL%G+ZcYwi({{8Xdm=6y^+m0W+abb z5Qg)}5?8dqX6%7ntGr|c642tE(?u#$OH~dKsWdH>f|R_(U6lH2fO7p|0`<*M!XBm3 zNNd^TM++$Z^o(-l$y}OrMj4~FrKU4Vsao26`6<5f!U2iiEj|5)Hwktd4OHA6jQgAg3M99w^n=Q*Z1e_0-gbeMQJZI{PH=XPS`T;1Ef2eAkrBA|W}6+`K>R?8abCn6^N!N-NTTK%Wii=FG!3)2b}i z-;H!Hy4nqzr4-kI7`$~A9N10$lc2}n{gbZ`sVCw%Rk)|`@YXV;z{)wM0Ns=Q!;zUT zXJAUAfkg~+5-wuJ+N8LHAWJURC*?_uNa>TZss~cuaa#Wz9__gTC2~4K|TP55p*e5qlZq!rzF$JeqAXk>IWakQQ(6hmD39 z$cJDpx5fjA+c0WXX%NR1_jOfmF7-{Emx}Nq!3Dm^&yQrcO=Ks1`!M^mIq6Kt#IRqR zlT^3bE>KUtkfXc=7HuYJJ>La_H!=(=tI&+kEJpG+$iTuf;pgU6K%iFVD*monKVI=M z0GjuA$uRbXnFP5`LE2_a?7~hX73d|(AHzFApwZv}e(!L|`8{M)m3&7!JUvJ(aeo6& zK7%+T9A%S_YLQgZd{nbY%NG|+!`czaLEyCT)PXyLr89BzQ!qw!@F`8B9|{z zxDDA)Er=fti)Rm75DVSkj-|CE-sbH+F~vW?Rf;Xb=yiZiX-T4@-rz{x5nE+k&N=aq z`-B%${s#OA^`60&-_B4^)Sjw#E0l&+UcGP8`{pBkfW^rtm8duVHSj zFyuisEVUIG;lH^W-}LS92;#$d)Oit-UZ1Idi4{GFm9-+>=(M5idMgq@Tf{PxC+R~! zk7Oe~At;|@u!Ww)pDxc}n>>jxoteRQc#=@BEVQ+5Ej|zX_hc-a;xpJSPm)57iLA3k zdeI-cvH21Sr|Y^o-jRrzv@Y$+<-FFsc8_k^S(8)dcV+uqlY+>Hn6RwmN9IzTztK+P z1*FrHFBbI8O5Rt{%QtJF=NR#M|%Z z0o=+?-cz^$_ZR(my!hU{!WV3&>mAs;KI8?ur~~uxB^~JN{aFuRj9Ny2Ci{{k+N3|L z_9cC3c|YdqhXDxf&yxKJql04EDL*VXQ!MjoLxO01XGd}y42;KlXopPsak_bJwKX~6 zR16#CPx8G+ah;L;kGT{NI58I23lg&vI}D9sm;Fhfxci-W=Kg}JY+EGI50EVM(Yk+z zOQg;ChfeW6D6DcqO(!-kfQ+CsJFy)BB;W55T&?Ll#CT1BSaMzq>gRy)w~j0$kj$d_ z9oe^mWFGC9#bScU-gd9{h6<@smsE0#Hrc-uCez&eEWSP$Yf?4_0AVwEBNV=I78gwX ze1F9p%oV$S8x*(L-{yra$^Hksu&Kdhg6|ZbAHo&JyS<*xAJB#U7EEUNUdH4gA5Z@L zME+*I{DA~EEriVR9r84PWhO7*kl#JQaVLblM??B!FbeQ=&vYI!=Lb{7VZFKdf(UyA z9uD+p--Yuz>&fnhlby-ef^dYm6NkIei^Z?FlEQFnURC)!W~|IAtKEGu6xZ{ep_=UO zxR`Nsp1@0XXqOH^{~f~WBS=o@1TetW-Nq2Ez#dxVuJ}~Az^}N-l@v(un?s+av2krl zTRJ6;y#^TgXCNqD@U|N))$mo=6Er<@oZrs)q_M+oNgQ?Qz-S~ysW6a5MUsWC>rhCQ z-s!=%Mv``PUjREFNjf&_-UE+YE{$z1oi9BwZ(6dc2R5WR6Rdgn(V*U0tZNjO_|{fz zcod1GL%XvjQDmSA*2bLaa4Ui2Z7cgF3YDlWS>twK`+hQuZAV6%dV?fNIOt7Rdb5q~ z$b8b9c}A15xP)MHqDds_#WqEgHgs)wRvt}qkaTNLY|-12iQ!HLI*$8~z8WHT^d@s&R7@hS43L$pSl&a5qq7 z*%}=yHu`3=0UgMG_sAqznAP0rIWv(3btKP`47Ri*IZbVTj zZB?t2D;Jd1n`QqQNOBuB;iVp|NgRoy?g=b5j?5&f?A_} zoa+b2v-&tPrb#NFy;X6ne>~|YLQRL9A1O_+;n( z1s;Q-@%j{ZcNz+w?<0FU@AUL8F381>_$_`YV-@maJF>OCNG9pvINytWM0}#q*2@^N zdq(T@10$?1?tA%#pvgiU8?B@kmu%PB)IMZ$(=$)taI%|TZ0j)hC99jzD|a1R29c%B z=-TtF-B|K*(`MHM!F!&L3u8%)E1iGEVVX>Qni0y{=98Z8$#;dns~ML2T~?S+f(c

o%bf6et1)!IIXUo53?RjL(ws#ms9AUGF2cgUMvo5pA6si*T_G~haLv=QZlvH9! z9O;R1x{)Qjo6T$SN#KLOS@$`_hZfyt!{_@jYretH|j*;!<~2CKQvRu+&R^f6&a3&3{9ZFaSQ-16LZ6YS2`uftEm zWY@oob^H1w!fq6jB@}SfORy+*-(qYojD#IG*qOQHRr*lJpS{Tzy+jVvB{x{Vd8B>& z2mjV>wM65`0Ni04R^1Tl*3AXhq9yrG(GT^ms#NzU`(PgF9y>?R#Cu0;+=aT=g{ij= zHyiTVx>u3F8mXOykC_+YaU5DU<33@n=94m?djy00M#CWgower)207)Od&Kd}eDX>o zy5tNSvxrQhLw<7XT}0Lsx_2}ivlwHzd^B6Xm;}=~quF;H=8k4RFUC4JT+0%dkWnpM zxcEKbjPve7yh8qF?=K-;THWG3^SlMY8{;kH_1rCXeF^FA&NFO{?n9zi-(m@`lF)cT z>VDrj)lCqNMLMfE?4hAP4EzRYx%afSu;)JzbJ;)XmSe@Mq`WbyW4&z{?l~1~x{Z{W zaJ}YS@+v>pn6_MHk8ETKxylwTC%yn1mXl0ZTzx9~ z7nv+0PPSV{2`4)*lfg}Yd=5qIDaW4U-s?_HztoZ+Yf1X$5A5v~Gwd%do78sti@{L$RS38KMoXs z#{yp`5w62|$~g2p*6(%l#4GV2Z?q{%1IZfbevnOEMIxF$I>5P_{yD%_uOgmJ|Im_` z4zR7OU8n zB+DuOGL#g$+KgWzzrMBe+u?k2&f-(mQ_QH~{5!Ckr6?q@a}3YN_r|8Kh&Y?g7i8Ar zCltTdf@}Ey%mcz^^yO52K-ILG*%jg!d94Zvh>7VWBFy#JNE$_H7kA zrjUQqtM`k+ssIp9coC^as)ZetlDXEygO>q4no0 z*{OA;cbB?M?mbUF#&s0GLM`|fd)@ayifuJo;Q5b-6ecdn;ic(e_C2!b-NWq14P-K%a)kBRNW5F0Cx$|!bN3it zt3SU+hWe>H!Cu%%W_q0C(qVKaX_>E-vHFc9t@-`0L4;mbpg@N*miQ)_+NP4@pu_@i zJ;6CRybMXKq_%LJpFYcecoT=--at8jzbUelcy=9uu;KNKsUK36N{~;W!*3I*BwfyE zwRRneCfQRiVF5OrL-D$!%6Ni%OH?ThHxp>U5>Vs!y<)d9&&|Zv{G~5JtsaW2u-N>k ze91oB45!RL-?Ph`iDyr=KXS3oi zsL~`ftsx!+Jg0^;Tg&vSVt>3tq60o%2i<%3WVX%!W5hU@G=A{G2Sdf-3CA5V?-EMe zA4hL7VD+DJikk&5aF<4aHPQbn5C!9%ZzqX;ei%qxuL@ z&uPDJ*oLpjH+0>0mh&~1S;y^;9bc2?#N$qJLsRkC%jEhoJ5xr2FqEtL8S~)R zXk2B1t1ac&M`&0VRhoxiJOS+9Ja5j?LYXhCfb3h7O^LXe`*&u_?2gKB$skwy z(gNnSn;eT6DkIzIJbxJLR_^PH0(~`1jfU|6CS7Kx3V0N*EO(5mfal6@8)hY2uD9aX zJzxra%IC+!J*=2%j<{`l~2t1Bxm^cj+3CunX6RcryIbbT+FlG(Y z4VY~j=J7$nCk>bnHB7yRu>!M6!<^$7-9g75?j@?{g@q-d}vx;n_o%0>`YLY;T=w|r1Gs0{cK@H6Lc7abc#U^3A zFq2O@zO3c$Y54(vq=6TalNTo%3MOEWSk@LgaS9%$Lz@mnx&UdeMTZDfVSqd!%wo5? z^7rjQ_8d3N>RZCb#E;V8o(hyK{Q!yWoF%a0)jYm`l%7O@3)DoA^U_&ywf_O0?&GGt z{!{I}$-xNr{sCgA@(V2PAbt$9=O(t`ARMmy!`UYX;W_$n2J=1yHC6Z~%Q}Qb|JQVO z^bqJi+{nCZacJ$ok>%HtH|Ug5*5ojZiBTKaq{HMM&7H=^9RZ!!diK!~a+$VgZ23{x zrd8|Mjiclx+HW13c8si~#sGHz7(7y+_^{05WTKx5_U)Yvk4A+)#d#iv`c~D5W98y! zSKeTIkCOr3zpdlFHkTZP$jW_#gtdatHP^DJ6R>on0@!mWNJ^kDuI(_6DRSSie*A-g za_Ry7yfY1jROzwe*mHt(B=m3|yLpoA?i~LbEbCHiz8EUg@HTt{->?P=v4NXQRtWti zbwq~P#7oz(%cn@|qQ`z5fuA2R{A_4zu+fP*Eg44%NQi;=ko`T+eUm z?5*cx*&fnJWYc@4EaMEx@^HR{YCFruOMo`h`Ip$HGo)j)q{+}QwK%CxcAPpxMiTnv z0!OE_X9-oJ6 z%>_8V5{BakL+}HBw`Ze&wzkzq^9F8Bc(aSI5jTEaCL_b?s}U8=W{wN+Rdkfu*$ZSS z{bjkM(?x=N%E)}y{AU;f%a=3D&m@y(ENAl1WVcUwAu-@77oNbW(ynC~W0+_iic=2G zV|l-jKTT_hVNQfKkH6yzTH_dZiR>X=-x>;6YlXG=Ah9M&m*zu;)>hm-XZ1T^4aFDG zrX#@MqC++n>U0M{XWB8}G3-~ej?hab?9boe=2~3rXnqCWOWI{1%jrsD86GB1?*GX2 ztjpx;C?QcFO@u*%pHzC*WpWaP|35C1Z6-GJ56xw={tp;c^jt0*TZhHDKG*SK9Y3Eh z8voIbtFR09jb-K6a4=jlmPyy)_Z>dg(e*m{7JGhU$6tSvKqK9n!8~r054@%rc>6x& zO9nGDw4Z2S%b~3HHa}l3WPjZz?K$-Shx|f=RQBW_+=ShG;)uAzwQT=^4(B}*OKHde z*5)D9&Gr6{tcN6^5taI~X)4K~7y7X8RE$npAIAlieB9W3IzRkw5%v9ImG4@`;WzxZ z>FmwMbhP_cd{l-BgJWVvE~8E8mgx1sK+|^P*Q2fQ0}X%E#5zERF4Rr(vDmA1QR6aIY7qe3MEH)LaS~h0d{|M(*ri zSRDQq@W2#}WL_rPxoOF0!2`E+15MPwRY$?2pw(n}PSWaIR2!pG9ZO8K1EF)fvu(}k z_N0nrjDX{S+0n>qb{q=bQ;#O*;oxE@>@FJv6Yx3*adex%*}yj=vhiU&8f-V^c5!Sr z(`|&>GaNZB=y5{plUQggT1ht#X3kc0uvZrU^#YU`A%5^I*_!7D=TQ4`Y@;V_Ljwl0 zd{64@@imCxAMK~*UKuNLTb~)kk3={EwK&7~b@4MA=LfM`Pntx=I+{rII~V$Ef40w? z#x=M1M>TxDVJ_MFJB&WmhmaADKwp|g$Z*FrKRT&V=pa7LzcuD#a|1tQ19w=;>BlPf zr4;`j*jHY06GD3mSD$&FzKLc983d!c;@o#n8fs? znu38)wf&Yg)5%hUsb6nBJBereBipAT8($u%d(-Vx@29(*w=A>7Bx#lNZxqC5>G(w} z_s+Vl`e7z}Czyu24?p=>b-4=mH&FZWV9Izao)4j+=?;q+JuHBKwAd?&3&LUw3L&Kh zCmyR>5f{T9ON^_vAY}3P;(PXH1#Q!oMWbHM?nnr=n3{pMz|8%{M)f$uZ^ZfV8j?ck zW)9DXQa^9rp)8kQmRUL{7C&$)Y_Z%$XJ87W{hQzKiTQG39$TwYcm|smM%y%F#~!PF zv^KvUM%Qvq;o&r_!^3pZ=H~c00hGdbW~EypV7%StlJ|wE8Mhs6Xgix5P9uVO<*2#o z@{%wZ>ISW5flAaz<%e*ZMgxyBHJr9?(*(sQ+oG+-m9$^-ALduLVky>#=#-r!Xdl`l zoh^)@2fX`ou{gskmH)_=@9Ms_puUcAZD|kEY)X5mdR+1TlEOZUqyh9!D65X7;jNB_ zVl5Vo=g*b7`G7BGdN4JTS_V$&0SV)F7tAe0wNDrKefmU&Qoz1A3gz%iZlMR$?;f*+ zR#@EjSnymB$jU5vj|E~LnJMFI0{pB*x_kncEx)F)wNbPS{WOKuM$tYVADXx#;lEZ3 zHq!0RETSE47ZsL*VdSTVgk3nA)yLw8!+`<8mq<>U~rx zGa7O#9Svjyx>Jen2xODH(<^jMG|Nt*cKS*byO2UZq$eX;aVl*~KaFIcr(z8>i)7h7 zF#XNiGTf`&qplH-$!YWop{rXuvU}16B(&lS@vUz;gaK{`QT->OT>BY+b>-?EKNHQq z%%DCaR{cwurW3xP5pHcD)ShO`rndwFFAv=L_Q1cP%Hx!>X~F-==o8AuSZF7j9>VMv znn520b9sqi_bhab*E=DwfyLK&_Tez)@bhG_bv_GW(=+K>kAH%>?BgT78ZPH*Ff;d} zQM7#!!>=($(ssdYQZM>EiDpN8(bgo|@p~^ioS4-BjN(16a^c?Y8OXA&)StBD`)O;o z*h(jR<3#)a;j&&F#hzH{84~F@okd*<&1k`X=|hug;})zruZ= z_%ebW?MuHZD)GZY3tuY2Wg=WI!WAN1DZ*7ETqDA@B3viJ^&;FT!c8LFEW&q0xJ88T zi||7c^55`R!#@?_=OWx%B)~Qiel5c7BK%f_J49G6!tX`6ON137+$+L;BCHZ&jR+5l zuvUaeM0iYuCq#Hkgl9x})~|@S=)6c=6yZ!?ju|1sJ|gTc!j2+b=EHMe7U2vLjuT;y z2z!b!UW5@M^cG<=5kA6mB78I>!b>7N;cZ9;YM)4aE5eUOxKV^FM7T(V`68Ss!XY9| z^90PgL1k zHthmV-D5L-`;dhVq^C@=#IP_oJMRyeWqU%H5ucZl5Eed&hL8}4br8)brbeFJw!*_i zT;R2G>>o^bx-{E$16-sUoF>`3!>PpH8crwU?y=DbERHsg7e~;(6m9!%6zvbtY&1R4 z%p1#I_B`mkp30h!q<)Tjqj5i8l!j07`M-nsx|9DquS2H}WAyw09lGoAk)FRphoUfo za(v9n|9z}OkwCCQr(2-Id<_;ECh3SFI<)98R)-sP*uW?PUrzIX@jA5VaEK0bbvRpx zOLe$Tho9=OTtEo%K^^h44sYqOaeJ*IUmdpBVNV?n)8R}VPEFA0hw0FwLvNj5gr07# zLj#BL=*f+Et>8r+*6Q$E9lo!_l{#F&j*q8aMSb*Sybi;3*i45v;xx8rbht~0@9S`Z z4kzhwkPaK`?R|jn$N9f&Iy|GpDh(DHw(E$Gbr`F6Y@ME7ro(wU%+=u_9a?nQQHKFK zbk*T)U4*Xau&3U@7y&VJK{~=!hx7CXJireg@_*m!vU)~`zv}Q$9X5{9=z?^Zs>A*| z9HYYm9j?&fW)4xmVVjQFrNa|CJg-Bi4j<^yy|Z4S4!i4cln!U=@Kqgd(&088R_X8( zAg|wWO-J0(A=P{CslzZG#_KRmhy8UpMu(GixIl-?bhufEpX;z(hgGpIqJG0A9l;yJ z|K;jKtECKvul2?8p$-?|H!k=;Uwu`(>+p__zpBIYIy|Vu?{&CUhnqSSX^fWZi1m5} zGxYQ*9cJsWn-1IS&|8O%ba-1Ye@%y%ba+gMyL8ylv6U_Lo-WYgmQF6pS0vnj$9o2x zpYe|p0XMy&L5~jrR|mI}TnA8*v3-mNH@%MnDB8Al!+$75QF<8uN*+-|iPP~QkFIOv zHh%oX!f7)Krp+8b-tp#C+ImvckuF+AqgZ(@{kAC5RjVlN7maW304;yhaSb2yKln*y z8b0WM@D~qjc#r?VKRT%4UH=Dv@qoZHkDurSN6r!2txfpOpKf1YdU;2B*`jXW%(_rs zjMs&R7n8=!nVmN#e}2K#7iV^uGj}HY>M9+uI;PROdOCuxU&%(*({>R)fkuNVzP-Uj z+8G+>8w~4L8pLq=;0HdMk`V?P!*Og^J&a$#pX+I=k<;X};A?bvM;<+f$}~2sz-Gl$ zHvD(uecClLc$3!*E%H5bQT)SeuQklC(;#9(>@}- zji*JoC+gh>!o@%gEay*b_yhl>gL#`i{F4T_iZ-DhmUfM{Va`8k7|&|{7i|aVNcsz= zGDi%$K@YY()k(W!{CpFB7ao7P<5|a>G~g+|4IKBe_?thTjk`%Bp5puAR-xCs2E&^1 zY~8c;VOtD_oA~>DJgd4%$Fay;wB%`_mp;RfKH_iRc(&^nO?`^rgj2^h{9PQ+n*WUh z4H~!gZ#pQ1Hx6TK2;OfnWZ>`ic%FhOhWHqfc7)ufJ;`&cP8tj!;jf^Hiy_$D*kHcW z%+PF7Q-d+y)$nW{@oH@F8scW~ifwhQMTI#(7|e3;mcW6(?YwM|t0Cyrs|N3aC8ai;kW%7~FGN^G7t89i=XAtmhp%$h%=k*-r+;J3ku?ZF99& zvkL0s#VYSmDWaic0hbJhcKE|&84g`G7~Jsp1^)Q|hxNr}_T&y-{D;e%3#X>t72Ho(AMTTXCN-Y@`8WtHP71dEm_wzfzk*B}zbML?TJYPSb zcV-S}=AD`M%)B$_%xTYFBVUs7PCwX34UY9wgJXPi`(G2&!x5WTYX-i z;+>~&S)%7PO`Xm#vjUB4%wy;EYrG>54${>7;Zn>bZlKHxHK_4F}dxUIBp%MP< zjiXdRn}3sE{h(Z*48zvjh;U-eBxcUfdZ@YVqJHBHSycH@ab2yckI06;x0pSG;UhJ* zv#oZ35ukTIqnkpsCvWlH)K5|)K?Wx<#1J-#Hw|H+$ z)s+A8CuSI_-dTF-PEB2J)6}@-Pi!~lq{?5Eg}ZOQruLItm-jB}E%sUW=u0rX~t;J%xT7z4f4(cF6WYM%S|q; zN;_z-)Qw5r1+;KgX&Tw*d3$q9w?5K5pc~7*i?MPOHLzJzM|e-eIPM*;KS}QxIlbH* z;bY8N+FxT+OIa489J+`8eQkdK_=g#{nMjR#*f;){)4jtq$NHu_R%*WNV_a!Amu=IO zi;Q!azJrVy?>y~#ng!X;mf$ew4jQnKrRA)O9qcX46SXyk*1{|sWQ2Hjc^8BFE8Q~_ znM(D~Y-t6X3i$&s+*M(o9yFi=K6Y_7OaA6CFZdecy)zN{qNc3-G*$77;de1C@g+?i zaF|p4j7hR47Wo;e!kvDG=Y#bVKVz?yw^)o@Zk1g(l6edDe4vg}L39fQX(PNJsO2(l z92-dNXvKFVlqJ_M!`J3HF1TbdMup4kJ=1uOB| z@0H!`i?1{_p;J>IILt=|8~bFQW&{{Zh1&y+D<6^vxoOx4($z(W`EI~~6|vutI};ojvy4h9le^zIz}>*beCcLLMgLGE5* zzH2oWdD<-5pk9j5RU7%^z}K&)>6a$dVO4`4yV8^gOw!ej*^!(Q>#4410t_dl$o8 zkyoah#Cb_}9>WGqw^xQSUGjio3*_jZVe*q&{r_?qmd0y~de2*}p5gl#n6O6QIxh6r zE|*`sT>3gJe(iGkwaew#E|>pxmrGz@SBtmHrRJ592453A8)A$%n?sBdHP%pLyH-;_ z%1H2kyouW&BaBK-^D%c%mZ#C&U_F;)vVzm#I-Jah0QW31je&%91p7iP|#WMl=^ zJA1oL`djmQXU%JjAEc{KYffKpqz;)|e6yY)pSk~knh#7fUP#R9eZz+TGiz4fYJ8(P z4X(4S|5;Z)ur5rgGF%uwhI1M`8vZ*M%4+c(7!Xcp32uc8$8+fex5KLxN;pi!x z)8S$8`5TpT<5lp+XdYpGnVE)(Hz^f?C&76)D;0;^;7*c;SHhrJCr92C;^)pQBU;-T@Qu;OGHQf;&>EIlKZ^&ZD+) z;aSp*cfu*D%qSiWE9Nt!GCiESP&%Y}7(pCzD}9GnJRZjI=#MBo76#nSsT^*FJ4rn5 zf(MpxkqWPe^Oq@Q$8%sg$;O3iGMQ04A3jRfL=!7oVz-U;KKPx1X63i0qe(u{Y*C(Bq$ycE7gl)*EZVd~RtIJgaN z-of^P7sEmS#RiL8;6zf3N5b1kJ)R76NFy$Mm^9(eO^A!MP@orD{k?)+3#Wrwy-qst z1~}nat~ucma6WP4cK94oJ}enLK`eM1EZ)hkhzs|S2t1`ysh>#{u68RmVmIqQjzS29 zS4je12frnDyc(dsrQKEPS6d;T_O# zuTnj@WiK1ktQS~-LChU2cJaV_ycBM!X3NLJ_p`o93f==3)luzu8vN)GZ;ZFXuU@D1 z#5>{O2G%`pg_B4f9tr1=20R)5=NqhUCxuPOITGk=s4n>MVX6+_1b35gyb3mvNL=_e ziI(YM(3`Ywcrg4ol7Pp-bdrn<|3qwf0o*~-@CxWW!habQj>4}=Hr@q?H?r^I!SDf+ zj~Bqpq!3p}lv+-Ta0fh1it%>%o406`@ML(Hl;e%?`nPGM@F@5kartsVs0s->$`;Fr zU}%zhycAv}jd%}y;2mlJFMywt7Q78EeU}Xu&wz(X2i^$pc#jPhPl5Z08?S~_o7iCK z!;OR6n^^xA3gr|g9Ag#W5pW|3!Hb~paaI8y2Dg$(yac{NqVYO-(+O4q9t+b+0zR-q zSvs9lnyGe%3sXrlZgV32y~@H_|5U1k5yJUoJ#L5Bwy@#gk0xkEIZatRe|`E_|6J%M0K=AG6`$+3>qh*!}RR zPnCL{l;9<>o|NI%Ha`7MvCH8d@B-<;d*GbUSRJ?xu4!j=;Q8(B|1VPT@#ljCwva$v zxa15=fM>vUBnmHtr6d*??jiBG3%)^;@J9GCNx|FTy0fejyb#8H%?67LKRm}w`_uln zA|t!lM;Q?U<4HR%%pumnG?_5&du9R`{)Hsq@+9)ti4AXnMmOsi_kpuX2A%}1Kd@)v zd9Z{O;=*bt{}oZFh8IXN-UDy>ktN0BU?C~Th5t<|@jB@96T1*De96rogjd50q!I6d zx1DFN!3*Hf3+yR)ApHI!2PwF-8*%ke)d5r^O!%1^z=buJsZzWaT9l?Ta3NPIRW2@! zB;|M<+-GR267Qyy@>+WCYw<|vB=vY9{DCy#ZulR*np1UAXrfF1e){AsRwjaD<<>Af z3Ko+BT=;{(rZ(YjxOp(0hB6$kwQ8yw&xf@``N0}ocyPF;I`Mk=*;Sef96|#HN7ES} zjYnL~7iALZmCv9cPoCdRTzC~+8ltI2JOj3q9$aXlx7{+7nt;p{E?!Zv!0Smo9tH0tNw^LEk)+^x&?I)e z6ee@Y#DNPBkxh6544BU(%6r)VJ1Nvs5PtCh zRfl)Lia%3zxUjB(r3j|ggWr=Vyc;f9&rIWX_|QY_#&{8YhZN&YaO4J78J@n8iw~q6 z-vnz(Wiai($QQ)Lhz=NB#7Nu*WBA5jJst};kS4qc+BRvbMW%uKH?wi!O|YAUU(NJe zsB#jC*Tc|9*|YEhcuz4KGM){ea`Imag)(@I*zsn#U@O&&+u=&$z;oc6k5SclBmA7K z$2(x?dBp#xqfK0I?2|OE4 z-@|If)n5K4vCH&u?F+0*JRhdJs39i>k&C1R?}1bHQ4@GHT>cU*BhGy{YC{c6&4XTw z;AM!*Yaj~7+grwaXZ{Z9C#%>bAW9K?|{yA|HB(oD1>M0+3oO7xaJU5 zB*WpWq!q7&=icPg5buH~k5DT(H}j~qZ?WC*l$>hFO+P9a7h2w7rtu{BA&JAeSx9X= zMs1Cf_8(~>>5LFAJI+@XA&*LVf)9-HQxA%rZPWeBg7pr z?Gz0TF03T^cr8?)v9;sEU8DrBgkkOMin#D;QiYd0kriiXa42NM?Vr;E$_P053qHT` z6!^Oi_5$1iPY_EO?F2mhB^x8&2=7130^s>D=qp-AJQ(sz?J5bErxDETWc$Dq;4+_(#lo2skua1I*bGb9?%pQfv?NdoSM_r>#LhY+TsIub8|8|JW-cm-T{ z2kRa$g#P@DZ60ogLccq8)rxaFr{dcK9RE>B zLh?u_F8n9y##>o@8XvYNte6;o+56%9dgsDvQD76g?BGuedFoy_N6R6Zinrp z1Q-5D%J9J7vv-g{y4o_}pNLyGJbVlfH)#Jws))^CUxy8(2p6`JLS8JCoBA4Yp*4fu zNxIjd+&0%tZ-`LthfAkpLAZic;KFsp&MCc6?sKcgg{}-IZ$iOE;Rx~JlvsG01ae|3 zyhyTfq1nN1uwiD@`x$HNn^ z;o3MK)-#0l;J6Vy#~t>d_wN;NVMF-iN5pCD5q>BCL|mlU$^7{@GR4bm080_$_#}2) z-EI8aOx(r?KdBiRKjSWr6FQ5EDlggQ6*l^EihlakTux|p{HfRnmT-lNex^7}Gnma0 ztMD8pzOBh*qs0rF6kz&jZJ7q5Ufb<5T{N4`pw2@%%N}O$fqXvl9dj&-Jdg% zh(aMJU#!>0Gqc#Py`6V06o>gH^P6+UIlle*oIG)@Z!CYiMSR{jHSm%79OpP3IKDv9 zXNaeLALGLoiSzvSu|Z;_|6tZC=KBxgoA0VypP+f3qX+&Tf&ua$|-c;qd-m~ zDLcLq8s-(xoh|MP80VKf4zrT47cU1SmFUNjMlhafj-2g0%tYiBMezmf;>i_mDqc*oU%72l%DI!A4^6)GU!D%bS-d4j77mX!Wcv0N!u z_Z%FWq;jjL2KnAz#A7|jL~Naw%VjsNjWkA-V!*J>BVV*@oc-eD)0-IBu)*w z<@yL742omh#H~S5(c8elnm{b|!PJT1+Q~R>9`&44ES?G)I9in{3_9VDHYoOP#ondZ zFTxPe(5&beQey3*&OHo@K$Xd1w2HnuQ5@APes&s23azo=?lM&ge^}EB(Zsp7yvbCd zL0`&gpw04Hs9T7R#k4sN2Nx=zR@Un(UxN;m%pC*KvT`etW>!H@R*o1br-jTg@$Ft) z*?zG=cc>RBa7H)UmiX2tB#ZIE@qN*wV7gG7hY<)Sxt6pmnA&S0*1X`6WkRcR+L}KT zvN=sMvZFoOPGmQu17I&8yTg;ch-`%?+m5WwlkHMYr(eoGr>j^D;j-+qu41m5>C{zB zS2GuM6}PLIi@J(jHPfZ5$U-K&u}oJnOvSY8Dw2>9O#a!8x{7dQ%SUm#iXdbxW_jpu zWHL@umjh~#Q|$iMFvuz2y#y<=-e8vZ!j55xB|bCHe+z9{In-J&_>it5Fn&e95!zQi z6`JHzR}{s{X~1>=4j62iMLsfQa=V4nDo! zsmXRUX_Uzq?d@WQB)=tI4vCD4yM;$51=ez?uY@nKz?!&vGtI1i zl$h2#Eh=mrC$IQZQG{maOzlu@Rn#c?(Tj>=k>|`M&lX^Pk1S~= zP(>=KM{-CJMPo9x36o3b6{mkRTfR>mqc<}_d|V$tdJyVTI}2SE+HNQl2KorZCj$(& z4l06Jm4r4cndPcOBk!QjQhI%{d{F#RKZFH|(cvjm;3KiXD+J6ECc#>YrX8zwZd0(3 zxq;fO4LjHtY%bxf*ln+w@@Y^OTKSt0=eJOM{b+Ghc-)Yi98!n1CQ0qV7DC*ogS%Ni zv%n+h5TNA6Vte?2!SDP+1|Inr;?XLx%R@}E*TWeaisjYf$UgDdcW3uW^uNA+R`rR> zGOH8bEp#fMVKF%)gvQzPijOupa^}O|aJF1WP8ww4>{+zK_$&G<8eeD4e7*Q(pZ)>r zW>F1PvG{Xhlz2;oH4@EI$7_ViRjK3sg{w}Mx=v#yM=3{(+ao5LFMY2lj>Az-Qxz87 zRArT>&@4wRrj=ZUdBA?R-rrDh)c!|mqbqL|Gt1NV(DIpFuGp)bOfXeAec-$yGnP?D z7%4BOZ`9Bk(>U(oBO9U4qYX2J3yup>79n#D#41czI>KBg7?r*pMNh6mtm-?a|5B9C zH=;>_rO-9`y{RB61*(KOO?wr+P5icRE(;NpA{P!=4Kz%t-n(K$r=puf!Q2ej&IEI( zqU=oz#^^SSyCaA8yK{hnbK0w&_A2EwD^~_@KHD$K&rJACn^@m(Z13sF)2u=_do?6&mKYM1R)*Q5?L3Subp@tKHCGD0h%*IV zNX>E%oK}-2p}sfmW8FW&4UdB8Uh#xX^3XJMYL(e4`*4W%PFu@i2i7azLOu3C)p?_) zxFcRLg+095f0S4s72@dwAsx7Qm)bn}sQ6u!EhN^QORKChkhT(pdmF?h(YLcS(H(7K zhs7(=ce7Y=QA|7=AUb0nWD7-2|1rGZU1DDU;Ve(Qzkdcx7I*fKNWn@jup+iC-+vX- zSY{}CgF37rY$%YszzX)|Dq8%p|A^5b=WMA3u8M>x(n#0r>qDhMRwP6K>lr0DsbgQ0 z5X{+g%5yd(it}Uj5O7s&|47w!M3Vwbe4yP@Ec+SCr%>7?@r~FdKXte{8^v?6anac4 zUNFFEo_`vw|ARUzE*8;GPF&wU)EVMCTSAz1Jt!gaXK?q^#e3uWS}Dk|s?o)C2wWiN z&nIsY?C9d{{XHRwH~7dywUv6iW?jJr@=Ipbp>Cr%N|og17V-#HPyXpg*FO=PB>ot; zjBOO>-ZCJAjD&V|O$qDZVUXT2{$)a|6Cy%jmxw^qkEPekU84JzI~XVW#SdDRL6hJK zz|!l_Z^8r>SS#kL7JXCIezH1jv>OpYVX0b6L1C5~R&p>!6|q!BS>mYnea9n)vXdUMsFOgNG+-S+;;@Y$$Y>20mWor$9Om=PUNj)~FlU_kr~WbEd5x zWm>GkYVYx4a6+2@HL1n?g!mL88kAnyUYmi5zoCvbDv!R`$7NmZVR|2Iw1g>{o6)t?v{}gvgOSN= z=Fz|m7?><#552)hoR!#&Q`0U%HAVD3s@H!+1eiOf`$DxB?-1BUEbT=HWq($%3H$ zA|z8jO|M^H1#|Wv7z-z#JTQrTq2p_xU$;Y^KCa1|r>cv@+5%#;JfVOT7i{I>(6Ivb zGr?5I9IaaLX{V{$W!mgCm6ORKSHhcJYd6Ys)qReRSbfaaZD1q+6Qfomv@Yyca3+qm z8#!ALCX)hQv1MRiz2i1$L9tgWr{ybHb+bFh*Q6zjR|objf7r6!l0nN_;SB@JJjvA(gDOsXOJ&nsOLf`Vpt8 zn_L+5ryePdR?5>}wB76M#dAp`*t4QOc`CbIygNBQk(NybG)o#uf)tLf*s?)pSLGV) z7uI}K&=w)SksQ%eb%tuR+a{h!UcI0L5wy#6(ppbM@=A)QTdjLgD3I@fFLG73LSpm& z9l%%$oe^7B*cV=ixL^l2;6CEk{IQ7GFzQJVy-)5qC8!`Zd^0 zD05nHorQIHBExkC3LDx`R=38fQws_ypiX*$aV4`6eRs+C-wN5f($8SJvj5aoY=&Hx z2_dh+OJI)#?vJ>4@nC91hz#|ce+g5 z&5e1P+c|l;s2LhNOOG9c4yM%<$Jan_@QR?Za>3-UdfU|%iiS?Q0og1+4iA7BNE%__ zN;FuqSfp}gDG>#iBv`}lf`4hbQ{B84itC2XXLpNV4NYQSi2iA*G4p>^lxuNJNwC>! z2H_1$5T~X^vQ^^Jv_Wj5xFu~+R20HXu!Z$}Nj=)xlNQ^l{-LUC>OTUsnT zCWa4-W3Py#hNYkhMM^fUV%pjfs+hKU&RoJef3w^yt{WC(q9d2%a9D>PQM6*;YMlvw z-T;n9V6f%^LA}vhrqwEE;P8yIL$To6j>wXZQJ;uk56cXP^C(1sn|`!IBN#Ns>_&T+ zfZMh~!}~_jdHpksMLpsac+c;jArcmF%O&19d@v%T$A>4}L9)=GN_^yuf6;VDpi_I* zUz;f-{^J%vSf#1FWUm%nCox-ssoJxIcZq!Z9Of&|OHbjqC5l3N&SC_fSgx2m&y5fI ziTDN{>BcP)YCY@BW$HFtOX>CqCuu!hwEU$ZlfQze{jUu@0d#+HNXG1u2k} z!knIGRUB@(06V`}NMbovyVPUwySC?NyhlzQF99`<}c@oIG+U z`#@YfGL|Xgb0hmk{o8=iA{yv&nJREUSk+|O04K;bnYNBd7e5_oj!d42$yYbq!_JBX zax?`qaLURYlq}|q8d?U)<(FHqcLIT(H!mSeshWtHIOc57Cg@g`@j#=!E)HpDHpbhRSqBD^Uo^2DW{ z^L!1B8cxg)7EAt2jeN4A%bpr`ZHgM8tpjwEP`IB$c1!vXwCFI@LMvTUp|BkVE7_Yx zK7W=vs3o(@K4S0Xy@fh3dJ%4F))ZLhK#?BPe3$xthXQ4xMP5hNTr``+kSif#$E>+o zgNeZ2x8^;#K1UZ@r&BWPku}Rxeg$#IsFFDr*+pAuQT7pAl)=`w#MhdEZp?C133>9s z1O6zHdp^((WzCR_zEGV(5spQ5-`~16C}dhGE)CWtWvxrF`!$VESWqgi&cDifi1$xS zi98oY&JGvDfvK&wXkkKNL283pS(PCl5#OEo0>9c?H}5vVhi$6sm|X9}PS)+5s`Mxu zQfT0&&NOi60e2M{xO0H{vkcr-z|*r0-0(RD?kzy*TmzQ{7!POz7>W(tQ-GQC4BW9h z4cy4P4BW{D1}<`;fy-QE;I;tb78|%c0K413RRaD2XaiUTTo2*xPns}ZOqug)#49}@ z`1mB}_(2`Lg((?@smFbq3#Q6q-4AnucvdO?SzOA(>Xy#?&4-N>?^-Y=r}Kwx;@6A&#I&x&+D~wuv>CV!NM}+i0~dzOD4Q65_duc?dw0UP z!&DXj$$H-^KSv`wc%FAOF~=YNE2@5dQAyZDwXTWWyGV8bwgEN+HUO#s<$%>T@uj=- zLy1yQsKc`W8PF{<>!P{yR}udA&ml!HvS21O?ZZyQIsVYB2z0dhq&6!Fl?&~vyhOag zhWutgJz)KKpZHIzCipmhR7Lwq6>ayGUtjEoaPwD*4b~(c$q&}FL~0A}VMw9?34lR> z48UkW9-wfQn0rrtm`9{iAXWpa09#jy&)u_y9jo@nHdC`+eDX#;&kH zx%aP_(x)fgQmj#98+*7MjKe%`DRJX>c102>v?~p)r#OCP|ELsoVcPrR9RxNNW}ql0 zGf8b-EKDJ8kb|xK?VqKMv3H_s@*q2zH4|88);ormGN1&xrV=9bG=Q z(Cj7~E)qwJ3s)~^8^n{VN3v=$@Bw3Nl{z9_#fMiF#bs(ngx3n6SK-CJbyb}DV7$2D zfuXEQtbafsaIIpTr-FZQXrF5w{tXv(69+N>!8XWsc6$;_uABCdhRwPauCOGm7+Ue$ z%OUpY@WtCUKcTU98)`~4W;x|ia$L35P$7t7@OssmNp*6_Un;aEku!@M(oeio^)vjZ zwx&8o+_h#P`f6R1se^_%n^rEY;h=WZM6DgiHjDGu4#!hmo6WY0$JY*FTkEc_{ocn{ zlV;@P2gQ&_!g_|`ia(exxHV~F&?B+DU=W8s5{IO2+9R8SdTfV6?7l+Kwz|&8C-#70 z9NZczPTq1aa_??ghv%(N)AOCD9o{<)2ZZ35GY&e}Kx+^^PVgsMig140R1m5SY>+Q~1qCe)7F zUVB~b+82vhz%Ed!$Gx4R^`*&dr}*|u!`L2CeyI|X>au@k)4SyCM%x!oyxc9G+aM?u zdgN4K5;$95E2Z+CdoeNCGy+sUgesCZyAfR-g9Ebe!9z|)te_sEa;=F=q1A9y@7;+S z98`7@K?e-XkyAx%i){U@l#zQ7UomM%5XL=XN5ZYDc^`q@I-}c>UvCP2p$(~HZ6Iz~ zo&{dXTtn?bGSE)wa;|NyRBWr2%+)F4#vNmOQPq3tSR7p{>gydz;ce)8ZwvKL=2>Kw zsoXC1e>nz&AM^4X{FJxFpI+{ZLXTJ0vG>HPS2l+1g~Dn}iJ9&&Zt^bpii2OB#>Bev zSD)b_Xw>UnW&XIy3hMU5Q{P3;=!^8s?4)PT1w0p5>mxw4xH>3MO`>HLscl!2#5Oe< zd=|;p$F;#A5lmdIMol%f{zzRW|JpDrSGINI##?I!DZ{@Sstm2wpxC7Cf^vFjxcVk- zx3Bop8!<`8Nc4Qvl`whuK`4oiP|ZMEFTMjS&>u$Dk(2KyUU;KlNIeQiJ@XA`RT~u` zM(p~gj98T53dGaSRhb06!6Q&hEXqJQCjSy%{^kIfTl=7II1B7fpdfbYrTJ`m!9GRt za<(X$Q2|h8ZYpNa?vL*d-cLc(B6NqrHNxP$1aTlJY7oQ+-t3LVQT^s+JRjSg+7Dzn zea4wBayj*>9tb8P?y6}C5I@=-(^sw6RYU`2vBy>M6gEW4fX}8BjOoon1XxYu!(WK47d(+;=#I zofW@5oS_Gqh3x^gQ1JPA3UvT zgE-=W4<4vH{-K2hzyk~U{?4GVp5oAtmaw+EXFiGnW!=7yFY@T%jZe(1ZTE>`TGR~x zEJ_2B?>I9B&*#o8gAWcm8_hb!)U(5pFFrd#%_oQ-ob843=VymczlgpLX%IZY8I#k^ zjJiVWw-ackWiD!JB7nf+rS)~aO6bI;L_c64oNOYGEOcHc=o06Bsn4OBepEwj{5@>& z;=~q?HIqP$Sl7DUywwu?_jfkdZuabLJ zTChpr1_hr(5SJoF&rK8ASK;`qD&ng{(8`&wUd2!!YfD0~w5u(ye;39B50J>gEh~vt za3mN};eOk@>h0A|v9oPR8Mx#4z5}SjS{fJ%+tfm=T5#k1}dwixDrF-=QiL%r*=M!b}qdv zN9toQEJ#0-6`hYfURP4!x-K2>p(mm2XbiS;;&ITA?(d}>AsnPXx4r2}a_#gO(1ELbdRze~3jJ!6XTg&OTe zVd4kv12CMQ+doU(j2ifV3V^}F?nCR2M0dzOzqj_Q|Ggz%Yfce=_;wWN2Yi=@=aTPY zCxaHR&uNlqD4xL!ll>iu<$>VhNTdOCwbSv$`VTTAZPL!f69+oZ;02?;PMCH5_>r2Yp$vo$7+AYzNi2yC=FCjt0<*3__|uvgN0Wr;Ji?m6Nwuh z(zc?f*fG0FeY>*S;ZcyMm8#Yym%@Bhdh20h5DWzl$kVJ`Ed9Q3N(5%!IX)2Wdn>E( zftk0GjI84s#}t3XImWq8qiFL}#JcYnu!y=o9UFbhAStOCySP`)R?fh22fx<&ZubFwG zCd`Dm?x#o`&bIt?ABBcJf0?C&cI|%j5}E8`eu`-OWiV=9_$AK|jGMH;{P4?wAmm}R zm=_-{n8f}%95e;LCgSRt4^GBxnC&NC7}*M zo4QM70{`IUB!tqnmoq|q$8iU{_}^RUMA4Y4ZSfUDlvFYF&&@+oldV08r#(Q<0PETo z!Zg}f*EA!8PvLCoIF1aJ16r_-JAK73{v0zu@2UWbmaA$1RoZ5f z_RuZVtM(Z67a(Vnq$FT=gMEJ1+(27GRx~>NO-WBuk+yTr2*P-Ui-lwN3fEw3z=uqk z&Ayw=3@-;Zy~ZHjrt=KaNxc}mlf_DG-a{YqZOdEE%+{G=ypjgo6jWw8GD>(Um|S)Upk|2^}dkJKFkFn?NF8;L9v5S=#5z zX7xlhKBEdmB(YiQ@5hGp0^(qsuPcucZmTrgk4+2(uJV$uf>yrM-X>_?2NSm+o8ODJ zi%a7*%+?Q|PH&YG{TXf+q$&PvSP!&uGKY3lvQ*{Ia)530XC*y1q5)h~IHq07?!oQ? z{<$7(K1+5hJ($R|_arfp#nE@X&P+6YH;@ej$q#|daudTzL2N7uzUJ8uUm*u%PX*zl z^NrH`L98#RKMP_8BoV!kY?O+6v1z^_#>ZRIo4r^<4D!MloKc6l9^?FR;}Pj7^XF^< zt_d{YVCmOh>=u?Hh3i;eC@kmq@qzStk$p-*A(Xm9=e}3R2J>M5ObAOvaxjEd^ZI1> z9lhD59x<_TMlPPewnZ=SQ-8hRaVrQhwvoPW_(y`gl?-}X0 z+3ZO_)n>c8rRB3&JhiNw!#;&l9-GTn1wv5y$#v)%w?tBpV)j5JFxb!J$JZ)~)7R0c zvB(w37S*DDYcWgmMLqKF4~p4J#+!ZIW9PFTjOnC_ce3Q5_wU8>sCkg+cj??^cd`Z+ z498r%mmcrYqh2RXTfpMukykSJ;3ZHvJn8)FOhxFj#K7gRCe^0ADM|4pPghKYZtMUGGZ>Tf-K;k{kv_c zMV^O}Dn{y)EkYx-J}8B({VNWpkuqOW3T0h}EQJx*Ws7Lp`}DSvlpHe7jcO zFqd91m!?Q>EMd0?14qaaKq9oh;41|zWg~mSVZ*b3s3ImUWp|i>kl)v$%ft)Fa*lxr z_iSkExl<@Ye2%*=3L#`krJ^k7XjVGfuMely*WI?1z0OiSV${yU8-IGqxW5epvlLWC zw+F*=)c1@vbVKRPZ1t7iS;m&k123ogDiFWG@c~mcEL8c{$Y|rZ5Lf`&yKurTOh1Ez zL4$n=K0m@o@wUJwEm4q^9q}+ND8$jV+Kyic%&esf+_1~Rl4Ci`gvWYkIh&&IrgM5j z#~gaQpaG{xaL%79MU}ENrjaI>!i{RA#ieXq@@nuB?A_`3nT!4c8l}T@>8MX>r&^{@ z?{VgqPL#5d@YMnLu|{?9bcP}Qs0WkVE5f z_A)M;qhghHf?dS>~)*n~1G#>p;gKW}X2$oJg1gi*^v}@o) zlcbC_*hbo=57!_BYL^~b3-{F`J-3#vWwI2tjS-JiY6KGg6#K6Y>4 z#WF(5CLT26UA&Q-aMZ|IKQeO1>W$pa!$xjzGrBlt9_^7lZQELq;wDCE9qO1V2sMa4PID#61n_7F~xl zu_zWI4WT4hy1j{w=>-?nkyLXLi8JV;uUl$jk5cFrbCi{^-oU1xan9`)d<((YjCZ!u z(;u+q2*mw7zs8p1_ zwu|6!*d_h@Lv}0ccHeT0?O^me6gTvOA5PSZgyZZeta*EaP!Vm5jPP4uN z(e`h#+W%$bUIOs#Sk-`8fNg*lcWWDai<7r-;0#5?i z2k7&wk-H4gUNmxbfG?$WzhQ+XOS^w#cttFI@f*wR*{r&3dryj^A}_J(q%iWorY;;7 zLp|;rMuOk$O=`M|Z)%PUjdae{&?)!hOKdi{r2GyKmn;?ijxEAh>d}S0=K68tpIvMl z!#0wCnca)z?aS=Gu=hM2-tEXSbK^MP{sr}%{U__6P>&k;^A=Kg5VlNw8KKx-u()P) zPQ$DCi)LJj9d@t#lcn=4K-zr;2b%!*maFV@hAV`X{5m#j(=YrIIy2*Ze(*aSrPDO! zu~&(GDdYQ%Qjfq5_B>(4jY0e;Ep^^$k*B_g;VSla{M1HjV7ws?Y>RL{FIqoYDL=}p zCh;1oP_EstGXny=*tvV5sZ%SQ!{0(1Id2JMT| zE-l{&_03v70m%qBwZl?2&G~mHpyg3AH-*Z z?Dio3F~%aKPkZt1o)KVG{ZLlsbk&CUxlENGgnw+M2KsNgvMd#j5x?oR7gS`9nCrPm8(T z5ygAHH;I(4M)B3yr-f*KVXsz_V6u$ggPj(llcX!r{JCH<78vM!=iIiA*;v%>ju?I= zd7XK&{CFQeZL0Loc>Z2W!xQ*>N1|iL}3ZLeKunf}^E{#d$7x^y*F;-c*^lB>q(6!PU zX~-ac7TX~?2l0bo6K@aV7r_{!2J^#_;NbsxFY*gQmxHc9$$Uo);bT!%IE0^!I=)Oy<3~V!afkX`>i%asKauZ^u2u%%i(4pw=CC$4 zMB+#A?f!JVau*%Oe;mQb!?Jsi#0XQ|V@L91Sm1Q9qZO&%i#bQ}|14|!mglMgPXqYx zc<7FKpufd9zfM=rupBo9xG{j?fB}GTfG^-;JI~EQN`EK5=D9XNCxH8g z=K=vSfZ>2#fCaGl8~l!C6*B7q&jEG=8Ud|<9|8QocrFq!0^omv=Pq^d++i?0fwU3u zHeefI17HncDZm2A0}KH~0RjM*zvsE{0Ve^60j~fy09Jip#&gBUU=Xh(-3V9* zZEr5aV>@6Mpc(KDfcrPk1p{ILBLGtX^8qUW<$#TVt$%?oPz;y^m<9SFNTUF~0GCnz@n3ujcMh2k0B-|c2fP4y5>O7H zzlG>@48SbajOIfJCO8dT`#uBL^R$6`XbA!ZoaC?lJvcI#dvaVZcgksaB2FK4?f))9 ze5Siu4qu?@d6)g3rM7#Ptpv(F&&WS@mljcy*-yG$%l~8S{xb${>AqYpsXLb|dfLd% z_vW`a5UP6fqGaUqy?OslM$X{P_t|KaTI%>a-AOOztJ<7R)Jqz-C>(Y;gKu+Y0-#B~sjeKDDRWhjAGT8CM;_LvpEfKOf=qXqiD; zvY(Ik(Uf~L&pSLBKB7YUem@`Mv+OZ%&H5^Drq2d%ruzvmZDp;r{s14tXYZ0;p>)P> zPa6BS^!))oGW5m02JU45?roJNNXzybq~-g0{lGh*F0XBzmM&64|yoH{r49h2`K;T7peq(bv2le zYdyVo#vqM7!uRd>fovcm^{?ut{1(~Mp>AHsjaJJ#4AS!ryk6}i?uQ!{gMaY!;j`cu zubh8|8l_hc@X@-1`WqH?CCo_5=o?=b?m=D?r}`#ERP{4*)%}c~;jT_GN&?uhQ_?4g z`2Mjz?IUihhZ3pY$~F!-{3i-+-Ud#rE7t{Bw zH}me7URFsy(yd3ps!@~c8QBWoT#wNHJv!rMSE%WFI zFQ(>JZ>HC1Z)W_MTq*l7bn)mo52k2%UhcIC=KPRv`O@PF2>A#cYLXe{b2o64;3;MT z*ZS2BoD3I}HVy9SrnaTh)FwW3a2T8k2lAGi$cF=$1oAwvzxHPXZmaY}6W{0mZ<7Ci zll*^VlXOq~fDdKTx)1qqY0iiIE$)pU^3SobOKid-+vMd7?MoMpzh~*9DR z`JBI<-Xqc9wiNf1pYyl+;DcND$7lKV0Ryygx!nH_(z<{1VjQk^F8+_;9xn4A>;Dgv CK|Y@V delta 19301 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 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: [PATCH 1355/2594] 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 96ec381ec6..0d839b5c6e 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -10,7 +10,7 @@ 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 @@ def test_prune_file_list(self): 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() 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: [PATCH 1356/2594] 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 96ec381ec6..0d839b5c6e 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -10,7 +10,7 @@ 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 @@ def test_prune_file_list(self): 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() 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: [PATCH 1357/2594] 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 96ec381ec6..0d839b5c6e 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -10,7 +10,7 @@ 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 @@ def test_prune_file_list(self): 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() From 77931446b65e84f947756a3a32eecf0cf3a05fde Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Fri, 30 Jan 2009 04:00:29 +0000 Subject: [PATCH 1358/2594] 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 ac620fccb9..94895f4269 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 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: [PATCH 1359/2594] Fix comment for #1835 --- __init__.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/__init__.py b/__init__.py index 57fcf97602..e418214c48 100644 --- a/__init__.py +++ b/__init__.py @@ -12,13 +12,8 @@ # 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-- 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: [PATCH 1360/2594] 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 34fc008a58..1cf8a6ea56 100644 --- a/__init__.py +++ b/__init__.py @@ -12,13 +12,8 @@ # 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-- From a1f7b98d85ddc39f6473eec042ddb6e85e39433f Mon Sep 17 00:00:00 2001 From: Neil Schemenauer Date: Thu, 5 Feb 2009 16:14:39 +0000 Subject: [PATCH 1361/2594] 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 9993fba1ab..ec2f8a9c33 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]) From d1ea619952d81916af8fcfdb82e1961ae114e4e1 Mon Sep 17 00:00:00 2001 From: Neil Schemenauer Date: Thu, 5 Feb 2009 16:19:05 +0000 Subject: [PATCH 1362/2594] 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 aa1187e77b..397bb12cfc 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -19,27 +19,10 @@ def test_get_python_lib(self): # 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) From bf94d914167a979fe2bc878bb594335fe3aaeb9b Mon Sep 17 00:00:00 2001 From: Neil Schemenauer Date: Thu, 5 Feb 2009 16:25:16 +0000 Subject: [PATCH 1363/2594] 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 31b8b48185..b268820fa2 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -11,6 +11,10 @@ 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 @@ def setUp(self): 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 @@ def tearDown(self): 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) From 092c2c911cf324eb35fe10416fe35d2fdb8d9e1c Mon Sep 17 00:00:00 2001 From: Neil Schemenauer Date: Thu, 5 Feb 2009 16:33:41 +0000 Subject: [PATCH 1364/2594] 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 b17743a865..de8c5fccd6 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") From 79a5c6b29982615cff8c78b6ee045cdd3a26ac55 Mon Sep 17 00:00:00 2001 From: Neil Schemenauer Date: Thu, 5 Feb 2009 16:35:04 +0000 Subject: [PATCH 1365/2594] 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 c6ab9aa5d5..490410e126 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -19,27 +19,10 @@ def test_get_python_lib(self): # 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) 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: [PATCH 1366/2594] 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 fbf2a0b217..2ed3ef65cc 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -231,10 +231,12 @@ def finalize_options (self): # 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 b268820fa2..780660da85 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -67,6 +67,27 @@ def tearDown(self): # 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): 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: [PATCH 1367/2594] 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 1461409f60..936ea8d099 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -233,10 +233,12 @@ def finalize_options (self): # 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 31b8b48185..d0cce9ba1a 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -65,6 +65,27 @@ def tearDown(self): # 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: 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: [PATCH 1368/2594] 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 7ef5becc6f..b12da63f84 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -229,10 +229,12 @@ def finalize_options(self): # 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 527529777d..4c5723255a 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -75,6 +75,27 @@ def tearDown(self): # 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: 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: [PATCH 1369/2594] 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 54f5c68e28..6d896ee408 100644 --- a/dir_util.py +++ b/dir_util.py @@ -16,7 +16,7 @@ # 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 3af6344f11..82a8b0ad19 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 0000000000..edf93c40c5 --- /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 0000000000..523f1aef17 --- /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") 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: [PATCH 1370/2594] 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 1f0d49c25f..db754e5cec 100644 --- a/dir_util.py +++ b/dir_util.py @@ -15,7 +15,7 @@ # 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 b46b0da678..00c5bc5b8e 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 0000000000..edf93c40c5 --- /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 0000000000..523f1aef17 --- /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") 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: [PATCH 1371/2594] README now reflects the current state --- README | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/README b/README index 45c7ca8ca9..23f488506f 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$ 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: [PATCH 1372/2594] 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 6d896ee408..5a134408fd 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 82a8b0ad19..0c890559ee 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: 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: [PATCH 1373/2594] 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 45c7ca8ca9..23f488506f 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$ 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: [PATCH 1374/2594] 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 db754e5cec..ed54ccefd4 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 00c5bc5b8e..65aa7e0fdd 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: 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: [PATCH 1375/2594] 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 ec2f8a9c33..deb51a1c1a 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 397bb12cfc..7f0bce63c9 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -8,6 +8,13 @@ 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 @@ def test_get_config_vars(self): 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() 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: [PATCH 1376/2594] 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 de8c5fccd6..386ae89b83 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 490410e126..cb802c62db 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -8,6 +8,13 @@ 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 @@ def test_get_config_vars(self): 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() 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: [PATCH 1377/2594] Fixed #3987 : removed unused import --- core.py | 1 - 1 file changed, 1 deletion(-) diff --git a/core.py b/core.py index 0e85ca6f06..0fb6f81efb 100644 --- a/core.py +++ b/core.py @@ -9,7 +9,6 @@ __revision__ = "$Id$" import sys, os -from types import * from distutils.debug import DEBUG from distutils.errors import * 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: [PATCH 1378/2594] 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 267cf1802e..2d6cfb187b 100644 --- a/cmd.py +++ b/cmd.py @@ -7,8 +7,7 @@ __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 @@ def _ensure_stringlike (self, option, what, default=None): 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 @@ def ensure_string_list (self, option): 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 0000000000..19079c033f --- /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()) 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: [PATCH 1379/2594] 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 c6572caa5f..4669dc2d97 100644 --- a/cmd.py +++ b/cmd.py @@ -7,7 +7,7 @@ __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 0000000000..19079c033f --- /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()) 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: [PATCH 1380/2594] 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 7f0bce63c9..4636c0c127 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 @@ def test_get_config_vars(self): 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 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: [PATCH 1381/2594] 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 cb802c62db..af6f183097 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 @@ def test_get_config_vars(self): 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 From ecaef925f14658bfbfc9d7ad6df97ec35a7b0591 Mon Sep 17 00:00:00 2001 From: Neil Schemenauer Date: Fri, 6 Feb 2009 21:33:45 +0000 Subject: [PATCH 1382/2594] 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 deb51a1c1a..4e06546acd 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]) From a719c0dc5cddc1cab0881c42889ad3df0b0dfcbd Mon Sep 17 00:00:00 2001 From: Neil Schemenauer Date: Fri, 6 Feb 2009 21:42:05 +0000 Subject: [PATCH 1383/2594] 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 386ae89b83..a3ef3f6d88 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 4c5723255a..5e42943c38 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -15,6 +15,10 @@ # 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 @@ def setUp(self): 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 @@ def test_solaris_enable_shared(self): 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) 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: [PATCH 1384/2594] #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 2d6cfb187b..1351f445c3 100644 --- a/cmd.py +++ b/cmd.py @@ -6,7 +6,7 @@ __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 @@ def finalize_options (self): "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 @@ def make_archive (self, base_name, format, 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 @@ def make_file (self, infiles, outfile, func, args, 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 19079c033f..a252c355e7 100644 --- a/tests/test_cmd.py +++ b/tests/test_cmd.py @@ -5,23 +5,23 @@ 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 @@ def initialize_options(self): 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) 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: [PATCH 1385/2594] 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 4669dc2d97..295c914032 100644 --- a/cmd.py +++ b/cmd.py @@ -155,15 +155,15 @@ 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 = 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 @@ def make_file(self, infiles, outfile, func, args, 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 @@ def make_file(self, infiles, outfile, func, args, 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 19079c033f..a252c355e7 100644 --- a/tests/test_cmd.py +++ b/tests/test_cmd.py @@ -5,23 +5,23 @@ 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 @@ def initialize_options(self): 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) 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: [PATCH 1386/2594] 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 4e06546acd..56cb861a6c 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 4636c0c127..dd17eb347f 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -26,6 +26,8 @@ def test_get_python_lib(self): # 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() 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: [PATCH 1387/2594] 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 9993fba1ab..615da07e99 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 aa1187e77b..9f820575ab 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -17,6 +17,8 @@ def test_get_python_lib(self): # 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, 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: [PATCH 1388/2594] 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 a3ef3f6d88..fbeb45f8cc 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 af6f183097..1e9dbd542f 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -26,6 +26,8 @@ def test_get_python_lib(self): # 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() 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: [PATCH 1389/2594] 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 780660da85..1891e682bc 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) 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: [PATCH 1390/2594] 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 d0cce9ba1a..00630ab908 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) 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: [PATCH 1391/2594] 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 5e42943c38..aa5378c41a 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) 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: [PATCH 1392/2594] 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 1891e682bc..3c7d228da7 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 @@ 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 @@ def test_build_ext(self): finally: sys.stdout = old_stdout + if ALREADY_TESTED: + return + else: + ALREADY_TESTED = True + import xx for attr in ('error', 'foo', 'new', 'roj'): 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: [PATCH 1393/2594] 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 00630ab908..dbec65e8d1 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 @@ 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 @@ def setUp(self): 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 @@ def test_build_ext(self): finally: sys.stdout = old_stdout + if ALREADY_TESTED: + return + else: + ALREADY_TESTED = True + import xx for attr in ('error', 'foo', 'new', 'roj'): 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: [PATCH 1394/2594] 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 aa5378c41a..5e42943c38 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) 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: [PATCH 1395/2594] 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 0000000000..3505fe26e7 --- /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") 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: [PATCH 1396/2594] 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 0000000000..3505fe26e7 --- /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") 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: [PATCH 1397/2594] 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 48e06aa562..2ad4c7beb9 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -102,8 +102,8 @@ def copy_scripts (self): 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 666ca44c1d..2acfab828e 100644 --- a/tests/test_build_scripts.py +++ b/tests/test_build_scripts.py @@ -5,6 +5,7 @@ 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 @@ def write_script(self, dir, name, text): 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) 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: [PATCH 1398/2594] 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 104be0b349..453330fea8 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -104,8 +104,8 @@ def copy_scripts (self): 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 666ca44c1d..2acfab828e 100644 --- a/tests/test_build_scripts.py +++ b/tests/test_build_scripts.py @@ -5,6 +5,7 @@ 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 @@ def write_script(self, dir, name, text): 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) 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: [PATCH 1399/2594] 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 dc04a9fcdb..8b08bfeaf0 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -104,8 +104,8 @@ def copy_scripts(self): 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 666ca44c1d..2acfab828e 100644 --- a/tests/test_build_scripts.py +++ b/tests/test_build_scripts.py @@ -5,6 +5,7 @@ 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 @@ def write_script(self, dir, name, text): 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) 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: [PATCH 1400/2594] 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 3505fe26e7..ca586268a3 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -35,11 +35,12 @@ def setUp(self): 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 @@ def test_get_platform(self): ('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 @@ def test_change_root(self): 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') 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: [PATCH 1401/2594] 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 3505fe26e7..ca586268a3 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -35,11 +35,12 @@ def setUp(self): 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 @@ def test_get_platform(self): ('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 @@ def test_change_root(self): 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') 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: [PATCH 1402/2594] 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 475ceee598..6dfc82cd5e 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 @@ def tearDown(self): 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 3c7d228da7..a38558b007 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -7,6 +7,7 @@ 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 @@ 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') + 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 cae7689884..d81276928a 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 @@ def tearDown(self): 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 edf93c40c5..bf416b6d79 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -9,8 +9,9 @@ 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 @@ def _log(self, msg, *args): 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 bf59c41844..0e9868ae67 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -11,7 +11,7 @@ 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 @@ def find_config_files(self): 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 @@ def test_command_packages_configfile(self): 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 @@ def test_write_pkg_file(self): # 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 @@ def test_write_pkg_file(self): '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 @@ def _warn(msg): 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 @@ def test_custom_pydistutils(self): 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 @@ def test_custom_pydistutils(self): # 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 523f1aef17..9373af8503 100644 --- a/tests/test_file_util.py +++ b/tests/test_file_util.py @@ -5,8 +5,9 @@ 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 @@ def _log(self, msg, *args): 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 0d839b5c6e..b2e9800888 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -5,6 +5,7 @@ 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.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 @@ 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 @@ 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 - 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 @@ def test_prune_file_list(self): 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 @@ def test_make_distribution(self): 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 @@ def test_make_distribution(self): 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, 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: [PATCH 1403/2594] 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 0e9868ae67..847df7bb60 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -268,7 +268,7 @@ def test_custom_pydistutils(self): # 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)) 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: [PATCH 1404/2594] 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 91e704cfcc..48bcbccfc5 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 @@ def tearDown(self): 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 5e42943c38..91b3de85d2 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -7,6 +7,7 @@ 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 @@ 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 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 bdc9b2b539..0df6af8412 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 @@ def tearDown(self): 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 edf93c40c5..bf416b6d79 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -9,8 +9,9 @@ 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 @@ def _log(self, msg, *args): 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 8e7ef5d33c..3ac0fdd1e1 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -9,6 +9,7 @@ import warnings from test.support import TESTFN +from distutils.tests import support class test_dist(distutils.cmd.Command): @@ -120,7 +121,7 @@ def _warn(msg): 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 @@ def test_custom_pydistutils(self): 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 @@ def test_custom_pydistutils(self): # 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 523f1aef17..9373af8503 100644 --- a/tests/test_file_util.py +++ b/tests/test_file_util.py @@ -5,8 +5,9 @@ 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 @@ def _log(self, msg, *args): 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 0d839b5c6e..b2e9800888 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -5,6 +5,7 @@ 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.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 @@ 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 @@ 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 - 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 @@ def test_prune_file_list(self): 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 @@ def test_make_distribution(self): 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 @@ def test_make_distribution(self): 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, From 348e5075053db429b26d72a22fa7e671dd4935d0 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Mon, 16 Feb 2009 18:22:15 +0000 Subject: [PATCH 1405/2594] remove another use of cmp() --- version.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/version.py b/version.py index 77814377a5..79d458d847 100644 --- a/version.py +++ b/version.py @@ -338,7 +338,12 @@ def _cmp (self, other): 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 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: [PATCH 1406/2594] 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 e8b6bce9c9..291e8123b8 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -259,6 +259,9 @@ def add_defaults (self): - 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 @@ def add_defaults (self): 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 6dfc82cd5e..fbbf35d8ea 100644 --- a/tests/support.py +++ b/tests/support.py @@ -42,6 +42,19 @@ def mkdtemp(self): 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 b2e9800888..de588112fd 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -12,6 +12,7 @@ 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 @@ 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 @@ def setUp(self): 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 @@ def test_prune_file_list(self): # 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 @@ def test_make_distribution(self): 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 @@ def test_make_distribution(self): # 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 @@ def test_make_distribution(self): 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) 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: [PATCH 1407/2594] #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 de588112fd..b4e922f90c 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -24,11 +24,11 @@ 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 @@ def test_add_defaults(self): # 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) 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: [PATCH 1408/2594] 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 054fe191d4..c057b667e8 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -252,6 +252,9 @@ def add_defaults(self): - 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 @@ def add_defaults(self): 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 48bcbccfc5..ecc8da174e 100644 --- a/tests/support.py +++ b/tests/support.py @@ -42,6 +42,19 @@ def mkdtemp(self): 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 b2e9800888..de588112fd 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -12,6 +12,7 @@ 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 @@ 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 @@ def setUp(self): 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 @@ def test_prune_file_list(self): # 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 @@ def test_make_distribution(self): 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 @@ def test_make_distribution(self): # 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 @@ def test_make_distribution(self): 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) 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: [PATCH 1409/2594] 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 de588112fd..b4e922f90c 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -24,11 +24,11 @@ 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 @@ def test_add_defaults(self): # 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) 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: [PATCH 1410/2594] #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 291e8123b8..a1a0fb799d 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -14,7 +14,7 @@ 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 @@ def add_defaults (self): # 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 b4e922f90c..82e5dc6db6 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -26,6 +26,8 @@ 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 @@ def test_add_defaults(self): 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 @@ def test_add_defaults(self): self.write_file((script_dir, 'script.py'), '#') dist.scripts = [join('scripts', 'script.py')] - cmd.formats = ['zip'] cmd.use_defaults = True @@ -194,7 +202,7 @@ def test_add_defaults(self): 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() 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: [PATCH 1411/2594] 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 c057b667e8..9bb2ae04e0 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -15,7 +15,7 @@ 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 @@ def add_defaults(self): # 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 b4e922f90c..82e5dc6db6 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -26,6 +26,8 @@ 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 @@ def test_add_defaults(self): 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 @@ def test_add_defaults(self): self.write_file((script_dir, 'script.py'), '#') dist.scripts = [join('scripts', 'script.py')] - cmd.formats = ['zip'] cmd.use_defaults = True @@ -194,7 +202,7 @@ def test_add_defaults(self): 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() 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: [PATCH 1412/2594] 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 a1a0fb799d..a9ce28a7eb 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -319,7 +319,7 @@ def add_defaults (self): 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 82e5dc6db6..9c579b40cb 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -23,6 +23,7 @@ MANIFEST = """\ README +inroot.txt setup.py data%(sep)sdata.dt scripts%(sep)sscript.py @@ -171,10 +172,13 @@ def test_add_defaults(self): 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 @@ def test_add_defaults(self): 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() 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: [PATCH 1413/2594] 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 9bb2ae04e0..1a64d0e3c1 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -311,7 +311,7 @@ def add_defaults(self): 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 82e5dc6db6..9c579b40cb 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -23,6 +23,7 @@ MANIFEST = """\ README +inroot.txt setup.py data%(sep)sdata.dt scripts%(sep)sscript.py @@ -171,10 +172,13 @@ def test_add_defaults(self): 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 @@ def test_add_defaults(self): 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() From 9f19d45baca8e8371e5706a41dfe3d6352f7a2dc Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Sat, 21 Feb 2009 20:27:01 +0000 Subject: [PATCH 1414/2594] 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 55d2d5df7e..e481e8de39 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -57,7 +57,7 @@ def test_run_setup_provides_file(self): 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() From 763718d5ed62b5076d9c022846e703eafa932a2f Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Sat, 21 Feb 2009 20:59:32 +0000 Subject: [PATCH 1415/2594] 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 170d76751e..7f021dcb3b 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -57,7 +57,7 @@ def test_run_setup_provides_file(self): 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() 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: [PATCH 1416/2594] 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 0000000000..8f4f7e83c0 --- /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 ff2878de1b..931f0bac19 100644 --- a/text_file.py +++ b/text_file.py @@ -303,80 +303,3 @@ def unreadline (self, line): 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) 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: [PATCH 1417/2594] 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 0000000000..00f083a130 --- /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 266466c1eb..97459fbf73 100644 --- a/text_file.py +++ b/text_file.py @@ -282,80 +282,3 @@ def unreadline(self, line): 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) 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: [PATCH 1418/2594] 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 8f4f7e83c0..00f083a130 100644 --- a/tests/test_text_file.py +++ b/tests/test_text_file.py @@ -18,8 +18,9 @@ def test_class(self): # 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", 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: [PATCH 1419/2594] Removing unused __main__ sections --- cmd.py | 4 ---- dist.py | 5 ----- 2 files changed, 9 deletions(-) diff --git a/cmd.py b/cmd.py index 1351f445c3..012fca15b5 100644 --- a/cmd.py +++ b/cmd.py @@ -474,7 +474,3 @@ def _copy_files (self, filelist): def get_outputs (self): return self.outfiles - - -if __name__ == "__main__": - print "ok" diff --git a/dist.py b/dist.py index 3dd776ccd2..2d57ad09e9 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" 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: [PATCH 1420/2594] 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 295c914032..800425d4a9 100644 --- a/cmd.py +++ b/cmd.py @@ -436,7 +436,3 @@ def _copy_files (self, filelist): def get_outputs (self): return self.outfiles - - -if __name__ == "__main__": - print("ok") diff --git a/dist.py b/dist.py index 6c4b4afbd1..7c30e8856f 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") 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: [PATCH 1421/2594] 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 0000000000..2863b620db --- /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 0000000000..747db94804 --- /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") 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: [PATCH 1422/2594] 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 0000000000..2863b620db --- /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 0000000000..fa21433046 --- /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") 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: [PATCH 1423/2594] 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 d81276928a..a18f45359a 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 @@ def tearDown(self): 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 9c579b40cb..15a8c8087c 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -37,10 +37,9 @@ 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 @@ def setUp(self): 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""" 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: [PATCH 1424/2594] 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 0df6af8412..09abfcd8ba 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 @@ def tearDown(self): 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 9c579b40cb..15a8c8087c 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -37,10 +37,9 @@ 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 @@ def setUp(self): 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""" 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: [PATCH 1425/2594] 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 2ed3ef65cc..ac0a0670a1 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -8,7 +8,7 @@ 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 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: [PATCH 1426/2594] 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 b12da63f84..1d5a75d2c5 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -7,7 +7,7 @@ __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 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: [PATCH 1427/2594] 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 23f488506f..408a203b85 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 ac0a0670a1..125fa7f52d 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -8,7 +8,6 @@ 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.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 adc9daf11a..4fd66cbf60 100644 --- a/command/install.py +++ b/command/install.py @@ -16,9 +16,16 @@ 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 @@ '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 @@ '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 @@ '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 @@ def finalize_options (self): '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 e30347e507..df82e4ca56 100644 --- a/command/upload.py +++ b/command/upload.py @@ -6,7 +6,7 @@ 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 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 a38558b007..ff60c12b1a 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 @@ def tearDown(self): # 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 @@ def test_solaris_enable_shared(self): # 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 c834b91b38..9ceccf68cf 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 @@ def check_path(got, expected): 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) 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: [PATCH 1428/2594] 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 23f488506f..408a203b85 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 1d5a75d2c5..1ed69f33c0 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -7,7 +7,6 @@ __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.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 b5c9554d61..de81173387 100644 --- a/command/install.py +++ b/command/install.py @@ -15,9 +15,16 @@ 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 @@ '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 @@ '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 @@ '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 @@ def finalize_options(self): '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 020e860a6d..eb2f51678a 100644 --- a/command/upload.py +++ b/command/upload.py @@ -6,7 +6,7 @@ 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 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 91b3de85d2..2e47953ee3 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 @@ def tearDown(self): # 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 @@ def test_solaris_enable_shared(self): # 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 c834b91b38..9ceccf68cf 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 @@ def check_path(got, expected): 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) 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: [PATCH 1429/2594] 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 a48e0f186f..34822ec70a 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 @@ def initialize_options (self): self.no_autoreq = 0 self.force_arch = None + self.quiet = 0 + self.force_optimize = 1 # initialize_options() @@ -322,6 +335,7 @@ def run (self): 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 @@ def run (self): '_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 @@ def _make_spec_file(self): # 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 0000000000..ff40628f42 --- /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()) 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: [PATCH 1430/2594] 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 83efb8e043..3afdafefda 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 @@ def initialize_options(self): 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 @@ def run(self): 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 @@ def run(self): '_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 @@ def _make_spec_file(self): # 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 0000000000..ff40628f42 --- /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()) 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: [PATCH 1431/2594] 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 34822ec70a..1eccc6a48a 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 @@ def initialize_options (self): self.force_arch = None self.quiet = 0 - self.force_optimize = 1 # initialize_options() @@ -504,14 +495,8 @@ def _make_spec_file(self): # 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 ff40628f42..2d84007f87 100644 --- a/tests/test_bdist_rpm.py +++ b/tests/test_bdist_rpm.py @@ -109,8 +109,6 @@ def test_no_optimize_flag(self): 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 @@ def test_no_optimize_flag(self): 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) 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: [PATCH 1432/2594] 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 3afdafefda..452f9502ad 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 @@ def initialize_options(self): 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 @@ def _make_spec_file(self): # 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 ff40628f42..2d84007f87 100644 --- a/tests/test_bdist_rpm.py +++ b/tests/test_bdist_rpm.py @@ -109,8 +109,6 @@ def test_no_optimize_flag(self): 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 @@ def test_no_optimize_flag(self): 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) 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: [PATCH 1433/2594] 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 68b7775830..00d76d4bea 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -17,10 +17,11 @@ 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 @@ def read_keys(cls, base, key): 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 @@ def read_values(cls, base, key): 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 @@ def convert_mbcs(s): except UnicodeError: pass return s + convert_mbcs = staticmethod(convert_mbcs) class MacroExpander: @@ -131,7 +132,7 @@ def load_macros(self, version): "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 @@ def compile(self, sources, 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 @@ def compile(self, sources, self.spawn([self.rc] + ["/fo" + obj] + [rc_file]) - except DistutilsExecError as msg: + except DistutilsExecError, msg: raise CompileError(msg) continue else: @@ -519,7 +520,7 @@ def compile(self, sources, 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 @@ def create_static_lib(self, 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 @@ def link(self, 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 @@ def link(self, # 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 0c8bd6e042..bde614e9a4 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -30,6 +30,28 @@ def _find_vcvarsall(version): 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) 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: [PATCH 1434/2594] 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 aae0637394..48551ddf51 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -17,10 +17,11 @@ 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 @@ def read_keys(cls, base, key): 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 @@ def read_values(cls, base, key): 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 @@ def convert_mbcs(s): except UnicodeError: pass return s + convert_mbcs = staticmethod(convert_mbcs) class MacroExpander: @@ -131,7 +132,7 @@ def load_macros(self, version): "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 @@ def compile(self, sources, 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 @@ def compile(self, sources, self.spawn([self.rc] + ["/fo" + obj] + [rc_file]) - except DistutilsExecError as msg: + except DistutilsExecError, msg: raise CompileError(msg) continue else: @@ -518,7 +519,7 @@ def compile(self, sources, 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 @@ def create_static_lib(self, 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 @@ def link(self, 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 @@ def link(self, # 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 0c8bd6e042..e7d1620fdd 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -30,6 +30,28 @@ def _find_vcvarsall(version): 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) 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: [PATCH 1435/2594] fixed except syntax for py3 --- msvc9compiler.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/msvc9compiler.py b/msvc9compiler.py index 48551ddf51..ef895422c6 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -479,7 +479,7 @@ def compile(self, sources, 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 @@ def compile(self, sources, self.spawn([self.rc] + ["/fo" + obj] + [rc_file]) - except DistutilsExecError, msg: + except DistutilsExecError as msg: raise CompileError(msg) continue else: @@ -519,7 +519,7 @@ def compile(self, sources, 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 @@ def create_static_lib(self, 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 @@ def link(self, 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 @@ def link(self, 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) From 2d5c28e86921686607c1f6d9e67a2eaddbefb447 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 7 Mar 2009 16:34:40 +0000 Subject: [PATCH 1436/2594] bump version to 3.1a1 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 1cf8a6ea56..12a430ecdd 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.1a0" +__version__ = "3.1a1" #--end constants-- 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: [PATCH 1437/2594] 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 ca586268a3..67474025d3 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -53,6 +53,8 @@ def tearDown(self): 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 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: [PATCH 1438/2594] 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 ca586268a3..67474025d3 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -53,6 +53,8 @@ def tearDown(self): 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 From 08c27fd41a67693d49c78ecbb512d3db78445a51 Mon Sep 17 00:00:00 2001 From: Hirokazu Yamamoto Date: Sun, 15 Mar 2009 22:43:14 +0000 Subject: [PATCH 1439/2594] 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 bde614e9a4..78ce3b7a84 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -34,6 +34,10 @@ 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 + return from distutils.msvc9compiler import Reg self.assertRaises(KeyError, Reg.get_value, 'xxx', 'xxx') From 1f49eec84ddf546ae1b2c4ec7d28e611cc4c9d80 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 21 Mar 2009 17:31:58 +0000 Subject: [PATCH 1440/2594] 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 e7d1620fdd..aefadd6dc3 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -34,6 +34,10 @@ 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 + return from distutils.msvc9compiler import Reg self.assertRaises(KeyError, Reg.get_value, 'xxx', 'xxx') From 48a187444772146188fbfb1ffb5483fc03a84e87 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 28 Mar 2009 00:48:48 +0000 Subject: [PATCH 1441/2594] Fix typo. --- version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.py b/version.py index 71a5614719..0fb5b6e204 100644 --- a/version.py +++ b/version.py @@ -162,7 +162,7 @@ def __cmp__ (self, other): # 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 From faad81c0beb9849650bd8673266556b2438bb73d Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Mon, 30 Mar 2009 14:51:56 +0000 Subject: [PATCH 1442/2594] 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 79d458d847..ebcab84e4e 100644 --- a/version.py +++ b/version.py @@ -207,7 +207,7 @@ def _cmp (self, other): # 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 From 31b701a80d234c9782272d4ce02f38b8c365e119 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 31 Mar 2009 00:34:54 +0000 Subject: [PATCH 1443/2594] 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 042306e913..4c1956a2b8 100644 --- a/util.py +++ b/util.py @@ -571,6 +571,39 @@ def log_debug(self, msg, *args): 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 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: [PATCH 1444/2594] 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 012fca15b5..dfcbe23537 100644 --- a/cmd.py +++ b/cmd.py @@ -352,9 +352,8 @@ def get_sub_commands (self): # -- 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 fcaa545a79..6f949d5179 100644 --- a/log.py +++ b/log.py @@ -18,13 +18,14 @@ def __init__(self, threshold=WARN): 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) 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: [PATCH 1445/2594] 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 ba03d1fff6..90ef35f1ca 100644 --- a/command/clean.py +++ b/command/clean.py @@ -11,7 +11,7 @@ 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 fbbf35d8ea..578cf40c09 100644 --- a/tests/support.py +++ b/tests/support.py @@ -4,7 +4,7 @@ import tempfile from distutils import log - +from distutils.core import Distribution class LoggingSilencer(object): @@ -42,7 +42,7 @@ def mkdtemp(self): 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 @@ def write_file(self, path, 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().""" diff --git a/tests/test_clean.py b/tests/test_clean.py new file mode 100755 index 0000000000..a94a812b1f --- /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") 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: [PATCH 1446/2594] 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 40661d8776..f3463dc289 100644 --- a/command/register.py +++ b/command/register.py @@ -90,14 +90,14 @@ def classifiers(self): ''' 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 @@ def send_metadata(self): 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 @@ def build_post_data(self, action): 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 @@ def post_to_server(self, data, auth=None): 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 b3543f50f8..7da8edb5d7 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -3,7 +3,9 @@ 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 @@ def __call__(self, prompt=''): 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 @@ def setUp(self): 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 @@ def test_create_pypirc(self): # 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 @@ def _no_way(prompt=''): 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) 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: [PATCH 1447/2594] 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 df82e4ca56..74b06283f5 100644 --- a/command/upload.py +++ b/command/upload.py @@ -192,4 +192,4 @@ def upload_file(self, command, pyversion, filename): 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 3f8ca6d6ad..f57a4a3f33 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 @@ [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 @@ def test_finalize_options(self): 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 @@ def test_saved_password(self): 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) 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: [PATCH 1448/2594] 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 cb11371023..ec78ce3431 100644 --- a/command/install_data.py +++ b/command/install_data.py @@ -12,7 +12,7 @@ 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 @@ def finalize_options (self): ('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 @@ def run (self): (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 0000000000..73c40371d6 --- /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") 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: [PATCH 1449/2594] 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 c25b771246..d892416a8c 100644 --- a/command/install_headers.py +++ b/command/install_headers.py @@ -8,7 +8,8 @@ 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 @@ def run (self): (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 0000000000..2564563faf --- /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") 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: [PATCH 1450/2594] 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 a18f45359a..7737538bf8 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -47,7 +47,9 @@ """ -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 15a8c8087c..8af080f950 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -34,7 +34,7 @@ somecode%(sep)sdoc.txt """ -class sdistTestCase(support.LoggingSilencer, PyPIRCCommandTestCase): +class sdistTestCase(PyPIRCCommandTestCase): def setUp(self): # PyPIRCCommandTestCase creates a temp dir already 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: [PATCH 1451/2594] 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 800425d4a9..46055b4b2c 100644 --- a/cmd.py +++ b/cmd.py @@ -333,7 +333,7 @@ def get_sub_commands(self): # -- 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 06a70b4ad4..ab40797b98 100644 --- a/command/install_data.py +++ b/command/install_data.py @@ -31,7 +31,6 @@ def initialize_options(self): 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 346daaad02..38125b5513 100644 --- a/command/install_headers.py +++ b/command/install_headers.py @@ -8,6 +8,7 @@ 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 c271b18c4c..7dd3099912 100644 --- a/command/register.py +++ b/command/register.py @@ -92,15 +92,14 @@ def classifiers(self): ''' 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 @@ def send_metadata(self): 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 @@ def build_post_data(self, action): 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 @@ def post_to_server(self, data, auth=None): 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 eb2f51678a..9249427a43 100644 --- a/command/upload.py +++ b/command/upload.py @@ -194,4 +194,4 @@ def upload_file(self, command, pyversion, filename): 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 ecc8da174e..ab2af9a6aa 100644 --- a/tests/support.py +++ b/tests/support.py @@ -4,7 +4,7 @@ import tempfile from distutils import log - +from distutils.core import Distribution class LoggingSilencer(object): @@ -42,7 +42,7 @@ def mkdtemp(self): 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 @@ def write_file(self, path, 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().""" diff --git a/tests/test_clean.py b/tests/test_clean.py new file mode 100755 index 0000000000..a94a812b1f --- /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 09abfcd8ba..7506f93227 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -46,7 +46,9 @@ """ -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 0000000000..73c40371d6 --- /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 0000000000..2564563faf --- /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 8826e90fe0..46f7761d80 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -3,7 +3,9 @@ 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 @@ def __call__(self, prompt=''): 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 @@ def setUp(self): 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 @@ def test_create_pypirc(self): # 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 @@ def test_create_pypirc(self): # 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 15a8c8087c..8af080f950 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -34,7 +34,7 @@ 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 3f8ca6d6ad..95e4ac3315 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 @@ [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 @@ def test_finalize_options(self): 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 @@ def test_saved_password(self): 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) 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: [PATCH 1452/2594] #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 125fa7f52d..905fa1ff7d 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -476,7 +476,13 @@ def build_extensions(self): 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 440d128cdc..c80c61e35b 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 @@ def __init__ (self, name, sources, 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 @@ def __init__ (self, name, sources, 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 ff60c12b1a..a27696d8cb 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -8,6 +8,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 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 @@ def test_user_site(self): 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): 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: [PATCH 1453/2594] 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 1ed69f33c0..229590664c 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -455,7 +455,13 @@ def build_extensions(self): 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 b271816844..f7e7b4edc9 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 @@ def __init__(self, name, sources, 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 @@ def __init__(self, name, sources, 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 2e47953ee3..094f4b6644 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -8,6 +8,9 @@ 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 @@ def test_user_site(self): 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): 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: [PATCH 1454/2594] 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 905fa1ff7d..2c6df1d2cd 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -478,7 +478,7 @@ def build_extensions(self): 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' % 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: [PATCH 1455/2594] 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 a27696d8cb..9097bee2e4 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -10,6 +10,7 @@ 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 @@ def test_optional_extension(self): 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}) 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: [PATCH 1456/2594] 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 229590664c..ade95be229 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -457,7 +457,7 @@ def build_extensions(self): 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 094f4b6644..5ea67bedb7 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -11,6 +11,7 @@ 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 @@ def test_optional_extension(self): 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}) From 0c34a6afb92b1530da9c19f1b96b2371aa6ff606 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Wed, 1 Apr 2009 04:28:33 +0000 Subject: [PATCH 1457/2594] #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 aefadd6dc3..11e5a47630 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -48,8 +48,8 @@ def test_reg_class(self): 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) From 865250689f39b67167b128293e9fb3ef3d0aad3e Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Wed, 1 Apr 2009 04:32:39 +0000 Subject: [PATCH 1458/2594] #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 add83f8740..274cb010b8 100644 --- a/command/__init__.py +++ b/command/__init__.py @@ -22,6 +22,8 @@ 'bdist_dumb', 'bdist_rpm', 'bdist_wininst', + 'upload', + # These two are reserved for future use: #'bdist_sdux', #'bdist_pkgtool', From 1b3f5a086d20cde5ddb2e399336974aac4291c8d Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 4 Apr 2009 21:06:52 +0000 Subject: [PATCH 1459/2594] bump version to 3.1a2 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 12a430ecdd..c66806361a 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.1a1" +__version__ = "3.1a2" #--end constants-- 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: [PATCH 1460/2594] 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 a94a812b1f..3026032712 100755 --- a/tests/test_clean.py +++ b/tests/test_clean.py @@ -8,6 +8,7 @@ from distutils.tests import support class cleanTestCase(support.TempdirManager, + support.LoggingSilencer, unittest.TestCase): def test_simple_run(self): 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: [PATCH 1461/2594] 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 a94a812b1f..3026032712 100755 --- a/tests/test_clean.py +++ b/tests/test_clean.py @@ -8,6 +8,7 @@ from distutils.tests import support class cleanTestCase(support.TempdirManager, + support.LoggingSilencer, unittest.TestCase): def test_simple_run(self): From 7a3178f5a9438241874c2cbee10b89c43831e2d8 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sun, 5 Apr 2009 19:13:16 +0000 Subject: [PATCH 1462/2594] 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 46055b4b2c..5829a56639 100644 --- a/cmd.py +++ b/cmd.py @@ -333,7 +333,8 @@ def get_sub_commands(self): # -- 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 97319a07aa..6f949d5179 100644 --- a/log.py +++ b/log.py @@ -18,13 +18,14 @@ def __init__(self, threshold=WARN): 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) From ba7be325ab789b28c9670887397c6924d42a8cf9 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 5 Apr 2009 21:11:43 +0000 Subject: [PATCH 1463/2594] 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 71a5614719..0fb5b6e204 100644 --- a/version.py +++ b/version.py @@ -162,7 +162,7 @@ def __cmp__ (self, other): # 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 From 443bc8fdb3ffe7aded9326275131e10a7bc6286b Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 5 Apr 2009 21:21:05 +0000 Subject: [PATCH 1464/2594] 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 0888c2712b..05c758a2aa 100644 --- a/command/__init__.py +++ b/command/__init__.py @@ -24,6 +24,8 @@ 'bdist_dumb', 'bdist_rpm', 'bdist_wininst', + 'upload', + # These two are reserved for future use: #'bdist_sdux', #'bdist_pkgtool', 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: [PATCH 1465/2594] 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 51a1d57e13..3ba5720ca9 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: [PATCH 1466/2594] 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 6d27cce64f..3ce5635047 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: [PATCH 1467/2594] 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 a80c71e8c7..58a2bfb108 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: [PATCH 1468/2594] 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 0000000000..33e8bcf6e9 --- /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") 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: [PATCH 1469/2594] 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 3df5ef4c42..cb7598df70 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -12,7 +12,7 @@ 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 @@ def initialize_options (self): 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 @@ def finalize_options (self): 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 @@ def run (self): 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 0000000000..be3ec74976 --- /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()) 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: [PATCH 1470/2594] 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 e3b047c66f..1a360b59e3 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 0000000000..f2849a9756 --- /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()) 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: [PATCH 1471/2594] 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 cb7598df70..ec3375e5c4 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 @@ def finalize_options (self): 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: From 67406893c1b9b727f313a374affe9868ec986fa6 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Tue, 7 Apr 2009 01:54:02 +0000 Subject: [PATCH 1472/2594] Bump to 2.6.2c1. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 8f7eb8427d..e8f55cf087 100644 --- a/__init__.py +++ b/__init__.py @@ -22,5 +22,5 @@ # #--start constants-- -__version__ = "2.6.1" +__version__ = "2.6.2c1" #--end constants-- 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: [PATCH 1473/2594] 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 ad6eee8ea8..8a6eca59d8 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -342,10 +342,15 @@ def get_exe_bytes (self): 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 0000000000..d851d6c799 --- /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()) 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: [PATCH 1474/2594] 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 e997e8f094..d6d01c630d 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -330,9 +330,14 @@ def get_exe_bytes(self): 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 0000000000..d851d6c799 --- /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()) 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: [PATCH 1475/2594] 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 f18e318cb9..d153e2bc38 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -344,10 +344,15 @@ def get_exe_bytes (self): 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 0000000000..de6601fbb7 --- /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()) 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: [PATCH 1476/2594] #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 274cb010b8..20b159f74e 100644 --- a/command/__init__.py +++ b/command/__init__.py @@ -23,7 +23,7 @@ '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 0000000000..c72914952e --- /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 0000000000..443fa35baf --- /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") 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: [PATCH 1477/2594] 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 add83f8740..f7dcde48ed 100644 --- a/command/__init__.py +++ b/command/__init__.py @@ -22,6 +22,7 @@ '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 0000000000..c72914952e --- /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 0000000000..443fa35baf --- /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") 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: [PATCH 1478/2594] 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 c72914952e..d164f3f527 100644 --- a/command/check.py +++ b/command/check.py @@ -65,7 +65,7 @@ def run(self): 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 443fa35baf..5e0c453140 100644 --- a/tests/test_check.py +++ b/tests/test_check.py @@ -85,6 +85,13 @@ def test_check_restructuredtext(self): 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) 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: [PATCH 1479/2594] 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 c72914952e..d164f3f527 100644 --- a/command/check.py +++ b/command/check.py @@ -65,7 +65,7 @@ def run(self): 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 443fa35baf..5e0c453140 100644 --- a/tests/test_check.py +++ b/tests/test_check.py @@ -85,6 +85,13 @@ def test_check_restructuredtext(self): 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) From c22d66c04f50a075bb19cae0a0ebcc3f8ade5a14 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 11 Apr 2009 20:45:40 +0000 Subject: [PATCH 1480/2594] 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 f7dcde48ed..c379edbed0 100644 --- a/command/__init__.py +++ b/command/__init__.py @@ -23,6 +23,7 @@ '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 d164f3f527..5dd73b07af 100644 --- a/command/check.py +++ b/command/check.py @@ -27,8 +27,9 @@ def system_message(self, level, message, *children, **kwargs): 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 0000000000..33e8bcf6e9 --- /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") 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: [PATCH 1481/2594] 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 6b358cf250..c36456ad6a 100644 --- a/command/config.py +++ b/command/config.py @@ -354,13 +354,17 @@ def check_header (self, header, include_dirs=None, # 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 0000000000..6fd1776668 --- /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") 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: [PATCH 1482/2594] 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 34f91884d8..144c5132b3 100644 --- a/command/config.py +++ b/command/config.py @@ -336,11 +336,16 @@ def check_header(self, header, include_dirs=None, library_dirs=None, 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 0000000000..6fd1776668 --- /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") 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: [PATCH 1483/2594] 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 c36456ad6a..74eed78391 100644 --- a/command/config.py +++ b/command/config.py @@ -18,10 +18,9 @@ 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 @@ def initialize_options (self): # 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 @@ def run (self): # 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 @@ def _check_compiler (self): 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 @@ def _gen_temp_sourcefile (self, body, headers, lang): 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 @@ def _compile (self, body, headers, include_dirs, lang): 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 @@ def _link (self, body, 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 @@ def _clean (self, *filenames): # 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 @@ def try_cpp (self, body=None, headers=None, include_dirs=None, lang="c"): 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 @@ def search_cpp (self, pattern, body=None, 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 @@ def try_compile (self, body, headers=None, include_dirs=None, lang="c"): 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 @@ def try_link (self, body, 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 @@ def try_run (self, body, # (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 @@ def check_func (self, func, # 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 @@ def check_lib (self, library, library_dirs=None, 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 @@ def check_header (self, header, include_dirs=None, include_dirs=include_dirs) -# class config - def dump_file(filename, head=None): """Dumps a file content into log.info. 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: [PATCH 1484/2594] 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 144c5132b3..385a47a726 100644 --- a/command/config.py +++ b/command/config.py @@ -53,8 +53,6 @@ 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 @@ -136,8 +134,8 @@ def _compile(self, body, headers, include_dirs, lang): 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 @@ def try_cpp(self, body=None, headers=None, include_dirs=None, lang="c"): 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 @@ def try_compile(self, body, headers=None, include_dirs=None, lang="c"): 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 @@ def try_link(self, body, headers=None, include_dirs=None, 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. 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: [PATCH 1485/2594] 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 74eed78391..43a8dc8439 100644 --- a/command/config.py +++ b/command/config.py @@ -202,11 +202,10 @@ def search_cpp(self, pattern, body=None, headers=None, include_dirs=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) + 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 6fd1776668..af16d4c821 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 @@ def test_dump_file(self): 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) 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: [PATCH 1486/2594] 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 385a47a726..c31220553c 100644 --- a/command/config.py +++ b/command/config.py @@ -198,9 +198,8 @@ def search_cpp(self, pattern, body=None, headers=None, include_dirs=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) + 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 6fd1776668..af16d4c821 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 @@ def test_dump_file(self): 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) 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: [PATCH 1487/2594] 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 43a8dc8439..f2ebe37dc9 100644 --- a/command/config.py +++ b/command/config.py @@ -12,7 +12,7 @@ __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 @@ def initialize_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: - 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 af16d4c821..45d480ba5a 100644 --- a/tests/test_config_cmd.py +++ b/tests/test_config_cmd.py @@ -46,6 +46,21 @@ def test_search_cpp(self): 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) 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: [PATCH 1488/2594] 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 c31220553c..ac80a54eb1 100644 --- a/command/config.py +++ b/command/config.py @@ -12,6 +12,7 @@ __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 af16d4c821..45d480ba5a 100644 --- a/tests/test_config_cmd.py +++ b/tests/test_config_cmd.py @@ -46,6 +46,21 @@ def test_search_cpp(self): 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) 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: [PATCH 1489/2594] 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 f2ebe37dc9..134fa3892d 100644 --- a/command/config.py +++ b/command/config.py @@ -11,7 +11,7 @@ __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 @@ def finalize_options(self): 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 @@ def _clean(self, *filenames): 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 @@ def check_func(self, func, headers=None, include_dirs=None, 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 45d480ba5a..f5c09e4f44 100644 --- a/tests/test_config_cmd.py +++ b/tests/test_config_cmd.py @@ -60,6 +60,24 @@ def test_finalize_options(self): 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) 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: [PATCH 1490/2594] 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 45d480ba5a..f5c09e4f44 100644 --- a/tests/test_config_cmd.py +++ b/tests/test_config_cmd.py @@ -60,6 +60,24 @@ def test_finalize_options(self): 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) 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: [PATCH 1491/2594] 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 67474025d3..f27c452404 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 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 @@ def setUp(self): 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 @@ def setUp(self): 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 @@ def tearDown(self): 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 @@ def test_get_platform(self): '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) ' 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: [PATCH 1492/2594] 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 67474025d3..f27c452404 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 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 @@ def setUp(self): 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 @@ def setUp(self): 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 @@ def tearDown(self): 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 @@ def test_get_platform(self): '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) ' 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: [PATCH 1493/2594] 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 f5c09e4f44..1e6ebfacf5 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 @@ def test_dump_file(self): 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) 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: [PATCH 1494/2594] 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 f5c09e4f44..1e6ebfacf5 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 @@ def test_dump_file(self): 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) 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: [PATCH 1495/2594] 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 dfcbe23537..0e56460036 100644 --- a/cmd.py +++ b/cmd.py @@ -214,7 +214,7 @@ def debug_print (self, msg): # 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 @@ def _ensure_stringlike (self, option, what, default=None): "'%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 @@ def ensure_string_list (self, option): (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 a252c355e7..8f2b36fb1f 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 @@ def _announce(msg, level): ' 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) 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: [PATCH 1496/2594] 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 a252c355e7..8f2b36fb1f 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 @@ def _announce(msg, level): ' 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) 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: [PATCH 1497/2594] pep8-fied --- cmd.py | 65 +++++++++++++++++++++------------------------------------- 1 file changed, 23 insertions(+), 42 deletions(-) diff --git a/cmd.py b/cmd.py index 0e56460036..869258babb 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 @@ def __init__ (self, dist): # 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 @@ def __getattr__ (self, attr): 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 @@ def ensure_finalized (self): # 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 @@ def initialize_options (self): 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 @@ def dump_options(self, header=None, indent=""): 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 @@ def run (self): 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 @@ def debug_print (self, msg): sys.stdout.flush() - # -- Option validation methods ------------------------------------- # (these are very handy in writing the 'finalize_options()' method) # @@ -279,14 +271,13 @@ def ensure_dirname(self, option): # -- 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 @@ def set_undefined_options (self, src_cmd, *option_pairs): 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 @@ def get_finalized_command (self, command, create=1): # 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 @@ def get_sub_commands (self): # -- 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 @@ def copy_file (self, infile, outfile, 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 @@ def make_archive (self, base_name, format, 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 @@ def make_file(self, infiles, outfile, func, args, 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 @@ def initialize_options (self): 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 @@ def _copy_files (self, filelist): 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 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: [PATCH 1498/2594] 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 5829a56639..aff7d68e3c 100644 --- a/cmd.py +++ b/cmd.py @@ -93,9 +93,8 @@ def __init__(self, dist): # 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 @@ def __getattr__ (self, attr): 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 @@ def run(self): This method must be implemented by all command classes. """ - raise RuntimeError("abstract method -- subclass %s must override" % self.__class__) @@ -351,7 +349,7 @@ def copy_file(self, infile, outfile, preserve_mode=1, preserve_times=1, 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 @@ 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): """Special case of 'execute()' for operations that process one or @@ -406,7 +403,6 @@ def make_file(self, infiles, outfile, func, args, 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 @@ def initialize_options (self): 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 @@ def _copy_files (self, filelist): 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 From 9c62b7799426d02dc52636b95a34f12988a19305 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Tue, 14 Apr 2009 13:16:19 +0000 Subject: [PATCH 1499/2594] 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 e8f55cf087..ba926063e1 100644 --- a/__init__.py +++ b/__init__.py @@ -22,5 +22,5 @@ # #--start constants-- -__version__ = "2.6.2c1" +__version__ = "2.6.2" #--end constants-- 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: [PATCH 1500/2594] 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 d164f3f527..7d56dfcf40 100644 --- a/command/check.py +++ b/command/check.py @@ -72,7 +72,7 @@ def run(self): # 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 5e0c453140..372bae367b 100644 --- a/tests/test_check.py +++ b/tests/test_check.py @@ -72,17 +72,16 @@ def test_check_restructuredtext(self): 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): 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: [PATCH 1501/2594] 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 5dd73b07af..9a8fca1d5f 100644 --- a/command/check.py +++ b/command/check.py @@ -73,7 +73,7 @@ def run(self): # 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 5e0c453140..372bae367b 100644 --- a/tests/test_check.py +++ b/tests/test_check.py @@ -72,17 +72,16 @@ def test_check_restructuredtext(self): 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): 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: [PATCH 1502/2594] #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 f27c452404..29be0cfd2d 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -18,7 +18,8 @@ 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 @@ def setUp(self): 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 @@ def tearDown(self): 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 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: [PATCH 1503/2594] 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 d851d6c799..f2cb4fdeba 100644 --- a/tests/test_bdist_wininst.py +++ b/tests/test_bdist_wininst.py @@ -5,6 +5,7 @@ from distutils.tests import support class BuildWinInstTestCase(support.TempdirManager, + support.LoggingSilencer, unittest.TestCase): def test_get_exe_bytes(self): 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: [PATCH 1504/2594] 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 f27c452404..29be0cfd2d 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -18,7 +18,8 @@ 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 @@ def setUp(self): 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 @@ def tearDown(self): 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 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: [PATCH 1505/2594] 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 d851d6c799..f2cb4fdeba 100644 --- a/tests/test_bdist_wininst.py +++ b/tests/test_bdist_wininst.py @@ -5,6 +5,7 @@ from distutils.tests import support class BuildWinInstTestCase(support.TempdirManager, + support.LoggingSilencer, unittest.TestCase): def test_get_exe_bytes(self): 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: [PATCH 1506/2594] 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 de6601fbb7..85aa02a286 100644 --- a/tests/test_bdist_wininst.py +++ b/tests/test_bdist_wininst.py @@ -7,6 +7,7 @@ from distutils.tests import support class BuildWinInstTestCase(support.TempdirManager, + support.LoggingSilencer, unittest.TestCase): def test_get_exe_bytes(self): 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: [PATCH 1507/2594] 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 29be0cfd2d..348933e901 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -29,7 +29,7 @@ def setUp(self): 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 @@ def tearDown(self): 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 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: [PATCH 1508/2594] 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 29be0cfd2d..348933e901 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -29,7 +29,7 @@ def setUp(self): 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 @@ def tearDown(self): 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 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: [PATCH 1509/2594] #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 2acfab828e..b55eb5857b 100644 --- a/tests/test_build_scripts.py +++ b/tests/test_build_scripts.py @@ -90,7 +90,7 @@ def test_version_int(self): # 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() 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: [PATCH 1510/2594] 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 2acfab828e..b55eb5857b 100644 --- a/tests/test_build_scripts.py +++ b/tests/test_build_scripts.py @@ -90,7 +90,7 @@ def test_version_int(self): # 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() 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: [PATCH 1511/2594] 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 2acfab828e..b55eb5857b 100644 --- a/tests/test_build_scripts.py +++ b/tests/test_build_scripts.py @@ -90,7 +90,7 @@ def test_version_int(self): # 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() 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: [PATCH 1512/2594] 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 1e6ebfacf5..bacf13a291 100644 --- a/tests/test_config_cmd.py +++ b/tests/test_config_cmd.py @@ -46,7 +46,7 @@ def test_search_cpp(self): 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): 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: [PATCH 1513/2594] 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 1e6ebfacf5..bacf13a291 100644 --- a/tests/test_config_cmd.py +++ b/tests/test_config_cmd.py @@ -46,7 +46,7 @@ def test_search_cpp(self): 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): From 534e308892506dae6bf5aedd724dcbe975ed1149 Mon Sep 17 00:00:00 2001 From: Steven Bethard Date: Tue, 5 May 2009 01:31:22 +0000 Subject: [PATCH 1514/2594] 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 f94d9579d2..18bddeb483 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 @@ def initialize_options (self): 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 @@ def finalize_options (self): 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 @@ def run (self): # 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 @@ def run (self): 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 @@ def run (self): 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 @@ def add_ui(self): [("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 @@ def add_ui(self): 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 @@ def add_ui(self): 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 From f70dabd395a2452cafc0e2a526070ed71b8ea7bd Mon Sep 17 00:00:00 2001 From: Steven Bethard Date: Tue, 5 May 2009 02:22:38 +0000 Subject: [PATCH 1515/2594] 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 aab89cd609..ffd668eb0e 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 @@ def initialize_options(self): 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 @@ def finalize_options(self): 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 @@ def run(self): # 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 @@ def run(self): 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 @@ def run(self): 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 @@ def add_ui(self): [("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 @@ def add_ui(self): 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 @@ def add_ui(self): 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 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: [PATCH 1516/2594] 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 fe921fb88b..1d8cd8c3fa 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -127,43 +127,41 @@ def run (self): # 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 0000000000..36c07b7953 --- /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") 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: [PATCH 1517/2594] 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 34f4983689..258d7c10be 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -118,13 +118,15 @@ def run(self): 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 @@ def check_library_list(self, libraries): 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 0000000000..36c07b7953 --- /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") 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: [PATCH 1518/2594] 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 1d8cd8c3fa..18415cfcf3 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -17,7 +17,6 @@ # 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 @@ def initialize_options (self): 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 @@ def finalize_options (self): # 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 @@ def run (self): self.build_libraries(self.libraries) - # run() - def check_library_list(self, libraries): """Ensure that the list of libraries is valid. @@ -163,10 +153,9 @@ def check_library_list(self, libraries): "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 @@ def get_library_names (self): 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 @@ def get_source_files (self): "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 @@ def build_libraries (self, libraries): self.compiler.create_static_lib(objects, lib_name, output_dir=self.build_clib, debug=self.debug) - - # for libraries - - # build_libraries () - -# class build_lib 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: [PATCH 1519/2594] 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 18415cfcf3..447ea94989 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -86,7 +86,7 @@ def finalize_options(self): 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 @@ def get_source_files(self): 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 @@ def get_source_files(self): 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 36c07b7953..7374c4956a 100644 --- a/tests/test_build_clib.py +++ b/tests/test_build_clib.py @@ -39,6 +39,63 @@ def test_check_library_dist(self): 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) 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: [PATCH 1520/2594] 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 36c07b7953..7374c4956a 100644 --- a/tests/test_build_clib.py +++ b/tests/test_build_clib.py @@ -39,6 +39,63 @@ def test_check_library_dist(self): 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) 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: [PATCH 1521/2594] 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 447ea94989..d475232bc3 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -16,7 +16,7 @@ # 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 @@ def finalize_options(self): 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' ? From 14c20a579282de3d0fc983b150630d1f2f5d7f99 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Wed, 6 May 2009 20:43:28 +0000 Subject: [PATCH 1522/2594] bump version to 3.1b1 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index c66806361a..80ccf6113d 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.1a2" +__version__ = "3.1b1" #--end constants-- 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: [PATCH 1523/2594] 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 d475232bc3..9545b27ad0 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -22,12 +22,12 @@ 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 @@ def get_source_files(self): 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)): 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: [PATCH 1524/2594] 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 56cb861a6c..099e0586fd 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 7374c4956a..3c6643f354 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 @@ def test_finalize_options(self): 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 dd17eb347f..040dfcb6ad 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -49,7 +49,8 @@ def test_customize_compiler(self): 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 @@ def set_executables(self, **kw): comp = compiler() sysconfig.customize_compiler(comp) - self.assertEquals(comp.exes['archiver'], 'xxx') + self.assertEquals(comp.exes['archiver'], 'my_ar -arflags') def test_suite(): 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: [PATCH 1525/2594] 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 fbeb45f8cc..223ff672b9 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 7374c4956a..3c6643f354 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 @@ def test_finalize_options(self): 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 1e9dbd542f..971aac6daf 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -49,7 +49,8 @@ def test_customize_compiler(self): 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 @@ def set_executables(self, **kw): comp = compiler() sysconfig.customize_compiler(comp) - self.assertEquals(comp.exes['archiver'], 'xxx') + self.assertEquals(comp.exes['archiver'], 'my_ar -arflags') def test_suite(): 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: [PATCH 1526/2594] 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 040dfcb6ad..bf0043aa05 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -11,11 +11,15 @@ 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() 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: [PATCH 1527/2594] 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 971aac6daf..f65bc725c6 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -11,11 +11,15 @@ 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() 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: [PATCH 1528/2594] 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 ffd668eb0e..9645158637 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 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: [PATCH 1529/2594] 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 9097bee2e4..141c2869b5 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -125,7 +125,7 @@ def test_user_site(self): 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 @@ def test_user_site(self): # 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 0000000000..94e9edfc09 --- /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 045368a925..0b1dc4af7d 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -273,8 +273,9 @@ def runtime_library_dir_option(self, dir): # 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 @@ def runtime_library_dir_option(self, 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++": - 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 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: [PATCH 1530/2594] #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 348933e901..ea7c5925b7 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -214,12 +214,17 @@ def test_check_environ(self): # 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() 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: [PATCH 1531/2594] 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 348933e901..ea7c5925b7 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -214,12 +214,17 @@ def test_check_environ(self): # 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() 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: [PATCH 1532/2594] 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 5ea67bedb7..e4a02391af 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -125,7 +125,7 @@ def test_user_site(self): 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 @@ def test_user_site(self): # 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 0000000000..94e9edfc09 --- /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 d65ab321b6..c11544d828 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -271,8 +271,9 @@ def runtime_library_dir_option(self, dir): # 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 @@ def runtime_library_dir_option(self, 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++": - 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 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: [PATCH 1533/2594] 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 2c6df1d2cd..dfabf91091 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -136,7 +136,7 @@ def initialize_options (self): 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 @@ def finalize_options (self): 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 @@ def finalize_options (self): 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 @@ def finalize_options (self): # 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 @@ def finalize_options (self): 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 @@ def run (self): 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 @@ def run (self): # 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 @@ def check_extensions_list (self, extensions): 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 @@ def check_extensions_list (self, extensions): # 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 @@ def check_extensions_list (self, extensions): 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 @@ def check_extensions_list (self, extensions): 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 @@ def get_source_files (self): 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 @@ def get_outputs (self): 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 141c2869b5..dab9712a56 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -9,8 +9,8 @@ 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 @@ def test_optional_extension(self): 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): 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: [PATCH 1534/2594] 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 936ea8d099..c03951cb12 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -128,7 +128,7 @@ def initialize_options (self): 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 @@ def finalize_options (self): 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 @@ def finalize_options (self): 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 @@ def finalize_options (self): # 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 @@ def finalize_options (self): 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 @@ def run (self): 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 @@ def run (self): # 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 @@ def check_extensions_list (self, extensions): 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 @@ def check_extensions_list (self, extensions): # 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 @@ def check_extensions_list (self, extensions): 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 @@ def check_extensions_list (self, extensions): 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 @@ def get_source_files (self): 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 @@ def get_outputs (self): 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 dbec65e8d1..98ae8cf3b4 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -7,6 +7,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 import unittest from test import test_support @@ -15,10 +17,13 @@ # 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 @@ def tearDown(self): 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 @@ def test_solaris_enable_shared(self): # 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: 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: [PATCH 1535/2594] 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 ade95be229..d3668e9f4c 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -329,7 +329,7 @@ def run(self): 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 @@ def check_extensions_list(self, extensions): 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 @@ def check_extensions_list(self, extensions): # 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 e4a02391af..c22be2ac09 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -10,8 +10,8 @@ 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 @@ def test_optional_extension(self): 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): 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: [PATCH 1536/2594] 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 4c62c7133a..87e3c7aa04 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -6,7 +6,6 @@ __revision__ = "$Id$" import os -from types import IntType from distutils.core import Command from distutils.errors import DistutilsOptionError @@ -17,7 +16,7 @@ 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 @@ def initialize_options (self): 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 @@ def finalize_options (self): 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 @@ def run (self): 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 @@ def install (self): 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 @@ def byte_compile (self, files): # -- 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 @@ def _mutate_outputs (self, has_any, build_cmd, cmd_option, output_dir): 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 @@ def _bytecode_filenames (self, py_filenames): # -- 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 @@ def get_outputs (self): 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 @@ def get_inputs (self): 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 0000000000..69a24faf71 --- /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") 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: [PATCH 1537/2594] 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 94895f4269..022efa48fc 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -5,7 +5,7 @@ __revision__ = "$Id$" -import sys, os +import os from distutils.core import Command from distutils.errors import DistutilsOptionError @@ -57,7 +57,6 @@ def initialize_options(self): 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 @@ def finalize_options(self): 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 @@ def run(self): 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 0000000000..69a24faf71 --- /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") 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: [PATCH 1538/2594] 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 578cf40c09..668edf9f5a 100644 --- a/tests/support.py +++ b/tests/support.py @@ -5,6 +5,7 @@ from distutils import log from distutils.core import Distribution +from test.test_support import EnvironmentVarGuard class LoggingSilencer(object): @@ -82,3 +83,13 @@ def __init__(self, **kwargs): 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 bf0043aa05..0a5ac2944a 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 @@ def test_customize_compiler(self): 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: 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: [PATCH 1539/2594] 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 ab2af9a6aa..cdcbc37862 100644 --- a/tests/support.py +++ b/tests/support.py @@ -5,6 +5,7 @@ from distutils import log from distutils.core import Distribution +from test.support import EnvironmentVarGuard class LoggingSilencer(object): @@ -82,3 +83,13 @@ def __init__(self, **kwargs): 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 f65bc725c6..322df39cf5 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 @@ def test_customize_compiler(self): 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: 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: [PATCH 1540/2594] 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 7737538bf8..fd778d1ba5 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -49,18 +49,14 @@ 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 @@ def initialize_options(self): 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 @@ def test_server_registration(self): # 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 @@ def test_server_registration(self): 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 @@ def test_server_registration(self): 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 847df7bb60..3304790b51 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -39,13 +39,13 @@ def find_config_files(self): 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 @@ def _warn(msg): 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 @@ def format_metadata(self, dist): 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 @@ def test_custom_pydistutils(self): # 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 ea7c5925b7..cee7d5263b 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -8,28 +8,23 @@ 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 @@ def tearDown(self): 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 @@ def tearDown(self): 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 @@ def test_get_platform(self): ('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 @@ def test_get_platform(self): 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 @@ def test_check_environ(self): # 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 @@ def test_rfc822_escape(self): self.assertEquals(res, wanted) def test_suite(): - return unittest.makeSuite(utilTestCase) + return unittest.makeSuite(UtilTestCase) if __name__ == "__main__": unittest.main(defaultTest="test_suite") 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: [PATCH 1541/2594] 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 7506f93227..0f97cf7a5e 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -48,18 +48,14 @@ 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 @@ def initialize_options(self): 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 @@ def test_server_registration(self): # 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 @@ def test_server_registration(self): 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 @@ def test_server_registration(self): 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 3ac0fdd1e1..092bd14f93 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -38,11 +38,13 @@ def find_config_files(self): 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 @@ def _warn(msg): 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 @@ def format_metadata(self, dist): 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 @@ def test_custom_pydistutils(self): # 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 ea7c5925b7..cee7d5263b 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -8,28 +8,23 @@ 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 @@ def tearDown(self): 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 @@ def tearDown(self): 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 @@ def test_get_platform(self): ('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 @@ def test_get_platform(self): 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 @@ def test_check_environ(self): # 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 @@ def test_rfc822_escape(self): self.assertEquals(res, wanted) def test_suite(): - return unittest.makeSuite(utilTestCase) + return unittest.makeSuite(UtilTestCase) if __name__ == "__main__": unittest.main(defaultTest="test_suite") 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: [PATCH 1542/2594] 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 69a24faf71..d768166829 100644 --- a/tests/test_install_lib.py +++ b/tests/test_install_lib.py @@ -57,7 +57,7 @@ def test_get_outputs(self): 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() 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: [PATCH 1543/2594] 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 69a24faf71..d768166829 100644 --- a/tests/test_install_lib.py +++ b/tests/test_install_lib.py @@ -57,7 +57,7 @@ def test_get_outputs(self): 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() 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: [PATCH 1544/2594] 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 dab9712a56..54d6b96d34 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -192,7 +192,7 @@ def test_finalize_options(self): 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 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: [PATCH 1545/2594] 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 c22be2ac09..6f71a4ab12 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -192,7 +192,7 @@ def test_finalize_options(self): 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 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: [PATCH 1546/2594] 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 98ae8cf3b4..dc40d13fc7 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -129,7 +129,7 @@ def test_finalize_options(self): 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 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: [PATCH 1547/2594] 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 3c6643f354..47d85cd8b4 100644 --- a/tests/test_build_clib.py +++ b/tests/test_build_clib.py @@ -109,7 +109,7 @@ def test_run(self): 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') 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: [PATCH 1548/2594] 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 3c6643f354..47d85cd8b4 100644 --- a/tests/test_build_clib.py +++ b/tests/test_build_clib.py @@ -109,7 +109,7 @@ def test_run(self): 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') 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: [PATCH 1549/2594] 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 87e3c7aa04..411db79cc2 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -80,7 +80,8 @@ def finalize_options(self): 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" 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: [PATCH 1550/2594] 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 81107a85cb..4ea61d78dc 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -80,7 +80,8 @@ def finalize_options (self): 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" 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: [PATCH 1551/2594] 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 022efa48fc..85fb3acd4e 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -77,7 +77,8 @@ def finalize_options(self): 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") 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: [PATCH 1552/2594] 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 dfabf91091..73fd768026 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -311,38 +311,38 @@ def run(self): # 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 @@ def get_outputs(self): # "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 @@ def build_extension(self, ext): "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 @@ def build_extension(self, ext): 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 @@ def build_extension(self, ext): 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 @@ def find_swig (self): # -- 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 @@ def get_libraries (self, ext): # 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 54d6b96d34..5cdfc2a9eb 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -113,7 +113,7 @@ def test_solaris_enable_shared(self): 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 @@ def test_get_source_files(self): 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): 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: [PATCH 1553/2594] 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 c03951cb12..c611ad48c0 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -303,38 +303,38 @@ def run(self): # 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 @@ def get_outputs(self): # "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 @@ def build_extension(self, ext): "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 @@ def build_extension(self, ext): 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 @@ def build_extension(self, ext): 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 @@ def find_swig (self): # -- 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 @@ def get_libraries (self, ext): # 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 475ceee598..d24a18ea0e 100644 --- a/tests/support.py +++ b/tests/support.py @@ -42,6 +42,18 @@ def mkdtemp(self): 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 dc40d13fc7..8889798c88 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -99,7 +99,7 @@ def test_solaris_enable_shared(self): 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 @@ def test_get_source_files(self): 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: 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: [PATCH 1554/2594] 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 d3668e9f4c..e305bde913 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -310,38 +310,38 @@ def run(self): # 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 @@ def get_outputs(self): # "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 @@ def build_extension(self, ext): "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 @@ def build_extension(self, ext): 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 @@ def build_extension(self, ext): 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 @@ def find_swig(self): # -- 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 @@ def get_libraries(self, ext): # 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 6f71a4ab12..86064fccc1 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -113,7 +113,7 @@ def test_solaris_enable_shared(self): 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 @@ def test_get_source_files(self): 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): From 5042c7cc1026361bd2ce98f951a19ad2f3ece578 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Tue, 12 May 2009 21:06:05 +0000 Subject: [PATCH 1555/2594] 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 73fd768026..10d50fade7 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -311,38 +311,38 @@ def run(self): # 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 @@ def build_extension(self, ext): 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 @@ def build_extension(self, ext): 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 @@ def get_libraries (self, ext): # 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' From 431ef0a9555d0ccdd974e61a4cad5159dbdbc22a Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Tue, 12 May 2009 21:20:41 +0000 Subject: [PATCH 1556/2594] 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 c611ad48c0..01bab3664f 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -303,38 +303,38 @@ def run(self): # 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 @@ def build_extension(self, ext): 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 @@ def build_extension(self, ext): 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 @@ def get_libraries (self, ext): # 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' From d39a44c5da79381e4ae7dd7502e8a398814072d1 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Tue, 12 May 2009 21:21:26 +0000 Subject: [PATCH 1557/2594] 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 e305bde913..c39bd72d62 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -310,38 +310,38 @@ def run(self): # 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 @@ def build_extension(self, ext): 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 @@ def build_extension(self, ext): 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 @@ def get_libraries(self, ext): # 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' 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: [PATCH 1558/2594] 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 5cdfc2a9eb..6d3852cc4c 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -296,7 +296,7 @@ def test_compiler_option(self): 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]}) 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: [PATCH 1559/2594] 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 8889798c88..20f0553985 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -233,7 +233,7 @@ def test_compiler_option(self): 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]}) 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: [PATCH 1560/2594] 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 6d3852cc4c..45c5b39e0f 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -296,7 +296,7 @@ def test_compiler_option(self): 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]}) 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: [PATCH 1561/2594] 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 20f0553985..cf254ac1c4 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -233,7 +233,7 @@ def test_compiler_option(self): 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]}) 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: [PATCH 1562/2594] 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 86064fccc1..64ca6505c1 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -296,7 +296,7 @@ def test_compiler_option(self): 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]}) 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: [PATCH 1563/2594] 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 a9ce28a7eb..f77f52c174 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -16,19 +16,18 @@ 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 8af080f950..551357edc7 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -7,12 +7,16 @@ 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 @@ def test_add_defaults(self): 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) 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: [PATCH 1564/2594] 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 1a64d0e3c1..836b962059 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -17,19 +17,18 @@ 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 8af080f950..ec95ffb3aa 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -7,12 +7,16 @@ 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 @@ def test_add_defaults(self): 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) 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: [PATCH 1565/2594] 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 f77f52c174..ab0ada1f45 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 @@ def initialize_options (self): 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 @@ def finalize_options (self): 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 @@ def run (self): # 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 @@ def check_metadata (self): "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 @@ def get_file_list (self): 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 @@ def add_defaults (self): 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 @@ def read_template (self): 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 @@ def prune_file_list (self): 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 @@ def write_manifest (self): (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 @@ def read_manifest (self): 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 @@ def make_release_tree (self, base_dir, files): 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 @@ def make_distribution (self): 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 551357edc7..4f3d4bb91d 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -13,7 +13,7 @@ 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 @@ def test_show_formats(self): 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) 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: [PATCH 1566/2594] 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 836b962059..cbb77c212e 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 @@ def initialize_options(self): self.archive_files = None - def finalize_options(self): if self.manifest is None: self.manifest = "MANIFEST" @@ -125,7 +124,6 @@ def finalize_options(self): 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 @@ def get_file_list(self): else: self.read_manifest() - def add_defaults(self): """Add all the default files to self.filelist: - README or README.txt @@ -373,7 +370,7 @@ def prune_file_list(self): 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 ec95ffb3aa..9e27933773 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -13,7 +13,7 @@ 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 @@ def test_show_formats(self): 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) 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: [PATCH 1567/2594] #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 45c5b39e0f..c6533ca9f7 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -312,12 +312,18 @@ def test_get_outputs(self): # 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() 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: [PATCH 1568/2594] 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 cf254ac1c4..2bd2292e8c 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -249,12 +249,18 @@ def test_get_outputs(self): # 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() 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: [PATCH 1569/2594] 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 64ca6505c1..6a463c0117 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -312,12 +312,18 @@ def test_get_outputs(self): # 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() From 6f1556e53cd1edc7db121e1c0bafa91e061ca989 Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Fri, 15 May 2009 17:27:30 +0000 Subject: [PATCH 1570/2594] 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 a56a03880e..5a9ff76d67 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -7,7 +7,6 @@ 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 @@ def set_include_dirs (self, dirs): 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 @@ def set_libraries (self, libnames): 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 @@ def set_library_dirs (self, dirs): 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 @@ def set_runtime_library_dirs (self, dirs): 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 @@ def set_link_objects (self, objects): 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 2d57ad09e9..5fe262b112 100644 --- a/dist.py +++ b/dist.py @@ -8,7 +8,6 @@ import sys, os, string, re from types import * -from copy import copy try: import warnings @@ -537,7 +536,7 @@ def _parse_command_opts (self, parser, args): # 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 From fb1d44bd30704af391fa97fa93b7350a28b16826 Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Fri, 15 May 2009 17:34:21 +0000 Subject: [PATCH 1571/2594] 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 cad663a348..875f96fac3 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -6,7 +6,6 @@ __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 @@ def set_include_dirs(self, dirs): 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 @@ def set_libraries(self, libnames): 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 @@ def set_library_dirs(self, dirs): 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 @@ def set_runtime_library_dirs(self, dirs): 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 @@ def set_link_objects(self, objects): 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 7c30e8856f..91adc4d37f 100644 --- a/dist.py +++ b/dist.py @@ -7,7 +7,6 @@ __revision__ = "$Id$" import sys, os, re -from copy import copy try: import warnings @@ -521,7 +520,7 @@ def _parse_command_opts (self, parser, args): # 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 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: [PATCH 1572/2594] #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 f3463dc289..3b5b2080ec 100644 --- a/command/register.py +++ b/command/register.py @@ -9,6 +9,7 @@ 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 @@ def run(self): 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 ab0ada1f45..c21f3917b6 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -8,6 +8,8 @@ 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 @@ def initialize_options(self): 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 @@ def run(self): # 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 @@ def run(self): 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 668edf9f5a..bce034949e 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 7da8edb5d7..91d3581373 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -4,10 +4,14 @@ 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 @@ def open(self, req): def read(self): return 'xxx' -class registerTestCase(PyPIRCCommandTestCase): +class RegisterTestCase(PyPIRCCommandTestCase): def setUp(self): PyPIRCCommandTestCase.setUp(self) @@ -76,10 +80,11 @@ def tearDown(self): 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 @@ def test_password_reset(self): 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 4f3d4bb91d..5808ca16eb 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -6,7 +6,9 @@ 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.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.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 @@ def setUp(self): 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 @@ def test_add_defaults(self): 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 @@ def test_finalize_options(self): def test_suite(): - return unittest.makeSuite(sdistTestCase) + return unittest.makeSuite(SDistTestCase) if __name__ == "__main__": unittest.main(defaultTest="test_suite") 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: [PATCH 1573/2594] 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 7dd3099912..bdf5f8f09c 100644 --- a/command/register.py +++ b/command/register.py @@ -10,6 +10,7 @@ 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 @@ def run(self): 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 cbb77c212e..ace9eeeb61 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -9,6 +9,8 @@ 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 @@ def initialize_options(self): 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 @@ def run(self): # 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 @@ def run(self): 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 cdcbc37862..1255413989 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 46f7761d80..f486cf797e 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -4,10 +4,14 @@ 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 @@ def open(self, req): def read(self): return 'xxx' -class registerTestCase(PyPIRCCommandTestCase): +class RegisterTestCase(PyPIRCCommandTestCase): def setUp(self): PyPIRCCommandTestCase.setUp(self) @@ -76,10 +80,11 @@ def tearDown(self): 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 @@ def test_password_reset(self): 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 9e27933773..b7e5859cb9 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -6,7 +6,9 @@ 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.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.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 @@ def setUp(self): 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 @@ def test_add_defaults(self): 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 @@ def test_finalize_options(self): def test_suite(): - return unittest.makeSuite(sdistTestCase) + return unittest.makeSuite(SDistTestCase) if __name__ == "__main__": unittest.main(defaultTest="test_suite") 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: [PATCH 1574/2594] pep8-fied distutils.dist module --- dist.py | 130 ++++++++++++++++---------------------------------------- 1 file changed, 37 insertions(+), 93 deletions(-) diff --git a/dist.py b/dist.py index 5fe262b112..0c77f85e7b 100644 --- a/dist.py +++ b/dist.py @@ -267,23 +267,18 @@ def __init__ (self, attrs=None): 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 @@ def dump_option_dicts (self, header=None, commands=None, indent=""): 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 @@ def find_config_files (self): 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 @@ def parse_config_files (self, filenames=None): 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 @@ def parse_command_line (self): # 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 @@ def _get_toplevel_options (self): "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 @@ def _parse_command_opts (self, parser, args): 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 @@ def finalize_options (self): 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 @@ def _show_help (self, 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 @@ def handle_display_options (self, option_order): 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 @@ def print_command_list (self, commands, header, max_length): 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 @@ def print_commands (self): descriptions come from the command class attribute 'description'. """ - import distutils.command std_commands = distutils.command.__all__ is_std = {} @@ -761,9 +728,7 @@ def print_commands (self): "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 @@ def get_command_list (self): # -- 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 @@ def get_command_packages (self): 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 @@ def get_command_class (self, command): 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 @@ def get_command_obj (self, command, create=1): 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 @@ def _set_command_options (self, command_obj, option_dict=None): 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 @@ def reinitialize_command (self, command, reinit_subcommands=0): 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 @@ def run_commands (self): 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 @@ def run_command (self, command): # -- 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 @@ def is_pure (self): # 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 @@ def __init__ (self): 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 @@ def write_pkg_file (self, file): 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 @@ def _write_field(self, file, name, value): 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 @@ 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.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 @@ def get_download_url(self): return self.download_url or "UNKNOWN" # PEP 314 - def get_requires(self): return self.requires or [] @@ -1212,10 +1159,7 @@ def set_obsoletes(self, value): 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. """ 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: [PATCH 1575/2594] 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 91adc4d37f..7969503507 100644 --- a/dist.py +++ b/dist.py @@ -262,21 +262,18 @@ def __init__ (self, attrs=None): 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 @@ def dump_option_dicts (self, header=None, commands=None, indent=""): 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 @@ def find_config_files (self): 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 @@ def parse_config_files (self, filenames=None): 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 @@ def parse_command_line (self): # 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 @@ def _get_toplevel_options (self): "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 @@ def _parse_command_opts (self, parser, args): 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 @@ def finalize_options (self): 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 @@ def _show_help (self, 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 @@ def handle_display_options (self, option_order): 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 @@ def print_command_list (self, commands, header, max_length): 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 @@ def print_commands (self): "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 @@ def get_command_list (self): # -- 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 @@ def get_command_packages (self): 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 @@ def get_command_class (self, command): 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 @@ def get_command_obj (self, command, create=1): 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 @@ def _set_command_options (self, command_obj, option_dict=None): 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 @@ def reinitialize_command (self, command, reinit_subcommands=0): 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 @@ def run_commands (self): 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 @@ def run_command (self, command): # -- 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 @@ def is_pure (self): # 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 @@ def __init__ (self): 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 @@ def write_pkg_file (self, file): 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 @@ 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.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 @@ def get_download_url(self): return self.download_url or "UNKNOWN" # PEP 314 - def get_requires(self): return self.requires or [] @@ -1170,8 +1144,7 @@ def set_obsoletes(self, value): 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. """ 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: [PATCH 1576/2594] 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 c6533ca9f7..7e3f716500 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -19,6 +19,12 @@ # 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 @@ def test_compiler_option(self): 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') 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: [PATCH 1577/2594] 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 2bd2292e8c..21020d3927 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -17,6 +17,12 @@ # 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 @@ def test_compiler_option(self): 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') 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: [PATCH 1578/2594] 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 6a463c0117..764aa727d1 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -20,6 +20,12 @@ # 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 @@ def test_compiler_option(self): 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') 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: [PATCH 1579/2594] 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 0c77f85e7b..93325b9720 100644 --- a/dist.py +++ b/dist.py @@ -414,11 +414,6 @@ def parse_command_line(self): # 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 @@ def parse_command_line(self): # 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 3304790b51..49230c54c0 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 @@ def initialize_options(self): 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 @@ def test_write_pkg_file(self): # 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 @@ def test_write_pkg_file(self): 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 @@ def test_provides(self): 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 @@ def test_provides(self): 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 @@ def test_requires(self): 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 @@ def test_requires(self): 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 @@ def test_obsoletes(self): 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 @@ def test_obsoletes(self): 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 @@ def test_custom_pydistutils(self): f.close() try: - dist = distutils.dist.Distribution() + dist = Distribution() # linux-style if sys.platform in ('linux', 'darwin'): @@ -269,6 +266,29 @@ def test_custom_pydistutils(self): 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)) 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: [PATCH 1580/2594] 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 7969503507..65c1ce912e 100644 --- a/dist.py +++ b/dist.py @@ -408,11 +408,6 @@ def parse_command_line(self): # 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 @@ def parse_command_line(self): # 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 092bd14f93..54b63f7ddf 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 @@ def initialize_options(self): 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 @@ def test_command_packages_configfile(self): 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 @@ def test_provides(self): 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 @@ def test_provides(self): 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 @@ def test_requires(self): 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 @@ def test_requires(self): 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 @@ def test_obsoletes(self): 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 @@ def test_obsoletes(self): 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 @@ def test_custom_pydistutils(self): f.close() try: - dist = distutils.dist.Distribution() + dist = Distribution() # linux-style if sys.platform in ('linux', 'darwin'): @@ -241,6 +237,29 @@ def test_custom_pydistutils(self): 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)) 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: [PATCH 1581/2594] 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 5a134408fd..9604c6d249 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 bf416b6d79..9bd6530c14 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -3,10 +3,8 @@ 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 @@ def test_copy_tree_verbosity(self): 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) 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: [PATCH 1582/2594] 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 ed54ccefd4..82608d2c87 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 bf416b6d79..9bd6530c14 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -3,10 +3,8 @@ 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 @@ def test_copy_tree_verbosity(self): 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) 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: [PATCH 1583/2594] 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 9604c6d249..55607e5b53 100644 --- a/dir_util.py +++ b/dir_util.py @@ -5,7 +5,6 @@ __revision__ = "$Id$" import os, sys -from types import * from distutils.errors import DistutilsFileError, DistutilsInternalError from distutils import log @@ -16,20 +15,21 @@ # 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: 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: [PATCH 1584/2594] 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 82608d2c87..98e6252c6c 100644 --- a/dir_util.py +++ b/dir_util.py @@ -15,15 +15,16 @@ # 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: 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: [PATCH 1585/2594] 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 b50563e317..f5959f5e64 100644 --- a/archive_util.py +++ b/archive_util.py @@ -11,15 +11,16 @@ 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 @@ def make_zipfile (base_name, base_dir, verbose=0, dry_run=0): '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 0000000000..f5fd9eabe4 --- /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") 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: [PATCH 1586/2594] 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 9444ff012f..08a9e56901 100644 --- a/archive_util.py +++ b/archive_util.py @@ -11,15 +11,16 @@ 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 @@ def make_zipfile (base_name, base_dir, verbose=0, dry_run=0): '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 0000000000..f5fd9eabe4 --- /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") 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: [PATCH 1587/2594] 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 f5fd9eabe4..3e1e04a75f 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 @@ def test_make_tarball(self): 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') 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: [PATCH 1588/2594] 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 f5fd9eabe4..3e1e04a75f 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 @@ def test_make_tarball(self): 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') 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: [PATCH 1589/2594] 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 10d50fade7..0c77aaa665 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -649,7 +649,8 @@ def get_ext_fullpath(self, ext_name): 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 @@ def get_ext_fullname(self, 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 = 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 7e3f716500..f1f80bea5d 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -19,12 +19,6 @@ # 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 @@ def test_compiler_option(self): 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 @@ def test_get_outputs(self): 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 @@ def test_get_outputs(self): 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) 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: [PATCH 1590/2594] 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 01bab3664f..85935d37bf 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -635,7 +635,8 @@ def get_ext_fullpath(self, ext_name): 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 @@ def get_ext_fullname(self, 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 = 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 21020d3927..bd906e35e5 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -17,12 +17,6 @@ # 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 @@ def test_get_outputs(self): 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 @@ def test_get_outputs(self): 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) 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: [PATCH 1591/2594] 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 c39bd72d62..eb4cb051c4 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -636,7 +636,8 @@ def get_ext_fullpath(self, ext_name): 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 764aa727d1..add2923b87 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -20,12 +20,6 @@ # 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 @@ def test_compiler_option(self): 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 @@ def test_get_outputs(self): 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 @@ def test_get_outputs(self): 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) 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: [PATCH 1592/2594] 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 bd906e35e5..fcca2f0d96 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -231,8 +231,6 @@ def test_compiler_option(self): 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') 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: [PATCH 1593/2594] 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 3e1e04a75f..1c88457d01 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -27,7 +27,14 @@ def test_make_tarball(self): 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 @@ def test_make_tarball(self): # 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)) 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: [PATCH 1594/2594] 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 3e1e04a75f..1c88457d01 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -27,7 +27,14 @@ def test_make_tarball(self): 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 @@ def test_make_tarball(self): # 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)) 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: [PATCH 1595/2594] 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 1c88457d01..cabb55bc15 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -3,6 +3,7 @@ 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 @@ def test_make_tarball(self): 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 @@ def test_make_tarball(self): 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 9bd6530c14..6b22f05ff0 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -88,7 +88,7 @@ def test_ensure_relative(self): 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(): 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: [PATCH 1596/2594] 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 1c88457d01..cabb55bc15 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -3,6 +3,7 @@ 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 @@ def test_make_tarball(self): 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 @@ def test_make_tarball(self): 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 9bd6530c14..6b22f05ff0 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -88,7 +88,7 @@ def test_ensure_relative(self): 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(): 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: [PATCH 1597/2594] 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 0c77aaa665..293c214de7 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -642,19 +642,21 @@ def get_ext_fullpath(self, ext_name): 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 f1f80bea5d..12b8581c97 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -336,6 +336,28 @@ def test_get_outputs(self): 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): 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: [PATCH 1598/2594] 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 85935d37bf..e6e33ab95c 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -628,19 +628,21 @@ def get_ext_fullpath(self, ext_name): 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 fcca2f0d96..dc8df98fd7 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -273,6 +273,28 @@ def test_get_outputs(self): 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: 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: [PATCH 1599/2594] 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 eb4cb051c4..31e036bceb 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -629,19 +629,21 @@ def get_ext_fullpath(self, ext_name): 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 add2923b87..4ea11a159d 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -336,6 +336,28 @@ def test_get_outputs(self): 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): 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: [PATCH 1600/2594] 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 18bddeb483..4272b818bd 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 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: [PATCH 1601/2594] 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 4272b818bd..b42e41b373 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -141,6 +141,8 @@ def finalize_options (self): 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()\ 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: [PATCH 1602/2594] 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 9645158637..bca98cc23e 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -141,6 +141,8 @@ def finalize_options(self): 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()\ 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: [PATCH 1603/2594] 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 f5959f5e64..62150b0ddd 100644 --- a/archive_util.py +++ b/archive_util.py @@ -6,6 +6,9 @@ __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 cabb55bc15..2b24152632 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -3,12 +3,15 @@ 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 @@ 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 @@ def test_make_tarball(self): 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 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: [PATCH 1604/2594] 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 08a9e56901..a568854e4e 100644 --- a/archive_util.py +++ b/archive_util.py @@ -6,6 +6,9 @@ __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 cabb55bc15..5db9a5d096 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -3,12 +3,15 @@ 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 @@ 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 @@ def test_make_tarball(self): 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 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: [PATCH 1605/2594] 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 2b24152632..29cd271faf 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -20,6 +20,7 @@ ZIP_SUPPORT = find_executable('zip') class ArchiveUtilTestCase(support.TempdirManager, + support.LoggingSilencer, unittest.TestCase): def test_make_tarball(self): @@ -82,7 +83,8 @@ def _create_files(self): 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 @@ def test_tarfile_vs_tar(self): # 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) 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: [PATCH 1606/2594] 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 5db9a5d096..663b33532a 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -20,6 +20,7 @@ ZIP_SUPPORT = find_executable('zip') class ArchiveUtilTestCase(support.TempdirManager, + support.LoggingSilencer, unittest.TestCase): def test_make_tarball(self): @@ -82,7 +83,8 @@ def _create_files(self): 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 @@ def test_tarfile_vs_tar(self): # 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) 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: [PATCH 1607/2594] 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 2863b620db..d2ea201bd4 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 6b22f05ff0..0f694aa020 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -18,7 +18,7 @@ def _log(self, msg, *args): 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 @@ def setUp(self): 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 9373af8503..fac4a5d1a9 100644 --- a/tests/test_file_util.py +++ b/tests/test_file_util.py @@ -16,7 +16,7 @@ def _log(self, msg, *args): 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 @@ def setUp(self): 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 91d3581373..0c6bf66989 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -66,7 +66,7 @@ def read(self): 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 @@ def _getpass(prompt): 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: From f9b04c5e43bf858c654b1557447ed19051bd85a4 Mon Sep 17 00:00:00 2001 From: Hirokazu Yamamoto Date: Fri, 29 May 2009 09:14:04 +0000 Subject: [PATCH 1608/2594] 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 2863b620db..d2ea201bd4 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 6b22f05ff0..0f694aa020 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -18,7 +18,7 @@ def _log(self, msg, *args): 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 @@ def setUp(self): 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 9373af8503..fac4a5d1a9 100644 --- a/tests/test_file_util.py +++ b/tests/test_file_util.py @@ -16,7 +16,7 @@ def _log(self, msg, *args): 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 @@ def setUp(self): 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 f486cf797e..d9ff3ec441 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -66,7 +66,7 @@ def read(self): 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 @@ def _getpass(prompt): 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: 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: [PATCH 1609/2594] 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 0d839b5c6e..59c24fd433 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -29,14 +29,14 @@ 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): From 13e74436fe53eb88f4f20555c16df795b16987fc Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 30 May 2009 15:30:16 +0000 Subject: [PATCH 1610/2594] bump to 3.1rc1 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 80ccf6113d..5a5db53272 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.1b1" +__version__ = "3.1rc1" #--end constants-- 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: [PATCH 1611/2594] 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 93325b9720..f4aecb419e 100644 --- a/dist.py +++ b/dist.py @@ -6,8 +6,7 @@ __revision__ = "$Id$" -import sys, os, string, re -from types import * +import sys, os, re try: import warnings @@ -251,7 +250,7 @@ def __init__ (self, attrs=None): # 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 @@ def dump_option_dicts(self, header=None, commands=None, indent=""): 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 @@ def parse_config_files(self, filenames=None): 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 @@ def parse_config_files(self, filenames=None): 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 @@ def _parse_command_opts(self, parser, args): # 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 @@ def _parse_command_opts(self, parser, args): # 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 @@ def _parse_command_opts(self, parser, args): 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 @@ def finalize_options(self): 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 @@ def _show_help(self, parser, global_options=1, display_options=1, 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 @@ def handle_display_options(self, option_order): # 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 @@ def handle_display_options(self, option_order): 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 @@ def print_command_list(self, commands, header, max_length): """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 @@ def print_command_list(self, commands, header, max_length): 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 @@ def get_command_list(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([])): - 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 @@ def get_command_obj(self, command, create=1): 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 @@ def _set_command_options(self, command_obj, option_dict=None): 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 @@ def _set_command_options(self, command_obj, option_dict=None): 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 @@ def write_pkg_file(self, file): 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 49230c54c0..7ccdd53b79 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 @@ def find_config_files(self): 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 @@ def test_write_pkg_file(self): 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 @@ def _warn(msg): 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): 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: [PATCH 1612/2594] 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 65c1ce912e..915d336431 100644 --- a/dist.py +++ b/dist.py @@ -246,7 +246,7 @@ def __init__ (self, attrs=None): # 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 @@ def dump_option_dicts(self, header=None, commands=None, indent=""): 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 @@ def parse_config_files(self, filenames=None): 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 @@ def _parse_command_opts(self, parser, args): 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 @@ def finalize_options(self): 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 @@ def _show_help(self, parser, global_options=1, display_options=1, 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 @@ def _show_help(self, parser, global_options=1, display_options=1, 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 @@ def handle_display_options(self, option_order): # 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 @@ def handle_display_options(self, option_order): 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 @@ def print_command_list(self, commands, header, max_length): """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 @@ def print_command_list(self, commands, header, max_length): 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 @@ def get_command_list(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([])): - 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 @@ def get_command_obj(self, command, create=1): 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 @@ def _set_command_options(self, command_obj, option_dict=None): 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 @@ def write_pkg_file(self, file): 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 54b63f7ddf..16ef68e962 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 @@ def find_config_files(self): 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 @@ def _warn(msg): 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): 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: [PATCH 1613/2594] 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 a6d3426390..5c014c4be2 100644 --- a/spawn.py +++ b/spawn.py @@ -8,17 +8,16 @@ __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 33e8bcf6e9..b9fd610e2f 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 @@ def test_nt_quote_args(self): 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) 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: [PATCH 1614/2594] 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 4c536d28af..8c476dc23f 100644 --- a/spawn.py +++ b/spawn.py @@ -8,13 +8,16 @@ __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 33e8bcf6e9..950e5789b5 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 @@ def test_nt_quote_args(self): 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) 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: [PATCH 1615/2594] 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 c80c61e35b..4d72c402ce 100644 --- a/extension.py +++ b/extension.py @@ -141,9 +141,11 @@ def __init__ (self, name, sources, # 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 0000000000..36c4290d8f --- /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 0000000000..1fcf0f5ecf --- /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") 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: [PATCH 1616/2594] 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 f7e7b4edc9..a4054ff454 100644 --- a/extension.py +++ b/extension.py @@ -139,8 +139,10 @@ def __init__(self, name, sources, 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 0000000000..36c4290d8f --- /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 0000000000..1fcf0f5ecf --- /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") 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: [PATCH 1617/2594] 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 4d72c402ce..98b841face 100644 --- a/extension.py +++ b/extension.py @@ -5,13 +5,9 @@ __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 @@ def __init__ (self, name, sources, 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 @@ def __init__ (self, name, sources, 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 1fcf0f5ecf..159ac2b76e 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 @@ def test_read_setup_file(self): 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) 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: [PATCH 1618/2594] 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 a4054ff454..16d2bef6f4 100644 --- a/extension.py +++ b/extension.py @@ -5,12 +5,9 @@ __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 @@ def __init__(self, name, sources, 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 1fcf0f5ecf..1ee30585fa 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 @@ def test_read_setup_file(self): 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) 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: [PATCH 1619/2594] assertion message was dropped --- extension.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extension.py b/extension.py index 98b841face..53ca8fd0c5 100644 --- a/extension.py +++ b/extension.py @@ -103,7 +103,7 @@ def __init__ (self, name, sources, 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" 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: [PATCH 1620/2594] 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 4fd66cbf60..8f089c3172 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 @@ 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 @@ def initialize_options (self): # 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 @@ def finalize_options (self): # $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 @@ def finalize_options (self): # 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 @@ def finalize_unix (self): 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 @@ def finalize_other (self): # Windows and Mac OS for now 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 @@ def select_scheme (self, name): 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 @@ def _expand_attrs (self, attrs): 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 @@ def handle_extra_path (self): # 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 @@ def handle_extra_path (self): 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 @@ def create_home_path(self): # -- 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 @@ def run (self): "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 @@ def create_path_file (self): # -- 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 @@ def get_outputs (self): 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 @@ def get_inputs (self): 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 @@ def has_data (self): ('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 9ceccf68cf..8d7e97227c 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -10,11 +10,14 @@ 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 @@ def _test_user_site(self): 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) 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: [PATCH 1621/2594] 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 de81173387..2a905d92f8 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 @@ 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 @@ def initialize_options(self): # 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 @@ def finalize_options(self): # 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 @@ def finalize_unix(self): 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 @@ def finalize_other(self): # Windows and Mac OS for now 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 @@ def select_scheme(self, name): 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 @@ def _expand_attrs(self, attrs): 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 @@ def handle_extra_path(self): 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 @@ def handle_extra_path(self): # 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 @@ def handle_extra_path(self): 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 @@ def create_home_path(self): # -- 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 @@ def run(self): 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 @@ def create_path_file(self): # -- 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 @@ def get_outputs(self): 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 @@ def get_inputs(self): 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 9ceccf68cf..8d7e97227c 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -10,11 +10,14 @@ 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 @@ def _test_user_site(self): 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) From 22e57debf56bf09cdf3e1bd12f6b6da4663037b7 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Thu, 4 Jun 2009 09:42:55 +0000 Subject: [PATCH 1622/2594] 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 9249427a43..3b4a036718 100644 --- a/command/upload.py +++ b/command/upload.py @@ -127,7 +127,7 @@ def upload_file(self, command, pyversion, filename): 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' 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: [PATCH 1623/2594] 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 b42e41b373..52e193eb51 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 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: [PATCH 1624/2594] 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 3ac1dceb3e..df5ebaf9a6 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -47,12 +47,19 @@ __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 @@ def __init__ (self, 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 0000000000..1d46580640 --- /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()) 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: [PATCH 1625/2594] 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 ea4c7971fd..f541489190 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -47,12 +47,19 @@ __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 @@ def __init__ (self, 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 0000000000..42c5ea40a6 --- /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()) 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: [PATCH 1626/2594] 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 099e0586fd..4a4fadde92 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 0a5ac2944a..8534881c32 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 @@ 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 @@ def set_executables(self, **kw): 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()) 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: [PATCH 1627/2594] 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 615da07e99..6f99de28e3 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 9f820575ab..9f13952133 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 @@ def test_get_config_vars(self): 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()) 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: [PATCH 1628/2594] 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 223ff672b9..0fbd5412bc 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 322df39cf5..2d8fa2710e 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 @@ def set_executables(self, **kw): 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()) 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: [PATCH 1629/2594] 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 4a4fadde92..dcc7231ac5 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -13,7 +13,6 @@ 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) 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: [PATCH 1630/2594] #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 df5ebaf9a6..fd5296c358 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 1d46580640..fb823e4d80 100644 --- a/tests/test_cygwinccompiler.py +++ b/tests/test_cygwinccompiler.py @@ -8,7 +8,8 @@ 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 @@ def test_get_versions(self): 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) 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: [PATCH 1631/2594] 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 94a7bd96ee..2dabc0f0fe 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): 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: [PATCH 1632/2594] 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 f541489190..a552ffd934 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 42c5ea40a6..c5a6495da5 100644 --- a/tests/test_cygwinccompiler.py +++ b/tests/test_cygwinccompiler.py @@ -8,7 +8,8 @@ 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 @@ def test_get_versions(self): 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) 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: [PATCH 1633/2594] pep8-fied cygwinccompiler module --- cygwinccompiler.py | 95 +++++++++++++++------------------------------- 1 file changed, 30 insertions(+), 65 deletions(-) diff --git a/cygwinccompiler.py b/cygwinccompiler.py index fd5296c358..c35e49d713 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 @@ def __init__ (self, verbose=0, dry_run=0, force=0): # 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 @@ def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): 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 @@ def link (self, 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 @@ def __init__ (self, # 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. 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: [PATCH 1634/2594] 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 a552ffd934..5f3a389e29 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 @@ def __init__ (self, verbose=0, dry_run=0, force=0): # 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 @@ def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): 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 @@ def link (self, 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 @@ def __init__ (self, # 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. 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: [PATCH 1635/2594] 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 52e193eb51..d69c4b690c 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -342,9 +342,14 @@ def add_find_python(self): 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)]) 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: [PATCH 1636/2594] 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 bca98cc23e..2e5685f5c4 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -341,9 +341,14 @@ def add_find_python(self): 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)]) 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: [PATCH 1637/2594] 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 f94d9579d2..da0b30d864 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -282,9 +282,14 @@ def add_find_python(self): 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")]) From 3dae7e94d2ac0d05e9cb68e114ee0babb6cc015e Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 13 Jun 2009 13:15:04 +0000 Subject: [PATCH 1638/2594] bump version to 3.1rc2 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 5a5db53272..374ccf50e9 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.1rc1" +__version__ = "3.1rc2" #--end constants-- 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: [PATCH 1639/2594] code cleanup --- command/upload.py | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/command/upload.py b/command/upload.py index 74b06283f5..d3066816b4 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 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 @@ def upload_file(self, command, pyversion, filename): 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 @@ def upload_file(self, command, pyversion, filename): 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: 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: [PATCH 1640/2594] 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 d3066816b4..8114feb8f2 100644 --- a/command/upload.py +++ b/command/upload.py @@ -5,7 +5,7 @@ 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 @@ def run(self): 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 @@ def upload_file(self, command, pyversion, filename): 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 f57a4a3f33..bbcd80db56 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 @@ [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 @@ def test_upload(self): 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) 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: [PATCH 1641/2594] 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 5a9ff76d67..e093eef2e8 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 0000000000..58c8c5da40 --- /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") 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: [PATCH 1642/2594] 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 94e9edfc09..96f5454e3e 100644 --- a/tests/test_unixccompiler.py +++ b/tests/test_unixccompiler.py @@ -86,6 +86,14 @@ def gcv(v): 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 0b1dc4af7d..129ac8cf11 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -288,23 +288,24 @@ def runtime_library_dir_option(self, 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++": - # 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 From b7c54cd287ae36a0e91930dd390fbae077b91d85 Mon Sep 17 00:00:00 2001 From: Steven Bethard Date: Sun, 21 Jun 2009 21:03:41 +0000 Subject: [PATCH 1643/2594] 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 d69c4b690c..7a5ca807a6 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -315,8 +315,7 @@ def add_files(self): 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): From 358676725f152706531025f7951588a1026f35b6 Mon Sep 17 00:00:00 2001 From: Steven Bethard Date: Sun, 21 Jun 2009 21:07:41 +0000 Subject: [PATCH 1644/2594] 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 2e5685f5c4..c4be47b579 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -314,8 +314,7 @@ def add_files(self): 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): From 32cbe82d88579474ca58b8e7d099a9b13b876e8c Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Fri, 26 Jun 2009 13:11:28 +0000 Subject: [PATCH 1645/2594] 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 374ccf50e9..73c77f1fa6 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.1rc2" +__version__ = "3.1" #--end constants-- From 66e12f773def859e16ea917cd0c03f90f00a43f5 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 27 Jun 2009 21:40:27 +0000 Subject: [PATCH 1646/2594] bump to 3.2a0 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 73c77f1fa6..18b02fc629 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.1" +__version__ = "3.2a0" #--end constants-- From 94800550afc7760fb7c596a29c7f5f7bc8fa56cb Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 27 Jun 2009 22:03:18 +0000 Subject: [PATCH 1647/2594] version to 3.1.1a0 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 73c77f1fa6..994e91fa29 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.1" +__version__ = "3.1.1a0" #--end constants-- 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: [PATCH 1648/2594] 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 3b4a036718..2692377558 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 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 @@ def upload_file(self, command, pyversion, filename): 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: 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: [PATCH 1649/2594] 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 2692377558..defdda642b 100644 --- a/command/upload.py +++ b/command/upload.py @@ -5,10 +5,9 @@ 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 @@ def run(self): 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 @@ def upload_file(self, command, pyversion, filename): 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 95e4ac3315..aaeaffde1c 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 @@ [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 @@ def test_upload(self): 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) 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: [PATCH 1650/2594] 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 875f96fac3..ff7f9df1c3 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 0000000000..58c8c5da40 --- /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") 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: [PATCH 1651/2594] 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 94e9edfc09..96f5454e3e 100644 --- a/tests/test_unixccompiler.py +++ b/tests/test_unixccompiler.py @@ -86,6 +86,14 @@ def gcv(v): 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 c11544d828..26d2856ad4 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -286,23 +286,24 @@ def runtime_library_dir_option(self, 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++": - # 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 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: [PATCH 1652/2594] 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 293c214de7..c2c1bf13a3 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -643,16 +643,16 @@ def get_ext_fullpath(self, ext_name): (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 12b8581c97..5f28b799a0 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -339,10 +339,9 @@ def test_get_outputs(self): # 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 @@ def test_get_outputs(self): 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): 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: [PATCH 1653/2594] 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 31e036bceb..57a110b0d9 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -630,16 +630,16 @@ def get_ext_fullpath(self, ext_name): (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 4ea11a159d..fe6009b617 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -339,10 +339,9 @@ def test_get_outputs(self): # 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 @@ def test_get_outputs(self): 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): 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: [PATCH 1654/2594] 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 31e036bceb..57a110b0d9 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -630,16 +630,16 @@ def get_ext_fullpath(self, ext_name): (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 4ea11a159d..fe6009b617 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -339,10 +339,9 @@ def test_get_outputs(self): # 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 @@ def test_get_outputs(self): 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): From 0b41ce09e9dbf51c0c44d2211982691097de4a0b Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Tue, 30 Jun 2009 22:57:08 +0000 Subject: [PATCH 1655/2594] 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 29cd271faf..91bc4e3d7c 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -47,7 +47,7 @@ def test_make_tarball(self): # 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 @@ def test_make_tarball(self): 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 @@ def test_tarfile_vs_tar(self): # 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 @@ def test_tarfile_vs_tar(self): 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 @@ def test_tarfile_vs_tar(self): 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 @@ def test_tarfile_vs_tar(self): 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 @@ def test_compress_deprecated(self): 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 @@ def test_compress_deprecated(self): 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 2d84007f87..c271567542 100644 --- a/tests/test_bdist_rpm.py +++ b/tests/test_bdist_rpm.py @@ -74,7 +74,7 @@ def test_quiet(self): 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 @@ def test_no_optimize_flag(self): 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 f2cb4fdeba..9b1ba6d107 100644 --- a/tests/test_bdist_wininst.py +++ b/tests/test_bdist_wininst.py @@ -21,7 +21,7 @@ def test_get_exe_bytes(self): # 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 47d85cd8b4..536cd67319 100644 --- a/tests/test_build_clib.py +++ b/tests/test_build_clib.py @@ -135,7 +135,7 @@ def test_run(self): 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 5f28b799a0..467e90d94b 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -73,15 +73,15 @@ def test_build_ext(self): 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 @@ def test_solaris_enable_shared(self): _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 @@ def test_user_site(self): # 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 @@ def test_user_site(self): # 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 @@ def test_finalize_options(self): 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 @@ def test_finalize_options(self): 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 @@ def test_check_extensions_list(self): '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 @@ def test_get_outputs(self): 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 @@ def test_get_outputs(self): 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 54a4ed80fd..c815d8185e 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -52,9 +52,9 @@ def test_package_data(self): 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 b55eb5857b..b1d2d079bd 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 @@ def test_build(self): 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 @@ def test_version_int(self): 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 3026032712..dbc4ee2a18 100755 --- a/tests/test_clean.py +++ b/tests/test_clean.py @@ -35,7 +35,7 @@ def test_simple_run(self): # 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 8f2b36fb1f..d6438b5ea1 100644 --- a/tests/test_cmd.py +++ b/tests/test_cmd.py @@ -70,7 +70,7 @@ def test_ensure_string(self): 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 fd778d1ba5..6947275569 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -105,9 +105,9 @@ def test_server_registration(self): 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 bacf13a291..ef2e7bc317 100644 --- a/tests/test_config_cmd.py +++ b/tests/test_config_cmd.py @@ -73,14 +73,14 @@ def test_clean(self): 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 7ccdd53b79..07f392c4ee 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -73,7 +73,7 @@ def test_command_packages_cmdline(self): 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 @@ def test_simple_metadata(self): "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 @@ def test_provides(self): 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 @@ def test_requires(self): 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 @@ def test_obsoletes(self): 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 @@ def test_custom_pydistutils(self): 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 @@ def test_show_help(self): 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 8d7e97227c..d0ad5ce159 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -88,7 +88,7 @@ def _expanduser(path): 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 @@ def _test_user_site(self): # 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 73c40371d6..7072136792 100644 --- a/tests/test_install_data.py +++ b/tests/test_install_data.py @@ -35,9 +35,9 @@ def test_simple_run(self): # 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 @@ def test_simple_run(self): # 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 @@ def test_simple_run(self): # 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 d768166829..793b95cad6 100644 --- a/tests/test_install_lib.py +++ b/tests/test_install_lib.py @@ -39,8 +39,8 @@ def test_byte_compile(self): 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 @@ def test_get_outputs(self): 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 fffa6ef2c3..b7eb625ac5 100644 --- a/tests/test_install_scripts.py +++ b/tests/test_install_scripts.py @@ -23,15 +23,15 @@ def test_default_settings(self): 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 @@ def write_script(name, text): 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 78ce3b7a84..21242a8eb9 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -46,7 +46,7 @@ def test_reg_class(self): # 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 @@ def test_reg_class(self): 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 0c6bf66989..ada77a015a 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -96,7 +96,7 @@ def test_create_pypirc(self): 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 @@ def test_create_pypirc(self): 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 @@ def _no_way(prompt=''): # 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 @@ def test_registering(self): 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 @@ def test_password_reset(self): 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 8534881c32..edc8fd8d60 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -21,12 +21,12 @@ def tearDown(self): 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 @@ def test_get_python_inc(self): # 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 bbcd80db56..0d95a0917e 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -96,11 +96,11 @@ def test_upload(self): # 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 cee7d5263b..c0acf5f481 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -225,10 +225,10 @@ def test_strtobool(self): 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' From d42774082d0532c78405d5b46f7d9896efcfb7a6 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Tue, 30 Jun 2009 23:06:06 +0000 Subject: [PATCH 1656/2594] 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 663b33532a..d88e0b350d 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -47,7 +47,7 @@ def test_make_tarball(self): # 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 @@ def test_make_tarball(self): 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 @@ def test_tarfile_vs_tar(self): # 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 @@ def test_tarfile_vs_tar(self): 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 @@ def test_tarfile_vs_tar(self): 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 @@ def test_tarfile_vs_tar(self): 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 @@ def test_compress_deprecated(self): 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 @@ def test_compress_deprecated(self): 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 2d84007f87..c271567542 100644 --- a/tests/test_bdist_rpm.py +++ b/tests/test_bdist_rpm.py @@ -74,7 +74,7 @@ def test_quiet(self): 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 @@ def test_no_optimize_flag(self): 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 f2cb4fdeba..9b1ba6d107 100644 --- a/tests/test_bdist_wininst.py +++ b/tests/test_bdist_wininst.py @@ -21,7 +21,7 @@ def test_get_exe_bytes(self): # 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 47d85cd8b4..536cd67319 100644 --- a/tests/test_build_clib.py +++ b/tests/test_build_clib.py @@ -135,7 +135,7 @@ def test_run(self): 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 fe6009b617..08e3e1ff1c 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -74,15 +74,15 @@ def test_build_ext(self): 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 @@ def test_solaris_enable_shared(self): _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 @@ def test_user_site(self): # 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 @@ def test_user_site(self): # 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 @@ def test_finalize_options(self): 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 @@ def test_finalize_options(self): 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 @@ def test_check_extensions_list(self): '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 @@ def test_get_outputs(self): 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 @@ def test_get_outputs(self): 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 75b6624f5a..8ad3bbc452 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -52,9 +52,9 @@ def test_package_data(self): 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 b55eb5857b..b1d2d079bd 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 @@ def test_build(self): 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 @@ def test_version_int(self): 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 3026032712..dbc4ee2a18 100755 --- a/tests/test_clean.py +++ b/tests/test_clean.py @@ -35,7 +35,7 @@ def test_simple_run(self): # 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 8f2b36fb1f..d6438b5ea1 100644 --- a/tests/test_cmd.py +++ b/tests/test_cmd.py @@ -70,7 +70,7 @@ def test_ensure_string(self): 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 0f97cf7a5e..879689d2bb 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -102,9 +102,9 @@ def test_server_registration(self): 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 bacf13a291..ef2e7bc317 100644 --- a/tests/test_config_cmd.py +++ b/tests/test_config_cmd.py @@ -73,14 +73,14 @@ def test_clean(self): 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 16ef68e962..c031cb85ac 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -72,7 +72,7 @@ def test_command_packages_cmdline(self): 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 @@ def test_simple_metadata(self): "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 @@ def test_provides(self): 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 @@ def test_requires(self): 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 @@ def test_obsoletes(self): 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 @@ def test_custom_pydistutils(self): 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 @@ def test_show_help(self): 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 8d7e97227c..d0ad5ce159 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -88,7 +88,7 @@ def _expanduser(path): 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 @@ def _test_user_site(self): # 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 73c40371d6..7072136792 100644 --- a/tests/test_install_data.py +++ b/tests/test_install_data.py @@ -35,9 +35,9 @@ def test_simple_run(self): # 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 @@ def test_simple_run(self): # 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 @@ def test_simple_run(self): # 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 d768166829..793b95cad6 100644 --- a/tests/test_install_lib.py +++ b/tests/test_install_lib.py @@ -39,8 +39,8 @@ def test_byte_compile(self): 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 @@ def test_get_outputs(self): 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 fffa6ef2c3..b7eb625ac5 100644 --- a/tests/test_install_scripts.py +++ b/tests/test_install_scripts.py @@ -23,15 +23,15 @@ def test_default_settings(self): 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 @@ def write_script(name, text): 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 11e5a47630..73827988bb 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -46,7 +46,7 @@ def test_reg_class(self): # 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 @@ def test_reg_class(self): 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 d9ff3ec441..c03ad10147 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -96,7 +96,7 @@ def test_create_pypirc(self): 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 @@ def test_create_pypirc(self): 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 @@ def _no_way(prompt=''): # 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 @@ def test_registering(self): 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 @@ def test_password_reset(self): 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 2d8fa2710e..eb36204439 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -21,12 +21,12 @@ def tearDown(self): 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 @@ def test_get_python_inc(self): # 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 aaeaffde1c..9281885876 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -96,11 +96,11 @@ def test_upload(self): # 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 cee7d5263b..c0acf5f481 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -225,10 +225,10 @@ def test_strtobool(self): 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' 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: [PATCH 1657/2594] 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 d2ea201bd4..b28f89f5a5 100644 --- a/tests/test_bdist_dumb.py +++ b/tests/test_bdist_dumb.py @@ -71,6 +71,21 @@ def test_simple_built(self): # 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) 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: [PATCH 1658/2594] 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 7324c6c2fa..3ab0330f83 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -7,16 +7,17 @@ __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 @@ def initialize_options (self): 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 @@ def finalize_options (self): ('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 @@ def run (self): if not self.keep_temp: remove_tree(self.bdist_dir, dry_run=self.dry_run) - - # run() - -# class bdist_dumb 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: [PATCH 1659/2594] 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 2d39922672..63c0a47537 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -7,16 +7,17 @@ __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 d2ea201bd4..b28f89f5a5 100644 --- a/tests/test_bdist_dumb.py +++ b/tests/test_bdist_dumb.py @@ -71,6 +71,21 @@ def test_simple_built(self): # 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) 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: [PATCH 1660/2594] 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 e9d29ff49c..5bbbf22560 100644 --- a/util.py +++ b/util.py @@ -12,9 +12,10 @@ 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 (match, local_vars=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 @@ def byte_compile (py_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 @@ def byte_compile (py_files, 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 @@ def byte_compile (py_files, 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) 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: [PATCH 1661/2594] 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 4c1956a2b8..ad7ae08d76 100644 --- a/util.py +++ b/util.py @@ -12,9 +12,10 @@ 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 (match, local_vars=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 @@ def byte_compile (py_files, 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 @@ def byte_compile (py_files, 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. """ From 414e03b9816532270f3f29a4f1777c9e8e67804c Mon Sep 17 00:00:00 2001 From: Jesus Cea Date: Thu, 2 Jul 2009 15:37:21 +0000 Subject: [PATCH 1662/2594] 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 55d2d5df7e..e481e8de39 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -57,7 +57,7 @@ def test_run_setup_provides_file(self): 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() 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: [PATCH 1663/2594] 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 c2c1bf13a3..96bd68be4c 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -644,17 +644,23 @@ def get_ext_fullpath(self, ext_name): """ 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 467e90d94b..2ecead3c0d 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -337,7 +337,8 @@ def test_get_outputs(self): 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 @@ def test_get_outputs(self): # 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 @@ def test_build_ext_inplace(self): 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): 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: [PATCH 1664/2594] 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 e6e33ab95c..8cb1efe67e 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -629,18 +629,27 @@ def get_ext_fullpath(self, ext_name): (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 dc8df98fd7..e01c882069 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -274,12 +274,12 @@ def test_get_outputs(self): 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 @@ def test_get_outputs(self): # 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: 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: [PATCH 1665/2594] 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 57a110b0d9..30457026d4 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -631,17 +631,23 @@ def get_ext_fullpath(self, ext_name): """ 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 08e3e1ff1c..6f0e2309d5 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -337,7 +337,8 @@ def test_get_outputs(self): 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 @@ def test_get_outputs(self): # 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 @@ def test_build_ext_inplace(self): 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): 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: [PATCH 1666/2594] 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 57a110b0d9..30457026d4 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -631,17 +631,23 @@ def get_ext_fullpath(self, ext_name): """ 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 fe6009b617..f0580c1f0f 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -337,7 +337,8 @@ def test_get_outputs(self): 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 @@ def test_get_outputs(self): # 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 @@ def test_build_ext_inplace(self): 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): 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: [PATCH 1667/2594] 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 d9c15749d0..5d249f3ddc 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -4,16 +4,15 @@ __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 @@ def initialize_options (self): 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 @@ def finalize_options (self): # 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 @@ def run (self): 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 @@ def get_data_files (self): 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 @@ def find_data_files (self, package, src_dir): 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 @@ def build_package_data (self): 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 @@ def get_package_dir (self, package): 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 @@ def check_package (self, package, package_dir): # __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 @@ def find_package_modules (self, package, package_dir): 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 @@ def find_modules (self): ".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 @@ def find_modules (self): # 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 @@ def find_modules (self): 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 @@ def find_all_modules (self): 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 @@ def get_outputs (self, include_bytecode=1): 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 @@ def build_module (self, module, module_file, package): 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 @@ def build_modules (self): # 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 @@ def build_packages (self): 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 @@ def byte_compile (self, files): if self.optimize > 0: byte_compile(files, optimize=self.optimize, force=self.force, prefix=prefix, dry_run=self.dry_run) - -# class build_py 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: [PATCH 1668/2594] 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 99d85be96c..4cc80f7b4a 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -4,15 +4,15 @@ __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)" 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: [PATCH 1669/2594] 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 fac4a5d1a9..99b421f73f 100644 --- a/tests/test_file_util.py +++ b/tests/test_file_util.py @@ -3,7 +3,7 @@ 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 @@ def test_move_file_verbosity(self): 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) 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: [PATCH 1670/2594] 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 0c890559ee..d8e8fd5f8d 100644 --- a/file_util.py +++ b/file_util.py @@ -10,49 +10,48 @@ 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 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: [PATCH 1671/2594] 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 65aa7e0fdd..758bde38bf 100644 --- a/file_util.py +++ b/file_util.py @@ -10,17 +10,18 @@ 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 fac4a5d1a9..99b421f73f 100644 --- a/tests/test_file_util.py +++ b/tests/test_file_util.py @@ -3,7 +3,7 @@ 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 @@ def test_move_file_verbosity(self): 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) 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: [PATCH 1672/2594] 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 f4aecb419e..7c29bf6ca7 100644 --- a/dist.py +++ b/dist.py @@ -914,8 +914,8 @@ def reinitialize_command(self, command, reinit_subcommands=0): # -- 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. 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: [PATCH 1673/2594] 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 915d336431..092ec0d220 100644 --- a/dist.py +++ b/dist.py @@ -907,8 +907,8 @@ def reinitialize_command(self, command, reinit_subcommands=0): # -- 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. 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: [PATCH 1674/2594] 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 915d336431..092ec0d220 100644 --- a/dist.py +++ b/dist.py @@ -907,8 +907,8 @@ def reinitialize_command(self, command, reinit_subcommands=0): # -- 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. 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: [PATCH 1675/2594] 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 7c29bf6ca7..afed545d92 100644 --- a/dist.py +++ b/dist.py @@ -602,14 +602,14 @@ def _show_help(self, parser, global_options=1, display_options=1, 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 @@ def _show_help(self, parser, global_options=1, display_options=1, 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 @@ def handle_display_options(self, option_order): # 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 @@ def handle_display_options(self, option_order): 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 @@ def print_command_list(self, commands, header, max_length): """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 @@ def print_command_list(self, commands, header, max_length): 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 07f392c4ee..75b74a2c28 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -165,24 +165,6 @@ def test_finalize_options(self): 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) 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: [PATCH 1676/2594] 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 092ec0d220..ac5a0ca012 100644 --- a/dist.py +++ b/dist.py @@ -596,14 +596,14 @@ def _show_help(self, parser, global_options=1, display_options=1, 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 @@ def _show_help(self, parser, global_options=1, display_options=1, 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 @@ def handle_display_options(self, option_order): # 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 @@ def handle_display_options(self, option_order): 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 @@ def print_command_list(self, commands, header, max_length): """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 @@ def print_command_list(self, commands, header, max_length): 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 c031cb85ac..9f795f4314 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -136,24 +136,6 @@ def test_finalize_options(self): 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) 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: [PATCH 1677/2594] 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 092ec0d220..ac5a0ca012 100644 --- a/dist.py +++ b/dist.py @@ -596,14 +596,14 @@ def _show_help(self, parser, global_options=1, display_options=1, 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 @@ def _show_help(self, parser, global_options=1, display_options=1, 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 @@ def handle_display_options(self, option_order): # 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 @@ def handle_display_options(self, option_order): 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 @@ def print_command_list(self, commands, header, max_length): """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 @@ def print_command_list(self, commands, header, max_length): 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 16ef68e962..40760390c8 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -136,24 +136,6 @@ def test_finalize_options(self): 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) 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: [PATCH 1678/2594] 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 96bd68be4c..be37313abc 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -8,6 +8,8 @@ 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 @@ def run(self): # 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 @@ def build_extension(self, ext): 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 @@ def build_extension(self, ext): 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 @@ def get_libraries (self, ext): # 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 2ecead3c0d..afd0df32dc 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -3,6 +3,9 @@ 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 @@ def test_ext_fullpath(self): 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): 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: [PATCH 1679/2594] 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 30457026d4..4a3aec2a65 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -7,6 +7,8 @@ __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 @@ def run(self): # 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 @@ def build_extension(self, ext): 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 @@ def build_extension(self, ext): 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 @@ def get_libraries(self, ext): # 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 6f0e2309d5..d531293015 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -3,6 +3,9 @@ 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 @@ def test_ext_fullpath(self): 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): 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: [PATCH 1680/2594] 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 be37313abc..fadd6337f7 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -134,13 +134,17 @@ def __setattr__(self, 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. " + 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 @@ def run(self): # 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 afd0df32dc..9356ff806e 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -402,12 +402,21 @@ 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 = 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() 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: [PATCH 1681/2594] 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 4a3aec2a65..d8bb251294 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -132,13 +132,17 @@ def __setattr__(self, 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. " + 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 @@ def run(self): # 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 d531293015..6829583746 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -402,12 +402,21 @@ 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 = 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() 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: [PATCH 1682/2594] 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 fadd6337f7..86513ea9e9 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -136,7 +136,7 @@ def _set_compiler(self, compiler): # 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 @@ def _get_compiler(self): # 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) 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: [PATCH 1683/2594] 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 d8bb251294..6f90499229 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -134,7 +134,7 @@ def _set_compiler(self, compiler): # 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 @@ def _get_compiler(self): # 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) 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: [PATCH 1684/2594] 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 9356ff806e..4c09dd80a3 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -362,6 +362,7 @@ def test_get_outputs(self): 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 @@ def test_ext_fullpath(self): 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 @@ def test_ext_fullpath(self): 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): 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: [PATCH 1685/2594] 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 e01c882069..153c875644 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -296,20 +296,21 @@ def test_get_outputs(self): 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 @@ def test_ext_fullpath(self): 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(): 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: [PATCH 1686/2594] 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 6829583746..d97a97eba0 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -362,6 +362,7 @@ def test_get_outputs(self): 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 @@ def test_ext_fullpath(self): 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 @@ def test_ext_fullpath(self): 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): 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: [PATCH 1687/2594] 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 f0580c1f0f..c671483622 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -359,6 +359,7 @@ def test_get_outputs(self): 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 @@ def test_ext_fullpath(self): 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 @@ def test_ext_fullpath(self): 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(): 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: [PATCH 1688/2594] 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 0000000000..6bca27ee06 --- /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") 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: [PATCH 1689/2594] 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 84e050204e..d394e4b1da 100644 --- a/command/build.py +++ b/command/build.py @@ -9,13 +9,11 @@ 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 @@ def initialize_options (self): 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 @@ def finalize_options (self): 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 @@ def run (self): 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 @@ def has_ext_modules (self): 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 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: [PATCH 1690/2594] 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 9c2667cfd2..715621e1d7 100644 --- a/command/build.py +++ b/command/build.py @@ -9,12 +9,10 @@ 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 @@ def run(self): 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 @@ def has_ext_modules(self): 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 0000000000..6bca27ee06 --- /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") 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: [PATCH 1691/2594] 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 86513ea9e9..0c79476bac 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -6,8 +6,7 @@ __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 @@ def _get_compiler(self): 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 @@ def finalize_options(self): 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 @@ def finalize_options(self): 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 @@ def build_extensions(self): 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 @@ def build_extension(self, ext): 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 @@ def swig_sources (self, sources, extension): 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 @@ def find_swig (self): ("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 @@ def get_ext_filename(self, ext_name): "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 @@ def get_libraries (self, ext): return ext.libraries + [pythonlib] else: return ext.libraries - -# class build_ext 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: [PATCH 1692/2594] 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 0c79476bac..17cb26b526 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -732,10 +732,10 @@ def get_ext_filename(self, ext_name): 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 4c09dd80a3..7886c792bd 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -299,7 +299,7 @@ def test_compiler_option(self): 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]}) 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: [PATCH 1693/2594] 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 8cb1efe67e..ccc3fe564c 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -680,11 +680,10 @@ def get_ext_filename(self, ext_name): 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 153c875644..d992548059 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -233,7 +233,7 @@ def test_compiler_option(self): 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]}) 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: [PATCH 1694/2594] 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 6f90499229..14c529ac5b 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -726,7 +726,7 @@ 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 "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 d97a97eba0..7a27f343a7 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -299,7 +299,7 @@ def test_compiler_option(self): 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]}) 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: [PATCH 1695/2594] 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 30457026d4..1596586499 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -679,7 +679,7 @@ 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 "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 c671483622..9861ee2993 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -296,7 +296,7 @@ def test_compiler_option(self): 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]}) 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: [PATCH 1696/2594] 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 17cb26b526..0c79476bac 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -732,10 +732,10 @@ def get_ext_filename(self, ext_name): 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 7886c792bd..51a21a68b4 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -299,7 +299,7 @@ def test_compiler_option(self): 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]}) 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: [PATCH 1697/2594] 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 ccc3fe564c..db82f1d0d6 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -680,10 +680,10 @@ def get_ext_filename(self, ext_name): 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 d992548059..d0716485dc 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -233,7 +233,7 @@ def test_compiler_option(self): 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]}) From 0e0874b3e717156832ea24bb06f46147af1103dd Mon Sep 17 00:00:00 2001 From: Hirokazu Yamamoto Date: Sun, 12 Jul 2009 02:04:47 +0000 Subject: [PATCH 1698/2594] 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 51a21a68b4..4c09dd80a3 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -299,7 +299,7 @@ def test_compiler_option(self): 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]}) From da220af37e1ec7552ff8f8724d03136f6aa96b4d Mon Sep 17 00:00:00 2001 From: Hirokazu Yamamoto Date: Sun, 12 Jul 2009 02:09:25 +0000 Subject: [PATCH 1699/2594] 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 d0716485dc..153c875644 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -233,7 +233,7 @@ def test_compiler_option(self): 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]}) 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: [PATCH 1700/2594] 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 5f3a389e29..8504371810 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 c5a6495da5..a57694d48e 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 @@ 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]) + # 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 @@ def test_get_versions(self): 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) 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: [PATCH 1701/2594] 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 5f3a389e29..8504371810 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 c5a6495da5..a57694d48e 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 @@ 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]) + # 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 @@ def test_get_versions(self): 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) 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: [PATCH 1702/2594] #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 c35e49d713..a8476e6579 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -50,16 +50,15 @@ 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 @@ def __init__(self, verbose=0, dry_run=0, force=0): % 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 f52e63232d..a5a2ef88fe 100644 --- a/emxccompiler.py +++ b/emxccompiler.py @@ -21,12 +21,15 @@ __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 @@ def __init__ (self, ("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 fb823e4d80..b54ffff361 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 @@ def setUp(self): 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 @@ def test_check_config_h(self): 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 @@ def test_get_msvcr(self): '[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 0000000000..6e1deced35 --- /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 c0acf5f481..ffa92bd07e 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -6,15 +6,33 @@ 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 @@ def setUp(self): 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 @@ def tearDown(self): 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 @@ def test_rfc822_escape(self): '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 5bbbf22560..459c36462d 100644 --- a/util.py +++ b/util.py @@ -7,10 +7,12 @@ __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 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: [PATCH 1703/2594] 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 8504371810..d9f4a43df7 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -50,16 +50,15 @@ 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 @@ def __init__(self, verbose=0, dry_run=0, force=0): % 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 62a4c5b4e8..50634d6c06 100644 --- a/emxccompiler.py +++ b/emxccompiler.py @@ -21,12 +21,15 @@ __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 @@ def __init__ (self, ("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 a57694d48e..98f0f08ef1 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 @@ def setUp(self): 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 @@ def test_check_config_h(self): 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 @@ def test_get_msvcr(self): '[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 0000000000..2176d641d0 --- /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 c0acf5f481..e9065ff59c 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -6,15 +6,33 @@ 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 @@ def setUp(self): 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 @@ def tearDown(self): 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 @@ def test_rfc822_escape(self): '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 ad7ae08d76..0c88b8197d 100644 --- a/util.py +++ b/util.py @@ -7,10 +7,12 @@ __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): From fcfd0cb5014a3b4f0bda28a8210a1286e8e63f71 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sun, 19 Jul 2009 21:52:02 +0000 Subject: [PATCH 1704/2594] 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 c03ad10147..acda1b1ac1 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -202,10 +202,10 @@ def test_strict(self): 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 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: [PATCH 1705/2594] 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 53ca8fd0c5..6af1810801 100644 --- a/extension.py +++ b/extension.py @@ -103,10 +103,11 @@ def __init__ (self, name, sources, 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 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: [PATCH 1706/2594] 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 16d2bef6f4..5c07bdae82 100644 --- a/extension.py +++ b/extension.py @@ -103,10 +103,11 @@ def __init__(self, name, sources, 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 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: [PATCH 1707/2594] 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 16d2bef6f4..5c07bdae82 100644 --- a/extension.py +++ b/extension.py @@ -103,10 +103,11 @@ def __init__(self, name, sources, 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 From 8c7f12ea567dc59e1031dbffdda12247dfce0b9e Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Thu, 13 Aug 2009 08:51:18 +0000 Subject: [PATCH 1708/2594] 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 663b33532a..d88e0b350d 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -47,7 +47,7 @@ def test_make_tarball(self): # 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 @@ def test_make_tarball(self): 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 @@ def test_tarfile_vs_tar(self): # 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 @@ def test_tarfile_vs_tar(self): 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 @@ def test_tarfile_vs_tar(self): 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 @@ def test_tarfile_vs_tar(self): 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 @@ def test_compress_deprecated(self): 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 @@ def test_compress_deprecated(self): 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 2d84007f87..c271567542 100644 --- a/tests/test_bdist_rpm.py +++ b/tests/test_bdist_rpm.py @@ -74,7 +74,7 @@ def test_quiet(self): 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 @@ def test_no_optimize_flag(self): 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 f2cb4fdeba..9b1ba6d107 100644 --- a/tests/test_bdist_wininst.py +++ b/tests/test_bdist_wininst.py @@ -21,7 +21,7 @@ def test_get_exe_bytes(self): # 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 47d85cd8b4..536cd67319 100644 --- a/tests/test_build_clib.py +++ b/tests/test_build_clib.py @@ -135,7 +135,7 @@ def test_run(self): 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 9861ee2993..1221fd43af 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -74,15 +74,15 @@ def test_build_ext(self): 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 @@ def test_solaris_enable_shared(self): _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 @@ def test_user_site(self): # 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 @@ def test_user_site(self): # 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 @@ def test_finalize_options(self): 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 @@ def test_finalize_options(self): 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 @@ def test_check_extensions_list(self): '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 @@ def test_get_outputs(self): 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 @@ def test_get_outputs(self): 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 75b6624f5a..8ad3bbc452 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -52,9 +52,9 @@ def test_package_data(self): 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 b55eb5857b..b1d2d079bd 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 @@ def test_build(self): 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 @@ def test_version_int(self): 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 3026032712..dbc4ee2a18 100755 --- a/tests/test_clean.py +++ b/tests/test_clean.py @@ -35,7 +35,7 @@ def test_simple_run(self): # 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 8f2b36fb1f..d6438b5ea1 100644 --- a/tests/test_cmd.py +++ b/tests/test_cmd.py @@ -70,7 +70,7 @@ def test_ensure_string(self): 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 0f97cf7a5e..879689d2bb 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -102,9 +102,9 @@ def test_server_registration(self): 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 bacf13a291..ef2e7bc317 100644 --- a/tests/test_config_cmd.py +++ b/tests/test_config_cmd.py @@ -73,14 +73,14 @@ def test_clean(self): 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 40760390c8..9f795f4314 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -72,7 +72,7 @@ def test_command_packages_cmdline(self): 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 @@ def test_simple_metadata(self): "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 @@ def test_provides(self): 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 @@ def test_requires(self): 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 @@ def test_obsoletes(self): 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 @@ def test_custom_pydistutils(self): 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 @@ def test_show_help(self): 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 8d7e97227c..d0ad5ce159 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -88,7 +88,7 @@ def _expanduser(path): 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 @@ def _test_user_site(self): # 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 73c40371d6..7072136792 100644 --- a/tests/test_install_data.py +++ b/tests/test_install_data.py @@ -35,9 +35,9 @@ def test_simple_run(self): # 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 @@ def test_simple_run(self): # 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 @@ def test_simple_run(self): # 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 d768166829..793b95cad6 100644 --- a/tests/test_install_lib.py +++ b/tests/test_install_lib.py @@ -39,8 +39,8 @@ def test_byte_compile(self): 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 @@ def test_get_outputs(self): 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 fffa6ef2c3..b7eb625ac5 100644 --- a/tests/test_install_scripts.py +++ b/tests/test_install_scripts.py @@ -23,15 +23,15 @@ def test_default_settings(self): 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 @@ def write_script(name, text): 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 11e5a47630..73827988bb 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -46,7 +46,7 @@ def test_reg_class(self): # 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 @@ def test_reg_class(self): 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 d9ff3ec441..c03ad10147 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -96,7 +96,7 @@ def test_create_pypirc(self): 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 @@ def test_create_pypirc(self): 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 @@ def _no_way(prompt=''): # 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 @@ def test_registering(self): 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 @@ def test_password_reset(self): 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 2d8fa2710e..eb36204439 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -21,12 +21,12 @@ def tearDown(self): 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 @@ def test_get_python_inc(self): # 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 95e4ac3315..828f20c539 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -107,7 +107,7 @@ def test_upload(self): # 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 cee7d5263b..c0acf5f481 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -225,10 +225,10 @@ def test_strtobool(self): 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' From 7329a6d2504982f7ad4261bf7a1f24e4063ee2d2 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Thu, 13 Aug 2009 15:47:36 +0000 Subject: [PATCH 1709/2594] 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 994e91fa29..e7f21232fd 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.1.1a0" +__version__ = "3.1.1rc1" #--end constants-- From e5d9d64f150067264eedc7738da02f46d06e7827 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sun, 16 Aug 2009 21:59:05 +0000 Subject: [PATCH 1710/2594] bump to 3.1.1 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index e7f21232fd..8c0e5a3cba 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.1.1rc1" +__version__ = "3.1.1" #--end constants-- 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: [PATCH 1711/2594] 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 3ba5720ca9..5a88fd4e21 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 86db557441..1faccfae7e 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) 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: [PATCH 1712/2594] 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 58a2bfb108..06a8da9a07 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 86db557441..1faccfae7e 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) 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: [PATCH 1713/2594] module cleanup --- filelist.py | 99 +++++++++++++++++++++++------------------------------ 1 file changed, 42 insertions(+), 57 deletions(-) diff --git a/filelist.py b/filelist.py index 5a88fd4e21..de665e7a8c 100644 --- a/filelist.py +++ b/filelist.py @@ -6,15 +6,13 @@ __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 @@ def debug_print (self, msg): # -- 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 @@ def sort (self): # -- 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 @@ def remove_duplicates (self): # -- "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 @@ def _parse_template_line (self, line): 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 @@ def process_template_line (self, line): # 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 @@ def process_template_line (self, line): 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 @@ def process_template_line (self, line): 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 @@ def process_template_line (self, line): # -- 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 @@ def include_pattern (self, pattern, 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 @@ def exclude_pattern (self, pattern, 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 () 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: [PATCH 1714/2594] 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 06a8da9a07..b6a1dfeebf 100644 --- a/filelist.py +++ b/filelist.py @@ -180,10 +180,11 @@ def process_template_line(self, line): 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 @@ def include_pattern(self, pattern, anchor=1, prefix=None, is_regex=0): 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: 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: [PATCH 1715/2594] 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 de665e7a8c..7cf05098ed 100644 --- a/filelist.py +++ b/filelist.py @@ -115,7 +115,7 @@ def process_template_line(self, line): # 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 @@ def process_template_line(self, line): 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 1faccfae7e..181a58d815 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 @@ def test_glob_to_re(self): 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) 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: [PATCH 1716/2594] 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 181a58d815..cf64c744d2 100644 --- a/tests/test_filelist.py +++ b/tests/test_filelist.py @@ -36,7 +36,7 @@ def test_process_template_line(self): 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 @@ def test_process_template_line(self): 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) 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: [PATCH 1717/2594] 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 b6a1dfeebf..bfc6df694a 100644 --- a/filelist.py +++ b/filelist.py @@ -108,7 +108,7 @@ def process_template_line(self, line): # 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 @@ def process_template_line(self, 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): diff --git a/tests/test_filelist.py b/tests/test_filelist.py index 1faccfae7e..cf64c744d2 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 @@ def test_glob_to_re(self): 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) 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: [PATCH 1718/2594] 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 96f5454e3e..1b7dd4cd64 100644 --- a/tests/test_unixccompiler.py +++ b/tests/test_unixccompiler.py @@ -36,7 +36,23 @@ def test_runtime_libdir_option(self): # 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 129ac8cf11..2083f82982 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -285,7 +285,9 @@ def runtime_library_dir_option(self, dir): # 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++": 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: [PATCH 1719/2594] 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 045368a925..7556cbdbf5 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -284,7 +284,9 @@ def runtime_library_dir_option(self, dir): # 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++": 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: [PATCH 1720/2594] 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 96f5454e3e..1b7dd4cd64 100644 --- a/tests/test_unixccompiler.py +++ b/tests/test_unixccompiler.py @@ -36,7 +36,23 @@ def test_runtime_libdir_option(self): # 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 26d2856ad4..da85c89696 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -283,7 +283,9 @@ def runtime_library_dir_option(self, dir): # 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++": 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: [PATCH 1721/2594] 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 94e9edfc09..be2df5c6e3 100644 --- a/tests/test_unixccompiler.py +++ b/tests/test_unixccompiler.py @@ -36,7 +36,23 @@ def test_runtime_libdir_option(self): # 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 c11544d828..8bbdb4b329 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -283,7 +283,9 @@ def runtime_library_dir_option(self, dir): # 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: 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: [PATCH 1722/2594] 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 db82f1d0d6..a3e3982f6d 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -642,9 +642,6 @@ def get_ext_fullpath(self, ext_name): # 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 153c875644..d8d3667751 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -329,6 +329,19 @@ def test_ext_fullpath(self): 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: From 7bb74b86b24f8f300907200e02d5319db044736f Mon Sep 17 00:00:00 2001 From: Ezio Melotti Date: Sat, 12 Sep 2009 14:43:43 +0000 Subject: [PATCH 1723/2594] #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 91bc4e3d7c..d6fb676920 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -19,10 +19,18 @@ 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 @@ def _create_files(self): 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 @@ def test_compress_deprecated(self): 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 b28f89f5a5..a838f73476 100644 --- a/tests/test_bdist_dumb.py +++ b/tests/test_bdist_dumb.py @@ -4,6 +4,13 @@ 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 @@ def tearDown(self): 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 5808ca16eb..c2feccb3ce 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -3,6 +3,14 @@ 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 @@ def _warn(*args): 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 @@ def test_prune_file_list(self): # 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 @@ def test_make_distribution(self): 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 @@ def test_add_defaults(self): 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={}) From a706937e76582c6005306a391d08feecc68e0966 Mon Sep 17 00:00:00 2001 From: Ezio Melotti Date: Sat, 12 Sep 2009 18:41:20 +0000 Subject: [PATCH 1724/2594] 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 d88e0b350d..d33b7e15b1 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -19,10 +19,18 @@ 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 @@ def _create_files(self): 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 @@ def test_compress_deprecated(self): 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 b28f89f5a5..a838f73476 100644 --- a/tests/test_bdist_dumb.py +++ b/tests/test_bdist_dumb.py @@ -4,6 +4,13 @@ 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 @@ def tearDown(self): 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 b7e5859cb9..986288e593 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -3,6 +3,14 @@ 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 @@ def _warn(*args): 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 @@ def test_prune_file_list(self): # 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 @@ def test_make_distribution(self): 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 @@ def test_add_defaults(self): 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={}) From 5132893c6fa2799cfb9d23b35ff1828f8c621421 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Tue, 15 Sep 2009 19:13:15 +0000 Subject: [PATCH 1725/2594] 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 459c36462d..fe6851cb87 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. From d057f0f1b7dbf7446e4b979ccf8da90ce89995de Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Tue, 15 Sep 2009 19:14:37 +0000 Subject: [PATCH 1726/2594] 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 e9d29ff49c..ee5829b077 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. From 589e2a746f2e3812fe1e7840391a61b20b3fdaf4 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Tue, 15 Sep 2009 19:16:02 +0000 Subject: [PATCH 1727/2594] 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 0c88b8197d..6709bbfe42 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. From b2dfd2fe8b6c80780cfa28e5ca013124fb1f45b6 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Tue, 15 Sep 2009 19:16:40 +0000 Subject: [PATCH 1728/2594] 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 4c1956a2b8..5054d0c867 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. From 56e40eafce0c788acacca65aef4f0fc93640aedd Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Tue, 15 Sep 2009 21:24:07 +0000 Subject: [PATCH 1729/2594] 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 ffa92bd07e..7190b2c9c6 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -142,15 +142,35 @@ def test_get_platform(self): '-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) ' 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: [PATCH 1730/2594] 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 d8d3667751..94000a57d9 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -338,7 +338,8 @@ def test_build_ext_inplace(self): 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) 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: [PATCH 1731/2594] 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 58c8c5da40..2c219b7b17 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 @@ def test_gen_lib_options(self): '-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 d6438b5ea1..2174efbf64 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 @@ def test_ensure_dirname(self): 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 e481e8de39..3c26fddbc6 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -6,6 +6,7 @@ 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 @@ def test_run_setup_uses_current_dir(self): 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 cf64c744d2..0cbb48bcc6 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 @@ def test_process_template_line(self): 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 d0ad5ce159..d2b8c8e9b6 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -6,6 +6,8 @@ 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.tests import support - class InstallTestCase(support.TempdirManager, support.LoggingSilencer, unittest.TestCase): @@ -183,6 +184,17 @@ def test_record(self): 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) 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: [PATCH 1732/2594] 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 aff7d68e3c..ae4efc7efc 100644 --- a/cmd.py +++ b/cmd.py @@ -157,7 +157,7 @@ def dump_options(self, header=None, indent=""): 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 72441fb43d..879d4d25bf 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -26,7 +26,7 @@ # 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 @@ 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 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 58c8c5da40..9db8d24617 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 @@ def test_gen_lib_options(self): '-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 d6438b5ea1..55ae421d46 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 @@ def test_ensure_dirname(self): 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 7f021dcb3b..b5f391f57a 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -6,6 +6,7 @@ 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 @@ def test_run_setup_uses_current_dir(self): 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 cf64c744d2..d98325ae54 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 @@ def test_process_template_line(self): 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 d0ad5ce159..2087a0eba0 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -6,6 +6,8 @@ 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.tests import support - class InstallTestCase(support.TempdirManager, support.LoggingSilencer, unittest.TestCase): @@ -183,6 +184,17 @@ def test_record(self): 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) 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: [PATCH 1733/2594] 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 aff7d68e3c..ae4efc7efc 100644 --- a/cmd.py +++ b/cmd.py @@ -157,7 +157,7 @@ def dump_options(self, header=None, indent=""): 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 72441fb43d..879d4d25bf 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -26,7 +26,7 @@ # 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 @@ 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 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 d6438b5ea1..55ae421d46 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 @@ def test_ensure_dirname(self): 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 7f021dcb3b..b5f391f57a 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -6,6 +6,7 @@ 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 @@ def test_run_setup_uses_current_dir(self): 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 86db557441..b9db8f61fe 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 @@ def test_glob_to_re(self): 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 d0ad5ce159..2087a0eba0 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -6,6 +6,8 @@ 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.tests import support - class InstallTestCase(support.TempdirManager, support.LoggingSilencer, unittest.TestCase): @@ -183,6 +184,17 @@ def test_record(self): 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) 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: [PATCH 1734/2594] 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 75b74a2c28..553f30c748 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -9,6 +9,7 @@ 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 @@ def create_distribution(self, configfiles=()): 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() 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: [PATCH 1735/2594] #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 afed545d92..f49afcce13 100644 --- a/dist.py +++ b/dist.py @@ -359,7 +359,7 @@ def parse_config_files(self, filenames=None): 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 6f949d5179..758857081c 100644 --- a/log.py +++ b/log.py @@ -17,6 +17,9 @@ def __init__(self, threshold=WARN): 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 553f30c748..3d4b25f0fc 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -200,6 +200,13 @@ def test_get_command_packages(self): 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): 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: [PATCH 1736/2594] 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 9f795f4314..91297f0e15 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -8,6 +8,7 @@ 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 @@ def create_distribution(self, configfiles=()): 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() 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: [PATCH 1737/2594] 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 bce034949e..3c328cc9a6 100644 --- a/tests/support.py +++ b/tests/support.py @@ -4,6 +4,7 @@ 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 @@ def tearDown(self): 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): 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: [PATCH 1738/2594] 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 ac5a0ca012..1c1ea477db 100644 --- a/dist.py +++ b/dist.py @@ -354,7 +354,7 @@ def parse_config_files(self, filenames=None): 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 6f949d5179..758857081c 100644 --- a/log.py +++ b/log.py @@ -17,6 +17,9 @@ def __init__(self, threshold=WARN): 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 1255413989..ea122111ca 100644 --- a/tests/support.py +++ b/tests/support.py @@ -4,6 +4,7 @@ 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 @@ def tearDown(self): 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 91297f0e15..799b0c0603 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -171,6 +171,13 @@ def test_get_command_packages(self): 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): 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: [PATCH 1739/2594] 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 ac5a0ca012..1c1ea477db 100644 --- a/dist.py +++ b/dist.py @@ -354,7 +354,7 @@ def parse_config_files(self, filenames=None): 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 6f949d5179..758857081c 100644 --- a/log.py +++ b/log.py @@ -17,6 +17,9 @@ def __init__(self, threshold=WARN): 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 1255413989..ea122111ca 100644 --- a/tests/support.py +++ b/tests/support.py @@ -4,6 +4,7 @@ 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 @@ def tearDown(self): 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 9f795f4314..70c9ec50e2 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -149,6 +149,13 @@ def test_get_command_packages(self): 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): 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: [PATCH 1740/2594] 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 e9065ff59c..84caea5cac 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -142,15 +142,35 @@ def test_get_platform(self): '-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) ' 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: [PATCH 1741/2594] 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 c0acf5f481..795edb8cdc 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -115,15 +115,35 @@ def test_get_platform(self): '-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) ' From 4c66627839fa1384e17f427eebf17adbc807376d Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Tue, 22 Sep 2009 19:27:44 +0000 Subject: [PATCH 1742/2594] 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 dcc7231ac5..6ca6720adb 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: From f7a196298b22da695467cb9494743ce15683fca2 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Tue, 22 Sep 2009 19:31:34 +0000 Subject: [PATCH 1743/2594] 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 6f99de28e3..8306202fb3 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: From 9c216a69630f8d8dfdf3365e5d26fe47b313b7ee Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Tue, 29 Sep 2009 22:41:09 +0000 Subject: [PATCH 1744/2594] Bumping for 2.6.3rc1 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index ba926063e1..c8787040e2 100644 --- a/__init__.py +++ b/__init__.py @@ -22,5 +22,5 @@ # #--start constants-- -__version__ = "2.6.2" +__version__ = "2.6.3rc1" #--end constants-- From 4d101962a4b4069c967df29bdea6e0be5e8071fd Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Thu, 1 Oct 2009 23:39:49 +0000 Subject: [PATCH 1745/2594] Bump to 2.6.3 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index c8787040e2..fd52b8d7c5 100644 --- a/__init__.py +++ b/__init__.py @@ -22,5 +22,5 @@ # #--start constants-- -__version__ = "2.6.3rc1" +__version__ = "2.6.3" #--end constants-- 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: [PATCH 1746/2594] 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 cae7689884..23291da819 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 @@ def tearDown(self): 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 59c24fd433..e322c1385c 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -12,9 +12,6 @@ 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 @@ def test_prune_file_list(self): 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 @@ def test_prune_file_list(self): 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 @@ def test_make_distribution(self): 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, 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: [PATCH 1747/2594] #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 62150b0ddd..75318eba61 100644 --- a/archive_util.py +++ b/archive_util.py @@ -14,15 +14,55 @@ 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 869258babb..dc40c14d61 100644 --- a/cmd.py +++ b/cmd.py @@ -385,10 +385,11 @@ def spawn (self, cmd, search_path=1, level=1): 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 ec3375e5c4..8e0ad080b9 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 @@ def initialize_options(self): 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 @@ def run(self): 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 3ab0330f83..358a70eacf 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 @@ def initialize_options (self): 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 @@ def finalize_options(self): ('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 @@ def run (self): # 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 c21f3917b6..5c74e5329f 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -74,6 +74,10 @@ def checking_metadata(self): ('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 @@ def initialize_options(self): 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 @@ def make_distribution(self): 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 d6fb676920..3faf5e0d3b 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -13,6 +13,13 @@ 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 @@ def test_make_tarball(self): 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 @@ def test_make_archive(self): 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 c2feccb3ce..b8e3dca560 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -3,6 +3,7 @@ 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 @@ 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 @@ def test_finalize_options(self): 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) 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: [PATCH 1748/2594] 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 a568854e4e..d4525998fd 100644 --- a/archive_util.py +++ b/archive_util.py @@ -14,15 +14,55 @@ 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 ae4efc7efc..08632099aa 100644 --- a/cmd.py +++ b/cmd.py @@ -367,9 +367,11 @@ def spawn(self, cmd, search_path=1, level=1): 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 1a360b59e3..2c81f3a9c0 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 @@ def initialize_options(self): 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 @@ def run(self): 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 63c0a47537..49fd653a19 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 @@ def initialize_options(self): 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 @@ def run(self): # 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 ace9eeeb61..76e1de8100 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -75,6 +75,10 @@ def checking_metadata(self): ('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 @@ def initialize_options(self): 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 @@ def make_distribution(self): 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 d33b7e15b1..9990df36c3 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -13,6 +13,13 @@ 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 @@ def test_make_tarball(self): 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 @@ def test_make_archive(self): 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 986288e593..c10e973ed7 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -3,6 +3,7 @@ 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 @@ 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 @@ def test_finalize_options(self): 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) 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: [PATCH 1749/2594] 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 75318eba61..4ace7bdbf0 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 55607e5b53..3e2cd35385 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 7cf05098ed..4aac6d397a 100644 --- a/filelist.py +++ b/filelist.py @@ -61,7 +61,7 @@ def sort(self): 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 --------------------------- 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: [PATCH 1750/2594] 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 9f13952133..88f9ed865d 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -30,30 +30,13 @@ def test_get_python_lib(self): 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() 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: [PATCH 1751/2594] 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 4ace7bdbf0..bc5edfd864 100644 --- a/archive_util.py +++ b/archive_util.py @@ -16,12 +16,12 @@ 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): 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: [PATCH 1752/2594] 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 d4525998fd..d051f917bb 100644 --- a/archive_util.py +++ b/archive_util.py @@ -16,12 +16,12 @@ 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): 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: [PATCH 1753/2594] 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 a3e3982f6d..1b7d310142 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -630,7 +630,8 @@ def get_ext_fullpath(self, ext_name): """ 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 0000000000..21fa9e8f43 --- /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 0000000000..ec6b690cdb --- /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 94000a57d9..36e0bb84dd 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -13,6 +13,7 @@ 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 @@ def test_build_ext_inplace(self): 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'] From f08d60462aba52ea189fe6b20741ee1c6fe58684 Mon Sep 17 00:00:00 2001 From: "Andrew M. Kuchling" Date: Mon, 5 Oct 2009 22:32:48 +0000 Subject: [PATCH 1754/2594] 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 7d56dfcf40..bc29baaba4 100644 --- a/command/check.py +++ b/command/check.py @@ -91,7 +91,7 @@ def check_metadata(self): 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, " + 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: [PATCH 1755/2594] #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 1b7d310142..da9cbfd574 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -303,7 +303,7 @@ def run(self): # 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 36e0bb84dd..00733a46a7 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -264,7 +264,7 @@ def test_get_outputs(self): 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] From 7bbdd446ed91b60cfb54abb7e2a38b5657253759 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Tue, 6 Oct 2009 13:21:07 +0000 Subject: [PATCH 1756/2594] Bumping to 2.6.4rc1 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index fd52b8d7c5..83c576f802 100644 --- a/__init__.py +++ b/__init__.py @@ -22,5 +22,5 @@ # #--start constants-- -__version__ = "2.6.3" +__version__ = "2.6.4rc1" #--end constants-- From c048eb0e670dc6081c319ff83357d7a676dde723 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Fri, 9 Oct 2009 22:05:45 +0000 Subject: [PATCH 1757/2594] replace callable() --- dist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist.py b/dist.py index f49afcce13..eb7d01c0e4 100644 --- a/dist.py +++ b/dist.py @@ -545,7 +545,7 @@ def _parse_command_opts(self, parser, args): 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( 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: [PATCH 1758/2594] 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 da9cbfd574..abc1584180 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -628,6 +628,8 @@ def get_ext_fullpath(self, ext_name): 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 00733a46a7..5dffe2ca03 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -363,6 +363,16 @@ def test_setuptools_compat(self): 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: 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: [PATCH 1759/2594] 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 abc1584180..8248089fec 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -628,8 +628,10 @@ def get_ext_fullpath(self, ext_name): 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 5dffe2ca03..93d18814bc 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -373,6 +373,19 @@ def test_build_ext_path_with_os_sep(self): 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: 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: [PATCH 1760/2594] 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 3faf5e0d3b..b91986ba95 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -209,6 +209,7 @@ def test_make_archive(self): 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 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: [PATCH 1761/2594] 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 9990df36c3..71d32dce19 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -209,6 +209,7 @@ def test_make_archive(self): 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 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: [PATCH 1762/2594] 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 3c328cc9a6..41d3cd3c66 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 a838f73476..5eaef2a9d7 100644 --- a/tests/test_bdist_dumb.py +++ b/tests/test_bdist_dumb.py @@ -26,16 +26,18 @@ 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 c271567542..2aa257f7e6 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 4c09dd80a3..317ce2bca0 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -34,7 +34,7 @@ def setUp(self): # 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 @@ def test_build_ext(self): 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 6947275569..0a1bb961ff 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -56,7 +56,7 @@ def setUp(self): """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 3c26fddbc6..48ec1b436e 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -8,7 +8,7 @@ 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 @@ """ -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 3d4b25f0fc..5f032ca60f 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -39,15 +39,17 @@ def find_config_files(self): 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 @@ def test_announce(self): 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 @@ def test_custom_pydistutils(self): # 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 @@ def test_fix_help_options(self): 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 d2b8c8e9b6..d44156dd45 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -17,6 +17,7 @@ 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 7072136792..377ae86e2f 100644 --- a/tests/test_install_data.py +++ b/tests/test_install_data.py @@ -9,6 +9,7 @@ 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 2564563faf..5b32b13ee4 100644 --- a/tests/test_install_headers.py +++ b/tests/test_install_headers.py @@ -9,6 +9,7 @@ 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 793b95cad6..b2185b8442 100644 --- a/tests/test_install_lib.py +++ b/tests/test_install_lib.py @@ -10,9 +10,9 @@ 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 edc8fd8d60..498714de81 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -17,8 +17,16 @@ def setUp(self): 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 @@ def test_customize_compiler(self): 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 7190b2c9c6..4099c950ad 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -121,7 +121,7 @@ def test_get_platform(self): ('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 @@ def test_get_platform(self): 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 @@ def _join(*path): 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): 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: [PATCH 1763/2594] 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 ea122111ca..e258d2e58d 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 a838f73476..5eaef2a9d7 100644 --- a/tests/test_bdist_dumb.py +++ b/tests/test_bdist_dumb.py @@ -26,16 +26,18 @@ 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 c271567542..2aa257f7e6 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 7a27f343a7..ebbb39979c 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -35,7 +35,7 @@ def setUp(self): # 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 @@ def test_build_ext(self): 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 879689d2bb..71c63678f8 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -55,7 +55,7 @@ def setUp(self): """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 b5f391f57a..b478fa6291 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -8,7 +8,7 @@ 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 @@ """ -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 799b0c0603..be1010c611 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -38,15 +38,17 @@ def find_config_files(self): 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 @@ def test_announce(self): 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 @@ def test_custom_pydistutils(self): # 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 @@ def test_fix_help_options(self): 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 2087a0eba0..76fa02acda 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -17,6 +17,7 @@ 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 7072136792..377ae86e2f 100644 --- a/tests/test_install_data.py +++ b/tests/test_install_data.py @@ -9,6 +9,7 @@ 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 2564563faf..5b32b13ee4 100644 --- a/tests/test_install_headers.py +++ b/tests/test_install_headers.py @@ -9,6 +9,7 @@ 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 793b95cad6..b2185b8442 100644 --- a/tests/test_install_lib.py +++ b/tests/test_install_lib.py @@ -10,9 +10,9 @@ 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 eb36204439..edc755ed15 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -17,8 +17,15 @@ def setUp(self): 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 @@ def test_customize_compiler(self): 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 84caea5cac..8068726df1 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -121,7 +121,7 @@ def test_get_platform(self): ('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 @@ def test_get_platform(self): 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 @@ def _join(*path): 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): 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: [PATCH 1764/2594] 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 ea122111ca..e258d2e58d 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 d2ea201bd4..5e76809f23 100644 --- a/tests/test_bdist_dumb.py +++ b/tests/test_bdist_dumb.py @@ -19,16 +19,18 @@ 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 c271567542..2aa257f7e6 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 1221fd43af..f992928d0a 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -32,7 +32,7 @@ def setUp(self): # 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 @@ def test_build_ext(self): 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 879689d2bb..71c63678f8 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -55,7 +55,7 @@ def setUp(self): """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 b5f391f57a..b478fa6291 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -8,7 +8,7 @@ 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 @@ """ -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 70c9ec50e2..5f96b97f9e 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -37,15 +37,17 @@ def find_config_files(self): 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 @@ def test_announce(self): 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 @@ def test_custom_pydistutils(self): # 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 @@ def test_fix_help_options(self): 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 2087a0eba0..76fa02acda 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -17,6 +17,7 @@ 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 7072136792..377ae86e2f 100644 --- a/tests/test_install_data.py +++ b/tests/test_install_data.py @@ -9,6 +9,7 @@ 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 2564563faf..5b32b13ee4 100644 --- a/tests/test_install_headers.py +++ b/tests/test_install_headers.py @@ -9,6 +9,7 @@ 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 793b95cad6..b2185b8442 100644 --- a/tests/test_install_lib.py +++ b/tests/test_install_lib.py @@ -10,9 +10,9 @@ 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 eb36204439..edc755ed15 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -17,8 +17,15 @@ def setUp(self): 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 @@ def test_customize_compiler(self): 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 795edb8cdc..35f81a6b37 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -94,7 +94,7 @@ def test_get_platform(self): ('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 @@ def test_get_platform(self): 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 @@ def _join(*path): 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): From 0b33d63b2cf8fae5959e157a8798007bbc7049c4 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Sun, 18 Oct 2009 16:50:06 +0000 Subject: [PATCH 1765/2594] Bump to 2.6.4rc2 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 83c576f802..4c8cf042fd 100644 --- a/__init__.py +++ b/__init__.py @@ -22,5 +22,5 @@ # #--start constants-- -__version__ = "2.6.4rc1" +__version__ = "2.6.4rc2" #--end constants-- 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: [PATCH 1766/2594] #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 bc5edfd864..c741cc0174 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 b91986ba95..a9b46d8e22 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -8,7 +8,8 @@ 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 @@ def test_tarfile_root_owner(self): 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) 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: [PATCH 1767/2594] 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 d051f917bb..28e93fed78 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 71d32dce19..682f19a2b3 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -8,7 +8,8 @@ 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 @@ def test_tarfile_root_owner(self): 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) 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: [PATCH 1768/2594] 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 a568854e4e..16164c7f1f 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 d88e0b350d..c6e08cbc2b 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -8,7 +8,8 @@ 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 @@ def test_make_archive(self): 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) 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: [PATCH 1769/2594] 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 5d249f3ddc..9b9f551830 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -5,6 +5,7 @@ __revision__ = "$Id$" import os +import sys from glob import glob from distutils.core import Command @@ -372,6 +373,10 @@ def build_packages(self): 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 411db79cc2..7b4b45be79 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -6,6 +6,8 @@ __revision__ = "$Id$" import os +import sys + from distutils.core import Command from distutils.errors import DistutilsOptionError @@ -118,6 +120,10 @@ def install(self): 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 963d83377c..acecacccb5 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 c815d8185e..e8c7ca921c 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -89,6 +89,22 @@ def test_empty_package_dir (self): 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 b2185b8442..fab66d15ae 100644 --- a/tests/test_install_lib.py +++ b/tests/test_install_lib.py @@ -31,6 +31,8 @@ def test_finalize_options(self): 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 @@ def test_get_inputs(self): # 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 4099c950ad..4da1e651ed 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 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 @@ def test_get_compiler_versions(self): 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 fe6851cb87..a87ef44a27 100644 --- a/util.py +++ b/util.py @@ -13,6 +13,7 @@ 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 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: [PATCH 1770/2594] 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 4da1e651ed..55ef160f37 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -355,7 +355,7 @@ def test_dont_write_bytecode(self): 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) 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: [PATCH 1771/2594] 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 9b9f551830..5c7b473b04 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -374,7 +374,7 @@ def build_packages(self): 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 7b4b45be79..043e8b6e27 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -121,7 +121,7 @@ def install(self): 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 e8c7ca921c..bfe6154273 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -103,7 +103,7 @@ def test_dont_write_bytecode(self): 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 fab66d15ae..99a6d90627 100644 --- a/tests/test_install_lib.py +++ b/tests/test_install_lib.py @@ -92,7 +92,7 @@ def test_dont_write_bytecode(self): 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 a87ef44a27..6bff44f786 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 From 283c3eac2d9e113f876c649e5ddb1dcbf9a6e096 Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Sat, 24 Oct 2009 20:11:21 +0000 Subject: [PATCH 1772/2594] 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 14c529ac5b..70dd81f10d 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -262,9 +262,9 @@ def finalize_options(self): 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 @@ def get_libraries(self, ext): # 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 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: [PATCH 1773/2594] 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 4cc80f7b4a..fa08579652 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -5,6 +5,7 @@ __revision__ = "$Id$" import os +import sys from glob import glob from distutils.core import Command @@ -369,6 +370,10 @@ def build_packages(self): 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 85fb3acd4e..6022d30f27 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -6,6 +6,8 @@ __revision__ = "$Id$" import os +import sys + from distutils.core import Command from distutils.errors import DistutilsOptionError @@ -115,6 +117,10 @@ def install(self): 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 963d83377c..acecacccb5 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 8ad3bbc452..582f24634a 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -89,6 +89,22 @@ def test_empty_package_dir (self): 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 b2185b8442..99a6d90627 100644 --- a/tests/test_install_lib.py +++ b/tests/test_install_lib.py @@ -31,6 +31,8 @@ def test_finalize_options(self): 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 @@ def test_get_inputs(self): # 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 8068726df1..a2f8ed2b59 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 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 @@ def test_get_compiler_versions(self): 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 6709bbfe42..a50621e3f4 100644 --- a/util.py +++ b/util.py @@ -13,6 +13,7 @@ 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 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: [PATCH 1774/2594] 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 99d85be96c..26002e4b3f 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -5,6 +5,7 @@ __revision__ = "$Id$" import sys, os +import sys from glob import glob from distutils.core import Command @@ -369,6 +370,10 @@ def build_packages(self): 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 85fb3acd4e..6022d30f27 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -6,6 +6,8 @@ __revision__ = "$Id$" import os +import sys + from distutils.core import Command from distutils.errors import DistutilsOptionError @@ -115,6 +117,10 @@ def install(self): 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 963d83377c..acecacccb5 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 8ad3bbc452..582f24634a 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -89,6 +89,22 @@ def test_empty_package_dir (self): 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 b2185b8442..99a6d90627 100644 --- a/tests/test_install_lib.py +++ b/tests/test_install_lib.py @@ -31,6 +31,8 @@ def test_finalize_options(self): 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 @@ def test_get_inputs(self): # 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 35f81a6b37..dcc1a2069b 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 @@ def test_rfc822_escape(self): '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 5054d0c867..4bc4c98413 100644 --- a/util.py +++ b/util.py @@ -11,6 +11,7 @@ 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 From 34ac4d6b68ea3f6955282afdbc7f113a084d3f31 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Mon, 26 Oct 2009 01:48:07 +0000 Subject: [PATCH 1775/2594] bumping to 2.6.4 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 4c8cf042fd..b37308c578 100644 --- a/__init__.py +++ b/__init__.py @@ -22,5 +22,5 @@ # #--start constants-- -__version__ = "2.6.4rc2" +__version__ = "2.6.4" #--end constants-- 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: [PATCH 1776/2594] 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 0fb6f81efb..8073a6bc2a 100644 --- a/core.py +++ b/core.py @@ -129,8 +129,9 @@ class found in 'cmdclass' is used in place of the default, which is 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 eb7d01c0e4..8dc64d43da 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 @@ def __init__ (self, attrs=None): 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 @@ def find_config_files(self): 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 @@ def find_config_files(self): 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 5f032ca60f..573d0b9b4f 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -209,6 +209,35 @@ def test_announce(self): 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): 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: [PATCH 1777/2594] 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 6e4892039e..95c035742e 100644 --- a/core.py +++ b/core.py @@ -129,8 +129,9 @@ class found in 'cmdclass' is used in place of the default, which is 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 1c1ea477db..90af6e2d3b 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 @@ def __init__ (self, attrs=None): 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 @@ def find_config_files(self): 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 @@ def find_config_files(self): 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 be1010c611..b1b184ea18 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -37,7 +37,8 @@ def find_config_files(self): return self._config_files -class DistributionTestCase(support.LoggingSilencer, +class DistributionTestCase(support.TempdirManager, + support.LoggingSilencer, support.EnvironGuard, unittest.TestCase): @@ -180,6 +181,35 @@ def test_announce(self): 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): 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: [PATCH 1778/2594] removed spurious spaces --- errors.py | 47 ++++++++++++++++++----------------------------- 1 file changed, 18 insertions(+), 29 deletions(-) diff --git a/errors.py b/errors.py index acecacccb5..d9c47c761c 100644 --- a/errors.py +++ b/errors.py @@ -10,90 +10,79 @@ __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.""" 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: [PATCH 1779/2594] 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 acecacccb5..d9c47c761c 100644 --- a/errors.py +++ b/errors.py @@ -10,90 +10,79 @@ __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.""" 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: [PATCH 1780/2594] 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 bfe6154273..472591dc09 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -69,6 +69,7 @@ def test_empty_package_dir (self): open(os.path.join(testdir, "testfile"), "w").close() os.chdir(sources) + old_stdout = sys.stdout sys.stdout = StringIO.StringIO() try: @@ -87,7 +88,7 @@ def test_empty_package_dir (self): 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 55ef160f37..6722997f58 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -60,6 +60,8 @@ def setUp(self): 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 @@ def tearDown(self): 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): 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: [PATCH 1781/2594] 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 582f24634a..3e45f6e89e 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -69,6 +69,7 @@ def test_empty_package_dir (self): open(os.path.join(testdir, "testfile"), "w").close() os.chdir(sources) + old_stdout = sys.stdout sys.stdout = io.StringIO() try: @@ -87,7 +88,7 @@ def test_empty_package_dir (self): 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 a2f8ed2b59..4172472658 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -60,6 +60,8 @@ def setUp(self): 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 @@ def tearDown(self): 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): 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: [PATCH 1782/2594] 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 3ce5635047..88b33c7c94 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 86db557441..1faccfae7e 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) 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: [PATCH 1783/2594] 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 58a2bfb108..06a8da9a07 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 582f24634a..3e45f6e89e 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -69,6 +69,7 @@ def test_empty_package_dir (self): open(os.path.join(testdir, "testfile"), "w").close() os.chdir(sources) + old_stdout = sys.stdout sys.stdout = io.StringIO() try: @@ -87,7 +88,7 @@ def test_empty_package_dir (self): 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 b9db8f61fe..331180d235 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() 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: [PATCH 1784/2594] 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 54a4ed80fd..4b045474f1 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -69,6 +69,7 @@ def test_empty_package_dir (self): open(os.path.join(testdir, "testfile"), "w").close() os.chdir(sources) + old_stdout = sys.stdout sys.stdout = StringIO.StringIO() try: @@ -87,7 +88,7 @@ def test_empty_package_dir (self): finally: # Restore state. os.chdir(cwd) - sys.stdout = sys.__stdout__ + sys.stdout = old_stdout def test_suite(): return unittest.makeSuite(BuildPyTestCase) From 868b46b50b94efc30bfdde021844d1020aafa4b6 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Fri, 13 Nov 2009 02:25:08 +0000 Subject: [PATCH 1785/2594] 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 9a8fca1d5f..12844cbf9f 100644 --- a/command/check.py +++ b/command/check.py @@ -92,7 +92,7 @@ def check_metadata(self): 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, " + From 83b75ba535abc50e9f0c9ac860903e0f97b28703 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Fri, 13 Nov 2009 02:29:35 +0000 Subject: [PATCH 1786/2594] 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 9a8fca1d5f..12844cbf9f 100644 --- a/command/check.py +++ b/command/check.py @@ -92,7 +92,7 @@ def check_metadata(self): 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, " + 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: [PATCH 1787/2594] #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 21242a8eb9..69a393caf8 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -44,17 +44,17 @@ def test_reg_class(self): # 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) 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: [PATCH 1788/2594] 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 73827988bb..7cf1a12ec7 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -44,17 +44,17 @@ def test_reg_class(self): # 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) 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: [PATCH 1789/2594] 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 73827988bb..7cf1a12ec7 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -44,17 +44,17 @@ def test_reg_class(self): # 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) 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: [PATCH 1790/2594] 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 69a393caf8..1264854d0d 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -46,7 +46,7 @@ def test_reg_class(self): # 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 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: [PATCH 1791/2594] 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 7cf1a12ec7..05d34e604c 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -46,7 +46,7 @@ def test_reg_class(self): # 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 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: [PATCH 1792/2594] 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 7cf1a12ec7..05d34e604c 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -46,7 +46,7 @@ def test_reg_class(self): # 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 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: [PATCH 1793/2594] 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 b8e3dca560..20b20aae0b 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -336,10 +336,13 @@ def test_make_distribution_owner_group(self): # 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() 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: [PATCH 1794/2594] 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 c10e973ed7..e0f1e93768 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -336,10 +336,13 @@ def test_make_distribution_owner_group(self): # 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() 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: [PATCH 1795/2594] 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 00d76d4bea..c2287d9291 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -17,6 +17,7 @@ import os import subprocess import sys +import re from distutils.errors import DistutilsExecError, DistutilsPlatformError, \ CompileError, LibError, LinkError @@ -646,6 +647,28 @@ def link(self, 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', 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: [PATCH 1796/2594] 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 68b7775830..9bf54c102d 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -17,6 +17,7 @@ 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 @@ def link(self, # 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', 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: [PATCH 1797/2594] 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 ef895422c6..c84fb0b4a6 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -17,6 +17,7 @@ import os import subprocess import sys +import re from distutils.errors import DistutilsExecError, DistutilsPlatformError, \ CompileError, LibError, LinkError @@ -645,6 +646,28 @@ def link(self, 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', 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: [PATCH 1798/2594] 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 ef895422c6..c84fb0b4a6 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -17,6 +17,7 @@ import os import subprocess import sys +import re from distutils.errors import DistutilsExecError, DistutilsPlatformError, \ CompileError, LibError, LinkError @@ -645,6 +646,28 @@ def link(self, 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', From 7ed6e18a1d4705bf067be40df44b831d9bab21e6 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 5 Dec 2009 17:47:56 +0000 Subject: [PATCH 1799/2594] bump version to 2.7a1 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index e418214c48..e8c213e4c7 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "2.6" +__version__ = "2.7a1" #--end constants-- 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: [PATCH 1800/2594] 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 573d0b9b4f..8b141ca675 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -6,6 +6,7 @@ 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 @@ def test_show_help(self): 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 6bff44f786..b8e4952fee 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+)*)') 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: [PATCH 1801/2594] 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 bf59c41844..af40186315 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -9,6 +9,7 @@ import sys import unittest import warnings +import textwrap from test.test_support import TESTFN @@ -283,6 +284,21 @@ def test_custom_pydistutils(self): 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 ee5829b077..7bc52f195b 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 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: [PATCH 1802/2594] 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 b1b184ea18..0e7d532df0 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -5,6 +5,7 @@ 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 @@ def test_show_help(self): 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 a50621e3f4..cca7b49f5e 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+)*)') 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: [PATCH 1803/2594] 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 5f96b97f9e..3b7637f3af 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -5,6 +5,7 @@ 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 @@ def test_show_help(self): 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 4bc4c98413..50ad8fef98 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 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: [PATCH 1804/2594] 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 8dc64d43da..a1fc7b36fc 100644 --- a/dist.py +++ b/dist.py @@ -7,6 +7,7 @@ __revision__ = "$Id$" import sys, os, re +import rfc822 try: import warnings @@ -1006,6 +1007,20 @@ def is_pure(self): # 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 8b141ca675..1eddf6d25c 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -8,10 +8,9 @@ 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 @@ def _expander(path): # 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 @@ def test_long_description(self): 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)) 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: [PATCH 1805/2594] 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 a1fc7b36fc..ee2cec9088 100644 --- a/dist.py +++ b/dist.py @@ -7,7 +7,7 @@ __revision__ = "$Id$" import sys, os, re -import rfc822 +from email import message_from_file try: import warnings @@ -1007,20 +1007,6 @@ def is_pure(self): # 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 @@ def __init__(self, path=None): 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 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: [PATCH 1806/2594] 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 90af6e2d3b..353525e17d 100644 --- a/dist.py +++ b/dist.py @@ -7,6 +7,7 @@ __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 0e7d532df0..4f51c16e51 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -7,7 +7,7 @@ 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 @@ def test_long_description(self): 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)) From 769868e0309a3e8b454d1638cd50d5883ef5b2f2 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Thu, 10 Dec 2009 10:27:09 +0000 Subject: [PATCH 1807/2594] 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 6ca6720adb..f3b2aca613 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'): From dde1c67ea48a115d96ccddc0f0e4ff44dfd0d286 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Thu, 10 Dec 2009 10:29:05 +0000 Subject: [PATCH 1808/2594] 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 8306202fb3..54ccec4953 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'): 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: [PATCH 1809/2594] 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 39eecfb248..4e40df6899 100644 --- a/dep_util.py +++ b/dep_util.py @@ -9,29 +9,27 @@ 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 0000000000..21fc7bc0b9 --- /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") 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: [PATCH 1810/2594] 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 07b3549c6f..b91a62eb56 100644 --- a/dep_util.py +++ b/dep_util.py @@ -9,29 +9,27 @@ 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 0000000000..21fc7bc0b9 --- /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") 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: [PATCH 1811/2594] 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 21fc7bc0b9..d81d9143b4 100644 --- a/tests/test_dep_util.py +++ b/tests/test_dep_util.py @@ -7,36 +7,26 @@ 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 @@ def test_newer_pairwise(self): 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 @@ def test_newer_group(self): 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')) 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: [PATCH 1812/2594] 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 21fc7bc0b9..d81d9143b4 100644 --- a/tests/test_dep_util.py +++ b/tests/test_dep_util.py @@ -7,36 +7,26 @@ 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 @@ def test_newer_pairwise(self): 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 @@ def test_newer_group(self): 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')) 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: [PATCH 1813/2594] 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 ec78ce3431..ab40797b98 100644 --- a/command/install_data.py +++ b/command/install_data.py @@ -8,7 +8,6 @@ __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 @@ def initialize_options(self): 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 @@ def finalize_options (self): 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: 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: [PATCH 1814/2594] 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 8114feb8f2..5d6ebcfa4c 100644 --- a/command/upload.py +++ b/command/upload.py @@ -6,7 +6,7 @@ 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 @@ def upload_file(self, command, pyversion, filename): 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 0d95a0917e..8f6701cc9b 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -10,6 +10,25 @@ 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 @@ def test_upload(self): 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 @@ def test_upload(self): 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) 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: [PATCH 1815/2594] 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 8805d41da0..9f1aae6aa4 100644 --- a/command/upload.py +++ b/command/upload.py @@ -11,7 +11,7 @@ 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 @@ def upload_file(self, command, pyversion, filename): 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 d24a18ea0e..9d373e94e4 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 @@ def write_file(self, path, content='xxx'): 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 b05ab1f78b..322beb778c 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.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 @@ def test_finalize_options(self): 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) 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: [PATCH 1816/2594] 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 defdda642b..674edd47c3 100644 --- a/command/upload.py +++ b/command/upload.py @@ -6,7 +6,7 @@ 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 @@ def upload_file(self, command, pyversion, filename): 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 9281885876..979ab228dd 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -10,6 +10,25 @@ 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 @@ def test_upload(self): 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 @@ def test_upload(self): 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) 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: [PATCH 1817/2594] 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 3b4a036718..f602fbeb68 100644 --- a/command/upload.py +++ b/command/upload.py @@ -12,7 +12,7 @@ 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 @@ def upload_file(self, command, pyversion, filename): 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 828f20c539..35e970051e 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -10,6 +10,25 @@ 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 @@ def test_upload(self): 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 @@ def test_upload(self): 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) 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: [PATCH 1818/2594] 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 5a0fa727cd..f26e7ae467 100644 --- a/bcppcompiler.py +++ b/bcppcompiler.py @@ -13,13 +13,11 @@ __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 e093eef2e8..c046915153 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -5,9 +5,11 @@ __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 (via the 'executables' class attribute), but most will have: # 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 @@ def _check_macro_definitions (self, definitions): 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 @@ def _setup_compile(self, outdir, macros, incdirs, sources, depends, """ 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 @@ def _fix_compile_args (self, output_dir, macros, include_dirs): 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 @@ def _fix_object_args (self, objects, output_dir): 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 @@ def _fix_lib_args (self, libraries, library_dirs, runtime_library_dirs): """ 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 @@ def _fix_lib_args (self, libraries, library_dirs, runtime_library_dirs): 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 @@ def _fix_lib_args (self, libraries, library_dirs, runtime_library_dirs): 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 @@ 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) @@ -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 8e0ad080b9..8cd69701cf 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -6,9 +6,9 @@ __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 7a5ca807a6..f3791bedfe 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -28,7 +28,6 @@ def __init__(self, *args, **kw): 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 @@ def add_ui(self): # 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 1eccc6a48a..6d9d47d2eb 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -5,14 +5,15 @@ __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 @@ def finalize_package_data (self): 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 @@ def _make_spec_file(self): '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 8a6eca59d8..cf4282d83f 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -5,11 +5,14 @@ __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 9545b27ad0..50b64dba25 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -18,7 +18,7 @@ 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 0c79476bac..ff1a4be315 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -10,7 +10,8 @@ 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 5c7b473b04..04c455f0eb 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -133,7 +133,6 @@ def find_data_files(self, package, src_dir): 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 134fa3892d..b084913563 100644 --- a/command/config.py +++ b/command/config.py @@ -11,7 +11,8 @@ __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 3b5b2080ec..fb547c93a8 100644 --- a/command/register.py +++ b/command/register.py @@ -7,12 +7,13 @@ __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 5c74e5329f..f1335e6a36 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -4,16 +4,17 @@ __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 @@ def add_defaults(self): 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 5d6ebcfa4c..3e18aeaad6 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 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 9166199ea9..afa403f2da 100644 --- a/config.py +++ b/config.py @@ -4,7 +4,6 @@ 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 @@ def _read_pypirc(self): 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 8073a6bc2a..0b725ff09d 100644 --- a/core.py +++ b/core.py @@ -8,10 +8,12 @@ __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 @@ 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 @@ def gen_usage (script_name): '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 @@ class found in 'cmdclass' is used in place of the default, which is 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 a8476e6579..eed9c321af 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -53,11 +53,9 @@ 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 3e2cd35385..9b4d2adb5c 100644 --- a/dir_util.py +++ b/dir_util.py @@ -4,7 +4,7 @@ __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 ee2cec9088..f20a92a21d 100644 --- a/dist.py +++ b/dist.py @@ -14,7 +14,8 @@ 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 a5a2ef88fe..f2f77e52d1 100644 --- a/emxccompiler.py +++ b/emxccompiler.py @@ -24,11 +24,9 @@ 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 6af1810801..9988ec0c2c 100644 --- a/extension.py +++ b/extension.py @@ -6,7 +6,6 @@ __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 e19319ae9d..2dea948025 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -10,10 +10,11 @@ __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 @@ def get_attr_name (self, long_option): 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 @@ def _grok_option_table (self): 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 @@ def __init__ (self, options=[]): '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 c2287d9291..2309d89488 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -19,10 +19,9 @@ 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 d38afb7223..0e69fd368c 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -10,12 +10,13 @@ __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 @@ def load_macros(self, version): 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 931f0bac19..09a798b190 100644 --- a/text_file.py +++ b/text_file.py @@ -6,8 +6,7 @@ __revision__ = "$Id$" -from types import * -import sys, os, string +import sys class TextFile: @@ -137,12 +136,12 @@ def gen_error (self, msg, line=None): 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 @@ def readline (self): # 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 @@ def readline (self): # # 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 @@ def readline (self): 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 @@ def readline (self): 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 @@ def readline (self): # 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 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: [PATCH 1819/2594] 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 c5e5cd2571..77d94a59a2 100644 --- a/bcppcompiler.py +++ b/bcppcompiler.py @@ -13,13 +13,11 @@ __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 ff7f9df1c3..407cabda46 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -5,8 +5,11 @@ __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 2c81f3a9c0..dd202ff449 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -6,8 +6,9 @@ __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 c4be47b579..f9205483d7 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -28,7 +28,6 @@ def __init__(self, *args, **kw): 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 @@ def add_ui(self): # 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 452f9502ad..0a579dbb93 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -5,13 +5,14 @@ __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 d6d01c630d..0b7044a00e 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -5,11 +5,13 @@ __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 258d7c10be..12bf1d2fe3 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -18,7 +18,7 @@ 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 70dd81f10d..de980bd841 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -10,7 +10,8 @@ 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 fa08579652..7cc935390f 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -133,7 +133,6 @@ def find_data_files(self, package, src_dir): 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 ac80a54eb1..830552cdc4 100644 --- a/command/config.py +++ b/command/config.py @@ -11,7 +11,8 @@ __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 bdf5f8f09c..7d3dc53afe 100644 --- a/command/register.py +++ b/command/register.py @@ -7,13 +7,15 @@ __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 76e1de8100..f33406cd59 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -7,14 +7,14 @@ 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 674edd47c3..bb1b7fc4fe 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 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 5b625f3f7d..fe41ce977e 100644 --- a/config.py +++ b/config.py @@ -4,7 +4,6 @@ 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 @@ def _read_pypirc(self): 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 95c035742e..c820a420b9 100644 --- a/core.py +++ b/core.py @@ -8,10 +8,12 @@ __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 @@ 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 @@ def gen_usage (script_name): '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 @@ class found in 'cmdclass' is used in place of the default, which is 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 d9f4a43df7..3e2a634f7e 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -53,11 +53,9 @@ 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 98e6252c6c..370025b734 100644 --- a/dir_util.py +++ b/dir_util.py @@ -4,7 +4,7 @@ __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 353525e17d..5a107e7d83 100644 --- a/dist.py +++ b/dist.py @@ -14,7 +14,8 @@ 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 50634d6c06..fd79aec2cf 100644 --- a/emxccompiler.py +++ b/emxccompiler.py @@ -24,11 +24,9 @@ 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 5c07bdae82..10aaf7c504 100644 --- a/extension.py +++ b/extension.py @@ -6,7 +6,6 @@ __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 879d4d25bf..73343ad5af 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -10,9 +10,11 @@ __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 @@ def __init__(self, options=[]): '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 c84fb0b4a6..4d05a44522 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -19,10 +19,9 @@ 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 1cd0f91d5f..dc3bd8de47 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -10,12 +10,12 @@ __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 @@ def load_macros(self, version): 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 97459fbf73..53c8561a3e 100644 --- a/text_file.py +++ b/text_file.py @@ -6,8 +6,8 @@ __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 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: [PATCH 1820/2594] 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 4d05a44522..6455fffa1f 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -645,28 +645,8 @@ def link(self, 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 @@ def link(self, 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 05d34e604c..e1f08d8ad8 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 @@ def _find_vcvarsall(version): 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 @@ def test_reg_class(self): 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) 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: [PATCH 1821/2594] 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 e1f08d8ad8..8a908d9954 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -60,7 +60,7 @@ """ -@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): 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: [PATCH 1822/2594] 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 c84fb0b4a6..ad021b5754 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -646,28 +646,8 @@ def link(self, 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 @@ def link(self, 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 05d34e604c..8a908d9954 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 @@ def _find_vcvarsall(version): 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 @@ def test_reg_class(self): 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) 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: [PATCH 1823/2594] 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 2309d89488..41d67faf59 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -646,28 +646,7 @@ def link(self, 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 @@ def link(self, 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 1264854d0d..503a5a8056 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 @@ def _find_vcvarsall(version): 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 @@ def test_reg_class(self): 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) From 518952766c40646198629634cbf5fc5df9ba7ae6 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Thu, 24 Dec 2009 13:06:39 +0000 Subject: [PATCH 1824/2594] 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 b8e4952fee..f4bb0633c5 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) From 40baf32676d4421839d651e3a3e561e4c045c639 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Thu, 24 Dec 2009 13:07:53 +0000 Subject: [PATCH 1825/2594] 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 7bc52f195b..d314961bf7 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 () From 576fd8c16b2727a04bf59e08db0a6a166d5dd66e Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Thu, 24 Dec 2009 13:14:21 +0000 Subject: [PATCH 1826/2594] 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 cca7b49f5e..8adf6e0d29 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) From 152d8eb8e580c3e346e8a30688a55cd28efd6a06 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Thu, 24 Dec 2009 13:16:53 +0000 Subject: [PATCH 1827/2594] 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 50ad8fef98..9a77561fff 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 () From 1be9d3f60881534ffd6251d3f76b098ef7a63f2f Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Thu, 24 Dec 2009 14:50:35 +0000 Subject: [PATCH 1828/2594] 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 6722997f58..80c5800961 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -119,6 +119,26 @@ def test_get_platform(self): 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 @@ def test_get_platform(self): 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 @@ def test_get_platform(self): 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) ' From 864559503a880c593e0333777b34f5e694d65fb5 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Sat, 26 Dec 2009 13:16:15 +0000 Subject: [PATCH 1829/2594] 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 4172472658..8f8d4a10c2 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -119,6 +119,26 @@ def test_get_platform(self): 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 @@ def test_get_platform(self): 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 @@ def test_get_platform(self): 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) ' 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: [PATCH 1830/2594] 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 3bf1267328..708ef0f38f 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -8,6 +8,7 @@ import string, os from types import * +import sys from glob import glob from distutils.core import Command @@ -418,6 +419,10 @@ def build_packages (self): 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 4ea61d78dc..7e0c708888 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -4,6 +4,8 @@ import os from types import IntType +import sys + from distutils.core import Command from distutils.errors import DistutilsOptionError @@ -122,6 +124,10 @@ def install (self): 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 e72221bdba..9d1756b20c 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 9d373e94e4..2a03765293 100644 --- a/tests/support.py +++ b/tests/support.py @@ -3,19 +3,50 @@ 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 4b045474f1..4a054f2a58 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -90,6 +90,22 @@ def test_empty_package_dir (self): 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 d314961bf7..36ac721386 100644 --- a/util.py +++ b/util.py @@ -11,6 +11,7 @@ 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 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: [PATCH 1831/2594] 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 1b7dd4cd64..008ae5d03a 100644 --- a/tests/test_unixccompiler.py +++ b/tests/test_unixccompiler.py @@ -82,6 +82,18 @@ def gcv(v): 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 2083f82982..67adcfcf97 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -266,6 +266,9 @@ def link(self, target_desc, objects, 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 @@ def runtime_library_dir_option(self, dir): # 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 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: [PATCH 1832/2594] 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 0000000000..3f233e2823 --- /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 7556cbdbf5..783d4dca84 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -266,6 +266,9 @@ def link(self, target_desc, objects, 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 @@ def runtime_library_dir_option(self, dir): # 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 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: [PATCH 1833/2594] 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 0000000000..78922f4702 --- /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 0000000000..981ad000da --- /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") 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: [PATCH 1834/2594] 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 1b7dd4cd64..008ae5d03a 100644 --- a/tests/test_unixccompiler.py +++ b/tests/test_unixccompiler.py @@ -82,6 +82,18 @@ def gcv(v): 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 da85c89696..51f6349a9e 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -264,6 +264,9 @@ def link(self, target_desc, objects, 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 @@ def runtime_library_dir_option(self, dir): # 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 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: [PATCH 1835/2594] 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 be2df5c6e3..3a41e6fcaa 100644 --- a/tests/test_unixccompiler.py +++ b/tests/test_unixccompiler.py @@ -82,6 +82,18 @@ def gcv(v): 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 8bbdb4b329..a33fdf0f7c 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -264,6 +264,9 @@ def link(self, target_desc, objects, 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 @@ def runtime_library_dir_option(self, dir): # 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 From 9692427a1f35e2911ad27ec342f939655d5fc337 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 9 Jan 2010 16:34:06 +0000 Subject: [PATCH 1836/2594] bump version to 2.7a2 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index e8c213e4c7..55aaf81a3d 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "2.7a1" +__version__ = "2.7a2" #--end constants-- 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: [PATCH 1837/2594] 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 c046915153..83ba83a02b 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -337,10 +337,7 @@ def set_link_objects (self, objects): 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 @@ def _setup_compile(self, outdir, macros, incdirs, sources, depends, 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 @@ def _setup_compile(self, outdir, macros, incdirs, sources, depends, 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 @@ def _fix_compile_args (self, output_dir, macros, include_dirs): # _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 @@ def compile(self, sources, output_dir=None, macros=None, Raises CompileError on failure. """ - # A concrete compiler class can either override this method # entirely or implement _compile(). 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: [PATCH 1838/2594] 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 87d6e27396..bf92d78026 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -338,10 +338,7 @@ def set_link_objects (self, objects): 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 @@ def _setup_compile(self, outdir, macros, incdirs, sources, depends, 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 @@ def _setup_compile(self, outdir, macros, incdirs, sources, depends, 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 @@ def _fix_compile_args (self, output_dir, macros, include_dirs): # _fix_compile_args () - def _prep_compile(self, sources, output_dir, depends=None): """Decide which souce files must be recompiled. @@ -477,42 +435,9 @@ def _prep_compile(self, sources, output_dir, depends=None): 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 @@ def compile(self, sources, output_dir=None, macros=None, Raises CompileError on failure. """ - # A concrete compiler class can either override this method # entirely or implement _compile(). 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: [PATCH 1839/2594] 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 407cabda46..38c4ae8ec5 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -314,10 +314,7 @@ def set_link_objects(self, objects): 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 @@ def _setup_compile(self, outdir, macros, incdirs, sources, depends, 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 @@ def _setup_compile(self, outdir, macros, incdirs, sources, depends, 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 @@ def _fix_compile_args(self, output_dir, macros, include_dirs): 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 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: [PATCH 1840/2594] 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 875f96fac3..34c77a37a3 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -311,10 +311,7 @@ def set_link_objects(self, objects): 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 @@ def _setup_compile(self, outdir, macros, incdirs, sources, depends, 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 @@ def _setup_compile(self, outdir, macros, incdirs, sources, depends, 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 @@ def _prep_compile(self, sources, output_dir, depends=None): 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. 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: [PATCH 1841/2594] module cleanup --- ccompiler.py | 217 ++++++++++++++++----------------------------------- 1 file changed, 69 insertions(+), 148 deletions(-) diff --git a/ccompiler.py b/ccompiler.py index 83ba83a02b..97c61dd46f 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -9,11 +9,12 @@ 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 @@ def __init__ (self, 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 (via the 'executables' class attribute), but most will have: (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 @@ def _check_macro_definitions (self, definitions): # -- 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 @@ def define_macro (self, name, value=None): 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 @@ def undefine_macro (self, name): 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 @@ def add_include_dir (self, 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 @@ -256,8 +242,7 @@ def set_include_dirs (self, dirs): """ 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 @@ def add_library (self, 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 @@ -282,29 +267,28 @@ def set_libraries (self, libnames): 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 @@ def set_runtime_library_dirs (self, dirs): """ 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 @@ def _get_cc_args(self, pp_opts, debug, before): 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 @@ def _fix_compile_args (self, output_dir, macros, include_dirs): """ 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 @@ def _fix_compile_args (self, output_dir, macros, include_dirs): 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 @@ def _fix_object_args (self, objects, output_dir): 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 @@ def _fix_lib_args (self, libraries, library_dirs, runtime_library_dirs): 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 @@ def _need_link (self, objects, output_file): 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 @@ def detect_language (self, sources): 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 @@ def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): # 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 @@ def create_static_lib (self, """ 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 @@ def link (self, # 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 @@ def link_shared_lib (self, 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 @@ def link_executable (self, # 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 @@ def library_filename(self, libname, lib_type='static', # or 'shared' # -- 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 @@ def mkpath (self, name, mode=0777): ) 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,) 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: [PATCH 1842/2594] 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 38c4ae8ec5..5a6bef447d 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -9,11 +9,12 @@ 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 @@ def create_static_lib(self, objects, output_libname, output_dir=None, """ 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 @@ def link(self, # 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 @@ def link_shared_lib(self, 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 @@ def link_shared_object(self, 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 @@ def spawn(self, cmd): 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) From 2cb71ea99a87e5897c8d9fbfd4921f7470957c2f Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Sun, 17 Jan 2010 18:52:29 +0000 Subject: [PATCH 1843/2594] 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 dcc1a2069b..0c732f8244 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -96,7 +96,12 @@ def test_get_platform(self): 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' 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: [PATCH 1844/2594] 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 97c61dd46f..a34177e71f 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -18,6 +18,58 @@ 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 8cd69701cf..764024a008 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -6,10 +6,10 @@ __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 358a70eacf..f7331d59b9 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -8,11 +8,11 @@ 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 cf4282d83f..0ece0306bc 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -9,11 +9,11 @@ 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 d394e4b1da..b84e40d6c9 100644 --- a/command/build.py +++ b/command/build.py @@ -5,9 +5,10 @@ __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 50b64dba25..8d49de75e1 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -19,7 +19,7 @@ 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 ff1a4be315..13bd030eb6 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -9,13 +9,14 @@ 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 @@ def initialize_options(self): 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 @@ def finalize_options(self): # 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 @@ def finalize_options(self): 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 @@ def finalize_options(self): # 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 @@ def get_ext_filename(self, ext_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 + _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 @@ def get_libraries(self, ext): # 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 @@ def get_libraries(self, ext): 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 2ad4c7beb9..567df6587e 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -6,7 +6,6 @@ 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 @@ def copy_scripts (self): 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 @@ def copy_scripts (self): 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 b084913563..da8da59538 100644 --- a/command/config.py +++ b/command/config.py @@ -16,7 +16,7 @@ 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 8f089c3172..68d6227ada 100644 --- a/command/install.py +++ b/command/install.py @@ -7,115 +7,25 @@ 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 @@ def initialize_options(self): 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 @@ def finalize_options(self): # 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 @@ def finalize_options(self): '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 @@ def finalize_unix(self): 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 @@ def finalize_unix(self): 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 @@ def finalize_other(self): 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 @@ def finalize_other(self): 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 @@ def _expand_attrs(self, attrs): 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 0b725ff09d..99ccf44fad 100644 --- a/core.py +++ b/core.py @@ -35,7 +35,7 @@ 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 eed9c321af..2a61bd5ad7 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 9988ec0c2c..244b1e78ec 100644 --- a/extension.py +++ b/extension.py @@ -134,14 +134,17 @@ def __init__ (self, name, sources, 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 41d67faf59..6ac24f83dc 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -23,10 +23,10 @@ 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 @@ 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() + 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 @@ def initialize(self, plat_name=None): # 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 f3b2aca613..bb8b5125e4 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -7,58 +7,42 @@ 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 @@ def parse_config_h(fp, g=None): _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 41d3cd3c66..783318e8d3 100644 --- a/tests/support.py +++ b/tests/support.py @@ -3,11 +3,19 @@ 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 6bca27ee06..2418e1656d 100644 --- a/tests/test_build.py +++ b/tests/test_build.py @@ -5,7 +5,7 @@ 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 536cd67319..145eff5453 100644 --- a/tests/test_build_clib.py +++ b/tests/test_build_clib.py @@ -120,8 +120,7 @@ def test_run(self): # 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 317ce2bca0..0e3f33e437 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -9,7 +9,7 @@ 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 @@ def test_solaris_enable_shared(self): 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 @@ def test_finalize_options(self): 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 b1d2d079bd..72e8915c00 100644 --- a/tests/test_build_scripts.py +++ b/tests/test_build_scripts.py @@ -5,7 +5,7 @@ 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 @@ def test_version_int(self): # --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 2c219b7b17..317e77fded 100644 --- a/tests/test_ccompiler.py +++ b/tests/test_ccompiler.py @@ -3,8 +3,10 @@ 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 @@ def find_library_file(self, dirs, lib, debug=0): 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 MyCCompiler(CCompiler): 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 b54ffff361..b67f987e6b 100644 --- a/tests/test_cygwinccompiler.py +++ b/tests/test_cygwinccompiler.py @@ -3,6 +3,7 @@ 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 @@ 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 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 159ac2b76e..cffa9a0915 100755 --- a/tests/test_extension.py +++ b/tests/test_extension.py @@ -5,9 +5,11 @@ 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 d44156dd45..8e3e942f02 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -5,12 +5,14 @@ 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 @@ def test_home_installation_scheme(self): 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 @@ def test_user_site(self): 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 @@ def _expanduser(path): # 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 @@ def test_record(self): 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 498714de81..9013220ffb 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -4,7 +4,6 @@ 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 @@ def cleanup_testfn(self): 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 @@ def test_get_python_lib(self): # 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 @@ def test_get_python_inc(self): 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 008ae5d03a..6976dd55f8 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 80c5800961..50f9ac173f 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -6,15 +6,14 @@ 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 @@ def setUp(self): 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 @@ def tearDown(self): 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 @@ def _set_uname(self, uname): 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 @@ def test_get_platform(self): 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 @@ def test_get_platform(self): 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 @@ def test_get_platform(self): 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 67adcfcf97..8fe1a6a13a 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -18,7 +18,6 @@ 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 @@ 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 @@ def runtime_library_dir_option(self, dir): # 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 @@ def runtime_library_dir_option(self, dir): # 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 f4bb0633c5..18d0d2ef4c 100644 --- a/util.py +++ b/util.py @@ -15,173 +15,7 @@ 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 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: [PATCH 1845/2594] 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 fb547c93a8..dc089902f1 100644 --- a/command/register.py +++ b/command/register.py @@ -266,7 +266,6 @@ def post_to_server(self, data, auth=None): 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 3e18aeaad6..18a10a0b7f 100644 --- a/command/upload.py +++ b/command/upload.py @@ -145,7 +145,7 @@ def upload_file(self, command, pyversion, filename): 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 f20a92a21d..5dbdaef19c 100644 --- a/dist.py +++ b/dist.py @@ -1139,16 +1139,19 @@ def write_pkg_file(self, file): 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 @@ def get_fullname(self): 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 @@ def get_license(self): 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 ada77a015a..370d659893 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 @@ def _no_way(prompt=''): 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 @@ def test_strict(self): # 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 8f6701cc9b..3ae89498a1 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 @@ def test_upload(self): 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(), 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: [PATCH 1846/2594] 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 bf7be96096..f531d0fc79 100644 --- a/command/register.py +++ b/command/register.py @@ -261,7 +261,6 @@ def post_to_server(self, data, auth=None): 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 9f1aae6aa4..93727aed62 100644 --- a/command/upload.py +++ b/command/upload.py @@ -133,7 +133,7 @@ def upload_file(self, command, pyversion, filename): 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 c15ca9770d..30260f393b 100644 --- a/dist.py +++ b/dist.py @@ -1114,18 +1114,20 @@ def write_pkg_file (self, file): 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 @@ def get_fullname (self): 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 @@ def get_license(self): 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 322beb778c..382697fb74 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 @@ def test_upload(self): 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 @@ def test_upload(self): 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] 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: [PATCH 1847/2594] 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 f3791bedfe..5ecf73b9e5 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 0000000000..ba2d3e19c2 --- /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()) 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: [PATCH 1848/2594] 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 bb8b5125e4..2d92dabf7b 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -21,6 +21,15 @@ # 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 18d0d2ef4c..8650d450c3 100644 --- a/util.py +++ b/util.py @@ -17,6 +17,10 @@ _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. 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: [PATCH 1849/2594] 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 764024a008..d7910b14d6 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -6,8 +6,8 @@ __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 f7331d59b9..7c60d39be9 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -8,8 +8,9 @@ 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 5ecf73b9e5..72578498f7 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 0ece0306bc..88c0532f81 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -9,12 +9,13 @@ 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 b84e40d6c9..f84bf359dc 100644 --- a/command/build.py +++ b/command/build.py @@ -5,8 +5,8 @@ __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 13bd030eb6..420d7f171d 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -9,14 +9,12 @@ 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 68d6227ada..b5336fefd6 100644 --- a/command/install.py +++ b/command/install.py @@ -7,15 +7,14 @@ 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 6ac24f83dc..41d67faf59 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -23,9 +23,9 @@ 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 @@ 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 = _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 @@ def initialize(self, plat_name=None): # 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 50f9ac173f..348b42be03 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -90,99 +90,12 @@ def _set_uname(self, 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')) - 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 8650d450c3..fbd3a67243 100644 --- a/util.py +++ b/util.py @@ -16,10 +16,27 @@ 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. 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: [PATCH 1850/2594] 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 5a6bef447d..1a4e8fb2af 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -18,6 +18,58 @@ 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 dd202ff449..a79982b645 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -6,10 +6,10 @@ __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 49fd653a19..c16125f229 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -8,11 +8,11 @@ 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 0b7044a00e..2cf28234ec 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -8,11 +8,11 @@ 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 715621e1d7..d7b0e3c5f9 100644 --- a/command/build.py +++ b/command/build.py @@ -5,9 +5,10 @@ __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 12bf1d2fe3..4c6443ca6e 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -19,7 +19,7 @@ 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 de980bd841..39d37dab84 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -9,13 +9,14 @@ 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 @@ def initialize_options(self): 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 @@ def finalize_options(self): # 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 @@ def finalize_options(self): 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 @@ def finalize_options(self): # 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 @@ def get_ext_filename(self, ext_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 + _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 @@ def get_libraries(self, ext): # 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 8b08bfeaf0..a54d6ed742 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -6,7 +6,6 @@ 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 @@ def copy_scripts(self): 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 @@ def copy_scripts(self): 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 830552cdc4..56f643c88b 100644 --- a/command/config.py +++ b/command/config.py @@ -16,7 +16,7 @@ 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 2a905d92f8..1f8d238a2d 100644 --- a/command/install.py +++ b/command/install.py @@ -7,115 +7,25 @@ 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 @@ def initialize_options(self): 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 @@ def finalize_options(self): # 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 @@ def finalize_options(self): '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 @@ def finalize_unix(self): 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 @@ def finalize_unix(self): 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 @@ def finalize_other(self): 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 @@ def finalize_other(self): 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 @@ def _expand_attrs(self, attrs): 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 c820a420b9..6ed3e8fa97 100644 --- a/core.py +++ b/core.py @@ -35,7 +35,7 @@ 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 3e2a634f7e..f381f60875 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 10aaf7c504..ebe5437c22 100644 --- a/extension.py +++ b/extension.py @@ -134,14 +134,17 @@ def __init__(self, name, sources, 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 6455fffa1f..38fc96f867 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -23,10 +23,10 @@ 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 @@ 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() + 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 @@ def initialize(self, plat_name=None): # 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 0fbd5412bc..48f22ad734 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -7,6 +7,9 @@ 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 @@ 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 @@ def parse_config_h(fp, g=None): _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 e258d2e58d..d60da854be 100644 --- a/tests/support.py +++ b/tests/support.py @@ -3,11 +3,19 @@ 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 6bca27ee06..2418e1656d 100644 --- a/tests/test_build.py +++ b/tests/test_build.py @@ -5,7 +5,7 @@ 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 536cd67319..145eff5453 100644 --- a/tests/test_build_clib.py +++ b/tests/test_build_clib.py @@ -120,8 +120,7 @@ def test_run(self): # 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 ebbb39979c..d09718366d 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -9,7 +9,7 @@ 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 @@ def test_solaris_enable_shared(self): 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 @@ def test_finalize_options(self): 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 b1d2d079bd..72e8915c00 100644 --- a/tests/test_build_scripts.py +++ b/tests/test_build_scripts.py @@ -5,7 +5,7 @@ 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 @@ def test_version_int(self): # --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 9db8d24617..27b51a0645 100644 --- a/tests/test_ccompiler.py +++ b/tests/test_ccompiler.py @@ -3,8 +3,10 @@ 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 @@ def find_library_file(self, dirs, lib, debug=0): 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 MyCCompiler(CCompiler): 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 98f0f08ef1..4b95dfa3aa 100644 --- a/tests/test_cygwinccompiler.py +++ b/tests/test_cygwinccompiler.py @@ -4,6 +4,7 @@ import os import subprocess import warnings +import sysconfig from test.support import check_warnings from test.support import captured_stdout @@ -23,13 +24,11 @@ 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 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 1ee30585fa..9d3cfe6f18 100755 --- a/tests/test_extension.py +++ b/tests/test_extension.py @@ -5,9 +5,11 @@ 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 76fa02acda..59e90801f2 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -5,12 +5,14 @@ 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 @@ def test_home_installation_scheme(self): 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 @@ def test_user_site(self): 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 @@ def _expanduser(path): # 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 @@ def test_record(self): 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 edc755ed15..e7df803168 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -4,7 +4,6 @@ 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 @@ def cleanup_testfn(self): 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 @@ def test_get_python_lib(self): # 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 @@ def test_get_python_inc(self): 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 008ae5d03a..6976dd55f8 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 8f8d4a10c2..ae6671de4f 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -6,15 +6,14 @@ 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 @@ def setUp(self): 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 @@ def tearDown(self): 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 @@ def _set_uname(self, uname): 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 @@ def test_get_platform(self): 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 @@ def test_get_platform(self): 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 @@ def test_get_platform(self): 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 51f6349a9e..c14a5d3fc6 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -17,7 +17,6 @@ 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 @@ 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 @@ def runtime_library_dir_option(self, dir): # 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 @@ def runtime_library_dir_option(self, dir): # 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 8adf6e0d29..0515fefd2f 100644 --- a/util.py +++ b/util.py @@ -15,173 +15,7 @@ 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 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: [PATCH 1851/2594] 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 a79982b645..72b0cefe42 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -6,8 +6,8 @@ __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 c16125f229..c2af95f13a 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -8,8 +8,9 @@ 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 f9205483d7..404f215f9b 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.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 2cf28234ec..71cc79881a 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -8,12 +8,13 @@ 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 d7b0e3c5f9..4d30f8ff2b 100644 --- a/command/build.py +++ b/command/build.py @@ -5,8 +5,8 @@ __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 39d37dab84..8f41facd4a 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -9,14 +9,12 @@ 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 1f8d238a2d..2a6d4dd127 100644 --- a/command/install.py +++ b/command/install.py @@ -7,15 +7,14 @@ 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 38fc96f867..6455fffa1f 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -23,9 +23,9 @@ 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 @@ 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 = _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 @@ def initialize(self, plat_name=None): # 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 48f22ad734..2561f573ea 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -25,6 +25,15 @@ # 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 ae6671de4f..896e1e07df 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -90,125 +90,12 @@ def _set_uname(self, 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'), '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 0515fefd2f..8fd2ca077f 100644 --- a/util.py +++ b/util.py @@ -16,6 +16,27 @@ 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. 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: [PATCH 1852/2594] 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 2d92dabf7b..a247bc2cd7 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -9,7 +9,7 @@ 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$" 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: [PATCH 1853/2594] 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 2561f573ea..4a8629e6bd 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -9,7 +9,7 @@ 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$" From efbb3664dba73095ae8d7ff40222b0dad5a13b75 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Wed, 3 Feb 2010 02:59:43 +0000 Subject: [PATCH 1854/2594] 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 404f215f9b..f13c73b36d 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 0000000000..ba2d3e19c2 --- /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()) 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: [PATCH 1855/2594] 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 b5336fefd6..60e4df0fea 100644 --- a/command/install.py +++ b/command/install.py @@ -17,6 +17,87 @@ 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) 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: [PATCH 1856/2594] 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 2a6d4dd127..cfebeeabd7 100644 --- a/command/install.py +++ b/command/install.py @@ -17,6 +17,87 @@ 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) From e04f6e5ca792f5ac3df0dac51226396914f83789 Mon Sep 17 00:00:00 2001 From: Collin Winter Date: Wed, 3 Feb 2010 22:06:03 +0000 Subject: [PATCH 1857/2594] 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 93d18814bc..a1c236aa15 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -13,11 +13,14 @@ 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 @@ def setUp(self): 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 @@ def test_build_ext_path_cross_platform(self): 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) From 6093446e3b99ec64d7b2207d36a9d9522114bd0e Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 6 Feb 2010 16:37:32 +0000 Subject: [PATCH 1858/2594] bump version to 2.7a3 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 55aaf81a3d..977643bf34 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "2.7a2" +__version__ = "2.7a3" #--end constants-- From eea5ca2f97e7bc3ff5477d01b0add5c0800b98a3 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 6 Feb 2010 23:53:52 +0000 Subject: [PATCH 1859/2594] 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 be3ec74976..a37f4a9b09 100644 --- a/tests/test_bdist.py +++ b/tests/test_bdist.py @@ -5,6 +5,8 @@ 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 5eaef2a9d7..f2220f474d 100644 --- a/tests/test_bdist_dumb.py +++ b/tests/test_bdist_dumb.py @@ -11,6 +11,8 @@ 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 ba2d3e19c2..7554e9f170 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 2aa257f7e6..25a5763a72 100644 --- a/tests/test_bdist_rpm.py +++ b/tests/test_bdist_rpm.py @@ -6,6 +6,8 @@ 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 9b1ba6d107..c2b13b314d 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 2174efbf64..cfd6485113 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 b67f987e6b..e97ac666a6 100644 --- a/tests/test_cygwinccompiler.py +++ b/tests/test_cygwinccompiler.py @@ -5,7 +5,7 @@ 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 6e1deced35..d5e07dcede 100644 --- a/tests/test_emxccompiler.py +++ b/tests/test_emxccompiler.py @@ -4,7 +4,7 @@ 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 9013220ffb..3066486066 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 From 98075501e10ea81e40f9f0e5aff17083d0ff96aa Mon Sep 17 00:00:00 2001 From: "R. David Murray" Date: Tue, 23 Feb 2010 00:24:49 +0000 Subject: [PATCH 1860/2594] 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 472591dc09..3c8bc41bae 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 @@ def test_package_data(self): 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 cffa9a0915..0b5dfb826f 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 @@ def test_read_setup_file(self): 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 99a6d90627..13d27abac0 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 @@ def test_finalize_options(self): 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 @@ def test_byte_compile(self): 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() 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: [PATCH 1861/2594] 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 60e4df0fea..fb17b4f6ea 100644 --- a/command/install.py +++ b/command/install.py @@ -499,10 +499,7 @@ def _expand_attrs(self, attrs): 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): 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: [PATCH 1862/2594] 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 cfebeeabd7..31d0387195 100644 --- a/command/install.py +++ b/command/install.py @@ -499,10 +499,7 @@ def _expand_attrs(self, attrs): 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): From 0d43be6127018ead86b1a3f2d20a9f97a85c92f3 Mon Sep 17 00:00:00 2001 From: "R. David Murray" Date: Wed, 24 Feb 2010 01:46:21 +0000 Subject: [PATCH 1863/2594] 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 3e45f6e89e..61e213a586 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 @@ def test_package_data(self): 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 9d3cfe6f18..857284dd55 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 @@ def test_read_setup_file(self): 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 99a6d90627..13d27abac0 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 @@ def test_finalize_options(self): 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 @@ def test_byte_compile(self): 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() From fa53c90a8e494240036abe332b3a0d4c1376ddd7 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Mon, 1 Mar 2010 22:10:45 +0000 Subject: [PATCH 1864/2594] 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 b37308c578..8c2ab8f3e5 100644 --- a/__init__.py +++ b/__init__.py @@ -22,5 +22,5 @@ # #--start constants-- -__version__ = "2.6.4" +__version__ = "2.6.5rc1" #--end constants-- 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: [PATCH 1865/2594] 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 420d7f171d..8248089fec 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -4,27 +4,21 @@ 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 @@ def initialize_options(self): 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 @@ def finalize_options(self): # 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 @@ def finalize_options(self): 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 @@ def finalize_options(self): 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 @@ def finalize_options(self): # 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 @@ def finalize_options(self): 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 @@ def run(self): # 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 @@ def build_extensions(self): 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 @@ def build_extension(self, ext): 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 @@ def build_extension(self, ext): 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 @@ def build_extension(self, ext): 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 @@ def swig_sources(self, sources, extension): 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 @@ def find_swig(self): ("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 @@ def get_ext_fullpath(self, ext_name): 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 @@ def get_ext_filename(self, ext_name): 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 @@ def get_export_symbols(self, ext): 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 @@ def get_libraries(self, ext): # 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 @@ def get_libraries(self, ext): # 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 @@ def get_libraries(self, ext): 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 fb17b4f6ea..44c76926ea 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 @@ }, } +# 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 @@ def initialize_options(self): 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 @@ def initialize_options(self): # 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 @@ def finalize_options(self): # $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 @@ def finalize_options(self): '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 @@ def finalize_options(self): # 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 @@ def finalize_unix(self): 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 @@ def finalize_unix(self): 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 @@ def finalize_other(self): 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 @@ def finalize_other(self): 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 @@ def handle_extra_path(self): # 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 @@ def handle_extra_path(self): 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 @@ def create_home_path(self): # -- 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 @@ def run(self): "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 @@ def create_path_file(self): # -- 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 @@ def get_outputs(self): 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 @@ def get_inputs(self): 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 @@ def has_data(self): ('install_data', has_data), ('install_egg_info', lambda self:True), ] + +# class install diff --git a/cygwinccompiler.py b/cygwinccompiler.py index 2a61bd5ad7..2dabc0f0fe 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -45,18 +45,16 @@ # * 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 @@ def __init__(self, verbose=0, dry_run=0, force=0): % 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 @@ def __init__(self, verbose=0, dry_run=0, force=0): # 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 @@ def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): 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 @@ def link(self, target_desc, objects, output_filename, output_dir=None, 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 @@ def __init__(self, verbose=0, dry_run=0, force=0): # 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 @@ def __init__(self, verbose=0, dry_run=0, force=0): 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 f2f77e52d1..f52e63232d 100644 --- a/emxccompiler.py +++ b/emxccompiler.py @@ -21,13 +21,12 @@ __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 @@ def __init__ (self, ("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 244b1e78ec..440d128cdc 100644 --- a/extension.py +++ b/extension.py @@ -5,8 +5,13 @@ __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 @@ def __init__ (self, name, sources, 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 @@ def __init__ (self, name, sources, 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 a247bc2cd7..54ccec4953 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -7,51 +7,59 @@ 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 @@ def get_makefile_filename(): _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 e97ac666a6..0000000000 --- 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 d5e07dcede..0000000000 --- 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 0b5dfb826f..0000000000 --- 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 8e3e942f02..c834b91b38 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 @@ def test_home_installation_scheme(self): 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 @@ def check_path(got, expected): 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 6976dd55f8..3f233e2823 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 @@ def gcv(v): 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 @@ def gcv(v): 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 @@ def gcv(v): 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 348b42be03..981ad000da 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 8fe1a6a13a..783d4dca84 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -18,6 +18,7 @@ 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 @@ 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 @@ def runtime_library_dir_option(self, dir): # 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 @@ def runtime_library_dir_option(self, 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 + 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 fbd3a67243..36ac721386 100644 --- a/util.py +++ b/util.py @@ -7,40 +7,184 @@ __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 (match, local_vars=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 @@ def byte_compile(py_files, optimize=0, force=0, prefix=None, base_dir=None, #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 @@ def byte_compile(py_files, optimize=0, force=0, prefix=None, base_dir=None, 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 @@ def byte_compile(py_files, optimize=0, force=0, prefix=None, base_dir=None, 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 From 17e4614fa2e3b907900dc2e4587a63c281db5425 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Fri, 5 Mar 2010 03:20:06 +0000 Subject: [PATCH 1866/2594] 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 From a5dade0cc3debdff89c52fbbfc9d23c0a6d21062 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Fri, 5 Mar 2010 03:33:11 +0000 Subject: [PATCH 1867/2594] 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 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: [PATCH 1868/2594] 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 0e3f33e437..a1c236aa15 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -3,17 +3,12 @@ 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 @@ 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[:] + 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 @@ def test_build_ext(self): 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 @@ def test_solaris_enable_shared(self): 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 @@ def test_finalize_options(self): 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 @@ def test_check_extensions_list(self): '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 @@ def test_check_extensions_list(self): 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 @@ 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') - 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 @@ def test_get_outputs(self): 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 @@ def test_get_outputs(self): 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 @@ def test_ext_fullpath(self): 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() 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: [PATCH 1869/2594] 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 a1c236aa15..5dea4dde7b 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -19,7 +19,11 @@ 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 0000000000..6b498dd6ca --- /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); +} 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: [PATCH 1870/2594] 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 5dea4dde7b..867ba96ec4 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -19,11 +19,10 @@ 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, 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: [PATCH 1871/2594] 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 0000000000..21fa9e8f43 --- /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 0000000000..ec6b690cdb --- /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 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: [PATCH 1872/2594] 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 867ba96ec4..4860c9bf24 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -19,10 +19,15 @@ 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 @@ def test_build_ext(self): 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 @@ def test_build_ext_inplace(self): 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 @@ def test_build_ext_path_cross_platform(self): 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()) 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: [PATCH 1873/2594] 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 41d67faf59..932b6ea2d9 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -38,6 +38,7 @@ _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) From 1e6721ca45da49df064748cdd8161fc717140487 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 6 Mar 2010 20:28:33 +0000 Subject: [PATCH 1874/2594] bump to 3.1.2rc1 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 8c0e5a3cba..2e6a6fb634 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.1.1" +__version__ = "3.1.2rc1" #--end constants-- From d9daa5ddcc3f32e2acd377eb210a3a061976c0ce Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 6 Mar 2010 20:34:14 +0000 Subject: [PATCH 1875/2594] bump version to 2.7a4 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 977643bf34..0158996441 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "2.7a3" +__version__ = "2.7a4" #--end constants-- From 32c211ba3c57c6d0405a86f29291c56f105a86f4 Mon Sep 17 00:00:00 2001 From: Florent Xicluna Date: Sun, 7 Mar 2010 12:14:25 +0000 Subject: [PATCH 1876/2594] 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 36ac721386..1a55f70894 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): From c0dac00e8b014f7b9180438f8e056f411f10a2c0 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Tue, 9 Mar 2010 22:31:52 +0000 Subject: [PATCH 1877/2594] Bumping to 2.6.5rc2 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 8c2ab8f3e5..888dd54a6b 100644 --- a/__init__.py +++ b/__init__.py @@ -22,5 +22,5 @@ # #--start constants-- -__version__ = "2.6.5rc1" +__version__ = "2.6.5rc2" #--end constants-- 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: [PATCH 1878/2594] 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 a1c236aa15..b6c521bf76 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -18,8 +18,7 @@ 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 0000000000..6b498dd6ca --- /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); +} 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: [PATCH 1879/2594] 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 b6c521bf76..5ecfe15bff 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -18,7 +18,10 @@ 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 6b498dd6ca..0000000000 --- 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); -} From 892006c7928c6f51cc81fa3238429b758f0cf719 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 14 Mar 2010 10:23:39 +0000 Subject: [PATCH 1880/2594] 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 f2849a9756..29dcc7c8ba 100644 --- a/tests/test_bdist.py +++ b/tests/test_bdist.py @@ -5,6 +5,8 @@ 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 5eaef2a9d7..746144bf5b 100644 --- a/tests/test_bdist_dumb.py +++ b/tests/test_bdist_dumb.py @@ -11,6 +11,8 @@ 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 ba2d3e19c2..2b2d8542ee 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 2aa257f7e6..1014e549af 100644 --- a/tests/test_bdist_rpm.py +++ b/tests/test_bdist_rpm.py @@ -6,6 +6,8 @@ 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 9b1ba6d107..ffe413536c 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 55ae421d46..728652e2ca 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 4b95dfa3aa..374f392d61 100644 --- a/tests/test_cygwinccompiler.py +++ b/tests/test_cygwinccompiler.py @@ -6,7 +6,7 @@ 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 2176d641d0..1360f8297a 100644 --- a/tests/test_emxccompiler.py +++ b/tests/test_emxccompiler.py @@ -4,7 +4,7 @@ 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 e7df803168..9496950f70 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 From 1e0efd73526d8014b1eeb4e238f5a288fa0d75a7 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Thu, 18 Mar 2010 22:14:36 +0000 Subject: [PATCH 1881/2594] 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 888dd54a6b..9e7ab903d2 100644 --- a/__init__.py +++ b/__init__.py @@ -22,5 +22,5 @@ # #--start constants-- -__version__ = "2.6.5rc2" +__version__ = "2.6.5" #--end constants-- 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: [PATCH 1882/2594] 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 1596586499..bd61bc56f3 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -310,7 +310,7 @@ def run(self): # 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 f992928d0a..b7cdc20aa4 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -329,6 +329,7 @@ def test_get_outputs(self): 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)) From 2fd59dc3b01d05c832ac667d16b3355315c35608 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 20 Mar 2010 20:47:27 +0000 Subject: [PATCH 1883/2594] version becomes 3.1.2 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 2e6a6fb634..6dca599fd6 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.1.2rc1" +__version__ = "3.1.2" #--end constants-- From 5db75ca65fe87f9aa02424394ac2c0dcc3ec1c7b Mon Sep 17 00:00:00 2001 From: Florent Xicluna Date: Sun, 21 Mar 2010 11:50:17 +0000 Subject: [PATCH 1884/2594] 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 4860c9bf24..f97ae1a7be 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -358,6 +358,9 @@ def test_setuptools_compat(self): 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 From 0ad92a85f3a3c1fc73154c6e95c2d39d6e189ae2 Mon Sep 17 00:00:00 2001 From: Florent Xicluna Date: Thu, 1 Apr 2010 18:17:09 +0000 Subject: [PATCH 1885/2594] #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 8248089fec..aeb6b744dc 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -676,7 +676,7 @@ def get_ext_filename(self, 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 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 1a55f70894..994dd211be 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 () 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: [PATCH 1886/2594] 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 f97ae1a7be..beb6d96c8b 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -19,15 +19,11 @@ 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 @@ def setUp(self): # 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 @@ def test_build_ext(self): 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 6b498dd6ca..0000000000 --- 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); -} 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: [PATCH 1887/2594] 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 d09718366d..aca2be2180 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -25,19 +25,23 @@ 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 @@ def setUp(self): 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 @@ def test_build_ext(self): 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) From 7b1cf0becdb877baafa3aa1ce8af17ac4c1ad0f7 Mon Sep 17 00:00:00 2001 From: Brian Curtin Date: Fri, 2 Apr 2010 22:38:52 +0000 Subject: [PATCH 1888/2594] 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 aca2be2180..e41a824fde 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -52,7 +52,7 @@ def setUp(self): 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 From 4a4731eda22170a77bb24dd3c7fc8ff4cafecf9d Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 10 Apr 2010 16:22:05 +0000 Subject: [PATCH 1889/2594] bump version to 2.7b1 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 0158996441..aa55cccb77 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "2.7a4" +__version__ = "2.7b1" #--end constants-- 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: [PATCH 1890/2594] 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 d60da854be..45c94411ee 100644 --- a/tests/support.py +++ b/tests/support.py @@ -63,6 +63,8 @@ def tearDown(self): 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): 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: [PATCH 1891/2594] 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 54ccec4953..bb53315bca 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": 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: [PATCH 1892/2594] 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 54ccec4953..bb53315bca 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": From dd745deff5b10a162a986ed9e8c886b70a2f9d32 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Wed, 5 May 2010 19:09:31 +0000 Subject: [PATCH 1893/2594] 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 44c76926ea..f1f3bd5c6f 100644 --- a/command/install.py +++ b/command/install.py @@ -69,20 +69,6 @@ '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 d8e8fd5f8d..b3d9d54ec0 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 bb53315bca..4d16b2674c 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 994dd211be..b3ec6e9606 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 From fde15c22137823177d21001b7d4e302d4986791d Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Wed, 5 May 2010 19:11:21 +0000 Subject: [PATCH 1894/2594] 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 31d0387195..3c28c660ec 100644 --- a/command/install.py +++ b/command/install.py @@ -65,20 +65,6 @@ '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 758bde38bf..3a71bfd1ac 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 8fd2ca077f..c8bf0064cd 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) From 7337895492390e5fc7b8573ebfa9f3b43d5af94b Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Sat, 8 May 2010 08:44:37 +0000 Subject: [PATCH 1895/2594] 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 783d4dca84..ba831f7b57 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -15,7 +15,7 @@ __revision__ = "$Id$" -import os, sys +import os, sys, re from types import StringType, NoneType from distutils import sysconfig @@ -305,10 +305,29 @@ def find_library_file(self, dirs, lib, debug=0): 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 From edfa9c8bc18c53f3fd81697b251f5f84726182a3 Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Sat, 8 May 2010 15:23:57 +0000 Subject: [PATCH 1896/2594] 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 ba831f7b57..783d4dca84 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -15,7 +15,7 @@ __revision__ = "$Id$" -import os, sys, re +import os, sys from types import StringType, NoneType from distutils import sysconfig @@ -305,29 +305,10 @@ def find_library_file(self, dirs, lib, debug=0): 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 From 5b183062b39a966eeac553886793c3eef3c706e7 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 8 May 2010 17:08:17 +0000 Subject: [PATCH 1897/2594] 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 aa55cccb77..5f401709cf 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "2.7b1" +__version__ = "2.7b2" #--end constants-- 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: [PATCH 1898/2594] 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 f1335e6a36..4cede72988 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -179,66 +179,34 @@ def get_file_list(self): 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 20b20aae0b..76f5b77471 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -346,6 +346,47 @@ def test_make_distribution_owner_group(self): 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) 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: [PATCH 1899/2594] 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 a366d1eaf6..b93994d07f 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -190,67 +190,34 @@ def get_file_list (self): 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 e322c1385c..6b8784ac3d 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -29,6 +29,7 @@ def setUp(self): 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 @@ def test_make_distribution(self): 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) 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: [PATCH 1900/2594] 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 f33406cd59..b2088f98d1 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -179,66 +179,34 @@ def get_file_list(self): 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 e0f1e93768..1bc36c5c8f 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -346,6 +346,47 @@ def test_make_distribution_owner_group(self): 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) 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: [PATCH 1901/2594] 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 ace9eeeb61..88fde46c30 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -173,66 +173,34 @@ def get_file_list(self): 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 b7e5859cb9..f95035dfb0 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -277,6 +277,47 @@ def test_finalize_options(self): 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) 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: [PATCH 1902/2594] 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 4cede72988..087ae9dcc6 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -63,7 +63,8 @@ def checking_metadata(self): "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', 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: [PATCH 1903/2594] 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 b93994d07f..535b8d2aea 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', 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: [PATCH 1904/2594] 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 b2088f98d1..f6e099b89f 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -63,7 +63,8 @@ def checking_metadata(self): "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', 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: [PATCH 1905/2594] 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 88fde46c30..bb2106120a 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -63,7 +63,8 @@ def checking_metadata(self): "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', From 8ea633f81832fc7cc959fa57b3ee97d886f1bd7d Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 18 May 2010 23:37:50 +0000 Subject: [PATCH 1906/2594] 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 7e0c708888..fb15530ead 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$" From b78e3623ca34400e29297da0409211a7d83728c6 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 19 May 2010 17:00:07 +0000 Subject: [PATCH 1907/2594] 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 758857081c..b301a8338c 100644 --- a/log.py +++ b/log.py @@ -27,6 +27,10 @@ def _log(self, level, msg, args): 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() From 727851619e008c1f44e79a60ddff41bb4ebf02b7 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 19 May 2010 17:15:50 +0000 Subject: [PATCH 1908/2594] 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 0000000000..d35de3456c --- /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") From 6cf4f07323aac001c1c1a0e3aafbe55a84cec391 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 19 May 2010 20:30:19 +0000 Subject: [PATCH 1909/2594] 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 758857081c..b301a8338c 100644 --- a/log.py +++ b/log.py @@ -27,6 +27,10 @@ def _log(self, level, msg, args): 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 0000000000..d35de3456c --- /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") From cdb97f5f0ebcf2aa7abf44d4e08696e01310b7c3 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Thu, 3 Jun 2010 09:47:21 +0000 Subject: [PATCH 1910/2594] 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 783d4dca84..b76f0d41cf 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -15,7 +15,7 @@ __revision__ = "$Id$" -import os, sys +import os, sys, re from types import StringType, NoneType from distutils import sysconfig @@ -305,10 +305,30 @@ def find_library_file(self, dirs, lib, debug=0): 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 From 0ca04fb2c44992cbbfbee30960d10ffafeb3f286 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Thu, 3 Jun 2010 14:42:25 +0000 Subject: [PATCH 1911/2594] 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 c14a5d3fc6..81a7de6191 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -15,7 +15,7 @@ __revision__ = "$Id$" -import os, sys +import os, sys, re from distutils.dep_util import newer from distutils.ccompiler import \ @@ -320,10 +320,31 @@ def find_library_file(self, dirs, lib, debug=0): 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 From cd778292f149a5d71319f00c1bb22943d766683d Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Thu, 3 Jun 2010 14:59:56 +0000 Subject: [PATCH 1912/2594] 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 783d4dca84..b76f0d41cf 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -15,7 +15,7 @@ __revision__ = "$Id$" -import os, sys +import os, sys, re from types import StringType, NoneType from distutils import sysconfig @@ -305,10 +305,30 @@ def find_library_file(self, dirs, lib, debug=0): 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 From 9c55a255899d88a40630c4ec8396638d17ebd89b Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Thu, 3 Jun 2010 16:21:03 +0000 Subject: [PATCH 1913/2594] 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 a33fdf0f7c..f4605f3044 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -15,7 +15,7 @@ __revision__ = "$Id$" -import os, sys +import os, sys, re from distutils import sysconfig from distutils.dep_util import newer @@ -317,10 +317,30 @@ def find_library_file(self, dirs, lib, debug=0): 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 From da66d6ba5d1d62159f678e9091f3946e9b096241 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sun, 6 Jun 2010 00:22:09 +0000 Subject: [PATCH 1914/2594] 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 5f401709cf..7f3021010b 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "2.7b2" +__version__ = "2.7rc1" #--end constants-- From 59d7c14ff6725c5dba040f899f9c145cca169185 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Tue, 15 Jun 2010 16:05:20 +0000 Subject: [PATCH 1915/2594] 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 3c28c660ec..e3e387ab5d 100644 --- a/command/install.py +++ b/command/install.py @@ -302,8 +302,8 @@ def finalize_options(self): # 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 @@ def finalize_options(self): 'sys_exec_prefix': exec_prefix, 'exec_prefix': exec_prefix, 'srcdir': srcdir, + 'projectbase': projectbase, } self.config_vars['userbase'] = self.install_userbase From d42efce4a037530f77c8cc91e7f1735e3c14b96c Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Mon, 21 Jun 2010 15:27:46 +0000 Subject: [PATCH 1916/2594] 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 932b6ea2d9..d5d7f66528 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -37,10 +37,20 @@ _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 From 898a004e6eee52a5ab4f92e2e40acbcaf0bdfd05 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Mon, 21 Jun 2010 15:37:16 +0000 Subject: [PATCH 1917/2594] 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 6455fffa1f..15425d7194 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -37,9 +37,18 @@ 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 From 39ba1bd353bca4a92a427e8e8ff3e9a870d47432 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Mon, 21 Jun 2010 15:39:28 +0000 Subject: [PATCH 1918/2594] 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 9bf54c102d..9fe8c744cb 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -37,9 +37,18 @@ _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 From 7bed757e02f1ccf941a3b6210b46c0edda5ea9e8 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Mon, 21 Jun 2010 15:42:48 +0000 Subject: [PATCH 1919/2594] 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 ad021b5754..761b9ca236 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -38,9 +38,18 @@ 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 From c54c55e5b4eeb636a400eff44da8f327ba383b93 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Mon, 21 Jun 2010 15:57:57 +0000 Subject: [PATCH 1920/2594] bump verson to 2.7rc2 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 7f3021010b..bdbf2b2d65 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "2.7rc1" +__version__ = "2.7rc2" #--end constants-- From 0f327d043dda9cbd1d528ff9e4e4051a803bd41a Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Sun, 27 Jun 2010 12:36:16 +0000 Subject: [PATCH 1921/2594] 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 b76f0d41cf..c49ac9ba91 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -324,7 +324,9 @@ def find_library_file(self, dirs, lib, debug=0): 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) From 177cb25982dd05cc40f8612a8ae14cf334509226 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Sun, 27 Jun 2010 12:37:46 +0000 Subject: [PATCH 1922/2594] 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 81a7de6191..081790827d 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -340,7 +340,9 @@ def find_library_file(self, dirs, lib, debug=0): 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) From 5d756882f69f443b18cf373e4a61b1e803905f99 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Sun, 27 Jun 2010 12:39:22 +0000 Subject: [PATCH 1923/2594] 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 b76f0d41cf..c49ac9ba91 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -324,7 +324,9 @@ def find_library_file(self, dirs, lib, debug=0): 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) From 1d311c0e512b4684b36e4a29a393a84d0452e8f5 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Sun, 27 Jun 2010 12:40:35 +0000 Subject: [PATCH 1924/2594] 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 f4605f3044..bf73416154 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -336,7 +336,9 @@ def find_library_file(self, dirs, lib, debug=0): 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) From 0342126c2eb6d6cecd585a296be6b111a63eaebd Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 3 Jul 2010 13:56:13 +0000 Subject: [PATCH 1925/2594] update to 2.7 final --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index bdbf2b2d65..665fd768d2 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "2.7rc2" +__version__ = "2.7" #--end constants-- From 6586ed42561965fc19b2c6c94f751b85b79b2f1c Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 3 Jul 2010 14:51:25 +0000 Subject: [PATCH 1926/2594] prepare for 2.7.1 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 665fd768d2..2c69a1836a 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "2.7" +__version__ = "2.7.1a0" #--end constants-- From 9a1b14784687365ce119d0cc229765a4e50016a0 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Sun, 11 Jul 2010 08:52:52 +0000 Subject: [PATCH 1927/2594] 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 b3ec6e9606..4dcfeb5505 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] From 611bf70820557ab8c49303983449064d99360242 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Sun, 11 Jul 2010 09:08:11 +0000 Subject: [PATCH 1928/2594] 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 36ac721386..c4a8711d08 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] From a74d1cd994eb527a565d5686fa315dcdafac6d31 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Sun, 11 Jul 2010 09:29:09 +0000 Subject: [PATCH 1929/2594] Fix for issue 9164 --- util.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/util.py b/util.py index 9a77561fff..8175434586 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] From b97dbcf42d529ab4dfb2896cef8c0d53960595d7 Mon Sep 17 00:00:00 2001 From: Ezio Melotti Date: Mon, 12 Jul 2010 19:49:41 +0000 Subject: [PATCH 1930/2594] #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 1bc36c5c8f..9a76eacb65 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -346,6 +346,7 @@ def test_make_distribution_owner_group(self): finally: archive.close() + @unittest.skipUnless(zlib, "requires zlib") def test_get_file_list(self): # make sure MANIFEST is recalculated dist, cmd = self.get_cmd() From 0c1ee9af184f3d93215a250b609f2607c2edfd62 Mon Sep 17 00:00:00 2001 From: Ezio Melotti Date: Mon, 12 Jul 2010 19:52:15 +0000 Subject: [PATCH 1931/2594] 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 76f5b77471..f83b9f29b6 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -346,6 +346,7 @@ def test_make_distribution_owner_group(self): finally: archive.close() + @unittest.skipUnless(zlib, "requires zlib") def test_get_file_list(self): # make sure MANIFEST is recalculated dist, cmd = self.get_cmd() From 9562a276832b4f27e2b7666e6157fa95937a22e1 Mon Sep 17 00:00:00 2001 From: Ezio Melotti Date: Mon, 12 Jul 2010 20:00:39 +0000 Subject: [PATCH 1932/2594] 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 f95035dfb0..aa3a99fa3f 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -277,6 +277,7 @@ def test_finalize_options(self): 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() From 6cdb93646957b4c0ef41fd23bc05278c6f949d73 Mon Sep 17 00:00:00 2001 From: Ezio Melotti Date: Mon, 12 Jul 2010 21:23:20 +0000 Subject: [PATCH 1933/2594] 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 aa3a99fa3f..f95035dfb0 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -277,7 +277,6 @@ def test_finalize_options(self): 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() 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: [PATCH 1934/2594] 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 28e93fed78..16164c7f1f 100644 --- a/archive_util.py +++ b/archive_util.py @@ -14,55 +14,15 @@ 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 77d94a59a2..c5e5cd2571 100644 --- a/bcppcompiler.py +++ b/bcppcompiler.py @@ -13,11 +13,13 @@ __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 1a4e8fb2af..34c77a37a3 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -5,71 +5,15 @@ __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 @@ def _fix_compile_args(self, output_dir, macros, include_dirs): 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 @@ def create_static_lib(self, objects, output_libname, output_dir=None, """ 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 @@ def link(self, target_desc, objects, output_filename, output_dir=None, # 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 @@ def link_shared_lib(self, objects, output_libname, output_dir=None, 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 @@ def link_shared_object(self, objects, output_filename, output_dir=None, 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 @@ def spawn(self, cmd): 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 08632099aa..ae4efc7efc 100644 --- a/cmd.py +++ b/cmd.py @@ -367,11 +367,9 @@ def spawn(self, cmd, search_path=1, level=1): 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 72b0cefe42..1a360b59e3 100644 --- a/command/bdist.py +++ b/command/bdist.py @@ -6,10 +6,9 @@ __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 @@ def initialize_options(self): 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 @@ def run(self): 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 c2af95f13a..2d39922672 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -7,18 +7,16 @@ __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 @@ def initialize_options(self): 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 @@ def run(self): # 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 f13c73b36d..c4be47b579 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 @@ def __init__(self, *args, **kw): 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 @@ def add_ui(self): # 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 0a579dbb93..452f9502ad 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -5,14 +5,13 @@ __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 71cc79881a..d6d01c630d 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -5,16 +5,13 @@ __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 4d30f8ff2b..9c2667cfd2 100644 --- a/command/build.py +++ b/command/build.py @@ -5,15 +5,16 @@ __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 @@ def run(self): 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 @@ def has_ext_modules(self): 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 4c6443ca6e..258d7c10be 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -18,8 +18,8 @@ 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 8f41facd4a..bd61bc56f3 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -7,14 +7,12 @@ __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 @@ def initialize_options(self): 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 @@ def finalize_options(self): # 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 @@ def finalize_options(self): 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 @@ def finalize_options(self): # 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 @@ def run(self): # 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 @@ def build_extension(self, ext): 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 @@ def build_extension(self, ext): 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 @@ def get_ext_filename(self, ext_name): 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 @@ def get_libraries(self, ext): # 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 @@ def get_libraries(self, ext): # 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 7cc935390f..26002e4b3f 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -4,16 +4,16 @@ __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 @@ def find_data_files(self, package, src_dir): 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 a54d6ed742..8b08bfeaf0 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -6,6 +6,7 @@ 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 @@ def copy_scripts(self): 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 @@ def copy_scripts(self): 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 56f643c88b..ac80a54eb1 100644 --- a/command/config.py +++ b/command/config.py @@ -11,12 +11,11 @@ __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 e3e387ab5d..2a905d92f8 100644 --- a/command/install.py +++ b/command/install.py @@ -7,17 +7,26 @@ 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 @@ '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 @@ '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 @@ def initialize_options(self): 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 @@ def finalize_options(self): # 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 @@ def finalize_options(self): '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 @@ def finalize_unix(self): 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 @@ def finalize_unix(self): 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 @@ def finalize_other(self): 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 @@ def finalize_other(self): 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 @@ def _expand_attrs(self, attrs): 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 7d3dc53afe..bdf5f8f09c 100644 --- a/command/register.py +++ b/command/register.py @@ -7,15 +7,13 @@ __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 f6e099b89f..bb2106120a 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -7,14 +7,14 @@ 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 @@ def checking_metadata(self): ('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 @@ def initialize_options(self): 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 @@ def make_distribution(self): 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 bb1b7fc4fe..f602fbeb68 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 @@ def run(self): 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 @@ def upload_file(self, command, pyversion, filename): 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 @@ def upload_file(self, command, pyversion, filename): 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 fe41ce977e..5b625f3f7d 100644 --- a/config.py +++ b/config.py @@ -4,6 +4,7 @@ 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 @@ def _read_pypirc(self): 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 6ed3e8fa97..6e4892039e 100644 --- a/core.py +++ b/core.py @@ -8,12 +8,10 @@ __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 @@ 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 @@ def gen_usage(script_name): '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 @@ class found in 'cmdclass' is used in place of the default, which is 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 @@ class found in 'cmdclass' is used in place of the default, which is 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 f381f60875..8504371810 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -50,13 +50,16 @@ 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 @@ def __init__(self, verbose=0, dry_run=0, force=0): % 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 b91a62eb56..07b3549c6f 100644 --- a/dep_util.py +++ b/dep_util.py @@ -9,27 +9,29 @@ 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 370025b734..98e6252c6c 100644 --- a/dir_util.py +++ b/dir_util.py @@ -4,7 +4,7 @@ __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 5a107e7d83..1c1ea477db 100644 --- a/dist.py +++ b/dist.py @@ -7,15 +7,13 @@ __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 @@ def __init__ (self, attrs=None): 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 @@ def find_config_files(self): 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 @@ def find_config_files(self): 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 fd79aec2cf..62a4c5b4e8 100644 --- a/emxccompiler.py +++ b/emxccompiler.py @@ -21,13 +21,12 @@ __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 @@ def __init__ (self, ("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 d9c47c761c..acecacccb5 100644 --- a/errors.py +++ b/errors.py @@ -10,79 +10,90 @@ __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 ebe5437c22..5c07bdae82 100644 --- a/extension.py +++ b/extension.py @@ -6,6 +6,7 @@ __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 @@ def __init__(self, name, sources, 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 73343ad5af..879d4d25bf 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -10,11 +10,9 @@ __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 @@ def __init__(self, options=[]): '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 3a71bfd1ac..65aa7e0fdd 100644 --- a/file_util.py +++ b/file_util.py @@ -10,18 +10,17 @@ 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 bfc6df694a..06a8da9a07 100644 --- a/filelist.py +++ b/filelist.py @@ -108,7 +108,7 @@ def process_template_line(self, line): # 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 @@ def process_template_line(self, 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): """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 @@ def include_pattern(self, pattern, anchor=1, prefix=None, is_regex=0): 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 15425d7194..761b9ca236 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -19,9 +19,10 @@ 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 dc3bd8de47..1cd0f91d5f 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -10,12 +10,12 @@ __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 @@ def load_macros(self, version): 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 4a8629e6bd..0fbd5412bc 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -7,9 +7,6 @@ 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 @@ 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 @@ def get_makefile_filename(): _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 45c94411ee..e258d2e58d 100644 --- a/tests/support.py +++ b/tests/support.py @@ -3,19 +3,11 @@ 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 @@ def tearDown(self): 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 682f19a2b3..c6e08cbc2b 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -14,31 +14,16 @@ 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 @@ def test_make_tarball(self): 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 @@ def _create_files(self): 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 @@ def test_compress_deprecated(self): 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 @@ def test_make_archive(self): 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 29dcc7c8ba..f2849a9756 100644 --- a/tests/test_bdist.py +++ b/tests/test_bdist.py @@ -5,8 +5,6 @@ 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 746144bf5b..5e76809f23 100644 --- a/tests/test_bdist_dumb.py +++ b/tests/test_bdist_dumb.py @@ -4,15 +4,6 @@ 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 @@ def tearDown(self): 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 @@ def test_simple_built(self): # 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 1014e549af..2aa257f7e6 100644 --- a/tests/test_bdist_rpm.py +++ b/tests/test_bdist_rpm.py @@ -6,8 +6,6 @@ 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 ffe413536c..9b1ba6d107 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 145eff5453..536cd67319 100644 --- a/tests/test_build_clib.py +++ b/tests/test_build_clib.py @@ -120,7 +120,8 @@ def test_run(self): # 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 e41a824fde..b7cdc20aa4 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -3,13 +3,10 @@ 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 @@ 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 @@ def setUp(self): 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 @@ def test_build_ext(self): 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 @@ def test_finalize_options(self): 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 @@ def test_get_outputs(self): 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 @@ def test_ext_fullpath(self): 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 61e213a586..3e45f6e89e 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 @@ def _setup_package_data(self): 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 72e8915c00..b1d2d079bd 100644 --- a/tests/test_build_scripts.py +++ b/tests/test_build_scripts.py @@ -5,7 +5,7 @@ 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 @@ def test_version_int(self): # --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 27b51a0645..0000000000 --- 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 728652e2ca..55ae421d46 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 374f392d61..a57694d48e 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 @@ 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 @@ -58,6 +81,40 @@ def test_check_config_h(self): 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 @@ def test_get_msvcr(self): '[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 4f51c16e51..3b7637f3af 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -7,9 +7,8 @@ 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 @@ def find_config_files(self): return self._config_files -class DistributionTestCase(support.TempdirManager, - support.LoggingSilencer, +class DistributionTestCase(support.LoggingSilencer, support.EnvironGuard, unittest.TestCase): @@ -60,27 +58,6 @@ def create_distribution(self, configfiles=()): 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 @@ def test_announce(self): 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 @@ def test_long_description(self): "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 1360f8297a..0000000000 --- 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 857284dd55..1ee30585fa 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 @@ def test_read_setup_file(self): 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 99b421f73f..fac4a5d1a9 100644 --- a/tests/test_file_util.py +++ b/tests/test_file_util.py @@ -3,7 +3,7 @@ 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 @@ def test_move_file_verbosity(self): 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 d98325ae54..331180d235 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 @@ def test_glob_to_re(self): 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 59e90801f2..76fa02acda 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -5,14 +5,12 @@ 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 @@ def test_home_installation_scheme(self): 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 @@ def test_user_site(self): 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 @@ def _expanduser(path): # 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 @@ def test_record(self): 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 13d27abac0..99a6d90627 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 @@ def test_finalize_options(self): 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 @@ def _setup_byte_compile(self): 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 acda1b1ac1..c03ad10147 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -202,10 +202,10 @@ def test_strict(self): 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 9a76eacb65..f95035dfb0 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -3,22 +3,6 @@ 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 @@ def _warn(*args): 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 @@ def test_prune_file_list(self): # 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 @@ def test_make_distribution(self): 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 @@ def test_add_defaults(self): 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 @@ def test_finalize_options(self): 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 9496950f70..edc755ed15 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 @@ def cleanup_testfn(self): 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 @@ def test_get_python_lib(self): # 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 @@ def test_get_python_inc(self): 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 6976dd55f8..3a41e6fcaa 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 @@ def gcv(v): 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 979ab228dd..35e970051e 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 @@ [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 @@ def test_upload(self): 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 896e1e07df..0c732f8244 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -3,33 +3,15 @@ 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 @@ def setUp(self): 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 @@ def setUp(self): 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 @@ def tearDown(self): 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 @@ 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) + + # 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 @@ def test_rfc822_escape(self): '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 53c8561a3e..97459fbf73 100644 --- a/text_file.py +++ b/text_file.py @@ -6,8 +6,8 @@ __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 081790827d..bf73416154 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -17,6 +17,7 @@ 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 @@ 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 @@ def runtime_library_dir_option(self, dir): # 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 @@ def runtime_library_dir_option(self, dir): 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 @@ def find_library_file(self, dirs, lib, debug=0): # 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 c8bf0064cd..8175434586 100644 --- a/util.py +++ b/util.py @@ -7,40 +7,182 @@ __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 (match, local_vars=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 @@ def byte_compile(py_files, optimize=0, force=0, prefix=None, base_dir=None, 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 @@ def byte_compile(py_files, optimize=0, force=0, prefix=None, base_dir=None, 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): From 575e9faa64b8e84e15de1124de71c3471b966327 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Fri, 23 Jul 2010 09:43:17 +0000 Subject: [PATCH 1935/2594] 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 0fbd5412bc..48f3fe4d59 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 edc755ed15..0167e0f1a3 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -93,6 +93,15 @@ def test_parse_makefile_literal_dollar(self): '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)) From 022d6c31d81f8d4f42776503786dbfa486417322 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 31 Jul 2010 08:56:11 +0000 Subject: [PATCH 1936/2594] 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 18b02fc629..7b2227916a 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.2a0" +__version__ = "3.2a1" #--end constants-- From b9efa362d457b32845e11e0e03f377472112c0f1 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 31 Jul 2010 21:54:24 +0000 Subject: [PATCH 1937/2594] #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 bb2106120a..f51d72fad7 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -240,8 +240,7 @@ def add_defaults(self): 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 From dd1e83e827cd6658e31699039aae197149e4557e Mon Sep 17 00:00:00 2001 From: "R. David Murray" Date: Sun, 1 Aug 2010 01:53:52 +0000 Subject: [PATCH 1938/2594] 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 264e66faf2..251c0df4e9 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 From f5fa4f4f916ef28498df9c9cb54fb3f563d824bd Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 1 Aug 2010 19:07:28 +0000 Subject: [PATCH 1939/2594] 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 bb2106120a..f51d72fad7 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -240,8 +240,7 @@ def add_defaults(self): 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 From 8e471803385c10cd1f4f98d669826eb388cc6476 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Mon, 2 Aug 2010 19:16:34 +0000 Subject: [PATCH 1940/2594] #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 c4be47b579..8a458d8536 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -148,7 +148,7 @@ def finalize_options(self): 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 d6d01c630d..3aa1dac7f3 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -89,7 +89,7 @@ def finalize_options(self): 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 From df831fea4bc33d771c8e6a1fb72533f71d7c2464 Mon Sep 17 00:00:00 2001 From: Ezio Melotti Date: Mon, 2 Aug 2010 20:26:41 +0000 Subject: [PATCH 1941/2594] 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 8248089fec..aeb6b744dc 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -676,7 +676,7 @@ def get_ext_filename(self, 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 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 c4a8711d08..90f68d8208 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 () From 5a5a41af466e35970c94b0ab039d62f68581de05 Mon Sep 17 00:00:00 2001 From: Ezio Melotti Date: Mon, 2 Aug 2010 21:35:06 +0000 Subject: [PATCH 1942/2594] 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 90f68d8208..e92bef1b5f 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): From 231e1cc9526df4a63a21c09b41015b05d98cd7f9 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Mon, 2 Aug 2010 21:44:25 +0000 Subject: [PATCH 1943/2594] 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 72578498f7..ded837d752 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -148,7 +148,7 @@ def finalize_options (self): 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 88c0532f81..a31a5f7bac 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -95,7 +95,7 @@ def finalize_options (self): 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 From 59c26754fc99abfc3cf85a7b435eb0ec14333812 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Mon, 2 Aug 2010 21:45:43 +0000 Subject: [PATCH 1944/2594] 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 da0b30d864..42271c4df3 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -138,7 +138,7 @@ def finalize_options (self): 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 d153e2bc38..128e1992cb 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -93,7 +93,7 @@ def finalize_options (self): 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 From 95fbccaf635ace75fe1838449f512c46dd5b2e49 Mon Sep 17 00:00:00 2001 From: Ezio Melotti Date: Tue, 3 Aug 2010 07:51:50 +0000 Subject: [PATCH 1945/2594] 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 251c0df4e9..782d4ef96c 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 708ef0f38f..9f8a759a74 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -157,7 +157,7 @@ def get_package_dir (self, package): 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 @@ def get_package_dir (self, package): 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 77f253255f..92f49346f7 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 88b33c7c94..4448d5c5a0 100644 --- a/filelist.py +++ b/filelist.py @@ -68,7 +68,7 @@ def sort (self): 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 5ecfe15bff..1ed9d04b99 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -349,6 +349,11 @@ def test_build_ext_inplace(self): 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 From 60f0fc889114642ceeba5b3d812c705ea4b97de9 Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Tue, 3 Aug 2010 21:18:06 +0000 Subject: [PATCH 1946/2594] - 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 bb53315bca..46d23ecc78 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -25,7 +25,7 @@ # 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 From 5673d48db60eb63f4da8a53f210656c22b95c7a4 Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Tue, 3 Aug 2010 21:33:04 +0000 Subject: [PATCH 1947/2594] 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 0fbd5412bc..9842d26c47 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -25,7 +25,7 @@ # 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 From 499b7fdd90857e215a9abcc6a56971e04e1aa1a0 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Tue, 3 Aug 2010 22:39:42 +0000 Subject: [PATCH 1948/2594] 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 9e7ab903d2..a861b3fb02 100644 --- a/__init__.py +++ b/__init__.py @@ -22,5 +22,5 @@ # #--start constants-- -__version__ = "2.6.5" +__version__ = "2.6.6rc1" #--end constants-- 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: [PATCH 1949/2594] 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 535b8d2aea..a366d1eaf6 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 @@ def get_file_list (self): 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 6b8784ac3d..e322c1385c 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -29,7 +29,6 @@ def setUp(self): 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 @@ def test_make_distribution(self): 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) 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: [PATCH 1950/2594] 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 f51d72fad7..818a45260d 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -335,8 +335,21 @@ def write_manifest(self): 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 f95035dfb0..209aa59baf 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -29,6 +29,7 @@ """ MANIFEST = """\ +# file GENERATED by distutils, do NOT edit README inroot.txt setup.py @@ -294,7 +295,7 @@ def test_get_file_list(self): 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 @@ def test_get_file_list(self): 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) 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: [PATCH 1951/2594] 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 f51d72fad7..818a45260d 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -335,8 +335,21 @@ def write_manifest(self): 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 f95035dfb0..209aa59baf 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -29,6 +29,7 @@ """ MANIFEST = """\ +# file GENERATED by distutils, do NOT edit README inroot.txt setup.py @@ -294,7 +295,7 @@ def test_get_file_list(self): 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 @@ def test_get_file_list(self): 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) 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: [PATCH 1952/2594] 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 087ae9dcc6..f2d2f94beb 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -349,8 +349,21 @@ def write_manifest(self): 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 f83b9f29b6..87adb45894 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -45,6 +45,7 @@ """ MANIFEST = """\ +# file GENERATED by distutils, do NOT edit README inroot.txt setup.py @@ -364,7 +365,7 @@ def test_get_file_list(self): 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 @@ def test_get_file_list(self): 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) From 55096b84cc9e0882c64d0772880b9d08f2f9e41e Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Mon, 16 Aug 2010 22:19:57 +0000 Subject: [PATCH 1953/2594] Bumping to 2.6.6rc2. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index a861b3fb02..c456c7487f 100644 --- a/__init__.py +++ b/__init__.py @@ -22,5 +22,5 @@ # #--start constants-- -__version__ = "2.6.6rc1" +__version__ = "2.6.6rc2" #--end constants-- From e870a5993437de693e68c83e15ed02f3b7af1575 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Mon, 23 Aug 2010 23:37:56 +0000 Subject: [PATCH 1954/2594] 2.6.6 final. \o/ --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index c456c7487f..90f96feae1 100644 --- a/__init__.py +++ b/__init__.py @@ -22,5 +22,5 @@ # #--start constants-- -__version__ = "2.6.6rc2" +__version__ = "2.6.6" #--end constants-- From 36cae28e19a68627c873ff982d2a2dd3279cea9c Mon Sep 17 00:00:00 2001 From: Florent Xicluna Date: Mon, 30 Aug 2010 14:05:50 +0000 Subject: [PATCH 1955/2594] 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 8a458d8536..b11957a7dc 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 3b7637f3af..007803e12d 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 From b202a1a25f6d7ab9707910d6e36df58e8f9a2508 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Fri, 3 Sep 2010 18:30:30 +0000 Subject: [PATCH 1956/2594] 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 b7cdc20aa4..52479d7d37 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -323,8 +323,8 @@ def test_get_outputs(self): 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 @@ def test_get_outputs(self): 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) From a51303cf88483095772fd60fc7184fceafc9073c Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 5 Sep 2010 08:30:40 +0000 Subject: [PATCH 1957/2594] Bump to 3.2a2. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 7b2227916a..92ed8f4db2 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.2a1" +__version__ = "3.2a2" #--end constants-- 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: [PATCH 1958/2594] 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 258d7c10be..428011a64d 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"), 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: [PATCH 1959/2594] 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 258d7c10be..428011a64d 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"), 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: [PATCH 1960/2594] 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 8d49de75e1..98a1726ffa 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"), 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: [PATCH 1961/2594] 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 f602fbeb68..41df127799 100644 --- a/command/upload.py +++ b/command/upload.py @@ -194,4 +194,5 @@ def upload_file(self, command, pyversion, filename): 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) 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: [PATCH 1962/2594] 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 f602fbeb68..41df127799 100644 --- a/command/upload.py +++ b/command/upload.py @@ -194,4 +194,5 @@ def upload_file(self, command, pyversion, filename): 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) 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: [PATCH 1963/2594] 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 18a10a0b7f..4b3ed3f991 100644 --- a/command/upload.py +++ b/command/upload.py @@ -186,4 +186,5 @@ def upload_file(self, command, pyversion, filename): 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) 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: [PATCH 1964/2594] 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 41df127799..99e03d747c 100644 --- a/command/upload.py +++ b/command/upload.py @@ -194,5 +194,5 @@ def upload_file(self, command, pyversion, filename): 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) 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: [PATCH 1965/2594] 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 41df127799..99e03d747c 100644 --- a/command/upload.py +++ b/command/upload.py @@ -194,5 +194,5 @@ def upload_file(self, command, pyversion, filename): 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) 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: [PATCH 1966/2594] 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 4b3ed3f991..c3f19d207f 100644 --- a/command/upload.py +++ b/command/upload.py @@ -186,5 +186,5 @@ def upload_file(self, command, pyversion, filename): 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) From 2f09caa926e12dbc91a0652c0e3d8ab494046579 Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Fri, 10 Sep 2010 19:44:44 +0000 Subject: [PATCH 1967/2594] =?UTF-8?q?Issue=20#941346:=20Improve=20the=20bu?= =?UTF-8?q?ild=20process=20under=20AIX=20and=20allow=20Python=20to=20be=20?= =?UTF-8?q?built=20as=20a=20shared=20library.=20=20Patch=20by=20S=C3=A9bas?= =?UTF-8?q?tien=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 bd61bc56f3..4e664642b8 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -748,6 +748,9 @@ def get_libraries(self, ext): 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'): From ba76a4b8033a52729a7ab55129b9113ccca9a2e4 Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Fri, 10 Sep 2010 19:55:19 +0000 Subject: [PATCH 1968/2594] 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 bd61bc56f3..4e664642b8 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -748,6 +748,9 @@ def get_libraries(self, ext): 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'): From 8bede4b22db5241e8e141a6b4c2d718b02f4a57b Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Fri, 10 Sep 2010 20:03:17 +0000 Subject: [PATCH 1969/2594] 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 aeb6b744dc..5b3be6fbd6 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -753,7 +753,9 @@ def get_libraries (self, ext): 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'): 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: [PATCH 1970/2594] 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 818a45260d..1118060cbb 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -73,7 +73,7 @@ def checking_metadata(self): ('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]"), ] 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: [PATCH 1971/2594] 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 818a45260d..1118060cbb 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -73,7 +73,7 @@ def checking_metadata(self): ('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]"), ] 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: [PATCH 1972/2594] 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 f2d2f94beb..0c3b0b55bf 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -73,7 +73,7 @@ def checking_metadata(self): ('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', From d4b44c7d2deb42b35cb3c54d8f367f16fa37e1ab Mon Sep 17 00:00:00 2001 From: Hirokazu Yamamoto Date: Sun, 12 Sep 2010 22:55:40 +0000 Subject: [PATCH 1973/2594] 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 8a908d9954..39e2c11cc2 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -109,6 +109,11 @@ def test_reg_class(self): 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') From 50e74d1aefc924a81eb8a4ce26c7376b4c41c055 Mon Sep 17 00:00:00 2001 From: Hirokazu Yamamoto Date: Mon, 13 Sep 2010 05:36:21 +0000 Subject: [PATCH 1974/2594] 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 39e2c11cc2..ec2b2e367a 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -60,7 +60,12 @@ """ +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 @@ def test_no_compiler(self): # 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 @@ def _find_vcvarsall(version): 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 @@ def test_reg_class(self): 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') From 74e96af334b6541dc32dba42f6ba5c042111ddfd Mon Sep 17 00:00:00 2001 From: Hirokazu Yamamoto Date: Mon, 13 Sep 2010 05:48:30 +0000 Subject: [PATCH 1975/2594] 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 8a908d9954..ec2b2e367a 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -60,7 +60,12 @@ """ +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 @@ def test_no_compiler(self): # 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 @@ def _find_vcvarsall(version): 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') From 9b43905136212f7c0463ec8e844c02c9830df14e Mon Sep 17 00:00:00 2001 From: Hirokazu Yamamoto Date: Mon, 13 Sep 2010 06:36:09 +0000 Subject: [PATCH 1976/2594] 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 503a5a8056..cd162516cf 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -60,7 +60,12 @@ """ +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 @@ def test_no_compiler(self): # 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 @@ def _find_vcvarsall(version): 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') From c9bc9d98d8766b49eea3d8fbc084e9313094b94e Mon Sep 17 00:00:00 2001 From: Hirokazu Yamamoto Date: Mon, 13 Sep 2010 07:18:30 +0000 Subject: [PATCH 1977/2594] 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 ec2b2e367a..f1da843fab 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -62,10 +62,14 @@ 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): From 93127be78a63414c9ca002640c693192224221aa Mon Sep 17 00:00:00 2001 From: Hirokazu Yamamoto Date: Mon, 13 Sep 2010 07:48:22 +0000 Subject: [PATCH 1978/2594] 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 ec2b2e367a..f1da843fab 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -62,10 +62,14 @@ 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): From 3033d62a264a06b0d60459c769af0d5d2ffb4db9 Mon Sep 17 00:00:00 2001 From: Hirokazu Yamamoto Date: Mon, 13 Sep 2010 08:14:41 +0000 Subject: [PATCH 1979/2594] 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 cd162516cf..c957954482 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -62,10 +62,14 @@ 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): From fdc07fb43978baae1dbe06a43707d5c81a14d1db Mon Sep 17 00:00:00 2001 From: Senthil Kumaran Date: Fri, 17 Sep 2010 16:35:37 +0000 Subject: [PATCH 1980/2594] 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 98e6252c6c..54376e5ca5 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 0f694aa020..8986ca55d4 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 @@ def test_mkpath_remove_tree_verbosity(self): 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) From 1fbe22435a7a8e26625d33b3f1c5531fd8eb0090 Mon Sep 17 00:00:00 2001 From: Senthil Kumaran Date: Fri, 17 Sep 2010 16:40:01 +0000 Subject: [PATCH 1981/2594] 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 9b4d2adb5c..5a968063f7 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 0f694aa020..8986ca55d4 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 @@ def test_mkpath_remove_tree_verbosity(self): 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) From 8ff8c31883ff47ba9489fa9e265dce2e3dfc2361 Mon Sep 17 00:00:00 2001 From: Senthil Kumaran Date: Fri, 17 Sep 2010 16:42:05 +0000 Subject: [PATCH 1982/2594] 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 98e6252c6c..54376e5ca5 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 0f694aa020..8986ca55d4 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 @@ def test_mkpath_remove_tree_verbosity(self): 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) From e2489f924e1603823889e36f900b10e9211d903d Mon Sep 17 00:00:00 2001 From: Senthil Kumaran Date: Sat, 18 Sep 2010 02:55:03 +0000 Subject: [PATCH 1983/2594] 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 8986ca55d4..892a66d2ad 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -3,6 +3,7 @@ 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 @@ def test_mkpath_remove_tree_verbosity(self): 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) From 2e99ac7174e503aa7a717d339eff6bcbd620e7d2 Mon Sep 17 00:00:00 2001 From: Senthil Kumaran Date: Sat, 18 Sep 2010 02:57:28 +0000 Subject: [PATCH 1984/2594] 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 8986ca55d4..892a66d2ad 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -3,6 +3,7 @@ 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 @@ def test_mkpath_remove_tree_verbosity(self): 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) From eb473877ee82b71b02cee7c530d5f279670f8a4d Mon Sep 17 00:00:00 2001 From: Senthil Kumaran Date: Sat, 18 Sep 2010 02:58:49 +0000 Subject: [PATCH 1985/2594] 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 8986ca55d4..892a66d2ad 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -3,6 +3,7 @@ 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 @@ def test_mkpath_remove_tree_verbosity(self): 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) From ebefff189367909ff68453fd010eb61213827644 Mon Sep 17 00:00:00 2001 From: Senthil Kumaran Date: Sun, 19 Sep 2010 03:09:54 +0000 Subject: [PATCH 1986/2594] 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 892a66d2ad..a1647fbcf5 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -53,10 +53,15 @@ def test_mkpath_remove_tree_verbosity(self): @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): From d8b66d133304a36d1597de05eeaac5dfb60a8712 Mon Sep 17 00:00:00 2001 From: Senthil Kumaran Date: Sun, 19 Sep 2010 03:12:28 +0000 Subject: [PATCH 1987/2594] 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 892a66d2ad..a1647fbcf5 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -53,10 +53,15 @@ def test_mkpath_remove_tree_verbosity(self): @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): From b5a3263df80e4b7b52e97e0f9ac4d92993c7b5dc Mon Sep 17 00:00:00 2001 From: Senthil Kumaran Date: Sun, 19 Sep 2010 03:12:35 +0000 Subject: [PATCH 1988/2594] 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 892a66d2ad..a1647fbcf5 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -53,10 +53,15 @@ def test_mkpath_remove_tree_verbosity(self): @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): From 70b1270636c9a42b0f1f4458d37bbbbabac4bb32 Mon Sep 17 00:00:00 2001 From: Vinay Sajip Date: Mon, 20 Sep 2010 10:13:13 +0000 Subject: [PATCH 1989/2594] logging: added hasHandlers() to LoggerAdapter. --- sysconfig.py | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/sysconfig.py b/sysconfig.py index 48f3fe4d59..3567db8349 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: From 8eb84ccb87fb80d1e95382003af64a8c7d2f83de Mon Sep 17 00:00:00 2001 From: Vinay Sajip Date: Mon, 20 Sep 2010 10:29:54 +0000 Subject: [PATCH 1990/2594] 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 3567db8349..48f3fe4d59 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: 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: [PATCH 1991/2594] 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 12844cbf9f..2657c696e5 100644 --- a/command/check.py +++ b/command/check.py @@ -13,7 +13,7 @@ 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 c03ad10147..7d0ac9ee3a 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -224,24 +224,24 @@ def test_strict(self): 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 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: [PATCH 1992/2594] 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 12844cbf9f..2657c696e5 100644 --- a/command/check.py +++ b/command/check.py @@ -13,7 +13,7 @@ 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 c03ad10147..7d0ac9ee3a 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -224,24 +224,24 @@ def test_strict(self): 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 From aa258286d3d9a2651701148dca8781bd2a38adc1 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Wed, 6 Oct 2010 08:26:09 +0000 Subject: [PATCH 1993/2594] 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 c4be47b579..8a458d8536 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -148,7 +148,7 @@ def finalize_options(self): 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 d6d01c630d..3aa1dac7f3 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -89,7 +89,7 @@ def finalize_options(self): 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 From 107ea98e7050ff3c6ea4aaf1a3b7f73aef69bfbf Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Sun, 10 Oct 2010 09:37:12 +0000 Subject: [PATCH 1994/2594] 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 48f3fe4d59..8847e31743 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 From 8a775b192d5d53db9bf259d3526348be1462df34 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 10 Oct 2010 09:40:34 +0000 Subject: [PATCH 1995/2594] Bump to 3.2a3. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 92ed8f4db2..9df61ab029 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.2a2" +__version__ = "3.2a3" #--end constants-- From 04895087400890db00af176be74899e4b51a5a84 Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Sun, 10 Oct 2010 09:54:59 +0000 Subject: [PATCH 1996/2594] 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 4d16b2674c..9888cd52cb 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 From f88b5e54e08c0a816c2a0154254959a0056f2184 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Sat, 16 Oct 2010 01:04:07 +0000 Subject: [PATCH 1997/2594] 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 4e664642b8..cc0d414cf9 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -754,9 +754,9 @@ def get_libraries(self, ext): 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 52479d7d37..4351c0f8d5 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 @@ def setUp(self): 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 @@ def test_build_ext(self): 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 @@ def test_check_extensions_list(self): 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 @@ def test_get_outputs(self): 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) 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: [PATCH 1998/2594] 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 b7cdc20aa4..04ebc5b1ef 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -42,6 +42,20 @@ def setUp(self): 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 @@ def test_build_ext(self): 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 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: [PATCH 1999/2594] 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 beb6d96c8b..154771f2c7 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -49,6 +49,20 @@ def tearDown(self): 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 @@ def test_build_ext(self): 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 From 7404daed7bf476fd57a47aac41c64ed48cc78d45 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Thu, 21 Oct 2010 22:13:29 +0000 Subject: [PATCH 2000/2594] 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 154771f2c7..e04593065d 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -60,8 +60,12 @@ def _fixup_command(self, cmd): # 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 @@ def test_get_outputs(self): 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) 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: [PATCH 2001/2594] 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 4351c0f8d5..6858e5a4b2 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -52,8 +52,12 @@ def _fixup_command(self, cmd): # 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 From d8c874020da7deef2a07f22929ad75ce5cd74021 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Fri, 22 Oct 2010 15:31:44 +0000 Subject: [PATCH 2002/2594] 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 e04593065d..d14c5f6b10 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -59,7 +59,8 @@ def _fixup_command(self, cmd): # 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 = ['.'] From d6acf0666e6011bcf96c64e07c5f95574c1e1fbd Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Fri, 22 Oct 2010 17:17:51 +0000 Subject: [PATCH 2003/2594] 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 6858e5a4b2..18e0011743 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -51,7 +51,8 @@ def _fixup_command(self, cmd): # 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 = ['.'] From 1208ea17d6e97abffe76f3994d8575be5db3582f Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Fri, 22 Oct 2010 18:34:13 +0000 Subject: [PATCH 2004/2594] 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 04ebc5b1ef..226f7bb1fe 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -52,9 +52,14 @@ def _fixup_command(self, cmd): # 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 @@ def test_get_outputs(self): 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) From c4405aad904aac1948e1798945118759ef677af6 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Sat, 23 Oct 2010 17:02:31 +0000 Subject: [PATCH 2005/2594] 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 8847e31743..6b5daa5190 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 97459fbf73..454725c626 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 @@ 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', errors=self.errors) self.current_line = 0 def close(self): From 91b89f544e774b04a09af9d1849334f4f7dc20b5 Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Fri, 29 Oct 2010 22:36:08 +0000 Subject: [PATCH 2006/2594] 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 6b5daa5190..8b55f217cd 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"): 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: [PATCH 2007/2594] 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 34c77a37a3..291c008f20 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -779,14 +779,16 @@ def has_function(self, funcname, includes=None, include_dirs=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("""\ + 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 3aa1dac7f3..b2e2fc6dc8 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -340,4 +340,8 @@ def get_exe_bytes(self): 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 99e03d747c..4926aa3e15 100644 --- a/command/upload.py +++ b/command/upload.py @@ -76,7 +76,11 @@ def upload_file(self, command, pyversion, filename): # 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 6e4892039e..fd2a43d7d2 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 8504371810..95fa3ed3c8 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 1c1ea477db..01f1f1cfc0 100644 --- a/dist.py +++ b/dist.py @@ -1012,9 +1012,11 @@ def __init__ (self): 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 62a4c5b4e8..16dce53524 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 5c07bdae82..2d1c36bd5c 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 65aa7e0fdd..c36e7128d0 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 3e45f6e89e..8a69aaa0d6 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 b1d2d079bd..85b0400460 100644 --- a/tests/test_build_scripts.py +++ b/tests/test_build_scripts.py @@ -71,8 +71,10 @@ def write_sample_scripts(self, dir): 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 71c63678f8..6a45a328b6 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -105,8 +105,12 @@ def test_server_empty_registration(self): 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 b478fa6291..e937b45a6b 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -52,7 +52,11 @@ def cleanup_testfn(self): 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 a1647fbcf5..aa9f9ebb9f 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -88,8 +88,10 @@ def test_copy_tree_verbosity(self): 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 007803e12d..ee8e8d4dae 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -79,29 +79,29 @@ def test_command_packages_cmdline(self): 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 @@ def test_custom_pydistutils(self): 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 fac4a5d1a9..74618b523a 100644 --- a/tests/test_file_util.py +++ b/tests/test_file_util.py @@ -31,8 +31,10 @@ def tearDown(self): 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 76fa02acda..bc407cf9ab 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -182,8 +182,11 @@ def test_record(self): # 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 f1da843fab..40cb8be6d1 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -113,17 +113,21 @@ def test_remove_visual_c_ref(self): 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 7d0ac9ee3a..3b80b6dc05 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -118,8 +118,12 @@ def test_create_pypirc(self): 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 209aa59baf..ad527c7dd6 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -215,8 +215,12 @@ def test_add_defaults(self): 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 0167e0f1a3..215dc82df0 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -75,9 +75,11 @@ def set_executables(self, **kw): 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 @@ def test_parse_makefile_base(self): 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 00f083a130..3093097dba 100644 --- a/tests/test_text_file.py +++ b/tests/test_text_file.py @@ -58,28 +58,46 @@ def test_input(count, description, file, expected_result): 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 8175434586..3081245b62 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 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: [PATCH 2008/2594] 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 b7eb625ac5..08360d297b 100644 --- a/tests/test_install_scripts.py +++ b/tests/test_install_scripts.py @@ -42,8 +42,10 @@ def test_installation(self): 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" 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: [PATCH 2009/2594] 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 452f9502ad..e2ae877d9a 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -343,22 +343,26 @@ def run(self): 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 761b9ca236..6d7825df86 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"): 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: [PATCH 2010/2594] 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 7bdb912463..1b939cbd5d 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -15,9 +15,10 @@ 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 c6e08cbc2b..9e6264aa89 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -12,7 +12,7 @@ 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 f2849a9756..bf56e842b7 100644 --- a/tests/test_bdist.py +++ b/tests/test_bdist.py @@ -4,6 +4,7 @@ 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 5e76809f23..7d9d0aa834 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 2b2d8542ee..9308c79d91 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 2aa257f7e6..804fb1355f 100644 --- a/tests/test_bdist_rpm.py +++ b/tests/test_bdist_rpm.py @@ -5,6 +5,7 @@ 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 9b1ba6d107..f9e8f89e21 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 2418e1656d..9f0e0ad23c 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 536cd67319..e59b8f9a19 100644 --- a/tests/test_build_clib.py +++ b/tests/test_build_clib.py @@ -3,6 +3,8 @@ 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 18e0011743..46ac9aafc3 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -14,6 +14,7 @@ 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 8a69aaa0d6..da3232cea8 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -10,6 +10,7 @@ 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 85b0400460..e3326b8517 100644 --- a/tests/test_build_scripts.py +++ b/tests/test_build_scripts.py @@ -8,6 +8,7 @@ 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 372bae367b..6ad2fe3920 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 dbc4ee2a18..649855f7ab 100644 --- a/tests/test_clean.py +++ b/tests/test_clean.py @@ -6,6 +6,7 @@ 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 55ae421d46..969f82ba0f 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 @@ def test_ensure_filename(self): 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 6a45a328b6..05a35da903 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -10,6 +10,7 @@ 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 ef2e7bc317..c224a5c36d 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 e937b45a6b..47fae245e7 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -6,7 +6,7 @@ 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 a57694d48e..e57bab269a 100644 --- a/tests/test_cygwinccompiler.py +++ b/tests/test_cygwinccompiler.py @@ -4,6 +4,7 @@ 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 d81d9143b4..390ed9b602 100644 --- a/tests/test_dep_util.py +++ b/tests/test_dep_util.py @@ -6,6 +6,7 @@ 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 aa9f9ebb9f..7356c766e6 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -10,6 +10,7 @@ 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 ee8e8d4dae..a8eb8b1d5a 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -9,7 +9,7 @@ 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 1ee30585fa..c9c8965706 100755 --- a/tests/test_extension.py +++ b/tests/test_extension.py @@ -3,7 +3,7 @@ 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 74618b523a..be743f3436 100644 --- a/tests/test_file_util.py +++ b/tests/test_file_util.py @@ -6,6 +6,7 @@ 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 331180d235..6312a29485 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 bc407cf9ab..22e79b8909 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -6,7 +6,7 @@ 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 377ae86e2f..6b3b4c82ce 100644 --- a/tests/test_install_data.py +++ b/tests/test_install_data.py @@ -6,6 +6,7 @@ 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 5b32b13ee4..dc74c58d6c 100644 --- a/tests/test_install_headers.py +++ b/tests/test_install_headers.py @@ -6,6 +6,7 @@ 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 99a6d90627..790d4ce300 100644 --- a/tests/test_install_lib.py +++ b/tests/test_install_lib.py @@ -7,6 +7,7 @@ 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 08360d297b..8952e744e5 100644 --- a/tests/test_install_scripts.py +++ b/tests/test_install_scripts.py @@ -7,6 +7,7 @@ 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 d35de3456c..5f87076bd7 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 40cb8be6d1..45bae77a6c 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -5,6 +5,7 @@ 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 3b80b6dc05..9336ac8604 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -6,7 +6,7 @@ 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 ad527c7dd6..73962d34f2 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -8,11 +8,9 @@ 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 950e5789b5..5b91aa5a11 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 3093097dba..953cb8ea08 100644 --- a/tests/test_text_file.py +++ b/tests/test_text_file.py @@ -3,6 +3,7 @@ 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 3a41e6fcaa..1bff38e9ee 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 35e970051e..4661ed3212 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 0c732f8244..3b7159d72f 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -3,6 +3,7 @@ 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 fa21433046..ff40f6b45c 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 8a60dbe806..28ae09dc20 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()) 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: [PATCH 2011/2594] 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 34c77a37a3..291c008f20 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -779,14 +779,16 @@ def has_function(self, funcname, includes=None, include_dirs=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("""\ + 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 452f9502ad..e2ae877d9a 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -343,22 +343,26 @@ def run(self): 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 3aa1dac7f3..b2e2fc6dc8 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -340,4 +340,8 @@ def get_exe_bytes(self): 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 99e03d747c..4926aa3e15 100644 --- a/command/upload.py +++ b/command/upload.py @@ -76,7 +76,11 @@ def upload_file(self, command, pyversion, filename): # 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 6e4892039e..fd2a43d7d2 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 8504371810..95fa3ed3c8 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 1c1ea477db..01f1f1cfc0 100644 --- a/dist.py +++ b/dist.py @@ -1012,9 +1012,11 @@ def __init__ (self): 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 62a4c5b4e8..16dce53524 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 5c07bdae82..2d1c36bd5c 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 65aa7e0fdd..c36e7128d0 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 761b9ca236..6d7825df86 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 3e45f6e89e..8a69aaa0d6 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 b1d2d079bd..85b0400460 100644 --- a/tests/test_build_scripts.py +++ b/tests/test_build_scripts.py @@ -71,8 +71,10 @@ def write_sample_scripts(self, dir): 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 71c63678f8..6a45a328b6 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -105,8 +105,12 @@ def test_server_empty_registration(self): 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 b478fa6291..e937b45a6b 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -52,7 +52,11 @@ def cleanup_testfn(self): 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 a1647fbcf5..aa9f9ebb9f 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -88,8 +88,10 @@ def test_copy_tree_verbosity(self): 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 3b7637f3af..f9c5a4fd96 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -80,29 +80,29 @@ def test_command_packages_cmdline(self): 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 @@ def test_custom_pydistutils(self): 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 fac4a5d1a9..74618b523a 100644 --- a/tests/test_file_util.py +++ b/tests/test_file_util.py @@ -31,8 +31,10 @@ def tearDown(self): 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 76fa02acda..bc407cf9ab 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -182,8 +182,11 @@ def test_record(self): # 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 b7eb625ac5..08360d297b 100644 --- a/tests/test_install_scripts.py +++ b/tests/test_install_scripts.py @@ -42,8 +42,10 @@ def test_installation(self): 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 f1da843fab..40cb8be6d1 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -113,17 +113,21 @@ def test_remove_visual_c_ref(self): 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 7d0ac9ee3a..3b80b6dc05 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -118,8 +118,12 @@ def test_create_pypirc(self): 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 209aa59baf..ad527c7dd6 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -215,8 +215,12 @@ def test_add_defaults(self): 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 edc755ed15..78bce9461f 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -75,9 +75,11 @@ def set_executables(self, **kw): 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 @@ def test_parse_makefile_base(self): 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 00f083a130..3093097dba 100644 --- a/tests/test_text_file.py +++ b/tests/test_text_file.py @@ -58,28 +58,46 @@ def test_input(count, description, file, expected_result): 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 8175434586..3081245b62 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 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: [PATCH 2012/2594] 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 a34177e71f..c2b1f6fbe9 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -794,14 +794,16 @@ def has_function(self, funcname, includes=None, include_dirs=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("""\ + 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 6d9d47d2eb..0bba363557 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -355,22 +355,26 @@ def run (self): 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 a31a5f7bac..36d46bd627 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -356,5 +356,9 @@ def get_exe_bytes (self): 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 c3f19d207f..980cf68d06 100644 --- a/command/upload.py +++ b/command/upload.py @@ -79,7 +79,11 @@ def upload_file(self, command, pyversion, filename): # 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 99ccf44fad..b89557d767 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 2dabc0f0fe..a1ee815c6c 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 5dbdaef19c..597909ea1a 100644 --- a/dist.py +++ b/dist.py @@ -1101,9 +1101,11 @@ def _read_list(name): 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 f52e63232d..a0172058a3 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 440d128cdc..9a67ca8b3e 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 b3d9d54ec0..b9f0786133 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 d5d7f66528..55a4db1880 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 3c8bc41bae..03517392fb 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 72e8915c00..df89cdec25 100644 --- a/tests/test_build_scripts.py +++ b/tests/test_build_scripts.py @@ -71,8 +71,10 @@ def write_sample_scripts(self, dir): 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 0a1bb961ff..6c85efad24 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -108,8 +108,12 @@ def test_server_empty_registration(self): 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 48ec1b436e..63f6b31da6 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -52,7 +52,11 @@ def cleanup_testfn(self): 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 a1647fbcf5..aa9f9ebb9f 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -88,8 +88,10 @@ def test_copy_tree_verbosity(self): 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 1eddf6d25c..d9b413f954 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -102,29 +102,29 @@ def test_command_packages_cmdline(self): 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 @@ def test_custom_pydistutils(self): 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 99b421f73f..823f211089 100644 --- a/tests/test_file_util.py +++ b/tests/test_file_util.py @@ -31,8 +31,10 @@ def tearDown(self): 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 b7eb625ac5..08360d297b 100644 --- a/tests/test_install_scripts.py +++ b/tests/test_install_scripts.py @@ -42,8 +42,10 @@ def test_installation(self): 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 c957954482..567505ddf1 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -113,17 +113,21 @@ def test_remove_visual_c_ref(self): 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 370d659893..6da479b139 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -119,8 +119,12 @@ def test_create_pypirc(self): 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 87adb45894..7cebbf53ad 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -234,8 +234,12 @@ def test_add_defaults(self): 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 3066486066..8449ddca78 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -50,9 +50,11 @@ def test_get_python_inc(self): 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 @@ def test_parse_makefile_base(self): 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 00f083a130..3093097dba 100644 --- a/tests/test_text_file.py +++ b/tests/test_text_file.py @@ -58,28 +58,46 @@ def test_input(count, description, file, expected_result): 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 4dcfeb5505..f06e4fdf88 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 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: [PATCH 2013/2594] 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 54376e5ca5..c7c9fccfd7 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 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: [PATCH 2014/2594] 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 54376e5ca5..c7c9fccfd7 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 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: [PATCH 2015/2594] 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 5a968063f7..d6875936d6 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 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: [PATCH 2016/2594] 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 2a905d92f8..58f6ec5b16 100644 --- a/command/install.py +++ b/command/install.py @@ -60,14 +60,6 @@ '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 @@ '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 c36e7128d0..e1eb932926 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 8b55f217cd..be91f92c36 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 3081245b62..ce3cd6ca33 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) 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: [PATCH 2017/2594] 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 9888cd52cb..33cc5a38a5 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 = {} 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: [PATCH 2018/2594] 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 95fa3ed3c8..5676e91a79 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 6d7825df86..488524db91 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()))) 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: [PATCH 2019/2594] 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 5676e91a79..95fa3ed3c8 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 From 6f3d8cbb6fec170ba7c4e07c6c67d4a9bb57d625 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 9 Nov 2010 09:32:19 +0000 Subject: [PATCH 2020/2594] 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 46ac9aafc3..203cbc98fe 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -318,7 +318,7 @@ def test_compiler_option(self): 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]}) 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: [PATCH 2021/2594] 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 6d7825df86..488524db91 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()))) 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: [PATCH 2022/2594] 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 55a4db1880..615d8b83a0 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()))) 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: [PATCH 2023/2594] 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 488524db91..e849e16d51 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() 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: [PATCH 2024/2594] 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 488524db91..e849e16d51 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() 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: [PATCH 2025/2594] 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 615d8b83a0..bf85ac75c7 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() From bbcb611025969e9a4b8a987a7ce1528776740ae2 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 13 Nov 2010 06:39:58 +0000 Subject: [PATCH 2026/2594] Bump to 3.2a4. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 9df61ab029..bae33b5530 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.2a3" +__version__ = "3.2a4" #--end constants-- From 65e43d3b792289e3c4025cf203f1f3f1a2949ca4 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 13 Nov 2010 17:28:56 +0000 Subject: [PATCH 2027/2594] bump to 3.1.3rc1 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 6dca599fd6..9684a11c45 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.1.2" +__version__ = "3.1.3rc1" #--end constants-- From 0f51e1c350bb195a7add16991c466fe9d59b9ced Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 13 Nov 2010 17:33:04 +0000 Subject: [PATCH 2028/2594] 2.7.1rc1 bump --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 2c69a1836a..f9f9792794 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "2.7.1a0" +__version__ = "2.7.1rc1" #--end constants-- From df79f96e47490dedbcb676f77c426fad14df1c6f Mon Sep 17 00:00:00 2001 From: Ezio Melotti Date: Sat, 20 Nov 2010 19:04:17 +0000 Subject: [PATCH 2029/2594] #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 9e6264aa89..aff426521d 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -113,7 +113,7 @@ def test_tarfile_vs_tar(self): 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 @@ def test_compress_deprecated(self): 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 @@ def test_compress_deprecated(self): 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 @@ def test_make_zipfile(self): 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 @@ def _breaks(*args, **kw): 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 bf56e842b7..94d40cc25b 100644 --- a/tests/test_bdist.py +++ b/tests/test_bdist.py @@ -24,7 +24,7 @@ def test_formats(self): 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 @@ def test_formats(self): 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 7d9d0aa834..cc37fef8b0 100644 --- a/tests/test_bdist_dumb.py +++ b/tests/test_bdist_dumb.py @@ -69,7 +69,7 @@ def test_simple_built(self): 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 9f0e0ad23c..3391f36d4b 100644 --- a/tests/test_build.py +++ b/tests/test_build.py @@ -18,11 +18,11 @@ def test_finalize_options(self): 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 @@ def test_finalize_options(self): 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 e59b8f9a19..69bd2bf624 100644 --- a/tests/test_build_clib.py +++ b/tests/test_build_clib.py @@ -57,14 +57,14 @@ def test_get_source_files(self): 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 @@ def test_finalize_options(self): 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 203cbc98fe..dcba75f703 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -96,11 +96,11 @@ def test_build_ext(self): 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 @@ def test_finalize_options(self): 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 @@ def test_finalize_options(self): 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 @@ def test_finalize_options(self): 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 @@ def test_check_extensions_list(self): # 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 @@ def test_check_extensions_list(self): 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 @@ def test_compiler_option(self): 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 @@ def test_get_outputs(self): 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 @@ def test_get_outputs(self): 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 @@ def test_get_outputs(self): 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 @@ def test_get_outputs(self): 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 @@ def test_get_outputs(self): # 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 @@ def test_ext_fullpath(self): 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 @@ def test_ext_fullpath(self): 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 6ad2fe3920..229ae25c6d 100644 --- a/tests/test_check.py +++ b/tests/test_check.py @@ -27,7 +27,7 @@ def test_check_metadata(self): # 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 @@ def test_check_metadata(self): '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 @@ def test_check_metadata(self): # 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 @@ def test_check_document(self): # 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 @@ def test_check_restructuredtext(self): 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 @@ def test_check_restructuredtext(self): # 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 969f82ba0f..195045cc7b 100644 --- a/tests/test_cmd.py +++ b/tests/test_cmd.py @@ -44,7 +44,7 @@ def test_make_file(self): # 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 @@ def _announce(msg, level): 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 @@ def test_ensure_string_list(self): 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 @@ def test_debug_print(self): 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 05a35da903..525bee9416 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -89,7 +89,7 @@ def test_server_registration(self): 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 @@ def test_server_registration(self): 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 @@ def test_server_empty_registration(self): 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 c224a5c36d..4f7ebdd9fc 100644 --- a/tests/test_config_cmd.py +++ b/tests/test_config_cmd.py @@ -35,7 +35,7 @@ def test_dump_file(self): 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 @@ def test_search_cpp(self): # 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 @@ def test_finalize_options(self): 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 47fae245e7..41321f7db4 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -89,7 +89,7 @@ def test_debug_mode(self): 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 @@ def test_debug_mode(self): 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 e57bab269a..856921679d 100644 --- a/tests/test_cygwinccompiler.py +++ b/tests/test_cygwinccompiler.py @@ -66,82 +66,82 @@ def test_check_config_h(self): 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 390ed9b602..3e1c366892 100644 --- a/tests/test_dep_util.py +++ b/tests/test_dep_util.py @@ -43,8 +43,8 @@ def test_newer_pairwise(self): 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 7356c766e6..ce74589dde 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -38,18 +38,18 @@ def test_mkpath_remove_tree_verbosity(self): 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 @@ def test_mkpath_with_custom_mode(self): 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 @@ def test_copy_tree_verbosity(self): 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 @@ def test_copy_tree_verbosity(self): 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 a8eb8b1d5a..a20d6c8ffc 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -124,7 +124,7 @@ def _warn(msg): finally: warnings.warn = old_warn - self.assertEquals(len(warns), 0) + self.assertEqual(len(warns), 0) def test_finalize_options(self): @@ -135,20 +135,20 @@ def test_finalize_options(self): 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 @@ def test_custom_pydistutils(self): 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 c9c8965706..e35f2738b6 100755 --- a/tests/test_extension.py +++ b/tests/test_extension.py @@ -28,38 +28,38 @@ def test_read_setup_file(self): '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 be743f3436..3c3e3dcb3b 100644 --- a/tests/test_file_util.py +++ b/tests/test_file_util.py @@ -39,14 +39,14 @@ def test_move_file_verbosity(self): 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 @@ def test_move_file_verbosity(self): 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 6312a29485..c7e5201b17 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 22e79b8909..a76e3e7a3c 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -123,23 +123,23 @@ def test_handle_extra_path(self): # 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 @@ def test_record(self): # 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 6b3b4c82ce..4d8c00acb9 100644 --- a/tests/test_install_data.py +++ b/tests/test_install_data.py @@ -28,14 +28,14 @@ def test_simple_run(self): 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 @@ def test_simple_run(self): 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 @@ def test_simple_run(self): 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 dc74c58d6c..d953157bb7 100644 --- a/tests/test_install_headers.py +++ b/tests/test_install_headers.py @@ -24,7 +24,7 @@ def test_simple_run(self): 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 @@ def test_simple_run(self): 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 790d4ce300..fddaabe111 100644 --- a/tests/test_install_lib.py +++ b/tests/test_install_lib.py @@ -19,8 +19,8 @@ def test_finalize_options(self): 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 @@ def test_finalize_options(self): 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 @@ def test_get_inputs(self): 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 5f87076bd7..ce66ee51e7 100644 --- a/tests/test_log.py +++ b/tests/test_log.py @@ -23,9 +23,9 @@ def test_non_ascii(self): 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 45bae77a6c..a0d62efcfd 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -104,7 +104,7 @@ def test_reg_class(self): 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 @@ def test_remove_visual_c_ref(self): 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 9336ac8604..c712f56a78 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -121,7 +121,7 @@ def test_create_pypirc(self): 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 @@ def _no_way(prompt=''): 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 @@ def test_password_not_in_file(self): # 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 @@ def test_registering(self): 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 @@ def test_password_reset(self): 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 @@ def test_check_metadata_deprecated(self): 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 73962d34f2..655e50dcfc 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -108,7 +108,7 @@ def test_prune_file_list(self): # 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 @@ def test_prune_file_list(self): 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 @@ def test_make_distribution(self): 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 @@ def test_make_distribution(self): 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 @@ def test_add_defaults(self): # 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 @@ def test_add_defaults(self): 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 @@ def test_metadata_check_option(self): 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 @@ def test_metadata_check_option(self): 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 @@ def test_check_metadata_deprecated(self): 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 @@ def test_show_formats(self): 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 @@ def test_finalize_options(self): 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 @@ def test_get_file_list(self): 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 @@ def test_get_file_list(self): 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 5b91aa5a11..6c7eb20c47 100644 --- a/tests/test_spawn.py +++ b/tests/test_spawn.py @@ -20,7 +20,7 @@ def test_nt_quote_args(self): (['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 215dc82df0..336c6a5b95 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -70,7 +70,7 @@ def set_executables(self, **kw): 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 @@ def test_parse_makefile_base(self): 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 @@ def test_parse_makefile_literal_dollar(self): 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 953cb8ea08..7e76240a9a 100644 --- a/tests/test_text_file.py +++ b/tests/test_text_file.py @@ -49,7 +49,7 @@ def test_class(self): 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 4661ed3212..4c6464a32e 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -89,7 +89,7 @@ def test_finalize_options(self): 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 @@ def test_saved_password(self): 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 @@ def test_upload(self): # 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 3b7159d72f..8ff5ae2085 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -67,21 +67,21 @@ def test_get_platform(self): 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 @@ def test_get_platform(self): 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 @@ def test_get_platform(self): '-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 @@ def test_get_platform(self): '-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 @@ def test_get_platform(self): 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 @@ def _join(path): 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 @@ def _join(*path): 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 @@ 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') + 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 @@ 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') + 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 @@ def test_check_environ(self): 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 @@ def test_rfc822_escape(self): 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 ff40f6b45c..15f14c7de3 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 @@ def test_cmp_strict(self): 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 @@ def test_cmp(self): 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) 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: [PATCH 2030/2594] 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 c7c9fccfd7..5b005f0865 100644 --- a/dir_util.py +++ b/dir_util.py @@ -5,6 +5,7 @@ __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 336c6a5b95..fbe26bf65d 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 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: [PATCH 2031/2594] 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 c7c9fccfd7..5b005f0865 100644 --- a/dir_util.py +++ b/dir_util.py @@ -5,6 +5,7 @@ __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 78bce9461f..309be7b8b1 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 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: [PATCH 2032/2594] 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 d6875936d6..9c5cf337c6 100644 --- a/dir_util.py +++ b/dir_util.py @@ -5,6 +5,7 @@ __revision__ = "$Id$" import os +import errno from distutils.errors import DistutilsFileError, DistutilsInternalError from distutils import log From dcc7727442e3d4796d7d2bb8db40c8e04f42703a Mon Sep 17 00:00:00 2001 From: Ezio Melotti Date: Sun, 21 Nov 2010 01:30:29 +0000 Subject: [PATCH 2033/2594] 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 c6e08cbc2b..d77302d547 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -113,7 +113,7 @@ def test_tarfile_vs_tar(self): 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 @@ def test_compress_deprecated(self): 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 @@ def test_compress_deprecated(self): 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 @@ def test_make_zipfile(self): 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 @@ def _breaks(*args, **kw): 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 f2849a9756..715283cb97 100644 --- a/tests/test_bdist.py +++ b/tests/test_bdist.py @@ -23,7 +23,7 @@ def test_formats(self): 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 @@ def test_formats(self): 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 5e76809f23..bf146eb573 100644 --- a/tests/test_bdist_dumb.py +++ b/tests/test_bdist_dumb.py @@ -68,7 +68,7 @@ def test_simple_built(self): 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 536cd67319..d6d1a4d0c1 100644 --- a/tests/test_build_clib.py +++ b/tests/test_build_clib.py @@ -55,14 +55,14 @@ def test_get_source_files(self): 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 @@ def test_finalize_options(self): 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 226f7bb1fe..e8b15f156d 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -96,11 +96,11 @@ def test_build_ext(self): 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 @@ def test_finalize_options(self): 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 @@ def test_finalize_options(self): 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 @@ def test_finalize_options(self): 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 @@ def test_check_extensions_list(self): # 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 @@ def test_check_extensions_list(self): 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 @@ def test_compiler_option(self): 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 @@ def test_get_outputs(self): 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 @@ def test_get_outputs(self): 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 @@ def test_get_outputs(self): 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 @@ def test_get_outputs(self): # 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 @@ def test_ext_fullpath(self): 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 @@ def test_ext_fullpath(self): 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 372bae367b..7c56c04339 100644 --- a/tests/test_check.py +++ b/tests/test_check.py @@ -26,7 +26,7 @@ def test_check_metadata(self): # 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 @@ def test_check_metadata(self): '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 @@ def test_check_metadata(self): # 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 @@ def test_check_document(self): # 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 @@ def test_check_restructuredtext(self): 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 @@ def test_check_restructuredtext(self): # 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 55ae421d46..5821fcd286 100644 --- a/tests/test_cmd.py +++ b/tests/test_cmd.py @@ -44,7 +44,7 @@ def test_make_file(self): # 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 @@ def _announce(msg, level): 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 @@ def test_ensure_string_list(self): 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 @@ def test_debug_print(self): 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 6a45a328b6..2c075d7ee7 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -88,7 +88,7 @@ def test_server_registration(self): 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 @@ def test_server_registration(self): 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 @@ def test_server_empty_registration(self): 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 ef2e7bc317..fcb798e73b 100644 --- a/tests/test_config_cmd.py +++ b/tests/test_config_cmd.py @@ -34,7 +34,7 @@ def test_dump_file(self): 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 @@ def test_search_cpp(self): # 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 @@ def test_finalize_options(self): 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 e937b45a6b..d781555da5 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -89,7 +89,7 @@ def test_debug_mode(self): 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 @@ def test_debug_mode(self): 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 a57694d48e..79377a7763 100644 --- a/tests/test_cygwinccompiler.py +++ b/tests/test_cygwinccompiler.py @@ -65,82 +65,82 @@ def test_check_config_h(self): 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 aa9f9ebb9f..84a0ec6cfc 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -37,18 +37,18 @@ def test_mkpath_remove_tree_verbosity(self): 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 @@ def test_mkpath_with_custom_mode(self): 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 @@ def test_copy_tree_verbosity(self): 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 @@ def test_copy_tree_verbosity(self): 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 f9c5a4fd96..2c19d89266 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -125,7 +125,7 @@ def _warn(msg): finally: warnings.warn = old_warn - self.assertEquals(len(warns), 0) + self.assertEqual(len(warns), 0) def test_finalize_options(self): @@ -136,20 +136,20 @@ def test_finalize_options(self): 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 @@ def test_custom_pydistutils(self): 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 1ee30585fa..d9a47a89c8 100755 --- a/tests/test_extension.py +++ b/tests/test_extension.py @@ -28,38 +28,38 @@ def test_read_setup_file(self): '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 74618b523a..730ffde4ff 100644 --- a/tests/test_file_util.py +++ b/tests/test_file_util.py @@ -38,14 +38,14 @@ def test_move_file_verbosity(self): 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 @@ def test_move_file_verbosity(self): 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 331180d235..d2a2b449ee 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 bc407cf9ab..f9c142e446 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -123,23 +123,23 @@ def test_handle_extra_path(self): # 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 @@ def test_record(self): # 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 377ae86e2f..86db4a12c5 100644 --- a/tests/test_install_data.py +++ b/tests/test_install_data.py @@ -27,14 +27,14 @@ def test_simple_run(self): 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 @@ def test_simple_run(self): 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 @@ def test_simple_run(self): 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 5b32b13ee4..aa8a4e6014 100644 --- a/tests/test_install_headers.py +++ b/tests/test_install_headers.py @@ -23,7 +23,7 @@ def test_simple_run(self): 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 @@ def test_simple_run(self): 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 99a6d90627..4636304692 100644 --- a/tests/test_install_lib.py +++ b/tests/test_install_lib.py @@ -18,8 +18,8 @@ def test_finalize_options(self): 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 @@ def test_finalize_options(self): 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 @@ def test_get_inputs(self): 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 d35de3456c..9d40dcd72c 100644 --- a/tests/test_log.py +++ b/tests/test_log.py @@ -22,9 +22,9 @@ def test_non_ascii(self): 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 40cb8be6d1..570fda121e 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -103,7 +103,7 @@ def test_reg_class(self): 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 @@ def test_remove_visual_c_ref(self): 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 3b80b6dc05..c98e28a1a4 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -121,7 +121,7 @@ def test_create_pypirc(self): 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 @@ def _no_way(prompt=''): 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 @@ def test_password_not_in_file(self): # 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 @@ def test_registering(self): 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 @@ def test_password_reset(self): 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 @@ def test_check_metadata_deprecated(self): 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 ad527c7dd6..d734ea0ac5 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -110,7 +110,7 @@ def test_prune_file_list(self): # 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 @@ def test_prune_file_list(self): 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 @@ def test_make_distribution(self): 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 @@ def test_make_distribution(self): 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 @@ def test_add_defaults(self): # 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 @@ def test_add_defaults(self): 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 @@ def test_metadata_check_option(self): 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 @@ def test_metadata_check_option(self): 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 @@ def test_check_metadata_deprecated(self): 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 @@ def test_show_formats(self): 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 @@ def test_finalize_options(self): 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 @@ def test_get_file_list(self): 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 @@ def test_get_file_list(self): 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 950e5789b5..0616c9f2e3 100644 --- a/tests/test_spawn.py +++ b/tests/test_spawn.py @@ -20,7 +20,7 @@ def test_nt_quote_args(self): (['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 309be7b8b1..41414bb52e 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -71,7 +71,7 @@ def set_executables(self, **kw): 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 @@ def test_parse_makefile_base(self): 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 @@ def test_parse_makefile_literal_dollar(self): 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 3093097dba..f1e32b6cc6 100644 --- a/tests/test_text_file.py +++ b/tests/test_text_file.py @@ -48,7 +48,7 @@ def test_class(self): 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 35e970051e..8891820d67 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -90,7 +90,7 @@ def test_finalize_options(self): 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 @@ def test_saved_password(self): 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 @@ def test_upload(self): # 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 0c732f8244..f40caac838 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -66,21 +66,21 @@ def test_get_platform(self): 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 @@ def test_get_platform(self): 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 @@ def test_get_platform(self): '-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 @@ def test_get_platform(self): '-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 @@ def test_get_platform(self): 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 @@ def _join(path): 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 @@ def _join(*path): 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 @@ 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') + 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 @@ 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') + 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 @@ def test_check_environ(self): 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 @@ def test_rfc822_escape(self): 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 fa21433046..980f83f29a 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 @@ def test_cmp_strict(self): 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 @@ def test_cmp(self): 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) From d82693d941869773f3654c04da63d586c5ad1a2c Mon Sep 17 00:00:00 2001 From: Ezio Melotti Date: Sun, 21 Nov 2010 13:34:58 +0000 Subject: [PATCH 2034/2594] 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 a9b46d8e22..bab91577f2 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -129,7 +129,7 @@ def test_tarfile_vs_tar(self): 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 @@ def test_compress_deprecated(self): 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 @@ def test_compress_deprecated(self): 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 @@ def test_make_zipfile(self): 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 @@ def test_tarfile_root_owner(self): 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 @@ def _breaks(*args, **kw): 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 a37f4a9b09..fa7cd5adb5 100644 --- a/tests/test_bdist.py +++ b/tests/test_bdist.py @@ -25,7 +25,7 @@ def test_formats(self): 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 @@ def test_formats(self): 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 f2220f474d..5a22a10ec8 100644 --- a/tests/test_bdist_dumb.py +++ b/tests/test_bdist_dumb.py @@ -78,7 +78,7 @@ def test_simple_built(self): 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 @@ 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) + 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 2418e1656d..3db570382e 100644 --- a/tests/test_build.py +++ b/tests/test_build.py @@ -17,11 +17,11 @@ def test_finalize_options(self): 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 @@ def test_finalize_options(self): 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 145eff5453..d77912ae08 100644 --- a/tests/test_build_clib.py +++ b/tests/test_build_clib.py @@ -55,14 +55,14 @@ def test_get_source_files(self): 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 @@ def test_finalize_options(self): 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 d14c5f6b10..86568ebb7a 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -103,15 +103,15 @@ def test_build_ext(self): 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 @@ def test_solaris_enable_shared(self): _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 @@ def test_finalize_options(self): 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 @@ def test_finalize_options(self): 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 @@ def test_check_extensions_list(self): '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 @@ def test_check_extensions_list(self): 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 @@ def test_compiler_option(self): 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 @@ def test_get_outputs(self): 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 @@ def test_get_outputs(self): 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 @@ def test_get_outputs(self): 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 @@ def test_get_outputs(self): # 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 @@ def test_ext_fullpath(self): 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 @@ def test_ext_fullpath(self): 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 @@ def test_build_ext_inplace(self): 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 @@ def test_setuptools_compat(self): 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 @@ def test_build_ext_path_with_os_sep(self): 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 @@ def test_build_ext_path_cross_platform(self): 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 317e77fded..e21873e821 100644 --- a/tests/test_ccompiler.py +++ b/tests/test_ccompiler.py @@ -32,7 +32,7 @@ def test_gen_lib_options(self): 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 MyCCompiler(CCompiler): 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 @@ def set_executables(self, **kw): 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 372bae367b..7c56c04339 100644 --- a/tests/test_check.py +++ b/tests/test_check.py @@ -26,7 +26,7 @@ def test_check_metadata(self): # 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 @@ def test_check_metadata(self): '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 @@ def test_check_metadata(self): # 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 @@ def test_check_document(self): # 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 @@ def test_check_restructuredtext(self): 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 @@ def test_check_restructuredtext(self): # 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 cfd6485113..97cdb8ad69 100644 --- a/tests/test_cmd.py +++ b/tests/test_cmd.py @@ -44,7 +44,7 @@ def test_make_file(self): # 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 @@ def _announce(msg, level): 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 @@ def test_ensure_string_list(self): 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 @@ def test_debug_print(self): 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 6c85efad24..8d71234d6e 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -90,7 +90,7 @@ def test_server_registration(self): 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 @@ def test_server_registration(self): 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 @@ def test_server_empty_registration(self): 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 ef2e7bc317..fcb798e73b 100644 --- a/tests/test_config_cmd.py +++ b/tests/test_config_cmd.py @@ -34,7 +34,7 @@ def test_dump_file(self): 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 @@ def test_search_cpp(self): # 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 @@ def test_finalize_options(self): 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 63f6b31da6..74d1f3b269 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -89,7 +89,7 @@ def test_debug_mode(self): 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 @@ def test_debug_mode(self): 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 d81d9143b4..3550819681 100644 --- a/tests/test_dep_util.py +++ b/tests/test_dep_util.py @@ -42,8 +42,8 @@ def test_newer_pairwise(self): 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 aa9f9ebb9f..84a0ec6cfc 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -37,18 +37,18 @@ def test_mkpath_remove_tree_verbosity(self): 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 @@ def test_mkpath_with_custom_mode(self): 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 @@ def test_copy_tree_verbosity(self): 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 @@ def test_copy_tree_verbosity(self): 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 d9b413f954..8ab7e9fc31 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -70,13 +70,13 @@ def test_debug_mode(self): 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 @@ def _warn(msg): finally: warnings.warn = old_warn - self.assertEquals(len(warns), 0) + self.assertEqual(len(warns), 0) def test_finalize_options(self): @@ -186,20 +186,20 @@ def test_finalize_options(self): 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 @@ def _expander(path): 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 @@ def test_custom_pydistutils(self): 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 @@ def test_read_metadata(self): 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 823f211089..dbc6283981 100644 --- a/tests/test_file_util.py +++ b/tests/test_file_util.py @@ -38,14 +38,14 @@ def test_move_file_verbosity(self): 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 @@ def test_move_file_verbosity(self): 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 @@ def test_write_file(self): 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 0cbb48bcc6..32c56c75bc 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 @@ def test_process_template_line(self): 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 377ae86e2f..86db4a12c5 100644 --- a/tests/test_install_data.py +++ b/tests/test_install_data.py @@ -27,14 +27,14 @@ def test_simple_run(self): 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 @@ def test_simple_run(self): 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 @@ def test_simple_run(self): 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 5b32b13ee4..aa8a4e6014 100644 --- a/tests/test_install_headers.py +++ b/tests/test_install_headers.py @@ -23,7 +23,7 @@ def test_simple_run(self): 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 @@ def test_simple_run(self): 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 13d27abac0..754ea636a5 100644 --- a/tests/test_install_lib.py +++ b/tests/test_install_lib.py @@ -18,8 +18,8 @@ def test_finalize_options(self): 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 @@ def test_finalize_options(self): 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 @@ def test_get_inputs(self): 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 567505ddf1..b8e2209482 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -103,7 +103,7 @@ def test_reg_class(self): 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 @@ def test_remove_visual_c_ref(self): 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 6da479b139..915427b7d9 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -122,7 +122,7 @@ def test_create_pypirc(self): 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 @@ def _no_way(prompt=''): 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 @@ def test_password_not_in_file(self): # 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 @@ def test_registering(self): 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 @@ def test_password_reset(self): 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 @@ def test_check_metadata_deprecated(self): 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 7cebbf53ad..b9d86bb02e 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -127,7 +127,7 @@ def test_prune_file_list(self): # 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 @@ def test_prune_file_list(self): 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 @@ def test_make_distribution(self): 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 @@ def test_make_distribution(self): 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 @@ def test_add_defaults(self): # 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 @@ def test_add_defaults(self): 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 @@ def test_metadata_check_option(self): 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 @@ def test_metadata_check_option(self): 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 @@ def test_check_metadata_deprecated(self): 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 @@ def test_show_formats(self): 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 @@ def test_finalize_options(self): 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 @@ def test_make_distribution_owner_group(self): 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 @@ def test_make_distribution_owner_group(self): # 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 @@ def test_get_file_list(self): 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 @@ def test_get_file_list(self): 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 b9fd610e2f..6caf039031 100644 --- a/tests/test_spawn.py +++ b/tests/test_spawn.py @@ -20,7 +20,7 @@ def test_nt_quote_args(self): (['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 8449ddca78..49570c4ce5 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -36,7 +36,7 @@ def test_get_python_lib(self): 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 @@ def test_parse_makefile_base(self): 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 @@ def test_parse_makefile_literal_dollar(self): 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 3093097dba..f1e32b6cc6 100644 --- a/tests/test_text_file.py +++ b/tests/test_text_file.py @@ -48,7 +48,7 @@ def test_class(self): 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 3ae89498a1..f45ee41e8c 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -80,7 +80,7 @@ def test_finalize_options(self): 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 @@ def test_saved_password(self): 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 @@ def test_upload(self): # 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 747db94804..1d9fbc7985 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 @@ def test_cmp_strict(self): 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 @@ def test_cmp(self): 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) From bc76c36d325cd64296b2d7a7420479c87e51be22 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Wed, 24 Nov 2010 19:43:47 +0000 Subject: [PATCH 2035/2594] 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 58f6ec5b16..7f9d00f2dd 100644 --- a/command/install.py +++ b/command/install.py @@ -48,7 +48,7 @@ '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 @@ 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 @@ def finalize_options(self): '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 be91f92c36..897b7d63d6 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -11,7 +11,6 @@ __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): From 47c1750f35348e99498ca6e8fcec38b67efe95d9 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Thu, 25 Nov 2010 03:46:44 +0000 Subject: [PATCH 2036/2594] 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 7f9d00f2dd..bdc3a09b22 100644 --- a/command/install.py +++ b/command/install.py @@ -313,6 +313,11 @@ def finalize_options(self): 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 @@ def finalize_options(self): 'prefix': prefix, 'sys_exec_prefix': exec_prefix, 'exec_prefix': exec_prefix, - 'abiflags': sys.abiflags, + 'abiflags': abiflags, } if HAS_USER_SITE: From 22eaecf1e9401c320a4da6e387798e094de0679b Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 27 Nov 2010 14:40:29 +0000 Subject: [PATCH 2037/2594] 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 f9f9792794..a82d4cb589 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "2.7.1rc1" +__version__ = "2.7.1" #--end constants-- From 8e91a860e081fec22be2322c09613ebac1a1968c Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 27 Nov 2010 14:46:13 +0000 Subject: [PATCH 2038/2594] 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 9684a11c45..6f55f016a2 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.1.3rc1" +__version__ = "3.1.3" #--end constants-- From a708da6b241d23acd2ae934de3a42d36a2bcaefb Mon Sep 17 00:00:00 2001 From: "Phillip J. Eby" Date: Fri, 3 Dec 2010 17:00:40 +0000 Subject: [PATCH 2039/2594] 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 980cf68d06..d0133353a6 100644 --- a/command/upload.py +++ b/command/upload.py @@ -176,6 +176,9 @@ def upload_file(self, command, pyversion, filename): 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 @@ def upload_file(self, command, pyversion, filename): 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) From 13652894fa830ff6d9eff472a956034f09f0922e Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 4 Dec 2010 19:09:24 +0000 Subject: [PATCH 2040/2594] Bump to 3.2b1. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index bae33b5530..8b85f35f9d 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.2a4" +__version__ = "3.2b1" #--end constants-- 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: [PATCH 2041/2594] 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 16164c7f1f..6dd0445dbe 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 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: [PATCH 2042/2594] 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 16164c7f1f..6dd0445dbe 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 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: [PATCH 2043/2594] 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 c741cc0174..834b722ed3 100644 --- a/archive_util.py +++ b/archive_util.py @@ -121,7 +121,7 @@ def _set_uid_gid(tarinfo): 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 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: [PATCH 2044/2594] 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 cc0d414cf9..fb31648951 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -214,7 +214,7 @@ def finalize_options(self): 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')) 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: [PATCH 2045/2594] 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 4e664642b8..502b39a82f 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -214,7 +214,7 @@ def finalize_options(self): 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')) 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: [PATCH 2046/2594] 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 5b3be6fbd6..55a4288ac0 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -207,7 +207,7 @@ def finalize_options(self): 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')) From 6451e39efcacf15bdbe420fd9a21e46dac0a0b2e Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 19 Dec 2010 10:30:28 +0000 Subject: [PATCH 2047/2594] Bump to 3.2b2. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 8b85f35f9d..1d752e5e03 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.2b1" +__version__ = "3.2b2" #--end constants-- From c0b2f9e8190d95578e94c2a550144a91aa9c5e18 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 15 Jan 2011 17:08:53 +0000 Subject: [PATCH 2048/2594] Bump to 3.2rc1. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 1d752e5e03..40ca9b2a3a 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.2b2" +__version__ = "3.2rc1" #--end constants-- From 663bdd57f912467e076d8e7f0be95a4755c99e4f Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 30 Jan 2011 14:03:33 +0000 Subject: [PATCH 2049/2594] Bump version. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 40ca9b2a3a..4bb5012318 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.2rc1" +__version__ = "3.2rc2" #--end constants-- 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: [PATCH 2050/2594] 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 7bdb912463..1b939cbd5d 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -15,9 +15,10 @@ 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 d77302d547..aff426521d 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -12,7 +12,7 @@ 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 715283cb97..94d40cc25b 100644 --- a/tests/test_bdist.py +++ b/tests/test_bdist.py @@ -4,6 +4,7 @@ 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 bf146eb573..cc37fef8b0 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 2aa257f7e6..804fb1355f 100644 --- a/tests/test_bdist_rpm.py +++ b/tests/test_bdist_rpm.py @@ -5,6 +5,7 @@ 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 9b1ba6d107..f9e8f89e21 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 d6d1a4d0c1..69bd2bf624 100644 --- a/tests/test_build_clib.py +++ b/tests/test_build_clib.py @@ -3,6 +3,8 @@ 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 e8b15f156d..11844d66d3 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -15,6 +15,7 @@ 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 8a69aaa0d6..da3232cea8 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -10,6 +10,7 @@ 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 85b0400460..e3326b8517 100644 --- a/tests/test_build_scripts.py +++ b/tests/test_build_scripts.py @@ -8,6 +8,7 @@ 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 7c56c04339..229ae25c6d 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 dbc4ee2a18..649855f7ab 100755 --- a/tests/test_clean.py +++ b/tests/test_clean.py @@ -6,6 +6,7 @@ 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 5821fcd286..195045cc7b 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 @@ def test_ensure_filename(self): 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 2c075d7ee7..525bee9416 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -10,6 +10,7 @@ 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 fcb798e73b..4f7ebdd9fc 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 d781555da5..41321f7db4 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -6,7 +6,7 @@ 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 79377a7763..856921679d 100644 --- a/tests/test_cygwinccompiler.py +++ b/tests/test_cygwinccompiler.py @@ -4,6 +4,7 @@ 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 84a0ec6cfc..ce74589dde 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -10,6 +10,7 @@ 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 2c19d89266..1624f00e53 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -10,7 +10,7 @@ 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 d9a47a89c8..e35f2738b6 100755 --- a/tests/test_extension.py +++ b/tests/test_extension.py @@ -3,7 +3,7 @@ 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 730ffde4ff..3c3e3dcb3b 100644 --- a/tests/test_file_util.py +++ b/tests/test_file_util.py @@ -6,6 +6,7 @@ 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 d2a2b449ee..c7e5201b17 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 f9c142e446..a76e3e7a3c 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -6,7 +6,7 @@ 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 86db4a12c5..4d8c00acb9 100644 --- a/tests/test_install_data.py +++ b/tests/test_install_data.py @@ -6,6 +6,7 @@ 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 aa8a4e6014..d953157bb7 100644 --- a/tests/test_install_headers.py +++ b/tests/test_install_headers.py @@ -6,6 +6,7 @@ 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 4636304692..fddaabe111 100644 --- a/tests/test_install_lib.py +++ b/tests/test_install_lib.py @@ -7,6 +7,7 @@ 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 08360d297b..8952e744e5 100644 --- a/tests/test_install_scripts.py +++ b/tests/test_install_scripts.py @@ -7,6 +7,7 @@ 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 9d40dcd72c..ce66ee51e7 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 570fda121e..a0d62efcfd 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -5,6 +5,7 @@ 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 c98e28a1a4..c712f56a78 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -6,7 +6,7 @@ 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 d734ea0ac5..655e50dcfc 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -8,11 +8,9 @@ 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 0616c9f2e3..6c7eb20c47 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 f1e32b6cc6..7e76240a9a 100644 --- a/tests/test_text_file.py +++ b/tests/test_text_file.py @@ -3,6 +3,7 @@ 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 3a41e6fcaa..1bff38e9ee 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 8891820d67..4c6464a32e 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 f40caac838..8ff5ae2085 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -3,6 +3,7 @@ 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 980f83f29a..15f14c7de3 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 8a60dbe806..28ae09dc20 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()) 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: [PATCH 2051/2594] 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 7bdb912463..697ff84045 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -15,9 +15,10 @@ 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 bab91577f2..1f7106f84f 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -12,7 +12,7 @@ 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 7554e9f170..1c897ab04d 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 3db570382e..eeb8d73e14 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 d77912ae08..4f4e2bc7c6 100644 --- a/tests/test_build_clib.py +++ b/tests/test_build_clib.py @@ -3,6 +3,8 @@ 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 03517392fb..937fa0ce90 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -10,6 +10,7 @@ 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 df89cdec25..4da93cc140 100644 --- a/tests/test_build_scripts.py +++ b/tests/test_build_scripts.py @@ -8,6 +8,7 @@ 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 7c56c04339..4ea83dcb79 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 dbc4ee2a18..2d1610da7c 100644 --- a/tests/test_clean.py +++ b/tests/test_clean.py @@ -6,6 +6,7 @@ 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 97cdb8ad69..e074099609 100644 --- a/tests/test_cmd.py +++ b/tests/test_cmd.py @@ -99,7 +99,7 @@ def test_ensure_filename(self): 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 8d71234d6e..cfd096ebc2 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -11,6 +11,7 @@ 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 fcb798e73b..a36a1d5b49 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 74d1f3b269..0d979bcde9 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -6,7 +6,7 @@ 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 3550819681..751043432e 100644 --- a/tests/test_dep_util.py +++ b/tests/test_dep_util.py @@ -6,6 +6,7 @@ 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 84a0ec6cfc..693f77cf64 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -10,6 +10,7 @@ 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 8ab7e9fc31..ba60638179 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -11,7 +11,7 @@ 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 dbc6283981..7dbcf52c68 100644 --- a/tests/test_file_util.py +++ b/tests/test_file_util.py @@ -6,6 +6,7 @@ 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 32c56c75bc..be11ea5d13 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 c834b91b38..4f976f34e6 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 86db4a12c5..477569444f 100644 --- a/tests/test_install_data.py +++ b/tests/test_install_data.py @@ -6,6 +6,7 @@ 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 aa8a4e6014..b37224b93d 100644 --- a/tests/test_install_headers.py +++ b/tests/test_install_headers.py @@ -6,6 +6,7 @@ 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 754ea636a5..4d863089c0 100644 --- a/tests/test_install_lib.py +++ b/tests/test_install_lib.py @@ -7,6 +7,7 @@ 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 08360d297b..46085458bf 100644 --- a/tests/test_install_scripts.py +++ b/tests/test_install_scripts.py @@ -7,6 +7,7 @@ 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 b8e2209482..e155dbfea3 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -5,6 +5,7 @@ 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 915427b7d9..dd60f8c804 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -7,7 +7,7 @@ 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 b9d86bb02e..ab8c3d917b 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -24,11 +24,9 @@ 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 6caf039031..defa54d87f 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 f1e32b6cc6..ce19cd4dcd 100644 --- a/tests/test_text_file.py +++ b/tests/test_text_file.py @@ -3,6 +3,7 @@ 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 3f233e2823..40c908a24d 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 f45ee41e8c..99111999d8 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 981ad000da..67cd4cc7e6 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 1d9fbc7985..2189956429 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 8a60dbe806..1d6c8d5a98 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()) From d3d4f43f285902d969a2cfb2e8f4523c4937a299 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 13 Feb 2011 10:00:57 +0000 Subject: [PATCH 2052/2594] Bump for 3.2rc3. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 4bb5012318..8a7e3ff92b 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.2rc2" +__version__ = "3.2rc3" #--end constants-- From 41ef9736602618dc38120340185ef00b954058ca Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 20 Feb 2011 10:29:04 +0000 Subject: [PATCH 2053/2594] 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 8a7e3ff92b..49b6d517f6 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.2rc3" +__version__ = "3.2" #--end constants-- From d6f2a6b6356b8963212a475e3488df3bb585fb80 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 20 Feb 2011 10:37:07 +0000 Subject: [PATCH 2054/2594] 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 49b6d517f6..bd7d9dd634 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.2" +__version__ = "3.3a0" #--end constants-- 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: [PATCH 2055/2594] 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 b2e2fc6dc8..b886055f27 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -263,11 +263,11 @@ def create_exe(self, arcname, fullname, bitmap=None): 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 From 3860cc209929f5e6ec927d71cc41420665311434 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 14 Mar 2011 20:03:36 -0400 Subject: [PATCH 2056/2594] 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 804fb1355f..030933f17c 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[:] From 5bff24b86b4ad1400e9f063f3e371cac0fdd0e59 Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Tue, 15 Mar 2011 21:02:59 +0100 Subject: [PATCH 2057/2594] 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 6dd0445dbe..c06eba351d 100644 --- a/archive_util.py +++ b/archive_util.py @@ -9,6 +9,12 @@ 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 aff426521d..f969849590 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -7,12 +7,13 @@ 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 @@ 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 @@ def _create_files(self): 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 @@ def test_compress_deprecated(self): 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 @@ def test_make_zipfile(self): # 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 cc37fef8b0..55ba58d14f 100644 --- a/tests/test_bdist_dumb.py +++ b/tests/test_bdist_dumb.py @@ -18,6 +18,13 @@ """ +try: + import zlib + ZLIB_SUPPORT = True +except ImportError: + ZLIB_SUPPORT = False + + class BuildDumbTestCase(support.TempdirManager, support.LoggingSilencer, support.EnvironGuard, @@ -34,6 +41,7 @@ def tearDown(self): 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 655e50dcfc..eaf39a45ba 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -40,6 +40,13 @@ 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 @@ def _warn(*args): 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 @@ def test_prune_file_list(self): # 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 @@ def test_make_distribution(self): 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 @@ def test_add_defaults(self): 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 @@ def test_finalize_options(self): 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 @@ def test_get_file_list(self): 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 @@ def test_manifest_marker(self): 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() From 877161a90666a528acaa1169d33db58abbf2c964 Mon Sep 17 00:00:00 2001 From: Ezio Melotti Date: Wed, 16 Mar 2011 11:05:33 +0200 Subject: [PATCH 2058/2594] #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 ae4efc7efc..5b1d085c32 100644 --- a/cmd.py +++ b/cmd.py @@ -359,7 +359,7 @@ def copy_tree(self, infile, outfile, preserve_mode=1, preserve_times=1, 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 95fa3ed3c8..536aa6b61b 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -155,7 +155,7 @@ def __init__(self, verbose=0, dry_run=0, force=0): 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 649855f7ab..eb8958bff5 100755 --- a/tests/test_clean.py +++ b/tests/test_clean.py @@ -39,7 +39,7 @@ def test_simple_run(self): 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 a76e3e7a3c..ed69b0cbb0 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -62,7 +62,7 @@ def test_user_site(self): 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 eaf39a45ba..c7dd47fb5c 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -311,7 +311,7 @@ def test_get_file_list(self): # 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() From 14b28f28de2b0ac9e40d9838ea72df93fb661b51 Mon Sep 17 00:00:00 2001 From: Ezio Melotti Date: Wed, 16 Mar 2011 12:34:31 +0200 Subject: [PATCH 2059/2594] #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 dc40c14d61..9ad5657e40 100644 --- a/cmd.py +++ b/cmd.py @@ -377,7 +377,7 @@ def copy_tree(self, infile, outfile, 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 2d1610da7c..7b988f7f32 100644 --- a/tests/test_clean.py +++ b/tests/test_clean.py @@ -39,7 +39,7 @@ def test_simple_run(self): 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 ab8c3d917b..54a32b88f5 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -370,7 +370,7 @@ def test_get_file_list(self): # 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() 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: [PATCH 2060/2594] 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 c712f56a78..cb72a11538 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -137,7 +137,7 @@ def _no_way(prompt=''): # 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 @@ def test_registering(self): 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 @@ def test_password_reset(self): 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') 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: [PATCH 2061/2594] 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 dd60f8c804..bf63487035 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -138,7 +138,7 @@ def _no_way(prompt=''): # 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 @@ def test_registering(self): 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 @@ def test_password_reset(self): 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') 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: [PATCH 2062/2594] 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 1118060cbb..fdbebd7e0f 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -294,17 +294,20 @@ def read_template(self): 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 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: [PATCH 2063/2594] 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 0c3b0b55bf..cf8982bd9d 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -306,17 +306,20 @@ def read_template(self): 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 From 540753de877e149efb1ff20e132031d004ab33de Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 8 May 2011 09:03:36 +0200 Subject: [PATCH 2064/2594] Bump to 3.2.1b1. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 49b6d517f6..17c89ee82e 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.2" +__version__ = "3.2.1b1" #--end constants-- 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: [PATCH 2065/2594] 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 54a32b88f5..61f9c1f79b 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -388,6 +388,7 @@ def test_get_file_list(self): 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 @@ def test_manifest_marker(self): 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() From 942c7a73182fd88a290e89dc3e06ac2871cfbced Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 11 May 2011 00:14:28 +0200 Subject: [PATCH 2066/2594] 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 8b08bfeaf0..a43a7c306a 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -11,9 +11,10 @@ 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 @@ def copy_scripts(self): # 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 @@ def copy_scripts(self): 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: From aadd806fbbb624e62206009ca056d30847176d9a Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 11 May 2011 00:14:28 +0200 Subject: [PATCH 2067/2594] 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 8b08bfeaf0..a43a7c306a 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -11,9 +11,10 @@ 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 @@ def copy_scripts(self): # 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 @@ def copy_scripts(self): 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: From 8c2ad1791ef7504de9f6483af7617da21cc53daf Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 11 May 2011 00:57:29 +0200 Subject: [PATCH 2068/2594] 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 8b08bfeaf0..b3c767e319 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -11,9 +11,11 @@ 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 @@ def copy_scripts(self): # 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 @@ def copy_scripts(self): 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: From 563a44dea6e49eff7883c6c9c8b08a887e096738 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Sun, 15 May 2011 16:44:27 +0200 Subject: [PATCH 2069/2594] 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 33cc5a38a5..d206e0cdf9 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 86568ebb7a..46dcb5ed5d 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -3,12 +3,13 @@ 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 @@ def test_build_ext_path_cross_platform(self): 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 f06e4fdf88..6c49f0b1cb 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, From bc38a51782ce2e7d290cc6afbb7a22a8fde5e109 Mon Sep 17 00:00:00 2001 From: Ronald Oussoren Date: Sun, 15 May 2011 16:46:11 +0200 Subject: [PATCH 2070/2594] 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 897b7d63d6..06bbc01cd6 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 dcba75f703..0aa99babee 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -2,6 +2,7 @@ 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 @@ def test_ext_fullpath(self): 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 8ff5ae2085..1a06d4c4a1 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -92,7 +92,7 @@ def test_get_platform(self): ('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 @@ def test_get_platform(self): 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 @@ def test_get_platform(self): 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 @@ def test_get_platform(self): 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 ce3cd6ca33..d6f89d65e5 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, From 47892871bc741f9c21e2cdec4384b5aec2f2bb1a Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 15 May 2011 17:52:42 +0200 Subject: [PATCH 2071/2594] Bump to 3.2.1rc1. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 17c89ee82e..70ab7dbf2b 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.2.1b1" +__version__ = "3.2.1rc1" #--end constants-- From 09653b8554161ad38cf06341a4893c610da44f5a Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 19 May 2011 15:18:36 +0200 Subject: [PATCH 2072/2594] 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 a43a7c306a..31be7930d7 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -128,10 +128,9 @@ def copy_scripts(self): "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: From d71f44e082fb25025dd678e6440aad3e623dcb2e Mon Sep 17 00:00:00 2001 From: Tarek Ziade Date: Thu, 19 May 2011 19:56:12 +0200 Subject: [PATCH 2073/2594] 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 da3232cea8..00a57cc922 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -58,7 +58,8 @@ def test_package_data(self): 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): From 2c6268e3d2cddb476763b780ffaaba62a154a38d Mon Sep 17 00:00:00 2001 From: Tarek Ziade Date: Sun, 22 May 2011 22:09:55 +0200 Subject: [PATCH 2074/2594] 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 0aa99babee..a5b9700fe6 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -35,7 +35,9 @@ def setUp(self): 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 @@ def _fixup_command(self, cmd): 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 From d9b753aa56fa9bd0b9f6d9aa0d7ea32a5cdbc750 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Mon, 23 May 2011 15:22:56 -0400 Subject: [PATCH 2075/2594] 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 90f96feae1..6f4c021c62 100644 --- a/__init__.py +++ b/__init__.py @@ -22,5 +22,5 @@ # #--start constants-- -__version__ = "2.6.6" +__version__ = "2.6.7rc1" #--end constants-- From 744a4d498494ca6760eae00670adf90c777e32e5 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Mon, 23 May 2011 15:26:11 -0400 Subject: [PATCH 2076/2594] 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 6f4c021c62..b2a208201c 100644 --- a/__init__.py +++ b/__init__.py @@ -22,5 +22,5 @@ # #--start constants-- -__version__ = "2.6.7rc1" +__version__ = "2.6.7rc2" #--end constants-- 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: [PATCH 2077/2594] 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 937fa0ce90..6c6ec208aa 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 @@ def _setup_package_data(self): 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 @@ def test_dont_write_bytecode(self): 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) 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: [PATCH 2078/2594] 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 da3232cea8..4e46339b43 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -57,11 +57,15 @@ def test_package_data(self): 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 @@ def test_dont_write_bytecode(self): 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) From b93d39b5f98d32aea6a9dc32ba80f2822018b953 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sun, 29 May 2011 16:06:00 -0500 Subject: [PATCH 2079/2594] bump to 3.1.4rc1 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 6f55f016a2..5f8425fb1d 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.1.3" +__version__ = "3.1.4rc1" #--end constants-- From 8dff8ac3dff450113681b7510636be1c8e9d0270 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sun, 29 May 2011 16:50:27 -0500 Subject: [PATCH 2080/2594] bump to 2.7.2rc1 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index a82d4cb589..e9fc2d6c45 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "2.7.1" +__version__ = "2.7.2rc1" #--end constants-- From 9b7671822624669b22aa7574daa190b990c6906a Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Fri, 3 Jun 2011 20:02:47 -0400 Subject: [PATCH 2081/2594] Replay svn r88850. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index b2a208201c..beb65bb568 100644 --- a/__init__.py +++ b/__init__.py @@ -22,5 +22,5 @@ # #--start constants-- -__version__ = "2.6.7rc2" +__version__ = "2.6.7" #--end constants-- 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: [PATCH 2082/2594] 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 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: [PATCH 2083/2594] 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 From e7cc03bf1da67e855bed7ac89acb895a51bfeb9a Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 11 Jun 2011 09:42:44 -0500 Subject: [PATCH 2084/2594] 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 e9fc2d6c45..a849f1a9cd 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "2.7.2rc1" +__version__ = "2.7.2" #--end constants-- From edb4ad2deb371136eba9ab0d9c27687c3aa5df23 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 11 Jun 2011 09:58:58 -0500 Subject: [PATCH 2085/2594] bump to 3.1.4 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 5f8425fb1d..a3e2993648 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.1.4rc1" +__version__ = "3.1.4" #--end constants-- From d6e07e40b2eb968c89070316983beed527c2b111 Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Tue, 28 Jun 2011 00:39:19 -0700 Subject: [PATCH 2086/2594] 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 46dcb5ed5d..44fc980a30 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -19,6 +19,11 @@ 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') From 32d401bb9f75df6acc4dd67a361d5b3de036f98a Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Tue, 28 Jun 2011 00:42:50 -0700 Subject: [PATCH 2087/2594] 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 0aa99babee..d924f585ba 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -22,6 +22,11 @@ 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 @@ def setUp(self): 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 @@ def _fixup_command(self, cmd): 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 From 303c2b4c7af695706369e68cd6b32606ab5401d6 Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Tue, 28 Jun 2011 19:39:10 -0700 Subject: [PATCH 2088/2594] 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 44fc980a30..ced1329efd 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -9,7 +9,8 @@ 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 @@ def test_build_ext_path_cross_platform(self): 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 @@ def _try_compile_deployment_target(self): 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], From d671b9c866f6f54493dfa07e5004a92b104b205d Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Tue, 28 Jun 2011 19:40:39 -0700 Subject: [PATCH 2089/2594] 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 5c014c4be2..7306099f6b 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 d206e0cdf9..0d6d4c4fea 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. From 140120ec23e70358cb6dfeadf92b9aada4af835b Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Tue, 28 Jun 2011 19:43:15 -0700 Subject: [PATCH 2090/2594] 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 d924f585ba..0ce7f0f81c 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -11,7 +11,8 @@ 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 @@ def test_ext_fullpath(self): @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 @@ def _try_compile_deployment_target(self): 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], From 40bee45e0bd8a042c3a8f893328f6a6a52698fdf Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Tue, 28 Jun 2011 19:44:24 -0700 Subject: [PATCH 2091/2594] 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 8c476dc23f..2b62c968a4 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 06bbc01cd6..9d7d1902aa 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. From bf799fa9dfd51f6ff2bda9e768f4f4333d351f76 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 30 Jun 2011 15:40:22 +0200 Subject: [PATCH 2092/2594] 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 fd2a43d7d2..c0a04de3af 100644 --- a/core.py +++ b/core.py @@ -8,7 +8,8 @@ __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 From f2b3625193af66b537f99fc0c7843b4051f1bd1b Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 30 Jun 2011 23:25:47 +0200 Subject: [PATCH 2093/2594] 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 4e46339b43..c7c36f3cc7 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -10,7 +10,7 @@ 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 @@ def test_empty_package_dir(self): # 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 From 4758a6576c8847d6848ebe98f99f6526673f35b8 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 3 Jul 2011 09:41:27 +0200 Subject: [PATCH 2094/2594] Bump to 3.2.1rc2. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 70ab7dbf2b..2602c5074b 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.2.1rc1" +__version__ = "3.2.1rc2" #--end constants-- From 7d80bbfb319d76b2306d4193fdb082acd919927d Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 9 Jul 2011 08:56:21 +0200 Subject: [PATCH 2095/2594] 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 2602c5074b..a470ade93e 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.2.1rc2" +__version__ = "3.2.1" #--end constants-- From 4320a750da7c64db5dd3c18714655f5673198b33 Mon Sep 17 00:00:00 2001 From: Senthil Kumaran Date: Thu, 28 Jul 2011 22:32:49 +0800 Subject: [PATCH 2096/2594] 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 a470ade93e..c06002eab3 100644 --- a/__init__.py +++ b/__init__.py @@ -8,8 +8,6 @@ setup (...) """ -__revision__ = "$Id$" - # Distutils version # # Updated automatically by the Python release process. diff --git a/archive_util.py b/archive_util.py index c06eba351d..fcda08e20a 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 c5e5cd2571..9f4c432d90 100644 --- a/bcppcompiler.py +++ b/bcppcompiler.py @@ -11,8 +11,6 @@ # 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 291c008f20..c795c958fe 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 5b1d085c32..3ea08101ac 100644 --- a/cmd.py +++ b/cmd.py @@ -4,8 +4,6 @@ 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 c379edbed0..481eea9fd4 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 1a360b59e3..c5188eb371 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 2d39922672..170e889461 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -4,8 +4,6 @@ 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 e2ae877d9a..678e118896 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 b2e2fc6dc8..b7916e31a1 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 9c2667cfd2..cfc15cf0dd 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 428011a64d..3e20ef23cd 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -4,8 +4,6 @@ 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 fb31648951..8d843d689f 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -4,8 +4,6 @@ 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 26002e4b3f..3868c12f5b 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 a43a7c306a..ec43477061 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 2657c696e5..b67c795308 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 ae1d22c376..0cb2701662 100644 --- a/command/clean.py +++ b/command/clean.py @@ -4,8 +4,6 @@ # 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 ac80a54eb1..847e858160 100644 --- a/command/config.py +++ b/command/config.py @@ -9,8 +9,6 @@ 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 bdc3a09b22..0161898f49 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 ab40797b98..947cd76a99 100644 --- a/command/install_data.py +++ b/command/install_data.py @@ -5,8 +5,6 @@ # 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 38125b5513..9bb0b18dc0 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 6022d30f27..3d01d07115 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 ea8d5aa654..31a1130ee5 100644 --- a/command/install_scripts.py +++ b/command/install_scripts.py @@ -5,8 +5,6 @@ # 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 bdf5f8f09c..99545affa4 100644 --- a/command/register.py +++ b/command/register.py @@ -5,8 +5,6 @@ # 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 fdbebd7e0f..48cb26b6ae 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 c0a04de3af..260332a2ac 100644 --- a/core.py +++ b/core.py @@ -6,8 +6,6 @@ really defined in distutils.dist and distutils.cmd. """ -__revision__ = "$Id$" - import os import sys diff --git a/cygwinccompiler.py b/cygwinccompiler.py index 536aa6b61b..819e1a97be 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -45,8 +45,6 @@ # * 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 2886744402..daf1660f0d 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 07b3549c6f..d74f5e4e92 100644 --- a/dep_util.py +++ b/dep_util.py @@ -4,8 +4,6 @@ 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 5b005f0865..30daf49a6e 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 01f1f1cfc0..02cd79ba2f 100644 --- a/dist.py +++ b/dist.py @@ -4,8 +4,6 @@ being built/installed/distributed. """ -__revision__ = "$Id$" - import sys, os, re try: diff --git a/emxccompiler.py b/emxccompiler.py index 16dce53524..3675f8df9c 100644 --- a/emxccompiler.py +++ b/emxccompiler.py @@ -19,8 +19,6 @@ # # * 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 acecacccb5..eb13c983e9 100644 --- a/errors.py +++ b/errors.py @@ -8,8 +8,6 @@ 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 2d1c36bd5c..a93655af2c 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 879d4d25bf..7d170dd277 100644 --- a/fancy_getopt.py +++ b/fancy_getopt.py @@ -8,8 +8,6 @@ * 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 e1eb932926..9bdd14e42e 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 06a8da9a07..a94b5c8e96 100644 --- a/filelist.py +++ b/filelist.py @@ -4,8 +4,6 @@ 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 e849e16d51..0cddb5c8e0 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -12,8 +12,6 @@ # 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 1cd0f91d5f..8116656961 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -8,8 +8,6 @@ # 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 2b62c968a4..f58c55f902 100644 --- a/spawn.py +++ b/spawn.py @@ -6,8 +6,6 @@ executable name. """ -__revision__ = "$Id$" - import sys import os diff --git a/sysconfig.py b/sysconfig.py index 9d7d1902aa..5ea724c096 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -9,8 +9,6 @@ Email: """ -__revision__ = "$Id$" - import os import re import sys diff --git a/tests/test_archive_util.py b/tests/test_archive_util.py index f969849590..8edfab49f8 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 454725c626..40b8484a68 100644 --- a/text_file.py +++ b/text_file.py @@ -4,8 +4,6 @@ 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 bf73416154..c70a3cc555 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -13,8 +13,6 @@ * 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 d6f89d65e5..023ddffc82 100644 --- a/util.py +++ b/util.py @@ -4,8 +4,6 @@ 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 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: [PATCH 2097/2594] 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 cf8982bd9d..75950f3d18 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -182,14 +182,20 @@ def get_file_list(self): 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 @@ def write_manifest(self): 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 @@ def read_manifest(self): """ 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 61f9c1f79b..8da6fe4de8 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 @@ 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 @@ def test_manifest_marker(self): 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 @@ def test_manual_manifest(self): 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) 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: [PATCH 2098/2594] 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 48cb26b6ae..21ea61d96a 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -174,14 +174,20 @@ def get_file_list(self): 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 @@ def write_manifest(self): 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 @@ def read_manifest(self): """ 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 c7dd47fb5c..440af9886c 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 @@ def test_manifest_marker(self): 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 @@ def test_manual_manifest(self): 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) 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: [PATCH 2099/2594] 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 4e40df6899..2b759056ea 100644 --- a/dep_util.py +++ b/dep_util.py @@ -7,6 +7,7 @@ __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 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: [PATCH 2100/2594] 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 c7c36f3cc7..4e46339b43 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -10,7 +10,7 @@ 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 @@ def test_empty_package_dir(self): # 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 From 2af5d249da9647a383d27799c9bb9bf9802566c9 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 13 Aug 2011 11:34:58 +0200 Subject: [PATCH 2101/2594] 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 c06002eab3..b31b2d203d 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.2.1" +__version__ = "3.2.2rc1" #--end constants-- From faba07dcbfdb0bec687496bd329985497f907fbb Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 17 Aug 2011 20:49:41 +0200 Subject: [PATCH 2102/2594] 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 030933f17c..9b0639a549 100644 --- a/tests/test_bdist_rpm.py +++ b/tests/test_bdist_rpm.py @@ -47,7 +47,7 @@ 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': + if not sys.platform.startswith('linux'): return # this test will run only if the rpm commands are found @@ -87,7 +87,7 @@ 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': + if not sys.platform.startswith('linux'): return # http://bugs.python.org/issue1533164 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: [PATCH 2103/2594] 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 e258d2e58d..0e33827fe8 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 @@ def _format(msg, args): def clear_logs(self): self.logs = [] + class TempdirManager(object): """Mix-in class that handles temporary directories for test cases. @@ -97,6 +101,7 @@ def create_dist(self, pkg_name='foo', **kw): return pkg_dir, dist + class DummyCommand: """Class to store options for retrieval via set_undefined_options().""" @@ -107,6 +112,7 @@ def __init__(self, **kwargs): def ensure_finalized(self): pass + class EnvironGuard(object): def setUp(self): @@ -123,3 +129,39 @@ def tearDown(self): 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 0ce7f0f81c..de53afb1c3 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 @@ 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 @@ def setUp(self): 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 @@ def _fixup_command(self, cmd): 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 @@ def _try_compile_deployment_target(self, operator, target): 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()) 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: [PATCH 2104/2594] 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 e258d2e58d..0e33827fe8 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 @@ def _format(msg, args): def clear_logs(self): self.logs = [] + class TempdirManager(object): """Mix-in class that handles temporary directories for test cases. @@ -97,6 +101,7 @@ def create_dist(self, pkg_name='foo', **kw): return pkg_dir, dist + class DummyCommand: """Class to store options for retrieval via set_undefined_options().""" @@ -107,6 +112,7 @@ def __init__(self, **kwargs): def ensure_finalized(self): pass + class EnvironGuard(object): def setUp(self): @@ -123,3 +129,39 @@ def tearDown(self): 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 0ce7f0f81c..de53afb1c3 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 @@ 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 @@ def setUp(self): 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 @@ def _fixup_command(self, cmd): 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 @@ def _try_compile_deployment_target(self, operator, target): 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()) 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: [PATCH 2105/2594] 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 ed69b0cbb0..3e47d819fe 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 @@ def test_finalize_options(self): 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 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: [PATCH 2106/2594] 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 0e33827fe8..562a65c861 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 de53afb1c3..8eb59b4d2e 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -7,7 +7,7 @@ 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 @@ def setUp(self): 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 @@ def test_build_ext(self): 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 @@ def test_user_site(self): # 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 @@ def test_get_outputs(self): 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) 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: [PATCH 2107/2594] 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 ed69b0cbb0..3e47d819fe 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 @@ def test_finalize_options(self): 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 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: [PATCH 2108/2594] 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 3e47d819fe..2133fa7916 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -7,11 +7,14 @@ 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 @@ def test_record(self): '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) 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: [PATCH 2109/2594] 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 3e47d819fe..2133fa7916 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -7,11 +7,14 @@ 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 @@ def test_record(self): '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) 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: [PATCH 2110/2594] 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 0e33827fe8..81289db4a1 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. 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: [PATCH 2111/2594] 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 440af9886c..f34f786c92 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -365,6 +365,7 @@ def test_manifest_comments(self): 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'), From c0f9165822276f0cd6b74ea6d0296e494b3e5a3f Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Sun, 21 Aug 2011 00:39:18 +0200 Subject: [PATCH 2112/2594] 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 8d843d689f..8baf538f2a 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -240,8 +240,7 @@ def finalize_options(self): # 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 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: [PATCH 2113/2594] 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 81289db4a1..49277111bf 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. 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: [PATCH 2114/2594] 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 49277111bf..ffef98bae4 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 de53afb1c3..8eb59b4d2e 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -7,7 +7,7 @@ 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 @@ def setUp(self): 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 @@ def test_build_ext(self): 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 @@ def test_user_site(self): # 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 @@ def test_get_outputs(self): 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) 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: [PATCH 2115/2594] 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 2133fa7916..e065aa3dcd 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -202,6 +202,7 @@ def test_record_extensions(self): support.copy_xxmodule_c(project_dir) buildcmd = build_ext(dist) + support.fixup_build_ext(buildcmd) buildcmd.ensure_finalized() buildcmd.run() 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: [PATCH 2116/2594] 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 2133fa7916..e065aa3dcd 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -202,6 +202,7 @@ def test_record_extensions(self): support.copy_xxmodule_c(project_dir) buildcmd = build_ext(dist) + support.fixup_build_ext(buildcmd) buildcmd.ensure_finalized() buildcmd.run() From 54bac89d98770275e3801f293c88251211354d8c Mon Sep 17 00:00:00 2001 From: Nadeem Vawda Date: Sun, 21 Aug 2011 22:35:41 +0200 Subject: [PATCH 2117/2594] 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 440af9886c..f34f786c92 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -365,6 +365,7 @@ def test_manifest_comments(self): 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'), From 6e23eb2a22ad885a41f58d035b7d11c3e3c0658a Mon Sep 17 00:00:00 2001 From: Nadeem Vawda Date: Sun, 21 Aug 2011 22:35:41 +0200 Subject: [PATCH 2118/2594] 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 440af9886c..f34f786c92 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -365,6 +365,7 @@ def test_manifest_comments(self): 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'), From 5b49959934750e3cfd186c85fe8f5a4bcffc253b Mon Sep 17 00:00:00 2001 From: Nadeem Vawda Date: Sun, 21 Aug 2011 22:40:04 +0200 Subject: [PATCH 2119/2594] 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 8da6fe4de8..42faa5d16b 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -351,6 +351,7 @@ def test_get_file_list(self): # 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() 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: [PATCH 2120/2594] 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 562a65c861..7a76ca05a0 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 8eb59b4d2e..1827437628 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -47,10 +47,6 @@ def test_build_ext(self): 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 @@ def test_get_outputs(self): 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 e065aa3dcd..5c105af95a 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -18,6 +18,14 @@ 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 @@ def test_record_extensions(self): 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 @@ def test_record_extensions(self): 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) 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: [PATCH 2121/2594] 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 ffef98bae4..dc0e660e69 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 8eb59b4d2e..1827437628 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -47,10 +47,6 @@ def test_build_ext(self): 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 @@ def test_get_outputs(self): 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 e065aa3dcd..5c105af95a 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -18,6 +18,14 @@ 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 @@ def test_record_extensions(self): 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 @@ def test_record_extensions(self): 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) From aa066f5e3d725c0b3aff6f5c10488b054d4bc846 Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Thu, 25 Aug 2011 18:32:02 +0200 Subject: [PATCH 2122/2594] 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 dc0e660e69..44fcd6b7d8 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() 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: [PATCH 2123/2594] 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 170e889461..1ab09d1616 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -47,7 +47,7 @@ def initialize_options(self): 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 @@ def finalize_options(self): 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 b11957a7dc..b3cfe9ceff 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -130,18 +130,22 @@ def initialize_options(self): 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 b7916e31a1..e3ed3ad82c 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -65,13 +65,15 @@ def initialize_options(self): 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 @@ def finalize_options(self): # 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 94d40cc25b..503a6e857d 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) From 48f414cea0e31da0a1267b9248ac38363d006fd4 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 3 Sep 2011 11:17:55 +0200 Subject: [PATCH 2124/2594] Bump to 3.2.2. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index b31b2d203d..9ec6165864 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.2.2rc1" +__version__ = "3.2.2" #--end constants-- From 3b9a1c74f1e174853a335c8662a3f6c7b81e4bd3 Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Thu, 25 Aug 2011 18:32:02 +0200 Subject: [PATCH 2125/2594] 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 7a76ca05a0..8452feb155 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() 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: [PATCH 2126/2594] 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 5c105af95a..dfc46b197b 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -20,9 +20,8 @@ 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') 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: [PATCH 2127/2594] 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 783318e8d3..788acdaa3e 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.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(*args, **kw): return func(*args, **kw) return _capture_warnings + class LoggingSilencer(object): def setUp(self): @@ -49,6 +54,7 @@ def _format(msg, args): def clear_logs(self): self.logs = [] + class TempdirManager(object): """Mix-in class that handles temporary directories for test cases. @@ -105,6 +111,7 @@ def create_dist(self, pkg_name='foo', **kw): return pkg_dir, dist + class DummyCommand: """Class to store options for retrieval via set_undefined_options().""" @@ -115,6 +122,7 @@ def __init__(self, **kwargs): def ensure_finalized(self): pass + class EnvironGuard(object): def setUp(self): @@ -131,3 +139,71 @@ def tearDown(self): 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 ced1329efd..30de0e2b7b 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 @@ # 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 @@ def test_finalize_options(self): 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 @@ def test_get_outputs(self): 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 @@ def _try_compile_deployment_target(self, operator, target): 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") 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: [PATCH 2128/2594] 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 30de0e2b7b..2fa63d300b 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -27,6 +27,12 @@ def setUp(self): 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 @@ def test_solaris_enable_shared(self): # 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. 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: [PATCH 2129/2594] 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 8da6fe4de8..4c80bc0bbe 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -422,6 +422,7 @@ def test_manifest_comments(self): 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'), 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: [PATCH 2130/2594] 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 4f976f34e6..ebfb04f446 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 @@ def check_path(got, expected): 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) 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: [PATCH 2131/2594] 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 788acdaa3e..648a8e4156 100644 --- a/tests/support.py +++ b/tests/support.py @@ -161,6 +161,8 @@ def test_compile(self): 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 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: [PATCH 2132/2594] 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 170e889461..1ab09d1616 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -47,7 +47,7 @@ def initialize_options(self): 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 @@ def finalize_options(self): 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 b11957a7dc..b3cfe9ceff 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -130,18 +130,22 @@ def initialize_options(self): 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 b7916e31a1..e3ed3ad82c 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -65,13 +65,15 @@ def initialize_options(self): 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 @@ def finalize_options(self): # 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 94d40cc25b..503a6e857d 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) 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: [PATCH 2133/2594] 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 7c60d39be9..2f3c66829a 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -58,7 +58,7 @@ def initialize_options (self): 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 @@ def finalize_options(self): 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 ded837d752..703f873b16 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -131,18 +131,22 @@ def initialize_options (self): 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 36d46bd627..aa9383af98 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -71,7 +71,7 @@ def initialize_options (self): 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 @@ def initialize_options (self): 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 @@ def finalize_options (self): # 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 fa7cd5adb5..121d0992db 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) 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: [PATCH 2134/2594] 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 44fcd6b7d8..d77bbee362 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 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: [PATCH 2135/2594] 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 f34f786c92..f9e28c8471 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -78,9 +78,6 @@ def get_cmd(self, metadata=None): 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 @@ def test_metadata_check_option(self): # 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 @@ def test_metadata_check_option(self): 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): 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: [PATCH 2136/2594] 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 2f09c9bea1..5134e6aba2 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -85,9 +85,6 @@ def get_cmd(self, metadata=None): 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 @@ def test_metadata_check_option(self): # 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 @@ def test_metadata_check_option(self): 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): 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: [PATCH 2137/2594] 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 21ea61d96a..a9429a4296 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -306,7 +306,10 @@ def read_template(self): 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 f9e28c8471..529b4ef5c6 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -15,6 +15,7 @@ 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 @@ def test_show_formats(self): self.assertEqual(len(output), num_formats) def test_finalize_options(self): - dist, cmd = self.get_cmd() cmd.finalize_options() @@ -285,6 +285,32 @@ def test_finalize_options(self): 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 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: [PATCH 2138/2594] 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 75950f3d18..d30de10673 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -320,7 +320,10 @@ def read_template(self): 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 5134e6aba2..33f6ee69a9 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -29,6 +29,7 @@ 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 @@ def test_show_formats(self): self.assertEqual(len(output), num_formats) def test_finalize_options(self): - dist, cmd = self.get_cmd() cmd.finalize_options() @@ -342,6 +342,32 @@ def test_make_distribution_owner_group(self): 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 From e95a6f1db95dfd2ebb63de9af3211718bb47346a Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 5 Sep 2011 23:44:56 +0200 Subject: [PATCH 2139/2594] 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 c8880310df..c2a7d649c0 100644 --- a/command/install_egg_info.py +++ b/command/install_egg_info.py @@ -40,9 +40,8 @@ def run(self): "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 02cd79ba2f..8ca5b6f4f1 100644 --- a/dist.py +++ b/dist.py @@ -1010,11 +1010,9 @@ def __init__ (self): 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. From beedb510e63fa7cf09ef572c178a0e6a161335fa Mon Sep 17 00:00:00 2001 From: Jesus Cea Date: Fri, 9 Sep 2011 18:50:59 +0200 Subject: [PATCH 2140/2594] 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 648a8e4156..a2190c0214 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() 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: [PATCH 2141/2594] 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 a20d6c8ffc..7050cbed26 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -74,7 +74,7 @@ def test_command_packages_cmdline(self): 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 @@ def test_command_packages_configfile(self): 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 @@ def test_get_command_packages(self): 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 @@ def test_announce(self): kwargs = {'level': 'ok2'} self.assertRaises(ValueError, dist.announce, args, kwargs) + class MetadataTestCase(support.TempdirManager, support.EnvironGuard, unittest.TestCase): @@ -170,15 +165,20 @@ def tearDown(self): 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 @@ def test_provides(self): 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 @@ def test_requires(self): 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 @@ def test_obsoletes(self): 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 @@ def test_obsoletes_illegal(self): "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 @@ def test_custom_pydistutils(self): 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 @@ def test_show_help(self): 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() 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: [PATCH 2142/2594] =?UTF-8?q?Fix=20determination=20of=20Metadata?= =?UTF-8?q?=20version=20(#8933).=20=20Patch=20by=20Filip=20Gruszczy=C5=84s?= =?UTF-8?q?ki.?= 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 8ca5b6f4f1..69825f206f 100644 --- a/dist.py +++ b/dist.py @@ -1018,7 +1018,8 @@ 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: + 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 7050cbed26..8aaae88cae 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -244,6 +244,20 @@ def test_obsoletes_illegal(self): "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:: 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: [PATCH 2143/2594] 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 ba60638179..ff9fa8fc58 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -8,12 +8,13 @@ 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 @@ def create_distribution(self, configfiles=()): 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 @@ def test_command_packages_cmdline(self): 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 @@ def test_command_packages_configfile(self): 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 @@ def test_write_pkg_file(self): '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 @@ def test_write_pkg_file(self): '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 @@ def test_get_command_packages(self): 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 @@ def tearDown(self): 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 @@ def test_provides(self): 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 @@ def test_requires(self): 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 @@ def test_obsoletes(self): 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 @@ def test_custom_pydistutils(self): 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 @@ def test_show_help(self): 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 @@ def test_read_metadata(self): self.assertEqual(metadata.obsoletes, None) self.assertEqual(metadata.requires, ['foo']) + def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(DistributionTestCase)) 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: [PATCH 2144/2594] =?UTF-8?q?Fix=20determination=20of=20Metadata?= =?UTF-8?q?=20version=20(#8933).=20=20Patch=20by=20Filip=20Gruszczy=C5=84s?= =?UTF-8?q?ki.?= 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 597909ea1a..e025313dbd 100644 --- a/dist.py +++ b/dist.py @@ -1111,7 +1111,8 @@ 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: + 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 ff9fa8fc58..4b7bbeb33e 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -245,6 +245,20 @@ def tearDown(self): 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:: 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: [PATCH 2145/2594] 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 4f7ebdd9fc..e2e6e4ebaa 100644 --- a/tests/test_config_cmd.py +++ b/tests/test_config_cmd.py @@ -44,10 +44,10 @@ def test_search_cpp(self): 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): 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: [PATCH 2146/2594] 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 4e46339b43..80316ad7d0 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 @@ def test_package_data(self): 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 fddaabe111..b42b03b2fa 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 @@ def test_finalize_options(self): 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 023ddffc82..631f5dfb70 100644 --- a/util.py +++ b/util.py @@ -4,7 +4,11 @@ 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 @@ def byte_compile (py_files, # 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: 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: [PATCH 2147/2594] 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 5ea724c096..ac06313b19 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') 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: [PATCH 2148/2594] 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 a36a1d5b49..2cf3886cb5 100644 --- a/tests/test_config_cmd.py +++ b/tests/test_config_cmd.py @@ -44,10 +44,10 @@ def test_search_cpp(self): 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): 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: [PATCH 2149/2594] 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 631f5dfb70..f42c6a18b9 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") 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: [PATCH 2150/2594] =?UTF-8?q?Fix=20distutils=E2=80=99=20check=20an?= =?UTF-8?q?d=20register=20Unicode=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 bc29baaba4..4b64e458bc 100644 --- a/command/check.py +++ b/command/check.py @@ -5,6 +5,7 @@ __revision__ = "$Id$" from distutils.core import Command +from distutils.dist import PKG_INFO_ENCODING from distutils.errors import DistutilsSetupError try: @@ -108,6 +109,8 @@ def check_metadata(self): 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 dc089902f1..edb42b955d 100644 --- a/command/register.py +++ b/command/register.py @@ -10,7 +10,6 @@ import urllib2 import getpass import urlparse -import StringIO from warnings import warn from distutils.core import PyPIRCCommand @@ -260,21 +259,30 @@ def post_to_server(self, data, auth=None): 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 4ea83dcb79..f73342ade8 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 @@ def test_check_metadata(self): 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 @@ def test_check_restructuredtext(self): 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 bf63487035..aa9bc43c5c 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 @@ def test_strict(self): 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() 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: [PATCH 2151/2594] =?UTF-8?q?Add=20tests=20for=20Unicode=20handlin?= =?UTF-8?q?g=20in=20distutils=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 229ae25c6d..4de64734c4 100644 --- a/tests/test_check.py +++ b/tests/test_check.py @@ -46,6 +46,15 @@ def test_check_metadata(self): 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 @@ def test_check_restructuredtext(self): 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 cb72a11538..5863ae1422 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -214,7 +214,7 @@ def test_strict(self): # 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 @@ def test_strict(self): 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() 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: [PATCH 2152/2594] 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 c7e5201b17..f3304b2e33 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 @@ def test_debug_print(self): 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) 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: [PATCH 2153/2594] 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 be11ea5d13..1e04077f82 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 @@ 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 @@ def test_process_template_line(self): 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 @@ def test_debug_print(self): 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) From ed45aad402e3df6f660d88c19710e56951126286 Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Mon, 17 Oct 2011 11:05:36 +1100 Subject: [PATCH 2154/2594] 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 bf85ac75c7..9838d64a0e 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -640,15 +640,7 @@ def link(self, 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 @@ def link(self, # 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 @@ def _remove_visual_c_ref(self, manifest_file): # 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 @@ def _remove_visual_c_ref(self, manifest_file): 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 e155dbfea3..73470729fd 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -7,7 +7,36 @@ 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 @@ def test_remove_visual_c_ref(self): 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 @@ def test_remove_visual_c_ref(self): # 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) From 1f218d0f3f8aa86e62b9b8c39efdb077d66481d4 Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Mon, 17 Oct 2011 11:05:57 +1100 Subject: [PATCH 2155/2594] 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 0cddb5c8e0..0807a36095 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -627,15 +627,7 @@ def link(self, 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 @@ def link(self, # 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 @@ def _remove_visual_c_ref(self, manifest_file): # 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 @@ def _remove_visual_c_ref(self, manifest_file): 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 a0d62efcfd..5fa1ca10d4 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -7,7 +7,36 @@ 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 @@ def test_remove_visual_c_ref(self): 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 @@ def test_remove_visual_c_ref(self): # 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) From a768a2ee006904565310189824cc120c62004568 Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Mon, 17 Oct 2011 11:35:06 +1100 Subject: [PATCH 2156/2594] 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 9838d64a0e..7ec9b92a5d 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -685,7 +685,7 @@ def manifest_setup_ldargs(self, output_filename, build_temp, ld_args): 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 @@ def _remove_visual_c_ref(self, manifest_file): 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: [PATCH 2157/2594] 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 0807a36095..b3f6ce10a8 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -715,7 +715,7 @@ def _remove_visual_c_ref(self, manifest_file): 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: [PATCH 2158/2594] 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 69825f206f..a702568278 100644 --- a/dist.py +++ b/dist.py @@ -537,7 +537,7 @@ def _parse_command_opts(self, parser, args): 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( 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: [PATCH 2159/2594] 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 55ba58d14f..1037d8216e 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 @@ def test_simple_built(self): # 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 dfc46b197b..8a63af992f 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -87,19 +87,17 @@ def _expanduser(path): 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 @@ def _test_user_site(self): # 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 @@ def _test_user_site(self): 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 @@ def test_finalize_options(self): 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 @@ 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) @@ -216,7 +212,7 @@ def test_record_extensions(self): 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 529b4ef5c6..d0d16b2f55 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -288,7 +288,7 @@ def test_finalize_options(self): # 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 @@ def _test_template(self, content): 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): From 4d25e1b74cf370c9013b6e016e0ac9da4d57f6bb Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Thu, 3 Nov 2011 02:45:46 +0100 Subject: [PATCH 2160/2594] 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 3868c12f5b..9e2473fbb6 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 @@ def get_outputs(self, include_bytecode=1): 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 3d01d07115..8a6bc7dec4 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -4,6 +4,7 @@ (install all Python modules).""" import os +import imp import sys from distutils.core import Command @@ -164,9 +165,9 @@ def _bytecode_filenames(self, py_filenames): 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 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: [PATCH 2161/2594] 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 3868c12f5b..1371b3d6ff 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 @@ def get_outputs(self, include_bytecode=1): 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 3d01d07115..15c08f1249 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -4,6 +4,7 @@ (install all Python modules).""" import os +import imp import sys from distutils.core import Command @@ -164,9 +165,11 @@ def _bytecode_filenames(self, py_filenames): 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 80316ad7d0..e416edd4a1 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 @@ def test_package_data(self): # 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 @@ def test_package_data(self): 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 @@ def test_empty_package_dir(self): 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 @@ def test_dont_write_bytecode(self): 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 8a63af992f..cb2e1f2879 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 @@ def check_path(got, expected): 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 @@ def test_finalize_options(self): 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 @@ def test_record(self): 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 @@ def test_debug_mode(self): 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 b42b03b2fa..2bd4dc6e96 100644 --- a/tests/test_install_lib.py +++ b/tests/test_install_lib.py @@ -10,13 +10,14 @@ 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 @@ def test_finalize_options(self): @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 @@ def test_dont_write_bytecode(self): self.assertTrue('byte-compiling is disabled' in self.logs[0][1]) + def test_suite(): return unittest.makeSuite(InstallLibTestCase) From 658d33c3b35e0cb0cdcb75c80026192adad14a4c Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Sat, 12 Nov 2011 01:20:45 +0100 Subject: [PATCH 2162/2594] 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 a94b5c8e96..87b2cc6bc4 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 From 74d45d2ff91f898f30dcf8c4a47192ed8fc509c3 Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Sat, 12 Nov 2011 01:33:59 +0100 Subject: [PATCH 2163/2594] 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 4aac6d397a..ebd1411817 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 From 490e58f300e1cbed87b5efa671c3502d79e0c6a6 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 26 Dec 2011 10:15:15 -0500 Subject: [PATCH 2164/2594] 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 1f7106f84f..2881148cc2 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 @@ def test_make_tarball(self): 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 @@ def test_make_tarball(self): 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 @@ def _breaks(*args, **kw): 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 33f6ee69a9..cb7beb7b2c 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -165,6 +165,28 @@ def test_make_distribution(self): 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): From 57661611c196b2b76a8be5463ae11db361e22eb4 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 26 Dec 2011 12:17:01 -0500 Subject: [PATCH 2165/2594] 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 8edfab49f8..a6aeaf04a0 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 @@ def test_make_tarball(self): 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 @@ def test_make_tarball(self): 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: From 812d67fafa516462fdac0294a56dcf0cd1631a59 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 28 Dec 2011 10:45:08 -0500 Subject: [PATCH 2166/2594] 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 a6aeaf04a0..1afdd46225 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 @@ 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 @@ def test_make_tarball(self): 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 @@ def test_make_tarball_latin1(self): 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 From 92f16a5603d9ba55dd73f8c1ad34efb276021dc4 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 28 Dec 2011 11:42:22 -0500 Subject: [PATCH 2167/2594] 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 2881148cc2..f01cec3263 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -4,6 +4,7 @@ import unittest import os +import sys import tarfile from os.path import splitdrive import warnings @@ -34,6 +35,18 @@ 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 @@ def test_make_tarball_unicode(self): 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 @@ def test_make_tarball_unicode_latin1(self): 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 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: [PATCH 2168/2594] 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 678e118896..357eaa575e 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -365,16 +365,28 @@ def run(self): 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 804fb1355f..ab7a1bf24e 100644 --- a/tests/test_bdist_rpm.py +++ b/tests/test_bdist_rpm.py @@ -78,6 +78,10 @@ def test_quiet(self): 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 @@ def test_no_optimize_flag(self): 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(): From 91c28e06be723398c598c6412136eeec653125be Mon Sep 17 00:00:00 2001 From: Jesus Cea Date: Wed, 18 Jan 2012 03:51:38 +0100 Subject: [PATCH 2169/2594] 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 6c49f0b1cb..2e4617ef27 100644 --- a/util.py +++ b/util.py @@ -6,7 +6,7 @@ __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) From 379a0e19faa93540a244372b3332262916b0140a Mon Sep 17 00:00:00 2001 From: Jesus Cea Date: Wed, 18 Jan 2012 03:58:42 +0100 Subject: [PATCH 2170/2594] 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 f42c6a18b9..36c4a68494 100644 --- a/util.py +++ b/util.py @@ -9,6 +9,7 @@ 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) From 411af75d7af96d11ff9a9fbed91298e58a2d7d07 Mon Sep 17 00:00:00 2001 From: Jesus Cea Date: Wed, 18 Jan 2012 04:25:28 +0100 Subject: [PATCH 2171/2594] 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 2e4617ef27..0c24e8ca3e 100644 --- a/util.py +++ b/util.py @@ -6,7 +6,7 @@ __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) From 026f3483351ce08ffa47f9143aa17d7da02f10ec Mon Sep 17 00:00:00 2001 From: Jesus Cea Date: Wed, 18 Jan 2012 04:27:37 +0100 Subject: [PATCH 2172/2594] 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 36c4a68494..416deec71d 100644 --- a/util.py +++ b/util.py @@ -9,7 +9,6 @@ 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) From 989fd55dddc6638eab5073ab0adcc9df0ceac154 Mon Sep 17 00:00:00 2001 From: Jesus Cea Date: Wed, 18 Jan 2012 05:04:49 +0100 Subject: [PATCH 2173/2594] 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 416deec71d..bce840274d 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) From e8934cbb01ea226144ff9222ad666a26a715b1a2 Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Fri, 3 Feb 2012 02:39:49 +0100 Subject: [PATCH 2174/2594] 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 a2190c0214..4e6058d0ec 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) From ef0dc103554be0c54d48e5bc028050423efb4594 Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Fri, 3 Feb 2012 02:42:16 +0100 Subject: [PATCH 2175/2594] 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 d77bbee362..84d9232328 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) From 8108212a71996cd25d5cb5366148d9c9dace27b5 Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Fri, 10 Feb 2012 12:59:06 +0100 Subject: [PATCH 2176/2594] 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 0d6d4c4fea..4f9041a794 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: From 008c905df03c73798516d88ffe6550261d4f9af3 Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Fri, 10 Feb 2012 13:01:08 +0100 Subject: [PATCH 2177/2594] 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 ac06313b19..16902ca920 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: From e1f6c4c9deda898b03f389edcbbe0fd16e547724 Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Sat, 11 Feb 2012 20:40:24 +0100 Subject: [PATCH 2178/2594] 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 c2b1f6fbe9..7076b93394 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -18,58 +18,6 @@ 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 98a1726ffa..205587e7fc 100644 --- a/command/build_clib.py +++ b/command/build_clib.py @@ -19,7 +19,7 @@ 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 da8da59538..b084913563 100644 --- a/command/config.py +++ b/command/config.py @@ -16,7 +16,7 @@ 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 4f9041a794..4b193b2703 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 4f4e2bc7c6..bef1bd9953 100644 --- a/tests/test_build_clib.py +++ b/tests/test_build_clib.py @@ -122,7 +122,8 @@ def test_run(self): # 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 e21873e821..45e477a429 100644 --- a/tests/test_ccompiler.py +++ b/tests/test_ccompiler.py @@ -4,7 +4,8 @@ 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 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: [PATCH 2179/2594] 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 ebd1411817..5540b39a38 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 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: [PATCH 2180/2594] 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 87b2cc6bc4..91220321a4 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 From 0dcd74259a364fdc273223482084e595460ed4a1 Mon Sep 17 00:00:00 2001 From: Nadeem Vawda Date: Mon, 13 Feb 2012 21:33:51 +0200 Subject: [PATCH 2181/2594] 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 1e04077f82..d86ea44491 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 @@ def test_process_template_line(self): # 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 @@ def test_process_template_line(self): 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) 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: [PATCH 2182/2594] 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 55a4288ac0..923197bac4 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -160,8 +160,7 @@ def finalize_options(self): 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 2fa63d300b..198cce3257 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -150,21 +150,21 @@ def test_finalize_options(self): # 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']) 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: [PATCH 2183/2594] 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 8d843d689f..34b61bdb82 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -165,8 +165,7 @@ def finalize_options(self): 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 1827437628..87cceee22d 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -178,21 +178,21 @@ def test_finalize_options(self): # 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']) 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: [PATCH 2184/2594] 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 87cceee22d..090eacfb2c 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -187,7 +187,8 @@ def test_finalize_options(self): 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 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: [PATCH 2185/2594] 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 198cce3257..b71cc983be 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -159,7 +159,8 @@ def test_finalize_options(self): 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 From 8b33fb69465d080b685d431e4ea0b8aeb17e84b5 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Thu, 23 Feb 2012 10:45:48 -0500 Subject: [PATCH 2186/2594] version now 3.1.5rc1 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index a3e2993648..0e427624b8 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.1.4" +__version__ = "3.1.5rc1" #--end constants-- From eb324661c378f36df356215c350772111a978ef4 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Thu, 23 Feb 2012 10:52:17 -0500 Subject: [PATCH 2187/2594] bump to 2.7.3rc1 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index a849f1a9cd..83126f679f 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "2.7.2" +__version__ = "2.7.3rc1" #--end constants-- From 2181299388e93cb98ddd887f4bf9fdd0c6caeeb5 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Thu, 23 Feb 2012 10:55:57 -0500 Subject: [PATCH 2188/2594] 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 beb65bb568..279e17a9df 100644 --- a/__init__.py +++ b/__init__.py @@ -22,5 +22,5 @@ # #--start constants-- -__version__ = "2.6.7" +__version__ = "2.6.8rc1" #--end constants-- From 500a248b355d0e418e8fc58f787ba47cab97c2eb Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Thu, 23 Feb 2012 21:14:12 +0100 Subject: [PATCH 2189/2594] 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 9ec6165864..369750551c 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.2.2" +__version__ = "3.2.3rc1" #--end constants-- 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: [PATCH 2190/2594] 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 5540b39a38..2f1c457ea0 100644 --- a/filelist.py +++ b/filelist.py @@ -210,6 +210,7 @@ def include_pattern(self, pattern, anchor=1, prefix=None, is_regex=0): 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: [PATCH 2191/2594] 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 5540b39a38..2f1c457ea0 100644 --- a/filelist.py +++ b/filelist.py @@ -210,6 +210,7 @@ def include_pattern(self, pattern, anchor=1, prefix=None, is_regex=0): 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: [PATCH 2192/2594] 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 91220321a4..db3f7a9680 100644 --- a/filelist.py +++ b/filelist.py @@ -201,6 +201,7 @@ def include_pattern(self, pattern, anchor=1, prefix=None, is_regex=0): 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: [PATCH 2193/2594] 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 0bba363557..595824367e 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -379,16 +379,28 @@ def run (self): 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 25a5763a72..37d89155c7 100644 --- a/tests/test_bdist_rpm.py +++ b/tests/test_bdist_rpm.py @@ -79,6 +79,10 @@ def test_quiet(self): 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 @@ def test_no_optimize_flag(self): 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(): 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: [PATCH 2194/2594] 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 5a22a10ec8..3378f49ea0 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 @@ 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 @@ def test_simple_built(self): # 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 ebfb04f446..f17baa1e50 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -86,19 +86,17 @@ def _expanduser(path): 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 @@ def _test_user_site(self): # 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 @@ def _test_user_site(self): 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 @@ def test_finalize_options(self): 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 @@ def test_record(self): 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 @@ 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) @@ -215,7 +213,7 @@ def test_record_extensions(self): 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 @@ def test_debug_mode(self): 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 9e422fca17..3a89f73fa2 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -6,6 +6,7 @@ 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 @@ 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 @@ def test_make_distribution_owner_group(self): # 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 @@ def _test_template(self, content): 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): 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: [PATCH 2195/2594] 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 fd71dac8d6..1ba2a1a6a9 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -6,6 +6,7 @@ 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 @@ 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 @@ def test_get_file_list(self): # 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() From a38d0a2e44526c9a32aceca07fd7d09ac100a62f Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 4 Mar 2012 16:23:53 +0100 Subject: [PATCH 2196/2594] Bump to 3.3.0a1. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index f883916f10..8b4261aa3c 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.3a0" +__version__ = "3.3.0a1" #--end constants-- 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: [PATCH 2197/2594] =?UTF-8?q?Make=20distutils=E2=80=99=20upload=20?= =?UTF-8?q?command=20work=20with=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 b3cfe9ceff..fde0f63fea 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -260,7 +260,7 @@ def run(self): 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 9308c79d91..9cbfb0c5c4 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) 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: [PATCH 2198/2594] =?UTF-8?q?Make=20distutils=E2=80=99=20upload=20?= =?UTF-8?q?command=20work=20with=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 703f873b16..09c6a89851 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -262,7 +262,7 @@ def run (self): 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 1c897ab04d..f175ed442b 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) 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: [PATCH 2199/2594] 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 f175ed442b..82a20cf1ff 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 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: [PATCH 2200/2594] 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 9cbfb0c5c4..e599461070 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 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: [PATCH 2201/2594] 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 fde0f63fea..b3cfe9ceff 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -260,7 +260,7 @@ def run(self): 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 e599461070..15d8bdff2b 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 @@ def test_minimal(self): 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(): 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: [PATCH 2202/2594] 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 09c6a89851..703f873b16 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -262,7 +262,7 @@ def run (self): 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 82a20cf1ff..f98b7a2199 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 @@ def test_minimal(self): 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(): From 52a047831e0c60eda6bafeed22ea1996f4d7f752 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Thu, 15 Mar 2012 12:25:54 -0500 Subject: [PATCH 2203/2594] bump to 2.7.3rc2 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 83126f679f..a137950efb 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "2.7.3rc1" +__version__ = "2.7.3rc2" #--end constants-- From da495dc219c9185da495363c1bddabd93659b17f Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Thu, 15 Mar 2012 13:57:27 -0500 Subject: [PATCH 2204/2594] bump to 3.1.5rc2 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 0e427624b8..6ab1662570 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.1.5rc1" +__version__ = "3.1.5rc2" #--end constants-- From 525cc3106994d2dd8e87f1eb28c2faadf0032b64 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Sat, 17 Mar 2012 18:19:15 -0400 Subject: [PATCH 2205/2594] Bump to 2.6.8rc2 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 279e17a9df..141b377b94 100644 --- a/__init__.py +++ b/__init__.py @@ -22,5 +22,5 @@ # #--start constants-- -__version__ = "2.6.8rc1" +__version__ = "2.6.8rc2" #--end constants-- From e29e39376dd2de7a9aa9ae03a6b92409489a5856 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 18 Mar 2012 07:34:49 +0100 Subject: [PATCH 2206/2594] Bump to 3.2.3rc2. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 369750551c..d8d61c5432 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.2.3rc1" +__version__ = "3.2.3rc2" #--end constants-- From ca25aeb8ebcee9808e9f2ead00586a1907a21f1b Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 1 Apr 2012 13:49:21 +0200 Subject: [PATCH 2207/2594] Bump to 3.3.0a2. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 8b4261aa3c..661a8b6df4 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.3.0a1" +__version__ = "3.3.0a2" #--end constants-- From 54af65c686e1b04ce8d83643ff3232c9043ad335 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Fri, 6 Apr 2012 13:17:25 -0400 Subject: [PATCH 2208/2594] 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 6ab1662570..d79bfa1315 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.1.5rc2" +__version__ = "3.1.5" #--end constants-- From d4855653e3edb3801094088e0c2b1514b02b6b86 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Mon, 9 Apr 2012 19:04:04 -0400 Subject: [PATCH 2209/2594] 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 a137950efb..036062cc33 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "2.7.3rc2" +__version__ = "2.7.3" #--end constants-- From c2f6398480d1b115bb8436ba7b5fba0e64de251a Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Tue, 10 Apr 2012 10:59:35 -0400 Subject: [PATCH 2210/2594] Bump to 2.6.8 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 141b377b94..e5008c9717 100644 --- a/__init__.py +++ b/__init__.py @@ -22,5 +22,5 @@ # #--start constants-- -__version__ = "2.6.8rc2" +__version__ = "2.6.8" #--end constants-- From ad8c62a6235689d9f30f55dbb3a7547ba9961d89 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 10 Apr 2012 19:28:09 +0200 Subject: [PATCH 2211/2594] 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 d8d61c5432..b52a9fe6c4 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.2.3rc2" +__version__ = "3.2.3" #--end constants-- From 912d0ff09025b61aec9937350e809ebeeaca61f8 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 1 May 2012 09:35:18 +0200 Subject: [PATCH 2212/2594] Bump to 3.3.0a3. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 661a8b6df4..d39d82eea1 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.3.0a2" +__version__ = "3.3.0a3" #--end constants-- From 12525d2ac00aeffb77cc20ffbc37f58fd086330a Mon Sep 17 00:00:00 2001 From: Brian Curtin Date: Sun, 13 May 2012 11:19:23 -0500 Subject: [PATCH 2213/2594] 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 59d0cd2df2..ac37c1320a 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -197,7 +197,7 @@ def finalize_options(self): # 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 0000000000000000000000000000000000000000..6fa0dce16315854223a9fefc3ac9f3cf71730a6a GIT binary patch literal 222208 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�?!H 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 N70sMIW8OSN{vUE=L&yLC literal 0 HcmV?d00001 diff --git a/command/wininst-10.0.exe b/command/wininst-10.0.exe new file mode 100644 index 0000000000000000000000000000000000000000..afc3bc6c14847283f32f53327184c6c2806efe0a GIT binary patch literal 190976 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

OPEHI2%PD3L3F4K9SA@F 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 literal 0 HcmV?d00001 From 0863f8d2a11e722a31624503f028fca44eb19ee6 Mon Sep 17 00:00:00 2001 From: Vinay Sajip Date: Sat, 26 May 2012 03:45:29 +0100 Subject: [PATCH 2214/2594] 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 16902ca920..977962f748 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -18,6 +18,8 @@ # 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 @@ # 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') From 0c18116bf577816e509ed9d150d36a5a9eab36f3 Mon Sep 17 00:00:00 2001 From: Vinay Sajip Date: Sat, 26 May 2012 20:36:12 +0100 Subject: [PATCH 2215/2594] 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 977962f748..59315f7279 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: From da98f1c9ec9114743a3cacfad74a5ec37ac2281e Mon Sep 17 00:00:00 2001 From: Vinay Sajip Date: Sun, 27 May 2012 17:30:09 +0100 Subject: [PATCH 2216/2594] 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 59315f7279..e86cb23236 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -50,6 +50,8 @@ def _is_python_source_dir(d): 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) From 54589c574b5f5acf9ed54dac1e1d0915cb5dc1f0 Mon Sep 17 00:00:00 2001 From: Nick Coghlan Date: Mon, 28 May 2012 22:34:46 +1000 Subject: [PATCH 2217/2594] 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 357eaa575e..401bc41872 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -190,7 +190,7 @@ def finalize_options(self): 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 @@ def run(self): 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)]) From 844216fd4e4c528b8064a750ee0ab99f5e987894 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Wed, 30 May 2012 22:04:31 +0200 Subject: [PATCH 2218/2594] 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 d39d82eea1..0da0d849f1 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.3.0a3" +__version__ = "3.3.0a4" #--end constants-- From e562c287d7ac8c6e0f1d3642db817f049a7a6dd6 Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Sat, 23 Jun 2012 16:02:19 -0700 Subject: [PATCH 2219/2594] 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 e86cb23236..dac3035f1b 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 c70a3cc555..5d45faa741 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 From 4e6c91d7daf073463c531f79dbd4f4bf4b6898b7 Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Mon, 25 Jun 2012 15:52:24 -0400 Subject: [PATCH 2220/2594] 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 401bc41872..ac4621791d 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 @@ def _make_spec_file(self): '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!) From 998406eedde8ea6686e42f77eb0e7c82486c874f Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 26 Jun 2012 09:43:40 +0200 Subject: [PATCH 2221/2594] 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 0da0d849f1..7a7b8d6cb1 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.3.0a4" +__version__ = "3.3.0b1" #--end constants-- From 2b4009c75e8ca5cb092424a21a53cbf2e231ea8d Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Tue, 26 Jun 2012 14:06:23 -0400 Subject: [PATCH 2222/2594] 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 401bc41872..ac4621791d 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 @@ def _make_spec_file(self): '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!) From 921b93fec344e0f75c80eff3cc8754215669f210 Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Fri, 29 Jun 2012 01:05:26 +0200 Subject: [PATCH 2223/2594] 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 4926aa3e15..8b36851d25 100644 --- a/command/upload.py +++ b/command/upload.py @@ -125,7 +125,7 @@ def upload_file(self, command, pyversion, filename): 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') From f1f3ff37a4e88ffa90c3821bfc18db2009e1ed3b Mon Sep 17 00:00:00 2001 From: "doko@ubuntu.com" Date: Sat, 30 Jun 2012 20:42:45 +0200 Subject: [PATCH 2224/2594] - 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 bce840274d..9833bf95c0 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. 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: [PATCH 2225/2594] 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 9c5cf337c6..5026e24668 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 693f77cf64..d82d9133d0 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -101,6 +101,24 @@ def test_copy_tree_verbosity(self): 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 3a89f73fa2..7e7d98d096 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -91,9 +91,8 @@ def get_cmd(self, metadata=None): @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 @@ def test_prune_file_list(self): 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() 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: [PATCH 2226/2594] 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 afa403f2da..9d8b30ea30 100644 --- a/config.py +++ b/config.py @@ -42,7 +42,7 @@ def _get_rc_file(self): 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: 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: [PATCH 2227/2594] 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 819e1a97be..0bdd539c37 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) From 14ab73a69d74f3ed8ba1dc8ce347271845899785 Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Sun, 15 Jul 2012 21:30:03 -0700 Subject: [PATCH 2228/2594] 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 dac3035f1b..f6e5d99909 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 5d45faa741..c70a3cc555 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 From cef0183bed891da1a3ec6e17083d5325141923b7 Mon Sep 17 00:00:00 2001 From: Vinay Sajip Date: Mon, 16 Jul 2012 18:24:55 +0100 Subject: [PATCH 2229/2594] 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 f6e5d99909..409ef74ca1 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": From 7e4558cfe24f0fb019e741f5da76cfc6e7cdc40f Mon Sep 17 00:00:00 2001 From: Vinay Sajip Date: Mon, 16 Jul 2012 18:30:03 +0100 Subject: [PATCH 2230/2594] 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 ac37c1320a..f16e2f17ef 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -8,6 +8,7 @@ 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 @@ def finalize_options(self): # 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': From 1f77c7ddcadb51accf01d3db76f23ec158a44256 Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Sat, 21 Jul 2012 05:36:30 -0700 Subject: [PATCH 2231/2594] 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 409ef74ca1..910e1047ab 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 1a06d4c4a1..eac9b5141d 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -13,6 +13,7 @@ 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 @@ def test_get_platform(self): ('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 @@ def test_get_platform(self): 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 @@ def test_get_platform(self): 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 @@ def test_get_platform(self): 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 @@ def test_get_platform(self): 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 c70a3cc555..094a2f0bd0 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -23,6 +23,9 @@ 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 @@ # 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 @@ def preprocess(self, source, output_file=None, macros=None, 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 @@ def link(self, target_desc, objects, 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 9833bf95c0..67d8166349 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) From a0418246ba7379453fadeb07a77c10770751f25e Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Sun, 22 Jul 2012 02:56:36 -0700 Subject: [PATCH 2232/2594] 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 fbe26bf65d..545ef3b548 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -102,7 +102,27 @@ 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('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')) From 1f80bd3ac6984d9941f06f1290639fc9572d59ed Mon Sep 17 00:00:00 2001 From: Richard Oudkerk Date: Fri, 27 Jul 2012 12:06:55 +0100 Subject: [PATCH 2233/2594] 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 910e1047ab..317640ca89 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 545ef3b548..546bb721a4 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -53,6 +53,34 @@ def test_get_config_vars(self): 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 From cd406fc66618543efe51a4867f8b0ecfe90c9a3e Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Fri, 27 Jul 2012 23:37:04 -0700 Subject: [PATCH 2234/2594] 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 546bb721a4..826ea4247d 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -67,7 +67,8 @@ def test_srcdir(self): 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 From 8a0aec34b9d22cd514bcc2404b3cab7242d90a79 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 11 Aug 2012 08:49:20 +0200 Subject: [PATCH 2235/2594] Bump to 3.3b2. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 7a7b8d6cb1..15042770d9 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.3.0b1" +__version__ = "3.3.0b2" #--end constants-- From 1cfadca0b0482f1f0ab379c7427dff4b627cec32 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 25 Aug 2012 12:16:37 +0200 Subject: [PATCH 2236/2594] Bump to 3.3.0rc1. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 15042770d9..8d73b51cbe 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.3.0b2" +__version__ = "3.3.0rc1" #--end constants-- From f0e94e21399229808cd34bea8cba5175f14ab07d Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 9 Sep 2012 08:56:46 +0200 Subject: [PATCH 2237/2594] Bump to 3.3.0rc2. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 8d73b51cbe..f23d886420 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.3.0rc1" +__version__ = "3.3.0rc2" #--end constants-- From db43cb973d253a6e01945b61c92f6161921d7ce2 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 23 Sep 2012 17:15:21 +0200 Subject: [PATCH 2238/2594] Bump to 3.3.0rc3. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index f23d886420..b755eaaf2c 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.3.0rc2" +__version__ = "3.3.0rc3" #--end constants-- From a5609a81b25425fdff1ac8aab120d8d6a4313f8b Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 29 Sep 2012 09:04:54 +0200 Subject: [PATCH 2239/2594] 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 b755eaaf2c..345ac4f8dd 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.3.0rc3" +__version__ = "3.3.0" #--end constants-- From ff6785063112982ccd38892d21859b8a8a45318a Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 29 Sep 2012 09:34:13 +0200 Subject: [PATCH 2240/2594] 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 345ac4f8dd..70a72b0967 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.3.0" +__version__ = "3.4.0a0" #--end constants-- From 495e4bf6c1029b10cab8fa83ce28caf7011d0955 Mon Sep 17 00:00:00 2001 From: Jesus Cea Date: Thu, 11 Oct 2012 01:20:12 +0200 Subject: [PATCH 2241/2594] 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 c795c958fe..911e84dd3b 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -351,7 +351,7 @@ def _setup_compile(self, outdir, macros, incdirs, sources, depends, 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 @@ def mkpath (self, name, mode=0o777): # 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 @@ def get_default_compiler(osname=None, platform=None): "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 c5188eb371..38b169afd1 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 1ab09d1616..eefdfea5ad 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 @@ def run(self): 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 f16e2f17ef..6b6a04e8bf 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -223,11 +223,6 @@ def finalize_options(self): 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 @@ def find_swig(self): 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 @@ def get_ext_filename(self, ext_name): """ 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 @@ def get_export_symbols(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). + 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 @@ def get_libraries(self, ext): 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 0161898f49..04326a1731 100644 --- a/command/install.py +++ b/command/install.py @@ -58,13 +58,6 @@ '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 @@ '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 3675f8df9c..0000000000 --- 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 f58c55f902..b1c5a442a5 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 317640ca89..3b6549fccc 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 1037d8216e..761051c155 100644 --- a/tests/test_bdist_dumb.py +++ b/tests/test_bdist_dumb.py @@ -75,8 +75,6 @@ def test_simple_built(self): # 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 cb2e1f2879..47d630ccc5 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -94,7 +94,7 @@ def cleanup(): 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 eac9b5141d..b73cfe9424 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -236,7 +236,7 @@ def _join(*path): 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 67d8166349..ba09ac450a 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) From 285e4e10ef6678f0b5bdb61ff5c183dd6360a4af Mon Sep 17 00:00:00 2001 From: Vinay Sajip Date: Tue, 23 Oct 2012 20:26:14 +0100 Subject: [PATCH 2242/2594] 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 f16e2f17ef..b1d951e6f8 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -160,6 +160,11 @@ def finalize_options(self): 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 @@ def finalize_options(self): # 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: 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: [PATCH 2243/2594] 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 30daf49a6e..2826ff805d 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 ce74589dde..1589f1297d 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -76,7 +76,6 @@ def test_create_tree_verbosity(self): remove_tree(self.root_target, verbose=0) - def test_copy_tree_verbosity(self): mkpath(self.target, verbose=0) @@ -88,11 +87,8 @@ def test_copy_tree_verbosity(self): 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 @@ def test_copy_tree_verbosity(self): 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 1ba2a1a6a9..e6359d6a8a 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -83,9 +83,8 @@ def get_cmd(self, metadata=None): @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 @@ def test_prune_file_list(self): 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() 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: [PATCH 2244/2594] Remove code unneeded after f833e7ec4de1 --- config.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/config.py b/config.py index 9d8b30ea30..1d327143be 100644 --- a/config.py +++ b/config.py @@ -47,11 +47,6 @@ def _store_pypirc(self, username, password): 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.""" 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: [PATCH 2245/2594] 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 5b625f3f7d..1fd53346e9 100644 --- a/config.py +++ b/config.py @@ -4,7 +4,6 @@ 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 @@ def _get_rc_file(self): 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.""" 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: [PATCH 2246/2594] 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 4b64e458bc..152bf0de98 100644 --- a/command/check.py +++ b/command/check.py @@ -26,6 +26,9 @@ def __init__(self, source, report_level, halt_level, stream=None, 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 aa9bc43c5c..9a0ff34c45 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 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 @@ def test_strict(self): 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() 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: [PATCH 2247/2594] 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 9a0ff34c45..4f34b18bd8 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -194,6 +194,7 @@ def test_password_reset(self): 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 @@ def test_strict(self): 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é', 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: [PATCH 2248/2594] 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 b67c795308..22b9349dd6 100644 --- a/command/check.py +++ b/command/check.py @@ -23,6 +23,9 @@ def __init__(self, source, report_level, halt_level, stream=None, 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 5863ae1422..a86b8606e4 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 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 @@ def test_password_reset(self): 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 @@ def test_strict(self): 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 @@ def test_strict(self): 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() From c24868ba84f1b66d88d6b1f5a3f9190bcd2ca5c5 Mon Sep 17 00:00:00 2001 From: Andrew Svetlov Date: Tue, 18 Dec 2012 21:14:22 +0200 Subject: [PATCH 2249/2594] 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 5fa1ca10d4..301d43d20c 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 From 75cf27d5a437064a0bfcf9f7e8ab7bc501260b61 Mon Sep 17 00:00:00 2001 From: Andrew Svetlov Date: Tue, 18 Dec 2012 21:27:37 +0200 Subject: [PATCH 2250/2594] 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 73470729fd..2d94a1117e 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 From ca8d16926ecb9629ca53aa93327c0bdd2a144f5c Mon Sep 17 00:00:00 2001 From: Andrew Svetlov Date: Tue, 18 Dec 2012 22:02:39 +0200 Subject: [PATCH 2251/2594] 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 260332a2ac..a43d72588b 100644 --- a/core.py +++ b/core.py @@ -148,7 +148,7 @@ class found in 'cmdclass' is used in place of the default, which is 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 2826ff805d..7d1c78ab29 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 9bdd14e42e..f6ed290f13 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: " From 437e5b731feb5f5b0e7205ff94d15c07c32e83a4 Mon Sep 17 00:00:00 2001 From: Andrew Svetlov Date: Tue, 18 Dec 2012 23:10:48 +0200 Subject: [PATCH 2252/2594] 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 8b36851d25..88990b2c6b 100644 --- a/command/upload.py +++ b/command/upload.py @@ -186,7 +186,7 @@ def upload_file(self, command, pyversion, filename): 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 From 8b21b15c3cebd6cf09d1ceb8abf91f0ebbafa499 Mon Sep 17 00:00:00 2001 From: Andrew Svetlov Date: Tue, 25 Dec 2012 16:47:37 +0200 Subject: [PATCH 2253/2594] 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 4b5b22ec20..90a8380a04 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -74,7 +74,7 @@ def copy_scripts(self): # 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 a43d72588b..91e5132934 100644 --- a/core.py +++ b/core.py @@ -148,7 +148,7 @@ class found in 'cmdclass' is used in place of the default, which is 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 0bdd539c37..0c23ab1b7f 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 7d1c78ab29..2b35aa318e 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 eb13c983e9..8b93059e19 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 b3f6ce10a8..9688f20019 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -729,7 +729,7 @@ def _remove_visual_c_ref(self, manifest_file): return manifest_file finally: manifest_f.close() - except IOError: + except OSError: pass # -- Miscellaneous methods ----------------------------------------- diff --git a/sysconfig.py b/sysconfig.py index 3b6549fccc..91ed1a498c 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 ba09ac450a..a2f9544519 100644 --- a/util.py +++ b/util.py @@ -207,8 +207,8 @@ def _subst (match, local_vars=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 From 2e7e993256b1f737cd3ddec9a1cfc205455b2be2 Mon Sep 17 00:00:00 2001 From: "doko@python.org" Date: Fri, 25 Jan 2013 14:33:33 +0100 Subject: [PATCH 2254/2594] - 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 317640ca89..d125e0b44a 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -24,7 +24,11 @@ # 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 From 0ad57ca49e9ca519a62ae5da00d2217ba91f464e Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 27 Jan 2013 19:45:49 +0200 Subject: [PATCH 2255/2594] - 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 b71cc983be..f6a503b538 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -77,8 +77,9 @@ def test_build_ext(self): 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)) From 28dd71b74f49f8abf4f65fb05bb29fbc00f4e574 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 27 Jan 2013 19:47:45 +0200 Subject: [PATCH 2256/2594] 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 090eacfb2c..065a6a21c9 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -73,8 +73,9 @@ def test_build_ext(self): 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)) From 30f6c6e37e64b06ccca07acb169bf517687ba2ec Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Thu, 31 Jan 2013 01:24:55 -0800 Subject: [PATCH 2257/2594] 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 4b193b2703..daa4dc77ca 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 49570c4ce5..c064d2b0f7 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -72,6 +72,35 @@ def test_parse_makefile_literal_dollar(self): '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 c49ac9ba91..2aa1cb1d27 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -26,6 +26,9 @@ 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 @@ # 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 @@ def preprocess(self, source, 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 @@ def link(self, target_desc, objects, 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 0c24e8ca3e..5279411294 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) From 2374a4ccde18f6e6f3f20a7279dc0b1a7a2cf89d Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Thu, 31 Jan 2013 01:28:23 -0800 Subject: [PATCH 2258/2594] 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 16902ca920..b6007a904f 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 fbe26bf65d..545ef3b548 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -102,7 +102,27 @@ 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('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 1a06d4c4a1..eac9b5141d 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -13,6 +13,7 @@ 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 @@ def test_get_platform(self): ('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 @@ def test_get_platform(self): 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 @@ def test_get_platform(self): 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 @@ def test_get_platform(self): 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 @@ def test_get_platform(self): 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 c70a3cc555..094a2f0bd0 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -23,6 +23,9 @@ 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 @@ # 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 @@ def preprocess(self, source, output_file=None, macros=None, 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 @@ def link(self, target_desc, objects, 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 bce840274d..52280db0b2 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) From 9c2f75cbc7aeb61b3a2aad135472c72cfbc1dd44 Mon Sep 17 00:00:00 2001 From: "doko@python.org" Date: Thu, 31 Jan 2013 23:52:03 +0100 Subject: [PATCH 2259/2594] - 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 daa4dc77ca..250ef38bed 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -37,6 +37,11 @@ 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 5279411294..ea6ed8a9ac 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. 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: [PATCH 2260/2594] 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 7076b93394..4907a0aa5a 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -17,6 +17,8 @@ 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 From 9ec4ed534ba8d93a5de4a97267807819fbdf1104 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Mon, 11 Mar 2013 17:56:17 -0400 Subject: [PATCH 2261/2594] 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 f1f3bd5c6f..b9f1c6c566 100644 --- a/command/install.py +++ b/command/install.py @@ -265,8 +265,8 @@ def finalize_options (self): 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 f17baa1e50..2996161797 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -166,7 +166,7 @@ def test_finalize_options(self): 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' From 6b2cfd3d4e003b9a39b2b3a89a150bce9ecdc250 Mon Sep 17 00:00:00 2001 From: Terry Jan Reedy Date: Mon, 11 Mar 2013 17:57:08 -0400 Subject: [PATCH 2262/2594] 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 0161898f49..9b1c36af82 100644 --- a/command/install.py +++ b/command/install.py @@ -278,8 +278,8 @@ def finalize_options(self): 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 cb2e1f2879..1bd31e2469 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -165,7 +165,7 @@ def test_finalize_options(self): 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' From 5bbae64025d2dd4378bc28a08f311a61c6478565 Mon Sep 17 00:00:00 2001 From: Ezio Melotti Date: Sat, 16 Mar 2013 19:48:51 +0200 Subject: [PATCH 2263/2594] #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 1037d8216e..0ad32d421e 100644 --- a/tests/test_bdist_dumb.py +++ b/tests/test_bdist_dumb.py @@ -88,9 +88,9 @@ def test_simple_built(self): 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(): From a72d9ae0b5ea2ad5fd4aed4b3745776bede75c7b Mon Sep 17 00:00:00 2001 From: Ezio Melotti Date: Sat, 16 Mar 2013 20:04:44 +0200 Subject: [PATCH 2264/2594] #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 3378f49ea0..5db3a850f8 100644 --- a/tests/test_bdist_dumb.py +++ b/tests/test_bdist_dumb.py @@ -87,8 +87,9 @@ def test_simple_built(self): 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): From 0c09bf1bc035dfbc0caf1339c05a04cca238fcd6 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Mon, 18 Mar 2013 15:20:56 -0700 Subject: [PATCH 2265/2594] 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 1fd53346e9..7439a83a4f 100644 --- a/config.py +++ b/config.py @@ -21,7 +21,7 @@ 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 @@ def _read_pypirc(self): 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 525bee9416..12593610aa 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -87,7 +87,7 @@ def test_server_registration(self): 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 @@ def test_server_registration(self): 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 4c6464a32e..d2696866fe 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 @@ def test_finalize_options(self): 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): From 156e81825b9ee6d636f84ffdddd25e952347678b Mon Sep 17 00:00:00 2001 From: "doko@ubuntu.com" Date: Thu, 21 Mar 2013 13:21:49 -0700 Subject: [PATCH 2266/2594] - 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 34b61bdb82..64f634caed 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -667,10 +667,10 @@ def get_ext_filename(self, ext_name): 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 b6007a904f..dec37f8be1 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 065a6a21c9..44a9852f4c 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -318,8 +318,8 @@ def test_get_outputs(self): 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 @@ def test_get_outputs(self): 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 @@ def test_get_outputs(self): 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 1bd31e2469..b1901273e9 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -23,7 +23,7 @@ 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, From 98057644b0bde93fc52346011633276b87ccb3a4 Mon Sep 17 00:00:00 2001 From: "doko@ubuntu.com" Date: Thu, 21 Mar 2013 15:02:16 -0700 Subject: [PATCH 2267/2594] - 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 250ef38bed..0c726d92ff 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -387,66 +387,11 @@ def expand_makefile_vars(s, vars): 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(): From c02b720fc0e7ec3db46e6fcb61b680be7c18fbff Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Fri, 22 Mar 2013 09:37:13 -0500 Subject: [PATCH 2268/2594] 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 0c726d92ff..250ef38bed 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -387,11 +387,66 @@ def expand_makefile_vars(s, vars): 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(): From 449644ec384bd9ec0e8066bf54a22d19f162977f Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 23 Mar 2013 16:02:08 +0100 Subject: [PATCH 2269/2594] Bump to 3.2.4rc1. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index b52a9fe6c4..834d89671b 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.2.3" +__version__ = "3.2.4rc1" #--end constants-- From b262efd97d7ac908bbefc87ad8e5eee19ea8f47a Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 23 Mar 2013 16:05:12 +0100 Subject: [PATCH 2270/2594] Bump to 3.3.1rc1. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 345ac4f8dd..c30da0bb41 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.3.0" +__version__ = "3.3.1rc1" #--end constants-- From 447a41bf99cf4b4dd10f41fc91a1ef7d1a94552c Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 23 Mar 2013 10:17:29 -0500 Subject: [PATCH 2271/2594] version to 2.7.4rc1 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 036062cc33..15b511bee5 100644 --- a/__init__.py +++ b/__init__.py @@ -15,5 +15,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "2.7.3" +__version__ = "2.7.4rc1" #--end constants-- From 51ce4bbdaea029fbf434a0df91c7e0c0e46eab1b Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 6 Apr 2013 09:36:20 +0200 Subject: [PATCH 2272/2594] Bump to 3.2.4. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 834d89671b..b8f1c164c0 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.2.4rc1" +__version__ = "3.2.4" #--end constants-- From d6653309f0330f1e4e239e58226640a70a5c2f5a Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 6 Apr 2013 09:40:02 +0200 Subject: [PATCH 2273/2594] Bump to 3.3.1. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index c30da0bb41..f8af1b336e 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.3.1rc1" +__version__ = "3.3.1" #--end constants-- From 036968d741e319006ddfcbeb48d5a22f5da574dc Mon Sep 17 00:00:00 2001 From: Ezio Melotti Date: Fri, 19 Apr 2013 04:23:09 +0300 Subject: [PATCH 2274/2594] 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 d2696866fe..4a71ca4a8d 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 From a5f153fd048ccd978ab90f54f940f46d77852f51 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 12 May 2013 12:28:20 +0200 Subject: [PATCH 2275/2594] 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 b8f1c164c0..f9016d6d17 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.2.4" +__version__ = "3.2.5" #--end constants-- From 590f1383d4eb948cafba8ff7bd63acad94531d19 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 12 May 2013 12:36:07 +0200 Subject: [PATCH 2276/2594] 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 a702568278..f7fac08918 100644 --- a/dist.py +++ b/dist.py @@ -343,6 +343,18 @@ def find_config_files(self): 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 @@ def parse_config_files(self, filenames=None): 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 8aaae88cae..66c20e27e2 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -6,6 +6,8 @@ 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 @@ def test_command_packages_cmdline(self): 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 @@ def test_custom_pydistutils(self): 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) From 7c5ce9920a18c33d93d4f8c7091db6b197dcbbe2 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 12 May 2013 12:51:38 +0200 Subject: [PATCH 2277/2594] bump to 3.3.2 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index f8af1b336e..83d7cb7f4c 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.3.1" +__version__ = "3.3.2" #--end constants-- From 8e4909b4686ce72b8cc7798927fd75b83f5bcbff Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Tue, 28 May 2013 16:35:30 -0700 Subject: [PATCH 2278/2594] 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 b73503d296..b9479889f4 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 1bff38e9ee..a5a63fdde3 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 @@ def gcv(v): 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 @@ def gcv(v): 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) From e2bc5bcf0766570d98e23ccbfaf77589491cc198 Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Thu, 13 Jun 2013 20:57:26 -0400 Subject: [PATCH 2279/2594] 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 fcda08e20a..ba74045dc0 100644 --- a/archive_util.py +++ b/archive_util.py @@ -9,7 +9,7 @@ try: import zipfile -except ImportError: +except ModuleNotFoundError: zipfile = None diff --git a/ccompiler.py b/ccompiler.py index 911e84dd3b..bc183fc5d1 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 f7fac08918..11f6ff8fbc 100644 --- a/dist.py +++ b/dist.py @@ -4,11 +4,11 @@ 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 @@ def get_command_class(self, command): 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 8116656961..9a94b41ad6 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -28,7 +28,7 @@ RegEnumValue = winreg.EnumValue RegError = winreg.error -except ImportError: +except ModuleNotFoundError: try: import win32api import win32con @@ -39,7 +39,7 @@ 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 a2f9544519..e241f59b94 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) From dc3634843234941af85a87b89458982f131e56a2 Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Sat, 15 Jun 2013 12:59:53 -0400 Subject: [PATCH 2280/2594] 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 1371b3d6ff..677723f0b1 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 @@ def get_outputs(self, include_bytecode=1): 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 15c08f1249..215813ba97 100644 --- a/command/install_lib.py +++ b/command/install_lib.py @@ -4,7 +4,7 @@ (install all Python modules).""" import os -import imp +import importlib.util import sys from distutils.core import Command @@ -165,10 +165,10 @@ def _bytecode_filenames(self, py_filenames): 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 73061668ae..c8ccdc2383 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 @@ def test_simple_built(self): 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 e416edd4a1..1b410c397d 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 @@ def test_package_data(self): 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 @@ def test_byte_compile(self): 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 @@ def test_byte_compile_optimized(self): 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 e9b2642732..42bd269bf4 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 @@ def test_record(self): 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 2bd4dc6e96..dbb3e9a67e 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 @@ def test_byte_compile(self): 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 e241f59b94..257de68e18 100644 --- a/util.py +++ b/util.py @@ -6,7 +6,7 @@ import os import re -import imp +import importlib.util import sys import string from distutils.errors import DistutilsPlatformError @@ -453,9 +453,10 @@ def byte_compile (py_files, # 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: From 32a9bf9e2e195447cb3d995e9d8778e8a8d5573e Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Thu, 4 Jul 2013 17:43:24 -0400 Subject: [PATCH 2281/2594] 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 ba74045dc0..fcda08e20a 100644 --- a/archive_util.py +++ b/archive_util.py @@ -9,7 +9,7 @@ try: import zipfile -except ModuleNotFoundError: +except ImportError: zipfile = None diff --git a/ccompiler.py b/ccompiler.py index bc183fc5d1..911e84dd3b 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 11f6ff8fbc..f7fac08918 100644 --- a/dist.py +++ b/dist.py @@ -4,11 +4,11 @@ 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 @@ def get_command_class(self, command): 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 9a94b41ad6..8116656961 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -28,7 +28,7 @@ RegEnumValue = winreg.EnumValue RegError = winreg.error -except ModuleNotFoundError: +except ImportError: try: import win32api import win32con @@ -39,7 +39,7 @@ 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 257de68e18..efb3834cf5 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) From 814117ef8e4aa624fc497ce1c47040f68d07daba Mon Sep 17 00:00:00 2001 From: Larry Hastings Date: Sat, 3 Aug 2013 12:58:12 -0700 Subject: [PATCH 2282/2594] 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 70a72b0967..f4076d7484 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.4.0a0" +__version__ = "3.4.0a1" #--end constants-- From 82af3811567b3e375d91163f5a0a706b2ecdc9c3 Mon Sep 17 00:00:00 2001 From: Ezio Melotti Date: Sat, 17 Aug 2013 16:11:40 +0300 Subject: [PATCH 2283/2594] =?UTF-8?q?#18741:=20fix=20more=20typos.=20=20Pa?= =?UTF-8?q?tch=20by=20F=C3=A9vry=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 9b1c36af82..3c675d1182 100644 --- a/command/install.py +++ b/command/install.py @@ -545,7 +545,7 @@ def handle_extra_path(self): 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 a9429a4296..116f67ef9b 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -175,7 +175,7 @@ def get_file_list(self): 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 69bd2bf624..ee1c04162b 100644 --- a/tests/test_build_clib.py +++ b/tests/test_build_clib.py @@ -77,7 +77,7 @@ def compile(*args, **kw): 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) From 6200cf0997fe3b832b4fda03a4a1cc0d6347c151 Mon Sep 17 00:00:00 2001 From: Larry Hastings Date: Sat, 7 Sep 2013 23:42:07 +1200 Subject: [PATCH 2284/2594] 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 f4076d7484..3afd84fefb 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.4.0a1" +__version__ = "3.4.0a2" #--end constants-- From d30e05735221377ec2823a2b2942375194753d35 Mon Sep 17 00:00:00 2001 From: Larry Hastings Date: Sat, 28 Sep 2013 23:51:00 +0100 Subject: [PATCH 2285/2594] 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 3afd84fefb..cc18893517 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.4.0a2" +__version__ = "3.4.0a3" #--end constants-- From 6e02dbc3db2e648c23ecbbafc81dff296e8cd262 Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Sun, 29 Sep 2013 01:48:40 +0200 Subject: [PATCH 2286/2594] 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 1ad0d5ff58..bc6a23f1b6 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -247,11 +247,10 @@ def finalize_options(self): # 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')) From f67dd5be98be8295fb480eb9180b730f53b3e95d Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sun, 29 Sep 2013 11:13:27 -0400 Subject: [PATCH 2287/2594] 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 195045cc7b..cf5197c30f 100644 --- a/tests/test_cmd.py +++ b/tests/test_cmd.py @@ -34,6 +34,18 @@ def test_ensure_string_list(self): 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 @@ def test_ensure_string(self): 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__ From 5cc44b40db683dc8402c77d5c7c13e726a4f7dec Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Mon, 30 Sep 2013 22:28:10 +0200 Subject: [PATCH 2288/2594] 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 0bdd539c37..e0074a18f3 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -48,7 +48,7 @@ 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 @@ def __init__(self, verbose=0, dry_run=0, force=0): 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') From 1c073f36e3b5850a448d65d90c383a92ddce83ff Mon Sep 17 00:00:00 2001 From: Larry Hastings Date: Sun, 20 Oct 2013 02:01:29 -0700 Subject: [PATCH 2289/2594] 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 cc18893517..355542edd0 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.4.0a3" +__version__ = "3.4.0a4" #--end constants-- From 6302a7c47d5c17bd2b2b51e4e604e2c3dc9378c9 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 27 Oct 2013 09:22:59 +0100 Subject: [PATCH 2290/2594] Bump to 3.3.3rc1. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 83d7cb7f4c..812d8f35dc 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.3.2" +__version__ = "3.3.3rc1" #--end constants-- From bdb93d0d82c82df219172499741eb33e22da359e Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 2 Nov 2013 11:29:33 -0400 Subject: [PATCH 2291/2594] 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 e416edd4a1..2ce9d4492d 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -121,6 +121,37 @@ def test_byte_compile_optimized(self): 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] From 35249e2c9b4afa5baf6699573c3900e442f0910f Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 2 Nov 2013 11:07:35 -0400 Subject: [PATCH 2292/2594] 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 1371b3d6ff..d48eb69900 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -127,7 +127,8 @@ def find_data_files(self, package, src_dir): # 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): From 60e84ced7d203fd12f5784889aeb80343d33b9b6 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Mon, 4 Nov 2013 07:43:32 +0100 Subject: [PATCH 2293/2594] 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 2ce9d4492d..e416edd4a1 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -121,37 +121,6 @@ def test_byte_compile_optimized(self): 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] From abe7d6ae5236f5d11f76e9f3b8c4665dda6a29f1 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Mon, 4 Nov 2013 07:43:41 +0100 Subject: [PATCH 2294/2594] 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 d48eb69900..1371b3d6ff 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -127,8 +127,7 @@ def find_data_files(self, package, src_dir): # 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): From b5e1e144277c1b90052d1e29451877e599b00fbd Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 10 Nov 2013 20:28:18 -0500 Subject: [PATCH 2295/2594] 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 c640b2ac6d..7d704cf166 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -117,7 +117,7 @@ def test_upload(self): 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(): From 5f1980bbc9dc1da41e154a2ed0bf988d91c4c7d6 Mon Sep 17 00:00:00 2001 From: Andrew Kuchling Date: Fri, 15 Nov 2013 13:01:52 -0500 Subject: [PATCH 2296/2594] 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 fcda08e20a..306ef6a838 100644 --- a/archive_util.py +++ b/archive_util.py @@ -18,15 +18,55 @@ 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 3ea08101ac..c89d5efc45 100644 --- a/cmd.py +++ b/cmd.py @@ -365,9 +365,11 @@ def spawn(self, cmd, search_path=1, level=1): 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 38b169afd1..6814a1c382 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 @@ def initialize_options(self): 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 @@ def run(self): 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 eefdfea5ad..4405d12c05 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 @@ def initialize_options(self): 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 @@ def run(self): # 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 116f67ef9b..7ea3d5fa27 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -74,6 +74,10 @@ def checking_metadata(self): ('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 @@ def initialize_options(self): 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 @@ def make_distribution(self): 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 1afdd46225..581c0cc841 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -15,6 +15,13 @@ 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 @@ def _make_tarball(self, target_name): 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 @@ def _breaks(*args, **kw): 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 e6359d6a8a..6170a48fea 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -14,6 +14,12 @@ 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 @@ def test_manual_manifest(self): 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) From 53ae4bd7bad05c5717d8b713e57d41e90d47057c Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Fri, 15 Nov 2013 23:08:21 +0100 Subject: [PATCH 2297/2594] 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 306ef6a838..82046a8ad7 100644 --- a/archive_util.py +++ b/archive_util.py @@ -20,12 +20,12 @@ 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): From 7c572215c02f83b007a506953969b34d2bb9dd9a Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 15 Nov 2013 23:13:17 +0100 Subject: [PATCH 2298/2594] 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 82046a8ad7..4470bb0222 100644 --- a/archive_util.py +++ b/archive_util.py @@ -20,12 +20,12 @@ 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): From 11857584767cb29d5a3761c9a4ed00793f576164 Mon Sep 17 00:00:00 2001 From: Andrew Kuchling Date: Sun, 10 Nov 2013 18:11:00 -0500 Subject: [PATCH 2299/2594] 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 91e5132934..c811d5bd9c 100644 --- a/core.py +++ b/core.py @@ -128,8 +128,9 @@ class found in 'cmdclass' is used in place of the default, which is 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 11a210279e..7eb04bc3f5 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 @@ def __init__ (self, attrs=None): 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 @@ def find_config_files(self): 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 @@ def find_config_files(self): 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 9a8ca19902..102c553db9 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -39,6 +39,7 @@ def find_config_files(self): class DistributionTestCase(support.LoggingSilencer, + support.TempdirManager, support.EnvironGuard, unittest.TestCase): @@ -213,6 +214,34 @@ def test_announce(self): 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): From 5edd62fecb7444f731574a38a7108e002893dac7 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 10 Nov 2013 18:15:03 -0500 Subject: [PATCH 2300/2594] 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 f7fac08918..11a210279e 100644 --- a/dist.py +++ b/dist.py @@ -5,6 +5,7 @@ """ 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 66c20e27e2..9a8ca19902 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -8,7 +8,7 @@ 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 @@ def test_show_help(self): 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)) From 66f3b499473504c130a52d222b827a4413127f12 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 10 Nov 2013 18:50:10 -0500 Subject: [PATCH 2301/2594] 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 8b36851d25..d2bc82cea3 100644 --- a/command/upload.py +++ b/command/upload.py @@ -10,10 +10,9 @@ 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 @@ def run(self): 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 @@ def upload_file(self, command, pyversion, filename): 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 4c6464a32e..a474596e33 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 @@ [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 @@ def test_upload(self): 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) From 341dbe79bfe1e225ff5a05731eb56e69ac989bce Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Mon, 11 Nov 2013 06:13:54 +0100 Subject: [PATCH 2302/2594] Bump to 3.3.3rc2. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 812d8f35dc..0650dd277a 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.3.3rc1" +__version__ = "3.3.3rc2" #--end constants-- From 303e2b0c71421dc5a0fa03019354124305579fbd Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 15 Nov 2013 19:24:07 -0500 Subject: [PATCH 2303/2594] 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 6170a48fea..280b9bca90 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -471,10 +471,13 @@ def test_make_distribution_owner_group(self): # 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() From ccce5b21bbd52d95a00704f48f6cf3923cad9776 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 15 Nov 2013 19:35:05 -0500 Subject: [PATCH 2304/2594] 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 280b9bca90..164586b784 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -437,7 +437,7 @@ 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): + find_executable('gzip') is None): return # now building a sdist @@ -455,8 +455,8 @@ def test_make_distribution_owner_group(self): 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 @@ def test_make_distribution_owner_group(self): # rights (see #7408) try: for member in archive.getmembers(): - self.assertEquals(member.uid, os.getuid()) + self.assertEqual(member.uid, os.getuid()) finally: archive.close() From 59262e8d7386c303a4f651ade1b5105d2b3e0c3c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 15 Nov 2013 19:38:51 -0500 Subject: [PATCH 2305/2594] 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 102c553db9..b7fd3fbf90 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -240,7 +240,7 @@ def _expander(path): 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 @@ def test_read_metadata(self): 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() From 5960d28f32e53337043371f95c7f349e34024f40 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 15 Nov 2013 19:41:57 -0500 Subject: [PATCH 2306/2594] 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 581c0cc841..caa0db20b7 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -329,8 +329,8 @@ def test_tarfile_root_owner(self): 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 7d704cf166..9a2265eeb8 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -115,9 +115,9 @@ def test_upload(self): 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(): From f169cafe969605899f196b8dfb86b0385e0da5ff Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 16 Nov 2013 10:35:46 -0500 Subject: [PATCH 2307/2594] 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 9a2265eeb8..8e8ff720ae 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -114,11 +114,11 @@ def test_upload(self): # 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) From ad0f0c5c29e6c8af182a9e72d995d91911cc8349 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 16 Nov 2013 10:47:17 -0500 Subject: [PATCH 2308/2594] 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 8e8ff720ae..1fcba889b2 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -114,7 +114,8 @@ def test_upload(self): # 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) From c0d4c5676e3118c7189a97a555bfb70447276f29 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 17 Nov 2013 00:17:46 +0200 Subject: [PATCH 2309/2594] 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 1afdd46225..d3fb24ad56 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -210,7 +210,7 @@ def test_compress_deprecated(self): 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 b090b79e0c..aa1445d10a 100644 --- a/tests/test_bdist_rpm.py +++ b/tests/test_bdist_rpm.py @@ -81,7 +81,7 @@ def test_quiet(self): 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 @@ def test_no_optimize_flag(self): 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 f9e8f89e21..5d17ab19a9 100644 --- a/tests/test_bdist_wininst.py +++ b/tests/test_bdist_wininst.py @@ -22,7 +22,7 @@ def test_get_exe_bytes(self): # 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 ee1c04162b..c2b981f20e 100644 --- a/tests/test_build_clib.py +++ b/tests/test_build_clib.py @@ -137,7 +137,7 @@ def test_run(self): 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 44a9852f4c..3cfaa2c692 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -76,8 +76,8 @@ def test_build_ext(self): 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 @@ def test_solaris_enable_shared(self): _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 @@ def test_user_site(self): # 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 @@ def test_finalize_options(self): 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 @@ def test_check_extensions_list(self): '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 e3326b8517..954fc76398 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 @@ def test_build(self): 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 @@ def test_version_int(self): 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 eb8958bff5..b64f300a04 100644 --- a/tests/test_clean.py +++ b/tests/test_clean.py @@ -36,7 +36,7 @@ def test_simple_run(self): # 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 525bee9416..0e8d65e271 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -103,7 +103,7 @@ def test_server_registration(self): 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 e2e6e4ebaa..79894c78d3 100644 --- a/tests/test_config_cmd.py +++ b/tests/test_config_cmd.py @@ -81,7 +81,7 @@ def test_clean(self): 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 b1901273e9..ede88e5d90 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -236,7 +236,7 @@ def test_debug_mode(self): 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 2bd4dc6e96..d0dfca00d7 100644 --- a/tests/test_install_lib.py +++ b/tests/test_install_lib.py @@ -103,7 +103,7 @@ def test_dont_write_bytecode(self): 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 8952e744e5..1f7b1038cb 100644 --- a/tests/test_install_scripts.py +++ b/tests/test_install_scripts.py @@ -24,10 +24,10 @@ def test_default_settings(self): 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 @@ def write_script(name, text): 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 301d43d20c..5e18c61360 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -128,7 +128,7 @@ def test_reg_class(self): # 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 @@ def test_reg_class(self): 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 @@ def test_remove_entire_manifest(self): 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 a86b8606e4..f4efa13d78 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -98,7 +98,7 @@ def test_create_pypirc(self): 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 @@ def _no_way(prompt=''): 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 @@ def test_registering(self): 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 @@ def test_password_reset(self): 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 826ea4247d..07812d80fc 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -50,7 +50,7 @@ def test_get_python_inc(self): 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 eac9b5141d..00219cfdba 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -266,7 +266,7 @@ def test_strtobool(self): 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' From ab27da2853e1eb770873803bd6a8008e612ac796 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 17 Nov 2013 07:58:22 +0100 Subject: [PATCH 2310/2594] 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 0650dd277a..6747ab8677 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.3.3rc2" +__version__ = "3.3.3" #--end constants-- From b1290f15cc21206e9b16c91fa45cf4451137f732 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Fri, 22 Nov 2013 15:31:35 -0500 Subject: [PATCH 2311/2594] 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 d9c9d65818..392d63d1d9 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 07812d80fc..e14646edf7 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.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 @@ def test_get_config_h_filename(self): 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 @@ def test_srcdir(self): 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 @@ def test_parse_makefile_literal_dollar(self): 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 @@ def test_sysconfig_compiler_vars(self): 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(): From 58403d4abc8fdef0ff3b0fc9bb14c203cfd17349 Mon Sep 17 00:00:00 2001 From: Larry Hastings Date: Sun, 24 Nov 2013 06:59:35 -0800 Subject: [PATCH 2312/2594] 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 355542edd0..38a50869ea 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.4.0a4" +__version__ = "3.4.0b1" #--end constants-- From 4f57b30b05e31f7a12d2411a99d0faeb6e669196 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 26 Nov 2013 17:08:24 +0200 Subject: [PATCH 2313/2594] 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 392d63d1d9..75537db8d0 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 e14646edf7..b5fdc98dc9 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -6,7 +6,7 @@ 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 @@ def test_SO_deprecation(self): @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') From aad9a7feb8d3ce64a6ea07e76044777303d62be3 Mon Sep 17 00:00:00 2001 From: Stefan Krah Date: Tue, 3 Dec 2013 13:55:20 +0100 Subject: [PATCH 2314/2594] 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 80689b6357..9be592370a 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -538,7 +538,7 @@ def build_extension(self, ext): 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) From 89924f9e965ce58b5ceab3545989e6d827931d2f Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 18 Dec 2013 16:41:01 +0200 Subject: [PATCH 2315/2594] 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 aa1445d10a..bcbb5633e8 100644 --- a/tests/test_bdist_rpm.py +++ b/tests/test_bdist_rpm.py @@ -43,18 +43,15 @@ def tearDown(self): 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 @@ def test_quiet(self): 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 c2b981f20e..acc99e78c1 100644 --- a/tests/test_build_clib.py +++ b/tests/test_build_clib.py @@ -102,11 +102,8 @@ def test_finalize_options(self): 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 @@ def test_run(self): 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 3cfaa2c692..9853abd4c5 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -61,9 +61,9 @@ def test_build_ext(self): 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 @@ def test_solaris_enable_shared(self): 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 4de64734c4..601b68686b 100644 --- a/tests/test_check.py +++ b/tests/test_check.py @@ -55,9 +55,8 @@ def test_check_metadata(self): 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 @@ def test_check_document(self): 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 79894c78d3..0c8dbd8269 100644 --- a/tests/test_config_cmd.py +++ b/tests/test_config_cmd.py @@ -37,9 +37,8 @@ def test_dump_file(self): 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 e6359d6a8a..c952406131 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -125,13 +125,11 @@ def test_prune_file_list(self): 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 07812d80fc..a1cb47d87c 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -82,12 +82,9 @@ def test_srcdir_independent_of_cwd(self): 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 @@ def test_sysconfig_compiler_vars(self): 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 a5a63fdde3..3d14e12aa9 100644 --- a/tests/test_unixccompiler.py +++ b/tests/test_unixccompiler.py @@ -21,12 +21,8 @@ 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): - - # not tested under windows - if sys.platform == 'win32': - return - # Issue#5900 # # Ensure RUNPATH is added to extension modules with RPATH if From 3beec05fa08399627b01eedec1555c8c697f4fdf Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Sat, 21 Dec 2013 22:19:46 +0100 Subject: [PATCH 2316/2594] 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 9a8ca19902..61ac57d230 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -406,14 +406,14 @@ def test_read_metadata(self): 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 a474596e33..df35b429d2 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -114,11 +114,11 @@ def test_upload(self): # 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) From c2c0846c8840e37fe93fc8bed174801e6799db7e Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Sat, 21 Dec 2013 22:57:56 +0100 Subject: [PATCH 2317/2594] 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 99545affa4..9b39ed36aa 100644 --- a/command/register.py +++ b/command/register.py @@ -5,6 +5,7 @@ # created 2002/10/21, Richard Jones +import cgi import os, string, getpass import io import urllib.parse, urllib.request @@ -87,7 +88,9 @@ def classifiers(self): ''' 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 84d9232328..71ad4f42b2 100644 --- a/tests/support.py +++ b/tests/support.py @@ -32,14 +32,15 @@ def tearDown(self): 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 f4efa13d78..8bcc85851a 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -10,6 +10,7 @@ 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 @@ def __init__(self): 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 @@ def test_check_metadata_deprecated(self): 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) From 17a7b8192a8fcba2649854c844ae22fa0f512085 Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Sun, 22 Dec 2013 00:44:01 +0100 Subject: [PATCH 2318/2594] 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 8bcc85851a..6180133994 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -81,11 +81,13 @@ def setUp(self): 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() From 9f4a8134cd20941bd90e6419948c7bdc83475ad0 Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Sun, 22 Dec 2013 01:35:53 +0100 Subject: [PATCH 2319/2594] 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 1fd53346e9..a97635f2c0 100644 --- a/config.py +++ b/config.py @@ -21,7 +21,7 @@ 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 525bee9416..12593610aa 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -87,7 +87,7 @@ def test_server_registration(self): 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 @@ def test_server_registration(self): 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 4c6464a32e..d2696866fe 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 @@ def test_finalize_options(self): 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): From a9d129418d7569a12e225e130711524a89868eca Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Sun, 22 Dec 2013 18:13:51 +0100 Subject: [PATCH 2320/2594] 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 9b39ed36aa..55656c2353 100644 --- a/command/register.py +++ b/command/register.py @@ -5,7 +5,6 @@ # created 2002/10/21, Richard Jones -import cgi import os, string, getpass import io import urllib.parse, urllib.request @@ -88,9 +87,7 @@ def classifiers(self): ''' 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 d2bc82cea3..e30c189791 100644 --- a/command/upload.py +++ b/command/upload.py @@ -196,5 +196,6 @@ def upload_file(self, command, pyversion, filename): 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 a97635f2c0..7e10fff9d9 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 @@ def _read_pypirc(self): 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 fbf80e3804..8532369aa5 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -6,6 +6,7 @@ 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 @@ def __init__(self, url): 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 @@ def test_upload(self): # 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 @@ def test_upload(self): '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) From 8ee4e0365d4d31b4d0b42511567fa2c2657dcdcb Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Sun, 22 Dec 2013 19:37:17 +0100 Subject: [PATCH 2321/2594] 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 7e10fff9d9..106e146598 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 @@ def _read_pypirc(self): 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) From 889b0255164812a81f21cec8fae9c4cd6929b532 Mon Sep 17 00:00:00 2001 From: Zachary Ware Date: Mon, 30 Dec 2013 14:39:46 -0600 Subject: [PATCH 2322/2594] 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 6b42c5a213..2d72af4aa6 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -308,7 +308,7 @@ def test_make_archive_owner_group(self): 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() From 8fd408904c0379e17ba17f8ee313627645562dfe Mon Sep 17 00:00:00 2001 From: Zachary Ware Date: Mon, 30 Dec 2013 15:09:20 -0600 Subject: [PATCH 2323/2594] 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 49e267e20a..5a04e0ddd0 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -429,7 +429,7 @@ def test_manual_manifest(self): 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") From e6808a7ee674d31e2f10310237cee1dc7d1ce6ca Mon Sep 17 00:00:00 2001 From: Larry Hastings Date: Sun, 5 Jan 2014 04:40:25 -0800 Subject: [PATCH 2324/2594] 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 38a50869ea..725a33d6dc 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.4.0b1" +__version__ = "3.4.0b2" #--end constants-- From 7f50db7ceebeb2134f9000f8228588c7c18e59a6 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 25 Jan 2014 09:19:50 +0100 Subject: [PATCH 2325/2594] Bump to 3.3.4rc1. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 6747ab8677..8a637d1bd9 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.3.3" +__version__ = "3.3.4rc1" #--end constants-- From 4a1da2e04a6c5ca6e81bf48c37daef69c38cdb23 Mon Sep 17 00:00:00 2001 From: Larry Hastings Date: Sun, 26 Jan 2014 00:48:23 -0800 Subject: [PATCH 2326/2594] 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 725a33d6dc..bf9badd5f5 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.4.0b2" +__version__ = "3.4.0b3" #--end constants-- From 51bb181329a0d4167451376ad8168a1af005ba70 Mon Sep 17 00:00:00 2001 From: Stefan Krah Date: Tue, 28 Jan 2014 15:04:40 +0100 Subject: [PATCH 2327/2594] 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 9be592370a..80689b6357 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -538,7 +538,7 @@ def build_extension(self, ext): 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) From 4988103fdd5b6a0c02e3df2a282bea48a7a34a7f Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 6 Feb 2014 22:49:45 +0200 Subject: [PATCH 2328/2594] 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 55656c2353..b49f86fe58 100644 --- a/command/register.py +++ b/command/register.py @@ -300,5 +300,5 @@ def post_to_server(self, data, auth=None): 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 From f5efce06e4cf3367c6f316bb78847c43eb4e2054 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 9 Feb 2014 08:43:05 +0100 Subject: [PATCH 2329/2594] 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 8a637d1bd9..de5560b4f6 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.3.4rc1" +__version__ = "3.3.4" #--end constants-- From 154e4ab5dff1cc780dcfa04140506bc574e22179 Mon Sep 17 00:00:00 2001 From: Larry Hastings Date: Mon, 10 Feb 2014 14:45:05 -0800 Subject: [PATCH 2330/2594] 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 bf9badd5f5..4c716e2075 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.4.0b3" +__version__ = "3.4.0rc1" #--end constants-- From ff69c43e5d22036c7f433331e26808fd53f2bf4d Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 23 Feb 2014 08:30:06 +0100 Subject: [PATCH 2331/2594] Bump to 3.3.5rc1. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index de5560b4f6..b79071a691 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.3.4" +__version__ = "3.3.5rc1" #--end constants-- From 63aab90f407ec2c12fc1bfcfaa50b0714687275c Mon Sep 17 00:00:00 2001 From: Larry Hastings Date: Sun, 23 Feb 2014 02:18:24 -0600 Subject: [PATCH 2332/2594] 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 4c716e2075..c03185bd80 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.4.0rc1" +__version__ = "3.4.0rc2" #--end constants-- From 27d5cd873de20e7ff51d7b13e515b73000e8f727 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 2 Mar 2014 09:19:03 +0100 Subject: [PATCH 2333/2594] Bump to 3.3.5rc2. --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index b79071a691..7d40ab4748 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.3.5rc1" +__version__ = "3.3.5rc2" #--end constants-- From 9c925df0618a7ae0eccfea65da04faab89b32fc7 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 9 Mar 2014 09:37:14 +0100 Subject: [PATCH 2334/2594] 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 7d40ab4748..2e8719f8b7 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.3.5rc2" +__version__ = "3.3.5" #--end constants-- From 971530e1532467ef3f1d412019cfc68cf5e041d9 Mon Sep 17 00:00:00 2001 From: Larry Hastings Date: Sun, 9 Mar 2014 04:13:05 -0700 Subject: [PATCH 2335/2594] 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 c03185bd80..954b50dbac 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.4.0rc2" +__version__ = "3.4.0rc3" #--end constants-- 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: [PATCH 2336/2594] =?UTF-8?q?Avoid=20=E2=80=9Cerror:=20None?= =?UTF-8?q?=E2=80=9D=20messages=20from=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 260332a2ac..25d91baa0f 100644 --- a/core.py +++ b/core.py @@ -11,7 +11,6 @@ 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 @@ class found in 'cmdclass' is used in place of the default, which is 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 2826ff805d..6a72bdd4cd 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 00219cfdba..a1abf8f029 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -8,7 +8,8 @@ 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 @@ def test_dont_write_bytecode(self): 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 67d8166349..b558957814 100644 --- a/util.py +++ b/util.py @@ -213,25 +213,10 @@ def _subst (match, local_vars=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()' 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: [PATCH 2337/2594] 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 f58c55f902..f66ff93dfb 100644 --- a/spawn.py +++ b/spawn.py @@ -10,6 +10,7 @@ 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'. From 8028742c28561801bde9e5a0ffdd287fc11fb5ff Mon Sep 17 00:00:00 2001 From: Larry Hastings Date: Sat, 15 Mar 2014 22:34:24 -0700 Subject: [PATCH 2338/2594] 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 954b50dbac..04804c1f42 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.4.0rc3" +__version__ = "3.4.0" #--end constants-- From 25d93525889d4bc3e8e4dc39091482ada4fbd84b Mon Sep 17 00:00:00 2001 From: Larry Hastings Date: Sun, 16 Mar 2014 23:05:59 -0700 Subject: [PATCH 2339/2594] 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 4c716e2075..328bea6ffe 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.4.0rc1" +__version__ = "3.5.0a0" #--end constants-- From a42e746af0306d9d6d10f7dfc46b95394f7829a7 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 20 Mar 2014 08:50:33 +0100 Subject: [PATCH 2340/2594] 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 d9cf950a9b..22e87e8fdb 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) From 84b76498861b75f70242a11c1751a382532e5b81 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Thu, 27 Mar 2014 14:14:16 +0100 Subject: [PATCH 2341/2594] 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 a93655af2c..cc04a18a3a 100644 --- a/extension.py +++ b/extension.py @@ -131,6 +131,14 @@ def __init__(self, name, sources, 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, From 1152b05d319cd460945037cdf09529f754c1b4db Mon Sep 17 00:00:00 2001 From: Larry Hastings Date: Sun, 4 May 2014 05:06:24 -0700 Subject: [PATCH 2342/2594] 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 04804c1f42..b00024940e 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.4.0" +__version__ = "3.4.1rc1" #--end constants-- From 19565334cb36e694328566d3f5011a5b57d6bee3 Mon Sep 17 00:00:00 2001 From: "doko@ubuntu.com" Date: Wed, 7 May 2014 04:44:42 +0200 Subject: [PATCH 2343/2594] - 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 71ad4f42b2..7385c6bbf6 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] From ddf2ea79184c3b3e10f33bbab12ac995395d15fb Mon Sep 17 00:00:00 2001 From: "doko@ubuntu.com" Date: Wed, 7 May 2014 12:57:44 +0200 Subject: [PATCH 2344/2594] - 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 71ad4f42b2..7385c6bbf6 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] From 8cbbd8e2876d95e3f338a5342e2f11c42e780768 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 10 May 2014 13:20:28 -0400 Subject: [PATCH 2345/2594] 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 d6762e46fd..4882db428f 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 @@ def upload_file(self, command, pyversion, filename): '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 @@ def upload_file(self, command, pyversion, filename): 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) From 0983f9dd893c03aabbaa826bf9c644a88b67a5b4 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 10 May 2014 13:21:02 -0400 Subject: [PATCH 2346/2594] 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 4882db428f..7824466605 100644 --- a/command/upload.py +++ b/command/upload.py @@ -12,7 +12,7 @@ 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 From 5eb3849be5200231ac49e5b2c6b58ae63f1ed023 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 10 May 2014 13:22:43 -0400 Subject: [PATCH 2347/2594] 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 7824466605..75498b14d5 100644 --- a/command/upload.py +++ b/command/upload.py @@ -5,10 +5,10 @@ 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.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 @@ def upload_file(self, command, pyversion, filename): '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', From 41140cfe90c79dcb3ac74486c192e435c0bd1c09 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 10 May 2014 13:24:18 -0400 Subject: [PATCH 2348/2594] 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 75498b14d5..c279b591c4 100644 --- a/command/upload.py +++ b/command/upload.py @@ -146,7 +146,7 @@ def upload_file(self, command, pyversion, filename): 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: From b160cdaa13bd69e006afee3763d05cf4d1fc6441 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 10 May 2014 13:24:58 -0400 Subject: [PATCH 2349/2594] 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 c279b591c4..b906435203 100644 --- a/command/upload.py +++ b/command/upload.py @@ -57,7 +57,8 @@ def finalize_options(self): 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) From 37ceab37dd39345a8dab9c88082bd1a644ad7311 Mon Sep 17 00:00:00 2001 From: Larry Hastings Date: Sat, 17 May 2014 21:46:35 -0700 Subject: [PATCH 2350/2594] 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 b00024940e..9463a35c79 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.4.1rc1" +__version__ = "3.4.1" #--end constants-- From d47b6a2fadf767fe5caa430dd660f1f774affe4f Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Wed, 18 Jun 2014 23:07:46 -0400 Subject: [PATCH 2351/2594] 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 d6762e46fd..180be7c750 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 @@ 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 @@ def upload_file(self, command, pyversion, filename): 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 @@ def upload_file(self, command, pyversion, filename): 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 f53eb266ed..0380f97944 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -6,6 +6,7 @@ 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 @@ 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 @@ def read(self): return b'xyzzy' def getcode(self): - return 200 + return self.code class uploadTestCase(PyPIRCCommandTestCase): @@ -68,13 +70,15 @@ def setUp(self): 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 @@ def test_upload(self): 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) From a8edd4b874fd83cf8d53b0c410c97477a3816c38 Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Wed, 25 Jun 2014 13:36:14 -0700 Subject: [PATCH 2352/2594] 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 9853abd4c5..e9958667a4 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -444,8 +444,16 @@ def _try_compile_deployment_target(self, operator, target): # 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], From e64901cd8e4b49102390886ee264889a226c1945 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 2 Jul 2014 08:36:19 -0400 Subject: [PATCH 2353/2594] Normalize style per PEP-8 --- dist.py | 69 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 34 insertions(+), 35 deletions(-) diff --git a/dist.py b/dist.py index 7eb04bc3f5..ffb33ff645 100644 --- a/dist.py +++ b/dist.py @@ -4,7 +4,9 @@ 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 @@ # 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 @@ class Distribution: # 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 @@ def _parse_command_opts(self, parser, args): # 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 @@ def _parse_command_opts(self, parser, args): # 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 @@ def _parse_command_opts(self, parser, args): 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 @@ def _show_help(self, parser, global_options=1, display_options=1, 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 @@ def get_command_class(self, command): klass_name = command try: - __import__ (module_name) + __import__(module_name) module = sys.modules[module_name] except ImportError: continue @@ -823,8 +823,8 @@ def get_command_class(self, command): 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 @@ def get_command_obj(self, command, create=1): 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 @@ def _set_command_options(self, command_obj, option_dict=None): 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 @@ def run_command(self, command): cmd_obj.run() self.have_run[command] = 1 - # -- Distribution query methods ------------------------------------ def has_pure_modules(self): @@ -1112,17 +1111,17 @@ def write_pkg_file(self, file): """ 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 @@ def write_pkg_file(self, file): 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()) From 9c8e39e04f9391ecc42bce0000f6fba119dae6eb Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Sun, 6 Jul 2014 16:14:33 -0700 Subject: [PATCH 2354/2594] 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 75537db8d0..5b94fa2378 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 95fa9dc111..fc4d1de185 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 @@ def test_SO_in_vars(self): 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() From 5ef6395c634d60e1e7aff46970590b615fc2b754 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 22 Jul 2014 15:00:37 +0300 Subject: [PATCH 2355/2594] 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 cc04a18a3a..7efbb74f89 100644 --- a/extension.py +++ b/extension.py @@ -134,7 +134,7 @@ def __init__(self, name, sources, def __repr__(self): return '<%s.%s(%r) at %#x>' % ( self.__class__.__module__, - self.__class__.__name__, + self.__class__.__qualname__, self.name, id(self)) From 3100d99a48f564f3d0673ce3f972a823fa514158 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sun, 17 Aug 2014 23:00:42 -0500 Subject: [PATCH 2356/2594] 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 456511cdfb..d768dc5463 100644 --- a/command/install.py +++ b/command/install.py @@ -15,32 +15,17 @@ 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': { From 83b03cdd69f31da4b693db56f0ee8631e6ecff88 Mon Sep 17 00:00:00 2001 From: Berker Peksag Date: Fri, 29 Aug 2014 07:07:35 +0300 Subject: [PATCH 2357/2594] 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 f6ed290f13..7b14efbc07 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 3c3e3dcb3b..270f81ebb6 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 @@ def test_move_file_verbosity(self): 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) From c1f5d6da2805e44809fec3e76b8a3bb9f45956f5 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 31 Aug 2014 13:43:02 -0400 Subject: [PATCH 2358/2594] 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 1589f1297d..7e84721f80 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, From c73fc06c76dd902e3d913711852fd060fd3278b9 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 31 Aug 2014 15:00:47 -0400 Subject: [PATCH 2359/2594] 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 7e84721f80..6181ec6ef1 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -51,7 +51,7 @@ def test_mkpath_remove_tree_verbosity(self): 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) From cc179ffe784d64d8ad67f77d8ee74610ae4f7faa Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 31 Aug 2014 15:02:42 -0400 Subject: [PATCH 2360/2594] #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 6181ec6ef1..eb83497fc6 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -3,7 +3,9 @@ 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.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 @@ def test_ensure_relative(self): 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) From d45818441e0698244819349b0bee441c9d9328de Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 31 Aug 2014 17:31:32 -0400 Subject: [PATCH 2361/2594] #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 eb83497fc6..c9f789c895 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -3,7 +3,7 @@ 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 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 @@ def test_ensure_relative(self): 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(): From 3896d0af83329e4c5e4d63bf76535a9de51c0772 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 31 Aug 2014 17:37:35 -0400 Subject: [PATCH 2362/2594] #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 c9f789c895..51e754a07e 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -128,7 +128,7 @@ def test_copy_tree_exception_in_listdir(self, listdir): 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(): From 58b1aba47823b942ab22d5c260d96707fe15c6dd Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 31 Aug 2014 17:51:22 -0400 Subject: [PATCH 2363/2594] #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 51e754a07e..d2696b822c 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -128,7 +128,8 @@ def test_copy_tree_exception_in_listdir(self, listdir): 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(): From 01dbd70d47c352d868dce71898197ebe4c04f277 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 31 Aug 2014 17:42:20 -0400 Subject: [PATCH 2364/2594] #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 9879b0dc07..0924c9b0f7 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) From 15ea2f254869b3304ba5e840708654fb0a937edb Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 6 Sep 2014 17:24:12 -0400 Subject: [PATCH 2365/2594] 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 80689b6357..3ab2d04bf9 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -14,13 +14,7 @@ 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 5b94fa2378..a1452fe167 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 e9958667a4..b9f407f401 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -31,12 +31,11 @@ def setUp(self): 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 @@ def tearDown(self): 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): From aa5115c381d6b79e8db3a860bf71bf403c6fdb99 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 20 Sep 2014 11:53:12 -0400 Subject: [PATCH 2366/2594] 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 d2696b822c..d436cf8319 100644 --- a/tests/test_dir_util.py +++ b/tests/test_dir_util.py @@ -122,12 +122,12 @@ def test_ensure_relative(self): 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 270f81ebb6..d3db5cef0e 100644 --- a/tests/test_file_util.py +++ b/tests/test_file_util.py @@ -61,24 +61,23 @@ def test_move_file_verbosity(self): 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) From 17fc42a928be89366e183179353ae2a69ce48d93 Mon Sep 17 00:00:00 2001 From: Larry Hastings Date: Sun, 21 Sep 2014 00:09:56 +0100 Subject: [PATCH 2367/2594] 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 9463a35c79..750a5033ac 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.4.1" +__version__ = "3.4.2rc1" #--end constants-- From 933a2d7cf491ccade77d1382ecba1eae7a510874 Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Fri, 26 Sep 2014 23:31:59 +0200 Subject: [PATCH 2368/2594] 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 cfc15cf0dd..337dd0bfc1 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 @@ def initialize_options(self): 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 @@ def finalize_options(self): 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 3ab2d04bf9..08449e1a51 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -4,7 +4,10 @@ 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 @@ def initialize_options(self): 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 @@ def finalize_options(self): ('compiler', 'compiler'), ('debug', 'debug'), ('force', 'force'), + ('parallel', 'parallel'), ('plat_name', 'plat_name'), ) @@ -274,6 +281,12 @@ def finalize_options(self): 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 @@ def get_outputs(self): 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 b9f407f401..366ffbec9f 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -37,6 +37,9 @@ def setUp(self): 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 @@ def test_build_ext(self): 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 @@ def tearDown(self): 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 @@ def test_solaris_enable_shared(self): 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 @@ def test_optional_extension(self): # 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 @@ def test_finalize_options(self): # 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 @@ def test_finalize_options(self): # 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 @@ def test_finalize_options(self): # 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 @@ def test_finalize_options(self): # 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 @@ def test_check_extensions_list(self): 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 @@ def test_compiler_option(self): # 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 @@ def test_get_outputs(self): 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 @@ def test_ext_fullpath(self): #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 @@ def _try_compile_deployment_target(self, operator, target): '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 @@ def _try_compile_deployment_target(self, operator, target): 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__) From 04d00c2dbfc4a8facae5d91617f3a30de5474312 Mon Sep 17 00:00:00 2001 From: R David Murray Date: Sat, 27 Sep 2014 16:56:15 -0400 Subject: [PATCH 2369/2594] #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 180be7c750..9b15b67bae 100644 --- a/command/upload.py +++ b/command/upload.py @@ -143,11 +143,11 @@ def upload_file(self, command, pyversion, filename): # 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 @@ def upload_file(self, command, pyversion, filename): 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 0380f97944..24015416eb 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -127,7 +127,7 @@ def test_upload(self): # 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') From 731789f97ab96b9d91f5485c801078c1a9e66972 Mon Sep 17 00:00:00 2001 From: R David Murray Date: Sun, 28 Sep 2014 11:01:11 -0400 Subject: [PATCH 2370/2594] #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 9b15b67bae..1a96e2221e 100644 --- a/command/upload.py +++ b/command/upload.py @@ -164,7 +164,6 @@ def upload_file(self, command, pyversion, filename): 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 24015416eb..dccaf77e3e 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -127,7 +127,7 @@ def test_upload(self): # 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') From 59423d00f1ab2ba30212906ea691db28297cc47f Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Tue, 30 Sep 2014 14:58:22 +0200 Subject: [PATCH 2371/2594] 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 08449e1a51..2ffab18162 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -545,15 +545,8 @@ def build_extension(self, ext): 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" -- From 198a91ac760d28bc16e919fd5068f6e607d56d12 Mon Sep 17 00:00:00 2001 From: R David Murray Date: Tue, 30 Sep 2014 20:53:21 -0400 Subject: [PATCH 2372/2594] #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 bcbb5633e8..25c14abd32 100644 --- a/tests/test_bdist_rpm.py +++ b/tests/test_bdist_rpm.py @@ -24,6 +24,7 @@ """ class BuildRpmTestCase(support.TempdirManager, + support.EnvironGuard, support.LoggingSilencer, unittest.TestCase): @@ -54,6 +55,7 @@ def tearDown(self): 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 @@ def test_quiet(self): 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) From 59b6796e92361bd1cc79deae7aba71ec12e32554 Mon Sep 17 00:00:00 2001 From: "doko@ubuntu.com" Date: Thu, 2 Oct 2014 02:10:47 +0200 Subject: [PATCH 2373/2594] - 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 3ab2d04bf9..acbe648036 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -237,7 +237,7 @@ def finalize_options(self): # 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: From 0d41723615b404279c772ce9535705f4f4e830cb Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 4 Oct 2014 14:15:42 +0200 Subject: [PATCH 2374/2594] Bump to 3.2.6rc1 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index f9016d6d17..382e4b1c9d 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.2.5" +__version__ = "3.2.6rc1" #--end constants-- From 3859f945627998b2c426bf0e26bc86d54fc94731 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 4 Oct 2014 14:22:11 +0200 Subject: [PATCH 2375/2594] Bump to 3.3.6rc1 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 2e8719f8b7..c58e98db56 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.3.5" +__version__ = "3.3.6rc1" #--end constants-- From 1fc386404ed75da5f5be0cd0ca0adeff8ff6dd72 Mon Sep 17 00:00:00 2001 From: Larry Hastings Date: Sun, 5 Oct 2014 19:05:50 -0700 Subject: [PATCH 2376/2594] 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 750a5033ac..dfe62ffa4a 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.4.2rc1" +__version__ = "3.4.2" #--end constants-- From 49b4b07a6a16dff95918ab15344d3c8976cdafb2 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 12 Oct 2014 08:50:38 +0200 Subject: [PATCH 2377/2594] Bump to 3.2.6 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 382e4b1c9d..6eb79cb639 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.2.6rc1" +__version__ = "3.2.6" #--end constants-- From 49dde81581fd34569b50071587daa71eaf9e4546 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 12 Oct 2014 09:03:40 +0200 Subject: [PATCH 2378/2594] Bump to 3.3.6 --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index c58e98db56..057e90b86c 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.3.6rc1" +__version__ = "3.3.6" #--end constants-- From 909f8ed55e9b027b28fe9de50682d4a21d99743d Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Thu, 30 Oct 2014 19:37:07 +0100 Subject: [PATCH 2379/2594] 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 7b14efbc07..b3fee35a6c 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 d3db5cef0e..a6d04f065d 100644 --- a/tests/test_file_util.py +++ b/tests/test_file_util.py @@ -5,7 +5,7 @@ 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 @@ def test_move_file_exception_unpacking_unlink(self): 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) From b880aac986be9e5c76411953dcaf56c443f5d584 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Sat, 22 Nov 2014 12:54:57 -0800 Subject: [PATCH 2380/2594] 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 54ce13454f..605efbd68f 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -209,7 +209,7 @@ def finalize_options(self): 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 0000000000000000000000000000000000000000..9affe13039517798970f5e3a8d9f4d1d13e79464 GIT binary patch literal 84480 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%0AApdsA%z|%g>up>$nUTcWSs85Z`;uJlfBlT1UEmo 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 F{6EauG?M@T literal 0 HcmV?d00001 diff --git a/command/wininst-14.0.exe b/command/wininst-14.0.exe new file mode 100644 index 0000000000000000000000000000000000000000..3ce71b9f1b49afc4e6eb8a60ef551e7ced3e04b6 GIT binary patch literal 75264 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

8;GyV40Z#1M} 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 y7FSD^Sp{2%Wmq+9WrNu;HiGTV#= 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 8116656961..1048cd4159 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 a1452fe167..573724ddd7 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -9,6 +9,7 @@ Email: """ +import _imp import os import re import sys @@ -22,23 +23,15 @@ 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)) From e25c444f65824e882fbb6493578ee1db836721bd Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Mon, 15 Dec 2014 15:03:44 -0800 Subject: [PATCH 2381/2594] 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 9affe13039517798970f5e3a8d9f4d1d13e79464..43b85b6d4f2dd93f4b77c4b904276293ff667054 100644 GIT binary patch delta 111 zcmZpe!rCx}bpr<@^QQFv&D@Nfv*m$|5C$M%0^%?rKB3OYaDtZ+B*XwxWIShPpQf E0SG@DrT_o{ diff --git a/command/wininst-14.0.exe b/command/wininst-14.0.exe index 3ce71b9f1b49afc4e6eb8a60ef551e7ced3e04b6..764524d746b35fe28228e41e8b75a6ed665d9228 100644 GIT binary patch delta 159 zcmZoT!_shuWdjEz^YXO*&D@MH8|8tF5C$M%0^$H5-onJlu;mj-gn Date: Mon, 15 Dec 2014 20:45:23 -0800 Subject: [PATCH 2382/2594] 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 605efbd68f..c5a3ce1915 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -691,10 +691,7 @@ def get_ext_filename(self, ext_name): """ 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 18e1e57505..9313330e2b 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -20,8 +20,6 @@ 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') From 3fcf597f4db0a82fabde4d06bc287b6573763496 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Tue, 13 Jan 2015 09:17:24 -0500 Subject: [PATCH 2383/2594] 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 0924c9b0f7..d5cd8e3e24 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 From 3789d73e316092bc2b65960f3fa8706bc0afa3c3 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Wed, 14 Jan 2015 23:56:35 -0500 Subject: [PATCH 2384/2594] 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 22b9349dd6..7ebe707cff 100644 --- a/command/check.py +++ b/command/check.py @@ -122,7 +122,7 @@ 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 = frontend.OptionParser(components=(Parser,)).get_default_values() settings.tab_width = 4 settings.pep_references = None settings.rfc_references = None @@ -138,8 +138,8 @@ def _check_rst_data(self, data): 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 601b68686b..959fa9085c 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 @@ def test_check_restructuredtext(self): 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'} From 78d6f0dc55852b9489a17c1e25e8215aef1c7df9 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sat, 31 Jan 2015 12:05:05 +0200 Subject: [PATCH 2385/2594] 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 ebcab84e4e..af14cc1348 100644 --- a/version.py +++ b/version.py @@ -48,12 +48,6 @@ def __eq__(self, other): 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: 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: [PATCH 2386/2594] 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 22e87e8fdb..5dd415a283 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( From c14a53783773ecd520e998f72a2fd8fe4d58d8ca Mon Sep 17 00:00:00 2001 From: Larry Hastings Date: Sat, 7 Feb 2015 16:00:45 -0800 Subject: [PATCH 2387/2594] 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 dfe62ffa4a..e8782819e1 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.4.2" +__version__ = "3.4.3rc1" #--end constants-- From 5bf2ae1154e0722c9de295d4ec9f9721b629af73 Mon Sep 17 00:00:00 2001 From: Larry Hastings Date: Sat, 7 Feb 2015 16:00:55 -0800 Subject: [PATCH 2388/2594] 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 328bea6ffe..67ec78b295 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.5.0a0" +__version__ = "3.5.0a1" #--end constants-- From 2f7d730baf32b841c1b99d58b8efe3f8ca0423b8 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Sat, 14 Feb 2015 09:50:59 -0800 Subject: [PATCH 2389/2594] 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 d768dc5463..67db007a02 100644 --- a/command/install.py +++ b/command/install.py @@ -51,7 +51,7 @@ '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', } From 1f83754d9d44682de0958692d91f1ac4c9bdfec3 Mon Sep 17 00:00:00 2001 From: Larry Hastings Date: Sun, 22 Feb 2015 23:55:39 -0800 Subject: [PATCH 2390/2594] 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 e8782819e1..00a5859738 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.4.3rc1" +__version__ = "3.4.3" #--end constants-- From 4b8f2273ff4fe2d8343d79d0ecb81db7a9ad323c Mon Sep 17 00:00:00 2001 From: Larry Hastings Date: Sun, 8 Mar 2015 00:24:34 -0800 Subject: [PATCH 2391/2594] 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 67ec78b295..451c5fbebc 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.5.0a1" +__version__ = "3.5.0a2" #--end constants-- From 95fd4bbb5ba5ce152673dcc1b03b370cea0fab89 Mon Sep 17 00:00:00 2001 From: Larry Hastings Date: Sun, 29 Mar 2015 15:34:26 -0700 Subject: [PATCH 2392/2594] 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 451c5fbebc..6b4287094b 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.5.0a2" +__version__ = "3.5.0a3" #--end constants-- From 7fa0438c7f004f96a81f810fa44e9daddad233f2 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 10 Apr 2015 13:24:41 +0300 Subject: [PATCH 2393/2594] 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 40b8484a68..478336f0d2 100644 --- a/text_file.py +++ b/text_file.py @@ -118,10 +118,11 @@ def open(self, filename): 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 = [] From 6b108b8442eba8295a12655281e1b32b85a94a7b Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Mon, 13 Apr 2015 14:21:02 -0400 Subject: [PATCH 2394/2594] 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 9100b96a2a..cf0ca57c32 100644 --- a/command/build_py.py +++ b/command/build_py.py @@ -314,10 +314,10 @@ def get_outputs(self, include_bytecode=1): 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 215813ba97..6154cf0943 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 @@ def _bytecode_filenames(self, py_filenames): 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 c8f6b89919..18283dc722 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -120,8 +120,8 @@ def test_byte_compile_optimized(self): 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 40dd1a95a6..5378aa8249 100644 --- a/tests/test_install_lib.py +++ b/tests/test_install_lib.py @@ -44,12 +44,11 @@ def test_byte_compile(self): 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 @@ def test_get_outputs(self): 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 5adcac5a95..e423325de4 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 @@ def byte_compile (py_files, # 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 From 2ce0b4fac1ee4933717f90da0a0f78784194e681 Mon Sep 17 00:00:00 2001 From: Larry Hastings Date: Sun, 19 Apr 2015 13:51:40 -0700 Subject: [PATCH 2395/2594] 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 6b4287094b..37bfd5a8f7 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.5.0a3" +__version__ = "3.5.0a4" #--end constants-- From 766d1fbd091c92d68c85eae404aee1d792c815aa Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sat, 16 May 2015 22:13:27 +0300 Subject: [PATCH 2396/2594] 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 4470bb0222..bed1384900 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 6814a1c382..014871d280 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 4405d12c05..f1bfb24923 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 2d72af4aa6..81d4c74a7b 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -13,7 +13,7 @@ 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 @@ 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 @@ 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') - 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 @@ def _make_tarball(self, target_name): 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 @@ def _tarinfo(self, path): 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 @@ def _create_files(self): 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 @@ def test_tarfile_vs_tar(self): 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 @@ def test_tarfile_vs_tar(self): @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 @@ def test_compress_deprecated(self): '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 @@ def fake_zipfile(*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) + 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 @@ def _breaks(*args, **kw): 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 @@ def test_make_archive_owner_group(self): 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 @@ def test_make_archive_owner_group(self): @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 503a6e857d..f762f5d987 100644 --- a/tests/test_bdist.py +++ b/tests/test_bdist.py @@ -21,7 +21,7 @@ def test_formats(self): # 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) From a583aac0ebff225a9b138cda7d60047d7f7f462c Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 17 May 2015 02:23:02 +0300 Subject: [PATCH 2397/2594] 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 81d4c74a7b..02fa1e27a4 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -325,7 +325,7 @@ def test_make_archive_bztar(self): 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') From 37ec45e05a5c9b6dfccbfc97caf466496b727616 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 20 May 2015 16:10:04 +0300 Subject: [PATCH 2398/2594] 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 2bfe66aa2f..f05b34b295 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 -- " From d034a5ec7f707499139f90eb846b9e720923124c Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Sat, 23 May 2015 09:02:50 -0700 Subject: [PATCH 2399/2594] 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 0000000000..896d9d927f --- /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 911e84dd3b..b7df394ecc 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 959a8bf62e..a3eff7e7cf 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -303,7 +303,6 @@ def get_installer_filename(self, fullname): 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 @@ def get_exe_bytes(self): # 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 c5a3ce1915..d4cb11e6db 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -19,10 +19,6 @@ 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 @@ def finalize_options(self): _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 @@ def get_libraries(self, ext): # 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 0000000000..b4407543b1 --- /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()) From 739202377f34c4058ce96bafd0150c4299fa21c0 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Sat, 23 May 2015 12:15:57 -0700 Subject: [PATCH 2400/2594] 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 a3eff7e7cf..0c0e2c1a26 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -331,8 +331,13 @@ def get_exe_bytes(self): 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 From 1ae0b75fb909b3fa1bd42702d4ab2a943a8f7155 Mon Sep 17 00:00:00 2001 From: Larry Hastings Date: Sat, 23 May 2015 17:43:05 -0700 Subject: [PATCH 2401/2594] 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 37bfd5a8f7..686ca1de88 100644 --- a/__init__.py +++ b/__init__.py @@ -13,5 +13,5 @@ # Updated automatically by the Python release process. # #--start constants-- -__version__ = "3.5.0a4" +__version__ = "3.5.0b1" #--end constants-- From 0db81b9974abf772d40f64f77c9d7220f880b4e9 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Mon, 25 May 2015 21:24:00 -0500 Subject: [PATCH 2402/2594] 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 6eb79cb639..d823d040a1 100644 --- a/__init__.py +++ b/__init__.py @@ -8,10 +8,6 @@ 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(' ')] From 7c42a077cf4a91e095a2bc4964927de32b5cab78 Mon Sep 17 00:00:00 2001 From: Robert Collins Date: Tue, 28 Jul 2015 15:55:07 +1200 Subject: [PATCH 2403/2594] 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 f05b34b295..d603d4a45a 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 41321f7db4..57856f19b6 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -28,6 +28,21 @@ 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 @@ def test_run_setup_provides_file(self): 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 From 84deb7074ef2c9086aeb440f84738fef0bc67f74 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Thu, 30 Jul 2015 11:51:06 -0700 Subject: [PATCH 2404/2594] 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 896d9d927f..4e2eed72a9 100644 --- a/_msvccompiler.py +++ b/_msvccompiler.py @@ -207,10 +207,10 @@ def initialize(self, plat_name=None): ] 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' From 8b68b23eea750175cdf4ee91547fac64434ce34c Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Wed, 5 Aug 2015 11:39:19 -0700 Subject: [PATCH 2405/2594] 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 4e2eed72a9..b344616e60 100644 --- a/_msvccompiler.py +++ b/_msvccompiler.py @@ -15,7 +15,6 @@ import os import subprocess -import re from distutils.errors import DistutilsExecError, DistutilsPlatformError, \ CompileError, LibError, LinkError @@ -182,7 +181,8 @@ def initialize(self, plat_name=None): 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 @@ def initialize(self, plat_name=None): 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 @@ 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) + 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 @@ def make_out_path(p): 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 @@ def create_static_lib(self, 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 @@ def link(self, 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 @@ def link(self, 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 @@ def link(self, 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 b4407543b1..1f8890792e 100644 --- a/tests/test_msvccompiler.py +++ b/tests/test_msvccompiler.py @@ -7,88 +7,6 @@ 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 @@ def _find_vcvarsall(): 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) From 8434fc905458f1fa6cadf7c52550b9fac80e2b7d Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Wed, 5 Aug 2015 11:48:14 -0700 Subject: [PATCH 2406/2594] 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 43b85b6d4f2dd93f4b77c4b904276293ff667054..7a5e78d52bcc5db0be1baff6cdad41e172a5b81f 100644 GIT binary patch delta 78379 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ν2$fvE@uf*`7>z|E_43%;l8kdl>NhUB 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 delta 27193 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%0D?ct8#tyV zQTipA_cAQ*PkGCq@{*oU)#1Wh%W(_lPmR%E&H6SW&V@IF{LkXi?%D$Ne7c_Rq384T ze5jsZuIGdF{3bmgqUY=MJiT6}zr%VSZ_fGOSuNk^Mqk@?hG3lmn+*>3xJWtKCRkja z%UbTO-WsHmTmP=><;A`Jb9qpkMsMt^LEL=Z_7|a-dwTxI`YsluGYt8^G$85a3-t#6 zte|%o^Yr2H4x=~!{~ktsPI%im?tVN*NDy4%u>Ms%>9~>o3Gkac7%Kqo0sIxO11o|5 z4)_;XeIxL7fOUA@15WU3JO_y%Z~#WG1$Y!-7M|0bVvY$!2Yl&1I1%u~`{B`e z9KZ=aQvgMQ$6z;!F2n^T@F9T9DxoOw<$wq990q<6&{BoVOyC61;kgJr-Ssd`{Rqs6 zj05nm)$n%wD__3@Ry+z12YzuqmUlcB;Oue6y5q?K9s<~kXCd$_fGVCe;@QC1(|8hr zZw7ox!Po=Gs~R?cBV#Lpfgc3S{44VKM=L4;k35ax0Nw)VzZpIPyc1w1o>9Py0e9lD0VjA7Pa1H7A!2LL>v$rE z(|fP;HJ$$uKAQYEE|BRvE&VUDd`cev`b z|AA=db9K)(K6myxb`h7csF(MA@$)PK(+jv|hqNPgNBWM;9eF#7cP!sgwqwnXH80e@ zK>z*FLX^kU#?@MCrP|cm^xDkYyxQX0<+W#PJ+;?s{kMf|i`o{qP1=^eEpJ=hw)$-+ zx1HVAzU}(9pzR^sW46a_4@upgzP)(+^6h2Y*KA+Eebe^Z?K`*EZLi->+eM!6e*kZ< B_80&F diff --git a/command/wininst-14.0.exe b/command/wininst-14.0.exe index 764524d746b35fe28228e41e8b75a6ed665d9228..cc43296b677a5b995899bb60bbe0e632163e64ae 100644 GIT binary patch literal 129024 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

V@jn;Qb*;W@BE; z2k`OyNbq_FKc8R1@8k>l*L*QwQDdj61Id(Y0yMoeBQ#4ixtblCQ{eeq%~y?$)?Ql| z(&-1u?5gbrz7N#S(r(f|(7uK=Itb2!tKcR0K_bTqlZEBN8sUNPT&Se8gEThOsdQa* zA-Zr~v~G|tTenKLL3crSoABBb_rJjF8gygYgASqx(9`IZ^dUU? zb~d}1UCyp$x3Rm~{a6pru$S3e>^=4o`+_ZEzp|Fn%2EfZi?qJfUz#ksq3GNbipDW~EbDubgqKQJP2vUSA5*5o8g^I5V3uS#} zQ)Me~KVG>|c~E&mc^Mpku6(bwfMkcLl2sQ~f2k^|9l+TpYDUdLqJ!0k)pyjd)RlP$ zz6l?Rm3A+Gga63a*Ywt;Vs*{Y?8fSPTJud~rLCfM($)j_Jhb83IBlwSsCK+|ns%4= zfcAv;0|;(bmzY19zpxfsvklm0ten-c zJ=h2~8KXRlUC8FLTi87I2Kx|uO#^8&sa&d)_LAH<=cDG8>UHWZ z>Rsyn>Z4eBFQ~7oZ$k#3s9&i6Qh!pHs4e)4yd7Vici|hLcOJY9J@n-L`1X7kK7{Yh zNAvyo6s*g``BD6MekwnkU&!ZRN8P}0q2dF%Xj9d*IFp4cbibt$^Rx=idBQ*?85i*;*s8+F@t2Xs$$ zMTD-pdNgK+7Wh-R6WM{A&Kb95A zT;z;gDHr5{@=*Cc`DyuU$gmyPoMXAR+%T-?%ek%GUhWiE#FcRM6pa)uuzHVHOi=7q z#3-|sQUJk=f5@t70ld}ZE|?+IQG;8*io_yhcD z{t{on|H;4O%`}{Ls4zpgBa{fXy2iTB=;cD)I^9v-OVY3DP+u@#tI~BayKSJMG-T4T z{@sS&F-Q7Q`c*mtefT6xfzDQkYmD`#19ySD!rjD5^OF0EGgDY8Dl1$R^%ad39JC2f zMWCXqB2>{&k*r8pWTO{T6-yK=6l<^#y;HPO>XhA-y_Msk>upyaQvR-dt!%8~RjpOs zRk5lBRf^cREvjeGE#9dbs++3a)rsl>>Otzo(EHY7reDSEeyo13w&iQ_^>{bF9p9Y~ zvh|7$DuV~d~&dVz!RXuZDZQ7TiKKB2ez24fHmC_ zI+>f)UCK(;Qcr1j=w(Z!+odO^6=dnMZt`LB=~!DX$ZukGkznlHI0fg+b>qUg0o-ut zU^_UCvYm2;@}qK$swE^mMBQJ#fbR`WXa!`XxzJ14ClEa|0y`NRutofBoC>99((CCa zOb=!W6NMc{E*&j(l}*J8cMEG+2)Bx}RCt2}V-%Yd`xR#uHx*B?|C=eRD(hhVkHXr2 zKzUAiTluH*Ggf}NDgrZVjB1DK80OO_Rb6!lXxW3+qtz4Do76|tC)JmsZ9l;JR-*2R zJ$<~!N!tiI*aB~@rRs+3 zc0dDrM*4A>qVnKTbUnHywB#A|2il6M%`|0tK_8A`;+g);0PG1{n9EE*b~9@$O@UVB zfwjH2Y&7~Ag;o2C{2lsQo14Jx;vRC96;qXmm2Z__l=je2WvXn|V$~+qY1J*&8`Wo( zAGFdD>e=d@>LPV-K9wH=xp)k@s0saJs>WK|SS!^If_Aw{dmD4*v$nR-Kxifm6n??V zaap*5SweKl&w?2wClM<|x^4)1lLZ}Pyl%E`la6!-U7{>#U%Dqfgq}d}p&!v-XbEG> zIHF$~W;;{NI6zC-$kxHW zRb5rHRclrI(T7^tkCW6x)Em@$)yFaWAMzbE!_oT1SWnMuu4~?DN;HmInbubu46SJe zPB9O)Zi1)KTIeAx5zdQ}Ukz(r5Z1YPT^eTiY~5bnP3Xg)bX0+UU#dk(od8E(V8x12Pf~AGAH88*wreP+nVAevju)z-78t1Ax;KK#>JL`$wgv$oWzRJw7s%f|m ziUh1!X^O#$VTuu0ea9&#DP}3wE4Cn7?+GOY^xT!6NR zHbUztsL;PQLI)v87%r?8E(q6!yU=e71$*5h^cMF!(N_swg&spMrPt8spheYW>M>oQ zMulBiT5NR60A9&0@#0lX2>q&n{tCvg@FO?Sh7Tls%0xyvmY2 z;tA^~Z3j(km~@(Sj`W7CP!_47@RE%`+S^Y^!hSpmt8OMvX=8TZS(hcirGsE#OrP@!Vu?IyalVaGHfXf$)53f4V&#NC(p)bQm2$N8@CiNT<;0Sl5Qr zS#&lugvs=DdNyQj5uHPCz!MJg;KK!Zn`&EXX{!x2ZyixBgZ zoHvd)q=*hmUKc1F^vC-^s_Bc+&kD!xSE1D{NaSkT;%u`Ue8|p4?@k+=JY4r~JI9hNO yZ6GapPk%)#Dl=K)d1N+|gVWy@$PcME5BmE*>F##W);&>sI!=EZSXA^s!2KWH-LYK& delta 26950 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 From 94e5864e4ff57bfb82bca76fe8d00e15b646fe98 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Fri, 7 Aug 2015 19:48:03 -0700 Subject: [PATCH 2407/2594] 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 9688f20019..a5a5010053 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -413,7 +413,7 @@ def initialize(self, plat_name=None): 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'] From d7f14ca7b710fe301273909ea858fefbaa093e6c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 30 Aug 2015 13:13:11 -0400 Subject: [PATCH 2408/2594] 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 e82bc3d2ed..278809c09b 100644 --- a/tests/test_filelist.py +++ b/tests/test_filelist.py @@ -7,7 +7,7 @@ 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 @@ def test_process_template(self): self.assertWarnings() -def test_suite(): - return unittest.makeSuite(FileListTestCase) - if __name__ == "__main__": - run_unittest(test_suite()) + unittest.main() From 2822a45bc27876169fd57bad2fdf2d9d5ae2aede Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 30 Aug 2015 13:22:56 -0400 Subject: [PATCH 2409/2594] 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 278809c09b..6b3bde0d9b 100644 --- a/tests/test_filelist.py +++ b/tests/test_filelist.py @@ -6,7 +6,9 @@ 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 @@ def test_process_template(self): 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() From 3bf3f37dd03a1978f6c8386dd348f681241494a8 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 30 Aug 2015 13:26:48 -0400 Subject: [PATCH 2410/2594] 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 6b3bde0d9b..45ff565373 100644 --- a/tests/test_filelist.py +++ b/tests/test_filelist.py @@ -301,6 +301,17 @@ def test_missing_symlink(self): 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() From e54077ba1964fbb59f3f1df7b9290694a4dc964d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 19 Sep 2015 17:32:51 +0200 Subject: [PATCH 2411/2594] 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 45ff565373..571acdbc08 100644 --- a/tests/test_filelist.py +++ b/tests/test_filelist.py @@ -302,6 +302,11 @@ def test_missing_symlink(self): 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 @@ def test_basic_discovery(self): 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() From 656ef5739b3f21070ed3a434ef4911a922b3cf5a Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 30 Aug 2015 14:05:58 -0400 Subject: [PATCH 2412/2594] 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 571acdbc08..e719198c6d 100644 --- a/tests/test_filelist.py +++ b/tests/test_filelist.py @@ -314,8 +314,8 @@ def test_basic_discovery(self): 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): """ From 0846f06f326b9f369772b6fe1c980434bc9b9c1b Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Tue, 8 Sep 2015 21:39:01 -0700 Subject: [PATCH 2413/2594] 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 b344616e60..82b78a0ffe 100644 --- a/_msvccompiler.py +++ b/_msvccompiler.py @@ -14,6 +14,8 @@ # 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 @@ 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 @@ def _find_exe(exe, paths=None): '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 @@ def initialize(self, plat_name=None): 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 @@ def initialize(self, plat_name=None): 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 @@ def link(self, 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 1f8890792e..0b8a69fa5a 100644 --- a/tests/test_msvccompiler.py +++ b/tests/test_msvccompiler.py @@ -3,6 +3,8 @@ 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 @@ 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 + 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) From fd5d6fbed8f41d9ad678d2b2ace8c1d6564f00a3 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Tue, 8 Sep 2015 23:42:51 -0700 Subject: [PATCH 2414/2594] 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 0b8a69fa5a..874d6035e8 100644 --- a/tests/test_msvccompiler.py +++ b/tests/test_msvccompiler.py @@ -3,8 +3,6 @@ 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 @@ def _find_vcvarsall(plat_spec): _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 @@ def _find_vcvarsall(plat_spec): _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 @@ def _find_vcvarsall(plat_spec): _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() From 465364f3af646e6285e2c6fd461fd44113164ff7 Mon Sep 17 00:00:00 2001 From: Larry Hastings Date: Wed, 9 Sep 2015 06:54:57 -0700 Subject: [PATCH 2415/2594] 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 82b78a0ffe..03a5f10ee7 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 @@ def initialize(self, plat_name=None): '/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 874d6035e8..c4d911ff83 100644 --- a/tests/test_msvccompiler.py +++ b/tests/test_msvccompiler.py @@ -77,7 +77,7 @@ def test_vcruntime_skip_copy(self): compiler.initialize() dll = compiler._vcruntime_redist self.assertTrue(os.path.isfile(dll)) - + compiler._copy_vcruntime(tempdir) self.assertFalse(os.path.isfile(os.path.join( From 2509f6beef005a88eede79503086f8c0ba1b484c Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Sat, 12 Sep 2015 17:20:47 -0700 Subject: [PATCH 2416/2594] 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 911e84dd3b..e93a27363f 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -752,7 +752,7 @@ def runtime_library_dir_option(self, dir): 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 From b7236c18f5a3b4f5f44d97eb67ce9e3cc56715f9 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 19 Sep 2015 18:12:15 +0200 Subject: [PATCH 2417/2594] 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 db3f7a9680..6522e69f06 100644 --- a/filelist.py +++ b/filelist.py @@ -6,6 +6,7 @@ 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 @@ def exclude_pattern (self, pattern, # ---------------------------------------------------------------------- # 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): From f8a519501546f5d5f81ad00ecc7d160bd72e748e Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Mon, 5 Oct 2015 10:35:00 -0700 Subject: [PATCH 2418/2594] 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 03a5f10ee7..10a9ffda24 100644 --- a/_msvccompiler.py +++ b/_msvccompiler.py @@ -28,15 +28,17 @@ 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(): From f06fb88362616b452cf6da3de78eb18a759526a5 Mon Sep 17 00:00:00 2001 From: Martin Panter Date: Mon, 2 Nov 2015 03:37:02 +0000 Subject: [PATCH 2419/2594] 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 d28b1b368a..c879646c0f 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -10,9 +10,9 @@ # # * 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 @@ def __init__(self, verbose=0, dry_run=0, force=0): 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" From 1b3e7be2deb5caf890dc7a53896357e4b938979e Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 12 Nov 2015 13:15:41 +0200 Subject: [PATCH 2420/2594] 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 ce66ee51e7..0c2ad7a426 100644 --- a/tests/test_log.py +++ b/tests/test_log.py @@ -14,8 +14,8 @@ def test_non_ascii(self): # 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 @@ def test_non_ascii(self): stderr.seek(0) self.assertEqual(stderr.read().rstrip(), "fatal:\\xe9") finally: + log.set_threshold(old_threshold) sys.stdout = old_stdout sys.stderr = old_stderr From 4e0ac6d444c79d2858a6d3cb8b7b523ccf3f54e3 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 12 Nov 2015 19:46:23 +0200 Subject: [PATCH 2421/2594] 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 41321f7db4..654227ca18 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -9,6 +9,7 @@ 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 @@ def setUp(self): 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 b7fd3fbf90..1f104cef67 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -13,6 +13,7 @@ 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 @@ def test_fix_help_options(self): 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 From bafe9fba05aefdd679770d54e3a74fc0cab538d5 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Sat, 16 Jan 2016 12:39:10 -0800 Subject: [PATCH 2422/2594] 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 10a9ffda24..d0ba7d6d1e 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 @@ def initialize(self, plat_name=None): 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: From 7a7aaaf398456bb31074f4446d8c1290d9695019 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Sat, 16 Jan 2016 13:54:53 -0800 Subject: [PATCH 2423/2594] 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 7a5e78d52bcc5db0be1baff6cdad41e172a5b81f..22299543a97ffc1525a3b1c778cb158d6c6430ad 100644 GIT binary patch literal 589824 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­3|d){2u$ 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≫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 D?R$@- diff --git a/command/wininst-14.0.exe b/command/wininst-14.0.exe index cc43296b677a5b995899bb60bbe0e632163e64ae..0dac1103d98db0af1e9027c41fe921136c5f6396 100644 GIT binary patch literal 460288 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 z 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%0D$5P;I$Hd0dhI;(+2Nnt2fK0F+e{f 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 delta 42184 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 Date: Mon, 18 Jan 2016 12:15:08 +0100 Subject: [PATCH 2424/2594] 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 e423325de4..fdcf6fabae 100644 --- a/util.py +++ b/util.py @@ -7,8 +7,8 @@ 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 @@ def byte_compile (py_files, 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) From 605d57a077d65dba6bc554426e50fe2ba04502af Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 11 Feb 2016 13:10:36 +0200 Subject: [PATCH 2425/2594] 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 b3cfe9ceff..f6c21aee44 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -199,7 +199,7 @@ def run(self): 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 0c0e2c1a26..d3e1d3af22 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -141,7 +141,7 @@ def run(self): 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 337dd0bfc1..c6f52e61e1 100644 --- a/command/build.py +++ b/command/build.py @@ -81,7 +81,7 @@ def finalize_options(self): "--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 @@ def finalize_options(self): '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 67db007a02..9474e9c599 100644 --- a/command/install.py +++ b/command/install.py @@ -290,8 +290,8 @@ def finalize_options(self): '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 c2a7d649c0..0ddc7367cc 100644 --- a/command/install_egg_info.py +++ b/command/install_egg_info.py @@ -21,10 +21,10 @@ def initialize_options(self): 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 573724ddd7..d203f8e42b 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 3391f36d4b..b020a5ba35 100644 --- a/tests/test_build.py +++ b/tests/test_build.py @@ -27,7 +27,7 @@ def test_finalize_options(self): # 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 @@ def test_finalize_options(self): 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) From ef3d89d581b83fa214d5563d4e3a9e7c901746ab Mon Sep 17 00:00:00 2001 From: Ned Deily Date: Thu, 25 Feb 2016 00:56:38 +1100 Subject: [PATCH 2426/2594] 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 82fd7d5c4d..b71d1d39bc 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -875,9 +875,9 @@ def executable_filename(self, basename, strip_dir=0, output_dir=''): 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 094a2f0bd0..92d14dfee7 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 @@ def library_option(self, 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: @@ -274,6 +292,7 @@ def find_library_file(self, dirs, lib, debug=0): 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 @@ def find_library_file(self, dirs, lib, debug=0): 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 @@ def find_library_file(self, dirs, lib, debug=0): # 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): From 39b80901d20354b093e8220c137ce971c35c20c5 Mon Sep 17 00:00:00 2001 From: Berker Peksag Date: Sun, 24 Apr 2016 01:55:09 +0300 Subject: [PATCH 2427/2594] 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 ffb33ff645..62a24516cf 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", ) From 95f6afb74ad25d2b616ed44499035595fe945fc5 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 24 Apr 2016 13:25:01 +0300 Subject: [PATCH 2428/2594] 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 b64f300a04..df88ec1e34 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 4de825a81e..0929f8a47d 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 4d8c00acb9..d73624d00b 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 d953157bb7..d9ed6b7fd2 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 3d14e12aa9..7c95be56d3 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 From 905adfca47183d923e35135c4795a3b0439c6104 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 24 Apr 2016 21:41:02 +0300 Subject: [PATCH 2429/2594] 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 25c14abd32..c1a2a041ee 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 366ffbec9f..3d84f8b1d7 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -166,7 +166,6 @@ def test_finalize_options(self): 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 df88ec1e34..c605afd860 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 0929f8a47d..d91dedd3a8 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 856921679d..9dc869de4c 100644 --- a/tests/test_cygwinccompiler.py +++ b/tests/test_cygwinccompiler.py @@ -3,11 +3,10 @@ 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 3e1c366892..c6fae39cfb 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 a6d04f065d..03040afc79 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 d73624d00b..32ab296a32 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 d9ed6b7fd2..2217b321e6 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 6c7eb20c47..f507ef7750 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 From 1746f3caf7a6a71c04f79da2b89c888cb5daadec Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 25 Apr 2016 00:12:32 +0300 Subject: [PATCH 2430/2594] 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 847e858160..b1fd09e016 100644 --- a/command/config.py +++ b/command/config.py @@ -9,7 +9,7 @@ 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 b49f86fe58..a3e0893a0b 100644 --- a/command/register.py +++ b/command/register.py @@ -5,7 +5,7 @@ # 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 7ea3d5fa27..35a06eb09b 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 7efbb74f89..c507da360a 100644 --- a/extension.py +++ b/extension.py @@ -4,7 +4,6 @@ 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 478336f0d2..93abad38f4 100644 --- a/text_file.py +++ b/text_file.py @@ -4,7 +4,7 @@ that (optionally) takes care of stripping comments, ignoring blank lines, and joining lines with backslashes.""" -import sys, os, io +import sys, io class TextFile: From a1b5cdeacee878322b0aa52af208a2f826b4b800 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 4 May 2016 11:57:32 -0400 Subject: [PATCH 2431/2594] 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 382aca8fc1..f0c7373171 100644 --- a/config.py +++ b/config.py @@ -4,7 +4,7 @@ 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 @@ def _read_pypirc(self): 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: From 30a0d1c5414ec93ffd4f83cfcc67d426847ce30d Mon Sep 17 00:00:00 2001 From: Martin Panter Date: Thu, 26 May 2016 05:35:26 +0000 Subject: [PATCH 2432/2594] 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 da4b21d22a..0b1fd19ff6 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -51,7 +51,7 @@ # 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 3d14e12aa9..e171ee9c4d 100644 --- a/tests/test_unixccompiler.py +++ b/tests/test_unixccompiler.py @@ -127,7 +127,7 @@ def gcv(v): 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 From 3f054be58270984d4c8e5b1e499f573dbae02975 Mon Sep 17 00:00:00 2001 From: Martin Panter Date: Thu, 2 Jun 2016 10:07:09 +0000 Subject: [PATCH 2433/2594] 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 366ffbec9f..8f62b1a848 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -279,7 +279,7 @@ def test_get_source_files(self): 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) From 29389166af9931b6a533a60556d9c6e6929b71b1 Mon Sep 17 00:00:00 2001 From: Berker Peksag Date: Thu, 2 Jun 2016 13:45:53 -0700 Subject: [PATCH 2434/2594] 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 1c4fc48a12..0afcbf233e 100644 --- a/command/upload.py +++ b/command/upload.py @@ -181,21 +181,21 @@ def upload_file(self, command, pyversion, filename): 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 dccaf77e3e..19193d5b05 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 @@ def test_upload_fails(self): 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) From f1c18134f23f8ad7cbf624097e242939bb540fa2 Mon Sep 17 00:00:00 2001 From: "doko@ubuntu.com" Date: Sun, 5 Jun 2016 00:41:58 +0200 Subject: [PATCH 2435/2594] - 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 d4cb11e6db..f03a4e31d8 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -748,7 +748,7 @@ def get_libraries(self, ext): 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 From 5b8e477917ada86de6f23ffe5ba9831b258e9329 Mon Sep 17 00:00:00 2001 From: "doko@ubuntu.com" Date: Sun, 5 Jun 2016 01:17:57 +0200 Subject: [PATCH 2436/2594] - 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 d203f8e42b..f205dcadeb 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -415,38 +415,11 @@ def expand_makefile_vars(s, vars): 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(): From 7c3fafbc0017fa29a42d0eda69b37b37c4b465c2 Mon Sep 17 00:00:00 2001 From: Berker Peksag Date: Fri, 10 Jun 2016 23:00:52 +0300 Subject: [PATCH 2437/2594] 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 b49f86fe58..86343c8017 100644 --- a/command/register.py +++ b/command/register.py @@ -296,9 +296,9 @@ def post_to_server(self, data, auth=None): 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 6180133994..01acf2375f 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -301,6 +301,20 @@ def test_list_classifiers(self): 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 19193d5b05..964aac7e80 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -140,7 +140,7 @@ def test_upload(self): # 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" From 9070733b989cf7350ff11aab41f0d6f541700f84 Mon Sep 17 00:00:00 2001 From: "doko@ubuntu.com" Date: Tue, 14 Jun 2016 08:55:19 +0200 Subject: [PATCH 2438/2594] - 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 f205dcadeb..e9cc4a95d4 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') From 089c6f4fa347563cf9753ea0370723ed1b00bc89 Mon Sep 17 00:00:00 2001 From: "doko@ubuntu.com" Date: Tue, 14 Jun 2016 09:22:16 +0200 Subject: [PATCH 2439/2594] - 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 e9cc4a95d4..f72b7f5a19 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -418,7 +418,9 @@ def expand_makefile_vars(s, vars): 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) From 7b9d0828be06389c7e81946ba049231396eb9f3d Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Fri, 17 Jun 2016 09:32:38 -0700 Subject: [PATCH 2440/2594] 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 d0ba7d6d1e..b120273fe9 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 c4d911ff83..4dc24886c8 100644 --- a/tests/test_msvccompiler.py +++ b/tests/test_msvccompiler.py @@ -83,6 +83,24 @@ def test_vcruntime_skip_copy(self): 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) From 574bde6ee98b09b974ac5e0adbe46eeebc5173b6 Mon Sep 17 00:00:00 2001 From: Berker Peksag Date: Sat, 18 Jun 2016 21:42:37 +0300 Subject: [PATCH 2441/2594] 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 0afcbf233e..1fd574a9f1 100644 --- a/command/upload.py +++ b/command/upload.py @@ -91,7 +91,7 @@ def upload_file(self, command, pyversion, filename): 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 964aac7e80..3eecf8afd4 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -130,13 +130,14 @@ def test_upload(self): # 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) From 32821f0c7684148df899f79932c2e138c3ace6f5 Mon Sep 17 00:00:00 2001 From: Berker Peksag Date: Mon, 20 Jun 2016 21:41:34 +0300 Subject: [PATCH 2442/2594] 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 4de825a81e..0b91d19a54 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -18,6 +18,7 @@ index-servers = server1 server2 + server3 [server1] username:me @@ -28,6 +29,10 @@ password: secret realm:acme repository:http://another.pypi/ + +[server3] +username:cbiggles +password:yh^%#rest-of-my-password """ PYPIRC_OLD = """\ @@ -113,6 +118,20 @@ def test_server_empty_registration(self): 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) From 90d7e0e1b1e1da326f73d4dbdc81e9a4e72daa5c Mon Sep 17 00:00:00 2001 From: Berker Peksag Date: Fri, 24 Jun 2016 08:48:27 +0300 Subject: [PATCH 2443/2594] 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 0b91d19a54..3dd92d6166 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -52,14 +52,14 @@ """ -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 @@ def initialize_options(self): 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 01acf2375f..e68b0af3ce 100644 --- a/tests/test_register.py +++ b/tests/test_register.py @@ -12,7 +12,7 @@ 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 @@ def getheader(self, name, default=None): }.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 5a04e0ddd0..5444b815a8 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -23,7 +23,7 @@ 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.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 3eecf8afd4..e836cc4947 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -12,7 +12,7 @@ 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 @@ def getcode(self): return self.code -class uploadTestCase(PyPIRCCommandTestCase): +class uploadTestCase(BasePyPIRCCommandTestCase): def setUp(self): super(uploadTestCase, self).setUp() From eb17f80c10c171808db4f6d81709dc3c9ebf27d6 Mon Sep 17 00:00:00 2001 From: Donald Stufft Date: Wed, 6 Jul 2016 15:27:35 -0400 Subject: [PATCH 2444/2594] 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 106e146598..9b06d7699e 100644 --- a/config.py +++ b/config.py @@ -21,7 +21,7 @@ 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 4de825a81e..c37381550d 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -87,7 +87,7 @@ def test_server_registration(self): 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 @@ def test_server_registration(self): 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 8532369aa5..5a462e5a94 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -127,7 +127,7 @@ def test_upload(self): 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 From cbbadc228b01710721939a776b4f2dd008e9c322 Mon Sep 17 00:00:00 2001 From: Donald Stufft Date: Wed, 6 Jul 2016 16:18:39 -0400 Subject: [PATCH 2445/2594] 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 382aca8fc1..2d5770c618 100644 --- a/config.py +++ b/config.py @@ -21,7 +21,7 @@ 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 4de825a81e..c37381550d 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -87,7 +87,7 @@ def test_server_registration(self): 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 @@ def test_server_registration(self): 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 dccaf77e3e..bf4d558bf7 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -90,7 +90,7 @@ def test_finalize_options(self): 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 @@ def test_upload(self): 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) From f568a513f3be48c8f778da29719b7573b869e6c0 Mon Sep 17 00:00:00 2001 From: Donald Stufft Date: Wed, 6 Jul 2016 17:46:37 -0400 Subject: [PATCH 2446/2594] 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 6763f57388..c7bbd6d108 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -130,7 +130,7 @@ def test_config_interpolation(self): 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) From f11c5c1af6806da60cedfdbc94b4548fefec73bb Mon Sep 17 00:00:00 2001 From: Martin Panter Date: Mon, 11 Jul 2016 07:51:37 +0000 Subject: [PATCH 2447/2594] 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 8f62b1a848..4e397ea4c9 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -243,7 +243,7 @@ def test_check_extensions_list(self): 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) From 7c0cfe7a3e4123bbb66f540e829e98673ca1ed7f Mon Sep 17 00:00:00 2001 From: Xavier de Gaye Date: Fri, 22 Jul 2016 12:15:29 +0200 Subject: [PATCH 2448/2594] 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 f507ef7750..5edc24a3a1 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 @@ def test_spawn(self): # 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 @@ def test_spawn(self): 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') From c3f23f70001777fd66c7c33b9101bb114540a53a Mon Sep 17 00:00:00 2001 From: Stefan Krah Date: Wed, 3 Aug 2016 11:18:26 +0200 Subject: [PATCH 2449/2594] 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 92d14dfee7..3f321c28dc 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -227,6 +227,8 @@ def runtime_library_dir_option(self, dir): 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] From e639c9f681154bb0e5530543beefd015040406ac Mon Sep 17 00:00:00 2001 From: Donald Stufft Date: Wed, 3 Aug 2016 18:43:38 -0400 Subject: [PATCH 2450/2594] 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 9b06d7699e..96469ca91c 100644 --- a/config.py +++ b/config.py @@ -21,7 +21,7 @@ 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 c37381550d..8286e1d9ac 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -87,7 +87,7 @@ def test_server_registration(self): 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 @@ def test_server_registration(self): 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 5a462e5a94..1402343e0a 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -127,7 +127,7 @@ def test_upload(self): 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 From 774707dce2a31748382aaa019848014a17d0a04a Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 20 Aug 2016 17:31:07 -0400 Subject: [PATCH 2451/2594] 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 35a06eb09b..f1b8d91977 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -91,9 +91,6 @@ def checking_metadata(self): 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 @@ def initialize_options(self): 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 @@ def finalize_options(self): 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: From f3f736a749a120d124e54e325875775c9e124ee0 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Tue, 30 Aug 2016 10:47:49 -0700 Subject: [PATCH 2452/2594] =?UTF-8?q?Issue=20#27895:=20=20Spelling=20fixes?= =?UTF-8?q?=20(Contributed=20by=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 5e18c61360..77a07ef39d 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -125,7 +125,7 @@ def test_reg_class(self): 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')) From 3ac5842595463683cbc1d980a8b770d84cef84f9 Mon Sep 17 00:00:00 2001 From: Vinay Sajip Date: Wed, 31 Aug 2016 08:22:29 +0100 Subject: [PATCH 2453/2594] 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 bed1384900..78ae5757c3 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 c89d5efc45..b5d9dc387d 100644 --- a/cmd.py +++ b/cmd.py @@ -329,8 +329,7 @@ def get_sub_commands(self): # -- 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 f1bfb24923..e9274d925a 100644 --- a/command/bdist_dumb.py +++ b/command/bdist_dumb.py @@ -85,7 +85,7 @@ def run(self): 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 f03a4e31d8..5e51ae4ba1 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -363,9 +363,9 @@ def check_extensions_list(self, extensions): 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 b1fd09e016..4ae153d194 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 9474e9c599..fca05d69c6 100644 --- a/command/install.py +++ b/command/install.py @@ -385,7 +385,7 @@ def dump_dirs(self, msg): 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 456d50d519..0fac94e9e5 100644 --- a/command/register.py +++ b/command/register.py @@ -94,7 +94,7 @@ def verify_metadata(self): ''' # 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 @@ def send_metadata(self): 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 @@ def send_metadata(self): 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 f1b8d91977..4fd1d4715d 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -412,7 +412,7 @@ def make_release_tree(self, base_dir, files): 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) From 0685847e2065e046808e038fd50d3368be24541f Mon Sep 17 00:00:00 2001 From: R David Murray Date: Wed, 31 Aug 2016 11:39:35 -0400 Subject: [PATCH 2454/2594] #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 18283dc722..0712e92c6a 100644 --- a/tests/test_build_py.py +++ b/tests/test_build_py.py @@ -168,7 +168,8 @@ def test_dont_write_bytecode(self): 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 5378aa8249..fda6315bbc 100644 --- a/tests/test_install_lib.py +++ b/tests/test_install_lib.py @@ -104,7 +104,8 @@ def test_dont_write_bytecode(self): 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(): From ac97b020927d8c00d43595afe46749ef6cab7159 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 1 Sep 2016 13:55:33 -0400 Subject: [PATCH 2455/2594] 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 fca05d69c6..0258d3deae 100644 --- a/command/install.py +++ b/command/install.py @@ -175,6 +175,7 @@ def initialize_options(self): 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 @@ def finalize_options(self): '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 @@ def handle_extra_path(self): 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(',') From b15f55c3838ea7dd2719178b3c8ca995b4e9e2e7 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 1 Sep 2016 21:55:22 -0400 Subject: [PATCH 2456/2594] 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 e719198c6d..391af3cba2 100644 --- a/tests/test_filelist.py +++ b/tests/test_filelist.py @@ -9,7 +9,7 @@ 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 @@ def test_non_local_discovery(self): 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()) From bfef652ebcddc99c4b891f96f3d42a3c51bfcccc Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 1 Sep 2016 23:27:45 -0400 Subject: [PATCH 2457/2594] 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 6522e69f06..db3f7a9680 100644 --- a/filelist.py +++ b/filelist.py @@ -6,7 +6,6 @@ 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 @@ def exclude_pattern (self, pattern, # ---------------------------------------------------------------------- # 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 391af3cba2..278809c09b 100644 --- a/tests/test_filelist.py +++ b/tests/test_filelist.py @@ -6,10 +6,8 @@ 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 @@ def test_process_template(self): 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() From 175d71c6352e17dc2b752160ec78d2cb90e7c271 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 5 Sep 2016 22:24:01 -0400 Subject: [PATCH 2458/2594] 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 278809c09b..e82bc3d2ed 100644 --- a/tests/test_filelist.py +++ b/tests/test_filelist.py @@ -7,7 +7,7 @@ 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 @@ def test_process_template(self): self.assertWarnings() +def test_suite(): + return unittest.makeSuite(FileListTestCase) + if __name__ == "__main__": - unittest.main() + run_unittest(test_suite()) From 84f35582fc98b213e51d9924ae9f726212069560 Mon Sep 17 00:00:00 2001 From: Martin Panter Date: Wed, 7 Sep 2016 12:03:06 +0000 Subject: [PATCH 2459/2594] =?UTF-8?q?Issue=20#27895:=20=20Spelling=20fixes?= =?UTF-8?q?=20(Contributed=20by=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 5e18c61360..77a07ef39d 100644 --- a/tests/test_msvc9compiler.py +++ b/tests/test_msvc9compiler.py @@ -125,7 +125,7 @@ def test_reg_class(self): 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')) From fe41e077426b41fee85c7bd5800029796fb4b8c9 Mon Sep 17 00:00:00 2001 From: R David Murray Date: Thu, 8 Sep 2016 13:59:53 -0400 Subject: [PATCH 2460/2594] #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 b5d9dc387d..939f795945 100644 --- a/cmd.py +++ b/cmd.py @@ -221,7 +221,7 @@ def ensure_string(self, option, default=None): 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 f6c21aee44..a4bd5a589d 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -623,7 +623,7 @@ def add_ui(self): 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 @@ def add_ui(self): 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 90a8380a04..ccc70e6465 100644 --- a/command/build_scripts.py +++ b/command/build_scripts.py @@ -51,7 +51,7 @@ def run(self): 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 c879646c0f..1c36990347 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 0b1fd19ff6..2119127622 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -716,7 +716,7 @@ def _remove_visual_c_ref(self, manifest_file): 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 f72b7f5a19..681359870c 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 b0dd9f45bf..062c98f248 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) From d0f84684de3a4c3d9dbb1602f9810d9688847e09 Mon Sep 17 00:00:00 2001 From: Zachary Ware Date: Fri, 9 Sep 2016 18:29:10 -0700 Subject: [PATCH 2461/2594] 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 681359870c..229626e1b4 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -418,7 +418,11 @@ def expand_makefile_vars(s, vars): 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 From ba17bdeda7e2d5c1ac05be611569785b3aec20bd Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 11 Sep 2016 12:50:02 +0300 Subject: [PATCH 2462/2594] 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 6522e69f06..c92d5fdba3 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 391af3cba2..c71342d0dc 100644 --- a/tests/test_filelist.py +++ b/tests/test_filelist.py @@ -51,14 +51,14 @@ def test_glob_to_re(self): 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) From 8a0f7a5412ea23ca0a9a2f9ef9352879b11909d1 Mon Sep 17 00:00:00 2001 From: Xavier de Gaye Date: Sun, 11 Sep 2016 22:22:24 +0200 Subject: [PATCH 2463/2594] 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 229626e1b4..8bf1a7016b 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -418,11 +418,12 @@ def expand_makefile_vars(s, vars): 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 From 0c6f3d33cd15a4558e437b70b0507f221f00e3eb Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Wed, 28 Sep 2016 23:13:58 -0700 Subject: [PATCH 2464/2594] 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 f03a4e31d8..7c278ef0af 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -166,6 +166,7 @@ def finalize_options(self): 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 4e397ea4c9..f3df564e37 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -195,6 +195,13 @@ def test_finalize_options(self): 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 From b8032e136b864af0607c781371ee6a8730f67efc Mon Sep 17 00:00:00 2001 From: Zachary Ware Date: Sat, 1 Oct 2016 16:15:09 -0500 Subject: [PATCH 2465/2594] 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 74de782d8a..9155626a47 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -715,13 +715,6 @@ def get_libraries(self, ext): 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 From 06e8590eef402ebcb2a179875572549b5fe8d946 Mon Sep 17 00:00:00 2001 From: Berker Peksag Date: Tue, 4 Oct 2016 20:54:44 +0300 Subject: [PATCH 2466/2594] 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 959fa9085c..3d22868e31 100644 --- a/tests/test_check.py +++ b/tests/test_check.py @@ -7,6 +7,12 @@ 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 @@ def foo(): 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): From 45112961a7fa764f5a26663563da5ff84abb4dc1 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 14 Oct 2016 14:06:28 -0400 Subject: [PATCH 2467/2594] 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 4fd1d4715d..690bd66f2c 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -4,17 +4,17 @@ 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 From dcbe64d83ff6c6f60e0fc5756f91c966da930b84 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 14 Oct 2016 14:08:28 -0400 Subject: [PATCH 2468/2594] 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 690bd66f2c..c13dbbf895 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -8,7 +8,9 @@ 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 From 03f46fcf082aa89ab4dbb516d333918429f6e4e0 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 14 Oct 2016 14:10:07 -0400 Subject: [PATCH 2469/2594] 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 c13dbbf895..28cd0d9ef1 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -261,11 +261,13 @@ def add_defaults(self): # 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) From c6f609397210c4fab5163aaa8eddf16dc0bee370 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 14 Oct 2016 14:44:11 -0400 Subject: [PATCH 2470/2594] 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 28cd0d9ef1..e5121666b8 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -95,6 +95,8 @@ def checking_metadata(self): 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 @@ def add_defaults(self): 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 From 8ed698da6784b13344a63c9988faa00d281bc6d9 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 14 Oct 2016 14:53:32 -0400 Subject: [PATCH 2471/2594] 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 e5121666b8..c66d82713d 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -220,6 +220,15 @@ def add_defaults(self): 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 @@ def add_defaults(self): 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 @@ def add_defaults(self): 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 @@ def add_defaults(self): 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()) From 3b05fac47289e9835e5bd6f2efb130df59a73ffb Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 14 Oct 2016 15:39:01 -0400 Subject: [PATCH 2472/2594] 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 c66d82713d..0cc01192f3 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 @@ def _add_defaults_standards(self): 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 @@ def _add_defaults_standards(self): 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) From ed0dd7aea6253700672dc87b3abe63b3b52b63e3 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 14 Oct 2016 15:41:42 -0400 Subject: [PATCH 2473/2594] 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 0cc01192f3..180e28626d 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 @@ def add_defaults(self): 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 @@ def _add_defaults_standards(self): 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 @@ def _add_defaults_standards(self): 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) From 2a8668d01cd6c522ce9b7af402bbd7e32a926cc3 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 23 Oct 2016 22:56:14 +0300 Subject: [PATCH 2474/2594] 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 25c14abd32..e9795ee4b2 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 @@ def tearDown(self): # 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 @@ def test_quiet(self): # 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') From 874f6ddbb74da00b9b323721cd79509fc1d5487b Mon Sep 17 00:00:00 2001 From: Xavier de Gaye Date: Thu, 17 Nov 2016 09:00:19 +0100 Subject: [PATCH 2475/2594] 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 acc99e78c1..85d09906f2 100644 --- a/tests/test_build_clib.py +++ b/tests/test_build_clib.py @@ -3,7 +3,7 @@ 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 @@ def test_run(self): 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 6be0ca233b..be7f5f38aa 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -41,6 +41,9 @@ 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') @@ -295,6 +298,9 @@ def test_compiler_option(self): 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 0c8dbd8269..6e566e7915 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 @@ def test_dump_file(self): @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 9313330e2b..287ab1989e 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -17,6 +17,7 @@ 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 @@ def test_record(self): 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 fc4d1de185..fe4a2994e3 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -39,15 +39,6 @@ def test_get_python_lib(self): 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) From f63a2b9c7a165de24a4a5b4b8c88a298d45dddad Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Tue, 13 Dec 2016 09:06:24 -0800 Subject: [PATCH 2476/2594] 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 22299543a97ffc1525a3b1c778cb158d6c6430ad..253c2e2eccefa79393827f44f85680536906574a 100644 GIT binary patch delta 117970 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

)t?oHVn5Sq%T?GqR|24h(;z`zbGppoRWXDn~U12 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 Q;XT#&G~W}hI31h(Fa4nPXaE2J delta 119901 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ൣ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 diff --git a/command/wininst-14.0.exe b/command/wininst-14.0.exe index 0dac1103d98db0af1e9027c41fe921136c5f6396..46f5f356676c800f99742deb6bf4c0a96aa166c0 100644 GIT binary patch delta 106589 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 z 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 Date: Sun, 18 Dec 2016 01:23:09 +0000 Subject: [PATCH 2477/2594] 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 e9795ee4b2..c5962dddd2 100644 --- a/tests/test_bdist_rpm.py +++ b/tests/test_bdist_rpm.py @@ -96,7 +96,7 @@ def test_quiet(self): @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') From 4499210cb8c3c947708479e4bd9d10370aabaf83 Mon Sep 17 00:00:00 2001 From: Berker Peksag Date: Wed, 1 Feb 2017 04:42:48 +0300 Subject: [PATCH 2478/2594] 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 939f795945..dba3191e58 100644 --- a/cmd.py +++ b/cmd.py @@ -401,34 +401,3 @@ def make_file(self, infiles, outfile, func, args, # 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 From 7bb68f652d75c6a8585c95a75757ef6ca0b7d151 Mon Sep 17 00:00:00 2001 From: Ryan Gonzalez Date: Fri, 14 Apr 2017 04:00:25 -0500 Subject: [PATCH 2479/2594] 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 180e28626d..52eaa15d47 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -96,7 +96,7 @@ def checking_metadata(self): 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 From 9381f65a8ab5cd89e262e60bdac83834e1eafd22 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 25 Apr 2017 02:11:09 +0200 Subject: [PATCH 2480/2594] 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 be7f5f38aa..96e5f03095 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -41,6 +41,13 @@ 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() + + def _test_build_ext(self): cmd = support.missing_compiler_executable() if cmd is not None: self.skipTest('The %r command is not found' % cmd) From 28091832dcb224724ef4d89eaa042ff6fea00746 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 2 May 2017 13:11:50 +0200 Subject: [PATCH 2481/2594] 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 96e5f03095..a72218274c 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -37,17 +37,28 @@ def setUp(self): 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 @@ def _test_build_ext(self): 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) From 37a5021a1e623c5edf1a6a01ba71d967c4b1a4d2 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 4 May 2017 23:29:09 +0200 Subject: [PATCH 2482/2594] 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 8bf1a7016b..90004acea8 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) From b19c8614bc78fef2b48148e4ec4ea46274320416 Mon Sep 17 00:00:00 2001 From: Jeremy Kloth Date: Tue, 9 May 2017 09:24:13 -0600 Subject: [PATCH 2483/2594] 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 90004acea8..2bcd1dd288 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": From 252748ea7dc284747562b5de212aba51d79a2479 Mon Sep 17 00:00:00 2001 From: Jon Dufresne Date: Thu, 18 May 2017 07:35:54 -0700 Subject: [PATCH 2484/2594] 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 2119127622..c401ddc86e 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: From c3052243e0aa7e756645a116fef7ed9ff09bfd5e Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Mon, 4 Sep 2017 16:36:05 -0700 Subject: [PATCH 2485/2594] 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 efba27e1c8..eef702cf01 100644 --- a/tests/test_unixccompiler.py +++ b/tests/test_unixccompiler.py @@ -51,14 +51,6 @@ def gcv(v): 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 3f321c28dc..ab4d4de156 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -233,8 +233,6 @@ def runtime_library_dir_option(self, dir): 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 fdcf6fabae..b8a69114c8 100644 --- a/util.py +++ b/util.py @@ -16,21 +16,17 @@ 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": From 91a97f1145f3d78667b162907af985b8d6fcc61a Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Wed, 6 Sep 2017 10:01:38 -0700 Subject: [PATCH 2486/2594] 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 d3e1d3af22..6309c3e248 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -318,26 +318,30 @@ def get_exe_bytes(self): # 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 @@ def get_exe_bytes(self): 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() From 3a5968943a1722688994990326d2009210179e9d Mon Sep 17 00:00:00 2001 From: Zachary Ware Date: Wed, 6 Sep 2017 15:45:25 -0700 Subject: [PATCH 2487/2594] 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 9155626a47..7444565562 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -208,7 +208,7 @@ def finalize_options(self): 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 c401ddc86e..4c0036a0f1 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -55,7 +55,6 @@ PLAT_TO_VCVARS = { 'win32' : 'x86', 'win-amd64' : 'amd64', - 'win-ia64' : 'ia64', } class Reg: @@ -344,7 +343,7 @@ def initialize(self, plat_name=None): 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 @@ def initialize(self, plat_name=None): # 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 1048cd4159..d1de2fbfcb 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 4e9d79b7c6..e2fc380958 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -78,13 +78,6 @@ def test_get_platform(self): 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 b8a69114c8..9394c1e056 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 From d3c1d635ce32d35fb3b520d1ab009e401c7c1722 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Thu, 7 Sep 2017 11:49:23 -0700 Subject: [PATCH 2488/2594] 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 b120273fe9..ef1356b97d 100644 --- a/_msvccompiler.py +++ b/_msvccompiler.py @@ -17,6 +17,7 @@ import shutil import stat import subprocess +import winreg from distutils.errors import DistutilsExecError, DistutilsPlatformError, \ CompileError, LibError, LinkError @@ -24,10 +25,9 @@ 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 @@ def _find_exe(exe, paths=None): '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 4dc24886c8..70a9c93a4e 100644 --- a/tests/test_msvccompiler.py +++ b/tests/test_msvccompiler.py @@ -76,12 +76,12 @@ def test_vcruntime_skip_copy(self): 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 @@ def test_get_vc_env_unicode(self): 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) 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: [PATCH 2489/2594] 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 2bcd1dd288..e07a6c8b94 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -23,7 +23,7 @@ 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"]) From 6b18d32f779cf78bf0b0384277d52d8bf803d621 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Thu, 28 Sep 2017 22:44:27 -0700 Subject: [PATCH 2490/2594] 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 9394c1e056..83682628ba 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('/', '-') From 65cf5a85b0b59d3bab1f4b4e153557c6484ae825 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Wed, 25 Oct 2017 23:55:14 -0700 Subject: [PATCH 2491/2594] 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 7444565562..8fad9cdc2b 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -215,9 +215,9 @@ def finalize_options(self): 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 @@ def get_libraries(self, ext): 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 From 50a9c568d04fda21590254e60a27227b9a6eb650 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 8 Nov 2017 14:44:44 -0800 Subject: [PATCH 2492/2594] 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 1c36990347..6c5d77746b 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -234,8 +234,8 @@ def link(self, target_desc, objects, output_filename, output_dir=None, # 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") From 9d391e77e247e4718d94c3f6000d34832576d356 Mon Sep 17 00:00:00 2001 From: xdegaye Date: Sat, 18 Nov 2017 18:17:16 +0100 Subject: [PATCH 2493/2594] 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 02fa1e27a4..14ba4ca34b 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -162,7 +162,7 @@ def test_tarfile_vs_tar(self): # 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 03040afc79..a4e2d025f9 100644 --- a/tests/test_file_util.py +++ b/tests/test_file_util.py @@ -8,7 +8,7 @@ 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 @@ def test_move_file_exception_unpacking_unlink(self): 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) From c644f6f0fd7b432b8ff18d03c9c646273774c75b Mon Sep 17 00:00:00 2001 From: Berker Peksag Date: Thu, 23 Nov 2017 21:34:20 +0300 Subject: [PATCH 2494/2594] 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 62a24516cf..78c29ede6c 100644 --- a/dist.py +++ b/dist.py @@ -1188,12 +1188,38 @@ def get_long_description(self): 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 1f104cef67..50b456ec94 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -195,6 +195,13 @@ def test_finalize_options(self): 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 @@ 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',)} + 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'} From f7194b879e22a498a573d277fbcb3b9226412f89 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 28 Nov 2017 15:30:32 +0100 Subject: [PATCH 2495/2594] 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 bf8d8dd2f5..e66d103f7d 100644 --- a/config.py +++ b/config.py @@ -77,7 +77,7 @@ def _read_pypirc(self): # 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 @@ def _read_pypirc(self): 'password': config.get(server, 'password'), 'repository': repository, 'server': server, - 'realm': self.DEFAULT_REALM} + 'realm': realm} return {} From 3c144a62210850ec965d8ebf81c0bad9558e18b0 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 28 Nov 2017 23:19:26 +0100 Subject: [PATCH 2496/2594] 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 e66d103f7d..2171abd696 100644 --- a/config.py +++ b/config.py @@ -51,7 +51,6 @@ def _read_pypirc(self): 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 @@ def _read_pypirc(self): # 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 @@ def _read_pypirc(self): 'password': config.get(server, 'password'), 'repository': repository, 'server': server, - 'realm': realm} + 'realm': self.DEFAULT_REALM} return {} From cba37f7ece298acceaa5506d24548f6ec039c9b3 Mon Sep 17 00:00:00 2001 From: Neil Schemenauer Date: Mon, 4 Dec 2017 18:58:12 -0800 Subject: [PATCH 2497/2594] 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 78c29ede6c..6cf0a0d663 100644 --- a/dist.py +++ b/dist.py @@ -27,6 +27,20 @@ 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 @@ def __init__(self, attrs=None): 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 @@ 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 + 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 @@ def set_requires(self, value): 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 @@ def set_obsoletes(self, value): 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 50b456ec94..0a19f0fb62 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -11,7 +11,9 @@ 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 @@ def test_requires_illegal(self): "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 @@ def test_obsoletes_illegal(self): "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 @@ def test_classifier(self): 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 @@ def test_keywords(self): 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 @@ def test_platforms(self): 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', From b0138c4df9e3d287a5a2995be53746c9d0581725 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 14 Dec 2017 11:39:34 +0100 Subject: [PATCH 2498/2594] 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 6309c3e248..83f0073265 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -338,8 +338,8 @@ def get_exe_bytes(self): 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' From 9a97c496dcd05e6b3d418903a3728daa790e29cc Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Fri, 19 Jan 2018 09:09:36 +1100 Subject: [PATCH 2499/2594] 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 ef1356b97d..c9d3c6c671 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 83f0073265..0871a4f7d6 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -337,11 +337,10 @@ def get_exe_bytes(self): # 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 From b3168e5e01e79300b17c1791eed97c4733fa7a49 Mon Sep 17 00:00:00 2001 From: Bo Bayles Date: Thu, 25 Jan 2018 18:02:03 -0600 Subject: [PATCH 2500/2594] 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 1fd574a9f1..f7752f9d12 100644 --- a/command/upload.py +++ b/command/upload.py @@ -159,8 +159,6 @@ def upload_file(self, command, pyversion, filename): 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 2cb2f6ce93..c17d8e7d54 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -143,6 +143,32 @@ def test_upload(self): 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 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: [PATCH 2501/2594] 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 f7752f9d12..32dda359ba 100644 --- a/command/upload.py +++ b/command/upload.py @@ -57,7 +57,8 @@ def finalize_options(self): 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) From 74d0f1dc010170301dafe46faacee84bf246c880 Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Mon, 16 Jul 2018 19:03:03 +0200 Subject: [PATCH 2502/2594] 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 e07a6c8b94..b433fc86ff 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -36,10 +36,8 @@ # 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 From 8be16112e6817cc23ee4f317bc58db5f1c7eb108 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Thu, 26 Jul 2018 04:23:10 -0700 Subject: [PATCH 2503/2594] 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 c9d3c6c671..30b3b47398 100644 --- a/_msvccompiler.py +++ b/_msvccompiler.py @@ -252,11 +252,11 @@ def initialize(self, plat_name=None): 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, From 0e48f661aab7b40bbe9c8e033594f61e7e712b4a Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 4 Sep 2018 11:01:09 +0200 Subject: [PATCH 2504/2594] 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 5dd415a283..5387688093 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 5edc24a3a1..0d455385d8 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 @@ def test_spawn(self): 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) From d2eb595732e54ff8e785483b2a47fd3d20431057 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 7 Sep 2018 17:30:33 +0200 Subject: [PATCH 2505/2594] 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 0258d3deae..41bf4bb9fb 100644 --- a/command/install.py +++ b/command/install.py @@ -223,7 +223,7 @@ def initialize_options(self): 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 From 4a6183a2492528b3072f91cd221c86bee8853b09 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 7 Sep 2018 18:13:10 +0200 Subject: [PATCH 2506/2594] 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 41bf4bb9fb..a1d1a1ea37 100644 --- a/command/install.py +++ b/command/install.py @@ -223,7 +223,7 @@ def initialize_options(self): 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 From 64f5d63b221c89c3ef543f4ef10a99ff0027a63c Mon Sep 17 00:00:00 2001 From: Julien Malard Date: Sun, 9 Sep 2018 02:01:26 +0530 Subject: [PATCH 2507/2594] 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 b301a8338c..3a6602bc8b 100644 --- a/log.py +++ b/log.py @@ -31,7 +31,10 @@ def _log(self, level, msg, args): # 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): From b277efb9be14751c874aff02f72237bad8600df3 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 12 Sep 2018 01:40:06 +0200 Subject: [PATCH 2508/2594] 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 a1d1a1ea37..c625c95bf7 100644 --- a/command/install.py +++ b/command/install.py @@ -223,7 +223,7 @@ def initialize_options(self): 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 From e5fede1f013671465e4023d5048d2fbab33a8932 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Thu, 20 Sep 2018 13:38:34 -0700 Subject: [PATCH 2509/2594] 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 f762f5d987..c80b3edc02 100644 --- a/tests/test_bdist.py +++ b/tests/test_bdist.py @@ -39,6 +39,9 @@ def test_skip_build(self): 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 5d17ab19a9..4c19bbab21 100644 --- a/tests/test_bdist_wininst.py +++ b/tests/test_bdist_wininst.py @@ -5,6 +5,8 @@ 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): From 323ff835845c6b4e7e8a8e004f5ad4eaf878e4d8 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 23 Sep 2018 09:12:59 +0300 Subject: [PATCH 2510/2594] 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 3a6602bc8b..8ef6b28ea2 100644 --- a/log.py +++ b/log.py @@ -27,14 +27,13 @@ def _log(self, level, msg, args): 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 0c2ad7a426..22c26246ca 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) From a23e74ec65eaca077c3e8f21d592823c821e98f6 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 23 Sep 2018 14:10:07 +0300 Subject: [PATCH 2511/2594] 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 22c26246ca..75cf900617 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 @@ def test_non_ascii(self): # 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), \ From f45153a292dc749defaa9c6c1ce9c4062f083b6d Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Sat, 27 Oct 2018 16:48:33 -0400 Subject: [PATCH 2512/2594] 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 30b3b47398..84b4ef5959 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 From b841e0aececb0e28ff3fabe29bf22c4b86fae9d8 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 5 Nov 2018 16:20:25 +0200 Subject: [PATCH 2513/2594] 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 e9274d925a..f0d6b5b8cd 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 a4bd5a589d..80104c372d 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 ac4621791d..02f10dd89d 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 0871a4f7d6..fde56754e8 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 8fad9cdc2b..158465d233 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -365,7 +365,7 @@ def check_extensions_list(self, extensions): 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 From 15789c14939f1c441c95963419e1b3f941a2831d Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 5 Dec 2018 21:46:25 +0200 Subject: [PATCH 2514/2594] 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 78ae5757c3..b002dc3b84 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 14ba4ca34b..e9aad0e40f 100644 --- a/tests/test_archive_util.py +++ b/tests/test_archive_util.py @@ -122,12 +122,13 @@ def _tarinfo(self, path): 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 @@ def test_make_zipfile(self): 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 @@ def fake_zipfile(*a, **kw): [((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 c8ccdc2383..01a233bce3 100644 --- a/tests/test_bdist_dumb.py +++ b/tests/test_bdist_dumb.py @@ -84,7 +84,7 @@ def test_simple_built(self): 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 5444b815a8..23db126959 100644 --- a/tests/test_sdist.py +++ b/tests/test_sdist.py @@ -128,7 +128,9 @@ def test_prune_file_list(self): 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 @@ def test_add_defaults(self): 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')) From 95c0e2810d32cfe69d84f5af41f7874fb57facca Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Mon, 17 Dec 2018 02:59:02 -0500 Subject: [PATCH 2515/2594] 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 32dda359ba..613ea71129 100644 --- a/command/upload.py +++ b/command/upload.py @@ -121,14 +121,8 @@ def upload_file(self, command, pyversion, filename): '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", From f7d1e8f497ec20e55a7757e34d0a6229642ba59a Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 18 Dec 2018 16:17:56 +0100 Subject: [PATCH 2516/2594] 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 e2fc380958..bf0d4333f9 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -4,6 +4,7 @@ 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 @@ def _join(*path): 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 83682628ba..30a21e4afa 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() From 8fa927e2b2fc81adb420285e470439fc3a28fcee Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 20 Dec 2018 19:00:14 +0200 Subject: [PATCH 2517/2594] 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 b002dc3b84..565a3117b4 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 80104c372d..f335a34898 100644 --- a/command/bdist_msi.py +++ b/command/bdist_msi.py @@ -390,18 +390,18 @@ def add_scripts(self): # 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 4ae153d194..f511b88857 100644 --- a/command/config.py +++ b/command/config.py @@ -106,15 +106,14 @@ def _check_compiler(self): 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 @@ def search_cpp(self, pattern, body=None, headers=None, include_dirs=None, 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 52eaa15d47..b4996fcb1d 100644 --- a/command/sdist.py +++ b/command/sdist.py @@ -407,14 +407,13 @@ def read_manifest(self): 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 30a21e4afa..15cd2ad9a9 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) From 1db80e46af67c5aa0ffebe3fc61dd5e0787a42c5 Mon Sep 17 00:00:00 2001 From: Michael Felt Date: Fri, 28 Dec 2018 15:03:17 +0100 Subject: [PATCH 2518/2594] 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 f511b88857..aeda408e73 100644 --- a/command/config.py +++ b/command/config.py @@ -328,7 +328,6 @@ def check_header(self, header, include_dirs=None, library_dirs=None, 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 6e566e7915..b735fd334d 100644 --- a/tests/test_config_cmd.py +++ b/tests/test_config_cmd.py @@ -39,11 +39,17 @@ def test_dump_file(self): @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 */') 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: [PATCH 2519/2594] 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 408a203b85..23f488506f 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$ From cb20330b1f00ca7864338c7518a8f0883b3844a1 Mon Sep 17 00:00:00 2001 From: Marc Schlaich Date: Sun, 20 Jan 2019 19:47:42 +0100 Subject: [PATCH 2520/2594] 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 84b4ef5959..58b20a2102 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 From 95d86241efd33f8db71e72352b31185389f4d68d Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Mon, 4 Feb 2019 17:15:13 -0800 Subject: [PATCH 2521/2594] 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 158465d233..0428466b00 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -161,9 +161,10 @@ def finalize_options(self): # 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 b433fc86ff..40af493cdf 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -29,9 +29,7 @@ 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 a72218274c..88847f9e9a 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -177,10 +177,12 @@ def test_finalize_options(self): 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 From 9d329c1bbe7e35a3a4b5810f90ff292d51d7a66d Mon Sep 17 00:00:00 2001 From: Kevin Adler Date: Mon, 4 Mar 2019 08:48:40 -0600 Subject: [PATCH 2522/2594] 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 ab4d4de156..d10a78da31 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -188,7 +188,15 @@ def link(self, target_desc, objects, 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) From f3e20900c492253924944707f390a9c12d7a30fd Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Tue, 12 Mar 2019 08:39:57 -0700 Subject: [PATCH 2523/2594] 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 77ef788e24..344084afb7 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -60,6 +60,7 @@ def setUp(self): 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 0a19f0fb62..cc34725a99 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -463,7 +463,7 @@ def test_custom_pydistutils(self): # 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)) From 1078515edfad6bf60085a2611fe24ffd497474ca Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 15 Mar 2019 14:57:52 +0100 Subject: [PATCH 2524/2594] 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 40af493cdf..a3494670db 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 fe4a2994e3..4bf6a067d4 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -9,7 +9,7 @@ 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 @@ def test_srcdir_independent_of_cwd(self): '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 compiler: 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 From 4c6000461394d2a969c17b98ff7b64e7d6f6eee4 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 18 Mar 2019 17:19:02 +0100 Subject: [PATCH 2525/2594] 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 4bf6a067d4..245a6c86b1 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 @@ def test_srcdir_independent_of_cwd(self): 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 compiler: 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 From 868d8498bbccf63c274056959055800d02cac0d5 Mon Sep 17 00:00:00 2001 From: Philipp A Date: Wed, 27 Mar 2019 22:34:19 +0100 Subject: [PATCH 2526/2594] 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 7ebe707cff..04c2f9642d 100644 --- a/command/check.py +++ b/command/check.py @@ -120,7 +120,8 @@ def check_restructuredtext(self): 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 0000000000..d7b4ae38b0 --- /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 3d22868e31..e534aca1d4 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 @@ 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 @@ def _run(self, metadata=None, **options): 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 @@ def test_check_restructuredtext(self): 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 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: [PATCH 2527/2594] 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 02f10dd89d..20ca7ac6dc 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -537,7 +537,8 @@ def _make_spec_file(self): '', '%' + 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 fde56754e8..1cf2e963e0 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -247,47 +247,49 @@ def create_exe(self, arcname, fullname, bitmap=None): 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: [PATCH 2528/2594] 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 1cf2e963e0..3a616883be 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -269,7 +269,7 @@ def create_exe(self, arcname, fullname, bitmap=None): # 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: From ef2792fe08aabeb870e8f489d2bbfe2898ce1766 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 11 Apr 2019 01:38:48 +0200 Subject: [PATCH 2529/2594] 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 245a6c86b1..236755d095 100644 --- a/tests/test_sysconfig.py +++ b/tests/test_sysconfig.py @@ -92,6 +92,9 @@ def set_executables(self, **kw): 'CCSHARED': '--sc-ccshared', 'LDSHARED': 'sc_ldshared', 'SHLIB_SUFFIX': 'sc_shutil_suffix', + + # On macOS, disable _osx_support.customize_compiler() + 'CUSTOMIZED_OSX_COMPILER': 'True', } comp = compiler() From 1d1c462903645fa567508a1272ed28c5f334c24c Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 17 Apr 2019 16:26:36 +0200 Subject: [PATCH 2530/2594] 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 5387688093..888327270e 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 0d455385d8..f9ae69ef86 100644 --- a/tests/test_spawn.py +++ b/tests/test_spawn.py @@ -87,11 +87,52 @@ def test_find_executable(self): 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) From 3dd8d681a9db9ca36372f3a6a8fe6457076ea8b0 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 17 Apr 2019 17:44:06 +0200 Subject: [PATCH 2531/2594] 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 888327270e..d3a12c2833 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: From 89a5c175e47d95d33a700eedecf402e78d2a964a Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 25 Apr 2019 11:59:34 +0200 Subject: [PATCH 2532/2594] 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 c6f52e61e1..a86df0bc7f 100644 --- a/command/build.py +++ b/command/build.py @@ -116,7 +116,7 @@ def finalize_options(self): 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 a3494670db..570a612d1b 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -28,7 +28,12 @@ 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 From eb3478f2cdccd5489b64f7bb8cec4988fba985c7 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 25 Apr 2019 20:13:10 +0200 Subject: [PATCH 2533/2594] 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 0428466b00..1672d02acf 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -714,20 +714,5 @@ def get_libraries(self, ext): # 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 From 03ae9d8b1618be1a9dc5c9a3349fb01b4c7c7d14 Mon Sep 17 00:00:00 2001 From: Paul Monson Date: Thu, 25 Apr 2019 11:36:45 -0700 Subject: [PATCH 2534/2594] 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 58b20a2102..c7ac3f049e 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 d3a12c2833..ceb94945dc 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 570a612d1b..b51629eb94 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -15,6 +15,7 @@ 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 15cd2ad9a9..50550e1893 100644 --- a/util.py +++ b/util.py @@ -15,7 +15,7 @@ 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, From 736a36a6342656cf7fda44eb3060ce2d3d092ef0 Mon Sep 17 00:00:00 2001 From: xdegaye Date: Mon, 29 Apr 2019 09:27:40 +0200 Subject: [PATCH 2535/2594] 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 1672d02acf..c3b9602461 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -714,5 +714,20 @@ def get_libraries(self, ext): # 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 From 9d9a6d30b814bfdd7265e67ae9eea2f6e2db1657 Mon Sep 17 00:00:00 2001 From: "E. M. Bray" Date: Fri, 24 May 2019 17:33:47 +0200 Subject: [PATCH 2536/2594] 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 c3b9602461..2d7cdf063f 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -714,20 +714,32 @@ def get_libraries(self, ext): # 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 From b30cb4676572d51a26f3b74a40035df97f8a41c7 Mon Sep 17 00:00:00 2001 From: Xtreak Date: Mon, 3 Jun 2019 04:42:33 +0530 Subject: [PATCH 2537/2594] 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 b71d1d39bc..1a411ed111 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -545,7 +545,7 @@ def compile(self, sources, output_dir=None, macros=None, '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 From d048c412f43c2b9eaa1e282fa2d3bacec2a2d82e Mon Sep 17 00:00:00 2001 From: Marcin Niemira Date: Sun, 9 Jun 2019 07:05:06 +1000 Subject: [PATCH 2538/2594] 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 20ca7ac6dc..74381cc69a 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -309,10 +309,7 @@ def run(self): # 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') From 69b935025661716ac64b3e2b049ed9b703dfe96a Mon Sep 17 00:00:00 2001 From: Paul Monson Date: Wed, 12 Jun 2019 10:16:49 -0700 Subject: [PATCH 2539/2594] 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 c7ac3f049e..6e14f330d7 100644 --- a/_msvccompiler.py +++ b/_msvccompiler.py @@ -93,6 +93,7 @@ def _find_vc2017(): 'x86' : 'x86', 'x86_amd64' : 'x64', 'x86_arm' : 'arm', + 'x86_arm64' : 'arm64' } def _find_vcvarsall(plat_spec): @@ -190,6 +191,7 @@ def _find_exe(exe, paths=None): '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 4c19bbab21..163f1cc97b 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 50550e1893..17a94bc428 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 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: [PATCH 2540/2594] 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 3a616883be..acaa184b5f 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 From 6b9a31e6602723d98fce4c37f7151fdbab17367b Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 3 Jul 2019 11:12:27 +0200 Subject: [PATCH 2541/2594] 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 7385c6bbf6..041309851d 100644 --- a/tests/support.py +++ b/tests/support.py @@ -6,6 +6,7 @@ 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 @@ def tearDown(self): 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 88847f9e9a..52d36b2484 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -15,6 +15,7 @@ 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 @@ def setUp(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. - 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 @@ def test_build_ext(self): 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'}) From 850d4faed59006e8dbfcee2bbec13d1985d6538a Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 5 Jul 2019 10:44:12 +0200 Subject: [PATCH 2542/2594] 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 acaa184b5f..b5ed6f041e 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 163f1cc97b..5c3d025d33 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 @@ def test_get_exe_bytes(self): # 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 From 3665ddcf056ef72aa6e8e92aee81684d146d5dde Mon Sep 17 00:00:00 2001 From: "Bernhard M. Wiedemann" Date: Thu, 1 Aug 2019 15:18:03 +0200 Subject: [PATCH 2543/2594] 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 2d7cdf063f..38bb8fd93c 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -490,7 +490,8 @@ def build_extension(self, ext): "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 From cab8dd1a30b14542fcfe7ab63f8cd8b5358222da Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 8 Aug 2019 08:42:54 +0300 Subject: [PATCH 2544/2594] 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 15f14c7de3..8671cd2fc5 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -45,6 +45,14 @@ def test_cmp_strict(self): 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 @@ def test_cmp(self): 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 af14cc1348..c33bebaed2 100644 --- a/version.py +++ b/version.py @@ -166,6 +166,8 @@ def __str__ (self): 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 @@ def __repr__ (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 From a85a63840b076c7a9d165db731ca6d1be28287e8 Mon Sep 17 00:00:00 2001 From: Pablo Galindo Date: Thu, 8 Aug 2019 23:25:46 +0100 Subject: [PATCH 2545/2594] 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 c80b3edc02..130d8bf155 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 @@ def test_skip_build(self): 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 From cd848cbc425ba0735e0987040f201de0b13f0486 Mon Sep 17 00:00:00 2001 From: Anonymous Maarten Date: Wed, 28 Aug 2019 19:11:03 +0200 Subject: [PATCH 2546/2594] 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 1a411ed111..4cfc6c7065 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -781,8 +781,9 @@ def has_function(self, funcname, includes=None, include_dirs=None, 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: From 5ffd6d26a7806d8b8cc0aad93e50cc63bae294a1 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Tue, 10 Sep 2019 14:52:23 +0100 Subject: [PATCH 2547/2594] 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 6e14f330d7..e8e4b717b9 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] From b09909462acad0d0175cf2fcef76cf1e1c7adc76 Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Tue, 19 Nov 2019 19:45:20 +0000 Subject: [PATCH 2548/2594] 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 041309851d..259af882ec 100644 --- a/tests/support.py +++ b/tests/support.py @@ -39,8 +39,6 @@ def _log(self, level, msg, args): 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] From 2bf131d79252ce71e82c64ffbfdf1126d9602e90 Mon Sep 17 00:00:00 2001 From: Michael Felt Date: Sun, 15 Dec 2019 15:17:53 +0100 Subject: [PATCH 2549/2594] 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 17a94bc428..4b002ecef1 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) 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: [PATCH 2550/2594] 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 04c2f9642d..7ceabd3adf 100644 --- a/command/check.py +++ b/command/check.py @@ -80,8 +80,11 @@ def run(self): 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 @@ def check_metadata(self): 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.""" From daf18dd027a08dd318f348e12e75e9f4d8aaed0a Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Tue, 4 Feb 2020 16:24:30 +0100 Subject: [PATCH 2551/2594] 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 38bb8fd93c..1a9bd1200f 100644 --- a/command/build_ext.py +++ b/command/build_ext.py @@ -689,7 +689,15 @@ def get_export_symbols(self, ext): 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 52d36b2484..7e3eafa8ef 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -304,6 +304,19 @@ def test_get_source_files(self): 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 From d6bd6b1ea7efff2df20ca970de45fb0c2fe701e7 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Thu, 6 Feb 2020 15:48:10 +1100 Subject: [PATCH 2552/2594] 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 7e3eafa8ef..5e47e0773a 100644 --- a/tests/test_build_ext.py +++ b/tests/test_build_ext.py @@ -312,8 +312,8 @@ def test_unicode_module_names(self): 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']) From 8e5f7c22088ad8ee03096d1e591449bed2f14e44 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Mon, 10 Feb 2020 15:26:40 +0200 Subject: [PATCH 2553/2594] 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 f335a34898..0863a1883e 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 15d8bdff2b..418e60ec72 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 @@ def test_minimal(self): # 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() From d8d513d728552adee0677d5814eecc06d7775749 Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Tue, 3 Mar 2020 00:04:11 +0000 Subject: [PATCH 2554/2594] 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 e8e4b717b9..03a5986d98 100644 --- a/_msvccompiler.py +++ b/_msvccompiler.py @@ -97,28 +97,11 @@ def _find_vc2017(): } 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 @@ def _find_exe(exe, paths=None): '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 @@ def initialize(self, plat_name=None): 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 @@ def initialize(self, plat_name=None): 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 @@ def initialize(self, plat_name=None): 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 @@ def link(self, 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 70a9c93a4e..b518d6a78b 100644 --- a/tests/test_msvccompiler.py +++ b/tests/test_msvccompiler.py @@ -32,57 +32,6 @@ def _find_vcvarsall(plat_spec): 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 From c6f749b2b761da2b88cac62d8c6aa29f7276e3a7 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 4 Mar 2020 14:50:19 +0100 Subject: [PATCH 2555/2594] 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 ceb94945dc..aad277b0ca 100644 --- a/spawn.py +++ b/spawn.py @@ -8,11 +8,18 @@ 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 f9ae69ef86..73b0f5cb73 100644 --- a/tests/test_spawn.py +++ b/tests/test_spawn.py @@ -8,7 +8,6 @@ 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): From 6e7341b2cb740741d77e0184db2b9028b1b93773 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 10 Mar 2020 09:53:09 +0100 Subject: [PATCH 2556/2594] 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 c625c95bf7..aaa300efa9 100644 --- a/command/install.py +++ b/command/install.py @@ -30,14 +30,14 @@ 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 @@ def finalize_options(self): '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 b51629eb94..01ee519792 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 287ab1989e..51c80e0421 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -58,7 +58,8 @@ def check_path(got, expected): 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")) From c3ae6d416043ca528e9cf88c4789afa9df223c5b Mon Sep 17 00:00:00 2001 From: Michael Felt Date: Fri, 3 Apr 2020 16:38:28 +0200 Subject: [PATCH 2557/2594] 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 b735fd334d..8bd2c94237 100644 --- a/tests/test_config_cmd.py +++ b/tests/test_config_cmd.py @@ -47,8 +47,7 @@ def test_search_cpp(self): 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 From 74e9f56dc7857cee680727406517c2c086511e12 Mon Sep 17 00:00:00 2001 From: Joshua Root Date: Wed, 22 Apr 2020 17:44:10 +1000 Subject: [PATCH 2558/2594] 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 d10a78da31..4d7a6de740 100644 --- a/unixccompiler.py +++ b/unixccompiler.py @@ -288,7 +288,7 @@ def find_library_file(self, dirs, lib, debug=0): # 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: From ea19518aa139d623a4fad7d01159674ee43b9f15 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 30 Apr 2020 02:21:30 +0200 Subject: [PATCH 2559/2594] 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 85d09906f2..abd8313770 100644 --- a/tests/test_build_clib.py +++ b/tests/test_build_clib.py @@ -8,7 +8,6 @@ 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 8bd2c94237..9aeab07b46 100644 --- a/tests/test_config_cmd.py +++ b/tests/test_config_cmd.py @@ -39,7 +39,6 @@ def test_dump_file(self): @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 cc34725a99..60956dadef 100644 --- a/tests/test_dist.py +++ b/tests/test_dist.py @@ -8,7 +8,7 @@ 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 73b0f5cb73..cf1faad5f4 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 From a672328f1e2515f5b103df12d19fa3c2ffba68fc Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 30 Apr 2020 11:28:09 +0200 Subject: [PATCH 2560/2594] 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 03a5986d98..af8099a407 100644 --- a/_msvccompiler.py +++ b/_msvccompiler.py @@ -14,8 +14,6 @@ # 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 9f4c432d90..071fea5d03 100644 --- a/bcppcompiler.py +++ b/bcppcompiler.py @@ -14,10 +14,10 @@ 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 4cfc6c7065..b5ef143e72 100644 --- a/ccompiler.py +++ b/ccompiler.py @@ -8,7 +8,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.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 74381cc69a..550cbfa1e2 100644 --- a/command/bdist_rpm.py +++ b/command/bdist_rpm.py @@ -6,7 +6,6 @@ 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 b5ed6f041e..0e9ddaa214 100644 --- a/command/bdist_wininst.py +++ b/command/bdist_wininst.py @@ -8,7 +8,7 @@ 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 7ceabd3adf..ada2500646 100644 --- a/command/check.py +++ b/command/check.py @@ -11,7 +11,6 @@ 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 11afa24b77..d822ba0133 100644 --- a/command/upload.py +++ b/command/upload.py @@ -7,7 +7,6 @@ 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 6c5d77746b..66c12dd358 100644 --- a/cygwinccompiler.py +++ b/cygwinccompiler.py @@ -51,12 +51,10 @@ 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 4c0036a0f1..6934e964ab 100644 --- a/msvc9compiler.py +++ b/msvc9compiler.py @@ -19,8 +19,7 @@ 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 d1de2fbfcb..d5857cb1ff 100644 --- a/msvccompiler.py +++ b/msvccompiler.py @@ -13,7 +13,7 @@ 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 01ee519792..37feae5df7 100644 --- a/sysconfig.py +++ b/sysconfig.py @@ -15,7 +15,6 @@ 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) From b9d48323ce2571376ba34c05d65450f66e1581e9 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 15 May 2020 18:06:23 +0200 Subject: [PATCH 2561/2594] 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 1b939cbd5d..5d2e69e3e6 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -15,6 +15,7 @@ import os import sys import unittest +import warnings from test.support import run_unittest @@ -22,6 +23,7 @@ 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 From 5210488f65e41038e5721d31792fae784c39d649 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Wed, 20 May 2020 16:37:25 +0200 Subject: [PATCH 2562/2594] 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 d822ba0133..95e9fda186 100644 --- a/command/upload.py +++ b/command/upload.py @@ -16,6 +16,16 @@ 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 @@ def upload_file(self, command, pyversion, filename): content = f.read() finally: f.close() + meta = self.distribution.metadata data = { # action @@ -101,7 +112,6 @@ def upload_file(self, command, pyversion, filename): '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 @@ def upload_file(self, command, pyversion, filename): 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 c17d8e7d54..bca5516d2f 100644 --- a/tests/test_upload.py +++ b/tests/test_upload.py @@ -130,14 +130,30 @@ def test_upload(self): # 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 @@ def test_upload_correct_cr(self): 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): From cd7d6d56a1cf84cafea44f7cf7e357a926821f03 Mon Sep 17 00:00:00 2001 From: xoviat Date: Wed, 8 Nov 2017 16:41:32 -0600 Subject: [PATCH 2563/2594] [maint] move all files into subfolder --- README => distutils/README | 0 __init__.py => distutils/__init__.py | 0 _msvccompiler.py => distutils/_msvccompiler.py | 0 archive_util.py => distutils/archive_util.py | 0 bcppcompiler.py => distutils/bcppcompiler.py | 0 ccompiler.py => distutils/ccompiler.py | 0 cmd.py => distutils/cmd.py | 0 {command => distutils/command}/__init__.py | 0 {command => distutils/command}/bdist.py | 0 {command => distutils/command}/bdist_dumb.py | 0 {command => distutils/command}/bdist_msi.py | 0 {command => distutils/command}/bdist_rpm.py | 0 {command => distutils/command}/bdist_wininst.py | 0 {command => distutils/command}/build.py | 0 {command => distutils/command}/build_clib.py | 0 {command => distutils/command}/build_ext.py | 0 {command => distutils/command}/build_py.py | 0 {command => distutils/command}/build_scripts.py | 0 {command => distutils/command}/check.py | 0 {command => distutils/command}/clean.py | 0 {command => distutils/command}/command_template | 0 {command => distutils/command}/config.py | 0 {command => distutils/command}/install.py | 0 {command => distutils/command}/install_data.py | 0 {command => distutils/command}/install_egg_info.py | 0 {command => distutils/command}/install_headers.py | 0 {command => distutils/command}/install_lib.py | 0 {command => distutils/command}/install_scripts.py | 0 {command => distutils/command}/register.py | 0 {command => distutils/command}/sdist.py | 0 {command => distutils/command}/upload.py | 0 .../command}/wininst-10.0-amd64.exe | Bin {command => distutils/command}/wininst-10.0.exe | Bin .../command}/wininst-14.0-amd64.exe | Bin {command => distutils/command}/wininst-14.0.exe | Bin {command => distutils/command}/wininst-6.0.exe | Bin {command => distutils/command}/wininst-7.1.exe | Bin {command => distutils/command}/wininst-8.0.exe | Bin .../command}/wininst-9.0-amd64.exe | Bin {command => distutils/command}/wininst-9.0.exe | Bin config.py => distutils/config.py | 0 core.py => distutils/core.py | 0 cygwinccompiler.py => distutils/cygwinccompiler.py | 0 debug.py => distutils/debug.py | 0 dep_util.py => distutils/dep_util.py | 0 dir_util.py => distutils/dir_util.py | 0 dist.py => distutils/dist.py | 0 errors.py => distutils/errors.py | 0 extension.py => distutils/extension.py | 0 fancy_getopt.py => distutils/fancy_getopt.py | 0 file_util.py => distutils/file_util.py | 0 filelist.py => distutils/filelist.py | 0 log.py => distutils/log.py | 0 msvc9compiler.py => distutils/msvc9compiler.py | 0 msvccompiler.py => distutils/msvccompiler.py | 0 spawn.py => distutils/spawn.py | 0 sysconfig.py => distutils/sysconfig.py | 0 {tests => distutils/tests}/Setup.sample | 0 {tests => distutils/tests}/__init__.py | 0 {tests => distutils/tests}/includetest.rst | 0 {tests => distutils/tests}/support.py | 0 {tests => distutils/tests}/test_archive_util.py | 0 {tests => distutils/tests}/test_bdist.py | 0 {tests => distutils/tests}/test_bdist_dumb.py | 0 {tests => distutils/tests}/test_bdist_msi.py | 0 {tests => distutils/tests}/test_bdist_rpm.py | 0 {tests => distutils/tests}/test_bdist_wininst.py | 0 {tests => distutils/tests}/test_build.py | 0 {tests => distutils/tests}/test_build_clib.py | 0 {tests => distutils/tests}/test_build_ext.py | 0 {tests => distutils/tests}/test_build_py.py | 0 {tests => distutils/tests}/test_build_scripts.py | 0 {tests => distutils/tests}/test_check.py | 0 {tests => distutils/tests}/test_clean.py | 0 {tests => distutils/tests}/test_cmd.py | 0 {tests => distutils/tests}/test_config.py | 0 {tests => distutils/tests}/test_config_cmd.py | 0 {tests => distutils/tests}/test_core.py | 0 {tests => distutils/tests}/test_cygwinccompiler.py | 0 {tests => distutils/tests}/test_dep_util.py | 0 {tests => distutils/tests}/test_dir_util.py | 0 {tests => distutils/tests}/test_dist.py | 0 {tests => distutils/tests}/test_extension.py | 0 {tests => distutils/tests}/test_file_util.py | 0 {tests => distutils/tests}/test_filelist.py | 0 {tests => distutils/tests}/test_install.py | 0 {tests => distutils/tests}/test_install_data.py | 0 {tests => distutils/tests}/test_install_headers.py | 0 {tests => distutils/tests}/test_install_lib.py | 0 {tests => distutils/tests}/test_install_scripts.py | 0 {tests => distutils/tests}/test_log.py | 0 {tests => distutils/tests}/test_msvc9compiler.py | 0 {tests => distutils/tests}/test_msvccompiler.py | 0 {tests => distutils/tests}/test_register.py | 0 {tests => distutils/tests}/test_sdist.py | 0 {tests => distutils/tests}/test_spawn.py | 0 {tests => distutils/tests}/test_sysconfig.py | 0 {tests => distutils/tests}/test_text_file.py | 0 {tests => distutils/tests}/test_unixccompiler.py | 0 {tests => distutils/tests}/test_upload.py | 0 {tests => distutils/tests}/test_util.py | 0 {tests => distutils/tests}/test_version.py | 0 {tests => distutils/tests}/test_versionpredicate.py | 0 text_file.py => distutils/text_file.py | 0 unixccompiler.py => distutils/unixccompiler.py | 0 util.py => distutils/util.py | 0 version.py => distutils/version.py | 0 .../versionpredicate.py | 0 108 files changed, 0 insertions(+), 0 deletions(-) rename README => distutils/README (100%) rename __init__.py => distutils/__init__.py (100%) rename _msvccompiler.py => distutils/_msvccompiler.py (100%) rename archive_util.py => distutils/archive_util.py (100%) rename bcppcompiler.py => distutils/bcppcompiler.py (100%) rename ccompiler.py => distutils/ccompiler.py (100%) rename cmd.py => distutils/cmd.py (100%) rename {command => distutils/command}/__init__.py (100%) rename {command => distutils/command}/bdist.py (100%) rename {command => distutils/command}/bdist_dumb.py (100%) rename {command => distutils/command}/bdist_msi.py (100%) rename {command => distutils/command}/bdist_rpm.py (100%) rename {command => distutils/command}/bdist_wininst.py (100%) rename {command => distutils/command}/build.py (100%) rename {command => distutils/command}/build_clib.py (100%) rename {command => distutils/command}/build_ext.py (100%) rename {command => distutils/command}/build_py.py (100%) rename {command => distutils/command}/build_scripts.py (100%) rename {command => distutils/command}/check.py (100%) rename {command => distutils/command}/clean.py (100%) rename {command => distutils/command}/command_template (100%) rename {command => distutils/command}/config.py (100%) rename {command => distutils/command}/install.py (100%) rename {command => distutils/command}/install_data.py (100%) rename {command => distutils/command}/install_egg_info.py (100%) rename {command => distutils/command}/install_headers.py (100%) rename {command => distutils/command}/install_lib.py (100%) rename {command => distutils/command}/install_scripts.py (100%) rename {command => distutils/command}/register.py (100%) rename {command => distutils/command}/sdist.py (100%) rename {command => distutils/command}/upload.py (100%) rename {command => distutils/command}/wininst-10.0-amd64.exe (100%) rename {command => distutils/command}/wininst-10.0.exe (100%) rename {command => distutils/command}/wininst-14.0-amd64.exe (100%) rename {command => distutils/command}/wininst-14.0.exe (100%) rename {command => distutils/command}/wininst-6.0.exe (100%) rename {command => distutils/command}/wininst-7.1.exe (100%) rename {command => distutils/command}/wininst-8.0.exe (100%) rename {command => distutils/command}/wininst-9.0-amd64.exe (100%) rename {command => distutils/command}/wininst-9.0.exe (100%) rename config.py => distutils/config.py (100%) rename core.py => distutils/core.py (100%) rename cygwinccompiler.py => distutils/cygwinccompiler.py (100%) rename debug.py => distutils/debug.py (100%) rename dep_util.py => distutils/dep_util.py (100%) rename dir_util.py => distutils/dir_util.py (100%) rename dist.py => distutils/dist.py (100%) rename errors.py => distutils/errors.py (100%) rename extension.py => distutils/extension.py (100%) rename fancy_getopt.py => distutils/fancy_getopt.py (100%) rename file_util.py => distutils/file_util.py (100%) rename filelist.py => distutils/filelist.py (100%) rename log.py => distutils/log.py (100%) rename msvc9compiler.py => distutils/msvc9compiler.py (100%) rename msvccompiler.py => distutils/msvccompiler.py (100%) rename spawn.py => distutils/spawn.py (100%) rename sysconfig.py => distutils/sysconfig.py (100%) rename {tests => distutils/tests}/Setup.sample (100%) rename {tests => distutils/tests}/__init__.py (100%) rename {tests => distutils/tests}/includetest.rst (100%) rename {tests => distutils/tests}/support.py (100%) rename {tests => distutils/tests}/test_archive_util.py (100%) rename {tests => distutils/tests}/test_bdist.py (100%) rename {tests => distutils/tests}/test_bdist_dumb.py (100%) rename {tests => distutils/tests}/test_bdist_msi.py (100%) rename {tests => distutils/tests}/test_bdist_rpm.py (100%) rename {tests => distutils/tests}/test_bdist_wininst.py (100%) rename {tests => distutils/tests}/test_build.py (100%) rename {tests => distutils/tests}/test_build_clib.py (100%) rename {tests => distutils/tests}/test_build_ext.py (100%) rename {tests => distutils/tests}/test_build_py.py (100%) rename {tests => distutils/tests}/test_build_scripts.py (100%) rename {tests => distutils/tests}/test_check.py (100%) rename {tests => distutils/tests}/test_clean.py (100%) rename {tests => distutils/tests}/test_cmd.py (100%) rename {tests => distutils/tests}/test_config.py (100%) rename {tests => distutils/tests}/test_config_cmd.py (100%) rename {tests => distutils/tests}/test_core.py (100%) rename {tests => distutils/tests}/test_cygwinccompiler.py (100%) rename {tests => distutils/tests}/test_dep_util.py (100%) rename {tests => distutils/tests}/test_dir_util.py (100%) rename {tests => distutils/tests}/test_dist.py (100%) rename {tests => distutils/tests}/test_extension.py (100%) rename {tests => distutils/tests}/test_file_util.py (100%) rename {tests => distutils/tests}/test_filelist.py (100%) rename {tests => distutils/tests}/test_install.py (100%) rename {tests => distutils/tests}/test_install_data.py (100%) rename {tests => distutils/tests}/test_install_headers.py (100%) rename {tests => distutils/tests}/test_install_lib.py (100%) rename {tests => distutils/tests}/test_install_scripts.py (100%) rename {tests => distutils/tests}/test_log.py (100%) rename {tests => distutils/tests}/test_msvc9compiler.py (100%) rename {tests => distutils/tests}/test_msvccompiler.py (100%) rename {tests => distutils/tests}/test_register.py (100%) rename {tests => distutils/tests}/test_sdist.py (100%) rename {tests => distutils/tests}/test_spawn.py (100%) rename {tests => distutils/tests}/test_sysconfig.py (100%) rename {tests => distutils/tests}/test_text_file.py (100%) rename {tests => distutils/tests}/test_unixccompiler.py (100%) rename {tests => distutils/tests}/test_upload.py (100%) rename {tests => distutils/tests}/test_util.py (100%) rename {tests => distutils/tests}/test_version.py (100%) rename {tests => distutils/tests}/test_versionpredicate.py (100%) rename text_file.py => distutils/text_file.py (100%) rename unixccompiler.py => distutils/unixccompiler.py (100%) rename util.py => distutils/util.py (100%) rename version.py => distutils/version.py (100%) rename versionpredicate.py => distutils/versionpredicate.py (100%) diff --git a/README b/distutils/README similarity index 100% rename from README rename to distutils/README diff --git a/__init__.py b/distutils/__init__.py similarity index 100% rename from __init__.py rename to distutils/__init__.py diff --git a/_msvccompiler.py b/distutils/_msvccompiler.py similarity index 100% rename from _msvccompiler.py rename to distutils/_msvccompiler.py diff --git a/archive_util.py b/distutils/archive_util.py similarity index 100% rename from archive_util.py rename to distutils/archive_util.py diff --git a/bcppcompiler.py b/distutils/bcppcompiler.py similarity index 100% rename from bcppcompiler.py rename to distutils/bcppcompiler.py diff --git a/ccompiler.py b/distutils/ccompiler.py similarity index 100% rename from ccompiler.py rename to distutils/ccompiler.py diff --git a/cmd.py b/distutils/cmd.py similarity index 100% rename from cmd.py rename to distutils/cmd.py diff --git a/command/__init__.py b/distutils/command/__init__.py similarity index 100% rename from command/__init__.py rename to distutils/command/__init__.py diff --git a/command/bdist.py b/distutils/command/bdist.py similarity index 100% rename from command/bdist.py rename to distutils/command/bdist.py diff --git a/command/bdist_dumb.py b/distutils/command/bdist_dumb.py similarity index 100% rename from command/bdist_dumb.py rename to distutils/command/bdist_dumb.py diff --git a/command/bdist_msi.py b/distutils/command/bdist_msi.py similarity index 100% rename from command/bdist_msi.py rename to distutils/command/bdist_msi.py diff --git a/command/bdist_rpm.py b/distutils/command/bdist_rpm.py similarity index 100% rename from command/bdist_rpm.py rename to distutils/command/bdist_rpm.py diff --git a/command/bdist_wininst.py b/distutils/command/bdist_wininst.py similarity index 100% rename from command/bdist_wininst.py rename to distutils/command/bdist_wininst.py diff --git a/command/build.py b/distutils/command/build.py similarity index 100% rename from command/build.py rename to distutils/command/build.py diff --git a/command/build_clib.py b/distutils/command/build_clib.py similarity index 100% rename from command/build_clib.py rename to distutils/command/build_clib.py diff --git a/command/build_ext.py b/distutils/command/build_ext.py similarity index 100% rename from command/build_ext.py rename to distutils/command/build_ext.py diff --git a/command/build_py.py b/distutils/command/build_py.py similarity index 100% rename from command/build_py.py rename to distutils/command/build_py.py diff --git a/command/build_scripts.py b/distutils/command/build_scripts.py similarity index 100% rename from command/build_scripts.py rename to distutils/command/build_scripts.py diff --git a/command/check.py b/distutils/command/check.py similarity index 100% rename from command/check.py rename to distutils/command/check.py diff --git a/command/clean.py b/distutils/command/clean.py similarity index 100% rename from command/clean.py rename to distutils/command/clean.py diff --git a/command/command_template b/distutils/command/command_template similarity index 100% rename from command/command_template rename to distutils/command/command_template diff --git a/command/config.py b/distutils/command/config.py similarity index 100% rename from command/config.py rename to distutils/command/config.py diff --git a/command/install.py b/distutils/command/install.py similarity index 100% rename from command/install.py rename to distutils/command/install.py diff --git a/command/install_data.py b/distutils/command/install_data.py similarity index 100% rename from command/install_data.py rename to distutils/command/install_data.py diff --git a/command/install_egg_info.py b/distutils/command/install_egg_info.py similarity index 100% rename from command/install_egg_info.py rename to distutils/command/install_egg_info.py diff --git a/command/install_headers.py b/distutils/command/install_headers.py similarity index 100% rename from command/install_headers.py rename to distutils/command/install_headers.py diff --git a/command/install_lib.py b/distutils/command/install_lib.py similarity index 100% rename from command/install_lib.py rename to distutils/command/install_lib.py diff --git a/command/install_scripts.py b/distutils/command/install_scripts.py similarity index 100% rename from command/install_scripts.py rename to distutils/command/install_scripts.py diff --git a/command/register.py b/distutils/command/register.py similarity index 100% rename from command/register.py rename to distutils/command/register.py diff --git a/command/sdist.py b/distutils/command/sdist.py similarity index 100% rename from command/sdist.py rename to distutils/command/sdist.py diff --git a/command/upload.py b/distutils/command/upload.py similarity index 100% rename from command/upload.py rename to distutils/command/upload.py diff --git a/command/wininst-10.0-amd64.exe b/distutils/command/wininst-10.0-amd64.exe similarity index 100% rename from command/wininst-10.0-amd64.exe rename to distutils/command/wininst-10.0-amd64.exe diff --git a/command/wininst-10.0.exe b/distutils/command/wininst-10.0.exe similarity index 100% rename from command/wininst-10.0.exe rename to distutils/command/wininst-10.0.exe diff --git a/command/wininst-14.0-amd64.exe b/distutils/command/wininst-14.0-amd64.exe similarity index 100% rename from command/wininst-14.0-amd64.exe rename to distutils/command/wininst-14.0-amd64.exe diff --git a/command/wininst-14.0.exe b/distutils/command/wininst-14.0.exe similarity index 100% rename from command/wininst-14.0.exe rename to distutils/command/wininst-14.0.exe diff --git a/command/wininst-6.0.exe b/distutils/command/wininst-6.0.exe similarity index 100% rename from command/wininst-6.0.exe rename to distutils/command/wininst-6.0.exe diff --git a/command/wininst-7.1.exe b/distutils/command/wininst-7.1.exe similarity index 100% rename from command/wininst-7.1.exe rename to distutils/command/wininst-7.1.exe diff --git a/command/wininst-8.0.exe b/distutils/command/wininst-8.0.exe similarity index 100% rename from command/wininst-8.0.exe rename to distutils/command/wininst-8.0.exe diff --git a/command/wininst-9.0-amd64.exe b/distutils/command/wininst-9.0-amd64.exe similarity index 100% rename from command/wininst-9.0-amd64.exe rename to distutils/command/wininst-9.0-amd64.exe diff --git a/command/wininst-9.0.exe b/distutils/command/wininst-9.0.exe similarity index 100% rename from command/wininst-9.0.exe rename to distutils/command/wininst-9.0.exe diff --git a/config.py b/distutils/config.py similarity index 100% rename from config.py rename to distutils/config.py diff --git a/core.py b/distutils/core.py similarity index 100% rename from core.py rename to distutils/core.py diff --git a/cygwinccompiler.py b/distutils/cygwinccompiler.py similarity index 100% rename from cygwinccompiler.py rename to distutils/cygwinccompiler.py diff --git a/debug.py b/distutils/debug.py similarity index 100% rename from debug.py rename to distutils/debug.py diff --git a/dep_util.py b/distutils/dep_util.py similarity index 100% rename from dep_util.py rename to distutils/dep_util.py diff --git a/dir_util.py b/distutils/dir_util.py similarity index 100% rename from dir_util.py rename to distutils/dir_util.py diff --git a/dist.py b/distutils/dist.py similarity index 100% rename from dist.py rename to distutils/dist.py diff --git a/errors.py b/distutils/errors.py similarity index 100% rename from errors.py rename to distutils/errors.py diff --git a/extension.py b/distutils/extension.py similarity index 100% rename from extension.py rename to distutils/extension.py diff --git a/fancy_getopt.py b/distutils/fancy_getopt.py similarity index 100% rename from fancy_getopt.py rename to distutils/fancy_getopt.py diff --git a/file_util.py b/distutils/file_util.py similarity index 100% rename from file_util.py rename to distutils/file_util.py diff --git a/filelist.py b/distutils/filelist.py similarity index 100% rename from filelist.py rename to distutils/filelist.py diff --git a/log.py b/distutils/log.py similarity index 100% rename from log.py rename to distutils/log.py diff --git a/msvc9compiler.py b/distutils/msvc9compiler.py similarity index 100% rename from msvc9compiler.py rename to distutils/msvc9compiler.py diff --git a/msvccompiler.py b/distutils/msvccompiler.py similarity index 100% rename from msvccompiler.py rename to distutils/msvccompiler.py diff --git a/spawn.py b/distutils/spawn.py similarity index 100% rename from spawn.py rename to distutils/spawn.py diff --git a/sysconfig.py b/distutils/sysconfig.py similarity index 100% rename from sysconfig.py rename to distutils/sysconfig.py diff --git a/tests/Setup.sample b/distutils/tests/Setup.sample similarity index 100% rename from tests/Setup.sample rename to distutils/tests/Setup.sample diff --git a/tests/__init__.py b/distutils/tests/__init__.py similarity index 100% rename from tests/__init__.py rename to distutils/tests/__init__.py diff --git a/tests/includetest.rst b/distutils/tests/includetest.rst similarity index 100% rename from tests/includetest.rst rename to distutils/tests/includetest.rst diff --git a/tests/support.py b/distutils/tests/support.py similarity index 100% rename from tests/support.py rename to distutils/tests/support.py diff --git a/tests/test_archive_util.py b/distutils/tests/test_archive_util.py similarity index 100% rename from tests/test_archive_util.py rename to distutils/tests/test_archive_util.py diff --git a/tests/test_bdist.py b/distutils/tests/test_bdist.py similarity index 100% rename from tests/test_bdist.py rename to distutils/tests/test_bdist.py diff --git a/tests/test_bdist_dumb.py b/distutils/tests/test_bdist_dumb.py similarity index 100% rename from tests/test_bdist_dumb.py rename to distutils/tests/test_bdist_dumb.py diff --git a/tests/test_bdist_msi.py b/distutils/tests/test_bdist_msi.py similarity index 100% rename from tests/test_bdist_msi.py rename to distutils/tests/test_bdist_msi.py diff --git a/tests/test_bdist_rpm.py b/distutils/tests/test_bdist_rpm.py similarity index 100% rename from tests/test_bdist_rpm.py rename to distutils/tests/test_bdist_rpm.py diff --git a/tests/test_bdist_wininst.py b/distutils/tests/test_bdist_wininst.py similarity index 100% rename from tests/test_bdist_wininst.py rename to distutils/tests/test_bdist_wininst.py diff --git a/tests/test_build.py b/distutils/tests/test_build.py similarity index 100% rename from tests/test_build.py rename to distutils/tests/test_build.py diff --git a/tests/test_build_clib.py b/distutils/tests/test_build_clib.py similarity index 100% rename from tests/test_build_clib.py rename to distutils/tests/test_build_clib.py diff --git a/tests/test_build_ext.py b/distutils/tests/test_build_ext.py similarity index 100% rename from tests/test_build_ext.py rename to distutils/tests/test_build_ext.py diff --git a/tests/test_build_py.py b/distutils/tests/test_build_py.py similarity index 100% rename from tests/test_build_py.py rename to distutils/tests/test_build_py.py diff --git a/tests/test_build_scripts.py b/distutils/tests/test_build_scripts.py similarity index 100% rename from tests/test_build_scripts.py rename to distutils/tests/test_build_scripts.py diff --git a/tests/test_check.py b/distutils/tests/test_check.py similarity index 100% rename from tests/test_check.py rename to distutils/tests/test_check.py diff --git a/tests/test_clean.py b/distutils/tests/test_clean.py similarity index 100% rename from tests/test_clean.py rename to distutils/tests/test_clean.py diff --git a/tests/test_cmd.py b/distutils/tests/test_cmd.py similarity index 100% rename from tests/test_cmd.py rename to distutils/tests/test_cmd.py diff --git a/tests/test_config.py b/distutils/tests/test_config.py similarity index 100% rename from tests/test_config.py rename to distutils/tests/test_config.py diff --git a/tests/test_config_cmd.py b/distutils/tests/test_config_cmd.py similarity index 100% rename from tests/test_config_cmd.py rename to distutils/tests/test_config_cmd.py diff --git a/tests/test_core.py b/distutils/tests/test_core.py similarity index 100% rename from tests/test_core.py rename to distutils/tests/test_core.py diff --git a/tests/test_cygwinccompiler.py b/distutils/tests/test_cygwinccompiler.py similarity index 100% rename from tests/test_cygwinccompiler.py rename to distutils/tests/test_cygwinccompiler.py diff --git a/tests/test_dep_util.py b/distutils/tests/test_dep_util.py similarity index 100% rename from tests/test_dep_util.py rename to distutils/tests/test_dep_util.py diff --git a/tests/test_dir_util.py b/distutils/tests/test_dir_util.py similarity index 100% rename from tests/test_dir_util.py rename to distutils/tests/test_dir_util.py diff --git a/tests/test_dist.py b/distutils/tests/test_dist.py similarity index 100% rename from tests/test_dist.py rename to distutils/tests/test_dist.py diff --git a/tests/test_extension.py b/distutils/tests/test_extension.py similarity index 100% rename from tests/test_extension.py rename to distutils/tests/test_extension.py diff --git a/tests/test_file_util.py b/distutils/tests/test_file_util.py similarity index 100% rename from tests/test_file_util.py rename to distutils/tests/test_file_util.py diff --git a/tests/test_filelist.py b/distutils/tests/test_filelist.py similarity index 100% rename from tests/test_filelist.py rename to distutils/tests/test_filelist.py diff --git a/tests/test_install.py b/distutils/tests/test_install.py similarity index 100% rename from tests/test_install.py rename to distutils/tests/test_install.py diff --git a/tests/test_install_data.py b/distutils/tests/test_install_data.py similarity index 100% rename from tests/test_install_data.py rename to distutils/tests/test_install_data.py diff --git a/tests/test_install_headers.py b/distutils/tests/test_install_headers.py similarity index 100% rename from tests/test_install_headers.py rename to distutils/tests/test_install_headers.py diff --git a/tests/test_install_lib.py b/distutils/tests/test_install_lib.py similarity index 100% rename from tests/test_install_lib.py rename to distutils/tests/test_install_lib.py diff --git a/tests/test_install_scripts.py b/distutils/tests/test_install_scripts.py similarity index 100% rename from tests/test_install_scripts.py rename to distutils/tests/test_install_scripts.py diff --git a/tests/test_log.py b/distutils/tests/test_log.py similarity index 100% rename from tests/test_log.py rename to distutils/tests/test_log.py diff --git a/tests/test_msvc9compiler.py b/distutils/tests/test_msvc9compiler.py similarity index 100% rename from tests/test_msvc9compiler.py rename to distutils/tests/test_msvc9compiler.py diff --git a/tests/test_msvccompiler.py b/distutils/tests/test_msvccompiler.py similarity index 100% rename from tests/test_msvccompiler.py rename to distutils/tests/test_msvccompiler.py diff --git a/tests/test_register.py b/distutils/tests/test_register.py similarity index 100% rename from tests/test_register.py rename to distutils/tests/test_register.py diff --git a/tests/test_sdist.py b/distutils/tests/test_sdist.py similarity index 100% rename from tests/test_sdist.py rename to distutils/tests/test_sdist.py diff --git a/tests/test_spawn.py b/distutils/tests/test_spawn.py similarity index 100% rename from tests/test_spawn.py rename to distutils/tests/test_spawn.py diff --git a/tests/test_sysconfig.py b/distutils/tests/test_sysconfig.py similarity index 100% rename from tests/test_sysconfig.py rename to distutils/tests/test_sysconfig.py diff --git a/tests/test_text_file.py b/distutils/tests/test_text_file.py similarity index 100% rename from tests/test_text_file.py rename to distutils/tests/test_text_file.py diff --git a/tests/test_unixccompiler.py b/distutils/tests/test_unixccompiler.py similarity index 100% rename from tests/test_unixccompiler.py rename to distutils/tests/test_unixccompiler.py diff --git a/tests/test_upload.py b/distutils/tests/test_upload.py similarity index 100% rename from tests/test_upload.py rename to distutils/tests/test_upload.py diff --git a/tests/test_util.py b/distutils/tests/test_util.py similarity index 100% rename from tests/test_util.py rename to distutils/tests/test_util.py diff --git a/tests/test_version.py b/distutils/tests/test_version.py similarity index 100% rename from tests/test_version.py rename to distutils/tests/test_version.py diff --git a/tests/test_versionpredicate.py b/distutils/tests/test_versionpredicate.py similarity index 100% rename from tests/test_versionpredicate.py rename to distutils/tests/test_versionpredicate.py diff --git a/text_file.py b/distutils/text_file.py similarity index 100% rename from text_file.py rename to distutils/text_file.py diff --git a/unixccompiler.py b/distutils/unixccompiler.py similarity index 100% rename from unixccompiler.py rename to distutils/unixccompiler.py diff --git a/util.py b/distutils/util.py similarity index 100% rename from util.py rename to distutils/util.py diff --git a/version.py b/distutils/version.py similarity index 100% rename from version.py rename to distutils/version.py diff --git a/versionpredicate.py b/distutils/versionpredicate.py similarity index 100% rename from versionpredicate.py rename to distutils/versionpredicate.py From b678ce30a356abb36cd49d523731a9f978fce0bf Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 25 May 2020 13:13:44 -0400 Subject: [PATCH 2564/2594] 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 811f3fd2e8..9df71a6d23 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 0000000000..a2fc1a8cb9 --- /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) From 151599602b9d626ebcfe5ae6960ea216b767fec2 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 25 May 2020 13:38:15 -0400 Subject: [PATCH 2565/2594] 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 a2fc1a8cb9..e0abbd77da 100644 --- a/setuptools/distutils_patch.py +++ b/setuptools/distutils_patch.py @@ -7,9 +7,23 @@ 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') From 81aab2da23caf1a66298847d93b209ba16eb878e Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 25 May 2020 16:35:24 -0400 Subject: [PATCH 2566/2594] Ignore distutils when running tests. --- conftest.py | 1 + 1 file changed, 1 insertion(+) diff --git a/conftest.py b/conftest.py index 1746bfb588..3f7e59b44e 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', ] From 781d42dffe2913f1a1f27128effa7198c27f569f Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 25 May 2020 16:35:50 -0400 Subject: [PATCH 2567/2594] 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 aaa300efa9..21f8c27c8b 100644 --- a/distutils/command/install.py +++ b/distutils/command/install.py @@ -298,7 +298,7 @@ def finalize_options(self): '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 37feae5df7..2109f746ba 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" From 4ac101a1805a3773649b42c7682038f41314738e Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 31 May 2020 08:43:36 -0400 Subject: [PATCH 2568/2594] 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 6cf0a0d663..37db4d6cd7 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 From 9962c0af814ccd13c370ce8b04742a52f77025c6 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 31 May 2020 08:50:31 -0400 Subject: [PATCH 2569/2594] 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 e0abbd77da..43b64c0fc8 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() From 38a8632835c90bfb0a220f91a8b2e982a69b81ab Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 31 May 2020 09:34:34 -0400 Subject: [PATCH 2570/2594] 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 2109f746ba..79391e8671 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 = {} From db7ff2e1b0ea51fee56b7f626a548cd2ef590e5e Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 31 May 2020 12:26:45 -0400 Subject: [PATCH 2571/2594] 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 0000000000..185542addf --- /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. From a8081a19cc48fc285dfd50953bb2c522cb4b8b02 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 21 Jun 2020 12:09:39 -0400 Subject: [PATCH 2572/2594] 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 43b64c0fc8..1416a7a3c8 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') From 51e062754df592b105547abbd6e20851972435d4 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 21 Jun 2020 12:49:50 -0400 Subject: [PATCH 2573/2594] 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 185542addf..29030b3210 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. From b596e4b0f684f5ac11673598e1de3aaa5bc74162 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 21 Jun 2020 14:47:11 -0400 Subject: [PATCH 2574/2594] 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 d823d040a1..7dac55b601 100644 --- a/distutils/__init__.py +++ b/distutils/__init__.py @@ -11,3 +11,5 @@ import sys __version__ = sys.version[:sys.version.index(' ')] + +local = True diff --git a/setuptools/distutils_patch.py b/setuptools/distutils_patch.py index 1416a7a3c8..f9e637988a 100644 --- a/setuptools/distutils_patch.py +++ b/setuptools/distutils_patch.py @@ -6,8 +6,10 @@ """ 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() From 5f8eebf06792a32d955689daeb5679fc6d3d019e Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 28 Jun 2020 20:21:35 -0400 Subject: [PATCH 2575/2594] 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 0000000000..f0ec98e74f --- /dev/null +++ b/conftest.py @@ -0,0 +1,5 @@ +import sys +import os + +sys.path.insert(0, os.getcwd()) +__import__('distutils') From b8abfd21934b8b109050696bd99c8fc823f901f4 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 28 Jun 2020 21:10:28 -0400 Subject: [PATCH 2576/2594] 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 f0ec98e74f..0000000000 --- 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 0000000000..1590e308c9 --- /dev/null +++ b/tox.ini @@ -0,0 +1,8 @@ +[testenv] +deps = + pytest +commands = + pytest {posargs} +setenv = + PYTHONPATH = {toxinidir} +skip_install = True From 73ac895667b5bc6e3f5d685e7bad3d4535f07a80 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 28 Jun 2020 21:16:53 -0400 Subject: [PATCH 2577/2594] 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 51c80e0421..eb684a09e6 100644 --- a/distutils/tests/test_install.py +++ b/distutils/tests/test_install.py @@ -58,7 +58,8 @@ def check_path(got, expected): 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, From c202075affc8a4d03401a4877639a96907438d83 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 28 Jun 2020 22:38:33 -0400 Subject: [PATCH 2578/2594] 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 60956dadef..d431085bbd 100644 --- a/distutils/tests/test_dist.py +++ b/distutils/tests/test_dist.py @@ -83,6 +83,10 @@ def test_command_packages_cmdline(self): 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) From 00ce1222d90ca28c6096d847c6ffe52e26bd5db2 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 31 May 2020 08:43:36 -0400 Subject: [PATCH 2579/2594] 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 6cf0a0d663..37db4d6cd7 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 From 3a4c22103551a68630b2e4b38f91deb4c78eae99 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 31 May 2020 09:34:34 -0400 Subject: [PATCH 2580/2594] 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 2109f746ba..79391e8671 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 = {} From 71d738a1f6eced80defe3587f6adbbe4ce1fb7d1 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 1 Jul 2020 20:19:22 -0400 Subject: [PATCH 2581/2594] 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 5e47e0773a..1aec153783 100644 --- a/distutils/tests/test_build_ext.py +++ b/distutils/tests/test_build_ext.py @@ -82,7 +82,7 @@ def test_build_ext(self): 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 @@ def test_xx(self): unittest.main() - """) + """.format(**locals())) assert_python_ok('-c', code) def test_solaris_enable_shared(self): From 253d03cad23ed022d020ae635ce419255240feef Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 1 Jul 2020 20:42:12 -0400 Subject: [PATCH 2582/2594] 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 0000000000..3eb86b5fe2 --- /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 abd8313770..259c43522d 100644 --- a/distutils/tests/test_build_clib.py +++ b/distutils/tests/test_build_clib.py @@ -3,7 +3,9 @@ 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 9aeab07b46..4cd9a6b9a0 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 cf1faad5f4..919d0ad98f 100644 --- a/distutils/tests/test_spawn.py +++ b/distutils/tests/test_spawn.py @@ -3,9 +3,11 @@ 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 From a4eb1127303fbe514ab8f77d6c3896614de02c5d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 1 Jul 2020 20:49:30 -0400 Subject: [PATCH 2583/2594] 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 3eb86b5fe2..0c755261ad 100644 --- a/distutils/tests/py35compat.py +++ b/distutils/tests/py35compat.py @@ -66,3 +66,12 @@ def _optim_args_from_interpreter_flags(): '_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 c71342d0dc..71fde2b718 100644 --- a/distutils/tests/test_filelist.py +++ b/distutils/tests/test_filelist.py @@ -12,6 +12,9 @@ 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 @@ def test_glob_to_re(self): ('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 From 4f1d15983b650c20dc6de48f6675c9ce84c0c3a9 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 1 Jul 2020 21:09:47 -0400 Subject: [PATCH 2584/2594] 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 0000000000..3e01b43900 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,4 @@ +[pytest] +filterwarnings= + # acknowledge that TestDistribution isn't a test + ignore:cannot collect test class 'TestDistribution' From 2f1fae1bfceb88cf812a45c44cd18ce604b69c69 Mon Sep 17 00:00:00 2001 From: mattip Date: Mon, 29 Jun 2020 00:25:26 +0300 Subject: [PATCH 2585/2594] 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 21f8c27c8b..13feeb890f 100644 --- a/distutils/command/install.py +++ b/distutils/command/install.py @@ -43,6 +43,20 @@ '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 @@ def finalize_other(self): 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 From 12f74fb9280897a07199db0df76103487263136b Mon Sep 17 00:00:00 2001 From: mattip Date: Mon, 29 Jun 2020 20:00:07 +0300 Subject: [PATCH 2586/2594] 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 79391e8671..255243ade5 100644 --- a/distutils/sysconfig.py +++ b/distutils/sysconfig.py @@ -16,6 +16,8 @@ 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 From 50d17611942813f840a69c4838173b2c4dc27f24 Mon Sep 17 00:00:00 2001 From: mattip Date: Mon, 29 Jun 2020 20:23:28 +0300 Subject: [PATCH 2587/2594] 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 255243ade5..879b6981ed 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 From 89d64e81e194d5b8b8a500e837b9cea4f2e1063e Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 1 Jul 2020 22:09:26 -0400 Subject: [PATCH 2588/2594] 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 236755d095..d5076391fc 100644 --- a/distutils/tests/test_sysconfig.py +++ b/distutils/tests/test_sysconfig.py @@ -45,6 +45,7 @@ def test_get_config_vars(self): 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') From c3a052aefbba0d5fda10790e676223c0dc12f0ed Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 2 Jul 2020 05:06:16 -0400 Subject: [PATCH 2589/2594] 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 eef702cf01..f2159662fd 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 @@ def rpath_foo(self): 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 @@ 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'] @@ -126,7 +134,13 @@ 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' From d9998e6281cbf4bb90cfd8c90e7f34b4ea3a350d Mon Sep 17 00:00:00 2001 From: mattip Date: Mon, 29 Jun 2020 20:00:24 +0300 Subject: [PATCH 2590/2594] 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 3dc87ca363..2ef8521d51 100644 --- a/setuptools/tests/test_build_ext.py +++ b/setuptools/tests/test_build_ext.py @@ -42,7 +42,7 @@ def test_abi3_filename(self): 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: From 9013321c25606a5cd63271cd029c2539490b16d3 Mon Sep 17 00:00:00 2001 From: mattip Date: Mon, 29 Jun 2020 20:37:43 +0300 Subject: [PATCH 2591/2594] 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 2e7d505901..61f2446159 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -1459,7 +1459,8 @@ def run_script(self, script_name, namespace): 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 308283ea93..0208fdf33b 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 213e39c9d5..09f8565ef6 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -643,8 +643,10 @@ def lookup(self, key, name): """ 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 @@ def lookup(self, key, name): return winreg.QueryValueEx(bkey, name)[0] except (OSError, IOError): pass + finally: + if bkey: + closekey(bkey) class SystemInfo: @@ -726,21 +731,22 @@ def find_reg_vs_vers(self): 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): From bb9fb1fcfe37c1ef1e29e1e6d1fc4e483c743380 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 3 Jul 2020 03:53:46 -0400 Subject: [PATCH 2592/2594] Move distutils into a submodule of setuptools. --- conftest.py | 2 +- {distutils => setuptools/_distutils}/README | 0 .../_distutils}/__init__.py | 0 .../_distutils}/_msvccompiler.py | 0 .../_distutils}/archive_util.py | 0 .../_distutils}/bcppcompiler.py | 0 .../_distutils}/ccompiler.py | 0 {distutils => setuptools/_distutils}/cmd.py | 0 .../_distutils}/command/__init__.py | 0 .../_distutils}/command/bdist.py | 0 .../_distutils}/command/bdist_dumb.py | 0 .../_distutils}/command/bdist_msi.py | 0 .../_distutils}/command/bdist_rpm.py | 0 .../_distutils}/command/bdist_wininst.py | 0 .../_distutils}/command/build.py | 0 .../_distutils}/command/build_clib.py | 0 .../_distutils}/command/build_ext.py | 0 .../_distutils}/command/build_py.py | 0 .../_distutils}/command/build_scripts.py | 0 .../_distutils}/command/check.py | 0 .../_distutils}/command/clean.py | 0 .../_distutils}/command/command_template | 0 .../_distutils}/command/config.py | 0 .../_distutils}/command/install.py | 0 .../_distutils}/command/install_data.py | 0 .../_distutils}/command/install_egg_info.py | 0 .../_distutils}/command/install_headers.py | 0 .../_distutils}/command/install_lib.py | 0 .../_distutils}/command/install_scripts.py | 0 .../_distutils}/command/register.py | 0 .../_distutils}/command/sdist.py | 0 .../_distutils}/command/upload.py | 0 .../command/wininst-10.0-amd64.exe | Bin .../_distutils}/command/wininst-10.0.exe | Bin .../command/wininst-14.0-amd64.exe | Bin .../_distutils}/command/wininst-14.0.exe | Bin .../_distutils}/command/wininst-6.0.exe | Bin .../_distutils}/command/wininst-7.1.exe | Bin .../_distutils}/command/wininst-8.0.exe | Bin .../_distutils}/command/wininst-9.0-amd64.exe | Bin .../_distutils}/command/wininst-9.0.exe | Bin .../_distutils}/config.py | 0 {distutils => setuptools/_distutils}/core.py | 0 .../_distutils}/cygwinccompiler.py | 0 {distutils => setuptools/_distutils}/debug.py | 0 .../_distutils}/dep_util.py | 0 .../_distutils}/dir_util.py | 0 {distutils => setuptools/_distutils}/dist.py | 0 .../_distutils}/errors.py | 0 .../_distutils}/extension.py | 0 .../_distutils}/fancy_getopt.py | 0 .../_distutils}/file_util.py | 0 .../_distutils}/filelist.py | 0 {distutils => setuptools/_distutils}/log.py | 0 .../_distutils}/msvc9compiler.py | 0 .../_distutils}/msvccompiler.py | 0 {distutils => setuptools/_distutils}/spawn.py | 0 .../_distutils}/sysconfig.py | 0 .../_distutils}/tests/Setup.sample | 0 .../_distutils}/tests/__init__.py | 0 .../_distutils}/tests/includetest.rst | 0 .../_distutils}/tests/py35compat.py | 0 .../_distutils}/tests/support.py | 0 .../_distutils}/tests/test_archive_util.py | 0 .../_distutils}/tests/test_bdist.py | 0 .../_distutils}/tests/test_bdist_dumb.py | 0 .../_distutils}/tests/test_bdist_msi.py | 0 .../_distutils}/tests/test_bdist_rpm.py | 0 .../_distutils}/tests/test_bdist_wininst.py | 0 .../_distutils}/tests/test_build.py | 0 .../_distutils}/tests/test_build_clib.py | 0 .../_distutils}/tests/test_build_ext.py | 0 .../_distutils}/tests/test_build_py.py | 0 .../_distutils}/tests/test_build_scripts.py | 0 .../_distutils}/tests/test_check.py | 0 .../_distutils}/tests/test_clean.py | 0 .../_distutils}/tests/test_cmd.py | 0 .../_distutils}/tests/test_config.py | 0 .../_distutils}/tests/test_config_cmd.py | 0 .../_distutils}/tests/test_core.py | 0 .../_distutils}/tests/test_cygwinccompiler.py | 0 .../_distutils}/tests/test_dep_util.py | 0 .../_distutils}/tests/test_dir_util.py | 0 .../_distutils}/tests/test_dist.py | 0 .../_distutils}/tests/test_extension.py | 0 .../_distutils}/tests/test_file_util.py | 0 .../_distutils}/tests/test_filelist.py | 0 .../_distutils}/tests/test_install.py | 0 .../_distutils}/tests/test_install_data.py | 0 .../_distutils}/tests/test_install_headers.py | 0 .../_distutils}/tests/test_install_lib.py | 0 .../_distutils}/tests/test_install_scripts.py | 0 .../_distutils}/tests/test_log.py | 0 .../_distutils}/tests/test_msvc9compiler.py | 0 .../_distutils}/tests/test_msvccompiler.py | 0 .../_distutils}/tests/test_register.py | 0 .../_distutils}/tests/test_sdist.py | 0 .../_distutils}/tests/test_spawn.py | 0 .../_distutils}/tests/test_sysconfig.py | 0 .../_distutils}/tests/test_text_file.py | 0 .../_distutils}/tests/test_unixccompiler.py | 0 .../_distutils}/tests/test_upload.py | 0 .../_distutils}/tests/test_util.py | 0 .../_distutils}/tests/test_version.py | 0 .../tests/test_versionpredicate.py | 0 .../_distutils}/text_file.py | 0 .../_distutils}/unixccompiler.py | 0 {distutils => setuptools/_distutils}/util.py | 0 .../_distutils}/version.py | 0 .../_distutils}/versionpredicate.py | 0 setuptools/distutils_patch.py | 21 +++++------------- 111 files changed, 7 insertions(+), 16 deletions(-) rename {distutils => setuptools/_distutils}/README (100%) rename {distutils => setuptools/_distutils}/__init__.py (100%) rename {distutils => setuptools/_distutils}/_msvccompiler.py (100%) rename {distutils => setuptools/_distutils}/archive_util.py (100%) rename {distutils => setuptools/_distutils}/bcppcompiler.py (100%) rename {distutils => setuptools/_distutils}/ccompiler.py (100%) rename {distutils => setuptools/_distutils}/cmd.py (100%) rename {distutils => setuptools/_distutils}/command/__init__.py (100%) rename {distutils => setuptools/_distutils}/command/bdist.py (100%) rename {distutils => setuptools/_distutils}/command/bdist_dumb.py (100%) rename {distutils => setuptools/_distutils}/command/bdist_msi.py (100%) rename {distutils => setuptools/_distutils}/command/bdist_rpm.py (100%) rename {distutils => setuptools/_distutils}/command/bdist_wininst.py (100%) rename {distutils => setuptools/_distutils}/command/build.py (100%) rename {distutils => setuptools/_distutils}/command/build_clib.py (100%) rename {distutils => setuptools/_distutils}/command/build_ext.py (100%) rename {distutils => setuptools/_distutils}/command/build_py.py (100%) rename {distutils => setuptools/_distutils}/command/build_scripts.py (100%) rename {distutils => setuptools/_distutils}/command/check.py (100%) rename {distutils => setuptools/_distutils}/command/clean.py (100%) rename {distutils => setuptools/_distutils}/command/command_template (100%) rename {distutils => setuptools/_distutils}/command/config.py (100%) rename {distutils => setuptools/_distutils}/command/install.py (100%) rename {distutils => setuptools/_distutils}/command/install_data.py (100%) rename {distutils => setuptools/_distutils}/command/install_egg_info.py (100%) rename {distutils => setuptools/_distutils}/command/install_headers.py (100%) rename {distutils => setuptools/_distutils}/command/install_lib.py (100%) rename {distutils => setuptools/_distutils}/command/install_scripts.py (100%) rename {distutils => setuptools/_distutils}/command/register.py (100%) rename {distutils => setuptools/_distutils}/command/sdist.py (100%) rename {distutils => setuptools/_distutils}/command/upload.py (100%) rename {distutils => setuptools/_distutils}/command/wininst-10.0-amd64.exe (100%) rename {distutils => setuptools/_distutils}/command/wininst-10.0.exe (100%) rename {distutils => setuptools/_distutils}/command/wininst-14.0-amd64.exe (100%) rename {distutils => setuptools/_distutils}/command/wininst-14.0.exe (100%) rename {distutils => setuptools/_distutils}/command/wininst-6.0.exe (100%) rename {distutils => setuptools/_distutils}/command/wininst-7.1.exe (100%) rename {distutils => setuptools/_distutils}/command/wininst-8.0.exe (100%) rename {distutils => setuptools/_distutils}/command/wininst-9.0-amd64.exe (100%) rename {distutils => setuptools/_distutils}/command/wininst-9.0.exe (100%) rename {distutils => setuptools/_distutils}/config.py (100%) rename {distutils => setuptools/_distutils}/core.py (100%) rename {distutils => setuptools/_distutils}/cygwinccompiler.py (100%) rename {distutils => setuptools/_distutils}/debug.py (100%) rename {distutils => setuptools/_distutils}/dep_util.py (100%) rename {distutils => setuptools/_distutils}/dir_util.py (100%) rename {distutils => setuptools/_distutils}/dist.py (100%) rename {distutils => setuptools/_distutils}/errors.py (100%) rename {distutils => setuptools/_distutils}/extension.py (100%) rename {distutils => setuptools/_distutils}/fancy_getopt.py (100%) rename {distutils => setuptools/_distutils}/file_util.py (100%) rename {distutils => setuptools/_distutils}/filelist.py (100%) rename {distutils => setuptools/_distutils}/log.py (100%) rename {distutils => setuptools/_distutils}/msvc9compiler.py (100%) rename {distutils => setuptools/_distutils}/msvccompiler.py (100%) rename {distutils => setuptools/_distutils}/spawn.py (100%) rename {distutils => setuptools/_distutils}/sysconfig.py (100%) rename {distutils => setuptools/_distutils}/tests/Setup.sample (100%) rename {distutils => setuptools/_distutils}/tests/__init__.py (100%) rename {distutils => setuptools/_distutils}/tests/includetest.rst (100%) rename {distutils => setuptools/_distutils}/tests/py35compat.py (100%) rename {distutils => setuptools/_distutils}/tests/support.py (100%) rename {distutils => setuptools/_distutils}/tests/test_archive_util.py (100%) rename {distutils => setuptools/_distutils}/tests/test_bdist.py (100%) rename {distutils => setuptools/_distutils}/tests/test_bdist_dumb.py (100%) rename {distutils => setuptools/_distutils}/tests/test_bdist_msi.py (100%) rename {distutils => setuptools/_distutils}/tests/test_bdist_rpm.py (100%) rename {distutils => setuptools/_distutils}/tests/test_bdist_wininst.py (100%) rename {distutils => setuptools/_distutils}/tests/test_build.py (100%) rename {distutils => setuptools/_distutils}/tests/test_build_clib.py (100%) rename {distutils => setuptools/_distutils}/tests/test_build_ext.py (100%) rename {distutils => setuptools/_distutils}/tests/test_build_py.py (100%) rename {distutils => setuptools/_distutils}/tests/test_build_scripts.py (100%) rename {distutils => setuptools/_distutils}/tests/test_check.py (100%) rename {distutils => setuptools/_distutils}/tests/test_clean.py (100%) rename {distutils => setuptools/_distutils}/tests/test_cmd.py (100%) rename {distutils => setuptools/_distutils}/tests/test_config.py (100%) rename {distutils => setuptools/_distutils}/tests/test_config_cmd.py (100%) rename {distutils => setuptools/_distutils}/tests/test_core.py (100%) rename {distutils => setuptools/_distutils}/tests/test_cygwinccompiler.py (100%) rename {distutils => setuptools/_distutils}/tests/test_dep_util.py (100%) rename {distutils => setuptools/_distutils}/tests/test_dir_util.py (100%) rename {distutils => setuptools/_distutils}/tests/test_dist.py (100%) rename {distutils => setuptools/_distutils}/tests/test_extension.py (100%) rename {distutils => setuptools/_distutils}/tests/test_file_util.py (100%) rename {distutils => setuptools/_distutils}/tests/test_filelist.py (100%) rename {distutils => setuptools/_distutils}/tests/test_install.py (100%) rename {distutils => setuptools/_distutils}/tests/test_install_data.py (100%) rename {distutils => setuptools/_distutils}/tests/test_install_headers.py (100%) rename {distutils => setuptools/_distutils}/tests/test_install_lib.py (100%) rename {distutils => setuptools/_distutils}/tests/test_install_scripts.py (100%) rename {distutils => setuptools/_distutils}/tests/test_log.py (100%) rename {distutils => setuptools/_distutils}/tests/test_msvc9compiler.py (100%) rename {distutils => setuptools/_distutils}/tests/test_msvccompiler.py (100%) rename {distutils => setuptools/_distutils}/tests/test_register.py (100%) rename {distutils => setuptools/_distutils}/tests/test_sdist.py (100%) rename {distutils => setuptools/_distutils}/tests/test_spawn.py (100%) rename {distutils => setuptools/_distutils}/tests/test_sysconfig.py (100%) rename {distutils => setuptools/_distutils}/tests/test_text_file.py (100%) rename {distutils => setuptools/_distutils}/tests/test_unixccompiler.py (100%) rename {distutils => setuptools/_distutils}/tests/test_upload.py (100%) rename {distutils => setuptools/_distutils}/tests/test_util.py (100%) rename {distutils => setuptools/_distutils}/tests/test_version.py (100%) rename {distutils => setuptools/_distutils}/tests/test_versionpredicate.py (100%) rename {distutils => setuptools/_distutils}/text_file.py (100%) rename {distutils => setuptools/_distutils}/unixccompiler.py (100%) rename {distutils => setuptools/_distutils}/util.py (100%) rename {distutils => setuptools/_distutils}/version.py (100%) rename {distutils => setuptools/_distutils}/versionpredicate.py (100%) diff --git a/conftest.py b/conftest.py index 3f7e59b44e..72edcf1439 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/setuptools/_distutils/README similarity index 100% rename from distutils/README rename to setuptools/_distutils/README diff --git a/distutils/__init__.py b/setuptools/_distutils/__init__.py similarity index 100% rename from distutils/__init__.py rename to setuptools/_distutils/__init__.py diff --git a/distutils/_msvccompiler.py b/setuptools/_distutils/_msvccompiler.py similarity index 100% rename from distutils/_msvccompiler.py rename to setuptools/_distutils/_msvccompiler.py diff --git a/distutils/archive_util.py b/setuptools/_distutils/archive_util.py similarity index 100% rename from distutils/archive_util.py rename to setuptools/_distutils/archive_util.py diff --git a/distutils/bcppcompiler.py b/setuptools/_distutils/bcppcompiler.py similarity index 100% rename from distutils/bcppcompiler.py rename to setuptools/_distutils/bcppcompiler.py diff --git a/distutils/ccompiler.py b/setuptools/_distutils/ccompiler.py similarity index 100% rename from distutils/ccompiler.py rename to setuptools/_distutils/ccompiler.py diff --git a/distutils/cmd.py b/setuptools/_distutils/cmd.py similarity index 100% rename from distutils/cmd.py rename to setuptools/_distutils/cmd.py diff --git a/distutils/command/__init__.py b/setuptools/_distutils/command/__init__.py similarity index 100% rename from distutils/command/__init__.py rename to setuptools/_distutils/command/__init__.py diff --git a/distutils/command/bdist.py b/setuptools/_distutils/command/bdist.py similarity index 100% rename from distutils/command/bdist.py rename to setuptools/_distutils/command/bdist.py diff --git a/distutils/command/bdist_dumb.py b/setuptools/_distutils/command/bdist_dumb.py similarity index 100% rename from distutils/command/bdist_dumb.py rename to setuptools/_distutils/command/bdist_dumb.py diff --git a/distutils/command/bdist_msi.py b/setuptools/_distutils/command/bdist_msi.py similarity index 100% rename from distutils/command/bdist_msi.py rename to setuptools/_distutils/command/bdist_msi.py diff --git a/distutils/command/bdist_rpm.py b/setuptools/_distutils/command/bdist_rpm.py similarity index 100% rename from distutils/command/bdist_rpm.py rename to setuptools/_distutils/command/bdist_rpm.py diff --git a/distutils/command/bdist_wininst.py b/setuptools/_distutils/command/bdist_wininst.py similarity index 100% rename from distutils/command/bdist_wininst.py rename to setuptools/_distutils/command/bdist_wininst.py diff --git a/distutils/command/build.py b/setuptools/_distutils/command/build.py similarity index 100% rename from distutils/command/build.py rename to setuptools/_distutils/command/build.py diff --git a/distutils/command/build_clib.py b/setuptools/_distutils/command/build_clib.py similarity index 100% rename from distutils/command/build_clib.py rename to setuptools/_distutils/command/build_clib.py diff --git a/distutils/command/build_ext.py b/setuptools/_distutils/command/build_ext.py similarity index 100% rename from distutils/command/build_ext.py rename to setuptools/_distutils/command/build_ext.py diff --git a/distutils/command/build_py.py b/setuptools/_distutils/command/build_py.py similarity index 100% rename from distutils/command/build_py.py rename to setuptools/_distutils/command/build_py.py diff --git a/distutils/command/build_scripts.py b/setuptools/_distutils/command/build_scripts.py similarity index 100% rename from distutils/command/build_scripts.py rename to setuptools/_distutils/command/build_scripts.py diff --git a/distutils/command/check.py b/setuptools/_distutils/command/check.py similarity index 100% rename from distutils/command/check.py rename to setuptools/_distutils/command/check.py diff --git a/distutils/command/clean.py b/setuptools/_distutils/command/clean.py similarity index 100% rename from distutils/command/clean.py rename to setuptools/_distutils/command/clean.py diff --git a/distutils/command/command_template b/setuptools/_distutils/command/command_template similarity index 100% rename from distutils/command/command_template rename to setuptools/_distutils/command/command_template diff --git a/distutils/command/config.py b/setuptools/_distutils/command/config.py similarity index 100% rename from distutils/command/config.py rename to setuptools/_distutils/command/config.py diff --git a/distutils/command/install.py b/setuptools/_distutils/command/install.py similarity index 100% rename from distutils/command/install.py rename to setuptools/_distutils/command/install.py diff --git a/distutils/command/install_data.py b/setuptools/_distutils/command/install_data.py similarity index 100% rename from distutils/command/install_data.py rename to setuptools/_distutils/command/install_data.py diff --git a/distutils/command/install_egg_info.py b/setuptools/_distutils/command/install_egg_info.py similarity index 100% rename from distutils/command/install_egg_info.py rename to setuptools/_distutils/command/install_egg_info.py diff --git a/distutils/command/install_headers.py b/setuptools/_distutils/command/install_headers.py similarity index 100% rename from distutils/command/install_headers.py rename to setuptools/_distutils/command/install_headers.py diff --git a/distutils/command/install_lib.py b/setuptools/_distutils/command/install_lib.py similarity index 100% rename from distutils/command/install_lib.py rename to setuptools/_distutils/command/install_lib.py diff --git a/distutils/command/install_scripts.py b/setuptools/_distutils/command/install_scripts.py similarity index 100% rename from distutils/command/install_scripts.py rename to setuptools/_distutils/command/install_scripts.py diff --git a/distutils/command/register.py b/setuptools/_distutils/command/register.py similarity index 100% rename from distutils/command/register.py rename to setuptools/_distutils/command/register.py diff --git a/distutils/command/sdist.py b/setuptools/_distutils/command/sdist.py similarity index 100% rename from distutils/command/sdist.py rename to setuptools/_distutils/command/sdist.py diff --git a/distutils/command/upload.py b/setuptools/_distutils/command/upload.py similarity index 100% rename from distutils/command/upload.py rename to setuptools/_distutils/command/upload.py diff --git a/distutils/command/wininst-10.0-amd64.exe b/setuptools/_distutils/command/wininst-10.0-amd64.exe similarity index 100% rename from distutils/command/wininst-10.0-amd64.exe rename to setuptools/_distutils/command/wininst-10.0-amd64.exe diff --git a/distutils/command/wininst-10.0.exe b/setuptools/_distutils/command/wininst-10.0.exe similarity index 100% rename from distutils/command/wininst-10.0.exe rename to setuptools/_distutils/command/wininst-10.0.exe diff --git a/distutils/command/wininst-14.0-amd64.exe b/setuptools/_distutils/command/wininst-14.0-amd64.exe similarity index 100% rename from distutils/command/wininst-14.0-amd64.exe rename to setuptools/_distutils/command/wininst-14.0-amd64.exe diff --git a/distutils/command/wininst-14.0.exe b/setuptools/_distutils/command/wininst-14.0.exe similarity index 100% rename from distutils/command/wininst-14.0.exe rename to setuptools/_distutils/command/wininst-14.0.exe diff --git a/distutils/command/wininst-6.0.exe b/setuptools/_distutils/command/wininst-6.0.exe similarity index 100% rename from distutils/command/wininst-6.0.exe rename to setuptools/_distutils/command/wininst-6.0.exe diff --git a/distutils/command/wininst-7.1.exe b/setuptools/_distutils/command/wininst-7.1.exe similarity index 100% rename from distutils/command/wininst-7.1.exe rename to setuptools/_distutils/command/wininst-7.1.exe diff --git a/distutils/command/wininst-8.0.exe b/setuptools/_distutils/command/wininst-8.0.exe similarity index 100% rename from distutils/command/wininst-8.0.exe rename to setuptools/_distutils/command/wininst-8.0.exe diff --git a/distutils/command/wininst-9.0-amd64.exe b/setuptools/_distutils/command/wininst-9.0-amd64.exe similarity index 100% rename from distutils/command/wininst-9.0-amd64.exe rename to setuptools/_distutils/command/wininst-9.0-amd64.exe diff --git a/distutils/command/wininst-9.0.exe b/setuptools/_distutils/command/wininst-9.0.exe similarity index 100% rename from distutils/command/wininst-9.0.exe rename to setuptools/_distutils/command/wininst-9.0.exe diff --git a/distutils/config.py b/setuptools/_distutils/config.py similarity index 100% rename from distutils/config.py rename to setuptools/_distutils/config.py diff --git a/distutils/core.py b/setuptools/_distutils/core.py similarity index 100% rename from distutils/core.py rename to setuptools/_distutils/core.py diff --git a/distutils/cygwinccompiler.py b/setuptools/_distutils/cygwinccompiler.py similarity index 100% rename from distutils/cygwinccompiler.py rename to setuptools/_distutils/cygwinccompiler.py diff --git a/distutils/debug.py b/setuptools/_distutils/debug.py similarity index 100% rename from distutils/debug.py rename to setuptools/_distutils/debug.py diff --git a/distutils/dep_util.py b/setuptools/_distutils/dep_util.py similarity index 100% rename from distutils/dep_util.py rename to setuptools/_distutils/dep_util.py diff --git a/distutils/dir_util.py b/setuptools/_distutils/dir_util.py similarity index 100% rename from distutils/dir_util.py rename to setuptools/_distutils/dir_util.py diff --git a/distutils/dist.py b/setuptools/_distutils/dist.py similarity index 100% rename from distutils/dist.py rename to setuptools/_distutils/dist.py diff --git a/distutils/errors.py b/setuptools/_distutils/errors.py similarity index 100% rename from distutils/errors.py rename to setuptools/_distutils/errors.py diff --git a/distutils/extension.py b/setuptools/_distutils/extension.py similarity index 100% rename from distutils/extension.py rename to setuptools/_distutils/extension.py diff --git a/distutils/fancy_getopt.py b/setuptools/_distutils/fancy_getopt.py similarity index 100% rename from distutils/fancy_getopt.py rename to setuptools/_distutils/fancy_getopt.py diff --git a/distutils/file_util.py b/setuptools/_distutils/file_util.py similarity index 100% rename from distutils/file_util.py rename to setuptools/_distutils/file_util.py diff --git a/distutils/filelist.py b/setuptools/_distutils/filelist.py similarity index 100% rename from distutils/filelist.py rename to setuptools/_distutils/filelist.py diff --git a/distutils/log.py b/setuptools/_distutils/log.py similarity index 100% rename from distutils/log.py rename to setuptools/_distutils/log.py diff --git a/distutils/msvc9compiler.py b/setuptools/_distutils/msvc9compiler.py similarity index 100% rename from distutils/msvc9compiler.py rename to setuptools/_distutils/msvc9compiler.py diff --git a/distutils/msvccompiler.py b/setuptools/_distutils/msvccompiler.py similarity index 100% rename from distutils/msvccompiler.py rename to setuptools/_distutils/msvccompiler.py diff --git a/distutils/spawn.py b/setuptools/_distutils/spawn.py similarity index 100% rename from distutils/spawn.py rename to setuptools/_distutils/spawn.py diff --git a/distutils/sysconfig.py b/setuptools/_distutils/sysconfig.py similarity index 100% rename from distutils/sysconfig.py rename to setuptools/_distutils/sysconfig.py diff --git a/distutils/tests/Setup.sample b/setuptools/_distutils/tests/Setup.sample similarity index 100% rename from distutils/tests/Setup.sample rename to setuptools/_distutils/tests/Setup.sample diff --git a/distutils/tests/__init__.py b/setuptools/_distutils/tests/__init__.py similarity index 100% rename from distutils/tests/__init__.py rename to setuptools/_distutils/tests/__init__.py diff --git a/distutils/tests/includetest.rst b/setuptools/_distutils/tests/includetest.rst similarity index 100% rename from distutils/tests/includetest.rst rename to setuptools/_distutils/tests/includetest.rst diff --git a/distutils/tests/py35compat.py b/setuptools/_distutils/tests/py35compat.py similarity index 100% rename from distutils/tests/py35compat.py rename to setuptools/_distutils/tests/py35compat.py diff --git a/distutils/tests/support.py b/setuptools/_distutils/tests/support.py similarity index 100% rename from distutils/tests/support.py rename to setuptools/_distutils/tests/support.py diff --git a/distutils/tests/test_archive_util.py b/setuptools/_distutils/tests/test_archive_util.py similarity index 100% rename from distutils/tests/test_archive_util.py rename to setuptools/_distutils/tests/test_archive_util.py diff --git a/distutils/tests/test_bdist.py b/setuptools/_distutils/tests/test_bdist.py similarity index 100% rename from distutils/tests/test_bdist.py rename to setuptools/_distutils/tests/test_bdist.py diff --git a/distutils/tests/test_bdist_dumb.py b/setuptools/_distutils/tests/test_bdist_dumb.py similarity index 100% rename from distutils/tests/test_bdist_dumb.py rename to setuptools/_distutils/tests/test_bdist_dumb.py diff --git a/distutils/tests/test_bdist_msi.py b/setuptools/_distutils/tests/test_bdist_msi.py similarity index 100% rename from distutils/tests/test_bdist_msi.py rename to setuptools/_distutils/tests/test_bdist_msi.py diff --git a/distutils/tests/test_bdist_rpm.py b/setuptools/_distutils/tests/test_bdist_rpm.py similarity index 100% rename from distutils/tests/test_bdist_rpm.py rename to setuptools/_distutils/tests/test_bdist_rpm.py diff --git a/distutils/tests/test_bdist_wininst.py b/setuptools/_distutils/tests/test_bdist_wininst.py similarity index 100% rename from distutils/tests/test_bdist_wininst.py rename to setuptools/_distutils/tests/test_bdist_wininst.py diff --git a/distutils/tests/test_build.py b/setuptools/_distutils/tests/test_build.py similarity index 100% rename from distutils/tests/test_build.py rename to setuptools/_distutils/tests/test_build.py diff --git a/distutils/tests/test_build_clib.py b/setuptools/_distutils/tests/test_build_clib.py similarity index 100% rename from distutils/tests/test_build_clib.py rename to setuptools/_distutils/tests/test_build_clib.py diff --git a/distutils/tests/test_build_ext.py b/setuptools/_distutils/tests/test_build_ext.py similarity index 100% rename from distutils/tests/test_build_ext.py rename to setuptools/_distutils/tests/test_build_ext.py diff --git a/distutils/tests/test_build_py.py b/setuptools/_distutils/tests/test_build_py.py similarity index 100% rename from distutils/tests/test_build_py.py rename to setuptools/_distutils/tests/test_build_py.py diff --git a/distutils/tests/test_build_scripts.py b/setuptools/_distutils/tests/test_build_scripts.py similarity index 100% rename from distutils/tests/test_build_scripts.py rename to setuptools/_distutils/tests/test_build_scripts.py diff --git a/distutils/tests/test_check.py b/setuptools/_distutils/tests/test_check.py similarity index 100% rename from distutils/tests/test_check.py rename to setuptools/_distutils/tests/test_check.py diff --git a/distutils/tests/test_clean.py b/setuptools/_distutils/tests/test_clean.py similarity index 100% rename from distutils/tests/test_clean.py rename to setuptools/_distutils/tests/test_clean.py diff --git a/distutils/tests/test_cmd.py b/setuptools/_distutils/tests/test_cmd.py similarity index 100% rename from distutils/tests/test_cmd.py rename to setuptools/_distutils/tests/test_cmd.py diff --git a/distutils/tests/test_config.py b/setuptools/_distutils/tests/test_config.py similarity index 100% rename from distutils/tests/test_config.py rename to setuptools/_distutils/tests/test_config.py diff --git a/distutils/tests/test_config_cmd.py b/setuptools/_distutils/tests/test_config_cmd.py similarity index 100% rename from distutils/tests/test_config_cmd.py rename to setuptools/_distutils/tests/test_config_cmd.py diff --git a/distutils/tests/test_core.py b/setuptools/_distutils/tests/test_core.py similarity index 100% rename from distutils/tests/test_core.py rename to setuptools/_distutils/tests/test_core.py diff --git a/distutils/tests/test_cygwinccompiler.py b/setuptools/_distutils/tests/test_cygwinccompiler.py similarity index 100% rename from distutils/tests/test_cygwinccompiler.py rename to setuptools/_distutils/tests/test_cygwinccompiler.py diff --git a/distutils/tests/test_dep_util.py b/setuptools/_distutils/tests/test_dep_util.py similarity index 100% rename from distutils/tests/test_dep_util.py rename to setuptools/_distutils/tests/test_dep_util.py diff --git a/distutils/tests/test_dir_util.py b/setuptools/_distutils/tests/test_dir_util.py similarity index 100% rename from distutils/tests/test_dir_util.py rename to setuptools/_distutils/tests/test_dir_util.py diff --git a/distutils/tests/test_dist.py b/setuptools/_distutils/tests/test_dist.py similarity index 100% rename from distutils/tests/test_dist.py rename to setuptools/_distutils/tests/test_dist.py diff --git a/distutils/tests/test_extension.py b/setuptools/_distutils/tests/test_extension.py similarity index 100% rename from distutils/tests/test_extension.py rename to setuptools/_distutils/tests/test_extension.py diff --git a/distutils/tests/test_file_util.py b/setuptools/_distutils/tests/test_file_util.py similarity index 100% rename from distutils/tests/test_file_util.py rename to setuptools/_distutils/tests/test_file_util.py diff --git a/distutils/tests/test_filelist.py b/setuptools/_distutils/tests/test_filelist.py similarity index 100% rename from distutils/tests/test_filelist.py rename to setuptools/_distutils/tests/test_filelist.py diff --git a/distutils/tests/test_install.py b/setuptools/_distutils/tests/test_install.py similarity index 100% rename from distutils/tests/test_install.py rename to setuptools/_distutils/tests/test_install.py diff --git a/distutils/tests/test_install_data.py b/setuptools/_distutils/tests/test_install_data.py similarity index 100% rename from distutils/tests/test_install_data.py rename to setuptools/_distutils/tests/test_install_data.py diff --git a/distutils/tests/test_install_headers.py b/setuptools/_distutils/tests/test_install_headers.py similarity index 100% rename from distutils/tests/test_install_headers.py rename to setuptools/_distutils/tests/test_install_headers.py diff --git a/distutils/tests/test_install_lib.py b/setuptools/_distutils/tests/test_install_lib.py similarity index 100% rename from distutils/tests/test_install_lib.py rename to setuptools/_distutils/tests/test_install_lib.py diff --git a/distutils/tests/test_install_scripts.py b/setuptools/_distutils/tests/test_install_scripts.py similarity index 100% rename from distutils/tests/test_install_scripts.py rename to setuptools/_distutils/tests/test_install_scripts.py diff --git a/distutils/tests/test_log.py b/setuptools/_distutils/tests/test_log.py similarity index 100% rename from distutils/tests/test_log.py rename to setuptools/_distutils/tests/test_log.py diff --git a/distutils/tests/test_msvc9compiler.py b/setuptools/_distutils/tests/test_msvc9compiler.py similarity index 100% rename from distutils/tests/test_msvc9compiler.py rename to setuptools/_distutils/tests/test_msvc9compiler.py diff --git a/distutils/tests/test_msvccompiler.py b/setuptools/_distutils/tests/test_msvccompiler.py similarity index 100% rename from distutils/tests/test_msvccompiler.py rename to setuptools/_distutils/tests/test_msvccompiler.py diff --git a/distutils/tests/test_register.py b/setuptools/_distutils/tests/test_register.py similarity index 100% rename from distutils/tests/test_register.py rename to setuptools/_distutils/tests/test_register.py diff --git a/distutils/tests/test_sdist.py b/setuptools/_distutils/tests/test_sdist.py similarity index 100% rename from distutils/tests/test_sdist.py rename to setuptools/_distutils/tests/test_sdist.py diff --git a/distutils/tests/test_spawn.py b/setuptools/_distutils/tests/test_spawn.py similarity index 100% rename from distutils/tests/test_spawn.py rename to setuptools/_distutils/tests/test_spawn.py diff --git a/distutils/tests/test_sysconfig.py b/setuptools/_distutils/tests/test_sysconfig.py similarity index 100% rename from distutils/tests/test_sysconfig.py rename to setuptools/_distutils/tests/test_sysconfig.py diff --git a/distutils/tests/test_text_file.py b/setuptools/_distutils/tests/test_text_file.py similarity index 100% rename from distutils/tests/test_text_file.py rename to setuptools/_distutils/tests/test_text_file.py diff --git a/distutils/tests/test_unixccompiler.py b/setuptools/_distutils/tests/test_unixccompiler.py similarity index 100% rename from distutils/tests/test_unixccompiler.py rename to setuptools/_distutils/tests/test_unixccompiler.py diff --git a/distutils/tests/test_upload.py b/setuptools/_distutils/tests/test_upload.py similarity index 100% rename from distutils/tests/test_upload.py rename to setuptools/_distutils/tests/test_upload.py diff --git a/distutils/tests/test_util.py b/setuptools/_distutils/tests/test_util.py similarity index 100% rename from distutils/tests/test_util.py rename to setuptools/_distutils/tests/test_util.py diff --git a/distutils/tests/test_version.py b/setuptools/_distutils/tests/test_version.py similarity index 100% rename from distutils/tests/test_version.py rename to setuptools/_distutils/tests/test_version.py diff --git a/distutils/tests/test_versionpredicate.py b/setuptools/_distutils/tests/test_versionpredicate.py similarity index 100% rename from distutils/tests/test_versionpredicate.py rename to setuptools/_distutils/tests/test_versionpredicate.py diff --git a/distutils/text_file.py b/setuptools/_distutils/text_file.py similarity index 100% rename from distutils/text_file.py rename to setuptools/_distutils/text_file.py diff --git a/distutils/unixccompiler.py b/setuptools/_distutils/unixccompiler.py similarity index 100% rename from distutils/unixccompiler.py rename to setuptools/_distutils/unixccompiler.py diff --git a/distutils/util.py b/setuptools/_distutils/util.py similarity index 100% rename from distutils/util.py rename to setuptools/_distutils/util.py diff --git a/distutils/version.py b/setuptools/_distutils/version.py similarity index 100% rename from distutils/version.py rename to setuptools/_distutils/version.py diff --git a/distutils/versionpredicate.py b/setuptools/_distutils/versionpredicate.py similarity index 100% rename from distutils/versionpredicate.py rename to setuptools/_distutils/versionpredicate.py diff --git a/setuptools/distutils_patch.py b/setuptools/distutils_patch.py index f9e637988a..06eed82f0e 100644 --- a/setuptools/distutils_patch.py +++ b/setuptools/distutils_patch.py @@ -8,19 +8,7 @@ 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() From 97192962e89a24a02effd1f7a541108335517253 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 3 Jul 2020 04:54:40 -0400 Subject: [PATCH 2593/2594] 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 06eed82f0e..b2095fbac7 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 From ca0065fb62c1728cb42b86ae79375533856d2248 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 3 Jul 2020 05:13:12 -0400 Subject: [PATCH 2594/2594] 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 29030b3210..244a698942 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.

}g6xF0(oRpEzalFW#W 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Ν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