summaryrefslogtreecommitdiff
path: root/setuptools
diff options
context:
space:
mode:
Diffstat (limited to 'setuptools')
-rw-r--r--setuptools/__init__.py3
-rw-r--r--setuptools/command/__init__.py2
-rwxr-xr-xsetuptools/command/alias.py2
-rw-r--r--setuptools/command/bdist_egg.py5
-rw-r--r--setuptools/command/build_py.py34
-rwxr-xr-xsetuptools/command/develop.py5
-rwxr-xr-xsetuptools/command/easy_install.py97
-rwxr-xr-xsetuptools/command/egg_info.py13
-rw-r--r--setuptools/command/install.py2
-rwxr-xr-xsetuptools/command/install_egg_info.py4
-rwxr-xr-xsetuptools/command/rotate.py5
-rwxr-xr-xsetuptools/command/sdist.py5
-rwxr-xr-xsetuptools/command/setopt.py4
-rw-r--r--setuptools/command/test.py8
-rw-r--r--setuptools/command/upload.py23
-rw-r--r--setuptools/command/upload_docs.py21
-rw-r--r--setuptools/compat.py70
-rw-r--r--setuptools/depends.py6
-rw-r--r--setuptools/dist.py27
-rw-r--r--setuptools/extension.py2
-rw-r--r--setuptools/extern/__init__.py5
-rw-r--r--setuptools/launch.py35
-rwxr-xr-xsetuptools/package_index.py97
-rw-r--r--setuptools/py26compat.py5
-rwxr-xr-xsetuptools/sandbox.py14
-rw-r--r--setuptools/ssl_support.py28
-rw-r--r--setuptools/tests/__init__.py4
-rw-r--r--setuptools/tests/contexts.py19
-rw-r--r--setuptools/tests/files.py32
-rw-r--r--setuptools/tests/server.py19
-rw-r--r--setuptools/tests/test_develop.py5
-rw-r--r--setuptools/tests/test_dist_info.py7
-rw-r--r--setuptools/tests/test_easy_install.py247
-rw-r--r--setuptools/tests/test_egg_info.py136
-rw-r--r--setuptools/tests/test_integration.py4
-rw-r--r--setuptools/tests/test_markerlib.py63
-rw-r--r--setuptools/tests/test_packageindex.py15
-rw-r--r--setuptools/tests/test_sdist.py24
-rw-r--r--setuptools/tests/test_setuptools.py2
-rw-r--r--setuptools/tests/test_test.py1
-rw-r--r--setuptools/tests/test_unicode_utils.py10
-rw-r--r--setuptools/unicode_utils.py12
42 files changed, 632 insertions, 490 deletions
diff --git a/setuptools/__init__.py b/setuptools/__init__.py
index b0a5401f..b8cec4c3 100644
--- a/setuptools/__init__.py
+++ b/setuptools/__init__.py
@@ -8,11 +8,12 @@ from distutils.core import Command as _Command
from distutils.util import convert_path
from fnmatch import fnmatchcase
+from setuptools.extern.six.moves import filterfalse, map
+
import pkg_resources
from setuptools.extension import Extension
from setuptools.dist import Distribution, Feature, _get_unpatched
from setuptools.depends import Require
-from setuptools.compat import filterfalse
__all__ = [
'setup', 'Distribution', 'Feature', 'Command', 'Extension', 'Require',
diff --git a/setuptools/command/__init__.py b/setuptools/command/__init__.py
index f6dbc39c..3fb2f6df 100644
--- a/setuptools/command/__init__.py
+++ b/setuptools/command/__init__.py
@@ -2,7 +2,7 @@ __all__ = [
'alias', 'bdist_egg', 'bdist_rpm', 'build_ext', 'build_py', 'develop',
'easy_install', 'egg_info', 'install', 'install_lib', 'rotate', 'saveopts',
'sdist', 'setopt', 'test', 'install_egg_info', 'install_scripts',
- 'register', 'bdist_wininst', 'upload_docs',
+ 'register', 'bdist_wininst', 'upload_docs', 'upload',
]
from distutils.command.bdist import bdist
diff --git a/setuptools/command/alias.py b/setuptools/command/alias.py
index 452a9244..4532b1cc 100755
--- a/setuptools/command/alias.py
+++ b/setuptools/command/alias.py
@@ -1,5 +1,7 @@
from distutils.errors import DistutilsOptionError
+from setuptools.extern.six.moves import map
+
from setuptools.command.setopt import edit_config, option_base, config_file
diff --git a/setuptools/command/bdist_egg.py b/setuptools/command/bdist_egg.py
index 87dce882..9cebd7fa 100644
--- a/setuptools/command/bdist_egg.py
+++ b/setuptools/command/bdist_egg.py
@@ -11,9 +11,10 @@ import os
import marshal
import textwrap
+from setuptools.extern import six
+
from pkg_resources import get_build_platform, Distribution, ensure_directory
from pkg_resources import EntryPoint
-from setuptools.compat import basestring
from setuptools.extension import Library
from setuptools import Command
@@ -413,7 +414,7 @@ def iter_symbols(code):
for name in code.co_names:
yield name
for const in code.co_consts:
- if isinstance(const, basestring):
+ if isinstance(const, six.string_types):
yield const
elif isinstance(const, CodeType):
for name in iter_symbols(const):
diff --git a/setuptools/command/build_py.py b/setuptools/command/build_py.py
index 8a50f032..8623c777 100644
--- a/setuptools/command/build_py.py
+++ b/setuptools/command/build_py.py
@@ -9,6 +9,7 @@ import distutils.errors
import collections
import itertools
+from setuptools.extern.six.moves import map
try:
from setuptools.lib2to3_ex import Mixin2to3
@@ -59,9 +60,10 @@ class build_py(orig.build_py, Mixin2to3):
self.byte_compile(orig.build_py.get_outputs(self, include_bytecode=0))
def __getattr__(self, attr):
- if attr == 'data_files': # lazily compute data files
- self.data_files = files = self._get_data_files()
- return files
+ "lazily compute data files"
+ if attr == 'data_files':
+ self.data_files = self._get_data_files()
+ return self.data_files
return orig.build_py.__getattr__(self, attr)
def build_module(self, module, module_file, package):
@@ -74,23 +76,21 @@ class build_py(orig.build_py, Mixin2to3):
def _get_data_files(self):
"""Generate list of '(package,src_dir,build_dir,filenames)' tuples"""
self.analyze_manifest()
- data = []
- for package in self.packages or ():
- # Locate package source directory
- src_dir = self.get_package_dir(package)
+ return list(map(self._get_pkg_data_files, self.packages or ()))
- # Compute package build directory
- build_dir = os.path.join(*([self.build_lib] + package.split('.')))
+ def _get_pkg_data_files(self, package):
+ # Locate package source directory
+ src_dir = self.get_package_dir(package)
- # Length of path to strip from found files
- plen = len(src_dir) + 1
+ # Compute package build directory
+ build_dir = os.path.join(*([self.build_lib] + package.split('.')))
- # Strip directory from globbed filenames
- filenames = [
- file[plen:] for file in self.find_data_files(package, src_dir)
- ]
- data.append((package, src_dir, build_dir, filenames))
- return data
+ # Strip directory from globbed filenames
+ filenames = [
+ os.path.relpath(file, src_dir)
+ for file in self.find_data_files(package, src_dir)
+ ]
+ return package, src_dir, build_dir, filenames
def find_data_files(self, package, src_dir):
"""Return filenames for package's data files in 'src_dir'"""
diff --git a/setuptools/command/develop.py b/setuptools/command/develop.py
index 07b66ccb..11b5df10 100755
--- a/setuptools/command/develop.py
+++ b/setuptools/command/develop.py
@@ -5,9 +5,10 @@ import os
import glob
import io
+from setuptools.extern import six
+
from pkg_resources import Distribution, PathMetadata, normalize_path
from setuptools.command.easy_install import easy_install
-from setuptools.compat import PY3
import setuptools
@@ -87,7 +88,7 @@ class develop(easy_install):
" installation directory", p, normalize_path(os.curdir))
def install_for_development(self):
- if PY3 and getattr(self.distribution, 'use_2to3', False):
+ if six.PY3 and getattr(self.distribution, 'use_2to3', False):
# If we run 2to3 we can not do this inplace:
# Ensure metadata is up-to-date
diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py
index 9ca3b515..ea5cb028 100755
--- a/setuptools/command/easy_install.py
+++ b/setuptools/command/easy_install.py
@@ -40,6 +40,9 @@ import subprocess
import shlex
import io
+from setuptools.extern import six
+from setuptools.extern.six.moves import configparser, map
+
from setuptools import Command
from setuptools.sandbox import run_setup
from setuptools.py31compat import get_path, get_config_vars
@@ -48,8 +51,6 @@ 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 setuptools.compat import (iteritems, maxsize, basestring, unicode,
- reraise, PY2, PY3)
from pkg_resources import (
yield_lines, normalize_path, resource_string, ensure_directory,
get_distribution, find_distributions, Environment, Requirement,
@@ -82,13 +83,13 @@ def samefile(p1, p2):
return norm_p1 == norm_p2
-if PY2:
+if six.PY2:
def _to_ascii(s):
return s
def isascii(s):
try:
- unicode(s, 'ascii')
+ six.text_type(s, 'ascii')
return True
except UnicodeError:
return False
@@ -320,7 +321,7 @@ class easy_install(Command):
self.local_index = Environment(self.shadow_path + sys.path)
if self.find_links is not None:
- if isinstance(self.find_links, basestring):
+ if isinstance(self.find_links, six.string_types):
self.find_links = self.find_links.split()
else:
self.find_links = []
@@ -413,7 +414,7 @@ class easy_install(Command):
try:
pid = os.getpid()
except:
- pid = random.randint(0, maxsize)
+ pid = random.randint(0, sys.maxsize)
return os.path.join(self.install_dir, "test-easy-install-%s" % pid)
def warn_deprecated_options(self):
@@ -709,10 +710,7 @@ class easy_install(Command):
elif requirement is None or dist not in requirement:
# if we wound up with a different version, resolve what we've got
distreq = dist.as_requirement()
- requirement = requirement or distreq
- requirement = Requirement(
- distreq.project_name, distreq.specs, requirement.extras
- )
+ requirement = Requirement(str(distreq))
log.info("Processing dependencies for %s", requirement)
try:
distros = WorkingSet([]).resolve(
@@ -782,7 +780,7 @@ class easy_install(Command):
There are a couple of template scripts in the package. This
function loads one of them and prepares it for use.
"""
- # See https://bitbucket.org/pypa/setuptools/issue/134 for info
+ # See https://github.com/pypa/setuptools/issues/134 for info
# on script file naming and downstream issues with SVR4
name = 'script.tmpl'
if dev_path:
@@ -1238,17 +1236,14 @@ class easy_install(Command):
sitepy = os.path.join(self.install_dir, "site.py")
source = resource_string("setuptools", "site-patch.py")
+ source = source.decode('utf-8')
current = ""
if os.path.exists(sitepy):
log.debug("Checking existing site.py in %s", self.install_dir)
- f = open(sitepy, 'rb')
- current = f.read()
- # we want str, not bytes
- if PY3:
- current = current.decode()
+ with io.open(sitepy) as strm:
+ current = strm.read()
- f.close()
if not current.startswith('def __boot():'):
raise DistutilsError(
"%s is not a setuptools-generated site.py; please"
@@ -1259,9 +1254,8 @@ class easy_install(Command):
log.info("Creating %s", sitepy)
if not self.dry_run:
ensure_directory(sitepy)
- f = open(sitepy, 'wb')
- f.write(source)
- f.close()
+ with io.open(sitepy, 'w', encoding='utf-8') as strm:
+ strm.write(source)
self.byte_compile([sitepy])
self.sitepy_installed = True
@@ -1271,7 +1265,7 @@ class easy_install(Command):
if not self.user:
return
home = convert_path(os.path.expanduser("~"))
- for name, path in iteritems(self.config_vars):
+ for name, path in six.iteritems(self.config_vars):
if path.startswith(home) and not os.path.isdir(path):
self.debug_print("os.makedirs('%s', 0o700)" % path)
os.makedirs(path, 0o700)
@@ -1415,9 +1409,6 @@ def extract_wininst_cfg(dist_filename):
return None
f.seek(prepended - 12)
- 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
@@ -1432,7 +1423,7 @@ def extract_wininst_cfg(dist_filename):
# Now the config is in bytes, but for RawConfigParser, it should
# be text, so decode it.
config = config.decode(sys.getfilesystemencoding())
- cfg.readfp(StringIO(config))
+ cfg.readfp(six.StringIO(config))
except configparser.Error:
return None
if not cfg.has_section('metadata') or not cfg.has_section('Setup'):
@@ -1467,7 +1458,7 @@ def get_exe_prefixes(exe_filename):
continue
if parts[0].upper() in ('PURELIB', 'PLATLIB'):
contents = z.read(name)
- if PY3:
+ if six.PY3:
contents = contents.decode()
for pth in yield_lines(contents):
pth = pth.strip().replace('\\', '/')
@@ -1643,7 +1634,7 @@ def auto_chmod(func, arg, exc):
chmod(arg, stat.S_IWRITE)
return func(arg)
et, ev, _ = sys.exc_info()
- reraise(et, (ev[0], ev[1] + (" %s %s" % (func, arg))))
+ six.reraise(et, (ev[0], ev[1] + (" %s %s" % (func, arg))))
def update_dist_caches(dist_path, fix_zipimporter_caches):
@@ -1771,7 +1762,7 @@ def _update_zipimporter_cache(normalized_path, cache, updater=None):
# * Does not support the dict.pop() method, forcing us to use the
# get/del patterns instead. For more detailed information see the
# following links:
- # https://bitbucket.org/pypa/setuptools/issue/202/more-robust-zipimporter-cache-invalidation#comment-10495960
+ # https://github.com/pypa/setuptools/issues/202#issuecomment-202913420
# https://bitbucket.org/pypy/pypy/src/dd07756a34a41f674c0cacfbc8ae1d4cc9ea2ae4/pypy/module/zipimport/interp_zipimport.py#cl-99
old_entry = cache[p]
del cache[p]
@@ -1878,17 +1869,6 @@ def chmod(path, mode):
log.debug("chmod failed: %s", e)
-def fix_jython_executable(executable, options):
- warnings.warn("Use JythonCommandSpec", DeprecationWarning, stacklevel=2)
-
- if not JythonCommandSpec.relevant():
- return executable
-
- cmd = CommandSpec.best().from_param(executable)
- cmd.install_options(options)
- return cmd.as_header().lstrip('#!').rstrip('\n')
-
-
class CommandSpec(list):
"""
A command spec for a #! header, specified as a list of arguments akin to
@@ -1903,7 +1883,7 @@ class CommandSpec(list):
"""
Choose the best CommandSpec class based on environmental conditions.
"""
- return cls if not JythonCommandSpec.relevant() else JythonCommandSpec
+ return cls
@classmethod
def _sys_executable(cls):
@@ -1970,36 +1950,6 @@ class WindowsCommandSpec(CommandSpec):
split_args = dict(posix=False)
-class JythonCommandSpec(CommandSpec):
- @classmethod
- def relevant(cls):
- return (
- sys.platform.startswith('java')
- and
- __import__('java').lang.System.getProperty('os.name') != 'Linux'
- )
-
- def as_header(self):
- """
- Workaround Jython's sys.executable being a .sh (an invalid
- shebang line interpreter)
- """
- if not is_sh(self[0]):
- return super(JythonCommandSpec, self).as_header()
-
- if self.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.")
- return super(JythonCommandSpec, self).as_header()
-
- items = ['/usr/bin/env'] + self + list(self.options)
- return self._render(items)
-
-
class ScriptWriter(object):
"""
Encapsulates behavior around writing entry point scripts for console and
@@ -2076,7 +2026,10 @@ class ScriptWriter(object):
"""
Select the best ScriptWriter for this environment.
"""
- return WindowsScriptWriter.best() if sys.platform == 'win32' else cls
+ if sys.platform == 'win32' or (os.name == 'java' and os._name == 'nt'):
+ return WindowsScriptWriter.best()
+ else:
+ return cls
@classmethod
def _get_script_args(cls, type_, name, header, script_text):
@@ -2209,7 +2162,7 @@ def get_win_launcher(type):
def load_launcher_manifest(name):
manifest = pkg_resources.resource_string(__name__, 'launcher manifest.xml')
- if PY2:
+ if six.PY2:
return manifest % vars()
else:
return manifest.decode('utf-8') % vars()
diff --git a/setuptools/command/egg_info.py b/setuptools/command/egg_info.py
index 1301bd84..d1bd9b04 100755
--- a/setuptools/command/egg_info.py
+++ b/setuptools/command/egg_info.py
@@ -14,7 +14,8 @@ import io
import warnings
import time
-from setuptools.compat import basestring, PY3, StringIO
+from setuptools.extern import six
+from setuptools.extern.six.moves import map
from setuptools import Command
from setuptools.command.sdist import sdist
@@ -26,7 +27,7 @@ from pkg_resources import (
safe_version, yield_lines, EntryPoint, iter_entry_points, to_filename)
import setuptools.unicode_utils as unicode_utils
-from pkg_resources import packaging
+from pkg_resources.extern import packaging
try:
from setuptools_svn import svn_utils
@@ -148,7 +149,7 @@ class egg_info(Command):
to the file.
"""
log.info("writing %s to %s", what, filename)
- if PY3:
+ if six.PY3:
data = data.encode("utf-8")
if not self.dry_run:
f = open(filename, 'wb')
@@ -412,7 +413,7 @@ def _write_requirements(stream, reqs):
def write_requirements(cmd, basename, filename):
dist = cmd.distribution
- data = StringIO()
+ data = six.StringIO()
_write_requirements(data, dist.install_requires)
extras_require = dist.extras_require or {}
for extra in sorted(extras_require):
@@ -452,12 +453,12 @@ def write_arg(cmd, basename, filename, force=False):
def write_entries(cmd, basename, filename):
ep = cmd.distribution.entry_points
- if isinstance(ep, basestring) or ep is None:
+ if isinstance(ep, six.string_types) or ep is None:
data = ep
elif ep is not None:
data = []
for section, contents in sorted(ep.items()):
- if not isinstance(contents, basestring):
+ if not isinstance(contents, six.string_types):
contents = EntryPoint.parse_group(section, contents)
contents = '\n'.join(sorted(map(str, contents.values())))
data.append('[%s]\n%s\n\n' % (section, contents))
diff --git a/setuptools/command/install.py b/setuptools/command/install.py
index d2bca2ec..31a5ddb5 100644
--- a/setuptools/command/install.py
+++ b/setuptools/command/install.py
@@ -8,7 +8,7 @@ import distutils.command.install as orig
import setuptools
# Prior to numpy 1.9, NumPy relies on the '_install' name, so provide it for
-# now. See https://bitbucket.org/pypa/setuptools/issue/199/
+# now. See https://github.com/pypa/setuptools/issues/199/
_install = orig.install
diff --git a/setuptools/command/install_egg_info.py b/setuptools/command/install_egg_info.py
index fd0f118b..60b615d2 100755
--- a/setuptools/command/install_egg_info.py
+++ b/setuptools/command/install_egg_info.py
@@ -1,6 +1,8 @@
from distutils import log, dir_util
import os
+from setuptools.extern.six.moves import map
+
from setuptools import Command
from setuptools.archive_util import unpack_archive
import pkg_resources
@@ -27,7 +29,7 @@ class install_egg_info(Command):
).egg_name() + '.egg-info'
self.source = ei_cmd.egg_info
self.target = os.path.join(self.install_dir, basename)
- self.outputs = [self.target]
+ self.outputs = []
def run(self):
self.run_command('egg_info')
diff --git a/setuptools/command/rotate.py b/setuptools/command/rotate.py
index 1b073620..804f962a 100755
--- a/setuptools/command/rotate.py
+++ b/setuptools/command/rotate.py
@@ -3,8 +3,9 @@ from distutils import log
from distutils.errors import DistutilsOptionError
import os
+from setuptools.extern import six
+
from setuptools import Command
-from setuptools.compat import basestring
class rotate(Command):
@@ -36,7 +37,7 @@ class rotate(Command):
self.keep = int(self.keep)
except ValueError:
raise DistutilsOptionError("--keep must be an integer")
- if isinstance(self.match, basestring):
+ if isinstance(self.match, six.string_types):
self.match = [
convert_path(p.strip()) for p in self.match.split(',')
]
diff --git a/setuptools/command/sdist.py b/setuptools/command/sdist.py
index 71196512..6640d4e3 100755
--- a/setuptools/command/sdist.py
+++ b/setuptools/command/sdist.py
@@ -5,7 +5,8 @@ import os
import sys
import io
-from setuptools.compat import PY3
+from setuptools.extern import six
+
from setuptools.utils import cs_path_exists
import pkg_resources
@@ -181,7 +182,7 @@ class sdist(orig.sdist):
manifest = open(self.manifest, 'rbU')
for line in manifest:
# The manifest must contain UTF-8. See #303.
- if PY3:
+ if six.PY3:
try:
line = line.decode('UTF-8')
except UnicodeDecodeError:
diff --git a/setuptools/command/setopt.py b/setuptools/command/setopt.py
index 74c7cad8..7f332be5 100755
--- a/setuptools/command/setopt.py
+++ b/setuptools/command/setopt.py
@@ -4,6 +4,8 @@ from distutils.errors import DistutilsOptionError
import distutils
import os
+from setuptools.extern.six.moves import configparser
+
from setuptools import Command
@@ -37,8 +39,6 @@ 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 setuptools.compat import configparser
-
log.debug("Reading configuration from %s", filename)
opts = configparser.RawConfigParser()
opts.read([filename])
diff --git a/setuptools/command/test.py b/setuptools/command/test.py
index c26f5fc9..371e913b 100644
--- a/setuptools/command/test.py
+++ b/setuptools/command/test.py
@@ -2,11 +2,13 @@ from distutils.errors import DistutilsOptionError
from unittest import TestLoader
import sys
+from setuptools.extern import six
+from setuptools.extern.six.moves import map
+
from pkg_resources import (resource_listdir, resource_exists, normalize_path,
working_set, _namespace_packages,
add_activation_listener, require, EntryPoint)
from setuptools import Command
-from setuptools.compat import PY3
from setuptools.py31compat import unittest_main
@@ -100,7 +102,7 @@ class test(Command):
yield self.test_suite
def with_project_on_sys_path(self, func):
- with_2to3 = PY3 and getattr(self.distribution, 'use_2to3', False)
+ with_2to3 = six.PY3 and getattr(self.distribution, 'use_2to3', False)
if with_2to3:
# If we run 2to3 we can not do this inplace:
@@ -160,7 +162,7 @@ class test(Command):
# Purge modules under test from sys.modules. The test loader will
# re-import them from the build location. Required when 2to3 is used
# with namespace packages.
- if PY3 and getattr(self.distribution, 'use_2to3', False):
+ if six.PY3 and getattr(self.distribution, 'use_2to3', False):
module = self.test_suite.split('.')[0]
if module in _namespace_packages:
del_modules = []
diff --git a/setuptools/command/upload.py b/setuptools/command/upload.py
new file mode 100644
index 00000000..08c20ba8
--- /dev/null
+++ b/setuptools/command/upload.py
@@ -0,0 +1,23 @@
+from distutils.command import upload as orig
+
+
+class upload(orig.upload):
+ """
+ Override default upload behavior to look up password
+ in the keyring if available.
+ """
+
+ def finalize_options(self):
+ orig.upload.finalize_options(self)
+ self.password or self._load_password_from_keyring()
+
+ def _load_password_from_keyring(self):
+ """
+ Attempt to load password from keyring. Suppress Exceptions.
+ """
+ try:
+ keyring = __import__('keyring')
+ self.password = keyring.get_password(self.repository,
+ self.username)
+ except Exception:
+ pass
diff --git a/setuptools/command/upload_docs.py b/setuptools/command/upload_docs.py
index 001ee936..f887b47e 100644
--- a/setuptools/command/upload_docs.py
+++ b/setuptools/command/upload_docs.py
@@ -8,25 +8,26 @@ PyPI's pythonhosted.org).
from base64 import standard_b64encode
from distutils import log
from distutils.errors import DistutilsOptionError
-from distutils.command.upload import upload
import os
import socket
import zipfile
import tempfile
-import sys
import shutil
-from setuptools.compat import httplib, urlparse, unicode, iteritems, PY3
+from setuptools.extern import six
+from setuptools.extern.six.moves import http_client, urllib
+
from pkg_resources import iter_entry_points
+from .upload import upload
-errors = 'surrogateescape' if PY3 else 'strict'
+errors = 'surrogateescape' if six.PY3 else '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):
+ if isinstance(s, six.text_type):
return s.encode(encoding, errors)
return s
@@ -113,7 +114,7 @@ class upload_docs(upload):
# set up the authentication
credentials = b(self.username + ':' + self.password)
credentials = standard_b64encode(credentials)
- if PY3:
+ if six.PY3:
credentials = credentials.decode('ascii')
auth = "Basic " + credentials
@@ -122,7 +123,7 @@ class upload_docs(upload):
sep_boundary = b('\n--') + b(boundary)
end_boundary = sep_boundary + b('--')
body = []
- for key, values in iteritems(data):
+ for key, values in six.iteritems(data):
title = '\nContent-Disposition: form-data; name="%s"' % key
# handle multiple entries for the same name
if not isinstance(values, list):
@@ -150,12 +151,12 @@ 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(self.repository)
+ urllib.parse.urlparse(self.repository)
assert not params and not query and not fragments
if schema == 'http':
- conn = httplib.HTTPConnection(netloc)
+ conn = http_client.HTTPConnection(netloc)
elif schema == 'https':
- conn = httplib.HTTPSConnection(netloc)
+ conn = http_client.HTTPSConnection(netloc)
else:
raise AssertionError("unsupported schema " + schema)
diff --git a/setuptools/compat.py b/setuptools/compat.py
deleted file mode 100644
index f0175a5d..00000000
--- a/setuptools/compat.py
+++ /dev/null
@@ -1,70 +0,0 @@
-import sys
-import itertools
-
-PY3 = sys.version_info >= (3,)
-PY2 = not PY3
-
-if PY2:
- basestring = basestring
- import __builtin__ as builtins
- import ConfigParser as configparser
- from StringIO import StringIO
- BytesIO = StringIO
- 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
- unichr = unichr
- unicode = unicode
- bytes = str
- from urllib import url2pathname, splittag, pathname2url
- import urllib2
- from urllib2 import urlopen, HTTPError, URLError, unquote, splituser
- from urlparse import urlparse, urlunparse, urljoin, urlsplit, urlunsplit
- filterfalse = itertools.ifilterfalse
- filter = itertools.ifilter
- map = itertools.imap
-
- exec("""def reraise(tp, value, tb=None):
- raise tp, value, tb""")
-
-if PY3:
- basestring = str
- import builtins
- import configparser
- 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
- unichr = chr
- unicode = str
- bytes = bytes
- from urllib.error import HTTPError, URLError
- import urllib.request as urllib2
- from urllib.request import urlopen, url2pathname, pathname2url
- from urllib.parse import (
- urlparse, urlunparse, unquote, splituser, urljoin, urlsplit,
- urlunsplit, splittag,
- )
- filterfalse = itertools.filterfalse
- filter = filter
- map = map
-
- 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 e87ef3f3..9f7c9a35 100644
--- a/setuptools/depends.py
+++ b/setuptools/depends.py
@@ -3,7 +3,8 @@ import imp
import marshal
from imp import PKG_DIRECTORY, PY_COMPILED, PY_SOURCE, PY_FROZEN
from distutils.version import StrictVersion
-from setuptools import compat
+
+from setuptools.extern import six
__all__ = [
'Require', 'find_module', 'get_module_constant', 'extract_constant'
@@ -99,7 +100,8 @@ def _iter_code(code):
ptr += 3
if op==EXTENDED_ARG:
- extended_arg = arg * compat.long_type(65536)
+ long_type = six.integer_types[-1]
+ extended_arg = arg * long_type(65536)
continue
else:
diff --git a/setuptools/dist.py b/setuptools/dist.py
index b0c52838..086e0a58 100644
--- a/setuptools/dist.py
+++ b/setuptools/dist.py
@@ -13,13 +13,14 @@ from distutils.core import Distribution as _Distribution
from distutils.errors import (DistutilsOptionError, DistutilsPlatformError,
DistutilsSetupError)
+from setuptools.extern import six
+from setuptools.extern.six.moves import map
+from pkg_resources.extern import packaging
+
from setuptools.depends import Require
-from setuptools.compat import basestring, PY2
from setuptools import windows_support
import pkg_resources
-packaging = pkg_resources.packaging
-
def _get_unpatched(cls):
"""Protect against re-patching the distutils if reloaded
@@ -138,7 +139,7 @@ def check_entry_points(dist, attr, value):
raise DistutilsSetupError(e)
def check_test_suite(dist, attr, value):
- if not isinstance(value,basestring):
+ if not isinstance(value, six.string_types):
raise DistutilsSetupError("test_suite must be a string")
def check_package_data(dist, attr, value):
@@ -160,7 +161,7 @@ def check_packages(dist, attr, value):
for pkgname in value:
if not re.match(r'\w+(\.\w+)*', pkgname):
distutils.log.warn(
- "WARNING: %r not a valid package name; please use only"
+ "WARNING: %r not a valid package name; please use only "
".-separated package names in setup.py", pkgname
)
@@ -439,6 +440,14 @@ class Distribution(_Distribution):
self.cmdclass[ep.name] = cmdclass
return _Distribution.print_commands(self)
+ def get_command_list(self):
+ for ep in pkg_resources.iter_entry_points('distutils.commands'):
+ if ep.name not in self.cmdclass:
+ # don't require extras as the commands won't be invoked
+ cmdclass = ep.resolve()
+ self.cmdclass[ep.name] = cmdclass
+ return _Distribution.get_command_list(self)
+
def _set_feature(self,name,status):
"""Set feature's inclusion status"""
setattr(self,self._feature_attrname(name),status)
@@ -674,7 +683,7 @@ class Distribution(_Distribution):
"""
import sys
- if PY2 or self.help_commands:
+ if six.PY2 or self.help_commands:
return _Distribution.handle_display_options(self, option_order)
# Stdout may be StringIO (e.g. in tests)
@@ -711,7 +720,7 @@ class Feature:
"""
**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
+ <https://github.com/pypa/setuptools/issues/58>`_ and will be removed in
a future version.
A subset of the distribution that can be excluded if unneeded/wanted
@@ -768,7 +777,7 @@ class Feature:
def warn_deprecated():
warnings.warn(
"Features are deprecated and will be removed in a future "
- "version. See http://bitbucket.org/pypa/setuptools/65.",
+ "version. See https://github.com/pypa/setuptools/issues/65.",
DeprecationWarning,
stacklevel=3,
)
@@ -817,7 +826,7 @@ class Feature:
if not self.available:
raise DistutilsPlatformError(
- self.description+" is required,"
+ self.description+" is required, "
"but is not available on this platform"
)
diff --git a/setuptools/extension.py b/setuptools/extension.py
index 35eb7c7c..d10609b6 100644
--- a/setuptools/extension.py
+++ b/setuptools/extension.py
@@ -5,6 +5,8 @@ import distutils.core
import distutils.errors
import distutils.extension
+from setuptools.extern.six.moves import map
+
from .dist import _get_unpatched
from . import msvc9_support
diff --git a/setuptools/extern/__init__.py b/setuptools/extern/__init__.py
new file mode 100644
index 00000000..6859aa5b
--- /dev/null
+++ b/setuptools/extern/__init__.py
@@ -0,0 +1,5 @@
+from pkg_resources.extern import VendorImporter
+
+
+names = 'six',
+VendorImporter(__name__, names, 'pkg_resources._vendor').install()
diff --git a/setuptools/launch.py b/setuptools/launch.py
new file mode 100644
index 00000000..06e15e1e
--- /dev/null
+++ b/setuptools/launch.py
@@ -0,0 +1,35 @@
+"""
+Launch the Python script on the command line after
+setuptools is bootstrapped via import.
+"""
+
+# Note that setuptools gets imported implicitly by the
+# invocation of this script using python -m setuptools.launch
+
+import tokenize
+import sys
+
+
+def run():
+ """
+ Run the script in sys.argv[1] as if it had
+ been invoked naturally.
+ """
+ __builtins__
+ script_name = sys.argv[1]
+ namespace = dict(
+ __file__ = script_name,
+ __name__ = '__main__',
+ __doc__ = None,
+ )
+ sys.argv[:] = sys.argv[1:]
+
+ open_ = getattr(tokenize, 'open', open)
+ script = open_(script_name).read()
+ norm_script = script.replace('\\r\\n', '\\n')
+ code = compile(norm_script, script_name, 'exec')
+ exec(code, namespace)
+
+
+if __name__ == '__main__':
+ run()
diff --git a/setuptools/package_index.py b/setuptools/package_index.py
index 2c565e88..c53343e4 100755
--- a/setuptools/package_index.py
+++ b/setuptools/package_index.py
@@ -9,6 +9,14 @@ import hashlib
import itertools
from functools import wraps
+try:
+ from urllib.parse import splituser
+except ImportError:
+ from urllib2 import splituser
+
+from setuptools.extern import six
+from setuptools.extern.six.moves import urllib, http_client, configparser, map
+
from pkg_resources import (
CHECKOUT_DIST, Distribution, BINARY_DIST, normalize_path, SOURCE_DIST,
require, Environment, find_distributions, safe_name, safe_version,
@@ -17,12 +25,6 @@ from pkg_resources import (
from setuptools import ssl_support
from distutils import log
from distutils.errors import DistutilsError
-from setuptools.compat import (
- urllib2, httplib, StringIO, HTTPError, urlparse, urlunparse, unquote,
- splituser, url2pathname, name2codepoint, unichr, urljoin, urlsplit,
- urlunsplit, configparser, filter, map,
-)
-from setuptools.compat import filterfalse
from fnmatch import translate
from setuptools.py26compat import strip_fragment
from setuptools.py27compat import get_all_headers
@@ -69,10 +71,11 @@ def parse_bdist_wininst(name):
def egg_info_for_url(url):
- scheme, server, path, parameters, query, fragment = urlparse(url)
- base = unquote(path.split('/')[-1])
+ parts = urllib.parse.urlparse(url)
+ scheme, server, path, parameters, query, fragment = parts
+ base = urllib.parse.unquote(path.split('/')[-1])
if server=='sourceforge.net' and base=='download': # XXX Yuck
- base = unquote(path.split('/')[-2])
+ base = urllib.parse.unquote(path.split('/')[-2])
if '#' in base: base, fragment = base.split('#',1)
return base,fragment
@@ -158,7 +161,7 @@ def unique_everseen(iterable, key=None):
seen = set()
seen_add = seen.add
if key is None:
- for element in filterfalse(seen.__contains__, iterable):
+ for element in six.moves.filterfalse(seen.__contains__, iterable):
seen_add(element)
yield element
else:
@@ -190,14 +193,14 @@ def find_external_links(url, page):
rels = set(map(str.strip, rel.lower().split(',')))
if 'homepage' in rels or 'download' in rels:
for match in HREF.finditer(tag):
- yield urljoin(url, htmldecode(match.group(1)))
+ yield urllib.parse.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 urljoin(url, htmldecode(match.group(1)))
+ yield urllib.parse.urljoin(url, htmldecode(match.group(1)))
user_agent = "Python-urllib/%s setuptools/%s" % (
sys.version[:3], require('setuptools')[0].version
@@ -240,7 +243,7 @@ class HashChecker(ContentChecker):
@classmethod
def from_url(cls, url):
"Construct a (possibly null) ContentChecker from a URL"
- fragment = urlparse(url)[-1]
+ fragment = urllib.parse.urlparse(url)[-1]
if not fragment:
return ContentChecker()
match = cls.pattern.search(fragment)
@@ -275,7 +278,7 @@ class PackageIndex(Environment):
self.to_scan = []
if verify_ssl and ssl_support.is_available and (ca_bundle or ssl_support.find_ca_bundle()):
self.opener = ssl_support.opener_for(ca_bundle)
- else: self.opener = urllib2.urlopen
+ else: self.opener = urllib.request.urlopen
def process_url(self, url, retrieve=False):
"""Evaluate a URL as a possible download, and maybe retrieve it"""
@@ -312,7 +315,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, HTTPError):
+ if isinstance(f, urllib.error.HTTPError):
# Errors have no charset, assume latin1:
charset = 'latin-1'
else:
@@ -320,7 +323,7 @@ class PackageIndex(Environment):
page = page.decode(charset, "ignore")
f.close()
for match in HREF.finditer(page):
- link = urljoin(base, htmldecode(match.group(1)))
+ link = urllib.parse.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)
@@ -343,7 +346,7 @@ class PackageIndex(Environment):
def url_ok(self, url, fatal=False):
s = URL_SCHEME(url)
- if (s and s.group(1).lower()=='file') or self.allows(urlparse(url)[1]):
+ if (s and s.group(1).lower()=='file') or self.allows(urllib.parse.urlparse(url)[1]):
return True
msg = ("\nNote: Bypassing %s (disallowed host; see "
"http://bit.ly/1dg9ijs for details).\n")
@@ -384,7 +387,7 @@ class PackageIndex(Environment):
# Process a URL to see if it's for a package page
if link.startswith(self.index_url):
parts = list(map(
- unquote, link[len(self.index_url):].split('/')
+ urllib.parse.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
@@ -397,7 +400,7 @@ class PackageIndex(Environment):
# process an index page into the package-page index
for match in HREF.finditer(page):
try:
- scan(urljoin(url, htmldecode(match.group(1))))
+ scan(urllib.parse.urljoin(url, htmldecode(match.group(1))))
except ValueError:
pass
@@ -673,7 +676,7 @@ class PackageIndex(Environment):
try:
checker = HashChecker.from_url(url)
fp = self.open_url(strip_fragment(url))
- if isinstance(fp, HTTPError):
+ if isinstance(fp, urllib.error.HTTPError):
raise DistutilsError(
"Can't download %s: %s %s" % (url, fp.code,fp.msg)
)
@@ -709,21 +712,21 @@ class PackageIndex(Environment):
return local_open(url)
try:
return open_with_auth(url, self.opener)
- except (ValueError, httplib.InvalidURL) as v:
+ except (ValueError, http_client.InvalidURL) as v:
msg = ' '.join([str(arg) for arg in v.args])
if warning:
self.warn(warning, msg)
else:
raise DistutilsError('%s %s' % (url, msg))
- except urllib2.HTTPError as v:
+ except urllib.error.HTTPError as v:
return v
- except urllib2.URLError as v:
+ except urllib.error.URLError as v:
if warning:
self.warn(warning, v.reason)
else:
raise DistutilsError("Download error for %s: %s"
% (url, v.reason))
- except httplib.BadStatusLine as v:
+ except http_client.BadStatusLine as v:
if warning:
self.warn(warning, v.line)
else:
@@ -732,7 +735,7 @@ class PackageIndex(Environment):
'down, %s' %
(url, v.line)
)
- except httplib.HTTPException as v:
+ except http_client.HTTPException as v:
if warning:
self.warn(warning, v)
else:
@@ -763,7 +766,7 @@ class PackageIndex(Environment):
elif scheme.startswith('hg+'):
return self._download_hg(url, filename)
elif scheme=='file':
- return url2pathname(urlparse(url)[2])
+ return urllib.request.url2pathname(urllib.parse.urlparse(url)[2])
else:
self.url_ok(url, True) # raises error if not allowed
return self._attempt_download(url, filename)
@@ -797,7 +800,7 @@ class PackageIndex(Environment):
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(url)
+ scheme, netloc, path, p, q, f = urllib.parse.urlparse(url)
if not netloc and path.startswith('//') and '/' in path[2:]:
netloc, path = path[2:].split('/',1)
auth, host = splituser(netloc)
@@ -808,14 +811,15 @@ class PackageIndex(Environment):
else:
creds = " --username="+auth
netloc = host
- url = urlunparse((scheme, netloc, url, p, q, f))
+ parts = scheme, netloc, url, p, q, f
+ url = urllib.parse.urlunparse(parts)
self.info("Doing subversion checkout from %s to %s", url, filename)
os.system("svn checkout%s -q %s %s" % (creds, url, filename))
return filename
@staticmethod
def _vcs_split_rev_from_url(url, pop_prefix=False):
- scheme, netloc, path, query, frag = urlsplit(url)
+ scheme, netloc, path, query, frag = urllib.parse.urlsplit(url)
scheme = scheme.split('+', 1)[-1]
@@ -827,7 +831,7 @@ class PackageIndex(Environment):
path, rev = path.rsplit('@', 1)
# Also, discard fragment
- url = urlunsplit((scheme, netloc, path, query, ''))
+ url = urllib.parse.urlunsplit((scheme, netloc, path, query, ''))
return url, rev
@@ -879,7 +883,7 @@ entity_sub = re.compile(r'&(#(\d+|x[\da-fA-F]+)|[\w.:-]+);?').sub
def uchr(c):
if not isinstance(c, int):
return c
- if c>255: return unichr(c)
+ if c>255: return six.unichr(c)
return chr(c)
def decode_entity(match):
@@ -889,7 +893,7 @@ def decode_entity(match):
elif what.startswith('#'):
what = int(what[1:])
else:
- what = name2codepoint.get(what, match.group(0))
+ what = six.moves.html_entities.name2codepoint.get(what, match.group(0))
return uchr(what)
def htmldecode(text):
@@ -920,7 +924,7 @@ def _encode_auth(auth):
>>> chr(10) in str(_encode_auth(long_auth))
False
"""
- auth_s = unquote(auth)
+ auth_s = urllib.parse.unquote(auth)
# convert to bytes
auth_bytes = auth_s.encode()
# use the legacy interface for Python 2.3 support
@@ -984,15 +988,15 @@ class PyPIConfig(configparser.RawConfigParser):
return cred
-def open_with_auth(url, opener=urllib2.urlopen):
+def open_with_auth(url, opener=urllib.request.urlopen):
"""Open a urllib2 request, handling HTTP authentication"""
- scheme, netloc, path, params, query, frag = urlparse(url)
+ scheme, netloc, path, params, query, frag = urllib.parse.urlparse(url)
# Double scheme does not raise on Mac OS X as revealed by a
# failing test. We would expect "nonnumeric port". Refs #20.
if netloc.endswith(':'):
- raise httplib.InvalidURL("nonnumeric port: ''")
+ raise http_client.InvalidURL("nonnumeric port: ''")
if scheme in ('http', 'https'):
auth, host = splituser(netloc)
@@ -1008,11 +1012,12 @@ def open_with_auth(url, opener=urllib2.urlopen):
if auth:
auth = "Basic " + _encode_auth(auth)
- new_url = urlunparse((scheme,host,path,params,query,frag))
- request = urllib2.Request(new_url)
+ parts = scheme, host, path, params, query, frag
+ new_url = urllib.parse.urlunparse(parts)
+ request = urllib.request.Request(new_url)
request.add_header("Authorization", auth)
else:
- request = urllib2.Request(url)
+ request = urllib.request.Request(url)
request.add_header('User-Agent', user_agent)
fp = opener(request)
@@ -1020,9 +1025,10 @@ 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(fp.url)
+ s2, h2, path2, param2, query2, frag2 = urllib.parse.urlparse(fp.url)
if s2==scheme and h2==host:
- fp.url = urlunparse((s2,netloc,path2,param2,query2,frag2))
+ parts = s2, netloc, path2, param2, query2, frag2
+ fp.url = urllib.parse.urlunparse(parts)
return fp
@@ -1035,10 +1041,10 @@ def fix_sf_url(url):
def local_open(url):
"""Read a local path, with special support for directories"""
- scheme, server, path, param, query, frag = urlparse(url)
- filename = url2pathname(path)
+ scheme, server, path, param, query, frag = urllib.parse.urlparse(url)
+ filename = urllib.request.url2pathname(path)
if os.path.isfile(filename):
- return urllib2.urlopen(url)
+ return urllib.request.urlopen(url)
elif path.endswith('/') and os.path.isdir(filename):
files = []
for f in os.listdir(filename):
@@ -1059,4 +1065,5 @@ def local_open(url):
status, message, body = 404, "Path not found", "Not found"
headers = {'content-type': 'text/html'}
- return HTTPError(url, status, message, headers, StringIO(body))
+ body_stream = six.StringIO(body)
+ return urllib.error.HTTPError(url, status, message, headers, body_stream)
diff --git a/setuptools/py26compat.py b/setuptools/py26compat.py
index 738b0cc4..e52bd85b 100644
--- a/setuptools/py26compat.py
+++ b/setuptools/py26compat.py
@@ -4,7 +4,10 @@ Compatibility Support for Python 2.6 and earlier
import sys
-from setuptools.compat import splittag
+try:
+ from urllib.parse import splittag
+except ImportError:
+ from urllib import splittag
def strip_fragment(url):
"""
diff --git a/setuptools/sandbox.py b/setuptools/sandbox.py
index 85de85ff..23e296b1 100755
--- a/setuptools/sandbox.py
+++ b/setuptools/sandbox.py
@@ -8,6 +8,9 @@ import re
import contextlib
import pickle
+from setuptools.extern import six
+from setuptools.extern.six.moves import builtins, map
+
import pkg_resources
if sys.platform.startswith('java'):
@@ -22,9 +25,6 @@ _open = open
from distutils.errors import DistutilsError
from pkg_resources import working_set
-from setuptools import compat
-from setuptools.compat import builtins
-
__all__ = [
"AbstractSandbox", "DirectorySandbox", "SandboxViolation", "run_setup",
]
@@ -138,7 +138,7 @@ class ExceptionSaver:
return
type, exc = map(pickle.loads, self._saved)
- compat.reraise(type, exc, self._tb)
+ six.reraise(type, exc, self._tb)
@contextlib.contextmanager
@@ -207,8 +207,12 @@ def _needs_hiding(mod_name):
True
>>> _needs_hiding('distutils')
True
+ >>> _needs_hiding('os')
+ False
+ >>> _needs_hiding('Cython')
+ True
"""
- pattern = re.compile('(setuptools|pkg_resources|distutils)(\.|$)')
+ pattern = re.compile('(setuptools|pkg_resources|distutils|Cython)(\.|$)')
return bool(pattern.match(mod_name))
diff --git a/setuptools/ssl_support.py b/setuptools/ssl_support.py
index cc7db067..657197cf 100644
--- a/setuptools/ssl_support.py
+++ b/setuptools/ssl_support.py
@@ -3,9 +3,10 @@ import socket
import atexit
import re
+from setuptools.extern.six.moves import urllib, http_client, map
+
import pkg_resources
from pkg_resources import ResolutionError, ExtractionError
-from setuptools.compat import urllib2
try:
import ssl
@@ -24,20 +25,15 @@ cert_paths = """
/usr/local/share/certs/ca-root.crt
/etc/ssl/cert.pem
/System/Library/OpenSSL/certs/cert.pem
+/usr/local/share/certs/ca-root-nss.crt
""".strip().split()
-HTTPSHandler = HTTPSConnection = object
-
-for what, where in (
- ('HTTPSHandler', ['urllib2','urllib.request']),
- ('HTTPSConnection', ['httplib', 'http.client']),
-):
- for module in where:
- try:
- exec("from %s import %s" % (module, what))
- except ImportError:
- pass
+try:
+ HTTPSHandler = urllib.request.HTTPSHandler
+ HTTPSConnection = http_client.HTTPSConnection
+except AttributeError:
+ HTTPSHandler = HTTPSConnection = object
is_available = ssl is not None and object not in (HTTPSHandler, HTTPSConnection)
@@ -198,7 +194,7 @@ class VerifyingHTTPSConn(HTTPSConnection):
def opener_for(ca_bundle=None):
"""Get a urlopen() replacement that uses ca_bundle for verification"""
- return urllib2.build_opener(
+ return urllib.request.build_opener(
VerifyingHTTPSHandler(ca_bundle or find_ca_bundle())
).open
@@ -223,6 +219,12 @@ def get_win_certfile():
self.addcerts(certs)
atexit.register(self.close)
+ def close(self):
+ try:
+ super(MyCertFile, self).close()
+ except OSError:
+ pass
+
_wincerts = MyCertFile(stores=['CA', 'ROOT'])
return _wincerts.name
diff --git a/setuptools/tests/__init__.py b/setuptools/tests/__init__.py
index f985a6e4..32447356 100644
--- a/setuptools/tests/__init__.py
+++ b/setuptools/tests/__init__.py
@@ -7,8 +7,8 @@ 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.extern import six
import pytest
import setuptools.dist
@@ -52,7 +52,7 @@ class TestDepends:
x = "test"
y = z
- fc = func_code(f1)
+ fc = six.get_function_code(f1)
# unrecognized name
assert dep.extract_constant(fc,'q', -1) is None
diff --git a/setuptools/tests/contexts.py b/setuptools/tests/contexts.py
index 1d29284b..ae28c7c3 100644
--- a/setuptools/tests/contexts.py
+++ b/setuptools/tests/contexts.py
@@ -5,7 +5,8 @@ import sys
import contextlib
import site
-from ..compat import StringIO
+from setuptools.extern import six
+import pkg_resources
@contextlib.contextmanager
@@ -57,8 +58,8 @@ def quiet():
old_stdout = sys.stdout
old_stderr = sys.stderr
- new_stdout = sys.stdout = StringIO()
- new_stderr = sys.stderr = StringIO()
+ new_stdout = sys.stdout = six.StringIO()
+ new_stderr = sys.stderr = six.StringIO()
try:
yield new_stdout, new_stderr
finally:
@@ -78,6 +79,18 @@ def save_user_site_setting():
@contextlib.contextmanager
+def save_pkg_resources_state():
+ pr_state = pkg_resources.__getstate__()
+ # also save sys.path
+ sys_path = sys.path[:]
+ try:
+ yield pr_state, sys_path
+ finally:
+ sys.path[:] = sys_path
+ pkg_resources.__setstate__(pr_state)
+
+
+@contextlib.contextmanager
def suppress_exceptions(*excs):
try:
yield
diff --git a/setuptools/tests/files.py b/setuptools/tests/files.py
new file mode 100644
index 00000000..4364241b
--- /dev/null
+++ b/setuptools/tests/files.py
@@ -0,0 +1,32 @@
+import os
+
+
+def build_files(file_defs, prefix=""):
+ """
+ Build a set of files/directories, as described by the file_defs dictionary.
+
+ Each key/value pair in the dictionary is interpreted as a filename/contents
+ pair. If the contents value is a dictionary, a directory is created, and the
+ dictionary interpreted as the files within it, recursively.
+
+ For example:
+
+ {"README.txt": "A README file",
+ "foo": {
+ "__init__.py": "",
+ "bar": {
+ "__init__.py": "",
+ },
+ "baz.py": "# Some code",
+ }
+ }
+ """
+ for name, contents in file_defs.items():
+ full_name = os.path.join(prefix, name)
+ if isinstance(contents, dict):
+ if not os.path.exists(full_name):
+ os.makedirs(full_name)
+ build_files(contents, prefix=full_name)
+ else:
+ with open(full_name, 'w') as f:
+ f.write(contents)
diff --git a/setuptools/tests/server.py b/setuptools/tests/server.py
index 6b214279..6a687937 100644
--- a/setuptools/tests/server.py
+++ b/setuptools/tests/server.py
@@ -3,10 +3,11 @@
import time
import threading
-from setuptools.compat import BaseHTTPRequestHandler
-from setuptools.compat import HTTPServer, SimpleHTTPRequestHandler
-class IndexServer(HTTPServer):
+from setuptools.extern.six.moves import BaseHTTPServer, SimpleHTTPServer
+
+
+class IndexServer(BaseHTTPServer.HTTPServer):
"""Basic single-threaded http server simulating a package index
You can use this server in unittest like this::
@@ -18,8 +19,9 @@ class IndexServer(HTTPServer):
s.stop()
"""
def __init__(self, server_address=('', 0),
- RequestHandlerClass=SimpleHTTPRequestHandler):
- HTTPServer.__init__(self, server_address, RequestHandlerClass)
+ RequestHandlerClass=SimpleHTTPServer.SimpleHTTPRequestHandler):
+ BaseHTTPServer.HTTPServer.__init__(self, server_address,
+ RequestHandlerClass)
self._run = True
def start(self):
@@ -40,19 +42,20 @@ class IndexServer(HTTPServer):
port = self.server_port
return 'http://127.0.0.1:%s/setuptools/tests/indexes/' % port
-class RequestRecorder(BaseHTTPRequestHandler):
+class RequestRecorder(BaseHTTPServer.BaseHTTPRequestHandler):
def do_GET(self):
requests = vars(self.server).setdefault('requests', [])
requests.append(self)
self.send_response(200, 'OK')
-class MockServer(HTTPServer, threading.Thread):
+class MockServer(BaseHTTPServer.HTTPServer, threading.Thread):
"""
A simple HTTP Server that records the requests made to it.
"""
def __init__(self, server_address=('', 0),
RequestHandlerClass=RequestRecorder):
- HTTPServer.__init__(self, server_address, RequestHandlerClass)
+ BaseHTTPServer.HTTPServer.__init__(self, server_address,
+ RequestHandlerClass)
threading.Thread.__init__(self)
self.setDaemon(True)
self.requests = []
diff --git a/setuptools/tests/test_develop.py b/setuptools/tests/test_develop.py
index ab5da00e..1b844499 100644
--- a/setuptools/tests/test_develop.py
+++ b/setuptools/tests/test_develop.py
@@ -5,12 +5,13 @@ import site
import sys
import io
+from setuptools.extern import six
+
import pytest
from setuptools.command.develop import develop
from setuptools.dist import Distribution
from . import contexts
-from setuptools.compat import PY3
SETUP_PY = """\
@@ -85,7 +86,7 @@ class TestDevelop:
with io.open(fn) as init_file:
init = init_file.read().strip()
- expected = 'print("foo")' if PY3 else 'print "foo"'
+ expected = 'print("foo")' if six.PY3 else 'print "foo"'
assert init == expected
def test_console_scripts(self, tmpdir):
diff --git a/setuptools/tests/test_dist_info.py b/setuptools/tests/test_dist_info.py
index 6d0ab587..9f226a55 100644
--- a/setuptools/tests/test_dist_info.py
+++ b/setuptools/tests/test_dist_info.py
@@ -4,6 +4,8 @@ import os
import shutil
import tempfile
+from setuptools.extern.six.moves import map
+
import pytest
import pkg_resources
@@ -26,14 +28,15 @@ class TestDistInfo:
assert versioned.version == '2.718' # from filename
assert unversioned.version == '0.3' # from METADATA
- @pytest.mark.importorskip('ast')
def test_conditional_dependencies(self):
specs = 'splort==4', 'quux>=1.1'
requires = list(map(pkg_resources.Requirement.parse, specs))
for d in pkg_resources.find_distributions(self.tmpdir):
assert d.requires() == requires[:1]
- assert d.requires(extras=('baz',)) == requires
+ assert d.requires(extras=('baz',)) == [
+ requires[0],
+ pkg_resources.Requirement.parse('quux>=1.1;extra=="baz"')]
assert d.extras == ['baz']
metadata_template = DALS("""
diff --git a/setuptools/tests/test_easy_install.py b/setuptools/tests/test_easy_install.py
index 00e16b63..55b8b05a 100644
--- a/setuptools/tests/test_easy_install.py
+++ b/setuptools/tests/test_easy_install.py
@@ -14,6 +14,10 @@ import tarfile
import logging
import itertools
import distutils.errors
+import io
+
+from setuptools.extern.six.moves import urllib
+import time
import pytest
try:
@@ -22,8 +26,6 @@ except ImportError:
import mock
from setuptools import sandbox
-from setuptools import compat
-from setuptools.compat import StringIO, BytesIO, urlparse
from setuptools.sandbox import run_setup
import setuptools.command.easy_install as ei
from setuptools.command.easy_install import PthDistributions
@@ -35,7 +37,7 @@ import setuptools.tests.server
import pkg_resources
from .py26compat import tarfile_open
-from . import contexts, is_ascii
+from . import contexts
from .textwrap import DALS
@@ -56,17 +58,13 @@ SETUP_PY = DALS("""
class TestEasyInstallTest:
- def test_install_site_py(self):
+ def test_install_site_py(self, tmpdir):
dist = Distribution()
cmd = ei.easy_install(dist)
cmd.sitepy_installed = False
- cmd.install_dir = tempfile.mkdtemp()
- try:
- cmd.install_site_py()
- sitepy = os.path.join(cmd.install_dir, 'site.py')
- assert os.path.exists(sitepy)
- finally:
- shutil.rmtree(cmd.install_dir)
+ cmd.install_dir = str(tmpdir)
+ cmd.install_site_py()
+ assert (tmpdir / 'site.py').exists()
def test_get_script_args(self):
header = ei.CommandSpec.best().from_environment().as_header()
@@ -272,7 +270,7 @@ class TestSetupRequires:
p_index = setuptools.tests.server.MockServer()
p_index.start()
netloc = 1
- p_index_loc = urlparse(p_index.url)[netloc]
+ p_index_loc = urllib.parse.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.
@@ -308,32 +306,32 @@ class TestSetupRequires:
"""
with contexts.tempdir() as dir:
dist_path = os.path.join(dir, 'setuptools-test-fetcher-1.0.tar.gz')
- script = DALS("""
- import setuptools
- setuptools.setup(
- name="setuptools-test-fetcher",
- version="1.0",
- setup_requires = ['does-not-exist'],
- )
- """)
- make_trivial_sdist(dist_path, script)
+ make_sdist(dist_path, [
+ ('setup.py', DALS("""
+ import setuptools
+ setuptools.setup(
+ name="setuptools-test-fetcher",
+ version="1.0",
+ setup_requires = ['does-not-exist'],
+ )
+ """))])
yield dist_path
def test_setup_requires_overrides_version_conflict(self):
"""
- Regression test for issue #323.
+ Regression test for distribution issue 323:
+ https://bitbucket.org/tarek/distribute/issues/323
Ensures that a distribution's setup_requires requirements can still be
installed and used locally even if a conflicting version of that
requirement is already on the path.
"""
- pr_state = pkg_resources.__getstate__()
fake_dist = PRDistribution('does-not-matter', project_name='foobar',
version='0.0')
working_set.add(fake_dist)
- try:
+ with contexts.save_pkg_resources_state():
with contexts.tempdir() as temp_dir:
test_pkg = create_setup_requires_package(temp_dir)
test_setup_py = os.path.join(test_pkg, 'setup.py')
@@ -345,19 +343,144 @@ class TestSetupRequires:
lines = stdout.readlines()
assert len(lines) > 0
assert lines[-1].strip(), 'test_pkg'
- finally:
- pkg_resources.__setstate__(pr_state)
+ def test_setup_requires_override_nspkg(self):
+ """
+ Like ``test_setup_requires_overrides_version_conflict`` but where the
+ ``setup_requires`` package is part of a namespace package that has
+ *already* been imported.
+ """
+
+ with contexts.save_pkg_resources_state():
+ with contexts.tempdir() as temp_dir:
+ foobar_1_archive = os.path.join(temp_dir, 'foo.bar-0.1.tar.gz')
+ make_nspkg_sdist(foobar_1_archive, 'foo.bar', '0.1')
+ # Now actually go ahead an extract to the temp dir and add the
+ # extracted path to sys.path so foo.bar v0.1 is importable
+ foobar_1_dir = os.path.join(temp_dir, 'foo.bar-0.1')
+ os.mkdir(foobar_1_dir)
+ with tarfile_open(foobar_1_archive) as tf:
+ tf.extractall(foobar_1_dir)
+ sys.path.insert(1, foobar_1_dir)
+
+ dist = PRDistribution(foobar_1_dir, project_name='foo.bar',
+ version='0.1')
+ working_set.add(dist)
+
+ template = DALS("""\
+ import foo # Even with foo imported first the
+ # setup_requires package should override
+ import setuptools
+ setuptools.setup(**%r)
+
+ if not (hasattr(foo, '__path__') and
+ len(foo.__path__) == 2):
+ print('FAIL')
+
+ if 'foo.bar-0.2' not in foo.__path__[0]:
+ print('FAIL')
+ """)
+
+ test_pkg = create_setup_requires_package(
+ temp_dir, 'foo.bar', '0.2', make_nspkg_sdist, template)
+
+ test_setup_py = os.path.join(test_pkg, 'setup.py')
+
+ with contexts.quiet() as (stdout, stderr):
+ try:
+ # Don't even need to install the package, just
+ # running the setup.py at all is sufficient
+ run_setup(test_setup_py, ['--name'])
+ except pkg_resources.VersionConflict:
+ self.fail('Installing setup.py requirements '
+ 'caused a VersionConflict')
+
+ assert 'FAIL' not in stdout.getvalue()
+ lines = stdout.readlines()
+ assert len(lines) > 0
+ assert lines[-1].strip() == 'test_pkg'
+
+
+def make_trivial_sdist(dist_path, distname, version):
+ """
+ Create a simple sdist tarball at dist_path, containing just a simple
+ setup.py.
+ """
+
+ make_sdist(dist_path, [
+ ('setup.py',
+ DALS("""\
+ import setuptools
+ setuptools.setup(
+ name=%r,
+ version=%r
+ )
+ """ % (distname, version)))])
+
+
+def make_nspkg_sdist(dist_path, distname, version):
+ """
+ Make an sdist tarball with distname and version which also contains one
+ package with the same name as distname. The top-level package is
+ designated a namespace package).
+ """
+
+ parts = distname.split('.')
+ nspackage = parts[0]
+
+ packages = ['.'.join(parts[:idx]) for idx in range(1, len(parts) + 1)]
+
+ setup_py = DALS("""\
+ import setuptools
+ setuptools.setup(
+ name=%r,
+ version=%r,
+ packages=%r,
+ namespace_packages=[%r]
+ )
+ """ % (distname, version, packages, nspackage))
+
+ init = "__import__('pkg_resources').declare_namespace(__name__)"
+
+ files = [('setup.py', setup_py),
+ (os.path.join(nspackage, '__init__.py'), init)]
+ for package in packages[1:]:
+ filename = os.path.join(*(package.split('.') + ['__init__.py']))
+ files.append((filename, ''))
+
+ make_sdist(dist_path, files)
+
+
+def make_sdist(dist_path, files):
+ """
+ Create a simple sdist tarball at dist_path, containing the files
+ listed in ``files`` as ``(filename, content)`` tuples.
+ """
-def create_setup_requires_package(path):
+ with tarfile_open(dist_path, 'w:gz') as dist:
+ for filename, content in files:
+ file_bytes = io.BytesIO(content.encode('utf-8'))
+ file_info = tarfile.TarInfo(name=filename)
+ file_info.size = len(file_bytes.getvalue())
+ file_info.mtime = int(time.time())
+ dist.addfile(file_info, fileobj=file_bytes)
+
+
+def create_setup_requires_package(path, distname='foobar', version='0.1',
+ make_package=make_trivial_sdist,
+ setup_py_template=None):
"""Creates a source tree under path for a trivial test package that has a
single requirement in setup_requires--a tarball for that requirement is
also created and added to the dependency_links argument.
+
+ ``distname`` and ``version`` refer to the name/version of the package that
+ the test package requires via ``setup_requires``. The name of the test
+ package itself is just 'test_pkg'.
"""
test_setup_attrs = {
'name': 'test_pkg', 'version': '0.0',
- 'setup_requires': ['foobar==0.1'],
+ 'setup_requires': ['%s==%s' % (distname, version)],
'dependency_links': [os.path.abspath(path)]
}
@@ -365,22 +488,17 @@ def create_setup_requires_package(path):
test_setup_py = os.path.join(test_pkg, 'setup.py')
os.mkdir(test_pkg)
- with open(test_setup_py, 'w') as f:
- f.write(DALS("""
+ if setup_py_template is None:
+ setup_py_template = DALS("""\
import setuptools
setuptools.setup(**%r)
- """ % test_setup_attrs))
+ """)
- foobar_path = os.path.join(path, 'foobar-0.1.tar.gz')
- make_trivial_sdist(
- foobar_path,
- DALS("""
- import setuptools
- setuptools.setup(
- name='foobar',
- version='0.1'
- )
- """))
+ with open(test_setup_py, 'w') as f:
+ f.write(setup_py_template % test_setup_attrs)
+
+ foobar_path = os.path.join(path, '%s-%s.tar.gz' % (distname, version))
+ make_package(foobar_path, distname, version)
return test_pkg
@@ -391,12 +509,7 @@ 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 = BytesIO
- except AttributeError:
- MemFile = StringIO
- setup_py_bytes = MemFile(setup_py.encode('utf-8'))
+ setup_py_bytes = io.BytesIO(setup_py.encode('utf-8'))
setup_py_file.size = len(setup_py_bytes.getvalue())
with tarfile_open(dist_path, 'w:gz') as dist:
dist.addfile(setup_py_file, fileobj=setup_py_bytes)
@@ -430,46 +543,6 @@ class TestScriptHeader:
expected = '#!"%s"\n' % self.exe_with_spaces
assert actual == expected
- @pytest.mark.xfail(
- compat.PY3 and is_ascii,
- reason="Test fails in this locale on Python 3"
- )
- @mock.patch.dict(sys.modules, java=mock.Mock(lang=mock.Mock(System=
- mock.Mock(getProperty=mock.Mock(return_value="")))))
- @mock.patch('sys.platform', 'java1.5.0_13')
- def test_get_script_header_jython_workaround(self, tmpdir):
- # Create a mock sys.executable that uses a shebang line
- header = DALS("""
- #!/usr/bin/python
- # -*- coding: utf-8 -*-
- """)
- exe = tmpdir / 'exe.py'
- with exe.open('w') as f:
- f.write(header)
- exe = str(exe)
-
- header = ei.ScriptWriter.get_script_header('#!/usr/local/bin/python',
- executable=exe)
- assert header == '#!/usr/bin/env %s\n' % exe
-
- expect_out = 'stdout' if sys.version_info < (2,7) else 'stderr'
-
- with contexts.quiet() as (stdout, stderr):
- # When options are included, generate a broken shebang line
- # with a warning emitted
- candidate = ei.ScriptWriter.get_script_header('#!/usr/bin/python -x',
- executable=exe)
- assert candidate == '#!%s -x\n' % exe
- output = locals()[expect_out]
- assert 'Unable to adapt shebang line' in output.getvalue()
-
- with contexts.quiet() as (stdout, stderr):
- candidate = ei.ScriptWriter.get_script_header('#!/usr/bin/python',
- executable=self.non_ascii_exe)
- assert candidate == '#!%s -x\n' % self.non_ascii_exe
- output = locals()[expect_out]
- assert 'Unable to adapt shebang line' in output.getvalue()
-
class TestCommandSpec:
def test_custom_launch_command(self):
diff --git a/setuptools/tests/test_egg_info.py b/setuptools/tests/test_egg_info.py
index 645c379c..d37567b4 100644
--- a/setuptools/tests/test_egg_info.py
+++ b/setuptools/tests/test_egg_info.py
@@ -1,9 +1,13 @@
import os
+import glob
import stat
+from setuptools.extern.six.moves import map
+
import pytest
from . import environment
+from .files import build_files
from .textwrap import DALS
from . import contexts
@@ -26,14 +30,13 @@ class TestEggInfo(object):
""")
def _create_project(self):
- with open('setup.py', 'w') as f:
- f.write(self.setup_script)
-
- with open('hello.py', 'w') as f:
- f.write(DALS("""
+ build_files({
+ 'setup.py': self.setup_script,
+ 'hello.py': DALS("""
def run():
print('hello')
- """))
+ """)
+ })
@pytest.yield_fixture
def env(self):
@@ -46,36 +49,20 @@ class TestEggInfo(object):
for dirname in subs
)
list(map(os.mkdir, env.paths.values()))
- config = os.path.join(env.paths['home'], '.pydistutils.cfg')
- with open(config, 'w') as f:
- f.write(DALS("""
+ build_files({
+ env.paths['home']: {
+ '.pydistutils.cfg': DALS("""
[egg_info]
egg-base = %(egg-base)s
- """ % env.paths))
+ """ % env.paths)
+ }
+ })
yield env
def test_egg_base_installed_egg_info(self, tmpdir_cwd, env):
self._create_project()
- environ = os.environ.copy().update(
- HOME=env.paths['home'],
- )
- cmd = [
- 'install',
- '--home', env.paths['home'],
- '--install-lib', env.paths['lib'],
- '--install-scripts', env.paths['scripts'],
- '--install-data', env.paths['data'],
- ]
- code, data = environment.run_setup_py(
- cmd=cmd,
- pypath=os.pathsep.join([env.paths['lib'], str(tmpdir_cwd)]),
- data_stream=1,
- env=environ,
- )
- if code:
- raise AssertionError(data)
-
+ self._run_install_command(tmpdir_cwd, env)
actual = self._find_egg_info_files(env.paths['lib'])
expected = [
@@ -88,9 +75,98 @@ class TestEggInfo(object):
]
assert sorted(actual) == expected
+ def test_manifest_template_is_read(self, tmpdir_cwd, env):
+ self._create_project()
+ build_files({
+ 'MANIFEST.in': DALS("""
+ recursive-include docs *.rst
+ """),
+ 'docs': {
+ 'usage.rst': "Run 'hi'",
+ }
+ })
+ self._run_install_command(tmpdir_cwd, env)
+ egg_info_dir = self._find_egg_info_files(env.paths['lib']).base
+ sources_txt = os.path.join(egg_info_dir, 'SOURCES.txt')
+ assert 'docs/usage.rst' in open(sources_txt).read().split('\n')
+
+ def _setup_script_with_requires(self, requires_line):
+ setup_script = DALS("""
+ from setuptools import setup
+
+ setup(
+ name='foo',
+ %s
+ zip_safe=False,
+ )
+ """ % requires_line)
+ build_files({
+ 'setup.py': setup_script,
+ })
+
+ @pytest.mark.xfail(reason="Functionality disabled; see #523")
+ def test_install_requires_with_markers(self, tmpdir_cwd, env):
+ self._setup_script_with_requires(
+ """install_requires=["barbazquux;python_version<'2'"],""")
+ self._run_install_command(tmpdir_cwd, env)
+ egg_info_dir = self._find_egg_info_files(env.paths['lib']).base
+ requires_txt = os.path.join(egg_info_dir, 'requires.txt')
+ assert "barbazquux;python_version<'2'" in open(
+ requires_txt).read().split('\n')
+ assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == []
+
+ @pytest.mark.xfail(reason="Functionality disabled; see #523")
+ def test_setup_requires_with_markers(self, tmpdir_cwd, env):
+ self._setup_script_with_requires(
+ """setup_requires=["barbazquux;python_version<'2'"],""")
+ self._run_install_command(tmpdir_cwd, env)
+ assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == []
+
+ @pytest.mark.xfail(reason="Functionality disabled; see #523")
+ def test_tests_require_with_markers(self, tmpdir_cwd, env):
+ self._setup_script_with_requires(
+ """tests_require=["barbazquux;python_version<'2'"],""")
+ self._run_install_command(
+ tmpdir_cwd, env, cmd=['test'], output="Ran 0 tests in")
+ assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == []
+
+ def test_extra_requires_with_markers(self, tmpdir_cwd, env):
+ self._setup_script_with_requires(
+ """extra_requires={":python_version<'2'": ["barbazquux"]},""")
+ self._run_install_command(tmpdir_cwd, env)
+ assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == []
+
+ def _run_install_command(self, tmpdir_cwd, env, cmd=None, output=None):
+ environ = os.environ.copy().update(
+ HOME=env.paths['home'],
+ )
+ if cmd is None:
+ cmd = [
+ 'install',
+ '--home', env.paths['home'],
+ '--install-lib', env.paths['lib'],
+ '--install-scripts', env.paths['scripts'],
+ '--install-data', env.paths['data'],
+ ]
+ code, data = environment.run_setup_py(
+ cmd=cmd,
+ pypath=os.pathsep.join([env.paths['lib'], str(tmpdir_cwd)]),
+ data_stream=1,
+ env=environ,
+ )
+ if code:
+ raise AssertionError(data)
+ if output:
+ assert output in data
+
def _find_egg_info_files(self, root):
+ class DirList(list):
+ def __init__(self, files, base):
+ super(DirList, self).__init__(files)
+ self.base = base
+
results = (
- filenames
+ DirList(filenames, dirpath)
for dirpath, dirnames, filenames in os.walk(root)
if os.path.basename(dirpath) == 'EGG-INFO'
)
diff --git a/setuptools/tests/test_integration.py b/setuptools/tests/test_integration.py
index 07f06db2..04772ba5 100644
--- a/setuptools/tests/test_integration.py
+++ b/setuptools/tests/test_integration.py
@@ -7,12 +7,12 @@ import glob
import os
import sys
+from setuptools.extern.six.moves import urllib
import pytest
from setuptools.command.easy_install import easy_install
from setuptools.command import easy_install as easy_install_pkg
from setuptools.dist import Distribution
-from setuptools.compat import urlopen
def setup_module(module):
@@ -26,7 +26,7 @@ def setup_module(module):
pass
try:
- urlopen('https://pypi.python.org/pypi')
+ urllib.request.urlopen('https://pypi.python.org/pypi')
except Exception as exc:
pytest.skip(str(exc))
diff --git a/setuptools/tests/test_markerlib.py b/setuptools/tests/test_markerlib.py
deleted file mode 100644
index 8197b49d..00000000
--- a/setuptools/tests/test_markerlib.py
+++ /dev/null
@@ -1,63 +0,0 @@
-import os
-
-import pytest
-
-
-class TestMarkerlib:
-
- @pytest.mark.importorskip('ast')
- def test_markers(self):
- from _markerlib import interpret, default_environment, compile
-
- os_name = os.name
-
- assert interpret("")
-
- assert interpret("os.name != 'buuuu'")
- assert interpret("os_name != 'buuuu'")
- assert interpret("python_version > '1.0'")
- assert interpret("python_version < '5.0'")
- assert interpret("python_version <= '5.0'")
- assert interpret("python_version >= '1.0'")
- assert interpret("'%s' in os.name" % os_name)
- assert interpret("'%s' in os_name" % os_name)
- assert interpret("'buuuu' not in os.name")
-
- assert not interpret("os.name == 'buuuu'")
- assert not interpret("os_name == 'buuuu'")
- assert not interpret("python_version < '1.0'")
- assert not interpret("python_version > '5.0'")
- assert not interpret("python_version >= '5.0'")
- assert not interpret("python_version <= '1.0'")
- assert not interpret("'%s' not in os.name" % os_name)
- assert not interpret("'buuuu' in os.name and python_version >= '5.0'")
- assert not interpret("'buuuu' in os_name and python_version >= '5.0'")
-
- environment = default_environment()
- environment['extra'] = 'test'
- assert interpret("extra == 'test'", environment)
- assert not interpret("extra == 'doc'", environment)
-
- def raises_nameError():
- try:
- interpret("python.version == '42'")
- except NameError:
- pass
- else:
- raise Exception("Expected NameError")
-
- raises_nameError()
-
- def raises_syntaxError():
- try:
- interpret("(x for x in (4,))")
- except SyntaxError:
- pass
- else:
- raise Exception("Expected SyntaxError")
-
- raises_syntaxError()
-
- statement = "python_version == '5'"
- assert compile(statement).__doc__ == statement
-
diff --git a/setuptools/tests/test_packageindex.py b/setuptools/tests/test_packageindex.py
index 746860d5..6a76b5fc 100644
--- a/setuptools/tests/test_packageindex.py
+++ b/setuptools/tests/test_packageindex.py
@@ -4,9 +4,10 @@ import sys
import os
import distutils.errors
-from setuptools.compat import httplib, HTTPError, unicode, pathname2url
-from .textwrap import DALS
+from setuptools.extern import six
+from setuptools.extern.six.moves import urllib, http_client
+from .textwrap import DALS
import pkg_resources
import setuptools.package_index
from setuptools.tests.server import IndexServer
@@ -22,7 +23,7 @@ class TestPackageIndex:
except Exception as v:
assert url in str(v)
else:
- assert isinstance(v, HTTPError)
+ assert isinstance(v, urllib.error.HTTPError)
def test_bad_url_typo(self):
# issue 16
@@ -38,7 +39,7 @@ class TestPackageIndex:
except Exception as v:
assert url in str(v)
else:
- assert isinstance(v, HTTPError)
+ assert isinstance(v, urllib.error.HTTPError)
def test_bad_url_bad_status_line(self):
index = setuptools.package_index.PackageIndex(
@@ -46,7 +47,7 @@ class TestPackageIndex:
)
def _urlopen(*args):
- raise httplib.BadStatusLine('line')
+ raise http_client.BadStatusLine('line')
index.opener = _urlopen
url = 'http://example.com'
@@ -70,7 +71,7 @@ class TestPackageIndex:
try:
index.open_url(url)
except distutils.errors.DistutilsError as error:
- msg = unicode(error)
+ msg = six.text_type(error)
assert 'nonnumeric port' in msg or 'getaddrinfo failed' in msg or 'Name or service not known' in msg
return
raise RuntimeError("Did not raise")
@@ -167,7 +168,7 @@ class TestPackageIndex:
index_file = tmpdir / 'index.html'
with index_file.open('w') as f:
f.write('<div>content</div>')
- url = 'file:' + pathname2url(str(tmpdir)) + '/'
+ url = 'file:' + urllib.request.pathname2url(str(tmpdir)) + '/'
res = setuptools.package_index.local_open(url)
assert 'content' in res.read()
diff --git a/setuptools/tests/test_sdist.py b/setuptools/tests/test_sdist.py
index 8ec9a4cb..d2a1f1bb 100644
--- a/setuptools/tests/test_sdist.py
+++ b/setuptools/tests/test_sdist.py
@@ -9,17 +9,19 @@ import unicodedata
import contextlib
import io
+from setuptools.extern import six
+from setuptools.extern.six.moves import map
+
import pytest
import pkg_resources
-from setuptools.compat import StringIO, unicode, PY3, PY2
from setuptools.command.sdist import sdist
from setuptools.command.egg_info import manifest_maker
from setuptools.dist import Distribution
from setuptools.tests import fail_on_ascii
-py3_only = pytest.mark.xfail(PY2, reason="Test runs on Python 3 only")
+py3_only = pytest.mark.xfail(six.PY2, reason="Test runs on Python 3 only")
SETUP_ATTRS = {
@@ -37,7 +39,7 @@ setup(**%r)
""" % SETUP_ATTRS
-if PY3:
+if six.PY3:
LATIN1_FILENAME = 'smörbröd.py'.encode('latin-1')
else:
LATIN1_FILENAME = 'sm\xf6rbr\xf6d.py'
@@ -47,7 +49,7 @@ else:
@contextlib.contextmanager
def quiet():
old_stdout, old_stderr = sys.stdout, sys.stderr
- sys.stdout, sys.stderr = StringIO(), StringIO()
+ sys.stdout, sys.stderr = six.StringIO(), six.StringIO()
try:
yield
finally:
@@ -56,14 +58,14 @@ def quiet():
# Fake byte literals for Python <= 2.5
def b(s, encoding='utf-8'):
- if PY3:
+ if six.PY3:
return s.encode(encoding)
return s
# Convert to POSIX path
def posix(path):
- if PY3 and not isinstance(path, str):
+ if six.PY3 and not isinstance(path, str):
return path.replace(os.sep.encode('ascii'), b('/'))
else:
return path.replace(os.sep, '/')
@@ -71,7 +73,7 @@ def posix(path):
# HFS Plus uses decomposed UTF-8
def decompose(path):
- if isinstance(path, unicode):
+ if isinstance(path, six.text_type):
return unicodedata.normalize('NFD', path)
try:
path = path.decode('utf-8')
@@ -184,7 +186,7 @@ class TestSdistTest:
u_contents = contents.decode('UTF-8')
# The manifest should contain the UTF-8 filename
- if PY2:
+ if six.PY2:
fs_enc = sys.getfilesystemencoding()
filename = filename.decode(fs_enc)
@@ -289,7 +291,7 @@ class TestSdistTest:
cmd.read_manifest()
# The filelist should contain the UTF-8 filename
- if PY3:
+ if six.PY3:
filename = filename.decode('utf-8')
assert filename in cmd.filelist.files
@@ -342,7 +344,7 @@ class TestSdistTest:
if sys.platform == 'darwin':
filename = decompose(filename)
- if PY3:
+ if six.PY3:
fs_enc = sys.getfilesystemencoding()
if sys.platform == 'win32':
@@ -374,7 +376,7 @@ class TestSdistTest:
with quiet():
cmd.run()
- if PY3:
+ if six.PY3:
# not all windows systems have a default FS encoding of cp1252
if sys.platform == 'win32':
# Latin-1 is similar to Windows-1252 however
diff --git a/setuptools/tests/test_setuptools.py b/setuptools/tests/test_setuptools.py
index 8aca593a..e59800d2 100644
--- a/setuptools/tests/test_setuptools.py
+++ b/setuptools/tests/test_setuptools.py
@@ -23,7 +23,7 @@ def test_findall(example_source):
def test_findall_curdir(example_source):
with example_source.as_cwd():
found = list(setuptools.findall())
- expected = ['readme.txt', 'foo/bar.py']
+ expected = ['readme.txt', os.path.join('foo', 'bar.py')]
assert found == expected
diff --git a/setuptools/tests/test_test.py b/setuptools/tests/test_test.py
index a66294c9..4155a5b1 100644
--- a/setuptools/tests/test_test.py
+++ b/setuptools/tests/test_test.py
@@ -4,6 +4,7 @@ from __future__ import unicode_literals
import os
import site
+from distutils.errors import DistutilsError
import pytest
diff --git a/setuptools/tests/test_unicode_utils.py b/setuptools/tests/test_unicode_utils.py
new file mode 100644
index 00000000..a24a9bd5
--- /dev/null
+++ b/setuptools/tests/test_unicode_utils.py
@@ -0,0 +1,10 @@
+from setuptools import unicode_utils
+
+
+def test_filesys_decode_fs_encoding_is_None(monkeypatch):
+ """
+ Test filesys_decode does not raise TypeError when
+ getfilesystemencoding returns None.
+ """
+ monkeypatch.setattr('sys.getfilesystemencoding', lambda: None)
+ unicode_utils.filesys_decode(b'test')
diff --git a/setuptools/unicode_utils.py b/setuptools/unicode_utils.py
index d2de941a..ffab3e24 100644
--- a/setuptools/unicode_utils.py
+++ b/setuptools/unicode_utils.py
@@ -1,11 +1,11 @@
import unicodedata
import sys
-from setuptools.compat import unicode as decoded_string
+from setuptools.extern import six
# HFS Plus uses decomposed UTF-8
def decompose(path):
- if isinstance(path, decoded_string):
+ if isinstance(path, six.text_type):
return unicodedata.normalize('NFD', path)
try:
path = path.decode('utf-8')
@@ -22,11 +22,13 @@ def filesys_decode(path):
NONE when no expected encoding works
"""
- fs_enc = sys.getfilesystemencoding()
- if isinstance(path, decoded_string):
+ if isinstance(path, six.text_type):
return path
- for enc in (fs_enc, "utf-8"):
+ fs_enc = sys.getfilesystemencoding() or 'utf-8'
+ candidates = fs_enc, 'utf-8'
+
+ for enc in candidates:
try:
return path.decode(enc)
except UnicodeDecodeError: