summaryrefslogtreecommitdiff
path: root/setuptools
diff options
context:
space:
mode:
Diffstat (limited to 'setuptools')
-rw-r--r--setuptools/__init__.py40
-rw-r--r--setuptools/_backport/__init__.py0
-rw-r--r--setuptools/_backport/hashlib/__init__.py146
-rw-r--r--setuptools/_backport/hashlib/_sha.py359
-rw-r--r--setuptools/_backport/hashlib/_sha256.py260
-rw-r--r--setuptools/_backport/hashlib/_sha512.py288
-rwxr-xr-xsetuptools/command/alias.py14
-rw-r--r--setuptools/command/bdist_egg.py5
-rwxr-xr-xsetuptools/command/easy_install.py613
-rwxr-xr-xsetuptools/command/egg_info.py45
-rwxr-xr-xsetuptools/command/install_scripts.py2
-rwxr-xr-xsetuptools/command/rotate.py1
-rwxr-xr-xsetuptools/command/saveopts.py3
-rwxr-xr-xsetuptools/command/sdist.py166
-rwxr-xr-xsetuptools/command/setopt.py4
-rw-r--r--setuptools/command/test.py2
-rwxr-xr-xsetuptools/command/upload.py32
-rw-r--r--setuptools/command/upload_docs.py22
-rw-r--r--setuptools/compat.py99
-rw-r--r--setuptools/depends.py4
-rw-r--r--setuptools/dist.py177
-rw-r--r--setuptools/extension.py2
-rwxr-xr-xsetuptools/package_index.py322
-rw-r--r--setuptools/py24compat.py6
-rw-r--r--setuptools/py26compat.py19
-rwxr-xr-xsetuptools/sandbox.py23
-rw-r--r--setuptools/script template (dev).py9
-rw-r--r--setuptools/ssl_support.py18
-rw-r--r--setuptools/svn_utils.py539
-rw-r--r--setuptools/tests/__init__.py13
-rw-r--r--setuptools/tests/doctest.py58
-rw-r--r--setuptools/tests/environment.py104
-rw-r--r--setuptools/tests/script-with-bom.py3
-rw-r--r--setuptools/tests/server.py12
-rw-r--r--setuptools/tests/svn17_example.zipbin0 -> 19342 bytes
-rw-r--r--setuptools/tests/svn_data/svn13_example.zipbin0 -> 48818 bytes
-rw-r--r--setuptools/tests/svn_data/svn13_ext_list.txt3
-rw-r--r--setuptools/tests/svn_data/svn13_ext_list.xml0
-rw-r--r--setuptools/tests/svn_data/svn13_info.xml121
-rw-r--r--setuptools/tests/svn_data/svn14_example.zipbin0 -> 31077 bytes
-rw-r--r--setuptools/tests/svn_data/svn14_ext_list.txt4
-rw-r--r--setuptools/tests/svn_data/svn14_ext_list.xml0
-rw-r--r--setuptools/tests/svn_data/svn14_info.xml119
-rw-r--r--setuptools/tests/svn_data/svn15_example.zipbin0 -> 31143 bytes
-rw-r--r--setuptools/tests/svn_data/svn15_ext_list.txt4
-rw-r--r--setuptools/tests/svn_data/svn15_ext_list.xml19
-rw-r--r--setuptools/tests/svn_data/svn15_info.xml125
-rw-r--r--setuptools/tests/svn_data/svn16_example.zipbin0 -> 29418 bytes
-rw-r--r--setuptools/tests/svn_data/svn16_ext_list.txt4
-rw-r--r--setuptools/tests/svn_data/svn16_ext_list.xml19
-rw-r--r--setuptools/tests/svn_data/svn16_info.xml125
-rw-r--r--setuptools/tests/svn_data/svn17_example.zipbin0 -> 46954 bytes
-rw-r--r--setuptools/tests/svn_data/svn17_ext_list.txt4
-rw-r--r--setuptools/tests/svn_data/svn17_ext_list.xml19
-rw-r--r--setuptools/tests/svn_data/svn17_info.xml130
-rw-r--r--setuptools/tests/svn_data/svn18_example.zipbin0 -> 47477 bytes
-rw-r--r--setuptools/tests/svn_data/svn18_ext_list.txt4
-rw-r--r--setuptools/tests/svn_data/svn18_ext_list.xml19
-rw-r--r--setuptools/tests/svn_data/svn18_info.xml136
-rw-r--r--setuptools/tests/test_bdist_egg.py2
-rw-r--r--setuptools/tests/test_develop.py24
-rw-r--r--setuptools/tests/test_dist_info.py43
-rw-r--r--setuptools/tests/test_easy_install.py24
-rw-r--r--setuptools/tests/test_egg_info.py34
-rw-r--r--setuptools/tests/test_markerlib.py4
-rw-r--r--setuptools/tests/test_packageindex.py66
-rw-r--r--setuptools/tests/test_resources.py148
-rw-r--r--setuptools/tests/test_sandbox.py13
-rw-r--r--setuptools/tests/test_sdist.py93
-rw-r--r--setuptools/tests/test_svn.py240
-rw-r--r--setuptools/tests/test_test.py14
-rw-r--r--setuptools/tests/win_script_wrapper.txt66
-rw-r--r--setuptools/version.py1
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 = [
- ("&lt;","<"), ("&gt;", ">"), ("&quot;", '"'), ("&apos;", "'"),
- ("&amp;", "&")
-]
-
-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
new file mode 100644
index 00000000..cfabd2b2
--- /dev/null
+++ b/setuptools/tests/svn17_example.zip
Binary files differ
diff --git a/setuptools/tests/svn_data/svn13_example.zip b/setuptools/tests/svn_data/svn13_example.zip
new file mode 100644
index 00000000..d85fb84f
--- /dev/null
+++ b/setuptools/tests/svn_data/svn13_example.zip
Binary files differ
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
new file mode 100644
index 00000000..57093c0b
--- /dev/null
+++ b/setuptools/tests/svn_data/svn14_example.zip
Binary files differ
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
new file mode 100644
index 00000000..52a1d45b
--- /dev/null
+++ b/setuptools/tests/svn_data/svn15_example.zip
Binary files differ
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
new file mode 100644
index 00000000..e886b2af
--- /dev/null
+++ b/setuptools/tests/svn_data/svn16_example.zip
Binary files differ
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
new file mode 100644
index 00000000..ba0e8823
--- /dev/null
+++ b/setuptools/tests/svn_data/svn17_example.zip
Binary files differ
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
new file mode 100644
index 00000000..4362f8e9
--- /dev/null
+++ b/setuptools/tests/svn_data/svn18_example.zip
Binary files differ
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'