diff options
Diffstat (limited to 'setuptools')
73 files changed, 3951 insertions, 1086 deletions
diff --git a/setuptools/__init__.py b/setuptools/__init__.py index e740e3d0..fc9b7b93 100644 --- a/setuptools/__init__.py +++ b/setuptools/__init__.py @@ -1,19 +1,24 @@ """Extensions to the 'distutils' for large or complex distributions""" -from setuptools.extension import Extension, Library -from setuptools.dist import Distribution, Feature, _get_unpatched -import distutils.core, setuptools.command -from setuptools.depends import Require -from distutils.core import Command as _Command -from distutils.util import convert_path + import os import sys +import distutils.core +import distutils.filelist +from distutils.core import Command as _Command +from distutils.util import convert_path + +import setuptools.version +from setuptools.extension import Extension +from setuptools.dist import Distribution, Feature, _get_unpatched +from setuptools.depends import Require -__version__ = '0.7.9' __all__ = [ 'setup', 'Distribution', 'Feature', 'Command', 'Extension', 'Require', 'find_packages' ] +__version__ = setuptools.version.__version__ + bootstrap_install_from = None # If we run 2to3 on .py files, should we also convert docstrings? @@ -37,10 +42,14 @@ def find_packages(where='.', exclude=()): where,prefix = stack.pop(0) for name in os.listdir(where): fn = os.path.join(where,name) - if ('.' not in name and os.path.isdir(fn) and - os.path.isfile(os.path.join(fn,'__init__.py')) - ): - out.append(prefix+name); stack.append((fn,prefix+name+'.')) + looks_like_package = ( + '.' not in name + and os.path.isdir(fn) + and os.path.isfile(os.path.join(fn, '__init__.py')) + ) + if looks_like_package: + out.append(prefix+name) + stack.append((fn, prefix+name+'.')) for pat in list(exclude)+['ez_setup']: from fnmatch import fnmatchcase out = [item for item in out if not fnmatchcase(item,pat)] @@ -67,7 +76,6 @@ class Command(_Command): setattr(cmd,k,v) # update command with keywords return cmd -import distutils.core distutils.core.Command = Command # we can't patch distutils.cmd, alas def findall(dir = os.curdir): @@ -83,12 +91,8 @@ def findall(dir = os.curdir): all_files.extend(filter(os.path.isfile, files)) return all_files -import distutils.filelist distutils.filelist.findall = findall # fix findall bug in distutils. # sys.dont_write_bytecode was introduced in Python 2.6. -if ((hasattr(sys, "dont_write_bytecode") and sys.dont_write_bytecode) or - (not hasattr(sys, "dont_write_bytecode") and os.environ.get("PYTHONDONTWRITEBYTECODE"))): - _dont_write_bytecode = True -else: - _dont_write_bytecode = False +_dont_write_bytecode = getattr(sys, 'dont_write_bytecode', + bool(os.environ.get("PYTHONDONTWRITEBYTECODE"))) diff --git a/setuptools/_backport/__init__.py b/setuptools/_backport/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/setuptools/_backport/__init__.py diff --git a/setuptools/_backport/hashlib/__init__.py b/setuptools/_backport/hashlib/__init__.py new file mode 100644 index 00000000..5aeab496 --- /dev/null +++ b/setuptools/_backport/hashlib/__init__.py @@ -0,0 +1,146 @@ +# $Id$ +# +# Copyright (C) 2005 Gregory P. Smith (greg@krypto.org) +# Licensed to PSF under a Contributor Agreement. +# + +__doc__ = """hashlib module - A common interface to many hash functions. + +new(name, string='') - returns a new hash object implementing the + given hash function; initializing the hash + using the given string data. + +Named constructor functions are also available, these are much faster +than using new(): + +md5(), sha1(), sha224(), sha256(), sha384(), and sha512() + +More algorithms may be available on your platform but the above are +guaranteed to exist. + +NOTE: If you want the adler32 or crc32 hash functions they are available in +the zlib module. + +Choose your hash function wisely. Some have known collision weaknesses. +sha384 and sha512 will be slow on 32 bit platforms. + +Hash objects have these methods: + - update(arg): Update the hash object with the string arg. Repeated calls + are equivalent to a single call with the concatenation of all + the arguments. + - digest(): Return the digest of the strings passed to the update() method + so far. This may contain non-ASCII characters, including + NUL bytes. + - hexdigest(): Like digest() except the digest is returned as a string of + double length, containing only hexadecimal digits. + - copy(): Return a copy (clone) of the hash object. This can be used to + efficiently compute the digests of strings that share a common + initial substring. + +For example, to obtain the digest of the string 'Nobody inspects the +spammish repetition': + + >>> import hashlib + >>> m = hashlib.md5() + >>> m.update("Nobody inspects") + >>> m.update(" the spammish repetition") + >>> m.digest() + '\\xbbd\\x9c\\x83\\xdd\\x1e\\xa5\\xc9\\xd9\\xde\\xc9\\xa1\\x8d\\xf0\\xff\\xe9' + +More condensed: + + >>> hashlib.sha224("Nobody inspects the spammish repetition").hexdigest() + 'a4337bc45a8fc544c03f52dc550cd6e1e87021bc896588bd79e901e2' + +""" + +# This tuple and __get_builtin_constructor() must be modified if a new +# always available algorithm is added. +__always_supported = ('md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512') + +algorithms = __always_supported + +__all__ = __always_supported + ('new', 'algorithms') + + +def __get_builtin_constructor(name): + try: + if name in ('SHA1', 'sha1'): + import _sha + return _sha.new + elif name in ('MD5', 'md5'): + import md5 + return md5.new + elif name in ('SHA256', 'sha256', 'SHA224', 'sha224'): + import _sha256 + bs = name[3:] + if bs == '256': + return _sha256.sha256 + elif bs == '224': + return _sha256.sha224 + elif name in ('SHA512', 'sha512', 'SHA384', 'sha384'): + import _sha512 + bs = name[3:] + if bs == '512': + return _sha512.sha512 + elif bs == '384': + return _sha512.sha384 + except ImportError: + pass # no extension module, this hash is unsupported. + + raise ValueError('unsupported hash type %s' % name) + + +def __get_openssl_constructor(name): + try: + f = getattr(_hashlib, 'openssl_' + name) + # Allow the C module to raise ValueError. The function will be + # defined but the hash not actually available thanks to OpenSSL. + f() + # Use the C function directly (very fast) + return f + except (AttributeError, ValueError): + return __get_builtin_constructor(name) + + +def __py_new(name, string=''): + """new(name, string='') - Return a new hashing object using the named algorithm; + optionally initialized with a string. + """ + return __get_builtin_constructor(name)(string) + + +def __hash_new(name, string=''): + """new(name, string='') - Return a new hashing object using the named algorithm; + optionally initialized with a string. + """ + try: + return _hashlib.new(name, string) + except ValueError: + # If the _hashlib module (OpenSSL) doesn't support the named + # hash, try using our builtin implementations. + # This allows for SHA224/256 and SHA384/512 support even though + # the OpenSSL library prior to 0.9.8 doesn't provide them. + return __get_builtin_constructor(name)(string) + + +try: + import _hashlib + new = __hash_new + __get_hash = __get_openssl_constructor +except ImportError: + new = __py_new + __get_hash = __get_builtin_constructor + +for __func_name in __always_supported: + # try them all, some may not work due to the OpenSSL + # version not supporting that algorithm. + try: + globals()[__func_name] = __get_hash(__func_name) + except ValueError: + import logging + logging.exception('code for hash %s was not found.', __func_name) + +# Cleanup locals() +del __always_supported, __func_name, __get_hash +del __py_new, __hash_new, __get_openssl_constructor diff --git a/setuptools/_backport/hashlib/_sha.py b/setuptools/_backport/hashlib/_sha.py new file mode 100644 index 00000000..d49993c8 --- /dev/null +++ b/setuptools/_backport/hashlib/_sha.py @@ -0,0 +1,359 @@ +# -*- coding: iso-8859-1 -*- +"""A sample implementation of SHA-1 in pure Python. + + Framework adapted from Dinu Gherman's MD5 implementation by + J. Hallén and L. Creighton. SHA-1 implementation based directly on + the text of the NIST standard FIPS PUB 180-1. +""" + + +__date__ = '2004-11-17' +__version__ = 0.91 # Modernised by J. Hallén and L. Creighton for Pypy + + +import struct, copy + + +# ====================================================================== +# Bit-Manipulation helpers +# +# _long2bytes() was contributed by Barry Warsaw +# and is reused here with tiny modifications. +# ====================================================================== + +def _long2bytesBigEndian(n, blocksize=0): + """Convert a long integer to a byte string. + + If optional blocksize is given and greater than zero, pad the front + of the byte string with binary zeros so that the length is a multiple + of blocksize. + """ + + # After much testing, this algorithm was deemed to be the fastest. + s = '' + pack = struct.pack + while n > 0: + s = pack('>I', n & 0xffffffff) + s + n = n >> 32 + + # Strip off leading zeros. + for i in range(len(s)): + if s[i] != '\000': + break + else: + # Only happens when n == 0. + s = '\000' + i = 0 + + s = s[i:] + + # Add back some pad bytes. This could be done more efficiently + # w.r.t. the de-padding being done above, but sigh... + if blocksize > 0 and len(s) % blocksize: + s = (blocksize - len(s) % blocksize) * '\000' + s + + return s + + +def _bytelist2longBigEndian(list): + "Transform a list of characters into a list of longs." + + imax = len(list) // 4 + hl = [0] * imax + + j = 0 + i = 0 + while i < imax: + b0 = ord(list[j]) << 24 + b1 = ord(list[j+1]) << 16 + b2 = ord(list[j+2]) << 8 + b3 = ord(list[j+3]) + hl[i] = b0 | b1 | b2 | b3 + i = i+1 + j = j+4 + + return hl + + +def _rotateLeft(x, n): + "Rotate x (32 bit) left n bits circularly." + + return (x << n) | (x >> (32-n)) + + +# ====================================================================== +# The SHA transformation functions +# +# ====================================================================== + +def f0_19(B, C, D): + return (B & C) | ((~ B) & D) + +def f20_39(B, C, D): + return B ^ C ^ D + +def f40_59(B, C, D): + return (B & C) | (B & D) | (C & D) + +def f60_79(B, C, D): + return B ^ C ^ D + + +f = [f0_19, f20_39, f40_59, f60_79] + +# Constants to be used +K = [ + 0x5A827999, # ( 0 <= t <= 19) + 0x6ED9EBA1, # (20 <= t <= 39) + 0x8F1BBCDC, # (40 <= t <= 59) + 0xCA62C1D6 # (60 <= t <= 79) + ] + +class sha: + "An implementation of the MD5 hash function in pure Python." + + digest_size = digestsize = 20 + block_size = 1 + + def __init__(self): + "Initialisation." + + # Initial message length in bits(!). + self.length = 0 + self.count = [0, 0] + + # Initial empty message as a sequence of bytes (8 bit characters). + self.input = [] + + # Call a separate init function, that can be used repeatedly + # to start from scratch on the same object. + self.init() + + + def init(self): + "Initialize the message-digest and set all fields to zero." + + self.length = 0 + self.input = [] + + # Initial 160 bit message digest (5 times 32 bit). + self.H0 = 0x67452301 + self.H1 = 0xEFCDAB89 + self.H2 = 0x98BADCFE + self.H3 = 0x10325476 + self.H4 = 0xC3D2E1F0 + + def _transform(self, W): + + for t in range(16, 80): + W.append(_rotateLeft( + W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16], 1) & 0xffffffff) + + A = self.H0 + B = self.H1 + C = self.H2 + D = self.H3 + E = self.H4 + + """ + This loop was unrolled to gain about 10% in speed + for t in range(0, 80): + TEMP = _rotateLeft(A, 5) + f[t/20] + E + W[t] + K[t/20] + E = D + D = C + C = _rotateLeft(B, 30) & 0xffffffff + B = A + A = TEMP & 0xffffffff + """ + + for t in range(0, 20): + TEMP = _rotateLeft(A, 5) + ((B & C) | ((~ B) & D)) + E + W[t] + K[0] + E = D + D = C + C = _rotateLeft(B, 30) & 0xffffffff + B = A + A = TEMP & 0xffffffff + + for t in range(20, 40): + TEMP = _rotateLeft(A, 5) + (B ^ C ^ D) + E + W[t] + K[1] + E = D + D = C + C = _rotateLeft(B, 30) & 0xffffffff + B = A + A = TEMP & 0xffffffff + + for t in range(40, 60): + TEMP = _rotateLeft(A, 5) + ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2] + E = D + D = C + C = _rotateLeft(B, 30) & 0xffffffff + B = A + A = TEMP & 0xffffffff + + for t in range(60, 80): + TEMP = _rotateLeft(A, 5) + (B ^ C ^ D) + E + W[t] + K[3] + E = D + D = C + C = _rotateLeft(B, 30) & 0xffffffff + B = A + A = TEMP & 0xffffffff + + + self.H0 = (self.H0 + A) & 0xffffffff + self.H1 = (self.H1 + B) & 0xffffffff + self.H2 = (self.H2 + C) & 0xffffffff + self.H3 = (self.H3 + D) & 0xffffffff + self.H4 = (self.H4 + E) & 0xffffffff + + + # Down from here all methods follow the Python Standard Library + # API of the sha module. + + def update(self, inBuf): + """Add to the current message. + + Update the md5 object with the string arg. Repeated calls + are equivalent to a single call with the concatenation of all + the arguments, i.e. m.update(a); m.update(b) is equivalent + to m.update(a+b). + + The hash is immediately calculated for all full blocks. The final + calculation is made in digest(). It will calculate 1-2 blocks, + depending on how much padding we have to add. This allows us to + keep an intermediate value for the hash, so that we only need to + make minimal recalculation if we call update() to add more data + to the hashed string. + """ + + leninBuf = len(inBuf) + + # Compute number of bytes mod 64. + index = (self.count[1] >> 3) & 0x3F + + # Update number of bits. + self.count[1] = self.count[1] + (leninBuf << 3) + if self.count[1] < (leninBuf << 3): + self.count[0] = self.count[0] + 1 + self.count[0] = self.count[0] + (leninBuf >> 29) + + partLen = 64 - index + + if leninBuf >= partLen: + self.input[index:] = list(inBuf[:partLen]) + self._transform(_bytelist2longBigEndian(self.input)) + i = partLen + while i + 63 < leninBuf: + self._transform(_bytelist2longBigEndian(list(inBuf[i:i+64]))) + i = i + 64 + else: + self.input = list(inBuf[i:leninBuf]) + else: + i = 0 + self.input = self.input + list(inBuf) + + + def digest(self): + """Terminate the message-digest computation and return digest. + + Return the digest of the strings passed to the update() + method so far. This is a 16-byte string which may contain + non-ASCII characters, including null bytes. + """ + + H0 = self.H0 + H1 = self.H1 + H2 = self.H2 + H3 = self.H3 + H4 = self.H4 + input = [] + self.input + count = [] + self.count + + index = (self.count[1] >> 3) & 0x3f + + if index < 56: + padLen = 56 - index + else: + padLen = 120 - index + + padding = ['\200'] + ['\000'] * 63 + self.update(padding[:padLen]) + + # Append length (before padding). + bits = _bytelist2longBigEndian(self.input[:56]) + count + + self._transform(bits) + + # Store state in digest. + digest = _long2bytesBigEndian(self.H0, 4) + \ + _long2bytesBigEndian(self.H1, 4) + \ + _long2bytesBigEndian(self.H2, 4) + \ + _long2bytesBigEndian(self.H3, 4) + \ + _long2bytesBigEndian(self.H4, 4) + + self.H0 = H0 + self.H1 = H1 + self.H2 = H2 + self.H3 = H3 + self.H4 = H4 + self.input = input + self.count = count + + return digest + + + def hexdigest(self): + """Terminate and return digest in HEX form. + + Like digest() except the digest is returned as a string of + length 32, containing only hexadecimal digits. This may be + used to exchange the value safely in email or other non- + binary environments. + """ + return ''.join(['%02x' % ord(c) for c in self.digest()]) + + def copy(self): + """Return a clone object. + + Return a copy ('clone') of the md5 object. This can be used + to efficiently compute the digests of strings that share + a common initial substring. + """ + + return copy.deepcopy(self) + + +# ====================================================================== +# Mimic Python top-level functions from standard library API +# for consistency with the _sha module of the standard library. +# ====================================================================== + +# These are mandatory variables in the module. They have constant values +# in the SHA standard. + +digest_size = 20 +digestsize = 20 +blocksize = 1 + +def new(arg=None): + """Return a new sha crypto object. + + If arg is present, the method call update(arg) is made. + """ + + crypto = sha() + if arg: + crypto.update(arg) + + return crypto + + +if __name__ == "__main__": + a_str = "just a test string" + + assert 'da39a3ee5e6b4b0d3255bfef95601890afd80709' == new().hexdigest() + assert '3f0cf2e3d9e5903e839417dfc47fed6bfa6457f6' == new(a_str).hexdigest() + assert '0852b254078fe3772568a4aba37b917f3d4066ba' == new(a_str*7).hexdigest() + + s = new(a_str) + s.update(a_str) + assert '8862c1b50967f39d3db6bdc2877d9ccebd3102e5' == s.hexdigest() diff --git a/setuptools/_backport/hashlib/_sha256.py b/setuptools/_backport/hashlib/_sha256.py new file mode 100644 index 00000000..805dbd08 --- /dev/null +++ b/setuptools/_backport/hashlib/_sha256.py @@ -0,0 +1,260 @@ +import struct + +SHA_BLOCKSIZE = 64 +SHA_DIGESTSIZE = 32 + + +def new_shaobject(): + return { + 'digest': [0]*8, + 'count_lo': 0, + 'count_hi': 0, + 'data': [0]* SHA_BLOCKSIZE, + 'local': 0, + 'digestsize': 0 + } + +ROR = lambda x, y: (((x & 0xffffffff) >> (y & 31)) | (x << (32 - (y & 31)))) & 0xffffffff +Ch = lambda x, y, z: (z ^ (x & (y ^ z))) +Maj = lambda x, y, z: (((x | y) & z) | (x & y)) +S = lambda x, n: ROR(x, n) +R = lambda x, n: (x & 0xffffffff) >> n +Sigma0 = lambda x: (S(x, 2) ^ S(x, 13) ^ S(x, 22)) +Sigma1 = lambda x: (S(x, 6) ^ S(x, 11) ^ S(x, 25)) +Gamma0 = lambda x: (S(x, 7) ^ S(x, 18) ^ R(x, 3)) +Gamma1 = lambda x: (S(x, 17) ^ S(x, 19) ^ R(x, 10)) + +def sha_transform(sha_info): + W = [] + + d = sha_info['data'] + for i in xrange(0,16): + W.append( (d[4*i]<<24) + (d[4*i+1]<<16) + (d[4*i+2]<<8) + d[4*i+3]) + + for i in xrange(16,64): + W.append( (Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16]) & 0xffffffff ) + + ss = sha_info['digest'][:] + + def RND(a,b,c,d,e,f,g,h,i,ki): + t0 = h + Sigma1(e) + Ch(e, f, g) + ki + W[i]; + t1 = Sigma0(a) + Maj(a, b, c); + d += t0; + h = t0 + t1; + return d & 0xffffffff, h & 0xffffffff + + ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],0,0x428a2f98); + ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],1,0x71374491); + ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],2,0xb5c0fbcf); + ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],3,0xe9b5dba5); + ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],4,0x3956c25b); + ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],5,0x59f111f1); + ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],6,0x923f82a4); + ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],7,0xab1c5ed5); + ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],8,0xd807aa98); + ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],9,0x12835b01); + ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],10,0x243185be); + ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],11,0x550c7dc3); + ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],12,0x72be5d74); + ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],13,0x80deb1fe); + ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],14,0x9bdc06a7); + ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],15,0xc19bf174); + ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],16,0xe49b69c1); + ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],17,0xefbe4786); + ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],18,0x0fc19dc6); + ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],19,0x240ca1cc); + ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],20,0x2de92c6f); + ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],21,0x4a7484aa); + ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],22,0x5cb0a9dc); + ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],23,0x76f988da); + ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],24,0x983e5152); + ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],25,0xa831c66d); + ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],26,0xb00327c8); + ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],27,0xbf597fc7); + ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],28,0xc6e00bf3); + ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],29,0xd5a79147); + ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],30,0x06ca6351); + ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],31,0x14292967); + ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],32,0x27b70a85); + ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],33,0x2e1b2138); + ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],34,0x4d2c6dfc); + ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],35,0x53380d13); + ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],36,0x650a7354); + ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],37,0x766a0abb); + ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],38,0x81c2c92e); + ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],39,0x92722c85); + ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],40,0xa2bfe8a1); + ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],41,0xa81a664b); + ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],42,0xc24b8b70); + ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],43,0xc76c51a3); + ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],44,0xd192e819); + ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],45,0xd6990624); + ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],46,0xf40e3585); + ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],47,0x106aa070); + ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],48,0x19a4c116); + ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],49,0x1e376c08); + ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],50,0x2748774c); + ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],51,0x34b0bcb5); + ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],52,0x391c0cb3); + ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],53,0x4ed8aa4a); + ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],54,0x5b9cca4f); + ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],55,0x682e6ff3); + ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],56,0x748f82ee); + ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],57,0x78a5636f); + ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],58,0x84c87814); + ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],59,0x8cc70208); + ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],60,0x90befffa); + ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],61,0xa4506ceb); + ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],62,0xbef9a3f7); + ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],63,0xc67178f2); + + dig = [] + for i, x in enumerate(sha_info['digest']): + dig.append( (x + ss[i]) & 0xffffffff ) + sha_info['digest'] = dig + +def sha_init(): + sha_info = new_shaobject() + sha_info['digest'] = [0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19] + sha_info['count_lo'] = 0 + sha_info['count_hi'] = 0 + sha_info['local'] = 0 + sha_info['digestsize'] = 32 + return sha_info + +def sha224_init(): + sha_info = new_shaobject() + sha_info['digest'] = [0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4] + sha_info['count_lo'] = 0 + sha_info['count_hi'] = 0 + sha_info['local'] = 0 + sha_info['digestsize'] = 28 + return sha_info + +def getbuf(s): + if isinstance(s, str): + return s + elif isinstance(s, unicode): + return str(s) + else: + return buffer(s) + +def sha_update(sha_info, buffer): + count = len(buffer) + buffer_idx = 0 + clo = (sha_info['count_lo'] + (count << 3)) & 0xffffffff + if clo < sha_info['count_lo']: + sha_info['count_hi'] += 1 + sha_info['count_lo'] = clo + + sha_info['count_hi'] += (count >> 29) + + if sha_info['local']: + i = SHA_BLOCKSIZE - sha_info['local'] + if i > count: + i = count + + # copy buffer + for x in enumerate(buffer[buffer_idx:buffer_idx+i]): + sha_info['data'][sha_info['local']+x[0]] = struct.unpack('B', x[1])[0] + + count -= i + buffer_idx += i + + sha_info['local'] += i + if sha_info['local'] == SHA_BLOCKSIZE: + sha_transform(sha_info) + sha_info['local'] = 0 + else: + return + + while count >= SHA_BLOCKSIZE: + # copy buffer + sha_info['data'] = [struct.unpack('B',c)[0] for c in buffer[buffer_idx:buffer_idx + SHA_BLOCKSIZE]] + count -= SHA_BLOCKSIZE + buffer_idx += SHA_BLOCKSIZE + sha_transform(sha_info) + + + # copy buffer + pos = sha_info['local'] + sha_info['data'][pos:pos+count] = [struct.unpack('B',c)[0] for c in buffer[buffer_idx:buffer_idx + count]] + sha_info['local'] = count + +def sha_final(sha_info): + lo_bit_count = sha_info['count_lo'] + hi_bit_count = sha_info['count_hi'] + count = (lo_bit_count >> 3) & 0x3f + sha_info['data'][count] = 0x80; + count += 1 + if count > SHA_BLOCKSIZE - 8: + # zero the bytes in data after the count + sha_info['data'] = sha_info['data'][:count] + ([0] * (SHA_BLOCKSIZE - count)) + sha_transform(sha_info) + # zero bytes in data + sha_info['data'] = [0] * SHA_BLOCKSIZE + else: + sha_info['data'] = sha_info['data'][:count] + ([0] * (SHA_BLOCKSIZE - count)) + + sha_info['data'][56] = (hi_bit_count >> 24) & 0xff + sha_info['data'][57] = (hi_bit_count >> 16) & 0xff + sha_info['data'][58] = (hi_bit_count >> 8) & 0xff + sha_info['data'][59] = (hi_bit_count >> 0) & 0xff + sha_info['data'][60] = (lo_bit_count >> 24) & 0xff + sha_info['data'][61] = (lo_bit_count >> 16) & 0xff + sha_info['data'][62] = (lo_bit_count >> 8) & 0xff + sha_info['data'][63] = (lo_bit_count >> 0) & 0xff + + sha_transform(sha_info) + + dig = [] + for i in sha_info['digest']: + dig.extend([ ((i>>24) & 0xff), ((i>>16) & 0xff), ((i>>8) & 0xff), (i & 0xff) ]) + return ''.join([chr(i) for i in dig]) + +class sha256(object): + digest_size = digestsize = SHA_DIGESTSIZE + block_size = SHA_BLOCKSIZE + + def __init__(self, s=None): + self._sha = sha_init() + if s: + sha_update(self._sha, getbuf(s)) + + def update(self, s): + sha_update(self._sha, getbuf(s)) + + def digest(self): + return sha_final(self._sha.copy())[:self._sha['digestsize']] + + def hexdigest(self): + return ''.join(['%.2x' % ord(i) for i in self.digest()]) + + def copy(self): + new = sha256.__new__(sha256) + new._sha = self._sha.copy() + return new + +class sha224(sha256): + digest_size = digestsize = 28 + + def __init__(self, s=None): + self._sha = sha224_init() + if s: + sha_update(self._sha, getbuf(s)) + + def copy(self): + new = sha224.__new__(sha224) + new._sha = self._sha.copy() + return new + +if __name__ == "__main__": + a_str = "just a test string" + + assert 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855' == sha256().hexdigest() + assert 'd7b553c6f09ac85d142415f857c5310f3bbbe7cdd787cce4b985acedd585266f' == sha256(a_str).hexdigest() + assert '8113ebf33c97daa9998762aacafe750c7cefc2b2f173c90c59663a57fe626f21' == sha256(a_str*7).hexdigest() + + s = sha256(a_str) + s.update(a_str) + assert '03d9963e05a094593190b6fc794cb1a3e1ac7d7883f0b5855268afeccc70d461' == s.hexdigest() diff --git a/setuptools/_backport/hashlib/_sha512.py b/setuptools/_backport/hashlib/_sha512.py new file mode 100644 index 00000000..68ff46f3 --- /dev/null +++ b/setuptools/_backport/hashlib/_sha512.py @@ -0,0 +1,288 @@ +""" +This code was Ported from CPython's sha512module.c +""" + +import struct + +SHA_BLOCKSIZE = 128 +SHA_DIGESTSIZE = 64 + + +def new_shaobject(): + return { + 'digest': [0]*8, + 'count_lo': 0, + 'count_hi': 0, + 'data': [0]* SHA_BLOCKSIZE, + 'local': 0, + 'digestsize': 0 + } + +ROR64 = lambda x, y: (((x & 0xffffffffffffffff) >> (y & 63)) | (x << (64 - (y & 63)))) & 0xffffffffffffffff +Ch = lambda x, y, z: (z ^ (x & (y ^ z))) +Maj = lambda x, y, z: (((x | y) & z) | (x & y)) +S = lambda x, n: ROR64(x, n) +R = lambda x, n: (x & 0xffffffffffffffff) >> n +Sigma0 = lambda x: (S(x, 28) ^ S(x, 34) ^ S(x, 39)) +Sigma1 = lambda x: (S(x, 14) ^ S(x, 18) ^ S(x, 41)) +Gamma0 = lambda x: (S(x, 1) ^ S(x, 8) ^ R(x, 7)) +Gamma1 = lambda x: (S(x, 19) ^ S(x, 61) ^ R(x, 6)) + +def sha_transform(sha_info): + W = [] + + d = sha_info['data'] + for i in xrange(0,16): + W.append( (d[8*i]<<56) + (d[8*i+1]<<48) + (d[8*i+2]<<40) + (d[8*i+3]<<32) + (d[8*i+4]<<24) + (d[8*i+5]<<16) + (d[8*i+6]<<8) + d[8*i+7]) + + for i in xrange(16,80): + W.append( (Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16]) & 0xffffffffffffffff ) + + ss = sha_info['digest'][:] + + def RND(a,b,c,d,e,f,g,h,i,ki): + t0 = (h + Sigma1(e) + Ch(e, f, g) + ki + W[i]) & 0xffffffffffffffff + t1 = (Sigma0(a) + Maj(a, b, c)) & 0xffffffffffffffff + d = (d + t0) & 0xffffffffffffffff + h = (t0 + t1) & 0xffffffffffffffff + return d & 0xffffffffffffffff, h & 0xffffffffffffffff + + ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],0,0x428a2f98d728ae22) + ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],1,0x7137449123ef65cd) + ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],2,0xb5c0fbcfec4d3b2f) + ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],3,0xe9b5dba58189dbbc) + ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],4,0x3956c25bf348b538) + ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],5,0x59f111f1b605d019) + ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],6,0x923f82a4af194f9b) + ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],7,0xab1c5ed5da6d8118) + ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],8,0xd807aa98a3030242) + ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],9,0x12835b0145706fbe) + ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],10,0x243185be4ee4b28c) + ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],11,0x550c7dc3d5ffb4e2) + ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],12,0x72be5d74f27b896f) + ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],13,0x80deb1fe3b1696b1) + ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],14,0x9bdc06a725c71235) + ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],15,0xc19bf174cf692694) + ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],16,0xe49b69c19ef14ad2) + ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],17,0xefbe4786384f25e3) + ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],18,0x0fc19dc68b8cd5b5) + ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],19,0x240ca1cc77ac9c65) + ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],20,0x2de92c6f592b0275) + ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],21,0x4a7484aa6ea6e483) + ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],22,0x5cb0a9dcbd41fbd4) + ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],23,0x76f988da831153b5) + ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],24,0x983e5152ee66dfab) + ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],25,0xa831c66d2db43210) + ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],26,0xb00327c898fb213f) + ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],27,0xbf597fc7beef0ee4) + ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],28,0xc6e00bf33da88fc2) + ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],29,0xd5a79147930aa725) + ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],30,0x06ca6351e003826f) + ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],31,0x142929670a0e6e70) + ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],32,0x27b70a8546d22ffc) + ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],33,0x2e1b21385c26c926) + ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],34,0x4d2c6dfc5ac42aed) + ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],35,0x53380d139d95b3df) + ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],36,0x650a73548baf63de) + ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],37,0x766a0abb3c77b2a8) + ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],38,0x81c2c92e47edaee6) + ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],39,0x92722c851482353b) + ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],40,0xa2bfe8a14cf10364) + ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],41,0xa81a664bbc423001) + ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],42,0xc24b8b70d0f89791) + ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],43,0xc76c51a30654be30) + ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],44,0xd192e819d6ef5218) + ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],45,0xd69906245565a910) + ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],46,0xf40e35855771202a) + ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],47,0x106aa07032bbd1b8) + ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],48,0x19a4c116b8d2d0c8) + ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],49,0x1e376c085141ab53) + ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],50,0x2748774cdf8eeb99) + ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],51,0x34b0bcb5e19b48a8) + ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],52,0x391c0cb3c5c95a63) + ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],53,0x4ed8aa4ae3418acb) + ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],54,0x5b9cca4f7763e373) + ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],55,0x682e6ff3d6b2b8a3) + ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],56,0x748f82ee5defb2fc) + ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],57,0x78a5636f43172f60) + ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],58,0x84c87814a1f0ab72) + ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],59,0x8cc702081a6439ec) + ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],60,0x90befffa23631e28) + ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],61,0xa4506cebde82bde9) + ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],62,0xbef9a3f7b2c67915) + ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],63,0xc67178f2e372532b) + ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],64,0xca273eceea26619c) + ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],65,0xd186b8c721c0c207) + ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],66,0xeada7dd6cde0eb1e) + ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],67,0xf57d4f7fee6ed178) + ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],68,0x06f067aa72176fba) + ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],69,0x0a637dc5a2c898a6) + ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],70,0x113f9804bef90dae) + ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],71,0x1b710b35131c471b) + ss[3], ss[7] = RND(ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],72,0x28db77f523047d84) + ss[2], ss[6] = RND(ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],73,0x32caab7b40c72493) + ss[1], ss[5] = RND(ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],ss[5],74,0x3c9ebe0a15c9bebc) + ss[0], ss[4] = RND(ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],ss[4],75,0x431d67c49c100d4c) + ss[7], ss[3] = RND(ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],ss[3],76,0x4cc5d4becb3e42b6) + ss[6], ss[2] = RND(ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],ss[2],77,0x597f299cfc657e2a) + ss[5], ss[1] = RND(ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],ss[1],78,0x5fcb6fab3ad6faec) + ss[4], ss[0] = RND(ss[1],ss[2],ss[3],ss[4],ss[5],ss[6],ss[7],ss[0],79,0x6c44198c4a475817) + + dig = [] + for i, x in enumerate(sha_info['digest']): + dig.append( (x + ss[i]) & 0xffffffffffffffff ) + sha_info['digest'] = dig + +def sha_init(): + sha_info = new_shaobject() + sha_info['digest'] = [ 0x6a09e667f3bcc908, 0xbb67ae8584caa73b, 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1, 0x510e527fade682d1, 0x9b05688c2b3e6c1f, 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179] + sha_info['count_lo'] = 0 + sha_info['count_hi'] = 0 + sha_info['local'] = 0 + sha_info['digestsize'] = 64 + return sha_info + +def sha384_init(): + sha_info = new_shaobject() + sha_info['digest'] = [ 0xcbbb9d5dc1059ed8, 0x629a292a367cd507, 0x9159015a3070dd17, 0x152fecd8f70e5939, 0x67332667ffc00b31, 0x8eb44a8768581511, 0xdb0c2e0d64f98fa7, 0x47b5481dbefa4fa4] + sha_info['count_lo'] = 0 + sha_info['count_hi'] = 0 + sha_info['local'] = 0 + sha_info['digestsize'] = 48 + return sha_info + +def getbuf(s): + if isinstance(s, str): + return s + elif isinstance(s, unicode): + return str(s) + else: + return buffer(s) + +def sha_update(sha_info, buffer): + count = len(buffer) + buffer_idx = 0 + clo = (sha_info['count_lo'] + (count << 3)) & 0xffffffff + if clo < sha_info['count_lo']: + sha_info['count_hi'] += 1 + sha_info['count_lo'] = clo + + sha_info['count_hi'] += (count >> 29) + + if sha_info['local']: + i = SHA_BLOCKSIZE - sha_info['local'] + if i > count: + i = count + + # copy buffer + for x in enumerate(buffer[buffer_idx:buffer_idx+i]): + sha_info['data'][sha_info['local']+x[0]] = struct.unpack('B', x[1])[0] + + count -= i + buffer_idx += i + + sha_info['local'] += i + if sha_info['local'] == SHA_BLOCKSIZE: + sha_transform(sha_info) + sha_info['local'] = 0 + else: + return + + while count >= SHA_BLOCKSIZE: + # copy buffer + sha_info['data'] = [struct.unpack('B',c)[0] for c in buffer[buffer_idx:buffer_idx + SHA_BLOCKSIZE]] + count -= SHA_BLOCKSIZE + buffer_idx += SHA_BLOCKSIZE + sha_transform(sha_info) + + # copy buffer + pos = sha_info['local'] + sha_info['data'][pos:pos+count] = [struct.unpack('B',c)[0] for c in buffer[buffer_idx:buffer_idx + count]] + sha_info['local'] = count + +def sha_final(sha_info): + lo_bit_count = sha_info['count_lo'] + hi_bit_count = sha_info['count_hi'] + count = (lo_bit_count >> 3) & 0x7f + sha_info['data'][count] = 0x80; + count += 1 + if count > SHA_BLOCKSIZE - 16: + # zero the bytes in data after the count + sha_info['data'] = sha_info['data'][:count] + ([0] * (SHA_BLOCKSIZE - count)) + sha_transform(sha_info) + # zero bytes in data + sha_info['data'] = [0] * SHA_BLOCKSIZE + else: + sha_info['data'] = sha_info['data'][:count] + ([0] * (SHA_BLOCKSIZE - count)) + + sha_info['data'][112] = 0; + sha_info['data'][113] = 0; + sha_info['data'][114] = 0; + sha_info['data'][115] = 0; + sha_info['data'][116] = 0; + sha_info['data'][117] = 0; + sha_info['data'][118] = 0; + sha_info['data'][119] = 0; + + sha_info['data'][120] = (hi_bit_count >> 24) & 0xff + sha_info['data'][121] = (hi_bit_count >> 16) & 0xff + sha_info['data'][122] = (hi_bit_count >> 8) & 0xff + sha_info['data'][123] = (hi_bit_count >> 0) & 0xff + sha_info['data'][124] = (lo_bit_count >> 24) & 0xff + sha_info['data'][125] = (lo_bit_count >> 16) & 0xff + sha_info['data'][126] = (lo_bit_count >> 8) & 0xff + sha_info['data'][127] = (lo_bit_count >> 0) & 0xff + + sha_transform(sha_info) + + dig = [] + for i in sha_info['digest']: + dig.extend([ ((i>>56) & 0xff), ((i>>48) & 0xff), ((i>>40) & 0xff), ((i>>32) & 0xff), ((i>>24) & 0xff), ((i>>16) & 0xff), ((i>>8) & 0xff), (i & 0xff) ]) + return ''.join([chr(i) for i in dig]) + +class sha512(object): + digest_size = digestsize = SHA_DIGESTSIZE + block_size = SHA_BLOCKSIZE + + def __init__(self, s=None): + self._sha = sha_init() + if s: + sha_update(self._sha, getbuf(s)) + + def update(self, s): + sha_update(self._sha, getbuf(s)) + + def digest(self): + return sha_final(self._sha.copy())[:self._sha['digestsize']] + + def hexdigest(self): + return ''.join(['%.2x' % ord(i) for i in self.digest()]) + + def copy(self): + new = sha512.__new__(sha512) + new._sha = self._sha.copy() + return new + +class sha384(sha512): + digest_size = digestsize = 48 + + def __init__(self, s=None): + self._sha = sha384_init() + if s: + sha_update(self._sha, getbuf(s)) + + def copy(self): + new = sha384.__new__(sha384) + new._sha = self._sha.copy() + return new + +if __name__ == "__main__": + a_str = "just a test string" + + assert sha512().hexdigest() == "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e" + assert sha512(a_str).hexdigest() == "68be4c6664af867dd1d01c8d77e963d87d77b702400c8fabae355a41b8927a5a5533a7f1c28509bbd65c5f3ac716f33be271fbda0ca018b71a84708c9fae8a53" + assert sha512(a_str*7).hexdigest() == "3233acdbfcfff9bff9fc72401d31dbffa62bd24e9ec846f0578d647da73258d9f0879f7fde01fe2cc6516af3f343807fdef79e23d696c923d79931db46bf1819" + + s = sha512(a_str) + s.update(a_str) + assert s.hexdigest() == "341aeb668730bbb48127d5531115f3c39d12cb9586a6ca770898398aff2411087cfe0b570689adf328cddeb1f00803acce6737a19f310b53bbdb0320828f75bb" diff --git a/setuptools/command/alias.py b/setuptools/command/alias.py index 40c00b55..52384e1a 100755 --- a/setuptools/command/alias.py +++ b/setuptools/command/alias.py @@ -9,7 +9,7 @@ def shquote(arg): """Quote an argument for later parsing by shlex.split()""" for c in '"', "'", "\\", "#": if c in arg: return repr(arg) - if arg.split()!=[arg]: + if arg.split() != [arg]: return repr(arg) return arg @@ -33,7 +33,7 @@ class alias(option_base): def finalize_options(self): option_base.finalize_options(self) - if self.remove and len(self.args)!=1: + if self.remove and len(self.args) != 1: raise DistutilsOptionError( "Must specify exactly one argument (the alias name) when " "using --remove" @@ -43,10 +43,10 @@ class alias(option_base): aliases = self.distribution.get_option_dict('aliases') if not self.args: - print "Command Aliases" - print "---------------" + print("Command Aliases") + print("---------------") for alias in aliases: - print "setup.py alias", format_alias(alias, aliases) + print("setup.py alias", format_alias(alias, aliases)) return elif len(self.args)==1: @@ -54,10 +54,10 @@ class alias(option_base): if self.remove: command = None elif alias in aliases: - print "setup.py alias", format_alias(alias, aliases) + print("setup.py alias", format_alias(alias, aliases)) return else: - print "No alias definition found for %r" % alias + print("No alias definition found for %r" % alias) return else: alias = self.args[0] diff --git a/setuptools/command/bdist_egg.py b/setuptools/command/bdist_egg.py index 1ba0499e..c5776158 100644 --- a/setuptools/command/bdist_egg.py +++ b/setuptools/command/bdist_egg.py @@ -21,6 +21,7 @@ from distutils.errors import DistutilsSetupError from pkg_resources import get_build_platform, Distribution, ensure_directory from pkg_resources import EntryPoint from types import CodeType +from setuptools.compat import basestring, next from setuptools.extension import Library def strip_module(filename): @@ -383,7 +384,7 @@ NATIVE_EXTENSIONS = dict.fromkeys('.dll .so .dylib .pyd'.split()) def walk_egg(egg_dir): """Walk an unpacked egg's contents, skipping the metadata directory""" walker = os.walk(egg_dir) - base,dirs,files = walker.next() + base,dirs,files = next(walker) if 'EGG-INFO' in dirs: dirs.remove('EGG-INFO') yield base,dirs,files @@ -411,7 +412,7 @@ def write_safety_flag(egg_dir, safe): for flag,fn in safety_flags.items(): fn = os.path.join(egg_dir, fn) if os.path.exists(fn): - if safe is None or bool(safe)!=flag: + if safe is None or bool(safe) != flag: os.unlink(fn) elif safe is not None and bool(safe)==flag: f=open(fn,'wt'); f.write('\n'); f.close() diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py index 60b3e011..6ce19fa4 100755 --- a/setuptools/command/easy_install.py +++ b/setuptools/command/easy_install.py @@ -1,5 +1,6 @@ #!python -"""\ + +""" Easy Install ------------ @@ -10,6 +11,7 @@ file, or visit the `EasyInstall home page`__. __ https://pythonhosted.org/setuptools/easy_install.html """ + import sys import os import zipimport @@ -20,11 +22,16 @@ import re import stat import random import platform +import textwrap +import warnings +import site +import struct from glob import glob +from distutils import log, dir_util + import pkg_resources from setuptools import Command, _dont_write_bytecode from setuptools.sandbox import run_setup -from distutils import log, dir_util try: # Python 2.7 or >=3.2 from sysconfig import get_config_vars, get_path @@ -49,36 +56,38 @@ from setuptools.archive_util import unpack_archive from setuptools.package_index import PackageIndex from setuptools.package_index import URL_SCHEME from setuptools.command import bdist_egg, egg_info -from pkg_resources import yield_lines, normalize_path, resource_string, \ - ensure_directory, get_distribution, find_distributions, \ - Environment, Requirement, Distribution, \ - PathMetadata, EggMetadata, WorkingSet, \ - DistributionNotFound, VersionConflict, \ - DEVELOP_DIST - -sys_executable = os.path.normpath(sys.executable) +from setuptools.compat import (iteritems, maxsize, xrange, basestring, unicode, + reraise) +from pkg_resources import ( + yield_lines, normalize_path, resource_string, ensure_directory, + get_distribution, find_distributions, Environment, Requirement, + Distribution, PathMetadata, EggMetadata, WorkingSet, DistributionNotFound, + VersionConflict, DEVELOP_DIST, +) + +if '__VENV_LAUNCHER__' in os.environ: + sys_executable = os.environ['__VENV_LAUNCHER__'] +else: + sys_executable = os.path.normpath(sys.executable) __all__ = [ 'samefile', 'easy_install', 'PthDistributions', 'extract_wininst_cfg', 'main', 'get_exe_prefixes', ] -import site HAS_USER_SITE = not sys.version < "2.6" and site.ENABLE_USER_SITE -import struct def is_64bit(): return struct.calcsize("P") == 8 -def samefile(p1,p2): - if hasattr(os.path,'samefile') and ( - os.path.exists(p1) and os.path.exists(p2) - ): - return os.path.samefile(p1,p2) - return ( - os.path.normpath(os.path.normcase(p1)) == - os.path.normpath(os.path.normcase(p2)) - ) +def samefile(p1, p2): + both_exist = os.path.exists(p1) and os.path.exists(p2) + use_samefile = hasattr(os.path, 'samefile') and both_exist + if use_samefile: + return os.path.samefile(p1, p2) + norm_p1 = os.path.normpath(os.path.normcase(p1)) + norm_p2 = os.path.normpath(os.path.normcase(p2)) + return norm_p1 == norm_p2 if sys.version_info <= (3,): def _to_ascii(s): @@ -115,38 +124,35 @@ class easy_install(Command): ("always-copy", "a", "Copy all needed packages to install dir"), ("index-url=", "i", "base URL of Python Package Index"), ("find-links=", "f", "additional URL(s) to search for packages"), - ("delete-conflicting", "D", "no longer needed; don't use this"), - ("ignore-conflicts-at-my-risk", None, - "no longer needed; don't use this"), ("build-directory=", "b", "download/extract/build in DIR; keep the results"), ('optimize=', 'O', - "also compile with optimization: -O1 for \"python -O\", " - "-O2 for \"python -OO\", and -O0 to disable [default: -O0]"), + "also compile with optimization: -O1 for \"python -O\", " + "-O2 for \"python -OO\", and -O0 to disable [default: -O0]"), ('record=', None, - "filename in which to record list of installed files"), + "filename in which to record list of installed files"), ('always-unzip', 'Z', "don't install as a zipfile, no matter what"), ('site-dirs=','S',"list of directories where .pth files work"), ('editable', 'e', "Install specified packages in editable form"), ('no-deps', 'N', "don't install dependencies"), ('allow-hosts=', 'H', "pattern(s) that hostnames must match"), - ('local-snapshots-ok', 'l', "allow building eggs from local checkouts"), + ('local-snapshots-ok', 'l', + "allow building eggs from local checkouts"), ('version', None, "print version information and exit"), ('no-find-links', None, - "Don't load find-links defined in packages being installed") + "Don't load find-links defined in packages being installed") ] boolean_options = [ 'zip-ok', 'multi-version', 'exclude-scripts', 'upgrade', 'always-copy', - 'delete-conflicting', 'ignore-conflicts-at-my-risk', 'editable', + 'editable', 'no-deps', 'local-snapshots-ok', 'version' ] if HAS_USER_SITE: - user_options.append(('user', None, - "install in user site-package '%s'" % site.USER_SITE)) + help_msg = "install in user site-package '%s'" % site.USER_SITE + user_options.append(('user', None, help_msg)) boolean_options.append('user') - negative_opt = {'always-unzip': 'zip-ok'} create_index = PackageIndex @@ -187,8 +193,6 @@ class easy_install(Command): # Options not specifiable via command line self.package_index = None self.pth_file = self.always_copy_from = None - self.delete_conflicting = None - self.ignore_conflicts_at_my_risk = None self.site_dirs = None self.installed_projects = {} self.sitepy_installed = False @@ -215,25 +219,26 @@ class easy_install(Command): def finalize_options(self): if self.version: - print 'setuptools %s' % get_distribution('setuptools').version + print('setuptools %s' % get_distribution('setuptools').version) sys.exit() 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(), - '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, - # Only python 3.2+ has abiflags - 'abiflags': getattr(sys, 'abiflags', ''), - } + self.config_vars = { + 'dist_name': self.distribution.get_name(), + 'dist_version': self.distribution.get_version(), + 'dist_fullname': self.distribution.get_fullname(), + 'py_version': py_version, + 'py_version_short': 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, + # Only python 3.2+ has abiflags + 'abiflags': getattr(sys, 'abiflags', ''), + } if HAS_USER_SITE: self.config_vars['userbase'] = self.install_userbase @@ -280,6 +285,8 @@ class easy_install(Command): self.script_dir = self.install_scripts # default --record from the install command self.set_undefined_options('install', ('record', 'record')) + # Should this be moved to the if statement below? It's not used + # elsewhere normpath = map(normalize_path, sys.path) self.all_site_dirs = get_site_dirs() if self.site_dirs is not None: @@ -329,11 +336,6 @@ class easy_install(Command): except ValueError: raise DistutilsOptionError("--optimize must be 0, 1, or 2") - if self.delete_conflicting and self.ignore_conflicts_at_my_risk: - raise DistutilsOptionError( - "Can't use both --delete-conflicting and " - "--ignore-conflicts-at-my-risk at the same time" - ) if self.editable and not self.build_directory: raise DistutilsArgError( "Must specify a build directory (-b) when using --editable" @@ -344,7 +346,6 @@ class easy_install(Command): self.outputs = [] - def _expand_attrs(self, attrs): for attr in attrs: val = getattr(self, attr) @@ -395,16 +396,11 @@ class easy_install(Command): try: pid = os.getpid() except: - pid = random.randint(0,sys.maxint) + pid = random.randint(0, maxsize) return os.path.join(self.install_dir, "test-easy-install-%s" % pid) def warn_deprecated_options(self): - if self.delete_conflicting or self.ignore_conflicts_at_my_risk: - log.warn( - "Note: The -D, --delete-conflicting and" - " --ignore-conflicts-at-my-risk no longer have any purpose" - " and should not be used." - ) + pass def check_site_dir(self): """Verify that self.install_dir is .pth-capable dir, if needed""" @@ -440,7 +436,7 @@ class easy_install(Command): self.pth_file = None PYTHONPATH = os.environ.get('PYTHONPATH','').split(os.pathsep) - if instdir not in map(normalize_path, filter(None,PYTHONPATH)): + if instdir not in map(normalize_path, [_f for _f in PYTHONPATH if _f]): # only PYTHONPATH dirs need a site.py, so pretend it's there self.sitepy_installed = True elif self.multi_version and not os.path.exists(pth_file): @@ -449,7 +445,7 @@ class easy_install(Command): self.install_dir = instdir def cant_write_to_target(self): - msg = """can't create or remove files in install directory + template = """can't create or remove files in install directory The following error occurred while trying to add or remove files in the installation directory: @@ -460,7 +456,8 @@ The installation directory you specified (via --install-dir, --prefix, or the distutils default setting) was: %s -""" % (sys.exc_info()[1], self.install_dir,) +""" + msg = template % (sys.exc_info()[1], self.install_dir,) if not os.path.exists(self.install_dir): msg += """ @@ -486,9 +483,6 @@ Please make the appropriate changes for your system and try again. """ raise DistutilsError(msg) - - - def check_pth_processing(self): """Empirically verify whether .pth files are supported in inst. dir""" instdir = self.install_dir @@ -507,7 +501,8 @@ Please make the appropriate changes for your system and try again. else: try: f.write("import os; f = open(%r, 'w'); f.write('OK'); f.close()\n" % (ok_file,)) - f.close(); f=None + f.close() + f=None executable = sys.executable if os.name=='nt': dirname,basename = os.path.split(executable) @@ -526,9 +521,12 @@ Please make the appropriate changes for your system and try again. ) return True finally: - if f: f.close() - if os.path.exists(ok_file): os.unlink(ok_file) - if os.path.exists(pth_file): os.unlink(pth_file) + if f: + f.close() + if os.path.exists(ok_file): + os.unlink(ok_file) + if os.path.exists(pth_file): + os.unlink(pth_file) if not self.multi_version: log.warn("TEST FAILED: %s does NOT support .pth files", instdir) return False @@ -573,11 +571,6 @@ Please make the appropriate changes for your system and try again. (spec.key, self.build_directory) ) - - - - - def easy_install(self, spec, deps=False): tmpdir = tempfile.mkdtemp(prefix="easy_install-") download = None @@ -647,7 +640,7 @@ Please make the appropriate changes for your system and try again. for dist in dists: self.process_distribution(spec, dist, deps) else: - dists = [self.check_conflicts(self.egg_distribution(download))] + dists = [self.egg_distribution(download)] self.process_distribution(spec, dists[0], deps, "Using") if spec is not None: @@ -655,8 +648,6 @@ Please make the appropriate changes for your system and try again. if dist in spec: return dist - - 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! @@ -666,9 +657,6 @@ Please make the appropriate changes for your system and try again. if getattr(self, attrname) is None: setattr(self, attrname, scheme[key]) - - - def process_distribution(self, requirement, dist, deps=True, *info): self.update_pth(dist) self.package_index.add(dist) @@ -677,7 +665,7 @@ Please make the appropriate changes for your system and try again. self.installed_projects[dist.key] = dist log.info(self.installation_report(requirement, dist, *info)) if (dist.has_metadata('dependency_links.txt') and - not self.no_find_links): + not self.no_find_links): self.package_index.add_find_links( dist.get_metadata_lines('dependency_links.txt') ) @@ -698,11 +686,13 @@ Please make the appropriate changes for your system and try again. distros = WorkingSet([]).resolve( [requirement], self.local_index, self.easy_install ) - except DistributionNotFound, e: + except DistributionNotFound: + e = sys.exc_info()[1] raise DistutilsError( "Could not find required distribution %s" % e.args ) - except VersionConflict, e: + except VersionConflict: + e = sys.exc_info()[1] raise DistutilsError( "Installed distribution %s conflicts with requirement %s" % e.args @@ -726,10 +716,8 @@ Please make the appropriate changes for your system and try again. def maybe_move(self, spec, dist_filename, setup_base): dst = os.path.join(self.build_directory, spec.key) if os.path.exists(dst): - log.warn( - "%r already exists in %s; build directory %s will not be kept", - spec.key, self.build_directory, setup_base - ) + msg = "%r already exists in %s; build directory %s will not be kept" + log.warn(msg, spec.key, self.build_directory, setup_base) return setup_base if os.path.isdir(dist_filename): setup_base = dist_filename @@ -742,7 +730,8 @@ Please make the appropriate changes for your system and try again. if os.path.isdir(dist_filename): # if the only thing there is a directory, move it instead setup_base = dist_filename - ensure_directory(dst); shutil.move(setup_base, dst) + ensure_directory(dst) + shutil.move(setup_base, dst) return dst def install_wrapper_scripts(self, dist): @@ -750,8 +739,6 @@ Please make the appropriate changes for your system and try again. for args in get_script_args(dist): self.write_script(*args) - - def install_script(self, dist, script_name, script_text, dev_path=None): """Generate a legacy script wrapper and install it""" spec = str(dist.as_requirement()) @@ -790,13 +777,12 @@ Please make the appropriate changes for your system and try again. mask = current_umask() if not self.dry_run: ensure_directory(target) + if os.path.exists(target): + os.unlink(target) f = open(target,"w"+mode) f.write(contents) f.close() - chmod(target, 0777-mask) - - - + chmod(target, 0x1FF-mask) # 0777 def install_eggs(self, spec, dist_filename, tmpdir): # .egg dirs or files are already built, so just return them @@ -813,8 +799,7 @@ Please make the appropriate changes for your system and try again. setup_base = os.path.abspath(dist_filename) if (setup_base.startswith(tmpdir) # something we downloaded - and self.build_directory and spec is not None - ): + and self.build_directory and spec is not None): setup_base = self.maybe_move(spec, dist_filename, setup_base) # Find the setup.py file @@ -853,7 +838,6 @@ Please make the appropriate changes for your system and try again. ensure_directory(destination) dist = self.egg_distribution(egg_path) - self.check_conflicts(dist) if not samefile(egg_path, destination): if os.path.isdir(destination) and not os.path.islink(destination): dir_util.remove_tree(destination, dry_run=self.dry_run) @@ -888,18 +872,19 @@ Please make the appropriate changes for your system and try again. "%s is not a valid distutils Windows .exe" % dist_filename ) # Create a dummy distribution object until we build the real distro - dist = Distribution(None, + dist = Distribution( + None, project_name=cfg.get('metadata','name'), - version=cfg.get('metadata','version'), platform=get_platform() + version=cfg.get('metadata','version'), platform=get_platform(), ) # Convert the .exe to an unpacked egg egg_path = dist.location = os.path.join(tmpdir, dist.egg_name()+'.egg') - egg_tmp = egg_path+'.tmp' - egg_info = os.path.join(egg_tmp, 'EGG-INFO') - pkg_inf = os.path.join(egg_info, 'PKG-INFO') + egg_tmp = egg_path + '.tmp' + _egg_info = os.path.join(egg_tmp, 'EGG-INFO') + pkg_inf = os.path.join(_egg_info, 'PKG-INFO') ensure_directory(pkg_inf) # make sure EGG-INFO dir exists - dist._provider = PathMetadata(egg_tmp, egg_info) # XXX + dist._provider = PathMetadata(egg_tmp, _egg_info) # XXX self.exe_to_egg(dist_filename, egg_tmp) # Write EGG-INFO/PKG-INFO @@ -907,10 +892,10 @@ Please make the appropriate changes for your system and try again. f = open(pkg_inf,'w') f.write('Metadata-Version: 1.0\n') for k,v in cfg.items('metadata'): - if k!='target_version': + if k != 'target_version': f.write('%s: %s\n' % (k.replace('_','-').title(), v)) f.close() - script_dir = os.path.join(egg_info,'scripts') + script_dir = os.path.join(_egg_info,'scripts') self.delete_blockers( # delete entry-point scripts to avoid duping [os.path.join(script_dir,args[0]) for args in get_script_args(dist)] ) @@ -956,7 +941,8 @@ Please make the appropriate changes for your system and try again. resource = parts[-1] parts[-1] = bdist_egg.strip_module(parts[-1])+'.py' pyfile = os.path.join(egg_tmp, *parts) - to_compile.append(pyfile); stubs.append(pyfile) + to_compile.append(pyfile) + stubs.append(pyfile) bdist_egg.write_stub(resource, pyfile) self.byte_compile(to_compile) # compile .py's bdist_egg.write_safety_flag(os.path.join(egg_tmp,'EGG-INFO'), @@ -970,82 +956,6 @@ Please make the appropriate changes for your system and try again. f.write('\n'.join(locals()[name])+'\n') f.close() - def check_conflicts(self, dist): - """Verify that there are no conflicting "old-style" packages""" - - return dist # XXX temporarily disable until new strategy is stable - from imp import find_module, get_suffixes - from glob import glob - - blockers = [] - names = dict.fromkeys(dist._get_metadata('top_level.txt')) # XXX private attr - - exts = {'.pyc':1, '.pyo':1} # get_suffixes() might leave one out - for ext,mode,typ in get_suffixes(): - exts[ext] = 1 - - for path,files in expand_paths([self.install_dir]+self.all_site_dirs): - for filename in files: - base,ext = os.path.splitext(filename) - if base in names: - if not ext: - # no extension, check for package - try: - f, filename, descr = find_module(base, [path]) - except ImportError: - continue - else: - if f: f.close() - if filename not in blockers: - blockers.append(filename) - elif ext in exts and base!='site': # XXX ugh - blockers.append(os.path.join(path,filename)) - if blockers: - self.found_conflicts(dist, blockers) - - return dist - - def found_conflicts(self, dist, blockers): - if self.delete_conflicting: - log.warn("Attempting to delete conflicting packages:") - return self.delete_blockers(blockers) - - msg = """\ -------------------------------------------------------------------------- -CONFLICT WARNING: - -The following modules or packages have the same names as modules or -packages being installed, and will be *before* the installed packages in -Python's search path. You MUST remove all of the relevant files and -directories before you will be able to use the package(s) you are -installing: - - %s - -""" % '\n '.join(blockers) - - if self.ignore_conflicts_at_my_risk: - msg += """\ -(Note: you can run EasyInstall on '%s' with the ---delete-conflicting option to attempt deletion of the above files -and/or directories.) -""" % dist.project_name - else: - msg += """\ -Note: you can attempt this installation again with EasyInstall, and use -either the --delete-conflicting (-D) option or the ---ignore-conflicts-at-my-risk option, to either delete the above files -and directories, or to ignore the conflicts, respectively. Note that if -you ignore the conflicts, the installed package(s) may not work. -""" - msg += """\ -------------------------------------------------------------------------- -""" - sys.stderr.write(msg) - sys.stderr.flush() - if not self.ignore_conflicts_at_my_risk: - raise DistutilsError("Installation aborted due to conflicts") - def installation_report(self, req, dist, what="Installed"): """Helpful installation message for display to package users""" msg = "\n%(what)s %(eggloc)s%(extras)s" @@ -1104,7 +1014,8 @@ See the setuptools documentation for the "develop" command for more info. ) try: run_setup(setup_script, args) - except SystemExit, v: + except SystemExit: + v = sys.exc_info()[1] raise DistutilsError("Setup script exited with %s" % (v.args[0],)) def build_and_install(self, setup_script, setup_base): @@ -1146,7 +1057,7 @@ See the setuptools documentation for the "develop" command for more info. 'site_dirs', 'allow_hosts', ) fetch_options = {} - for key, val in ei_opts.iteritems(): + for key, val in ei_opts.items(): if key not in fetch_directives: continue fetch_options[key.replace('_', '-')] = val[1] # create a settings dictionary suitable for `edit_config` @@ -1154,8 +1065,7 @@ See the setuptools documentation for the "develop" command for more info. cfg_filename = os.path.join(base, 'setup.cfg') setopt.edit_config(cfg_filename, settings) - - def update_pth(self,dist): + def update_pth(self, dist): if self.pth_file is None: return @@ -1197,9 +1107,10 @@ See the setuptools documentation for the "develop" command for more info. return dst # only unpack-and-compile skips files for dry run def unpack_and_compile(self, egg_path, destination): - to_compile = []; to_chmod = [] + to_compile = [] + to_chmod = [] - def pf(src,dst): + def pf(src, dst): if dst.endswith('.py') and not src.startswith('EGG-INFO/'): to_compile.append(dst) elif dst.endswith('.dll') or dst.endswith('.so'): @@ -1211,7 +1122,7 @@ See the setuptools documentation for the "develop" command for more info. self.byte_compile(to_compile) if not self.dry_run: for f in to_chmod: - mode = ((os.stat(f)[stat.ST_MODE]) | 0555) & 07755 + mode = ((os.stat(f)[stat.ST_MODE]) | 0x16D) & 0xFED # 0555, 07755 chmod(f, mode) def byte_compile(self, to_compile): @@ -1233,16 +1144,8 @@ See the setuptools documentation for the "develop" command for more info. finally: log.set_verbosity(self.verbose) # restore original verbosity - - - - - - - - def no_default_version_msg(self): - return """bad install directory or PYTHONPATH + template = """bad install directory or PYTHONPATH You are attempting to install a package to a directory that is not on PYTHONPATH and which Python does not read ".pth" files from. The @@ -1269,18 +1172,8 @@ Here are some of your options for correcting the problem: https://pythonhosted.org/setuptools/easy_install.html#custom-installation-locations -Please make the appropriate changes for your system and try again.""" % ( - self.install_dir, os.environ.get('PYTHONPATH','') - ) - - - - - - - - - +Please make the appropriate changes for your system and try again.""" + return template % (self.install_dir, os.environ.get('PYTHONPATH','')) def install_site_py(self): """Make sure there's a site.py in the target dir, if needed""" @@ -1318,35 +1211,26 @@ Please make the appropriate changes for your system and try again.""" % ( self.sitepy_installed = True - - - 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(): + for name, path in iteritems(self.config_vars): if path.startswith(home) and not os.path.isdir(path): self.debug_print("os.makedirs('%s', 0700)" % path) - os.makedirs(path, 0700) - - - - - - + os.makedirs(path, 0x1C0) # 0700 INSTALL_SCHEMES = dict( posix = dict( install_dir = '$base/lib/python$py_version_short/site-packages', - script_dir = '$base/bin', + script_dir = '$base/bin', ), ) DEFAULT_SCHEME = dict( install_dir = '$base/Lib/site-packages', - script_dir = '$base/Scripts', + script_dir = '$base/Scripts', ) def _expand(self, *attrs): @@ -1370,17 +1254,10 @@ Please make the appropriate changes for your system and try again.""" % ( val = os.path.expanduser(val) setattr(self, attr, val) - - - - - - - - def get_site_dirs(): # return a list of 'site' dirs - sitedirs = filter(None,os.environ.get('PYTHONPATH','').split(os.pathsep)) + sitedirs = [_f for _f in os.environ.get('PYTHONPATH', + '').split(os.pathsep) if _f] prefixes = [sys.prefix] if sys.exec_prefix != sys.prefix: prefixes.append(sys.exec_prefix) @@ -1417,7 +1294,7 @@ def get_site_dirs(): if HAS_USER_SITE: sitedirs.append(site.USER_SITE) - sitedirs = map(normalize_path, sitedirs) + sitedirs = list(map(normalize_path, sitedirs)) return sitedirs @@ -1479,7 +1356,8 @@ def extract_wininst_cfg(dist_filename): return None f.seek(prepended-12) - import struct, StringIO, ConfigParser + from setuptools.compat import StringIO, ConfigParser + import struct tag, cfglen, bmlen = struct.unpack("<iii",f.read(12)) if tag not in (0x1234567A, 0x1234567B): return None # not a valid tag @@ -1499,7 +1377,7 @@ def extract_wininst_cfg(dist_filename): # unicode for the RawConfigParser, so decode it. Is this the # right encoding? config = config.decode('ascii') - cfg.readfp(StringIO.StringIO(config)) + cfg.readfp(StringIO(config)) except ConfigParser.Error: return None if not cfg.has_section('metadata') or not cfg.has_section('Setup'): @@ -1510,12 +1388,6 @@ def extract_wininst_cfg(dist_filename): f.close() - - - - - - def get_exe_prefixes(exe_filename): """Get exe->egg path translations for a given .exe file""" @@ -1534,7 +1406,7 @@ def get_exe_prefixes(exe_filename): if parts[1].endswith('.egg-info'): prefixes.insert(0,('/'.join(parts[:2]), 'EGG-INFO/')) break - if len(parts)!=2 or not name.endswith('.pth'): + if len(parts) != 2 or not name.endswith('.pth'): continue if name.endswith('-nspkg.pth'): continue @@ -1549,7 +1421,8 @@ def get_exe_prefixes(exe_filename): finally: z.close() prefixes = [(x.lower(),y) for x, y in prefixes] - prefixes.sort(); prefixes.reverse() + prefixes.sort() + prefixes.reverse() return prefixes @@ -1567,11 +1440,13 @@ class PthDistributions(Environment): dirty = False def __init__(self, filename, sitedirs=()): - self.filename = filename; self.sitedirs=map(normalize_path, sitedirs) + self.filename = filename + self.sitedirs = list(map(normalize_path, sitedirs)) self.basedir = normalize_path(os.path.dirname(self.filename)) - self._load(); Environment.__init__(self, [], None, None) + self._load() + Environment.__init__(self, [], None, None) for path in yield_lines(self.paths): - map(self.add, find_distributions(path, True)) + list(map(self.add, find_distributions(path, True))) def _load(self): self.paths = [] @@ -1624,7 +1499,8 @@ class PthDistributions(Environment): if os.path.islink(self.filename): os.unlink(self.filename) f = open(self.filename,'wt') - f.write(data); f.close() + f.write(data) + f.close() elif os.path.exists(self.filename): log.debug("Deleting empty %s", self.filename) @@ -1632,22 +1508,22 @@ class PthDistributions(Environment): self.dirty = False - def add(self,dist): + def add(self, dist): """Add `dist` to the distribution map""" if (dist.location not in self.paths and ( dist.location not in self.sitedirs or - dist.location == os.getcwd() #account for '.' being in PYTHONPATH + dist.location == os.getcwd() # account for '.' being in PYTHONPATH )): self.paths.append(dist.location) self.dirty = True - Environment.add(self,dist) + Environment.add(self, dist) - def remove(self,dist): + def remove(self, dist): """Remove `dist` from the distribution map""" while dist.location in self.paths: - self.paths.remove(dist.location); self.dirty = True - Environment.remove(self,dist) - + self.paths.remove(dist.location) + self.dirty = True + Environment.remove(self, dist) def make_relative(self,path): npath, last = os.path.split(normalize_path(path)) @@ -1699,8 +1575,8 @@ def auto_chmod(func, arg, exc): if func is os.remove and os.name=='nt': chmod(arg, stat.S_IWRITE) return func(arg) - exc = sys.exc_info() - raise exc[0], (exc[1][0], exc[1][1] + (" %s %s" % (func,arg))) + et, ev, _ = sys.exc_info() + reraise(et, (ev[0], ev[1] + (" %s %s" % (func,arg)))) def uncache_zipdir(path): """Ensure that the importer caches dont have stale info for `path`""" @@ -1769,14 +1645,6 @@ def nt_quote_arg(arg): return ''.join(result) - - - - - - - - def is_python_script(script_text, filename): """Is this text, as a whole, a Python script? (as opposed to shell/bat/etc. """ @@ -1800,7 +1668,8 @@ def chmod(path, mode): log.debug("changing mode of %s to %o", path, mode) try: _chmod(path, mode) - except os.error, e: + except os.error: + e = sys.exc_info()[1] log.debug("chmod failed: %s", e) def fix_jython_executable(executable, options): @@ -1814,66 +1683,140 @@ def fix_jython_executable(executable, options): # shebang line interpreter) if options: # Can't apply the workaround, leave it broken - log.warn("WARNING: Unable to adapt shebang line for Jython," - " the following script is NOT executable\n" - " see http://bugs.jython.org/issue1112 for" - " more information.") + log.warn( + "WARNING: Unable to adapt shebang line for Jython," + " the following script is NOT executable\n" + " see http://bugs.jython.org/issue1112 for" + " more information.") else: return '/usr/bin/env %s' % executable return executable -def get_script_args(dist, executable=sys_executable, wininst=False): - """Yield write_script() argument tuples for a distribution's entrypoints""" - spec = str(dist.as_requirement()) - header = get_script_header("", executable, wininst) - for group in 'console_scripts', 'gui_scripts': - for name, ep in dist.get_entry_map(group).items(): - script_text = ( - "# EASY-INSTALL-ENTRY-SCRIPT: %(spec)r,%(group)r,%(name)r\n" - "__requires__ = %(spec)r\n" - "import sys\n" - "from pkg_resources import load_entry_point\n" - "\n" - "if __name__ == '__main__':" - "\n" - " sys.exit(\n" - " load_entry_point(%(spec)r, %(group)r, %(name)r)()\n" - " )\n" - ) % locals() - if sys.platform=='win32' or wininst: - # On Windows/wininst, add a .py extension and an .exe launcher - if group=='gui_scripts': - launcher_type = 'gui' - ext = '-script.pyw' - old = ['.pyw'] - new_header = re.sub('(?i)python.exe','pythonw.exe',header) - else: - launcher_type = 'cli' - ext = '-script.py' - old = ['.py','.pyc','.pyo'] - new_header = re.sub('(?i)pythonw.exe','python.exe',header) - if os.path.exists(new_header[2:-1].strip('"')) or sys.platform!='win32': - hdr = new_header - else: - hdr = header - yield (name+ext, hdr+script_text, 't', [name+x for x in old]) - yield ( - name+'.exe', get_win_launcher(launcher_type), - 'b' # write in binary mode - ) - if not is_64bit(): - # install a manifest for the launcher to prevent Windows - # from detecting it as an installer (which it will for - # launchers like easy_install.exe). Consider only - # adding a manifest for launchers detected as installers. - # See Distribute #143 for details. - m_name = name + '.exe.manifest' - yield (m_name, load_launcher_manifest(name), 't') - else: - # On other platforms, we assume the right thing to do is to - # just write the stub with no extension. - yield (name, header+script_text) +class ScriptWriter(object): + """ + Encapsulates behavior around writing entry point scripts for console and + gui apps. + """ + + template = textwrap.dedent(""" + # EASY-INSTALL-ENTRY-SCRIPT: %(spec)r,%(group)r,%(name)r + __requires__ = %(spec)r + import sys + from pkg_resources import load_entry_point + + if __name__ == '__main__': + sys.exit( + load_entry_point(%(spec)r, %(group)r, %(name)r)() + ) + """).lstrip() + + @classmethod + def get_script_args(cls, dist, executable=sys_executable, wininst=False): + """ + Yield write_script() argument tuples for a distribution's entrypoints + """ + gen_class = cls.get_writer(wininst) + spec = str(dist.as_requirement()) + header = get_script_header("", executable, wininst) + for type_ in 'console', 'gui': + group = type_ + '_scripts' + for name, ep in dist.get_entry_map(group).items(): + script_text = gen_class.template % locals() + for res in gen_class._get_script_args(type_, name, header, + script_text): + yield res + + @classmethod + def get_writer(cls, force_windows): + if force_windows or sys.platform=='win32': + return WindowsScriptWriter.get_writer() + return cls + + @classmethod + def _get_script_args(cls, type_, name, header, script_text): + # Simply write the stub with no extension. + yield (name, header+script_text) + + +class WindowsScriptWriter(ScriptWriter): + @classmethod + def get_writer(cls): + """ + Get a script writer suitable for Windows + """ + writer_lookup = dict( + executable=WindowsExecutableLauncherWriter, + natural=cls, + ) + # for compatibility, use the executable launcher by default + launcher = os.environ.get('SETUPTOOLS_LAUNCHER', 'executable') + return writer_lookup[launcher] + + @classmethod + def _get_script_args(cls, type_, name, header, script_text): + "For Windows, add a .py extension" + ext = dict(console='.pya', gui='.pyw')[type_] + if ext not in os.environ['PATHEXT'].lower().split(';'): + warnings.warn("%s not listed in PATHEXT; scripts will not be " + "recognized as executables." % ext, UserWarning) + old = ['.pya', '.py', '-script.py', '.pyc', '.pyo', '.pyw', '.exe'] + old.remove(ext) + header = cls._adjust_header(type_, header) + blockers = [name+x for x in old] + yield name+ext, header+script_text, 't', blockers + + @staticmethod + def _adjust_header(type_, orig_header): + """ + Make sure 'pythonw' is used for gui and and 'python' is used for + console (regardless of what sys.executable is). + """ + pattern = 'pythonw.exe' + repl = 'python.exe' + if type_ == 'gui': + pattern, repl = repl, pattern + pattern_ob = re.compile(re.escape(pattern), re.IGNORECASE) + new_header = pattern_ob.sub(string=orig_header, repl=repl) + clean_header = new_header[2:-1].strip('"') + if sys.platform == 'win32' and not os.path.exists(clean_header): + # the adjusted version doesn't exist, so return the original + return orig_header + return new_header + + +class WindowsExecutableLauncherWriter(WindowsScriptWriter): + @classmethod + def _get_script_args(cls, type_, name, header, script_text): + """ + For Windows, add a .py extension and an .exe launcher + """ + if type_=='gui': + launcher_type = 'gui' + ext = '-script.pyw' + old = ['.pyw'] + else: + launcher_type = 'cli' + ext = '-script.py' + old = ['.py','.pyc','.pyo'] + hdr = cls._adjust_header(type_, header) + blockers = [name+x for x in old] + yield (name+ext, hdr+script_text, 't', blockers) + yield ( + name+'.exe', get_win_launcher(launcher_type), + 'b' # write in binary mode + ) + if not is_64bit(): + # install a manifest for the launcher to prevent Windows + # from detecting it as an installer (which it will for + # launchers like easy_install.exe). Consider only + # adding a manifest for launchers detected as installers. + # See Distribute #143 for details. + m_name = name + '.exe.manifest' + yield (m_name, load_launcher_manifest(name), 't') + +# for backward-compatibility +get_script_args = ScriptWriter.get_script_args def get_win_launcher(type): """ @@ -1914,7 +1857,7 @@ def rmtree(path, ignore_errors=False, onerror=auto_chmod): names = [] try: names = os.listdir(path) - except os.error, err: + except os.error: onerror(os.listdir, path, sys.exc_info()) for name in names: fullname = os.path.join(path, name) @@ -1927,7 +1870,7 @@ def rmtree(path, ignore_errors=False, onerror=auto_chmod): else: try: os.remove(fullname) - except os.error, err: + except os.error: onerror(os.remove, fullname, sys.exc_info()) try: os.rmdir(path) @@ -1935,14 +1878,17 @@ def rmtree(path, ignore_errors=False, onerror=auto_chmod): onerror(os.rmdir, path, sys.exc_info()) def current_umask(): - tmp = os.umask(022) + tmp = os.umask(0x12) # 022 os.umask(tmp) return tmp def bootstrap(): # This function is called when setuptools*.egg is run using /bin/sh - import setuptools; argv0 = os.path.dirname(setuptools.__path__[0]) - sys.argv[0] = argv0; sys.argv.append(argv0); main() + import setuptools + argv0 = os.path.dirname(setuptools.__path__[0]) + sys.argv[0] = argv0 + sys.argv.append(argv0) + main() def main(argv=None, **kw): from setuptools import setup @@ -1954,9 +1900,10 @@ usage: %(script)s [options] requirement_or_url ... or: %(script)s --help """ - def gen_usage (script_name): - script = os.path.basename(script_name) - return USAGE % vars() + def gen_usage(script_name): + return USAGE % dict( + script=os.path.basename(script_name), + ) def with_ei_usage(f): old_gen_usage = distutils.core.gen_usage @@ -1982,7 +1929,3 @@ usage: %(script)s [options] requirement_or_url ... distclass=DistributionWithoutHelpCommands, **kw ) ) - - - - diff --git a/setuptools/command/egg_info.py b/setuptools/command/egg_info.py index b283b28a..a0ba5305 100755 --- a/setuptools/command/egg_info.py +++ b/setuptools/command/egg_info.py @@ -8,11 +8,14 @@ from setuptools import Command from distutils.errors import * from distutils import log from setuptools.command.sdist import sdist +from setuptools.compat import basestring +from setuptools import svn_utils from distutils.util import convert_path from distutils.filelist import FileList as _FileList from pkg_resources import parse_requirements, safe_name, parse_version, \ safe_version, yield_lines, EntryPoint, iter_entry_points, to_filename -from sdist import walk_revctrl +from setuptools.command.sdist import walk_revctrl + class egg_info(Command): description = "create a distribution's .egg-info directory" @@ -51,7 +54,7 @@ class egg_info(Command): self.vtags = None def save_version_info(self, filename): - from setopt import edit_config + from setuptools.command.setopt import edit_config edit_config( filename, {'egg_info': @@ -212,42 +215,9 @@ class egg_info(Command): @staticmethod def get_svn_revision(): - revision = 0 - urlre = re.compile('url="([^"]+)"') - revre = re.compile('committed-rev="(\d+)"') - - for base,dirs,files in os.walk(os.curdir): - if '.svn' not in dirs: - dirs[:] = [] - continue # no sense walking uncontrolled subdirs - dirs.remove('.svn') - f = open(os.path.join(base,'.svn','entries')) - data = f.read() - f.close() - - if data.startswith('<?xml'): - dirurl = urlre.search(data).group(1) # get repository URL - localrev = max([int(m.group(1)) for m in revre.finditer(data)]+[0]) - else: - try: svnver = int(data.splitlines()[0]) - except: svnver=-1 - if svnver<8: - log.warn("unrecognized .svn/entries format; skipping %s", base) - dirs[:] = [] - continue + return str(svn_utils.SvnInfo.load(os.curdir).get_revision()) - data = map(str.splitlines,data.split('\n\x0c\n')) - del data[0][0] # get rid of the '8' or '9' or '10' - dirurl = data[0][3] - localrev = max([int(d[9]) for d in data if len(d)>9 and d[9]]+[0]) - if base==os.curdir: - base_url = dirurl+'/' # save the root url - elif not dirurl.startswith(base_url): - dirs[:] = [] - continue # not part of the same svn tree, skip it - revision = max(revision, localrev) - return str(revision or get_pkg_info_revision()) @@ -416,7 +386,8 @@ def write_pkg_info(cmd, basename, filename): metadata.name, metadata.version = oldname, oldver safe = getattr(cmd.distribution,'zip_safe',None) - import bdist_egg; bdist_egg.write_safety_flag(cmd.egg_info, safe) + from setuptools.command import bdist_egg + bdist_egg.write_safety_flag(cmd.egg_info, safe) def warn_depends_obsolete(cmd, basename, filename): if os.path.exists(filename): diff --git a/setuptools/command/install_scripts.py b/setuptools/command/install_scripts.py index 82456035..105dabca 100755 --- a/setuptools/command/install_scripts.py +++ b/setuptools/command/install_scripts.py @@ -50,5 +50,5 @@ class install_scripts(_install_scripts): f = open(target,"w"+mode) f.write(contents) f.close() - chmod(target, 0777-mask) + chmod(target, 0x1FF-mask) # 0777 diff --git a/setuptools/command/rotate.py b/setuptools/command/rotate.py index 11b6eae8..b10acfb4 100755 --- a/setuptools/command/rotate.py +++ b/setuptools/command/rotate.py @@ -1,5 +1,6 @@ import distutils, os from setuptools import Command +from setuptools.compat import basestring from distutils.util import convert_path from distutils import log from distutils.errors import * diff --git a/setuptools/command/saveopts.py b/setuptools/command/saveopts.py index 1180a440..7209be4c 100755 --- a/setuptools/command/saveopts.py +++ b/setuptools/command/saveopts.py @@ -9,10 +9,9 @@ class saveopts(option_base): def run(self): dist = self.distribution - commands = dist.command_options.keys() settings = {} - for cmd in commands: + for cmd in dist.command_options: if cmd=='saveopts': continue # don't save our own options! diff --git a/setuptools/command/sdist.py b/setuptools/command/sdist.py index f8f964b3..6249e75c 100755 --- a/setuptools/command/sdist.py +++ b/setuptools/command/sdist.py @@ -1,46 +1,16 @@ +import os +import re +import sys +from glob import glob + +import pkg_resources from distutils.command.sdist import sdist as _sdist from distutils.util import convert_path from distutils import log -from glob import glob -import os, re, sys, pkg_resources -from glob import glob +from setuptools import svn_utils READMES = ('README', 'README.rst', 'README.txt') -entities = [ - ("<","<"), (">", ">"), (""", '"'), ("'", "'"), - ("&", "&") -] - -def unescape(data): - for old,new in entities: - data = data.replace(old,new) - return data - -def re_finder(pattern, postproc=None): - def find(dirname, filename): - f = open(filename,'rU') - data = f.read() - f.close() - for match in pattern.finditer(data): - path = match.group(1) - if postproc: - path = postproc(path) - yield joinpath(dirname,path) - return find - -def joinpath(prefix,suffix): - if not prefix: - return suffix - return os.path.join(prefix,suffix) - - - - - - - - def walk_revctrl(dirname=''): """Find all files under revision control""" @@ -48,80 +18,51 @@ def walk_revctrl(dirname=''): for item in ep.load()(dirname): yield item -def _default_revctrl(dirname=''): - for path, finder in finders: - path = joinpath(dirname,path) + +#TODO will need test case +class re_finder(object): + + def __init__(self, path, pattern, postproc=None): + self.pattern = pattern + self.postproc = postproc + self.path = convert_path(path) + + def _finder(self, dirname, filename): + f = open(filename,'rU') + try: + data = f.read() + finally: + f.close() + for match in self.pattern.finditer(data): + path = match.group(1) + if postproc: + #postproc used to be used when the svn finder + #was an re_finder for calling unescape + path = postproc(path) + yield svn_utils.joinpath(dirname,path) + def __call__(self, dirname=''): + path = svn_utils.joinpath(dirname, self.path) + if os.path.isfile(path): - for path in finder(dirname,path): + for path in self._finder(dirname,path): if os.path.isfile(path): yield path elif os.path.isdir(path): - for item in _default_revctrl(path): + for item in self.find(path): yield item -def externals_finder(dirname, filename): - """Find any 'svn:externals' directories""" - found = False - f = open(filename,'rt') - for line in iter(f.readline, ''): # can't use direct iter! - parts = line.split() - if len(parts)==2: - kind,length = parts - data = f.read(int(length)) - if kind=='K' and data=='svn:externals': - found = True - elif kind=='V' and found: - f.close() - break - else: - f.close() - return - - for line in data.splitlines(): - parts = line.split() - if parts: - yield joinpath(dirname, parts[0]) - - -entries_pattern = re.compile(r'name="([^"]+)"(?![^>]+deleted="true")', re.I) - -def entries_finder(dirname, filename): - f = open(filename,'rU') - data = f.read() - f.close() - if data.startswith('<?xml'): - for match in entries_pattern.finditer(data): - yield joinpath(dirname,unescape(match.group(1))) - else: - svnver=-1 - try: svnver = int(data.splitlines()[0]) - except: pass - if svnver<8: - log.warn("unrecognized .svn/entries format in %s", os.path.abspath(dirname)) - return - for record in map(str.splitlines, data.split('\n\x0c\n')[1:]): - # subversion 1.6/1.5/1.4 - if not record or len(record)>=6 and record[5]=="delete": - continue # skip deleted - yield joinpath(dirname, record[0]) - - -finders = [ - (convert_path('CVS/Entries'), - re_finder(re.compile(r"^\w?/([^/]+)/", re.M))), - (convert_path('.svn/entries'), entries_finder), - (convert_path('.svn/dir-props'), externals_finder), - (convert_path('.svn/dir-prop-base'), externals_finder), # svn 1.4 -] - - - - - - +def _default_revctrl(dirname=''): + 'Primary svn_cvs entry point' + for finder in finders: + for item in finder(dirname): + yield item +finders = [ + re_finder('CVS/Entries', re.compile(r"^\w?/([^/]+)/", re.M)), + svn_utils.svn_finder, +] @@ -179,11 +120,12 @@ class sdist(_sdist): # Beginning with Python 2.7.2, 3.1.4, and 3.2.1, this leaky file handle # has been fixed, so only override the method if we're using an earlier # Python. - if ( - sys.version_info < (2,7,2) - or (3,0) <= sys.version_info < (3,1,4) - or (3,2) <= sys.version_info < (3,2,1) - ): + has_leaky_handle = ( + sys.version_info < (2,7,2) + or (3,0) <= sys.version_info < (3,1,4) + or (3,2) <= sys.version_info < (3,2,1) + ) + if has_leaky_handle: read_template = __read_template_hack def add_defaults(self): @@ -210,7 +152,7 @@ class sdist(_sdist): optional = ['test/test*.py', 'setup.cfg'] for pattern in optional: - files = filter(os.path.isfile, glob(pattern)) + files = list(filter(os.path.isfile, glob(pattern))) if files: self.filelist.extend(files) @@ -248,7 +190,6 @@ class sdist(_sdist): "standard file not found: should have one of " +', '.join(READMES) ) - def make_release_tree(self, base_dir, files): _sdist.make_release_tree(self, base_dir, files) @@ -295,10 +236,3 @@ class sdist(_sdist): continue self.filelist.append(line) manifest.close() - - - - - - -# diff --git a/setuptools/command/setopt.py b/setuptools/command/setopt.py index dbf3a94e..aa468c88 100755 --- a/setuptools/command/setopt.py +++ b/setuptools/command/setopt.py @@ -47,9 +47,9 @@ def edit_config(filename, settings, dry_run=False): while a dictionary lists settings to be changed or deleted in that section. A setting of ``None`` means to delete that setting. """ - from ConfigParser import RawConfigParser + from setuptools.compat import ConfigParser log.debug("Reading configuration from %s", filename) - opts = RawConfigParser() + opts = ConfigParser.RawConfigParser() opts.read([filename]) for section, options in settings.items(): if options is None: diff --git a/setuptools/command/test.py b/setuptools/command/test.py index a02ac142..db2fc7b1 100644 --- a/setuptools/command/test.py +++ b/setuptools/command/test.py @@ -154,7 +154,7 @@ class test(Command): for name in sys.modules: if name.startswith(module): del_modules.append(name) - map(sys.modules.__delitem__, del_modules) + list(map(sys.modules.__delitem__, del_modules)) loader_ep = EntryPoint.parse("x="+self.test_loader) loader_class = loader_ep.load(require=False) diff --git a/setuptools/command/upload.py b/setuptools/command/upload.py index 4b500f68..a6eff385 100755 --- a/setuptools/command/upload.py +++ b/setuptools/command/upload.py @@ -2,22 +2,21 @@ Implements the Distutils 'upload' subcommand (upload package to PyPI).""" -from distutils.errors import * +from distutils import errors +from distutils import log from distutils.core import Command from distutils.spawn import spawn -from distutils import log try: from hashlib import md5 except ImportError: from md5 import md5 import os +import sys import socket import platform -import ConfigParser -import httplib import base64 -import urlparse -import cStringIO as StringIO + +from setuptools.compat import urlparse, StringIO, httplib, ConfigParser class upload(Command): @@ -46,10 +45,10 @@ class upload(Command): def finalize_options(self): if self.identity and not self.sign: - raise DistutilsOptionError( + raise errors.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) @@ -69,7 +68,7 @@ class upload(Command): def run(self): if not self.distribution.dist_files: - raise DistutilsOptionError("No dist file created in earlier command") + raise errors.DistutilsOptionError("No dist file created in earlier command") for command, pyversion, filename in self.distribution.dist_files: self.upload_file(command, pyversion, filename) @@ -120,10 +119,10 @@ class upload(Command): boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' sep_boundary = '\n--' + boundary end_boundary = sep_boundary + '--' - body = StringIO.StringIO() + body = 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: @@ -149,17 +148,16 @@ class upload(Command): # 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) + 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 + raise AssertionError("unsupported schema " + schema) data = '' - loglevel = log.INFO try: http.connect() http.putrequest("POST", url) @@ -169,7 +167,8 @@ class upload(Command): http.putheader('Authorization', auth) http.endheaders() http.send(body) - except socket.error, e: + except socket.error: + e = sys.exc_info()[1] self.announce(str(e), log.ERROR) return @@ -181,5 +180,4 @@ class upload(Command): self.announce('Upload failed (%s): %s' % (r.status, r.reason), log.ERROR) if self.show_response: - print '-'*75, r.read(), '-'*75 - + print('-'*75, r.read(), '-'*75) diff --git a/setuptools/command/upload_docs.py b/setuptools/command/upload_docs.py index 6df3f394..12bc916b 100644 --- a/setuptools/command/upload_docs.py +++ b/setuptools/command/upload_docs.py @@ -8,8 +8,6 @@ PyPI's pythonhosted.org). import os import socket import zipfile -import httplib -import urlparse import tempfile import sys import shutil @@ -25,12 +23,21 @@ try: except ImportError: from setuptools.command.upload import upload +from setuptools.compat import httplib, urlparse, unicode, iteritems + +_IS_PYTHON3 = sys.version > '3' + +if _IS_PYTHON3: + errors = 'surrogateescape' +else: + errors = 'strict' + # This is not just a replacement for byte literals # but works as a general purpose encoder def b(s, encoding='utf-8'): if isinstance(s, unicode): - return s.encode(encoding) + return s.encode(encoding, errors) return s @@ -126,7 +133,7 @@ class upload_docs(upload): sep_boundary = b('\n--') + b(boundary) end_boundary = sep_boundary + b('--') body = [] - for key, values in data.iteritems(): + for key, values in iteritems(data): title = '\nContent-Disposition: form-data; name="%s"' % key # handle multiple entries for the same name if type(values) != type([]): @@ -154,7 +161,7 @@ class upload_docs(upload): # 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) + urlparse(self.repository) assert not params and not query and not fragments if schema == 'http': conn = httplib.HTTPConnection(netloc) @@ -174,7 +181,8 @@ class upload_docs(upload): conn.putheader('Authorization', auth) conn.endheaders() conn.send(body) - except socket.error, e: + except socket.error: + e = sys.exc_info()[1] self.announce(str(e), log.ERROR) return @@ -192,4 +200,4 @@ class upload_docs(upload): 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/setuptools/compat.py b/setuptools/compat.py new file mode 100644 index 00000000..bbc98d66 --- /dev/null +++ b/setuptools/compat.py @@ -0,0 +1,99 @@ +import sys +import itertools + +if sys.version_info[0] < 3: + PY3 = False + + basestring = basestring + import __builtin__ as builtins + import ConfigParser + from StringIO import StringIO + BytesIO = StringIO + execfile = execfile + func_code = lambda o: o.func_code + func_globals = lambda o: o.func_globals + im_func = lambda o: o.im_func + from htmlentitydefs import name2codepoint + import httplib + from BaseHTTPServer import HTTPServer + from SimpleHTTPServer import SimpleHTTPRequestHandler + from BaseHTTPServer import BaseHTTPRequestHandler + iteritems = lambda o: o.iteritems() + long_type = long + maxsize = sys.maxint + next = lambda o: o.next() + numeric_types = (int, long, float) + reduce = reduce + unichr = unichr + unicode = unicode + bytes = str + from urllib import url2pathname, splittag + import urllib2 + from urllib2 import urlopen, HTTPError, URLError, unquote, splituser + from urlparse import urlparse, urlunparse, urljoin, urlsplit, urlunsplit + xrange = xrange + filterfalse = itertools.ifilterfalse + + def exec_(code, globs=None, locs=None): + if globs is None: + frame = sys._getframe(1) + globs = frame.f_globals + if locs is None: + locs = frame.f_locals + del frame + elif locs is None: + locs = globs + exec("""exec code in globs, locs""") + + exec_("""def reraise(tp, value, tb=None): + raise tp, value, tb""") +else: + PY3 = True + + basestring = str + import builtins + import configparser as ConfigParser + exec_ = eval('exec') + from io import StringIO, BytesIO + func_code = lambda o: o.__code__ + func_globals = lambda o: o.__globals__ + im_func = lambda o: o.__func__ + from html.entities import name2codepoint + import http.client as httplib + from http.server import HTTPServer, SimpleHTTPRequestHandler + from http.server import BaseHTTPRequestHandler + iteritems = lambda o: o.items() + long_type = int + maxsize = sys.maxsize + next = next + numeric_types = (int, float) + from functools import reduce + unichr = chr + unicode = str + bytes = bytes + from urllib.error import HTTPError, URLError + import urllib.request as urllib2 + from urllib.request import urlopen, url2pathname + from urllib.parse import ( + urlparse, urlunparse, unquote, splituser, urljoin, urlsplit, + urlunsplit, splittag, + ) + xrange = range + filterfalse = itertools.filterfalse + + def execfile(fn, globs=None, locs=None): + if globs is None: + globs = globals() + if locs is None: + locs = globs + f = open(fn, 'rb') + try: + source = f.read() + finally: + f.close() + exec_(compile(source, fn, 'exec'), globs, locs) + + def reraise(tp, value, tb=None): + if value.__traceback__ is not tb: + raise value.with_traceback(tb) + raise value diff --git a/setuptools/depends.py b/setuptools/depends.py index 5fdf2d7e..8b9d1217 100644 --- a/setuptools/depends.py +++ b/setuptools/depends.py @@ -36,7 +36,7 @@ class Require: def version_ok(self,version): """Is 'version' sufficiently up-to-date?""" return self.attribute is None or self.format is None or \ - str(version)!="unknown" and version >= self.requested_version + str(version) != "unknown" and version >= self.requested_version def get_version(self, paths=None, default="unknown"): @@ -103,7 +103,7 @@ def _iter_code(code): ptr += 3 if op==EXTENDED_ARG: - extended_arg = arg * 65536L + extended_arg = arg * long_type(65536) continue else: diff --git a/setuptools/dist.py b/setuptools/dist.py index 907ce550..c5b02f99 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -1,15 +1,19 @@ __all__ = ['Distribution'] import re +import os +import sys +import warnings +import distutils.log +import distutils.core +import distutils.cmd from distutils.core import Distribution as _Distribution +from distutils.errors import (DistutilsOptionError, DistutilsPlatformError, + DistutilsSetupError) + from setuptools.depends import Require -from setuptools.command.install import install -from setuptools.command.sdist import sdist -from setuptools.command.install_lib import install_lib -from distutils.errors import DistutilsOptionError, DistutilsPlatformError -from distutils.errors import DistutilsSetupError -import setuptools, pkg_resources, distutils.core, distutils.dist, distutils.cmd -import os, distutils.log +from setuptools.compat import numeric_types, basestring +import pkg_resources def _get_unpatched(cls): """Protect against re-patching the distutils if reloaded @@ -100,7 +104,8 @@ def check_entry_points(dist, attr, value): """Verify that entry_points map is parseable""" try: pkg_resources.EntryPoint.parse_map(value) - except ValueError, e: + except ValueError: + e = sys.exc_info()[1] raise DistutilsSetupError(e) def check_test_suite(dist, attr, value): @@ -129,38 +134,6 @@ def check_packages(dist, attr, value): "WARNING: %r not a valid package name; please use only" ".-separated package names in setup.py", pkgname ) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - class Distribution(_Distribution): @@ -191,7 +164,8 @@ class Distribution(_Distribution): EasyInstall and requests one of your extras, the corresponding additional requirements will be installed if needed. - 'features' -- a dictionary mapping option names to 'setuptools.Feature' + 'features' **deprecated** -- a dictionary mapping option names to + 'setuptools.Feature' objects. Features are a portion of the distribution that can be included or excluded based on user options, inter-feature dependencies, and availability on the current system. Excluded features are omitted @@ -245,10 +219,13 @@ class Distribution(_Distribution): dist._version = pkg_resources.safe_version(str(attrs['version'])) self._patched_dist = dist - def __init__ (self, attrs=None): + def __init__(self, attrs=None): have_package_data = hasattr(self, "package_data") if not have_package_data: self.package_data = {} + _attrs_dict = attrs or {} + if 'features' in _attrs_dict or 'require_features' in _attrs_dict: + Feature.warn_deprecated() self.require_features = [] self.features = {} self.dist_files = [] @@ -264,7 +241,7 @@ class Distribution(_Distribution): if not hasattr(self,ep.name): setattr(self,ep.name,None) _Distribution.__init__(self,attrs) - if isinstance(self.metadata.version, (int,long,float)): + if isinstance(self.metadata.version, numeric_types): # Some people apparently take "version number" too literally :) self.metadata.version = str(self.metadata.version) @@ -359,23 +336,6 @@ class Distribution(_Distribution): self.global_options = self.feature_options = go + self.global_options self.negative_opt = self.feature_negopt = no - - - - - - - - - - - - - - - - - def _finalize_features(self): """Add/remove features and resolve dependencies between them""" @@ -393,7 +353,6 @@ class Distribution(_Distribution): feature.exclude_from(self) self._set_feature(name,0) - def get_command_class(self, command): """Pluggable version of get_command_class()""" if command in self.cmdclass: @@ -413,10 +372,6 @@ class Distribution(_Distribution): self.cmdclass[ep.name] = cmdclass return _Distribution.print_commands(self) - - - - def _set_feature(self,name,status): """Set feature's inclusion status""" setattr(self,self._feature_attrname(name),status) @@ -431,8 +386,8 @@ class Distribution(_Distribution): if self.feature_is_included(name)==0: descr = self.features[name].description raise DistutilsOptionError( - descr + " is required, but was excluded or is not available" - ) + descr + " is required, but was excluded or is not available" + ) self.features[name].include_in(self) self._set_feature(name,1) @@ -480,7 +435,6 @@ class Distribution(_Distribution): if p.name != package and not p.name.startswith(pfx) ] - def has_contents_for(self,package): """Return true if 'exclude_package(package)' would do something""" @@ -490,15 +444,6 @@ class Distribution(_Distribution): if p==package or p.startswith(pfx): return True - - - - - - - - - def _exclude_misc(self,name,value): """Handle 'exclude()' for list/tuple attrs without a special handler""" if not isinstance(value,sequence): @@ -568,18 +513,7 @@ class Distribution(_Distribution): raise DistutilsSetupError( "packages: setting must be a list or tuple (%r)" % (packages,) ) - map(self.exclude_package, packages) - - - - - - - - - - - + list(map(self.exclude_package, packages)) def _parse_command_opts(self, parser, args): # Remove --with-X/--without-X options when processing command args @@ -607,21 +541,6 @@ class Distribution(_Distribution): return nargs - - - - - - - - - - - - - - - def get_cmdline_options(self): """Return a '{cmd: {opt:val}}' map of all command-line options @@ -662,7 +581,6 @@ class Distribution(_Distribution): return d - def iter_distribution_names(self): """Yield all packages, modules, and extension names in distribution""" @@ -681,7 +599,6 @@ class Distribution(_Distribution): name = name[:-6] yield name - def handle_display_options(self, option_order): """If there were any non-global "display-only" options (--help-commands or the metadata display options) on the command @@ -723,26 +640,14 @@ for module in distutils.dist, distutils.core, distutils.cmd: module.Distribution = Distribution - - - - - - - - - - - - - - - - - - class Feature: - """A subset of the distribution that can be excluded if unneeded/wanted + """ + **deprecated** -- The `Feature` facility was never completely implemented + or supported, `has reported issues + <https://bitbucket.org/pypa/setuptools/issue/58>`_ and will be removed in + a future version. + + A subset of the distribution that can be excluded if unneeded/wanted Features are created using these keyword arguments: @@ -791,9 +696,19 @@ class Feature: Aside from the methods, the only feature attributes that distributions look at are 'description' and 'optional'. """ + + @staticmethod + def warn_deprecated(): + warnings.warn( + "Features are deprecated and will be removed in a future " + "version. See http://bitbucket.org/pypa/setuptools/65.", + DeprecationWarning, + stacklevel=3, + ) + def __init__(self, description, standard=False, available=True, - optional=True, require_features=(), remove=(), **extras - ): + optional=True, require_features=(), remove=(), **extras): + self.warn_deprecated() self.description = description self.standard = standard @@ -844,8 +759,6 @@ class Feature: for f in self.require_features: dist.include_feature(f) - - def exclude_from(self,dist): """Ensure feature is excluded from distribution @@ -862,8 +775,6 @@ class Feature: for item in self.remove: dist.exclude_package(item) - - def validate(self,dist): """Verify that feature makes sense in context of distribution @@ -883,7 +794,3 @@ class Feature: " doesn't contain any packages or modules under %s" % (self.description, item, item) ) - - - - diff --git a/setuptools/extension.py b/setuptools/extension.py index eb8b836c..d7892d3d 100644 --- a/setuptools/extension.py +++ b/setuptools/extension.py @@ -35,7 +35,7 @@ class Extension(_Extension): if source.endswith('.pyx'): source = source[:-4] + '.c' return source - self.sources = map(pyx_to_c, self.sources) + self.sources = list(map(pyx_to_c, self.sources)) class Library(Extension): """Just like a regular Extension, but built as a library instead""" diff --git a/setuptools/package_index.py b/setuptools/package_index.py index 4f39c70a..4c9e40a7 100755 --- a/setuptools/package_index.py +++ b/setuptools/package_index.py @@ -1,18 +1,28 @@ """PyPI and direct package downloading""" -import sys, os.path, re, urlparse, urllib2, shutil, random, socket, cStringIO -import itertools +import sys +import os +import re +import shutil +import socket import base64 -import httplib, urllib + +from pkg_resources import ( + CHECKOUT_DIST, Distribution, BINARY_DIST, normalize_path, SOURCE_DIST, + require, Environment, find_distributions, safe_name, safe_version, + to_filename, Requirement, DEVELOP_DIST, +) from setuptools import ssl_support -from pkg_resources import * from distutils import log from distutils.errors import DistutilsError -try: - from hashlib import md5 -except ImportError: - from md5 import md5 +from setuptools.compat import (urllib2, httplib, StringIO, HTTPError, + urlparse, urlunparse, unquote, splituser, + url2pathname, name2codepoint, + unichr, urljoin, urlsplit, urlunsplit) +from setuptools.compat import filterfalse from fnmatch import translate +from setuptools.py24compat import hashlib from setuptools.py24compat import wraps +from setuptools.py26compat import strip_fragment from setuptools.py27compat import get_all_headers EGG_FRAGMENT = re.compile(r'^egg=([-A-Za-z0-9_.]+)$') @@ -57,10 +67,10 @@ def parse_bdist_wininst(name): def egg_info_for_url(url): - scheme, server, path, parameters, query, fragment = urlparse.urlparse(url) - base = urllib2.unquote(path.split('/')[-1]) + scheme, server, path, parameters, query, fragment = urlparse(url) + base = unquote(path.split('/')[-1]) if server=='sourceforge.net' and base=='download': # XXX Yuck - base = urllib2.unquote(path.split('/')[-2]) + base = unquote(path.split('/')[-2]) if '#' in base: base, fragment = base.split('#',1) return base,fragment @@ -104,9 +114,10 @@ def distros_for_filename(filename, metadata=None): ) -def interpret_distro_name(location, basename, metadata, - py_version=None, precedence=SOURCE_DIST, platform=None -): +def interpret_distro_name( + location, basename, metadata, py_version=None, precedence=SOURCE_DIST, + platform=None + ): """Generate alternative interpretations of a source distro name Note: if `location` is a filesystem filename, you should call @@ -146,7 +157,7 @@ def unique_everseen(iterable, key=None): seen = set() seen_add = seen.add if key is None: - for element in itertools.ifilterfalse(seen.__contains__, iterable): + for element in filterfalse(seen.__contains__, iterable): seen_add(element) yield element else: @@ -175,28 +186,85 @@ def find_external_links(url, page): for match in REL.finditer(page): tag, rel = match.groups() - rels = map(str.strip, rel.lower().split(',')) + rels = set(map(str.strip, rel.lower().split(','))) if 'homepage' in rels or 'download' in rels: for match in HREF.finditer(tag): - yield urlparse.urljoin(url, htmldecode(match.group(1))) + yield urljoin(url, htmldecode(match.group(1))) for tag in ("<th>Home Page", "<th>Download URL"): pos = page.find(tag) if pos!=-1: match = HREF.search(page,pos) if match: - yield urlparse.urljoin(url, htmldecode(match.group(1))) + yield urljoin(url, htmldecode(match.group(1))) user_agent = "Python-urllib/%s setuptools/%s" % ( sys.version[:3], require('setuptools')[0].version ) +class ContentChecker(object): + """ + A null content checker that defines the interface for checking content + """ + def feed(self, block): + """ + Feed a block of data to the hash. + """ + return + + def is_valid(self): + """ + Check the hash. Return False if validation fails. + """ + return True + + def report(self, reporter, template): + """ + Call reporter with information about the checker (hash name) + substituted into the template. + """ + return + +class HashChecker(ContentChecker): + pattern = re.compile( + r'(?P<hash_name>sha1|sha224|sha384|sha256|sha512|md5)=' + r'(?P<expected>[a-f0-9]+)' + ) + + def __init__(self, hash_name, expected): + self.hash_name = hash_name + self.hash = hashlib.new(hash_name) + self.expected = expected + + @classmethod + def from_url(cls, url): + "Construct a (possibly null) ContentChecker from a URL" + fragment = urlparse(url)[-1] + if not fragment: + return ContentChecker() + match = cls.pattern.search(fragment) + if not match: + return ContentChecker() + return cls(**match.groupdict()) + + def feed(self, block): + self.hash.update(block) + + def is_valid(self): + return self.hash.hexdigest() == self.expected + + def report(self, reporter, template): + msg = template % self.hash_name + return reporter(msg) + + class PackageIndex(Environment): """A distribution index that scans web pages for download URLs""" - def __init__(self, index_url="https://pypi.python.org/simple", hosts=('*',), - ca_bundle=None, verify_ssl=True, *args, **kw - ): + def __init__( + self, index_url="https://pypi.python.org/simple", hosts=('*',), + ca_bundle=None, verify_ssl=True, *args, **kw + ): Environment.__init__(self,*args,**kw) self.index_url = index_url + "/"[:not index_url.endswith('/')] self.scanned_urls = {} @@ -224,7 +292,7 @@ class PackageIndex(Environment): self.debug("Found link: %s", url) if dists or not retrieve or url in self.fetched_urls: - map(self.add, dists) + list(map(self.add, dists)) return # don't need the actual page if not self.url_ok(url): @@ -243,7 +311,7 @@ class PackageIndex(Environment): base = f.url # handle redirects page = f.read() if not isinstance(page, str): # We are in Python 3 and got bytes. We want str. - if isinstance(f, urllib2.HTTPError): + if isinstance(f, HTTPError): # Errors have no charset, assume latin1: charset = 'latin-1' else: @@ -251,7 +319,7 @@ class PackageIndex(Environment): page = page.decode(charset, "ignore") f.close() for match in HREF.finditer(page): - link = urlparse.urljoin(base, htmldecode(match.group(1))) + link = urljoin(base, htmldecode(match.group(1))) self.process_url(link) if url.startswith(self.index_url) and getattr(f,'code',None)!=404: page = self.process_index(url, page) @@ -270,13 +338,14 @@ class PackageIndex(Environment): dists = distros_for_filename(fn) if dists: self.debug("Found: %s", fn) - map(self.add, dists) + list(map(self.add, dists)) def url_ok(self, url, fatal=False): s = URL_SCHEME(url) - if (s and s.group(1).lower()=='file') or self.allows(urlparse.urlparse(url)[1]): + if (s and s.group(1).lower()=='file') or self.allows(urlparse(url)[1]): return True - msg = "\nLink to % s ***BLOCKED*** by --allow-hosts\n" + msg = ("\nNote: Bypassing %s (disallowed host; see " + "http://bit.ly/1dg9ijs for details).\n") if fatal: raise DistutilsError(msg % url) else: @@ -290,7 +359,8 @@ class PackageIndex(Environment): self.scan_egg_link(item, entry) def scan_egg_link(self, path, entry): - lines = filter(None, map(str.strip, open(os.path.join(path, entry)))) + lines = [_f for _f in map(str.strip, + open(os.path.join(path, entry))) if _f] if len(lines)==2: for dist in find_distributions(os.path.join(path, lines[0])): dist.location = os.path.join(path, *lines) @@ -302,9 +372,9 @@ class PackageIndex(Environment): def scan(link): # Process a URL to see if it's for a package page if link.startswith(self.index_url): - parts = map( - urllib2.unquote, link[len(self.index_url):].split('/') - ) + parts = list(map( + unquote, link[len(self.index_url):].split('/') + )) if len(parts)==2 and '#' not in parts[1]: # it's a package page, sanitize and index it pkg = safe_name(parts[0]) @@ -316,7 +386,7 @@ class PackageIndex(Environment): # process an index page into the package-page index for match in HREF.finditer(page): try: - scan( urlparse.urljoin(url, htmldecode(match.group(1))) ) + scan(urljoin(url, htmldecode(match.group(1)))) except ValueError: pass @@ -339,8 +409,6 @@ class PackageIndex(Environment): else: return "" # no sense double-scanning non-package pages - - def need_version_info(self, url): self.scan_all( "Page at %s links to .py file(s) without version info; an index " @@ -371,27 +439,28 @@ class PackageIndex(Environment): self.scan_url(url) def obtain(self, requirement, installer=None): - self.prescan(); self.find_packages(requirement) + self.prescan() + self.find_packages(requirement) for dist in self[requirement.key]: if dist in requirement: return dist self.debug("%s does not match %s", requirement, dist) return super(PackageIndex, self).obtain(requirement,installer) - - - - - def check_md5(self, cs, info, filename, tfp): - if re.match('md5=[0-9a-f]{32}$', info): - self.debug("Validating md5 checksum for %s", filename) - if cs.hexdigest()!=info[4:]: - tfp.close() - os.unlink(filename) - raise DistutilsError( - "MD5 validation failed for "+os.path.basename(filename)+ - "; possible download problem?" - ) + def check_hash(self, checker, filename, tfp): + """ + checker is a ContentChecker + """ + checker.report(self.debug, + "Validating %%s checksum for %s" % filename) + if not checker.is_valid(): + tfp.close() + os.unlink(filename) + raise DistutilsError( + "%s validation failed for %s; " + "possible download problem?" % ( + checker.hash.name, os.path.basename(filename)) + ) def add_find_links(self, urls): """Add `urls` to the list that will be prescanned for searches""" @@ -411,7 +480,7 @@ class PackageIndex(Environment): def prescan(self): """Scan urls scheduled for prescanning (e.g. --find-links)""" if self.to_scan: - map(self.scan_url, self.to_scan) + list(map(self.scan_url, self.to_scan)) self.to_scan = None # from now on, go ahead and process immediately def not_found_in_index(self, requirement): @@ -463,11 +532,10 @@ class PackageIndex(Environment): ) return getattr(self.fetch_distribution(spec, tmpdir),'location',None) - - def fetch_distribution(self, - requirement, tmpdir, force_scan=False, source=False, develop_ok=False, - local_index=None - ): + def fetch_distribution( + self, requirement, tmpdir, force_scan=False, source=False, + develop_ok=False, local_index=None + ): """Obtain a distribution suitable for fulfilling `requirement` `requirement` must be a ``pkg_resources.Requirement`` instance. @@ -505,8 +573,6 @@ class PackageIndex(Environment): if dist in req and (dist.precedence<=SOURCE_DIST or not source): return dist - - if force_scan: self.prescan() self.find_packages(requirement) @@ -533,7 +599,6 @@ class PackageIndex(Environment): self.info("Best match: %s", dist) return dist.clone(location=self.download(dist.location, tmpdir)) - def fetch(self, requirement, tmpdir, force_scan=False, source=False): """Obtain a file suitable for fulfilling `requirement` @@ -547,10 +612,10 @@ class PackageIndex(Environment): return dist.location return None - def gen_setup(self, filename, fragment, tmpdir): match = EGG_FRAGMENT.match(fragment) - dists = match and [d for d in + dists = match and [ + d for d in interpret_distro_name(filename, match.group(1), None) if d.version ] or [] @@ -595,14 +660,12 @@ class PackageIndex(Environment): # Download the file fp, tfp, info = None, None, None try: - if '#' in url: - url, info = url.split('#', 1) - fp = self.open_url(url) - if isinstance(fp, urllib2.HTTPError): + checker = HashChecker.from_url(url) + fp = self.open_url(strip_fragment(url)) + if isinstance(fp, HTTPError): raise DistutilsError( "Can't download %s: %s %s" % (url, fp.code,fp.msg) ) - cs = md5() headers = fp.info() blocknum = 0 bs = self.dl_blocksize @@ -616,13 +679,13 @@ class PackageIndex(Environment): while True: block = fp.read(bs) if block: - cs.update(block) + checker.feed(block) tfp.write(block) blocknum += 1 self.reporthook(url, filename, blocknum, bs, size) else: break - if info: self.check_md5(cs, info, filename, tfp) + self.check_hash(checker, filename, tfp) return headers finally: if fp: fp.close() @@ -631,34 +694,40 @@ class PackageIndex(Environment): def reporthook(self, url, filename, blocknum, blksize, size): pass # no-op - def open_url(self, url, warning=None): if url.startswith('file:'): return local_open(url) try: return open_with_auth(url, self.opener) - except (ValueError, httplib.InvalidURL), v: + except (ValueError, httplib.InvalidURL): + v = sys.exc_info()[1] msg = ' '.join([str(arg) for arg in v.args]) if warning: self.warn(warning, msg) else: raise DistutilsError('%s %s' % (url, msg)) - except urllib2.HTTPError, v: + except urllib2.HTTPError: + v = sys.exc_info()[1] return v - except urllib2.URLError, v: + except urllib2.URLError: + v = sys.exc_info()[1] if warning: self.warn(warning, v.reason) else: raise DistutilsError("Download error for %s: %s" % (url, v.reason)) - except httplib.BadStatusLine, v: + except httplib.BadStatusLine: + v = sys.exc_info()[1] if warning: self.warn(warning, v.line) else: - raise DistutilsError('%s returned a bad status line. ' - 'The server might be down, %s' % \ - (url, v.line)) - except httplib.HTTPException, v: + raise DistutilsError( + '%s returned a bad status line. The server might be ' + 'down, %s' % + (url, v.line) + ) + except httplib.HTTPException: + v = sys.exc_info()[1] if warning: self.warn(warning, v) else: @@ -689,7 +758,7 @@ class PackageIndex(Environment): elif scheme.startswith('hg+'): return self._download_hg(url, filename) elif scheme=='file': - return urllib.url2pathname(urlparse.urlparse(url)[2]) + return url2pathname(urlparse(url)[2]) else: self.url_ok(url, True) # raises error if not allowed return self._attempt_download(url, filename) @@ -697,7 +766,6 @@ class PackageIndex(Environment): def scan_url(self, url): self.process_url(url, True) - def _attempt_download(self, url, filename): headers = self._download_to(url, filename) if 'html' in headers.get('content-type','').lower(): @@ -720,29 +788,14 @@ class PackageIndex(Environment): os.unlink(filename) raise DistutilsError("Unexpected HTML page found at "+url) - - - - - - - - - - - - - - - def _download_svn(self, url, filename): url = url.split('#',1)[0] # remove any fragment for svn's sake creds = '' if url.lower().startswith('svn:') and '@' in url: - scheme, netloc, path, p, q, f = urlparse.urlparse(url) + scheme, netloc, path, p, q, f = urlparse(url) if not netloc and path.startswith('//') and '/' in path[2:]: netloc, path = path[2:].split('/',1) - auth, host = urllib.splituser(netloc) + auth, host = splituser(netloc) if auth: if ':' in auth: user, pw = auth.split(':',1) @@ -750,13 +803,14 @@ class PackageIndex(Environment): else: creds = " --username="+auth netloc = host - url = urlparse.urlunparse((scheme, netloc, url, p, q, f)) + url = urlunparse((scheme, netloc, url, p, q, f)) self.info("Doing subversion checkout from %s to %s", url, filename) os.system("svn checkout%s -q %s %s" % (creds, url, filename)) return filename - def _vcs_split_rev_from_url(self, url, pop_prefix=False): - scheme, netloc, path, query, frag = urlparse.urlsplit(url) + @staticmethod + def _vcs_split_rev_from_url(url, pop_prefix=False): + scheme, netloc, path, query, frag = urlsplit(url) scheme = scheme.split('+', 1)[-1] @@ -768,7 +822,7 @@ class PackageIndex(Environment): path, rev = path.rsplit('@', 1) # Also, discard fragment - url = urlparse.urlunsplit((scheme, netloc, path, query, '')) + url = urlunsplit((scheme, netloc, path, query, '')) return url, rev @@ -813,18 +867,6 @@ class PackageIndex(Environment): def warn(self, msg, *args): log.warn(msg, *args) - - - - - - - - - - - - # This pattern matches a character entity reference (a decimal numeric # references, a hexadecimal numeric reference, or a named reference). entity_sub = re.compile(r'&(#(\d+|x[\da-fA-F]+)|[\w.:-]+);?').sub @@ -842,7 +884,6 @@ def decode_entity(match): elif what.startswith('#'): what = int(what[1:]) else: - from htmlentitydefs import name2codepoint what = name2codepoint.get(what, match.group(0)) return uchr(what) @@ -850,20 +891,6 @@ def htmldecode(text): """Decode HTML entities in the given text.""" return entity_sub(decode_entity, text) - - - - - - - - - - - - - - def socket_timeout(timeout=15): def _socket_timeout(func): def _socket_timeout(*args, **kwargs): @@ -883,7 +910,7 @@ def _encode_auth(auth): >>> _encode_auth('username%3Apassword') u'dXNlcm5hbWU6cGFzc3dvcmQ=' """ - auth_s = urllib2.unquote(auth) + auth_s = unquote(auth) # convert to bytes auth_bytes = auth_s.encode() # use the legacy interface for Python 2.3 support @@ -896,7 +923,7 @@ def _encode_auth(auth): def open_with_auth(url, opener=urllib2.urlopen): """Open a urllib2 request, handling HTTP authentication""" - scheme, netloc, path, params, query, frag = urlparse.urlparse(url) + scheme, netloc, path, params, query, frag = urlparse(url) # Double scheme does not raise on Mac OS X as revealed by a # failing test. We would expect "nonnumeric port". Refs #20. @@ -904,13 +931,13 @@ def open_with_auth(url, opener=urllib2.urlopen): raise httplib.InvalidURL("nonnumeric port: ''") if scheme in ('http', 'https'): - auth, host = urllib.splituser(netloc) + auth, host = splituser(netloc) else: auth = None if auth: auth = "Basic " + _encode_auth(auth) - new_url = urlparse.urlunparse((scheme,host,path,params,query,frag)) + new_url = urlunparse((scheme,host,path,params,query,frag)) request = urllib2.Request(new_url) request.add_header("Authorization", auth) else: @@ -922,9 +949,9 @@ def open_with_auth(url, opener=urllib2.urlopen): if auth: # Put authentication info back into request URL if same host, # so that links found on the page will work - s2, h2, path2, param2, query2, frag2 = urlparse.urlparse(fp.url) + s2, h2, path2, param2, query2, frag2 = urlparse(fp.url) if s2==scheme and h2==host: - fp.url = urlparse.urlunparse((s2,netloc,path2,param2,query2,frag2)) + fp.url = urlunparse((s2,netloc,path2,param2,query2,frag2)) return fp @@ -932,22 +959,13 @@ def open_with_auth(url, opener=urllib2.urlopen): open_with_auth = socket_timeout(_SOCKET_TIMEOUT)(open_with_auth) - - - - - - - - - def fix_sf_url(url): return url # backward compatibility def local_open(url): """Read a local path, with special support for directories""" - scheme, server, path, param, query, frag = urlparse.urlparse(url) - filename = urllib.url2pathname(path) + scheme, server, path, param, query, frag = urlparse(url) + filename = url2pathname(path) if os.path.isfile(filename): return urllib2.urlopen(url) elif path.endswith('/') and os.path.isdir(filename): @@ -968,19 +986,5 @@ def local_open(url): else: status, message, body = 404, "Path not found", "Not found" - return urllib2.HTTPError(url, status, message, - {'content-type':'text/html'}, cStringIO.StringIO(body)) - - - - - - - - - - - - - -# this line is a kludge to keep the trailing blank lines for pje's editor + return HTTPError(url, status, message, + {'content-type':'text/html'}, StringIO(body)) diff --git a/setuptools/py24compat.py b/setuptools/py24compat.py index c5d7d204..40e9ae0f 100644 --- a/setuptools/py24compat.py +++ b/setuptools/py24compat.py @@ -9,3 +9,9 @@ except ImportError: def wraps(func): "Just return the function unwrapped" return lambda x: x + + +try: + import hashlib +except ImportError: + from setuptools._backport import hashlib diff --git a/setuptools/py26compat.py b/setuptools/py26compat.py new file mode 100644 index 00000000..738b0cc4 --- /dev/null +++ b/setuptools/py26compat.py @@ -0,0 +1,19 @@ +""" +Compatibility Support for Python 2.6 and earlier +""" + +import sys + +from setuptools.compat import splittag + +def strip_fragment(url): + """ + In `Python 8280 <http://bugs.python.org/issue8280>`_, Python 2.7 and + later was patched to disregard the fragment when making URL requests. + Do the same for Python 2.6 and earlier. + """ + url, fragment = splittag(url) + return url + +if sys.version_info >= (2,7): + strip_fragment = lambda x: x diff --git a/setuptools/sandbox.py b/setuptools/sandbox.py index f3095125..29fc07b8 100755 --- a/setuptools/sandbox.py +++ b/setuptools/sandbox.py @@ -1,4 +1,4 @@ -import os, sys, __builtin__, tempfile, operator, pkg_resources +import os, sys, tempfile, operator, pkg_resources if os.name == "java": import org.python.modules.posix.PosixModule as _os else: @@ -11,6 +11,8 @@ _open = open from distutils.errors import DistutilsError from pkg_resources import working_set +from setuptools.compat import builtins, execfile, reduce + __all__ = [ "AbstractSandbox", "DirectorySandbox", "SandboxViolation", "run_setup", ] @@ -69,7 +71,8 @@ def run_setup(setup_script, args): {'__file__':setup_script, '__name__':'__main__'} ) ) - except SystemExit, v: + except SystemExit: + v = sys.exc_info()[1] if v.args and v.args[0]: raise # Normal exit, just return @@ -83,7 +86,7 @@ def run_setup(setup_script, args): # exclude any encodings modules. See #285 and not mod_name.startswith('encodings.') ] - map(sys.modules.__delitem__, del_modules) + list(map(sys.modules.__delitem__, del_modules)) os.chdir(old_dir) sys.path[:] = save_path sys.argv[:] = save_argv @@ -111,15 +114,15 @@ class AbstractSandbox: try: self._copy(self) if _file: - __builtin__.file = self._file - __builtin__.open = self._open + builtins.file = self._file + builtins.open = self._open self._active = True return func() finally: self._active = False if _file: - __builtin__.file = _file - __builtin__.open = _open + builtins.file = _file + builtins.open = _open self._copy(_os) def _mk_dual_path_wrapper(name): @@ -267,11 +270,11 @@ class DirectorySandbox(AbstractSandbox): self._violation(operation, src, dst, *args, **kw) return (src,dst) - def open(self, file, flags, mode=0777): + def open(self, file, flags, mode=0x1FF, *args, **kw): # 0777 """Called for low-level os.open()""" if flags & WRITE_FLAGS and not self._ok(file): - self._violation("os.open", file, flags, mode) - return _os.open(file,flags,mode) + self._violation("os.open", file, flags, mode, *args, **kw) + return _os.open(file,flags,mode, *args, **kw) WRITE_FLAGS = reduce( operator.or_, [getattr(_os, a, 0) for a in diff --git a/setuptools/script template (dev).py b/setuptools/script template (dev).py index 6dd9dd45..b3fe209e 100644 --- a/setuptools/script template (dev).py +++ b/setuptools/script template (dev).py @@ -1,6 +1,11 @@ # EASY-INSTALL-DEV-SCRIPT: %(spec)r,%(script_name)r __requires__ = """%(spec)r""" -from pkg_resources import require; require("""%(spec)r""") +import sys +from pkg_resources import require +require("""%(spec)r""") del require __file__ = """%(dev_path)r""" -execfile(__file__) +if sys.version_info < (3, 0): + execfile(__file__) +else: + exec(compile(open(__file__).read(), __file__, 'exec')) diff --git a/setuptools/ssl_support.py b/setuptools/ssl_support.py index 6dca5fab..90359b2c 100644 --- a/setuptools/ssl_support.py +++ b/setuptools/ssl_support.py @@ -1,6 +1,7 @@ -import sys, os, socket, urllib2, atexit, re +import sys, os, socket, atexit, re import pkg_resources from pkg_resources import ResolutionError, ExtractionError +from setuptools.compat import urllib2 try: import ssl @@ -87,9 +88,16 @@ except ImportError: class CertificateError(ValueError): pass - def _dnsname_to_pat(dn): + def _dnsname_to_pat(dn, max_wildcards=1): pats = [] for frag in dn.split(r'.'): + if frag.count('*') > max_wildcards: + # Issue #17980: avoid denials of service by refusing more + # than one wildcard per fragment. A survery of established + # policy among SSL implementations showed it to be a + # reasonable choice. + raise CertificateError( + "too many wildcards in certificate DNS name: " + repr(dn)) if frag == '*': # When '*' is a fragment by itself, it matches a non-empty dotless # fragment. @@ -186,6 +194,12 @@ class VerifyingHTTPSConn(HTTPSConnection): sock = create_connection( (self.host, self.port), getattr(self,'source_address',None) ) + + # Handle the socket if a (proxy) tunnel is present + if hasattr(self, '_tunnel') and getattr(self, '_tunnel_host', None): + self.sock = sock + self._tunnel() + self.sock = ssl.wrap_socket( sock, cert_reqs=ssl.CERT_REQUIRED, ca_certs=self.ca_bundle ) diff --git a/setuptools/svn_utils.py b/setuptools/svn_utils.py new file mode 100644 index 00000000..22b45cd7 --- /dev/null +++ b/setuptools/svn_utils.py @@ -0,0 +1,539 @@ +import os
+import re
+import sys
+from distutils import log
+import xml.dom.pulldom
+import shlex
+import locale
+import unicodedata
+import warnings
+from setuptools.compat import unicode, bytes
+from xml.sax.saxutils import unescape
+
+try:
+ import urlparse
+except ImportError:
+ import urllib.parse as urlparse
+
+from subprocess import Popen as _Popen, PIPE as _PIPE
+
+#NOTE: Use of the command line options require SVN 1.3 or newer (December 2005)
+# and SVN 1.3 hasn't been supported by the developers since mid 2008.
+
+#subprocess is called several times with shell=(sys.platform=='win32')
+#see the follow for more information:
+# http://bugs.python.org/issue8557
+# http://stackoverflow.com/questions/5658622/
+# python-subprocess-popen-environment-path
+
+
+def _run_command(args, stdout=_PIPE, stderr=_PIPE):
+ #regarding the shell argument, see: http://bugs.python.org/issue8557
+ try:
+ args = [fsdecode(x) for x in args]
+ proc = _Popen(args, stdout=stdout, stderr=stderr,
+ shell=(sys.platform == 'win32'))
+
+ data = proc.communicate()[0]
+ except OSError:
+ return 1, ''
+
+ data = consoledecode(data)
+
+ #communciate calls wait()
+ return proc.returncode, data
+
+
+def _get_entry_schedule(entry):
+ schedule = entry.getElementsByTagName('schedule')[0]
+ return "".join([t.nodeValue
+ for t in schedule.childNodes
+ if t.nodeType == t.TEXT_NODE])
+
+
+def _get_target_property(target):
+ property_text = target.getElementsByTagName('property')[0]
+ return "".join([t.nodeValue
+ for t in property_text.childNodes
+ if t.nodeType == t.TEXT_NODE])
+
+
+def _get_xml_data(decoded_str):
+ if sys.version_info < (3, 0):
+ #old versions want an encoded string
+ data = decoded_str.encode('utf-8')
+ else:
+ data = decoded_str
+ return data
+
+
+def joinpath(prefix, *suffix):
+ if not prefix or prefix == '.':
+ return os.path.join(*suffix)
+ return os.path.join(prefix, *suffix)
+
+
+def fsencode(path):
+ "Path must be unicode or in file system encoding already"
+ encoding = sys.getfilesystemencoding()
+
+ if isinstance(path, unicode):
+ path = path.encode()
+ elif not isinstance(path, bytes):
+ raise TypeError('%s is not a string or byte type'
+ % type(path).__name__)
+
+ #getfilessystemencoding doesn't have the mac-roman issue
+ if encoding == 'utf-8' and sys.platform == 'darwin':
+ path = path.decode('utf-8')
+ path = unicodedata.normalize('NFD', path)
+ path = path.encode('utf-8')
+
+ return path
+
+
+def fsdecode(path):
+ "Path must be unicode or in file system encoding already"
+ encoding = sys.getfilesystemencoding()
+ if isinstance(path, bytes):
+ path = path.decode(encoding)
+ elif not isinstance(path, unicode):
+ raise TypeError('%s is not a byte type'
+ % type(path).__name__)
+
+ return unicodedata.normalize('NFC', path)
+
+
+def consoledecode(text):
+ encoding = locale.getpreferredencoding()
+ return text.decode(encoding)
+
+
+def parse_dir_entries(decoded_str):
+ '''Parse the entries from a recursive info xml'''
+ doc = xml.dom.pulldom.parseString(_get_xml_data(decoded_str))
+ entries = list()
+
+ for event, node in doc:
+ if event == 'START_ELEMENT' and node.nodeName == 'entry':
+ doc.expandNode(node)
+ if not _get_entry_schedule(node).startswith('delete'):
+ entries.append((node.getAttribute('path'),
+ node.getAttribute('kind')))
+
+ return entries[1:] # do not want the root directory
+
+
+def parse_externals_xml(decoded_str, prefix=''):
+ '''Parse a propget svn:externals xml'''
+ prefix = os.path.normpath(prefix)
+ prefix = os.path.normcase(prefix)
+
+ doc = xml.dom.pulldom.parseString(_get_xml_data(decoded_str))
+ externals = list()
+
+ for event, node in doc:
+ if event == 'START_ELEMENT' and node.nodeName == 'target':
+ doc.expandNode(node)
+ path = os.path.normpath(node.getAttribute('path'))
+
+ if os.path.normcase(path).startswith(prefix):
+ path = path[len(prefix)+1:]
+
+ data = _get_target_property(node)
+ for external in parse_external_prop(data):
+ externals.append(joinpath(path, external))
+
+ return externals # do not want the root directory
+
+
+def parse_external_prop(lines):
+ """
+ Parse the value of a retrieved svn:externals entry.
+
+ possible token setups (with quotng and backscaping in laters versions)
+ URL[@#] EXT_FOLDERNAME
+ [-r#] URL EXT_FOLDERNAME
+ EXT_FOLDERNAME [-r#] URL
+ """
+ externals = []
+ for line in lines.splitlines():
+ line = line.lstrip() # there might be a "\ "
+ if not line:
+ continue
+
+ if sys.version_info < (3, 0):
+ #shlex handles NULLs just fine and shlex in 2.7 tries to encode
+ #as ascii automatiically
+ line = line.encode('utf-8')
+ line = shlex.split(line)
+ if sys.version_info < (3, 0):
+ line = [x.decode('utf-8') for x in line]
+
+ #EXT_FOLDERNAME is either the first or last depending on where
+ #the URL falls
+ if urlparse.urlsplit(line[-1])[0]:
+ external = line[0]
+ else:
+ external = line[-1]
+
+ externals.append(os.path.normpath(external))
+
+ return externals
+
+
+def parse_prop_file(filename, key):
+ found = False
+ f = open(filename, 'rt')
+ data = ''
+ try:
+ for line in iter(f.readline, ''): # can't use direct iter!
+ parts = line.split()
+ if len(parts) == 2:
+ kind, length = parts
+ data = f.read(int(length))
+ if kind == 'K' and data == key:
+ found = True
+ elif kind == 'V' and found:
+ break
+ finally:
+ f.close()
+
+ return data
+
+
+class SvnInfo(object):
+ '''
+ Generic svn_info object. No has little knowledge of how to extract
+ information. Use cls.load to instatiate according svn version.
+
+ Paths are not filesystem encoded.
+ '''
+
+ @staticmethod
+ def get_svn_version():
+ code, data = _run_command(['svn', '--version', '--quiet'])
+ if code == 0 and data:
+ return unicode(data).strip()
+ else:
+ return unicode('')
+
+ #svnversion return values (previous implementations return max revision)
+ # 4123:4168 mixed revision working copy
+ # 4168M modified working copy
+ # 4123S switched working copy
+ # 4123:4168MS mixed revision, modified, switched working copy
+ revision_re = re.compile(r'(?:([\-0-9]+):)?(\d+)([a-z]*)\s*$', re.I)
+
+ @classmethod
+ def load(cls, dirname=''):
+ normdir = os.path.normpath(dirname)
+ code, data = _run_command(['svn', 'info', normdir])
+ has_svn = os.path.isdir(os.path.join(normdir, '.svn'))
+ svn_version = tuple(cls.get_svn_version().split('.'))
+
+ try:
+ base_svn_version = tuple(int(x) for x in svn_version[:2])
+ except ValueError:
+ base_svn_version = tuple()
+
+ if has_svn and (code or not base_svn_version
+ or base_svn_version < (1, 3)):
+ log.warn('Fallback onto .svn parsing')
+ warnings.warn(("No SVN 1.3+ command found: falling back "
+ "on pre 1.7 .svn parsing"), DeprecationWarning)
+ return SvnFileInfo(dirname)
+ elif not has_svn:
+ log.warn('Not SVN Repository')
+ return SvnInfo(dirname)
+ elif base_svn_version < (1, 5):
+ return Svn13Info(dirname)
+ else:
+ return Svn15Info(dirname)
+
+ def __init__(self, path=''):
+ self.path = path
+ self._entries = None
+ self._externals = None
+
+ def get_revision(self):
+ 'Retrieve the directory revision informatino using svnversion'
+ code, data = _run_command(['svnversion', '-c', self.path])
+ if code:
+ log.warn("svnversion failed")
+ return 0
+
+ parsed = self.revision_re.match(data)
+ if parsed:
+ return int(parsed.group(2))
+ else:
+ return 0
+
+ @property
+ def entries(self):
+ if self._entries is None:
+ self._entries = self.get_entries()
+ return self._entries
+
+ @property
+ def externals(self):
+ if self._externals is None:
+ self._externals = self.get_externals()
+ return self._externals
+
+ def iter_externals(self):
+ '''
+ Iterate over the svn:external references in the repository path.
+ '''
+ for item in self.externals:
+ yield item
+
+ def iter_files(self):
+ '''
+ Iterate over the non-deleted file entries in the repository path
+ '''
+ for item, kind in self.entries:
+ if kind.lower() == 'file':
+ yield item
+
+ def iter_dirs(self, include_root=True):
+ '''
+ Iterate over the non-deleted file entries in the repository path
+ '''
+ if include_root:
+ yield self.path
+ for item, kind in self.entries:
+ if kind.lower() == 'dir':
+ yield item
+
+ def get_entries(self):
+ return []
+
+ def get_externals(self):
+ return []
+
+
+class Svn13Info(SvnInfo):
+ def get_entries(self):
+ code, data = _run_command(['svn', 'info', '-R', '--xml', self.path])
+
+ if code:
+ log.debug("svn info failed")
+ return []
+
+ return parse_dir_entries(data)
+
+ def get_externals(self):
+ #Previous to 1.5 --xml was not supported for svn propget and the -R
+ #output format breaks the shlex compatible semantics.
+ cmd = ['svn', 'propget', 'svn:externals']
+ result = []
+ for folder in self.iter_dirs():
+ code, lines = _run_command(cmd + [folder])
+ if code != 0:
+ log.warn("svn propget failed")
+ return []
+ for external in parse_external_prop(lines):
+ if folder:
+ external = os.path.join(folder, external)
+ result.append(os.path.normpath(external))
+
+ return result
+
+
+class Svn15Info(Svn13Info):
+ def get_externals(self):
+ cmd = ['svn', 'propget', 'svn:externals', self.path, '-R', '--xml']
+ code, lines = _run_command(cmd)
+ if code:
+ log.debug("svn propget failed")
+ return []
+ return parse_externals_xml(lines, prefix=os.path.abspath(self.path))
+
+
+class SvnFileInfo(SvnInfo):
+
+ def __init__(self, path=''):
+ super(SvnFileInfo, self).__init__(path)
+ self._directories = None
+ self._revision = None
+
+ def _walk_svn(self, base):
+ entry_file = joinpath(base, '.svn', 'entries')
+ if os.path.isfile(entry_file):
+ entries = SVNEntriesFile.load(base)
+ yield (base, False, entries.parse_revision())
+ for path in entries.get_undeleted_records():
+ path = joinpath(base, path)
+ if os.path.isfile(path):
+ yield (path, True, None)
+ elif os.path.isdir(path):
+ for item in self._walk_svn(path):
+ yield item
+
+ def _build_entries(self):
+ dirs = list()
+ files = list()
+ rev = 0
+ for path, isfile, dir_rev in self._walk_svn(self.path):
+ if isfile:
+ files.append(path)
+ else:
+ dirs.append(path)
+ rev = max(rev, dir_rev)
+
+ self._directories = dirs
+ self._entries = files
+ self._revision = rev
+
+ def get_entries(self):
+ if self._entries is None:
+ self._build_entries()
+ return self._entries
+
+ def get_revision(self):
+ if self._revision is None:
+ self._build_entries()
+ return self._revision
+
+ def get_externals(self):
+ if self._directories is None:
+ self._build_entries()
+
+ prop_files = [['.svn', 'dir-prop-base'],
+ ['.svn', 'dir-props']]
+ externals = []
+
+ for dirname in self._directories:
+ prop_file = None
+ for rel_parts in prop_files:
+ filename = joinpath(dirname, *rel_parts)
+ if os.path.isfile(filename):
+ prop_file = filename
+
+ if prop_file is not None:
+ ext_prop = parse_prop_file(prop_file, 'svn:externals')
+ externals.extend(parse_external_prop(ext_prop))
+
+ return externals
+
+
+def svn_finder(dirname=''):
+ #combined externals due to common interface
+ #combined externals and entries due to lack of dir_props in 1.7
+ info = SvnInfo.load(dirname)
+ for path in info.iter_files():
+ yield fsencode(path)
+
+ for path in info.iter_externals():
+ sub_info = SvnInfo.load(path)
+ for sub_path in sub_info.iter_files():
+ yield fsencode(sub_path)
+
+
+class SVNEntriesFile(object):
+ def __init__(self, data):
+ self.data = data
+
+ @classmethod
+ def load(class_, base):
+ filename = os.path.join(base, '.svn', 'entries')
+ f = open(filename)
+ try:
+ result = SVNEntriesFile.read(f)
+ finally:
+ f.close()
+ return result
+
+ @classmethod
+ def read(class_, fileobj):
+ data = fileobj.read()
+ is_xml = data.startswith('<?xml')
+ class_ = [SVNEntriesFileText, SVNEntriesFileXML][is_xml]
+ return class_(data)
+
+ def parse_revision(self):
+ all_revs = self.parse_revision_numbers() + [0]
+ return max(all_revs)
+
+
+class SVNEntriesFileText(SVNEntriesFile):
+ known_svn_versions = {
+ '1.4.x': 8,
+ '1.5.x': 9,
+ '1.6.x': 10,
+ }
+
+ def __get_cached_sections(self):
+ return self.sections
+
+ def get_sections(self):
+ SECTION_DIVIDER = '\f\n'
+ sections = self.data.split(SECTION_DIVIDER)
+ sections = [x for x in map(str.splitlines, sections)]
+ try:
+ # remove the SVN version number from the first line
+ svn_version = int(sections[0].pop(0))
+ if not svn_version in self.known_svn_versions.values():
+ log.warn("Unknown subversion verson %d", svn_version)
+ except ValueError:
+ return
+ self.sections = sections
+ self.get_sections = self.__get_cached_sections
+ return self.sections
+
+ def is_valid(self):
+ return bool(self.get_sections())
+
+ def get_url(self):
+ return self.get_sections()[0][4]
+
+ def parse_revision_numbers(self):
+ revision_line_number = 9
+ rev_numbers = [
+ int(section[revision_line_number])
+ for section in self.get_sections()
+ if (len(section) > revision_line_number
+ and section[revision_line_number])
+ ]
+ return rev_numbers
+
+ def get_undeleted_records(self):
+ undeleted = lambda s: s and s[0] and (len(s) < 6 or s[5] != 'delete')
+ result = [
+ section[0]
+ for section in self.get_sections()
+ if undeleted(section)
+ ]
+ return result
+
+
+class SVNEntriesFileXML(SVNEntriesFile):
+ def is_valid(self):
+ return True
+
+ def get_url(self):
+ "Get repository URL"
+ urlre = re.compile('url="([^"]+)"')
+ return urlre.search(self.data).group(1)
+
+ def parse_revision_numbers(self):
+ revre = re.compile(r'committed-rev="(\d+)"')
+ return [
+ int(m.group(1))
+ for m in revre.finditer(self.data)
+ ]
+
+ def get_undeleted_records(self):
+ entries_pattern = \
+ re.compile(r'name="([^"]+)"(?![^>]+deleted="true")', re.I)
+ results = [
+ unescape(match.group(1))
+ for match in entries_pattern.finditer(self.data)
+ ]
+ return results
+
+
+if __name__ == '__main__':
+ for name in svn_finder(sys.argv[1]):
+ print(name)
diff --git a/setuptools/tests/__init__.py b/setuptools/tests/__init__.py index b6988a08..b5328ce6 100644 --- a/setuptools/tests/__init__.py +++ b/setuptools/tests/__init__.py @@ -2,14 +2,16 @@ import sys import os import unittest -import doctest +from setuptools.tests import doctest import distutils.core import distutils.cmd from distutils.errors import DistutilsOptionError, DistutilsPlatformError from distutils.errors import DistutilsSetupError from distutils.core import Extension from distutils.version import LooseVersion +from setuptools.compat import func_code +from setuptools.compat import func_code import setuptools.dist import setuptools.depends as dep from setuptools import Feature @@ -53,17 +55,18 @@ class DependsTests(unittest.TestCase): x = "test" y = z + fc = func_code(f1) # unrecognized name - self.assertEqual(dep.extract_constant(f1.func_code,'q', -1), None) + self.assertEqual(dep.extract_constant(fc,'q', -1), None) # constant assigned - self.assertEqual(dep.extract_constant(f1.func_code,'x', -1), "test") + self.assertEqual(dep.extract_constant(fc,'x', -1), "test") # expression assigned - self.assertEqual(dep.extract_constant(f1.func_code,'y', -1), -1) + self.assertEqual(dep.extract_constant(fc,'y', -1), -1) # recognized name, not assigned - self.assertEqual(dep.extract_constant(f1.func_code,'z', -1), None) + self.assertEqual(dep.extract_constant(fc,'z', -1), None) def testFindModule(self): self.assertRaises(ImportError, dep.find_module, 'no-such.-thing') diff --git a/setuptools/tests/doctest.py b/setuptools/tests/doctest.py index cc1e06c3..35d588d0 100644 --- a/setuptools/tests/doctest.py +++ b/setuptools/tests/doctest.py @@ -9,7 +9,7 @@ try: basestring except NameError: - basestring = str,unicode + basestring = str try: enumerate @@ -109,7 +109,7 @@ import __future__ import sys, traceback, inspect, linecache, os, re, types import unittest, difflib, pdb, tempfile import warnings -from StringIO import StringIO +from setuptools.compat import StringIO, execfile, exec_, func_code, im_func # Don't whine about the deprecated is_private function in this # module's tests. @@ -240,7 +240,7 @@ def _normalize_module(module, depth=2): """ if inspect.ismodule(module): return module - elif isinstance(module, (str, unicode)): + elif isinstance(module, basestring): return __import__(module, globals(), locals(), ["*"]) elif module is None: return sys.modules[sys._getframe(depth).f_globals['__name__']] @@ -367,9 +367,9 @@ class _OutputRedirectingPdb(pdb.Pdb): # [XX] Normalize with respect to os.path.pardir? def _module_relative_path(module, path): if not inspect.ismodule(module): - raise TypeError, 'Expected a module: %r' % module + raise TypeError('Expected a module: %r' % module) if path.startswith('/'): - raise ValueError, 'Module-relative files may not have absolute paths' + raise ValueError('Module-relative files may not have absolute paths') # Find the base directory for the path. if hasattr(module, '__file__'): @@ -877,7 +877,7 @@ class DocTestFinder: if module is None: return True elif inspect.isfunction(object): - return module.__dict__ is object.func_globals + return module.__dict__ is func_globals(object) elif inspect.isclass(object): return module.__name__ == object.__module__ elif inspect.getmodule(object) is not None: @@ -895,7 +895,7 @@ class DocTestFinder: add them to `tests`. """ if self._verbose: - print 'Finding tests in %s' % name + print('Finding tests in %s' % name) # If we've already processed this object, then ignore it. if id(obj) in seen: @@ -948,7 +948,7 @@ class DocTestFinder: if isinstance(val, staticmethod): val = getattr(obj, valname) if isinstance(val, classmethod): - val = getattr(obj, valname).im_func + val = im_func(getattr(obj, valname)) # Recurse to methods, properties, and nested classes. if ((inspect.isfunction(val) or inspect.isclass(val) or @@ -1020,8 +1020,8 @@ class DocTestFinder: break # Find the line number for functions & methods. - if inspect.ismethod(obj): obj = obj.im_func - if inspect.isfunction(obj): obj = obj.func_code + if inspect.ismethod(obj): obj = im_func(obj) + if inspect.isfunction(obj): obj = func_code(obj) if inspect.istraceback(obj): obj = obj.tb_frame if inspect.isframe(obj): obj = obj.f_code if inspect.iscode(obj): @@ -1250,8 +1250,8 @@ class DocTestRunner: # keyboard interrupts.) try: # Don't blink! This is where the user's code gets run. - exec compile(example.source, filename, "single", - compileflags, 1) in test.globs + exec_(compile(example.source, filename, "single", + compileflags, 1), test.globs) self.debugger.set_continue() # ==== Example Finished ==== exception = None except KeyboardInterrupt: @@ -1335,7 +1335,7 @@ class DocTestRunner: if m and m.group('name') == self.test.name: example = self.test.examples[int(m.group('examplenum'))] return example.source.splitlines(True) - elif self.save_linecache_getlines.func_code.co_argcount>1: + elif func_code(self.save_linecache_getlines).co_argcount > 1: return self.save_linecache_getlines(filename, module_globals) else: return self.save_linecache_getlines(filename) @@ -1427,28 +1427,28 @@ class DocTestRunner: failed.append(x) if verbose: if notests: - print len(notests), "items had no tests:" + print(len(notests), "items had no tests:") notests.sort() for thing in notests: - print " ", thing + print(" ", thing) if passed: - print len(passed), "items passed all tests:" + print(len(passed), "items passed all tests:") passed.sort() for thing, count in passed: - print " %3d tests in %s" % (count, thing) + print(" %3d tests in %s" % (count, thing)) if failed: - print self.DIVIDER - print len(failed), "items had failures:" + print(self.DIVIDER) + print(len(failed), "items had failures:") failed.sort() for thing, (f, t) in failed: - print " %3d of %3d in %s" % (f, t, thing) + print(" %3d of %3d in %s" % (f, t, thing)) if verbose: - print totalt, "tests in", len(self._name2ft), "items." - print totalt - totalf, "passed and", totalf, "failed." + print(totalt, "tests in", len(self._name2ft), "items.") + print(totalt - totalf, "passed and", totalf, "failed.") if totalf: - print "***Test Failed***", totalf, "failures." + print("***Test Failed***", totalf, "failures.") elif verbose: - print "Test passed." + print("Test passed.") return totalf, totalt #///////////////////////////////////////////////////////////////// @@ -1458,8 +1458,8 @@ class DocTestRunner: d = self._name2ft for name, (f, t) in other._name2ft.items(): if name in d: - print "*** DocTestRunner.merge: '" + name + "' in both" \ - " testers; summing outcomes." + print("*** DocTestRunner.merge: '" + name + "' in both" \ + " testers; summing outcomes.") f2, t2 = d[name] f = f + f2 t = t + t2 @@ -2039,10 +2039,10 @@ class Tester: def runstring(self, s, name): test = DocTestParser().get_doctest(s, self.globs, name, None, None) if self.verbose: - print "Running string", name + print("Running string", name) (f,t) = self.testrunner.run(test) if self.verbose: - print f, "of", t, "examples failed in string", name + print(f, "of", t, "examples failed in string", name) return (f,t) def rundoc(self, object, name=None, module=None): @@ -2556,7 +2556,7 @@ def debug_script(src, pm=False, globs=None): try: execfile(srcfilename, globs, globs) except: - print sys.exc_info()[1] + print(sys.exc_info()[1]) pdb.post_mortem(sys.exc_info()[2]) else: # Note that %r is vital here. '%s' instead can, e.g., cause diff --git a/setuptools/tests/environment.py b/setuptools/tests/environment.py new file mode 100644 index 00000000..7c754b8e --- /dev/null +++ b/setuptools/tests/environment.py @@ -0,0 +1,104 @@ +import os +import zipfile +import sys +import tempfile +import unittest +import shutil +import stat + + +def _extract(self, member, path=None, pwd=None): + """for zipfile py2.5 borrowed from cpython""" + if not isinstance(member, zipfile.ZipInfo): + member = self.getinfo(member) + + if path is None: + path = os.getcwd() + + return _extract_member(self, member, path, pwd) + + +def _extract_from_zip(self, name, dest_path): + dest_file = open(dest_path, 'wb') + try: + dest_file.write(self.read(name)) + finally: + dest_file.close() + +def _extract_member(self, member, targetpath, pwd): + """for zipfile py2.5 borrowed from cpython""" + # build the destination pathname, replacing + # forward slashes to platform specific separators. + # Strip trailing path separator, unless it represents the root. + if (targetpath[-1:] in (os.path.sep, os.path.altsep) + and len(os.path.splitdrive(targetpath)[1]) > 1): + targetpath = targetpath[:-1] + + # don't include leading "/" from file name if present + if member.filename[0] == '/': + targetpath = os.path.join(targetpath, member.filename[1:]) + else: + targetpath = os.path.join(targetpath, member.filename) + + targetpath = os.path.normpath(targetpath) + + # Create all upper directories if necessary. + upperdirs = os.path.dirname(targetpath) + if upperdirs and not os.path.exists(upperdirs): + os.makedirs(upperdirs) + + if member.filename[-1] == '/': + if not os.path.isdir(targetpath): + os.mkdir(targetpath) + return targetpath + + _extract_from_zip(self, member.filename, targetpath) + + return targetpath + + +def _remove_dir(target): + + #on windows this seems to a problem + for dir_path, dirs, files in os.walk(target): + os.chmod(dir_path, stat.S_IWRITE) + for filename in files: + os.chmod(os.path.join(dir_path, filename), stat.S_IWRITE) + shutil.rmtree(target) + + +class ZippedEnvironment(unittest.TestCase): + + datafile = None + dataname = None + old_cwd = None + + def setUp(self): + if not os.path.isfile(self.datafile): + self.old_cwd = None + return + + self.old_cwd = os.getcwd() + + self.temp_dir = tempfile.mkdtemp() + zip_file, source, target = [None, None, None] + try: + zip_file = zipfile.ZipFile(self.datafile) + for files in zip_file.namelist(): + _extract(zip_file, files, self.temp_dir) + finally: + if zip_file: + zip_file.close() + del zip_file + + os.chdir(os.path.join(self.temp_dir, self.dataname)) + + def tearDown(self): + try: + if self.old_cwd: + os.chdir(self.old_cwd) + _remove_dir(self.temp_dir) + except OSError: + #sigh? + pass + diff --git a/setuptools/tests/script-with-bom.py b/setuptools/tests/script-with-bom.py new file mode 100644 index 00000000..22dee0d2 --- /dev/null +++ b/setuptools/tests/script-with-bom.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +result = 'passed' diff --git a/setuptools/tests/server.py b/setuptools/tests/server.py index b2ab7acc..ae2381e3 100644 --- a/setuptools/tests/server.py +++ b/setuptools/tests/server.py @@ -1,12 +1,11 @@ """Basic http server for tests to simulate PyPI or custom indexes """ -import urllib2 import sys import time import threading -import BaseHTTPServer -from BaseHTTPServer import HTTPServer -from SimpleHTTPServer import SimpleHTTPRequestHandler +from setuptools.compat import BaseHTTPRequestHandler +from setuptools.compat import (urllib2, URLError, HTTPServer, + SimpleHTTPRequestHandler) class IndexServer(HTTPServer): """Basic single-threaded http server simulating a package index @@ -48,16 +47,17 @@ class IndexServer(HTTPServer): urllib2.urlopen(url, timeout=5) else: urllib2.urlopen(url) - except urllib2.URLError: + except URLError: # ignore any errors; all that's important is the request pass self.thread.join() + self.socket.close() def base_url(self): port = self.server_port return 'http://127.0.0.1:%s/setuptools/tests/indexes/' % port -class RequestRecorder(BaseHTTPServer.BaseHTTPRequestHandler): +class RequestRecorder(BaseHTTPRequestHandler): def do_GET(self): requests = vars(self.server).setdefault('requests', []) requests.append(self) diff --git a/setuptools/tests/svn17_example.zip b/setuptools/tests/svn17_example.zip Binary files differnew file mode 100644 index 00000000..cfabd2b2 --- /dev/null +++ b/setuptools/tests/svn17_example.zip diff --git a/setuptools/tests/svn_data/svn13_example.zip b/setuptools/tests/svn_data/svn13_example.zip Binary files differnew file mode 100644 index 00000000..d85fb84f --- /dev/null +++ b/setuptools/tests/svn_data/svn13_example.zip diff --git a/setuptools/tests/svn_data/svn13_ext_list.txt b/setuptools/tests/svn_data/svn13_ext_list.txt new file mode 100644 index 00000000..0bb0f438 --- /dev/null +++ b/setuptools/tests/svn_data/svn13_ext_list.txt @@ -0,0 +1,3 @@ +third_party3 file:///C:/development/svn_example/repos/svn13/extra1
+third_party2 -r3 file:///C:/development/svn_example/repos/svn13/extra1
+third_party -r1 file:///C:/development/svn_example/repos/svn13/extra1
diff --git a/setuptools/tests/svn_data/svn13_ext_list.xml b/setuptools/tests/svn_data/svn13_ext_list.xml new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/setuptools/tests/svn_data/svn13_ext_list.xml diff --git a/setuptools/tests/svn_data/svn13_info.xml b/setuptools/tests/svn_data/svn13_info.xml new file mode 100644 index 00000000..5c96520a --- /dev/null +++ b/setuptools/tests/svn_data/svn13_info.xml @@ -0,0 +1,121 @@ +<?xml version="1.0" encoding="utf-8"?>
+<info>
+<entry
+ kind="dir"
+ path="svn13_example"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn13/main</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn13/main</root>
+<uuid>d2996769-47b0-9946-b618-da1aa3eceda3</uuid>
+</repository>
+<wc-info>
+<schedule>normal</schedule>
+<prop-updated>2013-07-13T15:33:23.187500Z</prop-updated>
+</wc-info>
+<commit
+ revision="6">
+<author>ptt</author>
+<date>2013-07-13T15:33:28.359375Z</date>
+</commit>
+</entry>
+<entry
+ kind="file"
+ path="svn13_example\a file"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn13/main/a%20file</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn13/main</root>
+<uuid>d2996769-47b0-9946-b618-da1aa3eceda3</uuid>
+</repository>
+<wc-info>
+<schedule>normal</schedule>
+<text-updated>2013-07-13T15:33:21.109375Z</text-updated>
+<checksum>a6166e5e98a5a503089cde9bc8031293</checksum>
+</wc-info>
+<commit
+ revision="3">
+<author>ptt</author>
+<date>2013-07-13T15:33:21.312500Z</date>
+</commit>
+</entry>
+<entry
+ kind="file"
+ path="svn13_example\to_delete"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn13/main/to_delete</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn13/main</root>
+<uuid>d2996769-47b0-9946-b618-da1aa3eceda3</uuid>
+</repository>
+<wc-info>
+<schedule>delete</schedule>
+<text-updated>2013-07-13T15:33:28.140625Z</text-updated>
+<checksum>d41d8cd98f00b204e9800998ecf8427e</checksum>
+</wc-info>
+<commit
+ revision="6">
+<author>ptt</author>
+<date>2013-07-13T15:33:28.359375Z</date>
+</commit>
+</entry>
+<entry
+ kind="dir"
+ path="svn13_example\folder"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn13/main/folder</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn13/main</root>
+<uuid>d2996769-47b0-9946-b618-da1aa3eceda3</uuid>
+</repository>
+<wc-info>
+<schedule>normal</schedule>
+<prop-updated>2013-07-13T15:33:26.187500Z</prop-updated>
+</wc-info>
+<commit
+ revision="5">
+<author>ptt</author>
+<date>2013-07-13T15:33:26.312500Z</date>
+</commit>
+</entry>
+<entry
+ kind="file"
+ path="svn13_example\folder\quest.txt"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn13/main/folder/quest.txt</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn13/main</root>
+<uuid>d2996769-47b0-9946-b618-da1aa3eceda3</uuid>
+</repository>
+<wc-info>
+<schedule>normal</schedule>
+<text-updated>2013-07-13T15:33:20.109375Z</text-updated>
+<checksum>795240c6a830c14f83961e57e07dad12</checksum>
+</wc-info>
+<commit
+ revision="2">
+<author>ptt</author>
+<date>2013-07-13T15:33:20.312500Z</date>
+</commit>
+</entry>
+<entry
+ kind="file"
+ path="svn13_example\folder\lalala.txt"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn13/main/folder/lalala.txt</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn13/main</root>
+<uuid>d2996769-47b0-9946-b618-da1aa3eceda3</uuid>
+</repository>
+<wc-info>
+<schedule>normal</schedule>
+<text-updated>2013-07-13T15:33:19.375000Z</text-updated>
+<checksum>d41d8cd98f00b204e9800998ecf8427e</checksum>
+</wc-info>
+<commit
+ revision="1">
+<author>ptt</author>
+<date>2013-07-13T15:33:19.609375Z</date>
+</commit>
+</entry>
+</info>
diff --git a/setuptools/tests/svn_data/svn14_example.zip b/setuptools/tests/svn_data/svn14_example.zip Binary files differnew file mode 100644 index 00000000..57093c0b --- /dev/null +++ b/setuptools/tests/svn_data/svn14_example.zip diff --git a/setuptools/tests/svn_data/svn14_ext_list.txt b/setuptools/tests/svn_data/svn14_ext_list.txt new file mode 100644 index 00000000..800a0965 --- /dev/null +++ b/setuptools/tests/svn_data/svn14_ext_list.txt @@ -0,0 +1,4 @@ +third_party3 file:///C:/development/svn_example/repos/svn13/extra1
+third_party2 -r3 file:///C:/development/svn_example/repos/svn13/extra1
+third_party -r1 file:///C:/development/svn_example/repos/svn13/extra1
+
diff --git a/setuptools/tests/svn_data/svn14_ext_list.xml b/setuptools/tests/svn_data/svn14_ext_list.xml new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/setuptools/tests/svn_data/svn14_ext_list.xml diff --git a/setuptools/tests/svn_data/svn14_info.xml b/setuptools/tests/svn_data/svn14_info.xml new file mode 100644 index 00000000..a896a77f --- /dev/null +++ b/setuptools/tests/svn_data/svn14_info.xml @@ -0,0 +1,119 @@ +<?xml version="1.0"?>
+<info>
+<entry
+ kind="dir"
+ path="svn14_example"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn14/main</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn14/main</root>
+<uuid>c75942e5-8b7a-354d-b1cf-73dee23fa94f</uuid>
+</repository>
+<wc-info>
+<schedule>normal</schedule>
+</wc-info>
+<commit
+ revision="6">
+<author>ptt</author>
+<date>2013-07-13T15:34:14.406250Z</date>
+</commit>
+</entry>
+<entry
+ kind="file"
+ path="svn14_example\a file"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn14/main/a%20file</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn14/main</root>
+<uuid>c75942e5-8b7a-354d-b1cf-73dee23fa94f</uuid>
+</repository>
+<wc-info>
+<schedule>normal</schedule>
+<text-updated>2013-07-13T15:34:08.109375Z</text-updated>
+<checksum>a6166e5e98a5a503089cde9bc8031293</checksum>
+</wc-info>
+<commit
+ revision="3">
+<author>ptt</author>
+<date>2013-07-13T15:34:08.390625Z</date>
+</commit>
+</entry>
+<entry
+ kind="file"
+ path="svn14_example\to_delete"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn14/main/to_delete</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn14/main</root>
+<uuid>c75942e5-8b7a-354d-b1cf-73dee23fa94f</uuid>
+</repository>
+<wc-info>
+<schedule>delete</schedule>
+<text-updated>2013-07-13T15:34:14.125000Z</text-updated>
+<checksum>d41d8cd98f00b204e9800998ecf8427e</checksum>
+</wc-info>
+<commit
+ revision="6">
+<author>ptt</author>
+<date>2013-07-13T15:34:14.406250Z</date>
+</commit>
+</entry>
+<entry
+ kind="dir"
+ path="svn14_example\folder"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn14/main/folder</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn14/main</root>
+<uuid>c75942e5-8b7a-354d-b1cf-73dee23fa94f</uuid>
+</repository>
+<wc-info>
+<schedule>normal</schedule>
+</wc-info>
+<commit
+ revision="5">
+<author>ptt</author>
+<date>2013-07-13T15:34:12.390625Z</date>
+</commit>
+</entry>
+<entry
+ kind="file"
+ path="svn14_example\folder\quest.txt"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn14/main/folder/quest.txt</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn14/main</root>
+<uuid>c75942e5-8b7a-354d-b1cf-73dee23fa94f</uuid>
+</repository>
+<wc-info>
+<schedule>normal</schedule>
+<text-updated>2013-07-13T15:34:07.109375Z</text-updated>
+<checksum>795240c6a830c14f83961e57e07dad12</checksum>
+</wc-info>
+<commit
+ revision="2">
+<author>ptt</author>
+<date>2013-07-13T15:34:07.390625Z</date>
+</commit>
+</entry>
+<entry
+ kind="file"
+ path="svn14_example\folder\lalala.txt"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn14/main/folder/lalala.txt</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn14/main</root>
+<uuid>c75942e5-8b7a-354d-b1cf-73dee23fa94f</uuid>
+</repository>
+<wc-info>
+<schedule>normal</schedule>
+<text-updated>2013-07-13T15:34:06.250000Z</text-updated>
+<checksum>d41d8cd98f00b204e9800998ecf8427e</checksum>
+</wc-info>
+<commit
+ revision="1">
+<author>ptt</author>
+<date>2013-07-13T15:34:06.531250Z</date>
+</commit>
+</entry>
+</info>
diff --git a/setuptools/tests/svn_data/svn15_example.zip b/setuptools/tests/svn_data/svn15_example.zip Binary files differnew file mode 100644 index 00000000..52a1d45b --- /dev/null +++ b/setuptools/tests/svn_data/svn15_example.zip diff --git a/setuptools/tests/svn_data/svn15_ext_list.txt b/setuptools/tests/svn_data/svn15_ext_list.txt new file mode 100644 index 00000000..75fde4e6 --- /dev/null +++ b/setuptools/tests/svn_data/svn15_ext_list.txt @@ -0,0 +1,4 @@ +third_party3 file:///C:/development/svn_example/repos/svn15/extra1
+-r3 file:///C:/development/svn_example/repos/svn15/extra1 third_party2
+file:///C:/development/svn_example/repos/svn15/extra1@r1 third_party
+
diff --git a/setuptools/tests/svn_data/svn15_ext_list.xml b/setuptools/tests/svn_data/svn15_ext_list.xml new file mode 100644 index 00000000..6950b3c5 --- /dev/null +++ b/setuptools/tests/svn_data/svn15_ext_list.xml @@ -0,0 +1,19 @@ +<?xml version="1.0"?>
+<properties>
+<target
+ path="C:/development/svn_example/svn15_example/folder">
+<property
+ name="svn:externals">third_party3 file:///C:/development/svn_example/repos/svn15/extra2
+-r3 file:///C:/development/svn_example/repos/svn15/extra2 third_party2
+file:///C:/development/svn_example/repos/svn15/extra2@r1 third_party大介
+</property>
+</target>
+<target
+ path="C:/development/svn_example/svn15_example">
+<property
+ name="svn:externals">third_party3 file:///C:/development/svn_example/repos/svn15/extra1
+-r3 file:///C:/development/svn_example/repos/svn15/extra1 third_party2
+file:///C:/development/svn_example/repos/svn15/extra1@r1 third_party大介
+</property>
+</target>
+</properties>
diff --git a/setuptools/tests/svn_data/svn15_info.xml b/setuptools/tests/svn_data/svn15_info.xml new file mode 100644 index 00000000..0b3550af --- /dev/null +++ b/setuptools/tests/svn_data/svn15_info.xml @@ -0,0 +1,125 @@ +<?xml version="1.0"?>
+<info>
+<entry
+ kind="dir"
+ path="svn15_example"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn15/main</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn15/main</root>
+<uuid>4eab6983-54fe-384b-a282-9306f52d948f</uuid>
+</repository>
+<wc-info>
+<schedule>normal</schedule>
+<depth>infinity</depth>
+</wc-info>
+<commit
+ revision="6">
+<author>ptt</author>
+<date>2013-07-13T15:34:49.562500Z</date>
+</commit>
+</entry>
+<entry
+ kind="file"
+ path="svn15_example\a file"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn15/main/a%20file</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn15/main</root>
+<uuid>4eab6983-54fe-384b-a282-9306f52d948f</uuid>
+</repository>
+<wc-info>
+<schedule>normal</schedule>
+<depth>infinity</depth>
+<text-updated>2013-07-13T15:34:43.109375Z</text-updated>
+<checksum>a6166e5e98a5a503089cde9bc8031293</checksum>
+</wc-info>
+<commit
+ revision="3">
+<author>ptt</author>
+<date>2013-07-13T15:34:43.484375Z</date>
+</commit>
+</entry>
+<entry
+ kind="file"
+ path="svn15_example\to_delete"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn15/main/to_delete</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn15/main</root>
+<uuid>4eab6983-54fe-384b-a282-9306f52d948f</uuid>
+</repository>
+<wc-info>
+<schedule>delete</schedule>
+<depth>infinity</depth>
+<text-updated>2013-07-13T15:34:49.125000Z</text-updated>
+<checksum>d41d8cd98f00b204e9800998ecf8427e</checksum>
+</wc-info>
+<commit
+ revision="6">
+<author>ptt</author>
+<date>2013-07-13T15:34:49.562500Z</date>
+</commit>
+</entry>
+<entry
+ kind="dir"
+ path="svn15_example\folder"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn15/main/folder</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn15/main</root>
+<uuid>4eab6983-54fe-384b-a282-9306f52d948f</uuid>
+</repository>
+<wc-info>
+<schedule>normal</schedule>
+<depth>infinity</depth>
+</wc-info>
+<commit
+ revision="5">
+<author>ptt</author>
+<date>2013-07-13T15:34:47.515625Z</date>
+</commit>
+</entry>
+<entry
+ kind="file"
+ path="svn15_example\folder\quest.txt"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn15/main/folder/quest.txt</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn15/main</root>
+<uuid>4eab6983-54fe-384b-a282-9306f52d948f</uuid>
+</repository>
+<wc-info>
+<schedule>normal</schedule>
+<depth>infinity</depth>
+<text-updated>2013-07-13T15:34:42.109375Z</text-updated>
+<checksum>795240c6a830c14f83961e57e07dad12</checksum>
+</wc-info>
+<commit
+ revision="2">
+<author>ptt</author>
+<date>2013-07-13T15:34:42.484375Z</date>
+</commit>
+</entry>
+<entry
+ kind="file"
+ path="svn15_example\folder\lalala.txt"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn15/main/folder/lalala.txt</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn15/main</root>
+<uuid>4eab6983-54fe-384b-a282-9306f52d948f</uuid>
+</repository>
+<wc-info>
+<schedule>normal</schedule>
+<depth>infinity</depth>
+<text-updated>2013-07-13T15:34:41.375000Z</text-updated>
+<checksum>d41d8cd98f00b204e9800998ecf8427e</checksum>
+</wc-info>
+<commit
+ revision="1">
+<author>ptt</author>
+<date>2013-07-13T15:34:41.734375Z</date>
+</commit>
+</entry>
+</info>
diff --git a/setuptools/tests/svn_data/svn16_example.zip b/setuptools/tests/svn_data/svn16_example.zip Binary files differnew file mode 100644 index 00000000..e886b2af --- /dev/null +++ b/setuptools/tests/svn_data/svn16_example.zip diff --git a/setuptools/tests/svn_data/svn16_ext_list.txt b/setuptools/tests/svn_data/svn16_ext_list.txt new file mode 100644 index 00000000..3ca54893 --- /dev/null +++ b/setuptools/tests/svn_data/svn16_ext_list.txt @@ -0,0 +1,4 @@ +"third party3" file:///C:/development/svn_example/repos/svn16/extra1
+'third party3b' file:///C:/development/svn_example/repos/svn16/extra1
+-r3 file:///C:/development/svn_example/repos/svn16/extra1 third\ party2
+file:///C:/development/svn_example/repos/svn16/extra1@r1 third_party
diff --git a/setuptools/tests/svn_data/svn16_ext_list.xml b/setuptools/tests/svn_data/svn16_ext_list.xml new file mode 100644 index 00000000..8ddaed0a --- /dev/null +++ b/setuptools/tests/svn_data/svn16_ext_list.xml @@ -0,0 +1,19 @@ +<?xml version="1.0"?>
+<properties>
+<target
+ path="C:/development/svn_example/svn16_example/folder">
+<property
+ name="svn:externals">"third party3" file:///C:/development/svn_example/repos/svn16/extra2
+-r3 file:///C:/development/svn_example/repos/svn16/extra2 third\ party2
+file:///C:/development/svn_example/repos/svn16/extra2@r1 third_party大介
+</property>
+</target>
+<target
+ path="C:/development/svn_example/svn16_example">
+<property
+ name="svn:externals">"third party3" file:///C:/development/svn_example/repos/svn16/extra1
+-r3 file:///C:/development/svn_example/repos/svn16/extra1 third\ party2
+file:///C:/development/svn_example/repos/svn16/extra1@r1 third_party大介
+</property>
+</target>
+</properties>
diff --git a/setuptools/tests/svn_data/svn16_info.xml b/setuptools/tests/svn_data/svn16_info.xml new file mode 100644 index 00000000..745469c9 --- /dev/null +++ b/setuptools/tests/svn_data/svn16_info.xml @@ -0,0 +1,125 @@ +<?xml version="1.0"?>
+<info>
+<entry
+ kind="dir"
+ path="svn16_example"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn16/main</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn16/main</root>
+<uuid>bd8d2cfc-1a74-de45-b166-262010c17c0a</uuid>
+</repository>
+<wc-info>
+<schedule>normal</schedule>
+<depth>infinity</depth>
+</wc-info>
+<commit
+ revision="6">
+<author>ptt</author>
+<date>2013-07-13T15:35:17.390625Z</date>
+</commit>
+</entry>
+<entry
+ kind="file"
+ path="svn16_example\a file"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn16/main/a%20file</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn16/main</root>
+<uuid>bd8d2cfc-1a74-de45-b166-262010c17c0a</uuid>
+</repository>
+<wc-info>
+<schedule>normal</schedule>
+<depth>infinity</depth>
+<text-updated>2013-07-13T15:35:14.578125Z</text-updated>
+<checksum>a6166e5e98a5a503089cde9bc8031293</checksum>
+</wc-info>
+<commit
+ revision="3">
+<author>ptt</author>
+<date>2013-07-13T15:35:14.906250Z</date>
+</commit>
+</entry>
+<entry
+ kind="file"
+ path="svn16_example\to_delete"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn16/main/to_delete</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn16/main</root>
+<uuid>bd8d2cfc-1a74-de45-b166-262010c17c0a</uuid>
+</repository>
+<wc-info>
+<schedule>delete</schedule>
+<depth>infinity</depth>
+<text-updated>2013-07-13T15:35:17.046875Z</text-updated>
+<checksum>d41d8cd98f00b204e9800998ecf8427e</checksum>
+</wc-info>
+<commit
+ revision="6">
+<author>ptt</author>
+<date>2013-07-13T15:35:17.390625Z</date>
+</commit>
+</entry>
+<entry
+ kind="dir"
+ path="svn16_example\folder"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn16/main/folder</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn16/main</root>
+<uuid>bd8d2cfc-1a74-de45-b166-262010c17c0a</uuid>
+</repository>
+<wc-info>
+<schedule>normal</schedule>
+<depth>infinity</depth>
+</wc-info>
+<commit
+ revision="5">
+<author>ptt</author>
+<date>2013-07-13T15:35:16.406250Z</date>
+</commit>
+</entry>
+<entry
+ kind="file"
+ path="svn16_example\folder\quest.txt"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn16/main/folder/quest.txt</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn16/main</root>
+<uuid>bd8d2cfc-1a74-de45-b166-262010c17c0a</uuid>
+</repository>
+<wc-info>
+<schedule>normal</schedule>
+<depth>infinity</depth>
+<text-updated>2013-07-13T15:35:14.078125Z</text-updated>
+<checksum>795240c6a830c14f83961e57e07dad12</checksum>
+</wc-info>
+<commit
+ revision="2">
+<author>ptt</author>
+<date>2013-07-13T15:35:14.421875Z</date>
+</commit>
+</entry>
+<entry
+ kind="file"
+ path="svn16_example\folder\lalala.txt"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn16/main/folder/lalala.txt</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn16/main</root>
+<uuid>bd8d2cfc-1a74-de45-b166-262010c17c0a</uuid>
+</repository>
+<wc-info>
+<schedule>normal</schedule>
+<depth>infinity</depth>
+<text-updated>2013-07-13T15:35:12.171875Z</text-updated>
+<checksum>d41d8cd98f00b204e9800998ecf8427e</checksum>
+</wc-info>
+<commit
+ revision="1">
+<author>ptt</author>
+<date>2013-07-13T15:35:13.906250Z</date>
+</commit>
+</entry>
+</info>
diff --git a/setuptools/tests/svn_data/svn17_example.zip b/setuptools/tests/svn_data/svn17_example.zip Binary files differnew file mode 100644 index 00000000..ba0e8823 --- /dev/null +++ b/setuptools/tests/svn_data/svn17_example.zip diff --git a/setuptools/tests/svn_data/svn17_ext_list.txt b/setuptools/tests/svn_data/svn17_ext_list.txt new file mode 100644 index 00000000..a8b832a8 --- /dev/null +++ b/setuptools/tests/svn_data/svn17_ext_list.txt @@ -0,0 +1,4 @@ +"third party3" file:///C:/development/svn_example/repos/svn17/extra1
+'third party3b' file:///C:/development/svn_example/repos/svn17/extra1
+-r3 file:///C:/development/svn_example/repos/svn17/extra1 third\ party2
+file:///C:/development/svn_example/repos/svn17/extra1@r1 third_party
diff --git a/setuptools/tests/svn_data/svn17_ext_list.xml b/setuptools/tests/svn_data/svn17_ext_list.xml new file mode 100644 index 00000000..2879bb65 --- /dev/null +++ b/setuptools/tests/svn_data/svn17_ext_list.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?>
+<properties>
+<target
+ path="C:/development/svn_example/svn17_example">
+<property
+ name="svn:externals">"third party3" file:///C:/development/svn_example/repos/svn16/extra1
+-r3 file:///C:/development/svn_example/repos/svn16/extra1 third\ party2
+file:///C:/development/svn_example/repos/svn16/extra1@r1 third_party大介
+</property>
+</target>
+<target
+ path="C:/development/svn_example/svn17_example/folder">
+<property
+ name="svn:externals">"third party3" file:///C:/development/svn_example/repos/svn17/extra2
+-r3 file:///C:/development/svn_example/repos/svn17/extra2 third\ party2
+file:///C:/development/svn_example/repos/svn17/extra2@r1 third_party大介
+</property>
+</target>
+</properties>
diff --git a/setuptools/tests/svn_data/svn17_info.xml b/setuptools/tests/svn_data/svn17_info.xml new file mode 100644 index 00000000..6cffeffd --- /dev/null +++ b/setuptools/tests/svn_data/svn17_info.xml @@ -0,0 +1,130 @@ +<?xml version="1.0" encoding="UTF-8"?>
+<info>
+<entry
+ kind="dir"
+ path="svn17_example"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn17/main</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn17/main</root>
+<uuid>5ba45434-5197-164e-afab-81923f4744f5</uuid>
+</repository>
+<wc-info>
+<wcroot-abspath>C:/development/svn_example/svn17_example</wcroot-abspath>
+<schedule>normal</schedule>
+<depth>infinity</depth>
+</wc-info>
+<commit
+ revision="6">
+<author>ptt</author>
+<date>2013-07-13T15:35:36.171875Z</date>
+</commit>
+</entry>
+<entry
+ path="svn17_example\folder"
+ revision="6"
+ kind="dir">
+<url>file:///C:/development/svn_example/repos/svn17/main/folder</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn17/main</root>
+<uuid>5ba45434-5197-164e-afab-81923f4744f5</uuid>
+</repository>
+<wc-info>
+<wcroot-abspath>C:/development/svn_example/svn17_example</wcroot-abspath>
+<schedule>normal</schedule>
+<depth>infinity</depth>
+</wc-info>
+<commit
+ revision="5">
+<author>ptt</author>
+<date>2013-07-13T15:35:34.859375Z</date>
+</commit>
+</entry>
+<entry
+ kind="file"
+ path="svn17_example\folder\quest.txt"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn17/main/folder/quest.txt</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn17/main</root>
+<uuid>5ba45434-5197-164e-afab-81923f4744f5</uuid>
+</repository>
+<wc-info>
+<wcroot-abspath>C:/development/svn_example/svn17_example</wcroot-abspath>
+<schedule>normal</schedule>
+<depth>infinity</depth>
+<text-updated>2013-07-13T15:35:32.812500Z</text-updated>
+<checksum>bc80eba9e7a10c0a571a4678c520bc9683f3bac2</checksum>
+</wc-info>
+<commit
+ revision="2">
+<author>ptt</author>
+<date>2013-07-13T15:35:33.109375Z</date>
+</commit>
+</entry>
+<entry
+ kind="file"
+ path="svn17_example\folder\lalala.txt"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn17/main/folder/lalala.txt</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn17/main</root>
+<uuid>5ba45434-5197-164e-afab-81923f4744f5</uuid>
+</repository>
+<wc-info>
+<wcroot-abspath>C:/development/svn_example/svn17_example</wcroot-abspath>
+<schedule>normal</schedule>
+<depth>infinity</depth>
+<text-updated>2013-07-13T15:35:32.343750Z</text-updated>
+<checksum>da39a3ee5e6b4b0d3255bfef95601890afd80709</checksum>
+</wc-info>
+<commit
+ revision="1">
+<author>ptt</author>
+<date>2013-07-13T15:35:32.687500Z</date>
+</commit>
+</entry>
+<entry
+ path="svn17_example\a file"
+ revision="6"
+ kind="file">
+<url>file:///C:/development/svn_example/repos/svn17/main/a%20file</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn17/main</root>
+<uuid>5ba45434-5197-164e-afab-81923f4744f5</uuid>
+</repository>
+<wc-info>
+<wcroot-abspath>C:/development/svn_example/svn17_example</wcroot-abspath>
+<schedule>normal</schedule>
+<depth>infinity</depth>
+<text-updated>2013-07-13T15:35:33.187500Z</text-updated>
+<checksum>43785ab4b1816b49f242990883292813cd4f486c</checksum>
+</wc-info>
+<commit
+ revision="3">
+<author>ptt</author>
+<date>2013-07-13T15:35:33.515625Z</date>
+</commit>
+</entry>
+<entry
+ path="svn17_example\to_delete"
+ revision="6"
+ kind="file">
+<url>file:///C:/development/svn_example/repos/svn17/main/to_delete</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn17/main</root>
+<uuid>5ba45434-5197-164e-afab-81923f4744f5</uuid>
+</repository>
+<wc-info>
+<wcroot-abspath>C:/development/svn_example/svn17_example</wcroot-abspath>
+<schedule>delete</schedule>
+<depth>infinity</depth>
+<checksum>da39a3ee5e6b4b0d3255bfef95601890afd80709</checksum>
+</wc-info>
+<commit
+ revision="6">
+<author>ptt</author>
+<date>2013-07-13T15:35:36.171875Z</date>
+</commit>
+</entry>
+</info>
diff --git a/setuptools/tests/svn_data/svn18_example.zip b/setuptools/tests/svn_data/svn18_example.zip Binary files differnew file mode 100644 index 00000000..4362f8e9 --- /dev/null +++ b/setuptools/tests/svn_data/svn18_example.zip diff --git a/setuptools/tests/svn_data/svn18_ext_list.txt b/setuptools/tests/svn_data/svn18_ext_list.txt new file mode 100644 index 00000000..c90a5f11 --- /dev/null +++ b/setuptools/tests/svn_data/svn18_ext_list.txt @@ -0,0 +1,4 @@ +"third party3" file:///C:/development/svn_example/repos/svn18/extra1
+'third party3b' file:///C:/development/svn_example/repos/svn18/extra1
+-r3 file:///C:/development/svn_example/repos/svn18/extra1 third\ party2
+file:///C:/development/svn_example/repos/svn18/extra1@r1 third_party
diff --git a/setuptools/tests/svn_data/svn18_ext_list.xml b/setuptools/tests/svn_data/svn18_ext_list.xml new file mode 100644 index 00000000..9b5e9e96 --- /dev/null +++ b/setuptools/tests/svn_data/svn18_ext_list.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?>
+<properties>
+<target
+ path="C:/development/svn_example/svn18_example">
+<property
+ name="svn:externals">"third party3" file:///C:/development/svn_example/repos/svn16/extra1
+-r3 file:///C:/development/svn_example/repos/svn16/extra1 third\ party2
+file:///C:/development/svn_example/repos/svn16/extra1@r1 third_party大介
+</property>
+</target>
+<target
+ path="C:/development/svn_example/svn18_example/folder">
+<property
+ name="svn:externals">"third party3" file:///C:/development/svn_example/repos/svn18/extra2
+-r3 file:///C:/development/svn_example/repos/svn18/extra2 third\ party2
+file:///C:/development/svn_example/repos/svn18/extra2@r1 third_party大介
+</property>
+</target>
+</properties>
diff --git a/setuptools/tests/svn_data/svn18_info.xml b/setuptools/tests/svn_data/svn18_info.xml new file mode 100644 index 00000000..7ca55995 --- /dev/null +++ b/setuptools/tests/svn_data/svn18_info.xml @@ -0,0 +1,136 @@ +<?xml version="1.0" encoding="UTF-8"?>
+<info>
+<entry
+ path="svn18_example"
+ revision="6"
+ kind="dir">
+<url>file:///C:/development/svn_example/repos/svn18/main</url>
+<relative-url>^/</relative-url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn18/main</root>
+<uuid>3c5e3929-c92b-7045-9ba9-5e65d3dd1ee9</uuid>
+</repository>
+<wc-info>
+<wcroot-abspath>C:/development/svn_example/svn18_example</wcroot-abspath>
+<schedule>normal</schedule>
+<depth>infinity</depth>
+</wc-info>
+<commit
+ revision="6">
+<author>ptt</author>
+<date>2013-07-13T15:35:57.796875Z</date>
+</commit>
+</entry>
+<entry
+ kind="file"
+ path="svn18_example\a file"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn18/main/a%20file</url>
+<relative-url>^/a%20file</relative-url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn18/main</root>
+<uuid>3c5e3929-c92b-7045-9ba9-5e65d3dd1ee9</uuid>
+</repository>
+<wc-info>
+<wcroot-abspath>C:/development/svn_example/svn18_example</wcroot-abspath>
+<schedule>normal</schedule>
+<depth>infinity</depth>
+<text-updated>2013-07-13T15:35:54.906250Z</text-updated>
+<checksum>43785ab4b1816b49f242990883292813cd4f486c</checksum>
+</wc-info>
+<commit
+ revision="3">
+<author>ptt</author>
+<date>2013-07-13T15:35:55.265625Z</date>
+</commit>
+</entry>
+<entry
+ kind="file"
+ path="svn18_example\to_delete"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn18/main/to_delete</url>
+<relative-url>^/to_delete</relative-url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn18/main</root>
+<uuid>3c5e3929-c92b-7045-9ba9-5e65d3dd1ee9</uuid>
+</repository>
+<wc-info>
+<wcroot-abspath>C:/development/svn_example/svn18_example</wcroot-abspath>
+<schedule>delete</schedule>
+<depth>infinity</depth>
+<checksum>da39a3ee5e6b4b0d3255bfef95601890afd80709</checksum>
+</wc-info>
+<commit
+ revision="6">
+<author>ptt</author>
+<date>2013-07-13T15:35:57.796875Z</date>
+</commit>
+</entry>
+<entry
+ kind="dir"
+ path="svn18_example\folder"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn18/main/folder</url>
+<relative-url>^/folder</relative-url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn18/main</root>
+<uuid>3c5e3929-c92b-7045-9ba9-5e65d3dd1ee9</uuid>
+</repository>
+<wc-info>
+<wcroot-abspath>C:/development/svn_example/svn18_example</wcroot-abspath>
+<schedule>normal</schedule>
+<depth>infinity</depth>
+</wc-info>
+<commit
+ revision="5">
+<author>ptt</author>
+<date>2013-07-13T15:35:56.750000Z</date>
+</commit>
+</entry>
+<entry
+ path="svn18_example\folder\quest.txt"
+ revision="6"
+ kind="file">
+<url>file:///C:/development/svn_example/repos/svn18/main/folder/quest.txt</url>
+<relative-url>^/folder/quest.txt</relative-url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn18/main</root>
+<uuid>3c5e3929-c92b-7045-9ba9-5e65d3dd1ee9</uuid>
+</repository>
+<wc-info>
+<wcroot-abspath>C:/development/svn_example/svn18_example</wcroot-abspath>
+<schedule>normal</schedule>
+<depth>infinity</depth>
+<text-updated>2013-07-13T15:35:54.484375Z</text-updated>
+<checksum>bc80eba9e7a10c0a571a4678c520bc9683f3bac2</checksum>
+</wc-info>
+<commit
+ revision="2">
+<author>ptt</author>
+<date>2013-07-13T15:35:54.843750Z</date>
+</commit>
+</entry>
+<entry
+ path="svn18_example\folder\lalala.txt"
+ revision="6"
+ kind="file">
+<url>file:///C:/development/svn_example/repos/svn18/main/folder/lalala.txt</url>
+<relative-url>^/folder/lalala.txt</relative-url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn18/main</root>
+<uuid>3c5e3929-c92b-7045-9ba9-5e65d3dd1ee9</uuid>
+</repository>
+<wc-info>
+<wcroot-abspath>C:/development/svn_example/svn18_example</wcroot-abspath>
+<schedule>normal</schedule>
+<depth>infinity</depth>
+<text-updated>2013-07-13T15:35:54.015625Z</text-updated>
+<checksum>da39a3ee5e6b4b0d3255bfef95601890afd80709</checksum>
+</wc-info>
+<commit
+ revision="1">
+<author>ptt</author>
+<date>2013-07-13T15:35:54.375000Z</date>
+</commit>
+</entry>
+</info>
diff --git a/setuptools/tests/test_bdist_egg.py b/setuptools/tests/test_bdist_egg.py index 7da122cc..1a122186 100644 --- a/setuptools/tests/test_bdist_egg.py +++ b/setuptools/tests/test_bdist_egg.py @@ -4,9 +4,9 @@ import sys import os, re, shutil, tempfile, unittest import tempfile import site -from StringIO import StringIO from distutils.errors import DistutilsError +from setuptools.compat import StringIO from setuptools.command.bdist_egg import bdist_egg from setuptools.command import easy_install as easy_install_pkg from setuptools.dist import Distribution diff --git a/setuptools/tests/test_develop.py b/setuptools/tests/test_develop.py index 315058c5..7b90161a 100644 --- a/setuptools/tests/test_develop.py +++ b/setuptools/tests/test_develop.py @@ -4,11 +4,11 @@ import sys import os, shutil, tempfile, unittest import tempfile import site -from StringIO import StringIO from distutils.errors import DistutilsError from setuptools.command.develop import develop from setuptools.command import easy_install as easy_install_pkg +from setuptools.compat import StringIO from setuptools.dist import Distribution SETUP_PY = """\ @@ -43,7 +43,7 @@ class TestDevelopTest(unittest.TestCase): f = open(init, 'w') f.write(INIT_PY) f.close() - + os.chdir(self.dir) self.old_base = site.USER_BASE site.USER_BASE = tempfile.mkdtemp() @@ -51,9 +51,9 @@ class TestDevelopTest(unittest.TestCase): site.USER_SITE = tempfile.mkdtemp() def tearDown(self): - if sys.version < "2.6" or hasattr(sys, 'real_prefix'): + if sys.version < "2.6" or hasattr(sys, 'real_prefix') or (hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix): return - + os.chdir(self.old_cwd) shutil.rmtree(self.dir) shutil.rmtree(site.USER_BASE) @@ -90,11 +90,15 @@ class TestDevelopTest(unittest.TestCase): # Check that we are using the right code. egg_link_file = open(os.path.join(site.USER_SITE, 'foo.egg-link'), 'rt') - path = egg_link_file.read().split()[0].strip() - egg_link_file.close() + try: + path = egg_link_file.read().split()[0].strip() + finally: + egg_link_file.close() init_file = open(os.path.join(path, 'foo', '__init__.py'), 'rt') - init = init_file.read().strip() - init_file.close() + try: + init = init_file.read().strip() + finally: + init_file.close() if sys.version < "3": self.assertEqual(init, 'print "foo"') else: @@ -109,10 +113,10 @@ class TestDevelopTest(unittest.TestCase): try: try: dist = Distribution({'setup_requires': ['I_DONT_EXIST']}) - except DistutilsError, e: + except DistutilsError: + e = sys.exc_info()[1] error = str(e) if error == wanted: pass finally: os.chdir(old_dir) - diff --git a/setuptools/tests/test_dist_info.py b/setuptools/tests/test_dist_info.py index fcb78c36..a8adb68c 100644 --- a/setuptools/tests/test_dist_info.py +++ b/setuptools/tests/test_dist_info.py @@ -51,30 +51,33 @@ class TestDistInfo(unittest.TestCase): 'VersionedDistribution-2.718.dist-info') os.mkdir(versioned) metadata_file = open(os.path.join(versioned, 'METADATA'), 'w+') - metadata_file.write(DALS( - """ - Metadata-Version: 1.2 - Name: VersionedDistribution - Requires-Dist: splort (4) - Provides-Extra: baz - Requires-Dist: quux (>=1.1); extra == 'baz' - """)) - metadata_file.close() - + try: + metadata_file.write(DALS( + """ + Metadata-Version: 1.2 + Name: VersionedDistribution + Requires-Dist: splort (4) + Provides-Extra: baz + Requires-Dist: quux (>=1.1); extra == 'baz' + """)) + finally: + metadata_file.close() unversioned = os.path.join(self.tmpdir, 'UnversionedDistribution.dist-info') os.mkdir(unversioned) metadata_file = open(os.path.join(unversioned, 'METADATA'), 'w+') - metadata_file.write(DALS( - """ - Metadata-Version: 1.2 - Name: UnversionedDistribution - Version: 0.3 - Requires-Dist: splort (==4) - Provides-Extra: baz - Requires-Dist: quux (>=1.1); extra == 'baz' - """)) - metadata_file.close() + try: + metadata_file.write(DALS( + """ + Metadata-Version: 1.2 + Name: UnversionedDistribution + Version: 0.3 + Requires-Dist: splort (==4) + Provides-Extra: baz + Requires-Dist: quux (>=1.1); extra == 'baz' + """)) + finally: + metadata_file.close() def tearDown(self): shutil.rmtree(self.tmpdir) diff --git a/setuptools/tests/test_easy_install.py b/setuptools/tests/test_easy_install.py index d17a5340..189e3d55 100644 --- a/setuptools/tests/test_easy_install.py +++ b/setuptools/tests/test_easy_install.py @@ -6,14 +6,15 @@ import shutil import tempfile import unittest import site +from setuptools.compat import StringIO, BytesIO, next +from setuptools.compat import urlparse import textwrap import tarfile -import urlparse -import StringIO import distutils.core +from setuptools.compat import StringIO, BytesIO, next, urlparse from setuptools.sandbox import run_setup, SandboxViolation -from setuptools.command.easy_install import easy_install, fix_jython_executable, get_script_args +from setuptools.command.easy_install import easy_install, fix_jython_executable, get_script_args, nt_quote_arg from setuptools.command.easy_install import PthDistributions from setuptools.command import easy_install as easy_install_pkg from setuptools.dist import Distribution @@ -51,7 +52,7 @@ if __name__ == '__main__': sys.exit( load_entry_point('spec', 'console_scripts', 'name')() ) -""" % fix_jython_executable(sys.executable, "") +""" % nt_quote_arg(fix_jython_executable(sys.executable, "")) SETUP_PY = """\ from setuptools import setup @@ -78,7 +79,7 @@ class TestEasyInstallTest(unittest.TestCase): old_platform = sys.platform try: - name, script = [i for i in get_script_args(dist).next()][0:2] + name, script = [i for i in next(get_script_args(dist))][0:2] finally: sys.platform = old_platform @@ -104,8 +105,7 @@ class TestEasyInstallTest(unittest.TestCase): cmd.install_dir = os.path.join(tempfile.mkdtemp(), 'ok') cmd.args = ['ok'] cmd.ensure_finalized() - keys = cmd.package_index.scanned_urls.keys() - keys.sort() + keys = sorted(cmd.package_index.scanned_urls.keys()) self.assertEqual(keys, ['link1', 'link2']) @@ -269,8 +269,8 @@ class TestUserInstallTest(unittest.TestCase): old_stdout = sys.stdout old_stderr = sys.stderr - sys.stdout = StringIO.StringIO() - sys.stderr = StringIO.StringIO() + sys.stdout = StringIO() + sys.stderr = StringIO() try: try: reset_setup_stop_context( @@ -295,7 +295,7 @@ class TestSetupRequires(unittest.TestCase): p_index = setuptools.tests.server.MockServer() p_index.start() netloc = 1 - p_index_loc = urlparse.urlparse(p_index.url)[netloc] + p_index_loc = urlparse(p_index.url)[netloc] if p_index_loc.endswith(':0'): # Some platforms (Jython) don't find a port to which to bind, # so skip this test for them. @@ -362,9 +362,9 @@ def make_trivial_sdist(dist_path, setup_py): setup_py_file = tarfile.TarInfo(name='setup.py') try: # Python 3 (StringIO gets converted to io module) - MemFile = StringIO.BytesIO + MemFile = BytesIO except AttributeError: - MemFile = StringIO.StringIO + MemFile = StringIO setup_py_bytes = MemFile(setup_py.encode('utf-8')) setup_py_file.size = len(setup_py_bytes.getvalue()) dist = tarfile.open(dist_path, 'w:gz') diff --git a/setuptools/tests/test_egg_info.py b/setuptools/tests/test_egg_info.py index f26a1f51..7abafd71 100644 --- a/setuptools/tests/test_egg_info.py +++ b/setuptools/tests/test_egg_info.py @@ -1,10 +1,12 @@ import os +import sys import tempfile import shutil import unittest import pkg_resources from setuptools.command import egg_info +from setuptools import svn_utils ENTRIES_V10 = pkg_resources.resource_string(__name__, 'entries-v10') "An entries file generated with svn 1.6.17 against the legacy Setuptools repo" @@ -31,10 +33,42 @@ class TestEggInfo(unittest.TestCase): def test_version_10_format(self): """ """ + #keeping this set for 1.6 is a good check on the get_svn_revision + #to ensure I return using svnversion what would had been returned + version_str = svn_utils.SvnInfo.get_svn_version() + version = [int(x) for x in version_str.split('.')[:2]] + if version != [1,6]: + if hasattr(self, 'skipTest'): + self.skipTest('') + else: + sys.stderr.write('\n Skipping due to SVN Version\n') + return + self._write_entries(ENTRIES_V10) rev = egg_info.egg_info.get_svn_revision() self.assertEqual(rev, '89000') + def test_version_10_format_legacy_parser(self): + """ + """ + path_variable = None + for env in os.environ: + if env.lower() == 'path': + path_variable = env + + if path_variable is None: + self.skipTest('Cannot figure out how to modify path') + + old_path = os.environ[path_variable] + os.environ[path_variable] = '' + try: + self._write_entries(ENTRIES_V10) + rev = egg_info.egg_info.get_svn_revision() + finally: + os.environ[path_variable] = old_path + + self.assertEqual(rev, '89000') + def test_suite(): return unittest.defaultTestLoader.loadTestsFromName(__name__) diff --git a/setuptools/tests/test_markerlib.py b/setuptools/tests/test_markerlib.py index aa461846..dae71cba 100644 --- a/setuptools/tests/test_markerlib.py +++ b/setuptools/tests/test_markerlib.py @@ -19,20 +19,24 @@ class TestMarkerlib(unittest.TestCase): self.assertTrue(interpret("")) self.assertTrue(interpret("os.name != 'buuuu'")) + self.assertTrue(interpret("os_name != 'buuuu'")) self.assertTrue(interpret("python_version > '1.0'")) self.assertTrue(interpret("python_version < '5.0'")) self.assertTrue(interpret("python_version <= '5.0'")) self.assertTrue(interpret("python_version >= '1.0'")) self.assertTrue(interpret("'%s' in os.name" % os_name)) + self.assertTrue(interpret("'%s' in os_name" % os_name)) self.assertTrue(interpret("'buuuu' not in os.name")) self.assertFalse(interpret("os.name == 'buuuu'")) + self.assertFalse(interpret("os_name == 'buuuu'")) self.assertFalse(interpret("python_version < '1.0'")) self.assertFalse(interpret("python_version > '5.0'")) self.assertFalse(interpret("python_version >= '5.0'")) self.assertFalse(interpret("python_version <= '1.0'")) self.assertFalse(interpret("'%s' not in os.name" % os_name)) self.assertFalse(interpret("'buuuu' in os.name and python_version >= '5.0'")) + self.assertFalse(interpret("'buuuu' in os_name and python_version >= '5.0'")) environment = default_environment() environment['extra'] = 'test' diff --git a/setuptools/tests/test_packageindex.py b/setuptools/tests/test_packageindex.py index 1060e787..08969b7e 100644 --- a/setuptools/tests/test_packageindex.py +++ b/setuptools/tests/test_packageindex.py @@ -2,12 +2,11 @@ """ import sys import unittest -import urllib2 import pkg_resources -import httplib +from setuptools.compat import urllib2, httplib, HTTPError, unicode import distutils.errors import setuptools.package_index -from server import IndexServer +from setuptools.tests.server import IndexServer class TestPackageIndex(unittest.TestCase): @@ -16,10 +15,11 @@ class TestPackageIndex(unittest.TestCase): url = 'http://127.0.0.1:0/nonesuch/test_package_index' try: v = index.open_url(url) - except Exception, v: + except Exception: + v = sys.exc_info()[1] self.assertTrue(url in str(v)) else: - self.assertTrue(isinstance(v,urllib2.HTTPError)) + self.assertTrue(isinstance(v, HTTPError)) def test_bad_url_typo(self): # issue 16 @@ -32,10 +32,11 @@ class TestPackageIndex(unittest.TestCase): url = 'url:%20https://svn.plone.org/svn/collective/inquant.contentmirror.plone/trunk' try: v = index.open_url(url) - except Exception, v: + except Exception: + v = sys.exc_info()[1] self.assertTrue(url in str(v)) else: - self.assertTrue(isinstance(v, urllib2.HTTPError)) + self.assertTrue(isinstance(v, HTTPError)) def test_bad_url_bad_status_line(self): index = setuptools.package_index.PackageIndex( @@ -43,14 +44,14 @@ class TestPackageIndex(unittest.TestCase): ) def _urlopen(*args): - import httplib raise httplib.BadStatusLine('line') index.opener = _urlopen url = 'http://example.com' try: v = index.open_url(url) - except Exception, v: + except Exception: + v = sys.exc_info()[1] self.assertTrue('line' in str(v)) else: raise AssertionError('Should have raise here!') @@ -67,7 +68,8 @@ class TestPackageIndex(unittest.TestCase): url = 'http://http://svn.pythonpaste.org/Paste/wphp/trunk' try: index.open_url(url) - except distutils.errors.DistutilsError, error: + except distutils.errors.DistutilsError: + error = sys.exc_info()[1] msg = unicode(error) assert 'nonnumeric port' in msg or 'getaddrinfo failed' in msg or 'Name or service not known' in msg return @@ -139,3 +141,47 @@ class TestPackageIndex(unittest.TestCase): 'reportlab-2.5.win-amd64-py2.7.exe'), ('reportlab-2.5', '2.7', 'win-amd64')) self.assertEqual(setuptools.package_index.parse_bdist_wininst( 'reportlab-2.5.win-amd64.exe'), ('reportlab-2.5', None, 'win-amd64')) + + def test__vcs_split_rev_from_url(self): + """ + Test the basic usage of _vcs_split_rev_from_url + """ + vsrfu = setuptools.package_index.PackageIndex._vcs_split_rev_from_url + url, rev = vsrfu('https://example.com/bar@2995') + self.assertEqual(url, 'https://example.com/bar') + self.assertEqual(rev, '2995') + +class TestContentCheckers(unittest.TestCase): + + def test_md5(self): + checker = setuptools.package_index.HashChecker.from_url( + 'http://foo/bar#md5=f12895fdffbd45007040d2e44df98478') + checker.feed('You should probably not be using MD5'.encode('ascii')) + self.assertEqual(checker.hash.hexdigest(), + 'f12895fdffbd45007040d2e44df98478') + self.assertTrue(checker.is_valid()) + + def test_other_fragment(self): + "Content checks should succeed silently if no hash is present" + checker = setuptools.package_index.HashChecker.from_url( + 'http://foo/bar#something%20completely%20different') + checker.feed('anything'.encode('ascii')) + self.assertTrue(checker.is_valid()) + + def test_blank_md5(self): + "Content checks should succeed if a hash is empty" + checker = setuptools.package_index.HashChecker.from_url( + 'http://foo/bar#md5=') + checker.feed('anything'.encode('ascii')) + self.assertTrue(checker.is_valid()) + + def test_get_hash_name_md5(self): + checker = setuptools.package_index.HashChecker.from_url( + 'http://foo/bar#md5=f12895fdffbd45007040d2e44df98478') + self.assertEqual(checker.hash_name, 'md5') + + def test_report(self): + checker = setuptools.package_index.HashChecker.from_url( + 'http://foo/bar#md5=f12895fdffbd45007040d2e44df98478') + rep = checker.report(lambda x: x, 'My message about %s') + self.assertEqual(rep, 'My message about md5') diff --git a/setuptools/tests/test_resources.py b/setuptools/tests/test_resources.py index 34e341b5..c9fcf76c 100644 --- a/setuptools/tests/test_resources.py +++ b/setuptools/tests/test_resources.py @@ -1,10 +1,24 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -# NOTE: the shebang and encoding lines are for ScriptHeaderTests; do not remove -from unittest import TestCase, makeSuite; from pkg_resources import * -from setuptools.command.easy_install import get_script_header, is_sh -import os, pkg_resources, sys, StringIO, tempfile, shutil -try: frozenset +# NOTE: the shebang and encoding lines are for ScriptHeaderTests do not remove + +import os +import sys +import tempfile +import shutil +from unittest import TestCase + +import pkg_resources +from pkg_resources import (parse_requirements, VersionConflict, parse_version, + Distribution, EntryPoint, Requirement, safe_version, safe_name, + WorkingSet) + +from setuptools.command.easy_install import (get_script_header, is_sh, + nt_quote_arg) +from setuptools.compat import StringIO, iteritems + +try: + frozenset except NameError: from sets import ImmutableSet as frozenset @@ -14,11 +28,11 @@ def safe_repr(obj, short=False): result = repr(obj) except Exception: result = object.__repr__(obj) - if not short or len(result) < _MAX_LENGTH: + if not short or len(result) < pkg_resources._MAX_LENGTH: return result - return result[:_MAX_LENGTH] + ' [truncated]...' + return result[:pkg_resources._MAX_LENGTH] + ' [truncated]...' -class Metadata(EmptyProvider): +class Metadata(pkg_resources.EmptyProvider): """Mock object to return metadata as if from an on-disk distribution""" def __init__(self,*pairs): @@ -31,18 +45,20 @@ class Metadata(EmptyProvider): return self.metadata[name] def get_metadata_lines(self,name): - return yield_lines(self.get_metadata(name)) + return pkg_resources.yield_lines(self.get_metadata(name)) + +dist_from_fn = pkg_resources.Distribution.from_filename class DistroTests(TestCase): def testCollection(self): # empty path should produce no distributions - ad = Environment([], platform=None, python=None) + ad = pkg_resources.Environment([], platform=None, python=None) self.assertEqual(list(ad), []) self.assertEqual(ad['FooPkg'],[]) - ad.add(Distribution.from_filename("FooPkg-1.3_1.egg")) - ad.add(Distribution.from_filename("FooPkg-1.4-py2.4-win32.egg")) - ad.add(Distribution.from_filename("FooPkg-1.2-py2.4.egg")) + ad.add(dist_from_fn("FooPkg-1.3_1.egg")) + ad.add(dist_from_fn("FooPkg-1.4-py2.4-win32.egg")) + ad.add(dist_from_fn("FooPkg-1.2-py2.4.egg")) # Name is in there now self.assertTrue(ad['FooPkg']) @@ -59,27 +75,33 @@ class DistroTests(TestCase): [dist.version for dist in ad['FooPkg']], ['1.4','1.2'] ) # And inserting adds them in order - ad.add(Distribution.from_filename("FooPkg-1.9.egg")) + ad.add(dist_from_fn("FooPkg-1.9.egg")) self.assertEqual( [dist.version for dist in ad['FooPkg']], ['1.9','1.4','1.2'] ) ws = WorkingSet([]) - foo12 = Distribution.from_filename("FooPkg-1.2-py2.4.egg") - foo14 = Distribution.from_filename("FooPkg-1.4-py2.4-win32.egg") + foo12 = dist_from_fn("FooPkg-1.2-py2.4.egg") + foo14 = dist_from_fn("FooPkg-1.4-py2.4-win32.egg") req, = parse_requirements("FooPkg>=1.3") # Nominal case: no distros on path, should yield all applicable self.assertEqual(ad.best_match(req,ws).version, '1.9') # If a matching distro is already installed, should return only that - ws.add(foo14); self.assertEqual(ad.best_match(req,ws).version, '1.4') + ws.add(foo14) + self.assertEqual(ad.best_match(req,ws).version, '1.4') # If the first matching distro is unsuitable, it's a version conflict - ws = WorkingSet([]); ws.add(foo12); ws.add(foo14) + ws = WorkingSet([]) + ws.add(foo12) + ws.add(foo14) self.assertRaises(VersionConflict, ad.best_match, req, ws) # If more than one match on the path, the first one takes precedence - ws = WorkingSet([]); ws.add(foo14); ws.add(foo12); ws.add(foo14); + ws = WorkingSet([]) + ws.add(foo14) + ws.add(foo12) + ws.add(foo14) self.assertEqual(ad.best_match(req,ws).version, '1.4') def checkFooPkg(self,d): @@ -102,9 +124,9 @@ class DistroTests(TestCase): self.assertEqual(d.platform, None) def testDistroParse(self): - d = Distribution.from_filename("FooPkg-1.3_1-py2.4-win32.egg") + d = dist_from_fn("FooPkg-1.3_1-py2.4-win32.egg") self.checkFooPkg(d) - d = Distribution.from_filename("FooPkg-1.3_1-py2.4-win32.egg-info") + d = dist_from_fn("FooPkg-1.3_1-py2.4-win32.egg-info") self.checkFooPkg(d) def testDistroMetadata(self): @@ -116,7 +138,6 @@ class DistroTests(TestCase): ) self.checkFooPkg(d) - def distRequires(self, txt): return Distribution("/foo", metadata=Metadata(('depends.txt', txt))) @@ -130,33 +151,34 @@ class DistroTests(TestCase): for v in "Twisted>=1.5", "Twisted>=1.5\nZConfig>=2.0": self.checkRequires(self.distRequires(v), v) - def testResolve(self): - ad = Environment([]); ws = WorkingSet([]) + ad = pkg_resources.Environment([]) + ws = WorkingSet([]) # Resolving no requirements -> nothing to install - self.assertEqual( list(ws.resolve([],ad)), [] ) + self.assertEqual(list(ws.resolve([],ad)), []) # Request something not in the collection -> DistributionNotFound self.assertRaises( - DistributionNotFound, ws.resolve, parse_requirements("Foo"), ad + pkg_resources.DistributionNotFound, ws.resolve, parse_requirements("Foo"), ad ) Foo = Distribution.from_filename( "/foo_dir/Foo-1.2.egg", metadata=Metadata(('depends.txt', "[bar]\nBaz>=2.0")) ) - ad.add(Foo); ad.add(Distribution.from_filename("Foo-0.9.egg")) + ad.add(Foo) + ad.add(Distribution.from_filename("Foo-0.9.egg")) # Request thing(s) that are available -> list to activate for i in range(3): targets = list(ws.resolve(parse_requirements("Foo"), ad)) self.assertEqual(targets, [Foo]) - map(ws.add,targets) + list(map(ws.add,targets)) self.assertRaises(VersionConflict, ws.resolve, parse_requirements("Foo==0.9"), ad) ws = WorkingSet([]) # reset # Request an extra that causes an unresolved dependency for "Baz" self.assertRaises( - DistributionNotFound, ws.resolve,parse_requirements("Foo[bar]"), ad + pkg_resources.DistributionNotFound, ws.resolve,parse_requirements("Foo[bar]"), ad ) Baz = Distribution.from_filename( "/foo_dir/Baz-2.1.egg", metadata=Metadata(('depends.txt', "Foo")) @@ -168,9 +190,8 @@ class DistroTests(TestCase): list(ws.resolve(parse_requirements("Foo[bar]"), ad)), [Foo,Baz] ) # Requests for conflicting versions produce VersionConflict - self.assertRaises( VersionConflict, - ws.resolve, parse_requirements("Foo==1.2\nFoo!=1.2"), ad - ) + self.assertRaises(VersionConflict, + ws.resolve, parse_requirements("Foo==1.2\nFoo!=1.2"), ad) def testDistroDependsOptions(self): d = self.distRequires(""" @@ -195,7 +216,7 @@ class DistroTests(TestCase): d,"Twisted>=1.5 fcgiapp>=0.1 ZConfig>=2.0 docutils>=0.3".split(), ["fastcgi", "docgen"] ) - self.assertRaises(UnknownExtra, d.requires, ["foo"]) + self.assertRaises(pkg_resources.UnknownExtra, d.requires, ["foo"]) class EntryPointTests(TestCase): @@ -249,7 +270,7 @@ class EntryPointTests(TestCase): def checkSubMap(self, m): self.assertEqual(len(m), len(self.submap_expect)) - for key, ep in self.submap_expect.iteritems(): + for key, ep in iteritems(self.submap_expect): self.assertEqual(repr(m.get(key)), repr(ep)) submap_expect = dict( @@ -273,10 +294,10 @@ class EntryPointTests(TestCase): def testParseMap(self): m = EntryPoint.parse_map({'xyz':self.submap_str}) self.checkSubMap(m['xyz']) - self.assertEqual(m.keys(),['xyz']) + self.assertEqual(list(m.keys()),['xyz']) m = EntryPoint.parse_map("[xyz]\n"+self.submap_str) self.checkSubMap(m['xyz']) - self.assertEqual(m.keys(),['xyz']) + self.assertEqual(list(m.keys()),['xyz']) self.assertRaises(ValueError, EntryPoint.parse_map, ["[xyz]", "[xyz]"]) self.assertRaises(ValueError, EntryPoint.parse_map, self.submap_str) @@ -303,8 +324,8 @@ class RequirementsTests(TestCase): def testBasicContains(self): r = Requirement("Twisted", [('>=','1.2')], ()) foo_dist = Distribution.from_filename("FooPkg-1.3_1.egg") - twist11 = Distribution.from_filename("Twisted-1.1.egg") - twist12 = Distribution.from_filename("Twisted-1.2.egg") + twist11 = Distribution.from_filename("Twisted-1.1.egg") + twist12 = Distribution.from_filename("Twisted-1.2.egg") self.assertTrue(parse_version('1.2') in r) self.assertTrue(parse_version('1.1') not in r) self.assertTrue('1.2' in r) @@ -320,7 +341,6 @@ class RequirementsTests(TestCase): for v in ('1.2c1','1.3.1','1.5','1.9.1','2.0','2.5','3.0','4.0'): self.assertTrue(v not in r, (v,r)) - def testOptionsAndHashing(self): r1 = Requirement.parse("Twisted[foo,bar]>=1.2") r2 = Requirement.parse("Twisted[bar,FOO]>=1.2") @@ -365,15 +385,6 @@ class RequirementsTests(TestCase): Requirement.parse('setuptools >= 0.7').project_name, 'setuptools') - - - - - - - - - class ParseTests(TestCase): def testEmptyParse(self): @@ -387,9 +398,7 @@ class ParseTests(TestCase): self.assertEqual(list(pkg_resources.yield_lines(inp)),out) def testSplitting(self): - self.assertEqual( - list( - pkg_resources.split_sections(""" + sample = """ x [Y] z @@ -402,8 +411,7 @@ class ParseTests(TestCase): [q] v """ - ) - ), + self.assertEqual(list(pkg_resources.split_sections(sample)), [(None,["x"]), ("Y",["z","a"]), ("b",["c"]), ("d",[]), ("q",["v"])] ) self.assertRaises(ValueError,list,pkg_resources.split_sections("[foo")) @@ -454,7 +462,8 @@ class ParseTests(TestCase): c('0pre1', '0.0c1') c('0.0.0preview1', '0c1') c('0.0c1', '0-rc1') - c('1.2a1', '1.2.a.1'); c('1.2...a', '1.2a') + c('1.2a1', '1.2.a.1') + c('1.2...a', '1.2a') def testVersionOrdering(self): def c(s1,s2): @@ -491,30 +500,30 @@ class ParseTests(TestCase): c(v2,v1) - - - - - - class ScriptHeaderTests(TestCase): non_ascii_exe = '/Users/José/bin/python' + exe_with_spaces = r'C:\Program Files\Python33\python.exe' def test_get_script_header(self): if not sys.platform.startswith('java') or not is_sh(sys.executable): # This test is for non-Jython platforms + expected = '#!%s\n' % nt_quote_arg(os.path.normpath(sys.executable)) self.assertEqual(get_script_header('#!/usr/local/bin/python'), - '#!%s\n' % os.path.normpath(sys.executable)) + expected) + expected = '#!%s -x\n' % nt_quote_arg(os.path.normpath(sys.executable)) self.assertEqual(get_script_header('#!/usr/bin/python -x'), - '#!%s -x\n' % os.path.normpath(sys.executable)) + expected) self.assertEqual(get_script_header('#!/usr/bin/python', executable=self.non_ascii_exe), '#!%s -x\n' % self.non_ascii_exe) + candidate = get_script_header('#!/usr/bin/python', + executable=self.exe_with_spaces) + self.assertEqual(candidate, '#!"%s"\n' % self.exe_with_spaces) def test_get_script_header_jython_workaround(self): # This test doesn't work with Python 3 in some locales if (sys.version_info >= (3,) and os.environ.get("LC_CTYPE") - in (None, "C", "POSIX")): + in (None, "C", "POSIX")): return class java: @@ -537,12 +546,12 @@ class ScriptHeaderTests(TestCase): # Ensure we generate what is basically a broken shebang line # when there's options, with a warning emitted - sys.stdout = sys.stderr = StringIO.StringIO() + sys.stdout = sys.stderr = StringIO() self.assertEqual(get_script_header('#!/usr/bin/python -x', executable=exe), '#!%s -x\n' % exe) self.assertTrue('Unable to adapt shebang line' in sys.stdout.getvalue()) - sys.stdout = sys.stderr = StringIO.StringIO() + sys.stdout = sys.stderr = StringIO() self.assertEqual(get_script_header('#!/usr/bin/python', executable=self.non_ascii_exe), '#!%s -x\n' % self.non_ascii_exe) @@ -553,8 +562,6 @@ class ScriptHeaderTests(TestCase): sys.stdout, sys.stderr = stdout, stderr - - class NamespaceTests(TestCase): def setUp(self): @@ -602,13 +609,12 @@ class NamespaceTests(TestCase): self._assertIn("pkg1", pkg_resources._namespace_packages.keys()) try: import pkg1.pkg2 - except ImportError, e: + except ImportError: self.fail("Setuptools tried to import the parent namespace package") # check the _namespace_packages dict self._assertIn("pkg1.pkg2", pkg_resources._namespace_packages.keys()) self.assertEqual(pkg_resources._namespace_packages["pkg1"], ["pkg1.pkg2"]) # check the __path__ attribute contains both paths self.assertEqual(pkg1.pkg2.__path__, [ - os.path.join(self._tmpdir, "site-pkgs", "pkg1", "pkg2"), - os.path.join(self._tmpdir, "site-pkgs2", "pkg1", "pkg2") ]) - + os.path.join(self._tmpdir, "site-pkgs", "pkg1", "pkg2"), + os.path.join(self._tmpdir, "site-pkgs2", "pkg1", "pkg2")]) diff --git a/setuptools/tests/test_sandbox.py b/setuptools/tests/test_sandbox.py index 1609ee86..3dad1376 100644 --- a/setuptools/tests/test_sandbox.py +++ b/setuptools/tests/test_sandbox.py @@ -5,7 +5,10 @@ import os import shutil import unittest import tempfile +import types +import pkg_resources +import setuptools.sandbox from setuptools.sandbox import DirectorySandbox, SandboxViolation def has_win32com(): @@ -62,5 +65,15 @@ class TestSandbox(unittest.TestCase): finally: if os.path.exists(target): os.remove(target) + def test_setup_py_with_BOM(self): + """ + It should be possible to execute a setup.py with a Byte Order Mark + """ + target = pkg_resources.resource_filename(__name__, + 'script-with-bom.py') + namespace = types.ModuleType('namespace') + setuptools.sandbox.execfile(target, vars(namespace)) + assert namespace.result == 'passed' + if __name__ == '__main__': unittest.main() diff --git a/setuptools/tests/test_sdist.py b/setuptools/tests/test_sdist.py index f51d4567..65a9f0b3 100644 --- a/setuptools/tests/test_sdist.py +++ b/setuptools/tests/test_sdist.py @@ -1,21 +1,22 @@ # -*- coding: utf-8 -*- """sdist tests""" - +import locale import os import shutil import sys import tempfile import unittest -import urllib import unicodedata -from StringIO import StringIO - +from setuptools.tests import environment -from setuptools.command.sdist import sdist +from setuptools.compat import StringIO, unicode +from setuptools.tests.py26compat import skipIf +from setuptools.command.sdist import sdist, walk_revctrl from setuptools.command.egg_info import manifest_maker from setuptools.dist import Distribution - +from setuptools import svn_utils +from setuptools.svn_utils import fsencode SETUP_ATTRS = { 'name': 'sdist_test', @@ -57,7 +58,7 @@ def b(s, encoding='utf-8'): # Convert to POSIX path def posix(path): - if sys.version_info >= (3,) and not isinstance(path, unicode): + if sys.version_info >= (3,) and not isinstance(path, str): return path.replace(os.sep.encode('ascii'), b('/')) else: return path.replace(os.sep, '/') @@ -149,7 +150,8 @@ class TestSdistTest(unittest.TestCase): # The manifest should be UTF-8 encoded try: u_contents = contents.decode('UTF-8') - except UnicodeDecodeError, e: + except UnicodeDecodeError: + e = sys.exc_info()[1] self.fail(e) # The manifest should contain the UTF-8 filename @@ -190,7 +192,8 @@ class TestSdistTest(unittest.TestCase): # The manifest should be UTF-8 encoded try: contents.decode('UTF-8') - except UnicodeDecodeError, e: + except UnicodeDecodeError: + e = sys.exc_info()[1] self.fail(e) # The manifest should contain the UTF-8 filename @@ -228,7 +231,8 @@ class TestSdistTest(unittest.TestCase): # The manifest should be UTF-8 encoded try: contents.decode('UTF-8') - except UnicodeDecodeError, e: + except UnicodeDecodeError: + e = sys.exc_info()[1] self.fail(e) # The Latin-1 filename should have been skipped @@ -307,7 +311,8 @@ class TestSdistTest(unittest.TestCase): try: try: cmd.read_manifest() - except UnicodeDecodeError, e: + except UnicodeDecodeError: + e = sys.exc_info()[1] self.fail(e) finally: unquiet() @@ -316,6 +321,8 @@ class TestSdistTest(unittest.TestCase): filename = filename.decode('latin-1') self.assertFalse(filename in cmd.filelist.files) + @skipIf(sys.version_info >= (3,) and locale.getpreferredencoding() != 'UTF-8', + 'Unittest fails if locale is not utf-8 but the manifests is recorded correctly') def test_sdist_with_utf8_encoded_filename(self): # Test for #303. dist = Distribution(SETUP_ATTRS) @@ -339,7 +346,7 @@ class TestSdistTest(unittest.TestCase): if sys.version_info >= (3,): fs_enc = sys.getfilesystemencoding() - if sys.platform == 'win32': + if sys.platform == 'win32': if fs_enc == 'cp1252': # Python 3 mangles the UTF-8 filename filename = filename.decode('cp1252') @@ -374,14 +381,14 @@ class TestSdistTest(unittest.TestCase): if sys.version_info >= (3,): #not all windows systems have a default FS encoding of cp1252 if sys.platform == 'win32': - # Latin-1 is similar to Windows-1252 however + # Latin-1 is similar to Windows-1252 however # on mbcs filesys it is not in latin-1 encoding fs_enc = sys.getfilesystemencoding() if fs_enc == 'mbcs': filename = filename.decode('mbcs') else: filename = filename.decode('latin-1') - + self.assertTrue(filename in cmd.filelist.files) else: # The Latin-1 filename should have been skipped @@ -393,6 +400,64 @@ class TestSdistTest(unittest.TestCase): self.assertTrue(filename in cmd.filelist.files) +class TestSvn(environment.ZippedEnvironment): + + def setUp(self): + version = svn_utils.SvnInfo.get_svn_version() + self.base_version = tuple([int(x) for x in version.split('.')][:2]) + + if not self.base_version: + raise ValueError('No SVN tools installed') + elif self.base_version < (1,3): + raise ValueError('Insufficient SVN Version %s' % version) + elif self.base_version >= (1,9): + #trying the latest version + self.base_version = (1,8) + + self.dataname = "svn%i%i_example" % self.base_version + self.datafile = os.path.join('setuptools', 'tests', + 'svn_data', self.dataname + ".zip") + super(TestSvn, self).setUp() + + def test_walksvn(self): + if self.base_version >= (1,6): + folder2 = 'third party2' + folder3 = 'third party3' + else: + folder2 = 'third_party2' + folder3 = 'third_party3' + + #TODO is this right + expected = set([ + os.path.join('a file'), + os.path.join(folder2, 'Changes.txt'), + os.path.join(folder2, 'MD5SUMS'), + os.path.join(folder2, 'README.txt'), + os.path.join(folder3, 'Changes.txt'), + os.path.join(folder3, 'MD5SUMS'), + os.path.join(folder3, 'README.txt'), + os.path.join(folder3, 'TODO.txt'), + os.path.join(folder3, 'fin'), + os.path.join('third_party', 'README.txt'), + os.path.join('folder', folder2, 'Changes.txt'), + os.path.join('folder', folder2, 'MD5SUMS'), + os.path.join('folder', folder2, 'WatashiNiYomimasu.txt'), + os.path.join( 'folder', folder3, 'Changes.txt'), + os.path.join('folder', folder3, 'fin'), + os.path.join('folder', folder3, 'MD5SUMS'), + os.path.join('folder', folder3, 'oops'), + os.path.join('folder', folder3, 'WatashiNiYomimasu.txt'), + os.path.join('folder', folder3, 'ZuMachen.txt'), + os.path.join('folder', 'third_party', 'WatashiNiYomimasu.txt'), + os.path.join('folder', 'lalala.txt'), + os.path.join('folder', 'quest.txt'), + #The example will have a deleted file (or should) but shouldn't return it + ]) + expected = set(fsencode(x) for x in expected) + self.assertEqual(set(x for x in walk_revctrl()), expected) + + + def test_suite(): return unittest.defaultTestLoader.loadTestsFromName(__name__) diff --git a/setuptools/tests/test_svn.py b/setuptools/tests/test_svn.py new file mode 100644 index 00000000..59ecb25b --- /dev/null +++ b/setuptools/tests/test_svn.py @@ -0,0 +1,240 @@ +# -*- coding: utf-8 -*- +"""svn tests""" + + +import os +import sys +import unittest +import codecs +from setuptools.tests import environment +from setuptools.svn_utils import fsencode +from setuptools.compat import unicode, unichr + +from setuptools import svn_utils + +#requires python >= 2.4 +from subprocess import call as _call + +from distutils import log + + + +class TestSvnVersion(unittest.TestCase): + + def test_no_svn_found(self): + path_variable = None + for env in os.environ: + if env.lower() == 'path': + path_variable = env + + if path_variable is None: + self.skipTest('Cannot figure out how to modify path') + + old_path = os.environ[path_variable] + os.environ[path_variable] = '' + try: + version = svn_utils.SvnInfo.get_svn_version() + self.assertEqual(version, '') + finally: + os.environ[path_variable] = old_path + + def test_svn_should_exist(self): + version = svn_utils.SvnInfo.get_svn_version() + self.assertNotEqual(version, '') + +def _read_utf8_file(path): + fileobj = None + try: + fileobj = codecs.open(path, 'r', 'utf-8') + data = fileobj.read() + return data + finally: + if fileobj: + fileobj.close() + + +class ParserInfoXML(unittest.TestCase): + + def parse_tester(self, svn_name, ext_spaces): + path = os.path.join('setuptools', 'tests', + 'svn_data', svn_name + '_info.xml') + #Remember these are pre-generated to test XML parsing + # so these paths might not valid on your system + example_base = "%s_example" % svn_name + + data = _read_utf8_file(path) + + if ext_spaces: + folder2 = 'third party2' + folder3 = 'third party3' + else: + folder2 = 'third_party2' + folder3 = 'third_party3' + + expected = set([ + ("\\".join((example_base, 'a file')), 'file'), + ("\\".join((example_base, 'folder')), 'dir'), + ("\\".join((example_base, 'folder', 'lalala.txt')), 'file'), + ("\\".join((example_base, 'folder', 'quest.txt')), 'file'), + ]) + self.assertEqual(set(x for x in svn_utils.parse_dir_entries(data)), + expected) + + def test_svn13(self): + self.parse_tester('svn13', False) + + def test_svn14(self): + self.parse_tester('svn14', False) + + def test_svn15(self): + self.parse_tester('svn15', False) + + def test_svn16(self): + self.parse_tester('svn16', True) + + def test_svn17(self): + self.parse_tester('svn17', True) + + def test_svn18(self): + self.parse_tester('svn18', True) + +class ParserExternalXML(unittest.TestCase): + + def parse_tester(self, svn_name, ext_spaces): + path = os.path.join('setuptools', 'tests', + 'svn_data', svn_name + '_ext_list.xml') + example_base = svn_name + '_example' + data = _read_utf8_file(path) + + if ext_spaces: + folder2 = 'third party2' + folder3 = 'third party3' + else: + folder2 = 'third_party2' + folder3 = 'third_party3' + + expected = set([ + os.sep.join((example_base, folder2)), + os.sep.join((example_base, folder3)), + #third_party大介 + os.sep.join((example_base, + unicode('third_party') + + unichr(0x5927) + unichr(0x4ecb))), + os.sep.join((example_base, 'folder', folder2)), + os.sep.join((example_base, 'folder', folder3)), + os.sep.join((example_base, 'folder', + unicode('third_party') + + unichr(0x5927) + unichr(0x4ecb))), + ]) + + expected = set(os.path.normpath(x) for x in expected) + dir_base = os.sep.join(('C:', 'development', 'svn_example')) + self.assertEqual(set(x for x \ + in svn_utils.parse_externals_xml(data, dir_base)), expected) + + def test_svn15(self): + self.parse_tester('svn15', False) + + def test_svn16(self): + self.parse_tester('svn16', True) + + def test_svn17(self): + self.parse_tester('svn17', True) + + def test_svn18(self): + self.parse_tester('svn18', True) + + +class ParseExternal(unittest.TestCase): + + def parse_tester(self, svn_name, ext_spaces): + path = os.path.join('setuptools', 'tests', + 'svn_data', svn_name + '_ext_list.txt') + example_base = svn_name + '_example' + data = _read_utf8_file(path) + + if ext_spaces: + expected = set(['third party2', 'third party3', + 'third party3b', 'third_party']) + else: + expected = set(['third_party2', 'third_party3', 'third_party']) + + self.assertEqual(set(x for x in svn_utils.parse_external_prop(data)), + expected) + + def test_svn13(self): + self.parse_tester('svn13', False) + + def test_svn14(self): + self.parse_tester('svn14', False) + + def test_svn15(self): + self.parse_tester('svn15', False) + + def test_svn16(self): + self.parse_tester('svn16', True) + + def test_svn17(self): + self.parse_tester('svn17', True) + + def test_svn18(self): + self.parse_tester('svn18', True) + + +class TestSvn(environment.ZippedEnvironment): + + def setUp(self): + version = svn_utils.SvnInfo.get_svn_version() + self.base_version = tuple([int(x) for x in version.split('.')[:2]]) + + if not self.base_version: + raise ValueError('No SVN tools installed') + elif self.base_version < (1,3): + raise ValueError('Insufficient SVN Version %s' % version) + elif self.base_version >= (1,9): + #trying the latest version + self.base_version = (1,8) + + self.dataname = "svn%i%i_example" % self.base_version + self.datafile = os.path.join('setuptools', 'tests', + 'svn_data', self.dataname + ".zip") + super(TestSvn, self).setUp() + + def test_revision(self): + rev = svn_utils.SvnInfo.load('.').get_revision() + self.assertEqual(rev, 6) + + def test_entries(self): + expected = set([ + (os.path.join('a file'), 'file'), + (os.path.join('folder'), 'dir'), + (os.path.join('folder', 'lalala.txt'), 'file'), + (os.path.join('folder', 'quest.txt'), 'file'), + #The example will have a deleted file (or should) + #but shouldn't return it + ]) + info = svn_utils.SvnInfo.load('.') + self.assertEqual(set(x for x in info.entries), expected) + + def test_externals(self): + if self.base_version >= (1,6): + folder2 = 'third party2' + folder3 = 'third party3' + else: + folder2 = 'third_party2' + folder3 = 'third_party3' + + expected = set([ + os.path.join(folder2), + os.path.join(folder3), + os.path.join('third_party'), + os.path.join('folder', folder2), + os.path.join('folder', folder3), + os.path.join('folder', 'third_party'), + ]) + info = svn_utils.SvnInfo.load('.') + self.assertEqual(set([x for x in info.externals]), expected) + +def test_suite(): + return unittest.defaultTestLoader.loadTestsFromName(__name__) + diff --git a/setuptools/tests/test_test.py b/setuptools/tests/test_test.py index ad7cbd0f..7a06a403 100644 --- a/setuptools/tests/test_test.py +++ b/setuptools/tests/test_test.py @@ -1,4 +1,4 @@ -# -*- coding: UTF-8 -*- +# -*- coding: UTF-8 -*- """develop tests """ @@ -6,9 +6,9 @@ import sys import os, shutil, tempfile, unittest import tempfile import site -from StringIO import StringIO from distutils.errors import DistutilsError +from setuptools.compat import StringIO from setuptools.command.test import test from setuptools.command import easy_install as easy_install_pkg from setuptools.dist import Distribution @@ -23,7 +23,7 @@ setup(name='foo', ) """ -NS_INIT = """# -*- coding: Latin-1 -*- +NS_INIT = """# -*- coding: Latin-1 -*- # Söme Arbiträry Ünicode to test Issüé 310 try: __import__('pkg_resources').declare_namespace(__name__) @@ -77,7 +77,7 @@ class TestTestTest(unittest.TestCase): f = open(init, 'wt') f.write(TEST_PY) f.close() - + os.chdir(self.dir) self.old_base = site.USER_BASE site.USER_BASE = tempfile.mkdtemp() @@ -87,7 +87,7 @@ class TestTestTest(unittest.TestCase): def tearDown(self): if sys.version < "2.6" or hasattr(sys, 'real_prefix'): return - + os.chdir(self.old_cwd) shutil.rmtree(self.dir) shutil.rmtree(site.USER_BASE) @@ -98,7 +98,7 @@ class TestTestTest(unittest.TestCase): def test_test(self): if sys.version < "2.6" or hasattr(sys, 'real_prefix'): return - + dist = Distribution(dict( name='foo', packages=['name', 'name.space', 'name.space.tests'], @@ -121,4 +121,4 @@ class TestTestTest(unittest.TestCase): pass finally: sys.stdout = old_stdout -
\ No newline at end of file + diff --git a/setuptools/tests/win_script_wrapper.txt b/setuptools/tests/win_script_wrapper.txt index db1daf6b..731243dd 100644 --- a/setuptools/tests/win_script_wrapper.txt +++ b/setuptools/tests/win_script_wrapper.txt @@ -49,37 +49,16 @@ GUI programs, the suffix '-script-pyw' is added.) This is why we named out script the way we did. Now we can run out script by running the wrapper: - >>> from subprocess import Popen, PIPE, STDOUT - >>> try: - ... unicode=unicode - ... except: - ... unicode=str - >>> def popen4(cmd, *args): - ... if hasattr(os, 'popen4'): - ... input, output = os.popen4(cmd + " ".join(args)) - ... return input, output - ... else: - ... #emulate popen4 in python 3 - ... if cmd[0] == '"' and cmd[-1] != '"': - ... cmd = cmd[1:] - ... cmd += " ".join(args) - ... p = Popen(cmd, shell=True, bufsize=0, - ... stdin=PIPE, stdout=PIPE, stderr=STDOUT) - ... return p.stdin, p.stdout - - >>> input, output = popen4('"' + nt_quote_arg(os.path.join(sample_directory, 'foo.exe')), - ... r' arg1', r'"arg 2"', r'"arg \"2\\\""', r'"arg 4\\"', r'"arg5 a\\b"') - >>> bytes_written = input.write('hello\nworld\n'.encode('utf-8')) - >>> input.close() - >>> # This is needed for line ending differences between py2 and py3 on win32 - >>> msg = unicode(output.read(), encoding='utf-8').split("\n") - >>> for line in msg: - ... print(line.strip()) + >>> import subprocess + >>> cmd = [os.path.join(sample_directory, 'foo.exe'), 'arg1', 'arg 2', + ... 'arg "2\\"', 'arg 4\\', 'arg5 a\\\\b'] + >>> proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE) + >>> stdout, stderr = proc.communicate('hello\nworld\n'.encode('ascii')) + >>> bytes = sys.stdout.write(stdout.decode('ascii').replace('\r\n', '\n')) \foo-script.py ['arg1', 'arg 2', 'arg "2\\"', 'arg 4\\', 'arg5 a\\\\b'] 'hello\nworld\n' non-optimized - <BLANKLINE> This example was a little pathological in that it exercised windows (MS C runtime) quoting rules: @@ -115,18 +94,14 @@ enter the interpreter after running the script, you could use -Oi: ... sys.ps1 = '---' ... """ % dict(python_exe=nt_quote_arg(sys.executable))) >>> f.close() - - >>> input, output = popen4(nt_quote_arg(os.path.join(sample_directory, 'foo.exe'))) - >>> input.close() - >>> # This is needed for line ending differences between py2 and py3 on win32 - >>> msg = unicode(output.read(), encoding='utf-8').split("\n") - >>> for line in msg: - ... print(line.strip()) + >>> cmd = [os.path.join(sample_directory, 'foo.exe')] + >>> proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT) + >>> stdout, stderr = proc.communicate() + >>> bytes = sys.stdout.write(stdout.decode('ascii').replace('\r\n', '\n')) \foo-script.py [] '' --- - <BLANKLINE> Testing the GUI Version ----------------------- @@ -157,18 +132,19 @@ We'll also copy gui.exe to the sample-directory with the name bar.exe: Finally, we'll run the script and check the result: - >>> input, output = popen4('"'+nt_quote_arg(os.path.join(sample_directory, 'bar.exe')), - ... r' "%s" "Test Argument"' % os.path.join(sample_directory, 'test_output.txt')) - >>> input.close() - >>> # This is needed for line ending differences between py2 and py3 on win32 - >>> msg = unicode(output.read(), encoding='utf-8').split("\n") - >>> for line in msg: - ... print(line.strip()) + >>> cmd = [ + ... os.path.join(sample_directory, 'bar.exe'), + ... os.path.join(sample_directory, 'test_output.txt'), + ... 'Test Argument', + ... ] + >>> proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT) + >>> stdout, stderr = proc.communicate() + >>> print(stdout.decode('ascii')) <BLANKLINE> - >>> f = open(os.path.join(sample_directory, 'test_output.txt'), 'rb') - >>> print(unicode(f.read(), encoding='utf-8')) + >>> f_out = open(os.path.join(sample_directory, 'test_output.txt'), 'rb') + >>> print(f_out.read().decode('ascii')) 'Test Argument' - >>> f.close() + >>> f_out.close() We're done with the sample_directory: diff --git a/setuptools/version.py b/setuptools/version.py new file mode 100644 index 00000000..64477cf2 --- /dev/null +++ b/setuptools/version.py @@ -0,0 +1 @@ +__version__ = '1.2' |
