From 14827711f669a830190313951ab5aef7b71ab2c6 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 6 Nov 2016 15:54:07 -0500 Subject: Install -nspkg.pth under develop command. Fixes namespace package support as long as __init__.py is omitted. --- setuptools/command/develop.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'setuptools') diff --git a/setuptools/command/develop.py b/setuptools/command/develop.py index 3eb86120..8de24fd7 100755 --- a/setuptools/command/develop.py +++ b/setuptools/command/develop.py @@ -9,10 +9,11 @@ from setuptools.extern import six from pkg_resources import Distribution, PathMetadata, normalize_path from setuptools.command.easy_install import easy_install +from setuptools import namespaces import setuptools -class develop(easy_install): +class develop(namespaces.DevelopInstaller, easy_install): """Set up package for development""" description = "install package in 'development mode'" @@ -123,6 +124,8 @@ class develop(easy_install): self.easy_install(setuptools.bootstrap_install_from) setuptools.bootstrap_install_from = None + self.install_namespaces() + # create an .egg-link in the installation dir, pointing to our egg log.info("Creating %s (link to %s)", self.egg_link, self.egg_base) if not self.dry_run: -- cgit v1.2.1 From 355603259aa37e424cf7466c3de6518375a935e3 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 6 Nov 2016 16:41:23 -0500 Subject: Add uninstall support for namespace packages --- setuptools/command/develop.py | 1 + setuptools/namespaces.py | 8 ++++++++ 2 files changed, 9 insertions(+) (limited to 'setuptools') diff --git a/setuptools/command/develop.py b/setuptools/command/develop.py index 8de24fd7..aa82f959 100755 --- a/setuptools/command/develop.py +++ b/setuptools/command/develop.py @@ -31,6 +31,7 @@ class develop(namespaces.DevelopInstaller, easy_install): if self.uninstall: self.multi_version = True self.uninstall_link() + self.uninstall_namespaces() else: self.install_for_development() self.warn_deprecated_options() diff --git a/setuptools/namespaces.py b/setuptools/namespaces.py index cc934b7e..a6371c99 100755 --- a/setuptools/namespaces.py +++ b/setuptools/namespaces.py @@ -30,6 +30,14 @@ class Installer: with open(filename, 'wt') as f: f.writelines(lines) + def uninstall_namespaces(self): + filename, ext = os.path.splitext(self._get_target()) + filename += self.nspkg_ext + if not os.path.exists(filename): + return + log.info("Removing %s", filename) + os.remove(filename) + def _get_target(self): return self.target -- cgit v1.2.1 From bcddc3dd9aa2b436a4260d4345d4ab1c6d4363ca Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 7 Nov 2016 03:53:32 -0500 Subject: Add test capturing expectation for #250. --- setuptools/tests/test_develop.py | 73 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) (limited to 'setuptools') diff --git a/setuptools/tests/test_develop.py b/setuptools/tests/test_develop.py index 4cf483f2..bf162d8d 100644 --- a/setuptools/tests/test_develop.py +++ b/setuptools/tests/test_develop.py @@ -114,3 +114,76 @@ class TestDevelop: cmd.install_dir = tmpdir cmd.run() # assert '0.0' not in foocmd_text + + +import textwrap +import subprocess + + +class TestNamespaces: + @staticmethod + def build_namespace_package(tmpdir, name): + src_dir = tmpdir / name + src_dir.mkdir() + setup_py = src_dir / 'setup.py' + namespace, sep, rest = name.partition('.') + script = textwrap.dedent(""" + import setuptools + setuptools.setup( + name={name!r}, + version="1.0", + namespace_packages=[{namespace!r}], + packages=[{namespace!r}], + ) + """).format(**locals()) + setup_py.write_text(script, encoding='utf-8') + ns_pkg_dir = src_dir / namespace + ns_pkg_dir.mkdir() + pkg_init = ns_pkg_dir / '__init__.py' + tmpl = '__import__("pkg_resources").declare_namespace({namespace!r})' + decl = tmpl.format(**locals()) + pkg_init.write_text(decl, encoding='utf-8') + pkg_mod = ns_pkg_dir / (rest + '.py') + some_functionality = 'name = {rest!r}'.format(**locals()) + pkg_mod.write_text(some_functionality, encoding='utf-8') + return src_dir + + @staticmethod + def make_site_dir(target): + """ + Add a sitecustomize.py module in target to cause + target to be added to site dirs such that .pth files + are processed there. + """ + sc = target / 'sitecustomize.py' + target_str = str(target) + tmpl = '__import__("site").addsitedir({target_str!r})' + sc.write_text(tmpl.format(**locals()), encoding='utf-8') + + def test_namespace_package_importable(self, tmpdir): + """ + Installing two packages sharing the same namespace, one installed + naturally using pip or `--single-version-externally-managed` + and the other installed using `develop` should leave the namespace + in tact and both packages reachable by import. + """ + pkg_A = self.build_namespace_package(tmpdir, 'myns.pkgA') + pkg_B = self.build_namespace_package(tmpdir, 'myns.pkgB') + target = tmpdir / 'packages' + # use pip to install to the target directory + install_cmd = [ + 'pip', + 'install', + '-e', str(pkg_B), + str(pkg_A), + '-t', str(target), + ] + subprocess.check_call(install_cmd) + self.make_site_dir(target) + try_import = [ + sys.executable, + '-c', 'import myns.pkgA; import myns.pkgB', + ] + env = dict(PYTHONPATH=str(target)) + subprocess.check_call(try_import, env=env) + -- cgit v1.2.1 From fd623755cad7ee9fc1492029f4ebdde8ae6b1c1a Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 7 Nov 2016 04:03:42 -0500 Subject: pip can't accept -e and -t --- setuptools/tests/test_develop.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'setuptools') diff --git a/setuptools/tests/test_develop.py b/setuptools/tests/test_develop.py index bf162d8d..8e905357 100644 --- a/setuptools/tests/test_develop.py +++ b/setuptools/tests/test_develop.py @@ -160,6 +160,19 @@ class TestNamespaces: tmpl = '__import__("site").addsitedir({target_str!r})' sc.write_text(tmpl.format(**locals()), encoding='utf-8') + @staticmethod + def install_develop(src_dir, target): + + develop_cmd = [ + sys.executable, + 'setup.py', + 'develop', + '--install-dir', str(target), + ] + env = dict(PYTHONPATH=str(target)) + with src_dir.as_cwd(): + subprocess.check_call(develop_cmd, env=env) + def test_namespace_package_importable(self, tmpdir): """ Installing two packages sharing the same namespace, one installed @@ -174,11 +187,11 @@ class TestNamespaces: install_cmd = [ 'pip', 'install', - '-e', str(pkg_B), str(pkg_A), '-t', str(target), ] subprocess.check_call(install_cmd) + self.install_develop(pkg_B, target) self.make_site_dir(target) try_import = [ sys.executable, -- cgit v1.2.1 From 3caf0231808268654570492a0623e72b22988243 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 7 Nov 2016 04:08:04 -0500 Subject: Move imports to top and use absolute_import for Python 2.7 compatibility --- setuptools/tests/test_develop.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'setuptools') diff --git a/setuptools/tests/test_develop.py b/setuptools/tests/test_develop.py index 8e905357..d6c24219 100644 --- a/setuptools/tests/test_develop.py +++ b/setuptools/tests/test_develop.py @@ -1,9 +1,14 @@ """develop tests """ + +from __future__ import absolute_import + import os import site import sys import io +import textwrap +import subprocess from setuptools.extern import six @@ -116,10 +121,6 @@ class TestDevelop: # assert '0.0' not in foocmd_text -import textwrap -import subprocess - - class TestNamespaces: @staticmethod def build_namespace_package(tmpdir, name): -- cgit v1.2.1 From d580f089b95b58a9a414faa8ed35419e71c995e1 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 7 Nov 2016 04:08:49 -0500 Subject: Use unicode literals for Python 2.7 compatibility --- setuptools/tests/test_develop.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'setuptools') diff --git a/setuptools/tests/test_develop.py b/setuptools/tests/test_develop.py index d6c24219..fb8e0e3f 100644 --- a/setuptools/tests/test_develop.py +++ b/setuptools/tests/test_develop.py @@ -1,7 +1,7 @@ """develop tests """ -from __future__ import absolute_import +from __future__ import absolute_import, unicode_literals import os import site -- cgit v1.2.1 From 712a6d85e47979ac50a1fb32d2cc76dc61acade8 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 7 Nov 2016 04:18:56 -0500 Subject: In -nspkg.pth, always add the path to the namespace package, even if a __init__ exists, allowing for better cooperation between PEP 420 packages and older, __init__ namespace packages. --- setuptools/namespaces.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'setuptools') diff --git a/setuptools/namespaces.py b/setuptools/namespaces.py index a6371c99..93d358a2 100755 --- a/setuptools/namespaces.py +++ b/setuptools/namespaces.py @@ -45,8 +45,7 @@ class Installer: "import sys, types, os", "pep420 = sys.version_info > (3, 3)", "p = os.path.join(%(root)s, *%(pth)r)", - "ie = os.path.exists(os.path.join(p,'__init__.py'))", - "m = not ie and not pep420 and " + "m = not pep420 and " "sys.modules.setdefault(%(pkg)r, types.ModuleType(%(pkg)r))", "mp = (m or []) and m.__dict__.setdefault('__path__',[])", "(p not in mp) and mp.append(p)", -- cgit v1.2.1 From 2268fb9887cfea2e28e156bd2c8314132261402f Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 13 Nov 2016 09:21:40 -0500 Subject: Provisionally revert the -nspkg.pth suppression on PEP 420 Pythons. Ref #250. --- setuptools/namespaces.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'setuptools') diff --git a/setuptools/namespaces.py b/setuptools/namespaces.py index 93d358a2..9c1f0243 100755 --- a/setuptools/namespaces.py +++ b/setuptools/namespaces.py @@ -45,7 +45,7 @@ class Installer: "import sys, types, os", "pep420 = sys.version_info > (3, 3)", "p = os.path.join(%(root)s, *%(pth)r)", - "m = not pep420 and " + "m = " "sys.modules.setdefault(%(pkg)r, types.ModuleType(%(pkg)r))", "mp = (m or []) and m.__dict__.setdefault('__path__',[])", "(p not in mp) and mp.append(p)", -- cgit v1.2.1 From 7e25fd910d1ff5259c0768d3b54a9bf03bce4279 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 7 Dec 2016 20:51:36 -0500 Subject: Consider using module_from_spec to create the module. --- setuptools/namespaces.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'setuptools') diff --git a/setuptools/namespaces.py b/setuptools/namespaces.py index cc934b7e..b6720609 100755 --- a/setuptools/namespaces.py +++ b/setuptools/namespaces.py @@ -34,12 +34,15 @@ class Installer: return self.target _nspkg_tmpl = ( - "import sys, types, os", + "import sys, types, os, importlib.util, importlib.machinery", "pep420 = sys.version_info > (3, 3)", "p = os.path.join(%(root)s, *%(pth)r)", "ie = os.path.exists(os.path.join(p,'__init__.py'))", "m = not ie and not pep420 and " - "sys.modules.setdefault(%(pkg)r, types.ModuleType(%(pkg)r))", + "sys.modules.setdefault(%(pkg)r, " + "importlib.util.module_from_spec(" + "importlib.machinery.PathFinder.find_spec(%(pkg)r, " + "[os.path.dirname(p)])))", "mp = (m or []) and m.__dict__.setdefault('__path__',[])", "(p not in mp) and mp.append(p)", ) -- cgit v1.2.1 From 41a9da1833b1f55b4a3da0d427e9b569075f0cc7 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 7 Dec 2016 20:57:11 -0500 Subject: module_from_spec is only available on Python 3.5 --- setuptools/namespaces.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'setuptools') diff --git a/setuptools/namespaces.py b/setuptools/namespaces.py index b6720609..9266860f 100755 --- a/setuptools/namespaces.py +++ b/setuptools/namespaces.py @@ -1,4 +1,5 @@ import os +import sys from distutils import log import itertools @@ -42,7 +43,10 @@ class Installer: "sys.modules.setdefault(%(pkg)r, " "importlib.util.module_from_spec(" "importlib.machinery.PathFinder.find_spec(%(pkg)r, " - "[os.path.dirname(p)])))", + "[os.path.dirname(p)])))" + if sys.version_info >= (3, 5) else + "m = not ie and not pep420 and " + "sys.modules.setdefault(%(pkg)r, types.ModuleType(%(pkg)r))", "mp = (m or []) and m.__dict__.setdefault('__path__',[])", "(p not in mp) and mp.append(p)", ) -- cgit v1.2.1 From 17f72f5bc7031accc037b377ced5a0b411520bfb Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 7 Dec 2016 21:24:21 -0500 Subject: Always check at run time as some builds share files across Python versions. --- setuptools/namespaces.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'setuptools') diff --git a/setuptools/namespaces.py b/setuptools/namespaces.py index 9266860f..2399dae5 100755 --- a/setuptools/namespaces.py +++ b/setuptools/namespaces.py @@ -37,15 +37,15 @@ class Installer: _nspkg_tmpl = ( "import sys, types, os, importlib.util, importlib.machinery", "pep420 = sys.version_info > (3, 3)", + "has_mfs = sys.version_info > (3, 5)", "p = os.path.join(%(root)s, *%(pth)r)", "ie = os.path.exists(os.path.join(p,'__init__.py'))", - "m = not ie and not pep420 and " + "m = not ie and not pep420 and has_mfs and " "sys.modules.setdefault(%(pkg)r, " "importlib.util.module_from_spec(" "importlib.machinery.PathFinder.find_spec(%(pkg)r, " - "[os.path.dirname(p)])))" - if sys.version_info >= (3, 5) else - "m = not ie and not pep420 and " + "[os.path.dirname(p)])))", + "m = not ie and not pep420 and not has_mfs and " "sys.modules.setdefault(%(pkg)r, types.ModuleType(%(pkg)r))", "mp = (m or []) and m.__dict__.setdefault('__path__',[])", "(p not in mp) and mp.append(p)", -- cgit v1.2.1 From 27b7d4e47a4d80f5f377cefb87f91aa0e0110246 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 7 Dec 2016 21:38:16 -0500 Subject: Only rely on pep420 for Python 3.3 and 3.4 where method_from_spec isn't available. --- setuptools/namespaces.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'setuptools') diff --git a/setuptools/namespaces.py b/setuptools/namespaces.py index 2399dae5..ce16286f 100755 --- a/setuptools/namespaces.py +++ b/setuptools/namespaces.py @@ -36,7 +36,7 @@ class Installer: _nspkg_tmpl = ( "import sys, types, os, importlib.util, importlib.machinery", - "pep420 = sys.version_info > (3, 3)", + "pep420 = (3, 3) < sys.version_info < (3, 5)", "has_mfs = sys.version_info > (3, 5)", "p = os.path.join(%(root)s, *%(pth)r)", "ie = os.path.exists(os.path.join(p,'__init__.py'))", -- cgit v1.2.1 From 56dea7f0334f60603d4ca6a884ca523fe3389ef3 Mon Sep 17 00:00:00 2001 From: idle sign Date: Sat, 10 Dec 2016 12:06:26 +0700 Subject: `read_configuration()` now accepts `ignore_option_errors`. --- setuptools/config.py | 40 +++++++++++++++++++++++++++++++++------- setuptools/tests/test_config.py | 16 ++++++++++++++++ 2 files changed, 49 insertions(+), 7 deletions(-) (limited to 'setuptools') diff --git a/setuptools/config.py b/setuptools/config.py index 889dc683..007d24e2 100644 --- a/setuptools/config.py +++ b/setuptools/config.py @@ -10,7 +10,8 @@ from setuptools.py26compat import import_module from setuptools.extern.six import string_types -def read_configuration(filepath, find_others=False): +def read_configuration( + filepath, find_others=False, ignore_option_errors=False): """Read given configuration file and returns options from it as a dict. :param str|unicode filepath: Path to configuration file @@ -19,6 +20,11 @@ def read_configuration(filepath, find_others=False): :param bool find_others: Whether to search for other configuration files which could be on in various places. + :param bool ignore_option_errors: Whether to silently ignore + options, values of which could not be resolved (e.g. due to exceptions + in directives such as file:, attr:, etc.). + If False exceptions are propagated as expected. + :rtype: dict """ from setuptools.dist import Distribution, _Distribution @@ -40,7 +46,9 @@ def read_configuration(filepath, find_others=False): _Distribution.parse_config_files(dist, filenames=filenames) - handlers = parse_configuration(dist, dist.command_options) + handlers = parse_configuration( + dist, dist.command_options, + ignore_option_errors=ignore_option_errors) os.chdir(current_directory) @@ -76,7 +84,8 @@ def configuration_to_dict(handlers): return config_dict -def parse_configuration(distribution, command_options): +def parse_configuration( + distribution, command_options, ignore_option_errors=False): """Performs additional parsing of configuration options for a distribution. @@ -84,12 +93,18 @@ def parse_configuration(distribution, command_options): :param Distribution distribution: :param dict command_options: + :param bool ignore_option_errors: Whether to silently ignore + options, values of which could not be resolved (e.g. due to exceptions + in directives such as file:, attr:, etc.). + If False exceptions are propagated as expected. :rtype: list """ - meta = ConfigMetadataHandler(distribution.metadata, command_options) + meta = ConfigMetadataHandler( + distribution.metadata, command_options, ignore_option_errors) meta.parse() - options = ConfigOptionsHandler(distribution, command_options) + options = ConfigOptionsHandler( + distribution, command_options, ignore_option_errors) options.parse() return [meta, options] @@ -111,7 +126,7 @@ class ConfigHandler(object): """ - def __init__(self, target_obj, options): + def __init__(self, target_obj, options, ignore_option_errors=False): sections = {} section_prefix = self.section_prefix @@ -122,6 +137,7 @@ class ConfigHandler(object): section_name = section_name.replace(section_prefix, '').strip('.') sections[section_name] = section_options + self.ignore_option_errors = ignore_option_errors self.target_obj = target_obj self.sections = sections self.set_options = [] @@ -148,9 +164,19 @@ class ConfigHandler(object): # Already inhabited. Skipping. return + skip_option = False parser = self.parsers.get(option_name) if parser: - value = parser(value) + try: + value = parser(value) + + except Exception: + skip_option = True + if not self.ignore_option_errors: + raise + + if skip_option: + return setter = getattr(target_obj, 'set_%s' % option_name, None) if setter is None: diff --git a/setuptools/tests/test_config.py b/setuptools/tests/test_config.py index 21487720..aaf78aef 100644 --- a/setuptools/tests/test_config.py +++ b/setuptools/tests/test_config.py @@ -73,6 +73,22 @@ class TestConfigurationReader: with pytest.raises(DistutilsFileError): read_configuration('%s' % tmpdir.join('setup.cfg')) + def test_ignore_errors(self, tmpdir): + fake_env( + tmpdir, + '[metadata]\n' + 'version = attr: none.VERSION\n' + 'keywords = one, two\n' + ) + with pytest.raises(ImportError): + read_configuration('%s' % tmpdir.join('setup.cfg')) + + config_dict = read_configuration( + '%s' % tmpdir.join('setup.cfg'), ignore_option_errors=True) + + assert config_dict['metadata']['keywords'] == ['one', 'two'] + assert 'version' not in config_dict['metadata'] + class TestMetadata: -- cgit v1.2.1 From b73891f82d5f1a353a2ad0090b1f5edece921508 Mon Sep 17 00:00:00 2001 From: idle sign Date: Sat, 10 Dec 2016 12:30:24 +0700 Subject: config tests refactored. --- setuptools/tests/test_config.py | 43 +++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 17 deletions(-) (limited to 'setuptools') diff --git a/setuptools/tests/test_config.py b/setuptools/tests/test_config.py index aaf78aef..35bdbad1 100644 --- a/setuptools/tests/test_config.py +++ b/setuptools/tests/test_config.py @@ -9,6 +9,13 @@ class ErrConfigHandler(ConfigHandler): """Erroneous handler. Fails to implement required methods.""" +def make_package_dir(name, base_dir): + dir_package = base_dir.mkdir(name) + init_file = dir_package.join('__init__.py') + init_file.write('') + return dir_package, init_file + + def fake_env(tmpdir, setup_cfg, setup_py=None): if setup_py is None: @@ -18,11 +25,12 @@ def fake_env(tmpdir, setup_cfg, setup_py=None): ) tmpdir.join('setup.py').write(setup_py) - tmpdir.join('setup.cfg').write(setup_cfg) + config = tmpdir.join('setup.cfg') + config.write(setup_cfg) + + package_dir, init_file = make_package_dir('fake_package', tmpdir) - package_name = 'fake_package' - dir_package = tmpdir.mkdir(package_name) - dir_package.join('__init__.py').write( + init_file.write( 'VERSION = (1, 2, 3)\n' '\n' 'VERSION_MAJOR = 1' @@ -31,6 +39,7 @@ def fake_env(tmpdir, setup_cfg, setup_py=None): ' return [3, 4, 5, "dev"]\n' '\n' ) + return package_dir, config @contextlib.contextmanager @@ -55,7 +64,7 @@ def test_parsers_implemented(): class TestConfigurationReader: def test_basic(self, tmpdir): - fake_env( + _, config = fake_env( tmpdir, '[metadata]\n' 'version = 10.1.1\n' @@ -64,7 +73,7 @@ class TestConfigurationReader: '[options]\n' 'scripts = bin/a.py, bin/b.py\n' ) - config_dict = read_configuration('%s' % tmpdir.join('setup.cfg')) + config_dict = read_configuration('%s' % config) assert config_dict['metadata']['version'] == '10.1.1' assert config_dict['metadata']['keywords'] == ['one', 'two'] assert config_dict['options']['scripts'] == ['bin/a.py', 'bin/b.py'] @@ -74,17 +83,17 @@ class TestConfigurationReader: read_configuration('%s' % tmpdir.join('setup.cfg')) def test_ignore_errors(self, tmpdir): - fake_env( + _, config = fake_env( tmpdir, '[metadata]\n' 'version = attr: none.VERSION\n' 'keywords = one, two\n' ) with pytest.raises(ImportError): - read_configuration('%s' % tmpdir.join('setup.cfg')) + read_configuration('%s' % config) config_dict = read_configuration( - '%s' % tmpdir.join('setup.cfg'), ignore_option_errors=True) + '%s' % config, ignore_option_errors=True) assert config_dict['metadata']['keywords'] == ['one', 'two'] assert 'version' not in config_dict['metadata'] @@ -188,7 +197,7 @@ class TestMetadata: def test_version(self, tmpdir): - fake_env( + _, config = fake_env( tmpdir, '[metadata]\n' 'version = attr: fake_package.VERSION\n' @@ -196,14 +205,14 @@ class TestMetadata: with get_dist(tmpdir) as dist: assert dist.metadata.version == '1.2.3' - tmpdir.join('setup.cfg').write( + config.write( '[metadata]\n' 'version = attr: fake_package.get_version\n' ) with get_dist(tmpdir) as dist: assert dist.metadata.version == '3.4.5.dev' - tmpdir.join('setup.cfg').write( + config.write( '[metadata]\n' 'version = attr: fake_package.VERSION_MAJOR\n' ) @@ -214,7 +223,7 @@ class TestMetadata: subpack.join('__init__.py').write('') subpack.join('submodule.py').write('VERSION = (2016, 11, 26)') - tmpdir.join('setup.cfg').write( + config.write( '[metadata]\n' 'version = attr: fake_package.subpackage.submodule.VERSION\n' ) @@ -250,7 +259,7 @@ class TestMetadata: ]) # From file. - fake_env( + _, config = fake_env( tmpdir, '[metadata]\n' 'classifiers = file: classifiers\n' @@ -265,7 +274,7 @@ class TestMetadata: assert set(dist.metadata.classifiers) == expected # From section. - tmpdir.join('setup.cfg').write( + config.write( '[metadata.classifiers]\n' 'Framework :: Django\n' 'Programming Language :: Python :: 3.5\n' @@ -454,7 +463,7 @@ class TestOptions: } def test_entry_points(self, tmpdir): - fake_env( + _, config = fake_env( tmpdir, '[options.entry_points]\n' 'group1 = point1 = pack.module:func, ' @@ -479,7 +488,7 @@ class TestOptions: tmpdir.join('entry_points').write(expected) # From file. - tmpdir.join('setup.cfg').write( + config.write( '[options]\n' 'entry_points = file: entry_points\n' ) -- cgit v1.2.1 From a262947e39e6125ee15d3752a2124acf0c62bda6 Mon Sep 17 00:00:00 2001 From: idle sign Date: Sat, 10 Dec 2016 13:33:57 +0700 Subject: Implemented find() configuration support for `packages`. --- setuptools/config.py | 33 +++++++++++++++++++++++++++++++-- setuptools/tests/test_config.py | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 2 deletions(-) (limited to 'setuptools') diff --git a/setuptools/config.py b/setuptools/config.py index 007d24e2..743575f0 100644 --- a/setuptools/config.py +++ b/setuptools/config.py @@ -361,7 +361,10 @@ class ConfigHandler(object): method_postfix = '_%s' % section_name section_parser_method = getattr( - self, 'parse_section%s' % method_postfix, None) + self, + # Dots in section names are tranlsated into dunderscores. + ('parse_section%s' % method_postfix).replace('.', '__'), + None) if section_parser_method is None: raise DistutilsOptionError( @@ -481,8 +484,34 @@ class ConfigOptionsHandler(ConfigHandler): if not value.startswith(find_directive): return self._parse_list(value) + # Read function arguments from a dedicated section. + find_kwargs = self.parse_section_packages__find( + self.sections.get('packages.find', {})) + from setuptools import find_packages - return find_packages() + + return find_packages(**find_kwargs) + + def parse_section_packages__find(self, section_options): + """Parses `packages.find` configuration file section. + + To be used in conjunction with _parse_packages(). + + :param dict section_options: + """ + section_data = self._parse_section_to_dict( + section_options, self._parse_list) + + valid_keys = ['where', 'include', 'exclude'] + + find_kwargs = dict( + [(k, v) for k, v in section_data.items() if k in valid_keys and v]) + + where = find_kwargs.get('where') + if where is not None: + find_kwargs['where'] = where[0] # cast list to single val + + return find_kwargs def parse_section_entry_points(self, section_options): """Parses `entry_points` configuration file section. diff --git a/setuptools/tests/test_config.py b/setuptools/tests/test_config.py index 35bdbad1..08e398b3 100644 --- a/setuptools/tests/test_config.py +++ b/setuptools/tests/test_config.py @@ -446,6 +446,44 @@ class TestOptions: with get_dist(tmpdir) as dist: assert dist.packages == ['fake_package'] + def test_find_directive(self, tmpdir): + dir_package, config = fake_env( + tmpdir, + '[options]\n' + 'packages = find:\n' + ) + + dir_sub_one, _ = make_package_dir('sub_one', dir_package) + dir_sub_two, _ = make_package_dir('sub_two', dir_package) + + with get_dist(tmpdir) as dist: + assert dist.packages == [ + 'fake_package', 'fake_package.sub_two', 'fake_package.sub_one'] + + config.write( + '[options]\n' + 'packages = find:\n' + '\n' + '[options.packages.find]\n' + 'where = .\n' + 'include =\n' + ' fake_package.sub_one\n' + ' two\n' + ) + with get_dist(tmpdir) as dist: + assert dist.packages == ['fake_package.sub_one'] + + config.write( + '[options]\n' + 'packages = find:\n' + '\n' + '[options.packages.find]\n' + 'exclude =\n' + ' fake_package.sub_one\n' + ) + with get_dist(tmpdir) as dist: + assert dist.packages == ['fake_package', 'fake_package.sub_two'] + def test_extras_require(self, tmpdir): fake_env( tmpdir, -- cgit v1.2.1 From f85a821b039a03eb0231e6bd0fc925a4e37f3911 Mon Sep 17 00:00:00 2001 From: idle sign Date: Sat, 10 Dec 2016 13:48:03 +0700 Subject: Fixed test for `find()` results. --- setuptools/tests/test_config.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'setuptools') diff --git a/setuptools/tests/test_config.py b/setuptools/tests/test_config.py index 08e398b3..677ccf2c 100644 --- a/setuptools/tests/test_config.py +++ b/setuptools/tests/test_config.py @@ -457,8 +457,9 @@ class TestOptions: dir_sub_two, _ = make_package_dir('sub_two', dir_package) with get_dist(tmpdir) as dist: - assert dist.packages == [ - 'fake_package', 'fake_package.sub_two', 'fake_package.sub_one'] + assert set(dist.packages) == set([ + 'fake_package', 'fake_package.sub_two', 'fake_package.sub_one' + ]) config.write( '[options]\n' @@ -482,7 +483,8 @@ class TestOptions: ' fake_package.sub_one\n' ) with get_dist(tmpdir) as dist: - assert dist.packages == ['fake_package', 'fake_package.sub_two'] + assert set(dist.packages) == set( + ['fake_package', 'fake_package.sub_two']) def test_extras_require(self, tmpdir): fake_env( -- cgit v1.2.1 From 35f3d1f37fdb137d5f5316058d49c96b9f28ca2d Mon Sep 17 00:00:00 2001 From: idle sign Date: Sat, 10 Dec 2016 15:23:49 +0700 Subject: `test_ignore_errors` side effect mitigated. --- setuptools/tests/test_config.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'setuptools') diff --git a/setuptools/tests/test_config.py b/setuptools/tests/test_config.py index 677ccf2c..fa8d523b 100644 --- a/setuptools/tests/test_config.py +++ b/setuptools/tests/test_config.py @@ -98,6 +98,8 @@ class TestConfigurationReader: assert config_dict['metadata']['keywords'] == ['one', 'two'] assert 'version' not in config_dict['metadata'] + config.remove() + class TestMetadata: -- cgit v1.2.1 From c471788dbccf4fcf669d141e6f1325c1b43b8b94 Mon Sep 17 00:00:00 2001 From: idle sign Date: Sat, 10 Dec 2016 22:24:01 +0700 Subject: Proper finalization for `read_configuration()`. --- setuptools/config.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) (limited to 'setuptools') diff --git a/setuptools/config.py b/setuptools/config.py index 743575f0..d71ff028 100644 --- a/setuptools/config.py +++ b/setuptools/config.py @@ -38,19 +38,21 @@ def read_configuration( current_directory = os.getcwd() os.chdir(os.path.dirname(filepath)) - dist = Distribution() + try: + dist = Distribution() - filenames = dist.find_config_files() if find_others else [] - if filepath not in filenames: - filenames.append(filepath) + filenames = dist.find_config_files() if find_others else [] + if filepath not in filenames: + filenames.append(filepath) - _Distribution.parse_config_files(dist, filenames=filenames) + _Distribution.parse_config_files(dist, filenames=filenames) - handlers = parse_configuration( - dist, dist.command_options, - ignore_option_errors=ignore_option_errors) + handlers = parse_configuration( + dist, dist.command_options, + ignore_option_errors=ignore_option_errors) - os.chdir(current_directory) + finally: + os.chdir(current_directory) return configuration_to_dict(handlers) -- cgit v1.2.1 From 6070cc15ed234a71777747c4cdd55f228f3d7b90 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 11 Dec 2016 15:58:49 -0500 Subject: Don't nullify module when has_mfs --- setuptools/namespaces.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'setuptools') diff --git a/setuptools/namespaces.py b/setuptools/namespaces.py index 96aa20e7..69debe4b 100755 --- a/setuptools/namespaces.py +++ b/setuptools/namespaces.py @@ -51,7 +51,7 @@ class Installer: "importlib.util.module_from_spec(" "importlib.machinery.PathFinder.find_spec(%(pkg)r, " "[os.path.dirname(p)])))", - "m = not has_mfs and " + "m = m or not has_mfs and " "sys.modules.setdefault(%(pkg)r, types.ModuleType(%(pkg)r))", "mp = (m or []) and m.__dict__.setdefault('__path__',[])", "(p not in mp) and mp.append(p)", -- cgit v1.2.1 From 8fa9a8640eb85e7250b8e3ca15124e74ce6014e7 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 11 Dec 2016 16:02:14 -0500 Subject: Only import modules when they're expected to be present --- setuptools/namespaces.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'setuptools') diff --git a/setuptools/namespaces.py b/setuptools/namespaces.py index 69debe4b..0a889f22 100755 --- a/setuptools/namespaces.py +++ b/setuptools/namespaces.py @@ -43,9 +43,11 @@ class Installer: return self.target _nspkg_tmpl = ( - "import sys, types, os, importlib.util, importlib.machinery", + "import sys, types, os", "has_mfs = sys.version_info > (3, 5)", "p = os.path.join(%(root)s, *%(pth)r)", + "importlib = has_mfs and __import__('importlib.util')", + "has_mfs and __import__('importlib.machinery')", "m = has_mfs and " "sys.modules.setdefault(%(pkg)r, " "importlib.util.module_from_spec(" -- cgit v1.2.1 From 230ffaafd061b42f8674b6b6b75ed8133e8d6934 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 11 Dec 2016 16:18:29 -0500 Subject: Expect failure on Python 3.4 and earlier as module_from_spec isn't available. Ref #250. --- setuptools/tests/test_namespaces.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'setuptools') diff --git a/setuptools/tests/test_namespaces.py b/setuptools/tests/test_namespaces.py index 28c5e9de..e7fa4ee6 100644 --- a/setuptools/tests/test_namespaces.py +++ b/setuptools/tests/test_namespaces.py @@ -11,8 +11,8 @@ from . import namespaces class TestNamespaces: - @pytest.mark.xfail(sys.version_info < (3, 3), - reason="Requires PEP 420") + @pytest.mark.xfail(sys.version_info < (3, 5), + reason="Requires importlib.util.module_from_spec") @pytest.mark.skipif(bool(os.environ.get("APPVEYOR")), reason="https://github.com/pypa/setuptools/issues/851") def test_mixed_site_and_non_site(self, tmpdir): -- cgit v1.2.1 From 8c055ff64792c23e80f85f4c127d003fd2ae4b7d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 11 Dec 2016 20:48:07 -0500 Subject: Mark another test to fail. Ref #851 --- setuptools/tests/test_develop.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'setuptools') diff --git a/setuptools/tests/test_develop.py b/setuptools/tests/test_develop.py index e0227453..d1e40929 100644 --- a/setuptools/tests/test_develop.py +++ b/setuptools/tests/test_develop.py @@ -136,6 +136,8 @@ class TestNamespaces: with src_dir.as_cwd(): subprocess.check_call(develop_cmd, env=env) + @pytest.mark.skipif(bool(os.environ.get("APPVEYOR")), + reason="https://github.com/pypa/setuptools/issues/851") def test_namespace_package_importable(self, tmpdir): """ Installing two packages sharing the same namespace, one installed -- cgit v1.2.1 From 357b7c659b6893dec4145d492084217b9317bca1 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 12 Dec 2016 19:45:51 -0500 Subject: Add test attempting to capture failure, but it passes. Ref #885. --- setuptools/tests/test_namespaces.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'setuptools') diff --git a/setuptools/tests/test_namespaces.py b/setuptools/tests/test_namespaces.py index e7fa4ee6..2aefb487 100644 --- a/setuptools/tests/test_namespaces.py +++ b/setuptools/tests/test_namespaces.py @@ -50,3 +50,26 @@ class TestNamespaces: ] env = dict(PYTHONPATH=python_path) subprocess.check_call(try_import, env=env) + + def test_pkg_resources_import(self, tmpdir): + """ + Ensure that a namespace package doesn't break on import + of pkg_resources. + """ + pkg = namespaces.build_namespace_package(tmpdir, 'myns.pkgA') + target = tmpdir / 'packages' + target.mkdir() + env = dict(PYTHONPATH=str(target)) + install_cmd = [ + sys.executable, + '-m', 'easy_install', + '-d', str(target), + str(pkg), + ] + subprocess.check_call(install_cmd, env=env) + namespaces.make_site_dir(target) + try_import = [ + sys.executable, + '-c', 'import pkg_resources', + ] + subprocess.check_call(try_import, env=env) -- cgit v1.2.1 From d9c9284e19ce475c2366b279dd4db82a2751a571 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 12 Dec 2016 21:14:34 -0500 Subject: Additionally, in test_develop, ensure that pkg_resources is importable. Ref #885. --- setuptools/tests/test_develop.py | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'setuptools') diff --git a/setuptools/tests/test_develop.py b/setuptools/tests/test_develop.py index d1e40929..430a07e6 100644 --- a/setuptools/tests/test_develop.py +++ b/setuptools/tests/test_develop.py @@ -165,3 +165,9 @@ class TestNamespaces: env = dict(PYTHONPATH=str(target)) subprocess.check_call(try_import, env=env) + # additionally ensure that pkg_resources import works + pkg_resources_imp = [ + sys.executable, + '-c', 'import pkg_resources', + ] + subprocess.check_call(pkg_resources_imp, env=env) -- cgit v1.2.1 From bffd26f485964e1cfb881ccf85179b2947d5e734 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 13 Dec 2016 21:06:00 -0500 Subject: Add test capturing expectation when a package is both installed and in the current working directory. Ref #885. --- setuptools/tests/test_namespaces.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'setuptools') diff --git a/setuptools/tests/test_namespaces.py b/setuptools/tests/test_namespaces.py index 2aefb487..5199129e 100644 --- a/setuptools/tests/test_namespaces.py +++ b/setuptools/tests/test_namespaces.py @@ -73,3 +73,30 @@ class TestNamespaces: '-c', 'import pkg_resources', ] subprocess.check_call(try_import, env=env) + + @pytest.mark.skipif(bool(os.environ.get("APPVEYOR")), + reason="https://github.com/pypa/setuptools/issues/851") + def test_namespace_package_installed_and_cwd(self, tmpdir): + """ + Installing a namespace packages but also having it in the current + working directory, only one version should take precedence. + """ + pkg_A = namespaces.build_namespace_package(tmpdir, 'myns.pkgA') + target = tmpdir / 'packages' + # use pip to install to the target directory + install_cmd = [ + 'pip', + 'install', + str(pkg_A), + '-t', str(target), + ] + subprocess.check_call(install_cmd) + namespaces.make_site_dir(target) + + # ensure that package imports and pkg_resources imports + pkg_resources_imp = [ + sys.executable, + '-c', 'import pkg_resources; import myns.pkgA', + ] + env = dict(PYTHONPATH=str(target)) + subprocess.check_call(pkg_resources_imp, env=env, cwd=str(pkg_A)) -- cgit v1.2.1 From 3e05a9630ff0e0596a539d4b6d24f97d3fb987f8 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 13 Dec 2016 21:17:10 -0500 Subject: Skip again on appveyor --- setuptools/tests/test_namespaces.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'setuptools') diff --git a/setuptools/tests/test_namespaces.py b/setuptools/tests/test_namespaces.py index 5199129e..451df3c4 100644 --- a/setuptools/tests/test_namespaces.py +++ b/setuptools/tests/test_namespaces.py @@ -51,6 +51,8 @@ class TestNamespaces: env = dict(PYTHONPATH=python_path) subprocess.check_call(try_import, env=env) + @pytest.mark.skipif(bool(os.environ.get("APPVEYOR")), + reason="https://github.com/pypa/setuptools/issues/851") def test_pkg_resources_import(self, tmpdir): """ Ensure that a namespace package doesn't break on import -- cgit v1.2.1 From e066c97f828ec5ab8be4dde6e340dbfdcd312ec0 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 14 Dec 2016 10:47:11 -0500 Subject: Backport config file parsing behavior from Python 3.7. Ref #889. --- setuptools/dist.py | 3 +- setuptools/py36compat.py | 82 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 setuptools/py36compat.py (limited to 'setuptools') diff --git a/setuptools/dist.py b/setuptools/dist.py index c04e6426..159464be 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -21,6 +21,7 @@ from setuptools import windows_support from setuptools.monkey import get_unpatched from setuptools.config import parse_configuration import pkg_resources +from .py36compat import Distribution_parse_config_files def _get_unpatched(cls): @@ -213,7 +214,7 @@ def check_packages(dist, attr, value): _Distribution = get_unpatched(distutils.core.Distribution) -class Distribution(_Distribution): +class Distribution(Distribution_parse_config_files, _Distribution): """Distribution with support for features, tests, and package data This is an enhanced version of 'distutils.dist.Distribution' that diff --git a/setuptools/py36compat.py b/setuptools/py36compat.py new file mode 100644 index 00000000..5bad9630 --- /dev/null +++ b/setuptools/py36compat.py @@ -0,0 +1,82 @@ +import sys +from distutils.errors import DistutilsOptionError +from distutils.util import strtobool +from distutils.debug import DEBUG + + +class Distribution_parse_config_files: + """ + Mix-in providing forward-compatibility for functionality to be + included by default on Python 3.7. + + Do not edit the code in this class except to update functionality + as implemented in distutils. + """ + def parse_config_files(self, filenames=None): + from configparser import ConfigParser + + # Ignore install directory options if we have a venv + if sys.prefix != sys.base_prefix: + ignore_options = [ + 'install-base', 'install-platbase', 'install-lib', + 'install-platlib', 'install-purelib', 'install-headers', + 'install-scripts', 'install-data', 'prefix', 'exec-prefix', + 'home', 'user', 'root'] + else: + ignore_options = [] + + ignore_options = frozenset(ignore_options) + + if filenames is None: + filenames = self.find_config_files() + + if DEBUG: + self.announce("Distribution.parse_config_files():") + + parser = ConfigParser() + for filename in filenames: + if DEBUG: + self.announce(" reading %s" % filename) + parser.read(filename) + for section in parser.sections(): + options = parser.options(section) + opt_dict = self.get_option_dict(section) + + for opt in options: + if opt != '__name__' and opt not in ignore_options: + val = parser.get(section,opt) + opt = opt.replace('-', '_') + opt_dict[opt] = (filename, val) + + # Make the ConfigParser forget everything (so we retain + # the original filenames that options come from) + parser.__init__() + + # If there was a "global" section in the config file, use it + # to set Distribution options. + + if 'global' in self.command_options: + for (opt, (src, val)) in self.command_options['global'].items(): + alias = self.negative_opt.get(opt) + try: + if alias: + setattr(self, alias, not strtobool(val)) + elif opt in ('verbose', 'dry_run'): # ugh! + setattr(self, opt, strtobool(val)) + else: + setattr(self, opt, val) + except ValueError as msg: + raise DistutilsOptionError(msg) + + +if sys.version_info < (3,): + # Python 2 behavior is sufficient + class Distribution_parse_config_files: + pass + + +if False: + # When updated behavior is available upstream, + # disable override here. + class Distribution_parse_config_files: + pass -- cgit v1.2.1 From 1802d1322ccffcd0b79c63aa7ce9d22e0106015c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 14 Dec 2016 11:00:45 -0500 Subject: Apply patch, disabling interpolation. Fixes #889. --- setuptools/py36compat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'setuptools') diff --git a/setuptools/py36compat.py b/setuptools/py36compat.py index 5bad9630..f5279696 100644 --- a/setuptools/py36compat.py +++ b/setuptools/py36compat.py @@ -33,7 +33,7 @@ class Distribution_parse_config_files: if DEBUG: self.announce("Distribution.parse_config_files():") - parser = ConfigParser() + parser = ConfigParser(interpolation=None) for filename in filenames: if DEBUG: self.announce(" reading %s" % filename) -- cgit v1.2.1 From 980c2c5afd20fb9f7e50cb8ec0c42abc664da352 Mon Sep 17 00:00:00 2001 From: Tim Heap Date: Thu, 15 Dec 2016 10:11:37 +1100 Subject: Revert "Fix #849 global-exclude globbing" This reverts commit 23aba916e1070d3cf9723af85a6ce07c89053931. --- setuptools/command/egg_info.py | 4 ++-- setuptools/tests/test_manifest.py | 12 ------------ 2 files changed, 2 insertions(+), 14 deletions(-) (limited to 'setuptools') diff --git a/setuptools/command/egg_info.py b/setuptools/command/egg_info.py index 8a06e496..6f8fd874 100755 --- a/setuptools/command/egg_info.py +++ b/setuptools/command/egg_info.py @@ -457,7 +457,7 @@ class FileList(_FileList): """ if self.allfiles is None: self.findall() - match = translate_pattern(os.path.join('**', '*' + pattern)) + match = translate_pattern(os.path.join('**', pattern)) found = [f for f in self.allfiles if match.match(f)] self.extend(found) return bool(found) @@ -466,7 +466,7 @@ class FileList(_FileList): """ Exclude all files anywhere that match the pattern. """ - match = translate_pattern(os.path.join('**', '*' + pattern)) + match = translate_pattern(os.path.join('**', pattern)) return self._remove_files(match.match) def append(self, item): diff --git a/setuptools/tests/test_manifest.py b/setuptools/tests/test_manifest.py index 62b6d708..602c43a2 100644 --- a/setuptools/tests/test_manifest.py +++ b/setuptools/tests/test_manifest.py @@ -449,11 +449,6 @@ class TestFileListTest(TempDirTestCase): assert file_list.files == ['a.py', l('d/c.py')] self.assertWarnings() - file_list.process_template_line('global-include .txt') - file_list.sort() - assert file_list.files == ['a.py', 'b.txt', l('d/c.py')] - self.assertNoWarnings() - def test_global_exclude(self): l = make_local_path # global-exclude @@ -470,13 +465,6 @@ class TestFileListTest(TempDirTestCase): assert file_list.files == ['b.txt'] self.assertWarnings() - file_list = FileList() - file_list.files = ['a.py', 'b.txt', l('d/c.pyc'), 'e.pyo'] - file_list.process_template_line('global-exclude .py[co]') - file_list.sort() - assert file_list.files == ['a.py', 'b.txt'] - self.assertNoWarnings() - def test_recursive_include(self): l = make_local_path # recursive-include -- cgit v1.2.1 From 9f37eb817df5d9453e49edbcf2760832f333af68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Fri, 16 Dec 2016 15:32:16 +0100 Subject: Exit on test failure When test fails, it should not continue to run other commands. Fixes #891 --- setuptools/command/test.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'setuptools') diff --git a/setuptools/command/test.py b/setuptools/command/test.py index 9a5117be..60ba2354 100644 --- a/setuptools/command/test.py +++ b/setuptools/command/test.py @@ -226,12 +226,14 @@ class test(Command): list(map(sys.modules.__delitem__, del_modules)) exit_kwarg = {} if sys.version_info < (2, 7) else {"exit": False} - unittest_main( + test = unittest_main( None, None, self._argv, testLoader=self._resolve_as_ep(self.test_loader), testRunner=self._resolve_as_ep(self.test_runner), **exit_kwarg ) + if not test.result.wasSuccessful(): + sys.exit(1) @property def _argv(self): -- cgit v1.2.1 From 2c4fd43277fc477d85b50e15c37b176136676270 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Fri, 16 Dec 2016 16:15:04 +0100 Subject: Raise DistutilsError and log result --- setuptools/command/test.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'setuptools') diff --git a/setuptools/command/test.py b/setuptools/command/test.py index 60ba2354..ef0af12f 100644 --- a/setuptools/command/test.py +++ b/setuptools/command/test.py @@ -3,7 +3,8 @@ import operator import sys import contextlib import itertools -from distutils.errors import DistutilsOptionError +from distutils.errors import DistutilsError, DistutilsOptionError +from distutils import log from unittest import TestLoader from setuptools.extern import six @@ -233,7 +234,9 @@ class test(Command): **exit_kwarg ) if not test.result.wasSuccessful(): - sys.exit(1) + msg = 'Test failed: %s' % test.result + self.announce(msg, log.ERROR) + raise DistutilsError(msg) @property def _argv(self): -- cgit v1.2.1 From 53e5575e11a35aaf761da56aa6728cdcd7c04ec0 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 18 Dec 2016 08:38:56 -0500 Subject: When invoking rmtree, ensure the parameter is unicode to avoid errors when the tree contains Unicode filenames. Fixes #704. --- setuptools/command/easy_install.py | 3 +++ setuptools/tests/test_easy_install.py | 6 ++---- 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'setuptools') diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py index 03dd6768..7d982d89 100755 --- a/setuptools/command/easy_install.py +++ b/setuptools/command/easy_install.py @@ -666,6 +666,9 @@ class easy_install(Command): finally: if os.path.exists(tmpdir): + # workaround for http://bugs.python.org/issue24672 + if six.PY2: + tmpdir = six.u(tmpdir) rmtree(tmpdir) def install_item(self, spec, download, tmpdir, deps, install_needed=False): diff --git a/setuptools/tests/test_easy_install.py b/setuptools/tests/test_easy_install.py index 209e6b78..08138efc 100644 --- a/setuptools/tests/test_easy_install.py +++ b/setuptools/tests/test_easy_install.py @@ -169,10 +169,8 @@ class TestEasyInstallTest: sdist_zip.close() return str(sdist) - @pytest.mark.xfail(reason="#709 and #710") - # also - #@pytest.mark.xfail(setuptools.tests.is_ascii, - # reason="https://github.com/pypa/setuptools/issues/706") + @pytest.mark.xfail(setuptools.tests.is_ascii, + reason="https://github.com/pypa/setuptools/issues/706") def test_unicode_filename_in_sdist(self, sdist_unicode, tmpdir, monkeypatch): """ The install command should execute correctly even if -- cgit v1.2.1 From 7f89e790d228aeece7df1fda8eb6c762d772c450 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 18 Dec 2016 09:30:21 -0500 Subject: Can't use six.u as 'c:\users' triggers unicode_escape and fails. Ref #704. --- setuptools/command/easy_install.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'setuptools') diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py index 7d982d89..e3b7161a 100755 --- a/setuptools/command/easy_install.py +++ b/setuptools/command/easy_install.py @@ -668,7 +668,7 @@ class easy_install(Command): if os.path.exists(tmpdir): # workaround for http://bugs.python.org/issue24672 if six.PY2: - tmpdir = six.u(tmpdir) + tmpdir = tmpdir.decode('ascii') rmtree(tmpdir) def install_item(self, spec, download, tmpdir, deps, install_needed=False): -- cgit v1.2.1 From 674d5ecb1695de056226d1ad65c5d8b48ca99f3b Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 18 Dec 2016 09:45:13 -0500 Subject: Extract tmpdir as a context manager --- setuptools/command/easy_install.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) (limited to 'setuptools') diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py index e3b7161a..b9d41cb0 100755 --- a/setuptools/command/easy_install.py +++ b/setuptools/command/easy_install.py @@ -627,12 +627,24 @@ class easy_install(Command): (spec.key, self.build_directory) ) - def easy_install(self, spec, deps=False): + @contextlib.contextmanager + def _tmpdir(self): tmpdir = tempfile.mkdtemp(prefix="easy_install-") + try: + yield tmpdir + finally: + if not os.path.exists(tmpdir): + return + # workaround for http://bugs.python.org/issue24672 + if six.PY2: + tmpdir = tmpdir.decode('ascii') + rmtree(tmpdir) + + def easy_install(self, spec, deps=False): if not self.editable: self.install_site_py() - try: + with self._tmpdir() as tmpdir: if not isinstance(spec, Requirement): if URL_SCHEME(spec): # It's a url, download it to tmpdir and process @@ -664,13 +676,6 @@ class easy_install(Command): else: return self.install_item(spec, dist.location, tmpdir, deps) - finally: - if os.path.exists(tmpdir): - # workaround for http://bugs.python.org/issue24672 - if six.PY2: - tmpdir = tmpdir.decode('ascii') - rmtree(tmpdir) - def install_item(self, spec, download, tmpdir, deps, install_needed=False): # Installation is also needed if file in tmpdir or is not an egg -- cgit v1.2.1 From 6c6f4caa4bacce4b719b1e03fb02fdb857b1a135 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 18 Dec 2016 10:07:11 -0500 Subject: Move toward future compatibility using unicode strings, but cast to native str as workaround for #709, #710, and #712. --- setuptools/command/easy_install.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) (limited to 'setuptools') diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py index b9d41cb0..14ad25c2 100755 --- a/setuptools/command/easy_install.py +++ b/setuptools/command/easy_install.py @@ -629,16 +629,12 @@ class easy_install(Command): @contextlib.contextmanager def _tmpdir(self): - tmpdir = tempfile.mkdtemp(prefix="easy_install-") + tmpdir = tempfile.mkdtemp(prefix=six.u("easy_install-")) try: - yield tmpdir + # cast to str as workaround for #709 and #710 and #712 + yield str(tmpdir) finally: - if not os.path.exists(tmpdir): - return - # workaround for http://bugs.python.org/issue24672 - if six.PY2: - tmpdir = tmpdir.decode('ascii') - rmtree(tmpdir) + os.path.exists(tmpdir) and rmtree(tmpdir) def easy_install(self, spec, deps=False): if not self.editable: -- cgit v1.2.1 From cd2a519c6592d90e8658a5e90d8b0342d08b9ae9 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 18 Dec 2016 11:51:19 -0500 Subject: In sandbox.run_setup, always ensure that __file__ is str. Fixes #712. --- setuptools/sandbox.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'setuptools') diff --git a/setuptools/sandbox.py b/setuptools/sandbox.py index d882d715..817a3afa 100755 --- a/setuptools/sandbox.py +++ b/setuptools/sandbox.py @@ -241,8 +241,15 @@ def run_setup(setup_script, args): working_set.__init__() working_set.callbacks.append(lambda dist: dist.activate()) + # __file__ should be a byte string on Python 2 (#712) + dunder_file = ( + setup_script + if isinstance(setup_script, str) else + setup_script.encode(sys.getfilesystemencoding()) + ) + def runner(): - ns = dict(__file__=setup_script, __name__='__main__') + ns = dict(__file__=dunder_file, __name__='__main__') _execfile(setup_script, ns) DirectorySandbox(setup_dir).run(runner) -- cgit v1.2.1 From 5ad13718686bee04a93b4e86929c1bb170f14a52 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 21 Dec 2016 14:15:37 -0500 Subject: Cast the value to rmtree to bytes on Linux and Python 2 when the filesystemencoding is ascii, and let posixpath work its voodoo. Fixes #706. --- setuptools/command/easy_install.py | 3 ++- setuptools/py27compat.py | 11 +++++++++++ setuptools/tests/test_easy_install.py | 2 -- 3 files changed, 13 insertions(+), 3 deletions(-) (limited to 'setuptools') diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py index 14ad25c2..36e7f359 100755 --- a/setuptools/command/easy_install.py +++ b/setuptools/command/easy_install.py @@ -46,6 +46,7 @@ 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 +from setuptools.py27compat import rmtree_safe from setuptools.command import setopt from setuptools.archive_util import unpack_archive from setuptools.package_index import ( @@ -634,7 +635,7 @@ class easy_install(Command): # cast to str as workaround for #709 and #710 and #712 yield str(tmpdir) finally: - os.path.exists(tmpdir) and rmtree(tmpdir) + os.path.exists(tmpdir) and rmtree(rmtree_safe(tmpdir)) def easy_install(self, spec, deps=False): if not self.editable: diff --git a/setuptools/py27compat.py b/setuptools/py27compat.py index 4e3e4ab3..a71a936e 100644 --- a/setuptools/py27compat.py +++ b/setuptools/py27compat.py @@ -3,6 +3,7 @@ Compatibility Support for Python 2.7 and earlier """ import sys +import platform def get_all_headers(message, key): @@ -16,3 +17,13 @@ if sys.version_info < (3,): def get_all_headers(message, key): return message.getheaders(key) + + +linux_py2_ascii = ( + platform.system() == 'Linux' and + sys.getfilesystemencoding() == 'ascii' and + sys.version_info < (3,) +) + +rmtree_safe = str if linux_py2_ascii else lambda x: x +"""Workaround for http://bugs.python.org/issue24672""" diff --git a/setuptools/tests/test_easy_install.py b/setuptools/tests/test_easy_install.py index 08138efc..1ea33b08 100644 --- a/setuptools/tests/test_easy_install.py +++ b/setuptools/tests/test_easy_install.py @@ -169,8 +169,6 @@ class TestEasyInstallTest: sdist_zip.close() return str(sdist) - @pytest.mark.xfail(setuptools.tests.is_ascii, - reason="https://github.com/pypa/setuptools/issues/706") def test_unicode_filename_in_sdist(self, sdist_unicode, tmpdir, monkeypatch): """ The install command should execute correctly even if -- cgit v1.2.1 From 7a1c700e16523ad07532bea2ed87718fe29d3595 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 22 Dec 2016 09:31:40 -0500 Subject: Re-use test.paths_on_pythonpath to extend the PYTHONPATH variable rather than erasing it. When tests are run under pytest-runner (or other setup.py test invocations), the PYTHONPATH is carefully curated to include dependencies and the project under test. Overwriting PYTHONPATH will break tests in those environments. Fixes #884. --- setuptools/tests/test_develop.py | 12 +++++++----- setuptools/tests/test_namespaces.py | 17 +++++++++-------- 2 files changed, 16 insertions(+), 13 deletions(-) (limited to 'setuptools') diff --git a/setuptools/tests/test_develop.py b/setuptools/tests/test_develop.py index 430a07e6..5dd72aae 100644 --- a/setuptools/tests/test_develop.py +++ b/setuptools/tests/test_develop.py @@ -10,6 +10,7 @@ import io import subprocess from setuptools.extern import six +from setuptools.command import test import pytest @@ -132,9 +133,9 @@ class TestNamespaces: 'develop', '--install-dir', str(target), ] - env = dict(PYTHONPATH=str(target)) with src_dir.as_cwd(): - subprocess.check_call(develop_cmd, env=env) + with test.test.paths_on_pythonpath([str(target)]): + subprocess.check_call(develop_cmd) @pytest.mark.skipif(bool(os.environ.get("APPVEYOR")), reason="https://github.com/pypa/setuptools/issues/851") @@ -162,12 +163,13 @@ class TestNamespaces: sys.executable, '-c', 'import myns.pkgA; import myns.pkgB', ] - env = dict(PYTHONPATH=str(target)) - subprocess.check_call(try_import, env=env) + with test.test.paths_on_pythonpath([str(target)]): + subprocess.check_call(try_import) # additionally ensure that pkg_resources import works pkg_resources_imp = [ sys.executable, '-c', 'import pkg_resources', ] - subprocess.check_call(pkg_resources_imp, env=env) + with test.test.paths_on_pythonpath([str(target)]): + subprocess.check_call(pkg_resources_imp) diff --git a/setuptools/tests/test_namespaces.py b/setuptools/tests/test_namespaces.py index 451df3c4..721cad1e 100644 --- a/setuptools/tests/test_namespaces.py +++ b/setuptools/tests/test_namespaces.py @@ -7,6 +7,7 @@ import subprocess import pytest from . import namespaces +from setuptools.command import test class TestNamespaces: @@ -27,7 +28,6 @@ class TestNamespaces: site_packages = tmpdir / 'site-packages' path_packages = tmpdir / 'path-packages' targets = site_packages, path_packages - python_path = os.pathsep.join(map(str, targets)) # use pip to install to the target directory install_cmd = [ 'pip', @@ -48,8 +48,8 @@ class TestNamespaces: sys.executable, '-c', 'import myns.pkgA; import myns.pkgB', ] - env = dict(PYTHONPATH=python_path) - subprocess.check_call(try_import, env=env) + with test.test.paths_on_pythonpath(map(str, targets)): + subprocess.check_call(try_import) @pytest.mark.skipif(bool(os.environ.get("APPVEYOR")), reason="https://github.com/pypa/setuptools/issues/851") @@ -61,20 +61,21 @@ class TestNamespaces: pkg = namespaces.build_namespace_package(tmpdir, 'myns.pkgA') target = tmpdir / 'packages' target.mkdir() - env = dict(PYTHONPATH=str(target)) install_cmd = [ sys.executable, '-m', 'easy_install', '-d', str(target), str(pkg), ] - subprocess.check_call(install_cmd, env=env) + with test.test.paths_on_pythonpath([str(target)]): + subprocess.check_call(install_cmd) namespaces.make_site_dir(target) try_import = [ sys.executable, '-c', 'import pkg_resources', ] - subprocess.check_call(try_import, env=env) + with test.test.paths_on_pythonpath([str(target)]): + subprocess.check_call(try_import) @pytest.mark.skipif(bool(os.environ.get("APPVEYOR")), reason="https://github.com/pypa/setuptools/issues/851") @@ -100,5 +101,5 @@ class TestNamespaces: sys.executable, '-c', 'import pkg_resources; import myns.pkgA', ] - env = dict(PYTHONPATH=str(target)) - subprocess.check_call(pkg_resources_imp, env=env, cwd=str(pkg_A)) + with test.test.paths_on_pythonpath([str(target)]): + subprocess.check_call(pkg_resources_imp, cwd=str(pkg_A)) -- cgit v1.2.1 From 07a7b06dd8c6dc08c789505f263986e5f084f802 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 24 Dec 2016 14:21:02 -0500 Subject: Traverse the class hierarchy when searching for the unpatched class. Ref #889. --- setuptools/monkey.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'setuptools') diff --git a/setuptools/monkey.py b/setuptools/monkey.py index aabc280f..dbe9a617 100644 --- a/setuptools/monkey.py +++ b/setuptools/monkey.py @@ -7,6 +7,7 @@ import distutils.filelist import platform import types import functools +import inspect from .py26compat import import_module from setuptools.extern import six @@ -35,12 +36,16 @@ def get_unpatched_class(cls): Also ensures that no other distutils extension monkeypatched the distutils first. """ - while cls.__module__.startswith('setuptools'): - cls, = cls.__bases__ - if not cls.__module__.startswith('distutils'): + external_bases = ( + cls + for cls in inspect.getmro(cls) + if not cls.__module__.startswith('setuptools') + ) + base = next(external_bases) + if not base.__module__.startswith('distutils'): msg = "distutils has already been patched by %r" % cls raise AssertionError(msg) - return cls + return base def patch_all(): -- cgit v1.2.1 From 1bd827efdf08b77f8a0a29c58dfc805368466964 Mon Sep 17 00:00:00 2001 From: Preston Landers Date: Wed, 28 Dec 2016 13:14:53 -0600 Subject: Attempt to fix issue #866 by iterating over code with `dis.Bytecode` instead of the internal `_iter_code`. The `dis` module was already used in `_iter_code` so I figured it was safe to use `Bytecode` from it. Not sure how this assumption holds up across all supported Python releases. I can only assume `Bytecode` wasn't there before when `_iter_code` was originally written? Note that `_iter_code` doesn't appear to be called anywhere in light of this change so I removed it. I should also note that `get_module_constant` has never worked with `setuptools.__version__` (returns -1) because it's not a string literal; it gets that attribute from another module. But this change does work in cases where a string literal is requested. https://github.com/pypa/setuptools/issues/866 --- setuptools/depends.py | 43 ++++++------------------------------------- 1 file changed, 6 insertions(+), 37 deletions(-) (limited to 'setuptools') diff --git a/setuptools/depends.py b/setuptools/depends.py index 89d39a50..d8496ef8 100644 --- a/setuptools/depends.py +++ b/setuptools/depends.py @@ -4,7 +4,6 @@ import marshal from distutils.version import StrictVersion from imp import PKG_DIRECTORY, PY_COMPILED, PY_SOURCE, PY_FROZEN -from setuptools.extern import six __all__ = [ 'Require', 'find_module', 'get_module_constant', 'extract_constant' @@ -78,39 +77,6 @@ class Require: return self.version_ok(version) -def _iter_code(code): - """Yield '(op,arg)' pair for each operation in code object 'code'""" - - from array import array - from dis import HAVE_ARGUMENT, EXTENDED_ARG - - bytes = array('b', code.co_code) - eof = len(code.co_code) - - ptr = 0 - extended_arg = 0 - - while ptr < eof: - - op = bytes[ptr] - - if op >= HAVE_ARGUMENT: - - arg = bytes[ptr + 1] + bytes[ptr + 2] * 256 + extended_arg - ptr += 3 - - if op == EXTENDED_ARG: - long_type = six.integer_types[-1] - extended_arg = arg * long_type(65536) - continue - - else: - arg = None - ptr += 1 - - yield op, arg - - def find_module(module, paths=None): """Just like 'imp.find_module()', but with package support""" @@ -176,11 +142,12 @@ def extract_constant(code, symbol, default=-1): only 'STORE_NAME' and 'STORE_GLOBAL' opcodes are checked, and 'symbol' must be present in 'code.co_names'. """ - if symbol not in code.co_names: - # name's not there, can't possibly be an assigment + # name's not there, can't possibly be an assignment return None + from dis import Bytecode + name_idx = list(code.co_names).index(symbol) STORE_NAME = 90 @@ -189,7 +156,9 @@ def extract_constant(code, symbol, default=-1): const = default - for op, arg in _iter_code(code): + for byte_code in Bytecode(code): + op = byte_code.opcode + arg = byte_code.arg if op == LOAD_CONST: const = code.co_consts[arg] -- cgit v1.2.1 From 6a960cb9e8cf17385e3b8a52d46cf71a9747e19d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 28 Dec 2016 17:03:33 -0500 Subject: Add tests capturing failure of depends.get_module_constant on Python 3.6. Ref #866. --- setuptools/tests/mod_with_constant.py | 1 + setuptools/tests/test_depends.py | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 setuptools/tests/mod_with_constant.py create mode 100644 setuptools/tests/test_depends.py (limited to 'setuptools') diff --git a/setuptools/tests/mod_with_constant.py b/setuptools/tests/mod_with_constant.py new file mode 100644 index 00000000..ef755dd1 --- /dev/null +++ b/setuptools/tests/mod_with_constant.py @@ -0,0 +1 @@ +value = 'three, sir!' diff --git a/setuptools/tests/test_depends.py b/setuptools/tests/test_depends.py new file mode 100644 index 00000000..e0cfa880 --- /dev/null +++ b/setuptools/tests/test_depends.py @@ -0,0 +1,16 @@ +import sys + +from setuptools import depends + + +class TestGetModuleConstant: + + def test_basic(self): + """ + Invoke get_module_constant on a module in + the test package. + """ + mod_name = 'setuptools.tests.mod_with_constant' + val = depends.get_module_constant(mod_name, 'value') + assert val == 'three, sir!' + assert 'setuptools.tests.mod_with_constant' not in sys.modules -- cgit v1.2.1 From b911a237e03a3892f423f3d5e2ec0caad1986b8d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 28 Dec 2016 17:19:50 -0500 Subject: Add two more tests for _iter_code per #866, capturing the apparent expectation in the byte-code processing that's now failing on Python 3.6. --- setuptools/tests/test_depends.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'setuptools') diff --git a/setuptools/tests/test_depends.py b/setuptools/tests/test_depends.py index e0cfa880..fcf2a636 100644 --- a/setuptools/tests/test_depends.py +++ b/setuptools/tests/test_depends.py @@ -14,3 +14,15 @@ class TestGetModuleConstant: val = depends.get_module_constant(mod_name, 'value') assert val == 'three, sir!' assert 'setuptools.tests.mod_with_constant' not in sys.modules + + +class TestIterCode: + def test_empty(self): + code = compile('', '', mode='exec') + expected = (100, 0), (83, None) + assert tuple(depends._iter_code(code)) == expected + + def test_constant(self): + code = compile('value = "three, sir!"', '', mode='exec') + expected = (100, 0), (90, 0), (100, 1), (83, None) + assert tuple(depends._iter_code(code)) == expected -- cgit v1.2.1 From a29c9a4de0c8cf75b89582a0bf718056d58b0c10 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 28 Dec 2016 20:10:39 -0500 Subject: Use dis module rather than manually disassembling the bytecode. Fixes #866. --- setuptools/depends.py | 36 +++++------------------------------- 1 file changed, 5 insertions(+), 31 deletions(-) (limited to 'setuptools') diff --git a/setuptools/depends.py b/setuptools/depends.py index 89d39a50..c150c52b 100644 --- a/setuptools/depends.py +++ b/setuptools/depends.py @@ -1,11 +1,10 @@ import sys import imp import marshal +import dis from distutils.version import StrictVersion from imp import PKG_DIRECTORY, PY_COMPILED, PY_SOURCE, PY_FROZEN -from setuptools.extern import six - __all__ = [ 'Require', 'find_module', 'get_module_constant', 'extract_constant' ] @@ -80,35 +79,10 @@ class Require: def _iter_code(code): """Yield '(op,arg)' pair for each operation in code object 'code'""" - - from array import array - from dis import HAVE_ARGUMENT, EXTENDED_ARG - - bytes = array('b', code.co_code) - eof = len(code.co_code) - - ptr = 0 - extended_arg = 0 - - while ptr < eof: - - op = bytes[ptr] - - if op >= HAVE_ARGUMENT: - - arg = bytes[ptr + 1] + bytes[ptr + 2] * 256 + extended_arg - ptr += 3 - - if op == EXTENDED_ARG: - long_type = six.integer_types[-1] - extended_arg = arg * long_type(65536) - continue - - else: - arg = None - ptr += 1 - - yield op, arg + return ( + (op.opcode, op.arg) + for op in dis.Bytecode(code) + ) def find_module(module, paths=None): -- cgit v1.2.1 From 3000d975dbb11430be5c79498a461d33a5522a40 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 28 Dec 2016 20:52:09 -0500 Subject: Re-introduce _iter_code functionality as a Bytecode backport. Fixes failing tests. Ref #866. --- setuptools/depends.py | 5 +++-- setuptools/py33compat.py | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 setuptools/py33compat.py (limited to 'setuptools') diff --git a/setuptools/depends.py b/setuptools/depends.py index d417fa32..45e7052d 100644 --- a/setuptools/depends.py +++ b/setuptools/depends.py @@ -1,10 +1,11 @@ import sys import imp import marshal -import dis from distutils.version import StrictVersion from imp import PKG_DIRECTORY, PY_COMPILED, PY_SOURCE, PY_FROZEN +from .py33compat import Bytecode + __all__ = [ 'Require', 'find_module', 'get_module_constant', 'extract_constant' @@ -155,7 +156,7 @@ def extract_constant(code, symbol, default=-1): const = default - for byte_code in dis.Bytecode(code): + for byte_code in Bytecode(code): op = byte_code.opcode arg = byte_code.arg diff --git a/setuptools/py33compat.py b/setuptools/py33compat.py new file mode 100644 index 00000000..2588d680 --- /dev/null +++ b/setuptools/py33compat.py @@ -0,0 +1,46 @@ +import dis +import code +import array +import collections + +from setuptools.extern import six + + +OpArg = collections.namedtuple('OpArg', 'opcode arg') + + +class Bytecode_compat(object): + def __init__(self, code): + self.code = code + + def __iter__(self): + """Yield '(op,arg)' pair for each operation in code object 'code'""" + + bytes = array.array('b', self.code.co_code) + eof = len(self.code.co_code) + + ptr = 0 + extended_arg = 0 + + while ptr < eof: + + op = bytes[ptr] + + if op >= dis.HAVE_ARGUMENT: + + arg = bytes[ptr + 1] + bytes[ptr + 2] * 256 + extended_arg + ptr += 3 + + if op == dis.EXTENDED_ARG: + long_type = six.integer_types[-1] + extended_arg = arg * long_type(65536) + continue + + else: + arg = None + ptr += 1 + + yield OpArg(op, arg) + + +Bytecode = getattr(dis, 'Bytecode', Bytecode_compat) -- cgit v1.2.1 From 249f870f2c6fb60734e9db34e44f7e44cd35c914 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 1 Jan 2017 10:08:00 -0500 Subject: Drop support for 'tag_svn_version' distribution option. Fixes #619. --- setuptools/command/egg_info.py | 19 ------------------- setuptools/tests/test_egg_info.py | 7 ++----- 2 files changed, 2 insertions(+), 24 deletions(-) (limited to 'setuptools') diff --git a/setuptools/command/egg_info.py b/setuptools/command/egg_info.py index 6f8fd874..98a87300 100755 --- a/setuptools/command/egg_info.py +++ b/setuptools/command/egg_info.py @@ -32,11 +32,6 @@ from setuptools.glob import glob from pkg_resources.extern import packaging -try: - from setuptools_svn import svn_utils -except ImportError: - pass - def translate_pattern(glob): """ @@ -147,7 +142,6 @@ class egg_info(Command): self.egg_base = None self.egg_info = None self.tag_build = None - self.tag_svn_revision = 0 self.tag_date = 0 self.broken_egg_info = False self.vtags = None @@ -165,7 +159,6 @@ class egg_info(Command): # when PYTHONHASHSEED=0 egg_info['tag_build'] = self.tags() egg_info['tag_date'] = 0 - egg_info['tag_svn_revision'] = 0 edit_config(filename, dict(egg_info=egg_info)) def finalize_options(self): @@ -282,22 +275,10 @@ class egg_info(Command): version = '' if self.tag_build: version += self.tag_build - if self.tag_svn_revision: - warnings.warn( - "tag_svn_revision is deprecated and will not be honored " - "in a future release" - ) - version += '-r%s' % self.get_svn_revision() if self.tag_date: version += time.strftime("-%Y%m%d") return version - @staticmethod - def get_svn_revision(): - if 'svn_utils' not in globals(): - return "0" - return str(svn_utils.SvnInfo.load(os.curdir).get_revision()) - def find_sources(self): """Generate SOURCES.txt manifest file""" manifest_filename = os.path.join(self.egg_info, "SOURCES.txt") diff --git a/setuptools/tests/test_egg_info.py b/setuptools/tests/test_egg_info.py index 75ae18df..a32b981d 100644 --- a/setuptools/tests/test_egg_info.py +++ b/setuptools/tests/test_egg_info.py @@ -88,9 +88,8 @@ class TestEggInfo(object): assert '[egg_info]' in content assert 'tag_build =' in content assert 'tag_date = 0' in content - assert 'tag_svn_revision = 0' in content - expected_order = 'tag_build', 'tag_date', 'tag_svn_revision' + expected_order = 'tag_build', 'tag_date', self._validate_content_order(content, expected_order) @@ -117,7 +116,6 @@ class TestEggInfo(object): [egg_info] tag_build = tag_date = 0 - tag_svn_revision = 0 """), }) dist = Distribution() @@ -131,9 +129,8 @@ class TestEggInfo(object): assert '[egg_info]' in content assert 'tag_build =' in content assert 'tag_date = 0' in content - assert 'tag_svn_revision = 0' in content - expected_order = 'tag_build', 'tag_date', 'tag_svn_revision' + expected_order = 'tag_build', 'tag_date', self._validate_content_order(content, expected_order) -- cgit v1.2.1 From ff371f18f0076bc63da05334f7e551c1cc29e10d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 1 Jan 2017 22:34:28 -0500 Subject: Strip out vendored packages and require them instead. Ref #581. --- setuptools/__init__.py | 2 +- setuptools/command/alias.py | 2 +- setuptools/command/bdist_egg.py | 2 +- setuptools/command/build_ext.py | 2 +- setuptools/command/build_py.py | 4 ++-- setuptools/command/develop.py | 2 +- setuptools/command/easy_install.py | 4 ++-- setuptools/command/egg_info.py | 6 +++--- setuptools/command/py36compat.py | 2 +- setuptools/command/rotate.py | 2 +- setuptools/command/sdist.py | 2 +- setuptools/command/setopt.py | 2 +- setuptools/command/test.py | 4 ++-- setuptools/command/upload_docs.py | 4 ++-- setuptools/config.py | 2 +- setuptools/dist.py | 6 +++--- setuptools/extension.py | 2 +- setuptools/extern/__init__.py | 4 ---- setuptools/glob.py | 2 +- setuptools/monkey.py | 2 +- setuptools/msvc.py | 6 +++--- setuptools/namespaces.py | 2 +- setuptools/package_index.py | 4 ++-- setuptools/py33compat.py | 2 +- setuptools/sandbox.py | 4 ++-- setuptools/ssl_support.py | 2 +- setuptools/tests/__init__.py | 2 +- setuptools/tests/contexts.py | 2 +- setuptools/tests/server.py | 2 +- setuptools/tests/test_archive_util.py | 2 +- setuptools/tests/test_build_ext.py | 2 +- setuptools/tests/test_develop.py | 2 +- setuptools/tests/test_dist_info.py | 2 +- setuptools/tests/test_easy_install.py | 2 +- setuptools/tests/test_egg_info.py | 2 +- setuptools/tests/test_integration.py | 2 +- setuptools/tests/test_manifest.py | 2 +- setuptools/tests/test_packageindex.py | 4 ++-- setuptools/tests/test_sdist.py | 4 ++-- setuptools/unicode_utils.py | 2 +- 40 files changed, 53 insertions(+), 57 deletions(-) delete mode 100644 setuptools/extern/__init__.py (limited to 'setuptools') diff --git a/setuptools/__init__.py b/setuptools/__init__.py index 54577ced..c60e1eab 100644 --- a/setuptools/__init__.py +++ b/setuptools/__init__.py @@ -7,7 +7,7 @@ import distutils.filelist from distutils.util import convert_path from fnmatch import fnmatchcase -from setuptools.extern.six.moves import filter, filterfalse, map +from six.moves import filter, filterfalse, map import setuptools.version from setuptools.extension import Extension diff --git a/setuptools/command/alias.py b/setuptools/command/alias.py index 4532b1cc..35ece78d 100755 --- a/setuptools/command/alias.py +++ b/setuptools/command/alias.py @@ -1,6 +1,6 @@ from distutils.errors import DistutilsOptionError -from setuptools.extern.six.moves import map +from 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 8cd9dfef..ae344cd0 100644 --- a/setuptools/command/bdist_egg.py +++ b/setuptools/command/bdist_egg.py @@ -11,7 +11,7 @@ import os import textwrap import marshal -from setuptools.extern import six +import six from pkg_resources import get_build_platform, Distribution, ensure_directory from pkg_resources import EntryPoint diff --git a/setuptools/command/build_ext.py b/setuptools/command/build_ext.py index 36f53f0d..c2fd8704 100644 --- a/setuptools/command/build_ext.py +++ b/setuptools/command/build_ext.py @@ -10,7 +10,7 @@ from distutils.errors import DistutilsError from distutils import log from setuptools.extension import Library -from setuptools.extern import six +import six try: # Attempt to use Cython for building extensions, if available diff --git a/setuptools/command/build_py.py b/setuptools/command/build_py.py index b0314fd4..56daa2bd 100644 --- a/setuptools/command/build_py.py +++ b/setuptools/command/build_py.py @@ -8,8 +8,8 @@ import io import distutils.errors import itertools -from setuptools.extern import six -from setuptools.extern.six.moves import map, filter, filterfalse +import six +from six.moves import map, filter, filterfalse try: from setuptools.lib2to3_ex import Mixin2to3 diff --git a/setuptools/command/develop.py b/setuptools/command/develop.py index aa82f959..1489de9e 100755 --- a/setuptools/command/develop.py +++ b/setuptools/command/develop.py @@ -5,7 +5,7 @@ import os import glob import io -from setuptools.extern import six +import six from pkg_resources import Distribution, PathMetadata, normalize_path from setuptools.command.easy_install import easy_install diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py index 36e7f359..d3eabfc3 100755 --- a/setuptools/command/easy_install.py +++ b/setuptools/command/easy_install.py @@ -40,8 +40,8 @@ import subprocess import shlex import io -from setuptools.extern import six -from setuptools.extern.six.moves import configparser, map +import six +from six.moves import configparser, map from setuptools import Command from setuptools.sandbox import run_setup diff --git a/setuptools/command/egg_info.py b/setuptools/command/egg_info.py index 98a87300..2bc57b18 100755 --- a/setuptools/command/egg_info.py +++ b/setuptools/command/egg_info.py @@ -16,8 +16,8 @@ import warnings import time import collections -from setuptools.extern import six -from setuptools.extern.six.moves import map +import six +from six.moves import map from setuptools import Command from setuptools.command.sdist import sdist @@ -30,7 +30,7 @@ from pkg_resources import ( import setuptools.unicode_utils as unicode_utils from setuptools.glob import glob -from pkg_resources.extern import packaging +import packaging def translate_pattern(glob): diff --git a/setuptools/command/py36compat.py b/setuptools/command/py36compat.py index 61063e75..a2c74b2d 100644 --- a/setuptools/command/py36compat.py +++ b/setuptools/command/py36compat.py @@ -3,7 +3,7 @@ from glob import glob from distutils.util import convert_path from distutils.command import sdist -from setuptools.extern.six.moves import filter +from six.moves import filter class sdist_add_defaults: diff --git a/setuptools/command/rotate.py b/setuptools/command/rotate.py index b89353f5..7ea36e96 100755 --- a/setuptools/command/rotate.py +++ b/setuptools/command/rotate.py @@ -4,7 +4,7 @@ from distutils.errors import DistutilsOptionError import os import shutil -from setuptools.extern import six +import six from setuptools import Command diff --git a/setuptools/command/sdist.py b/setuptools/command/sdist.py index 84e29a1b..2c2d88af 100755 --- a/setuptools/command/sdist.py +++ b/setuptools/command/sdist.py @@ -5,7 +5,7 @@ import sys import io import contextlib -from setuptools.extern import six +import six from .py36compat import sdist_add_defaults diff --git a/setuptools/command/setopt.py b/setuptools/command/setopt.py index 7e57cc02..6f6298c4 100755 --- a/setuptools/command/setopt.py +++ b/setuptools/command/setopt.py @@ -4,7 +4,7 @@ from distutils.errors import DistutilsOptionError import distutils import os -from setuptools.extern.six.moves import configparser +from six.moves import configparser from setuptools import Command diff --git a/setuptools/command/test.py b/setuptools/command/test.py index ef0af12f..e7a386d1 100644 --- a/setuptools/command/test.py +++ b/setuptools/command/test.py @@ -7,8 +7,8 @@ from distutils.errors import DistutilsError, DistutilsOptionError from distutils import log from unittest import TestLoader -from setuptools.extern import six -from setuptools.extern.six.moves import map, filter +import six +from six.moves import map, filter from pkg_resources import (resource_listdir, resource_exists, normalize_path, working_set, _namespace_packages, diff --git a/setuptools/command/upload_docs.py b/setuptools/command/upload_docs.py index 269dc2d5..eeb0718b 100644 --- a/setuptools/command/upload_docs.py +++ b/setuptools/command/upload_docs.py @@ -16,8 +16,8 @@ import shutil import itertools import functools -from setuptools.extern import six -from setuptools.extern.six.moves import http_client, urllib +import six +from six.moves import http_client, urllib from pkg_resources import iter_entry_points from .upload import upload diff --git a/setuptools/config.py b/setuptools/config.py index d71ff028..19b39629 100644 --- a/setuptools/config.py +++ b/setuptools/config.py @@ -7,7 +7,7 @@ from functools import partial from distutils.errors import DistutilsOptionError, DistutilsFileError from setuptools.py26compat import import_module -from setuptools.extern.six import string_types +from six import string_types def read_configuration( diff --git a/setuptools/dist.py b/setuptools/dist.py index 159464be..be55dc4e 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -12,9 +12,9 @@ from distutils.errors import (DistutilsOptionError, DistutilsPlatformError, DistutilsSetupError) from distutils.util import rfc822_escape -from setuptools.extern import six -from setuptools.extern.six.moves import map -from pkg_resources.extern import packaging +import six +from six.moves import map +import packaging from setuptools.depends import Require from setuptools import windows_support diff --git a/setuptools/extension.py b/setuptools/extension.py index 29468894..34a36dfb 100644 --- a/setuptools/extension.py +++ b/setuptools/extension.py @@ -4,7 +4,7 @@ import distutils.core import distutils.errors import distutils.extension -from setuptools.extern.six.moves import map +from six.moves import map from .monkey import get_unpatched diff --git a/setuptools/extern/__init__.py b/setuptools/extern/__init__.py deleted file mode 100644 index 2cd08b7e..00000000 --- a/setuptools/extern/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from pkg_resources.extern import VendorImporter - -names = 'six', -VendorImporter(__name__, names, 'pkg_resources._vendor').install() diff --git a/setuptools/glob.py b/setuptools/glob.py index 6c781de3..f2644026 100644 --- a/setuptools/glob.py +++ b/setuptools/glob.py @@ -10,7 +10,7 @@ Changes include: import os import re import fnmatch -from setuptools.extern.six import binary_type +from six import binary_type __all__ = ["glob", "iglob", "escape"] diff --git a/setuptools/monkey.py b/setuptools/monkey.py index dbe9a617..5843c46b 100644 --- a/setuptools/monkey.py +++ b/setuptools/monkey.py @@ -10,7 +10,7 @@ import functools import inspect from .py26compat import import_module -from setuptools.extern import six +import six import setuptools diff --git a/setuptools/msvc.py b/setuptools/msvc.py index 447ddb38..97e27303 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -20,14 +20,14 @@ import sys import platform import itertools import distutils.errors -from pkg_resources.extern.packaging.version import LegacyVersion +from packaging.version import LegacyVersion -from setuptools.extern.six.moves import filterfalse +from six.moves import filterfalse from .monkey import get_unpatched if platform.system() == 'Windows': - from setuptools.extern.six.moves import winreg + from six.moves import winreg safe_env = os.environ else: """ diff --git a/setuptools/namespaces.py b/setuptools/namespaces.py index 0a889f22..ba907439 100755 --- a/setuptools/namespaces.py +++ b/setuptools/namespaces.py @@ -3,7 +3,7 @@ import sys from distutils import log import itertools -from setuptools.extern.six.moves import map +from six.moves import map flatten = itertools.chain.from_iterable diff --git a/setuptools/package_index.py b/setuptools/package_index.py index d80d43bc..65c3c433 100755 --- a/setuptools/package_index.py +++ b/setuptools/package_index.py @@ -14,8 +14,8 @@ try: except ImportError: from urllib2 import splituser -from setuptools.extern import six -from setuptools.extern.six.moves import urllib, http_client, configparser, map +import six +from six.moves import urllib, http_client, configparser, map import setuptools from pkg_resources import ( diff --git a/setuptools/py33compat.py b/setuptools/py33compat.py index 2588d680..ad91d73e 100644 --- a/setuptools/py33compat.py +++ b/setuptools/py33compat.py @@ -3,7 +3,7 @@ import code import array import collections -from setuptools.extern import six +import six OpArg = collections.namedtuple('OpArg', 'opcode arg') diff --git a/setuptools/sandbox.py b/setuptools/sandbox.py index 817a3afa..0ddd2332 100755 --- a/setuptools/sandbox.py +++ b/setuptools/sandbox.py @@ -8,8 +8,8 @@ import re import contextlib import pickle -from setuptools.extern import six -from setuptools.extern.six.moves import builtins, map +import six +from six.moves import builtins, map import pkg_resources diff --git a/setuptools/ssl_support.py b/setuptools/ssl_support.py index 82f8870a..efeef71d 100644 --- a/setuptools/ssl_support.py +++ b/setuptools/ssl_support.py @@ -3,7 +3,7 @@ import socket import atexit import re -from setuptools.extern.six.moves import urllib, http_client, map +from six.moves import urllib, http_client, map import pkg_resources from pkg_resources import ResolutionError, ExtractionError diff --git a/setuptools/tests/__init__.py b/setuptools/tests/__init__.py index dbf16201..f54c478e 100644 --- a/setuptools/tests/__init__.py +++ b/setuptools/tests/__init__.py @@ -8,7 +8,7 @@ from distutils.errors import DistutilsSetupError from distutils.core import Extension from distutils.version import LooseVersion -from setuptools.extern import six +import six import pytest import setuptools.dist diff --git a/setuptools/tests/contexts.py b/setuptools/tests/contexts.py index 535ae107..77ebecf9 100644 --- a/setuptools/tests/contexts.py +++ b/setuptools/tests/contexts.py @@ -5,7 +5,7 @@ import sys import contextlib import site -from setuptools.extern import six +import six import pkg_resources diff --git a/setuptools/tests/server.py b/setuptools/tests/server.py index 35312120..5cdde217 100644 --- a/setuptools/tests/server.py +++ b/setuptools/tests/server.py @@ -4,7 +4,7 @@ import time import threading -from setuptools.extern.six.moves import BaseHTTPServer, SimpleHTTPServer +from six.moves import BaseHTTPServer, SimpleHTTPServer class IndexServer(BaseHTTPServer.HTTPServer): diff --git a/setuptools/tests/test_archive_util.py b/setuptools/tests/test_archive_util.py index b789e9ac..5cdf63f2 100644 --- a/setuptools/tests/test_archive_util.py +++ b/setuptools/tests/test_archive_util.py @@ -3,7 +3,7 @@ import tarfile import io -from setuptools.extern import six +import six import pytest diff --git a/setuptools/tests/test_build_ext.py b/setuptools/tests/test_build_ext.py index 60257154..59a896d8 100644 --- a/setuptools/tests/test_build_ext.py +++ b/setuptools/tests/test_build_ext.py @@ -2,7 +2,7 @@ import sys import distutils.command.build_ext as orig from distutils.sysconfig import get_config_var -from setuptools.extern import six +import six from setuptools.command.build_ext import build_ext, get_abi3_suffix from setuptools.dist import Distribution diff --git a/setuptools/tests/test_develop.py b/setuptools/tests/test_develop.py index 5dd72aae..3b1b1e2d 100644 --- a/setuptools/tests/test_develop.py +++ b/setuptools/tests/test_develop.py @@ -9,7 +9,7 @@ import sys import io import subprocess -from setuptools.extern import six +import six from setuptools.command import test import pytest diff --git a/setuptools/tests/test_dist_info.py b/setuptools/tests/test_dist_info.py index f7e7d2bf..24c5149a 100644 --- a/setuptools/tests/test_dist_info.py +++ b/setuptools/tests/test_dist_info.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals -from setuptools.extern.six.moves import map +from six.moves import map import pytest diff --git a/setuptools/tests/test_easy_install.py b/setuptools/tests/test_easy_install.py index 1ea33b08..f5c932da 100644 --- a/setuptools/tests/test_easy_install.py +++ b/setuptools/tests/test_easy_install.py @@ -16,7 +16,7 @@ import io import zipfile import time -from setuptools.extern.six.moves import urllib +from six.moves import urllib import pytest try: diff --git a/setuptools/tests/test_egg_info.py b/setuptools/tests/test_egg_info.py index a32b981d..c9a4425a 100644 --- a/setuptools/tests/test_egg_info.py +++ b/setuptools/tests/test_egg_info.py @@ -6,7 +6,7 @@ import sys from setuptools.command.egg_info import egg_info, manifest_maker from setuptools.dist import Distribution -from setuptools.extern.six.moves import map +from six.moves import map import pytest diff --git a/setuptools/tests/test_integration.py b/setuptools/tests/test_integration.py index 78fb0627..a83d4fe8 100644 --- a/setuptools/tests/test_integration.py +++ b/setuptools/tests/test_integration.py @@ -7,7 +7,7 @@ import glob import os import sys -from setuptools.extern.six.moves import urllib +from six.moves import urllib import pytest from setuptools.command.easy_install import easy_install diff --git a/setuptools/tests/test_manifest.py b/setuptools/tests/test_manifest.py index 602c43a2..cf39346a 100644 --- a/setuptools/tests/test_manifest.py +++ b/setuptools/tests/test_manifest.py @@ -11,7 +11,7 @@ from distutils.errors import DistutilsTemplateError from setuptools.command.egg_info import FileList, egg_info, translate_pattern from setuptools.dist import Distribution -from setuptools.extern import six +import six from setuptools.tests.textwrap import DALS import pytest diff --git a/setuptools/tests/test_packageindex.py b/setuptools/tests/test_packageindex.py index f09dd78c..d68867c2 100644 --- a/setuptools/tests/test_packageindex.py +++ b/setuptools/tests/test_packageindex.py @@ -4,8 +4,8 @@ import sys import os import distutils.errors -from setuptools.extern import six -from setuptools.extern.six.moves import urllib, http_client +import six +from six.moves import urllib, http_client import pkg_resources import setuptools.package_index diff --git a/setuptools/tests/test_sdist.py b/setuptools/tests/test_sdist.py index f34068dc..38fdda24 100644 --- a/setuptools/tests/test_sdist.py +++ b/setuptools/tests/test_sdist.py @@ -9,8 +9,8 @@ import unicodedata import contextlib import io -from setuptools.extern import six -from setuptools.extern.six.moves import map +import six +from six.moves import map import pytest diff --git a/setuptools/unicode_utils.py b/setuptools/unicode_utils.py index 7c63efd2..6a84f9be 100644 --- a/setuptools/unicode_utils.py +++ b/setuptools/unicode_utils.py @@ -1,7 +1,7 @@ import unicodedata import sys -from setuptools.extern import six +import six # HFS Plus uses decomposed UTF-8 -- cgit v1.2.1 From dc8683bd8e8600680a7581fd3a2d34ba8f1fa0ab Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 2 Jan 2017 09:29:42 -0500 Subject: More aggressively remove references to 'tag_svn_revision' option in egg_info. Ref #619. --- setuptools/command/egg_info.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) (limited to 'setuptools') diff --git a/setuptools/command/egg_info.py b/setuptools/command/egg_info.py index 98a87300..e3578074 100755 --- a/setuptools/command/egg_info.py +++ b/setuptools/command/egg_info.py @@ -121,18 +121,13 @@ class egg_info(Command): user_options = [ ('egg-base=', 'e', "directory containing .egg-info directories" " (default: top of the source tree)"), - ('tag-svn-revision', 'r', - "Add subversion revision ID to version number"), ('tag-date', 'd', "Add date stamp (e.g. 20050528) to version number"), ('tag-build=', 'b', "Specify explicit tag to add to version number"), - ('no-svn-revision', 'R', - "Don't add subversion revision ID [default]"), ('no-date', 'D', "Don't include date stamp [default]"), ] - boolean_options = ['tag-date', 'tag-svn-revision'] + boolean_options = ['tag-date'] negative_opt = { - 'no-svn-revision': 'tag-svn-revision', 'no-date': 'tag-date', } @@ -148,8 +143,8 @@ class egg_info(Command): def save_version_info(self, filename): """ - Materialize the values of svn_revision and date into the - build tag. Install these keys in a deterministic order + Materialize the value of date into the + build tag. Install build keys in a deterministic order to avoid arbitrary reordering on subsequent builds. """ # python 2.6 compatibility -- cgit v1.2.1 From b848627d17328cab9bdce4fabd314c76d5f7d96e Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 2 Jan 2017 09:56:47 -0500 Subject: Add a no-op property for 'tag_svn_revision' to suppress errors when distutils attempts to detect and set these values based on settings in setup.cfg as found in sdists built by earlier versions of setuptools. Ref #619. --- setuptools/command/egg_info.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'setuptools') diff --git a/setuptools/command/egg_info.py b/setuptools/command/egg_info.py index e3578074..ca6a4348 100755 --- a/setuptools/command/egg_info.py +++ b/setuptools/command/egg_info.py @@ -141,6 +141,18 @@ class egg_info(Command): self.broken_egg_info = False self.vtags = None + #################################### + # allow the 'tag_svn_revision' to be detected and + # set, supporting sdists built on older Setuptools. + @property + def tag_svn_revision(self): + pass + + @tag_svn_revision.setter + def tag_svn_revision(self, value): + pass + #################################### + def save_version_info(self, filename): """ Materialize the value of date into the -- cgit v1.2.1 From e9f0e6f16b46d084bdf92606cd0217b3f12ed25a Mon Sep 17 00:00:00 2001 From: David Szotten Date: Thu, 5 Jan 2017 11:47:09 +0000 Subject: strip trailing slash from package_dir before counting slashes --- setuptools/command/develop.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'setuptools') diff --git a/setuptools/command/develop.py b/setuptools/command/develop.py index aa82f959..ca05d1e3 100755 --- a/setuptools/command/develop.py +++ b/setuptools/command/develop.py @@ -79,7 +79,7 @@ class develop(namespaces.DevelopInstaller, easy_install): project_name=ei.egg_name ) - p = self.egg_base.replace(os.sep, '/') + p = self.egg_base.replace(os.sep, '/').rstrip('/') if p != os.curdir: p = '../' * (p.count('/') + 1) self.setup_path = p -- cgit v1.2.1 From bad51fc70140efc0d9fdf5de632ca4798b995752 Mon Sep 17 00:00:00 2001 From: Ilya Kulakov Date: Fri, 13 Jan 2017 18:20:28 -0800 Subject: Fix certifi fallback is not used on Windows. --- setuptools/ssl_support.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) (limited to 'setuptools') diff --git a/setuptools/ssl_support.py b/setuptools/ssl_support.py index 82f8870a..661b6b52 100644 --- a/setuptools/ssl_support.py +++ b/setuptools/ssl_support.py @@ -237,14 +237,21 @@ def get_win_certfile(): def find_ca_bundle(): """Return an existing CA bundle path, or None""" + ca_bundle_path = None + if os.name == 'nt': - return get_win_certfile() + ca_bundle_path = get_win_certfile() else: for cert_path in cert_paths: if os.path.isfile(cert_path): - return cert_path - try: - import certifi - return certifi.where() - except (ImportError, ResolutionError, ExtractionError): - return None + ca_bundle_path = cert_path + break + + if ca_bundle_path is None: + try: + import certifi + ca_bundle_path = certifi.where() + except (ImportError, ResolutionError, ExtractionError): + pass + + return ca_bundle_path -- cgit v1.2.1 From 59d325c236500eb5cd8e88e9f13094253d689de7 Mon Sep 17 00:00:00 2001 From: Daniel Nunes Date: Sat, 14 Jan 2017 22:05:18 +0000 Subject: Added newer_pairwise_group() convenience function. --- setuptools/dep_util.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 setuptools/dep_util.py (limited to 'setuptools') diff --git a/setuptools/dep_util.py b/setuptools/dep_util.py new file mode 100644 index 00000000..2931c13e --- /dev/null +++ b/setuptools/dep_util.py @@ -0,0 +1,23 @@ +from distutils.dep_util import newer_group + +# yes, this is was almost entirely copy-pasted from +# 'newer_pairwise()', this is just another convenience +# function. +def newer_pairwise_group(sources_groups, targets): + """Walk both arguments in parallel, testing if each source group is newer + than its corresponding target. Returns a pair of lists (sources_groups, + targets) where sources is newer than target, according to the semantics + of 'newer_group()'. + """ + if len(sources_groups) != len(targets): + raise ValueError("'sources_group' and 'targets' must be the same length") + + # build a pair of lists (sources_groups, targets) where source is newer + n_sources = [] + n_targets = [] + for i in range(len(sources_groups)): + if newer_group(sources_groups[i], targets[i]): + n_sources.append(sources_groups[i]) + n_targets.append(targets[i]) + + return n_sources, n_targets -- cgit v1.2.1 From a40114a442e18cd29271bd3c37dbfcaf6a2ec817 Mon Sep 17 00:00:00 2001 From: Daniel Nunes Date: Sun, 15 Jan 2017 01:36:02 +0000 Subject: Added tests for newer_pairwise_group(). --- setuptools/tests/test_dep_util.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 setuptools/tests/test_dep_util.py (limited to 'setuptools') diff --git a/setuptools/tests/test_dep_util.py b/setuptools/tests/test_dep_util.py new file mode 100644 index 00000000..e5027c10 --- /dev/null +++ b/setuptools/tests/test_dep_util.py @@ -0,0 +1,30 @@ +from setuptools.dep_util import newer_pairwise_group +import os +import pytest + + +@pytest.fixture +def groups_target(tmpdir): + """Sets up some older sources, a target and newer sources. + Returns a 3-tuple in this order. + """ + creation_order = ['older.c', 'older.h', 'target.o', 'newer.c', 'newer.h'] + mtime = 0 + + for i in range(len(creation_order)): + creation_order[i] = os.path.join(str(tmpdir), creation_order[i]) + with open(creation_order[i], 'w'): + pass + + # make sure modification times are sequential + os.utime(creation_order[i], (mtime, mtime)) + mtime += 1 + + return creation_order[:2], creation_order[2], creation_order[3:] + + +def test_newer_pairwise_group(groups_target): + older = newer_pairwise_group([groups_target[0]], [groups_target[1]]) + newer = newer_pairwise_group([groups_target[2]], [groups_target[1]]) + assert older == ([], []) + assert newer == ([groups_target[2]], [groups_target[1]]) -- cgit v1.2.1 From 827c4c365fccad37907688b3518e7d351d3bfc5b Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 16 Jan 2017 14:20:39 -0500 Subject: Refactor find_ca_bundle to simplify branching logic. --- setuptools/ssl_support.py | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) (limited to 'setuptools') diff --git a/setuptools/ssl_support.py b/setuptools/ssl_support.py index 661b6b52..2313651f 100644 --- a/setuptools/ssl_support.py +++ b/setuptools/ssl_support.py @@ -3,7 +3,7 @@ import socket import atexit import re -from setuptools.extern.six.moves import urllib, http_client, map +from setuptools.extern.six.moves import urllib, http_client, map, filter import pkg_resources from pkg_resources import ResolutionError, ExtractionError @@ -237,21 +237,16 @@ def get_win_certfile(): def find_ca_bundle(): """Return an existing CA bundle path, or None""" - ca_bundle_path = None + extant_cert_paths = filter(os.path.isfile, cert_paths) + return ( + get_win_certfile() + or next(extant_cert_paths, None) + or _certifi_where() + ) - if os.name == 'nt': - ca_bundle_path = get_win_certfile() - else: - for cert_path in cert_paths: - if os.path.isfile(cert_path): - ca_bundle_path = cert_path - break - if ca_bundle_path is None: - try: - import certifi - ca_bundle_path = certifi.where() - except (ImportError, ResolutionError, ExtractionError): - pass - - return ca_bundle_path +def _certifi_where(): + try: + return __import__('certifi').where() + except (ImportError, ResolutionError, ExtractionError): + pass -- cgit v1.2.1 From 88c77110867997d171cdee17e4b52e4011e00911 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 16 Jan 2017 14:25:32 -0500 Subject: Replace global variable serving as an implicit cache with an explicit 'once' decorator. --- setuptools/ssl_support.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'setuptools') diff --git a/setuptools/ssl_support.py b/setuptools/ssl_support.py index 2313651f..0635c43d 100644 --- a/setuptools/ssl_support.py +++ b/setuptools/ssl_support.py @@ -2,6 +2,7 @@ import os import socket import atexit import re +import functools from setuptools.extern.six.moves import urllib, http_client, map, filter @@ -204,14 +205,18 @@ def opener_for(ca_bundle=None): ).open -_wincerts = None +# from jaraco.functools +def once(func): + @functools.wraps(func) + def wrapper(*args, **kwargs): + if not hasattr(func, 'always_returns'): + func.always_returns = func(*args, **kwargs) + return func.always_returns + return wrapper +@once def get_win_certfile(): - global _wincerts - if _wincerts is not None: - return _wincerts.name - try: from wincertstore import CertFile except ImportError: -- cgit v1.2.1 From f024bd4e0ade92953f5dff85364bd12dd06e1e8f Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 16 Jan 2017 14:25:46 -0500 Subject: Remove unused import --- setuptools/ssl_support.py | 1 - 1 file changed, 1 deletion(-) (limited to 'setuptools') diff --git a/setuptools/ssl_support.py b/setuptools/ssl_support.py index 0635c43d..d827d11b 100644 --- a/setuptools/ssl_support.py +++ b/setuptools/ssl_support.py @@ -6,7 +6,6 @@ import functools from setuptools.extern.six.moves import urllib, http_client, map, filter -import pkg_resources from pkg_resources import ResolutionError, ExtractionError try: -- cgit v1.2.1 From 89cbd6c2c6355f0fa99e86394a6d86e1b624950d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 16 Jan 2017 14:29:59 -0500 Subject: Remove unused parameter --- setuptools/ssl_support.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'setuptools') diff --git a/setuptools/ssl_support.py b/setuptools/ssl_support.py index d827d11b..efd22d5f 100644 --- a/setuptools/ssl_support.py +++ b/setuptools/ssl_support.py @@ -222,11 +222,10 @@ def get_win_certfile(): return None class MyCertFile(CertFile): - def __init__(self, stores=(), certs=()): + def __init__(self, stores=()): CertFile.__init__(self) for store in stores: self.addstore(store) - self.addcerts(certs) atexit.register(self.close) def close(self): -- cgit v1.2.1 From 7b322451271218e3e8f5a207958928268c34e916 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 16 Jan 2017 14:31:41 -0500 Subject: Rely on namespacing to discriminate between novel class and parent. --- setuptools/ssl_support.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'setuptools') diff --git a/setuptools/ssl_support.py b/setuptools/ssl_support.py index efd22d5f..af28ca2f 100644 --- a/setuptools/ssl_support.py +++ b/setuptools/ssl_support.py @@ -217,24 +217,24 @@ def once(func): @once def get_win_certfile(): try: - from wincertstore import CertFile + import wincertstore except ImportError: return None - class MyCertFile(CertFile): + class CertFile(wincertstore.CertFile): def __init__(self, stores=()): - CertFile.__init__(self) + super(CertFile, self).__init__() for store in stores: self.addstore(store) atexit.register(self.close) def close(self): try: - super(MyCertFile, self).close() + super(CertFile, self).close() except OSError: pass - _wincerts = MyCertFile(stores=['CA', 'ROOT']) + _wincerts = CertFile(stores=['CA', 'ROOT']) return _wincerts.name -- cgit v1.2.1 From e316a702071feecc059102e032439e8ca0551c1a Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 16 Jan 2017 14:35:33 -0500 Subject: Simply invoke addstore twice, rather than looping. --- setuptools/ssl_support.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'setuptools') diff --git a/setuptools/ssl_support.py b/setuptools/ssl_support.py index af28ca2f..72b18ef2 100644 --- a/setuptools/ssl_support.py +++ b/setuptools/ssl_support.py @@ -222,10 +222,8 @@ def get_win_certfile(): return None class CertFile(wincertstore.CertFile): - def __init__(self, stores=()): + def __init__(self): super(CertFile, self).__init__() - for store in stores: - self.addstore(store) atexit.register(self.close) def close(self): @@ -234,7 +232,9 @@ def get_win_certfile(): except OSError: pass - _wincerts = CertFile(stores=['CA', 'ROOT']) + _wincerts = CertFile() + _wincerts.addstore('CA') + _wincerts.addstore('ROOT') return _wincerts.name -- cgit v1.2.1 From b9ca305f48b5da7cfe0f831fdc32dc0aa3890035 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 16 Jan 2017 14:37:02 -0500 Subject: Remove unused imports --- setuptools/__init__.py | 2 +- setuptools/namespaces.py | 1 - setuptools/package_index.py | 3 +-- setuptools/py33compat.py | 1 - 4 files changed, 2 insertions(+), 5 deletions(-) (limited to 'setuptools') diff --git a/setuptools/__init__.py b/setuptools/__init__.py index 54577ced..04f76740 100644 --- a/setuptools/__init__.py +++ b/setuptools/__init__.py @@ -7,7 +7,7 @@ import distutils.filelist from distutils.util import convert_path from fnmatch import fnmatchcase -from setuptools.extern.six.moves import filter, filterfalse, map +from setuptools.extern.six.moves import filter, map import setuptools.version from setuptools.extension import Extension diff --git a/setuptools/namespaces.py b/setuptools/namespaces.py index 0a889f22..bd17d9e7 100755 --- a/setuptools/namespaces.py +++ b/setuptools/namespaces.py @@ -1,5 +1,4 @@ import os -import sys from distutils import log import itertools diff --git a/setuptools/package_index.py b/setuptools/package_index.py index d80d43bc..5c25c9d6 100755 --- a/setuptools/package_index.py +++ b/setuptools/package_index.py @@ -17,10 +17,9 @@ except ImportError: from setuptools.extern import six from setuptools.extern.six.moves import urllib, http_client, configparser, map -import setuptools from pkg_resources import ( CHECKOUT_DIST, Distribution, BINARY_DIST, normalize_path, SOURCE_DIST, - require, Environment, find_distributions, safe_name, safe_version, + Environment, find_distributions, safe_name, safe_version, to_filename, Requirement, DEVELOP_DIST, ) from setuptools import ssl_support diff --git a/setuptools/py33compat.py b/setuptools/py33compat.py index 2588d680..af64d5d1 100644 --- a/setuptools/py33compat.py +++ b/setuptools/py33compat.py @@ -1,5 +1,4 @@ import dis -import code import array import collections -- cgit v1.2.1 From 552ea0d4a08ad24e06c2aa5b1746e669be1c48f5 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 16 Jan 2017 14:50:30 -0500 Subject: Restore setuptools import, falsely identified as an unused import by linter. --- setuptools/package_index.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'setuptools') diff --git a/setuptools/package_index.py b/setuptools/package_index.py index 5c25c9d6..45fac01a 100755 --- a/setuptools/package_index.py +++ b/setuptools/package_index.py @@ -17,6 +17,7 @@ except ImportError: from setuptools.extern import six from setuptools.extern.six.moves import urllib, http_client, configparser, map +import setuptools from pkg_resources import ( CHECKOUT_DIST, Distribution, BINARY_DIST, normalize_path, SOURCE_DIST, Environment, find_distributions, safe_name, safe_version, @@ -47,7 +48,7 @@ __all__ = [ _SOCKET_TIMEOUT = 15 _tmpl = "setuptools/{setuptools.__version__} Python-urllib/{py_major}" -user_agent = _tmpl.format(py_major=sys.version[:3], **globals()) +user_agent = _tmpl.format(py_major=sys.version[:3], setuptools=setuptools) def parse_requirement_arg(spec): -- cgit v1.2.1 From 9f440c3f7461e5c097705ca0823b316c0c11e2f9 Mon Sep 17 00:00:00 2001 From: Daniel Nunes Date: Thu, 19 Jan 2017 22:01:33 +0000 Subject: Added build_clib module and unit tests. Added rudimentary dependency system for build_libraries. Added obj_deps and cflags keys to build_info dictionary. --- setuptools/command/__init__.py | 2 +- setuptools/command/build_clib.py | 98 +++++++++++++++++++++++++++++++++++++ setuptools/tests/test_build_clib.py | 59 ++++++++++++++++++++++ 3 files changed, 158 insertions(+), 1 deletion(-) create mode 100644 setuptools/command/build_clib.py create mode 100644 setuptools/tests/test_build_clib.py (limited to 'setuptools') diff --git a/setuptools/command/__init__.py b/setuptools/command/__init__.py index efbe9411..c96d33c2 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', 'upload', + 'register', 'bdist_wininst', 'upload_docs', 'upload', 'build_clib', ] from distutils.command.bdist import bdist diff --git a/setuptools/command/build_clib.py b/setuptools/command/build_clib.py new file mode 100644 index 00000000..09caff6f --- /dev/null +++ b/setuptools/command/build_clib.py @@ -0,0 +1,98 @@ +import distutils.command.build_clib as orig +from distutils.errors import DistutilsSetupError +from distutils import log +from setuptools.dep_util import newer_pairwise_group + + +class build_clib(orig.build_clib): + """ + Override the default build_clib behaviour to do the following: + + 1. Implement a rudimentary timestamp-based dependency system + so 'compile()' doesn't run every time. + 2. Add more keys to the 'build_info' dictionary: + * obj_deps - specify dependencies for each object compiled. + this should be a dictionary mapping a key + with the source filename to a list of + dependencies. Use an empty string for global + dependencies. + * cflags - specify a list of additional flags to pass to + the compiler. + """ + + def build_libraries(self, libraries): + for (lib_name, build_info) in libraries: + sources = build_info.get('sources') + if sources is None or not isinstance(sources, (list, tuple)): + raise DistutilsSetupError( + "in 'libraries' option (library '%s'), " + "'sources' must be present and must be " + "a list of source filenames" % lib_name) + sources = list(sources) + + log.info("building '%s' library", lib_name) + + # Make sure everything is the correct type. + # obj_deps should be a dictionary of keys as sources + # and a list/tuple of files that are its dependencies. + obj_deps = build_info.get('obj_deps', dict()) + if not isinstance(obj_deps, dict): + raise DistutilsSetupError( + "in 'libraries' option (library '%s'), " + "'obj_deps' must be a dictionary of " + "type 'source: list'" % lib_name) + dependencies = [] + + # Get the global dependencies that are specified by the '' key. + # These will go into every source's dependency list. + global_deps = obj_deps.get('', list()) + if not isinstance(global_deps, (list, tuple)): + raise DistutilsSetupError( + "in 'libraries' option (library '%s'), " + "'obj_deps' must be a dictionary of " + "type 'source: list'" % lib_name) + + # Build the list to be used by newer_pairwise_group + # each source will be auto-added to its dependencies. + for source in sources: + src_deps = [source] + src_deps.extend(global_deps) + extra_deps = obj_deps.get(source, list()) + if not isinstance(extra_deps, (list, tuple)): + raise DistutilsSetupError( + "in 'libraries' option (library '%s'), " + "'obj_deps' must be a dictionary of " + "type 'source: list'" % lib_name) + src_deps.extend(extra_deps) + dependencies.append(src_deps) + + expected_objects = self.compiler.object_filenames( + sources, + output_dir=self.build_temp + ) + + if newer_pairwise_group(dependencies, expected_objects) != ([], []): + # First, compile the source code to object files in the library + # directory. (This should probably change to putting object + # files in a temporary build directory.) + macros = build_info.get('macros') + include_dirs = build_info.get('include_dirs') + cflags = build_info.get('cflags') + objects = self.compiler.compile( + sources, + output_dir=self.build_temp, + macros=macros, + include_dirs=include_dirs, + extra_postargs=cflags, + debug=self.debug + ) + + # Now "link" the object files together into a static library. + # (On Unix at least, this isn't really linking -- it just + # builds an archive. Whatever.) + self.compiler.create_static_lib( + expected_objects, + lib_name, + output_dir=self.build_clib, + debug=self.debug + ) diff --git a/setuptools/tests/test_build_clib.py b/setuptools/tests/test_build_clib.py new file mode 100644 index 00000000..7e3d1de9 --- /dev/null +++ b/setuptools/tests/test_build_clib.py @@ -0,0 +1,59 @@ +import pytest +import os +import shutil + +from unittest import mock +from distutils.errors import DistutilsSetupError +from setuptools.command.build_clib import build_clib +from setuptools.dist import Distribution + + +class TestBuildCLib: + @mock.patch( + 'setuptools.command.build_clib.newer_pairwise_group' + ) + def test_build_libraries(self, mock_newer): + dist = Distribution() + cmd = build_clib(dist) + + # this will be a long section, just making sure all + # exceptions are properly raised + libs = [('example', {'sources': 'broken.c'})] + with pytest.raises(DistutilsSetupError): + cmd.build_libraries(libs) + + obj_deps = 'some_string' + libs = [('example', {'sources': ['source.c'], 'obj_deps': obj_deps})] + with pytest.raises(DistutilsSetupError): + cmd.build_libraries(libs) + + obj_deps = {'': ''} + libs = [('example', {'sources': ['source.c'], 'obj_deps': obj_deps})] + with pytest.raises(DistutilsSetupError): + cmd.build_libraries(libs) + + obj_deps = {'source.c': ''} + libs = [('example', {'sources': ['source.c'], 'obj_deps': obj_deps})] + with pytest.raises(DistutilsSetupError): + cmd.build_libraries(libs) + + # with that out of the way, let's see if the crude dependency + # system works + cmd.compiler = mock.MagicMock(spec=cmd.compiler) + mock_newer.return_value = ([],[]) + + obj_deps = {'': ('global.h',), 'example.c': ('example.h',)} + libs = [('example', {'sources': ['example.c'] ,'obj_deps': obj_deps})] + + cmd.build_libraries(libs) + assert [['example.c', 'global.h', 'example.h']] in mock_newer.call_args[0] + assert not cmd.compiler.compile.called + assert cmd.compiler.create_static_lib.call_count == 1 + + # reset the call numbers so we can test again + cmd.compiler.reset_mock() + + mock_newer.return_value = '' # anything as long as it's not ([],[]) + cmd.build_libraries(libs) + assert cmd.compiler.compile.call_count == 1 + assert cmd.compiler.create_static_lib.call_count == 1 -- cgit v1.2.1 From 56274b32724933cd2016488c4e667e86d30572ef Mon Sep 17 00:00:00 2001 From: Hatem Nassrat Date: Mon, 23 Jan 2017 18:07:21 +0000 Subject: fixes #935 - allows for glob syntax in graft --- setuptools/command/egg_info.py | 4 +++- setuptools/tests/test_manifest.py | 9 +++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) (limited to 'setuptools') diff --git a/setuptools/command/egg_info.py b/setuptools/command/egg_info.py index 5ab54dc7..62bf00aa 100755 --- a/setuptools/command/egg_info.py +++ b/setuptools/command/egg_info.py @@ -429,7 +429,9 @@ class FileList(_FileList): def graft(self, dir): """Include all files from 'dir/'.""" - found = distutils.filelist.findall(dir) + found = [] + for match_dir in glob(dir): + found += distutils.filelist.findall(match_dir) self.extend(found) return bool(found) diff --git a/setuptools/tests/test_manifest.py b/setuptools/tests/test_manifest.py index cf39346a..3b34c888 100644 --- a/setuptools/tests/test_manifest.py +++ b/setuptools/tests/test_manifest.py @@ -206,6 +206,15 @@ class TestManifestTest(TempDirTestCase): l('app/static/app.css'), l('app/static/app.css.map')]) assert files == self.get_files() + def test_graft_glob_syntax(self): + """Include the whole app/static/ directory.""" + l = make_local_path + self.make_manifest("graft */static") + files = default_files | set([ + l('app/static/app.js'), l('app/static/app.js.map'), + l('app/static/app.css'), l('app/static/app.css.map')]) + assert files == self.get_files() + def test_graft_global_exclude(self): """Exclude all *.map files in the project.""" l = make_local_path -- cgit v1.2.1 From 9cb83c3711d737fa3bff56f55e4def8267bae83c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 23 Jan 2017 17:01:33 -0500 Subject: Prefer list comprehension to init/append loop. Ref #936. --- setuptools/command/egg_info.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'setuptools') diff --git a/setuptools/command/egg_info.py b/setuptools/command/egg_info.py index 62bf00aa..1a6ea9cb 100755 --- a/setuptools/command/egg_info.py +++ b/setuptools/command/egg_info.py @@ -429,9 +429,11 @@ class FileList(_FileList): def graft(self, dir): """Include all files from 'dir/'.""" - found = [] - for match_dir in glob(dir): - found += distutils.filelist.findall(match_dir) + found = [ + item + for match_dir in glob(dir) + for item in distutils.filelist.findall(match_dir) + ] self.extend(found) return bool(found) -- cgit v1.2.1 From 322472e4ffdc03901be1f584a548b00f1372037d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 24 Jan 2017 10:13:24 -0500 Subject: Extract staticmethod for resolving setup path --- setuptools/command/develop.py | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) (limited to 'setuptools') diff --git a/setuptools/command/develop.py b/setuptools/command/develop.py index 1489de9e..97708ba3 100755 --- a/setuptools/command/develop.py +++ b/setuptools/command/develop.py @@ -79,15 +79,28 @@ class develop(namespaces.DevelopInstaller, easy_install): project_name=ei.egg_name ) - p = self.egg_base.replace(os.sep, '/') - if p != os.curdir: - p = '../' * (p.count('/') + 1) - self.setup_path = p - p = normalize_path(os.path.join(self.install_dir, self.egg_path, p)) - if p != normalize_path(os.curdir): + self.setup_path = self._resolve_setup_path( + self.egg_base, + self.install_dir, + self.egg_path, + ) + + @staticmethod + def _resolve_setup_path(egg_base, install_dir, egg_path): + """ + Generate a path from egg_base back to '.' where the + setup script resides and ensure that path points to the + setup path from $install_dir/$egg_path. + """ + path_to_setup = egg_base.replace(os.sep, '/') + if path_to_setup != os.curdir: + path_to_setup = '../' * (path_to_setup.count('/') + 1) + resolved = normalize_path(os.path.join(install_dir, egg_path, path_to_setup)) + if resolved != normalize_path(os.curdir): raise DistutilsOptionError( "Can't get a consistent path to setup script from" - " installation directory", p, normalize_path(os.curdir)) + " installation directory", resolved, normalize_path(os.curdir)) + return path_to_setup def install_for_development(self): if six.PY3 and getattr(self.distribution, 'use_2to3', False): -- cgit v1.2.1 From ba15c9294488451900766898865c024dad2bf2be Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 24 Jan 2017 10:23:51 -0500 Subject: Add tests for _resolve_setup_path, including one that elicits the error reported in #913. --- setuptools/tests/test_develop.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'setuptools') diff --git a/setuptools/tests/test_develop.py b/setuptools/tests/test_develop.py index 3b1b1e2d..54e199c3 100644 --- a/setuptools/tests/test_develop.py +++ b/setuptools/tests/test_develop.py @@ -122,6 +122,22 @@ class TestDevelop: # assert '0.0' not in foocmd_text +class TestResolver: + """ + TODO: These tests were written with a minimal understanding + of what _resolve_setup_path is intending to do. Come up with + more meaningful cases that look like real-world scenarios. + """ + def test_resolve_setup_path_cwd(self): + assert develop._resolve_setup_path('.', '.', '.') == '.' + + def test_resolve_setup_path_one_dir(self): + assert develop._resolve_setup_path('pkgs', '.', 'pkgs') == '../' + + def test_resolve_setup_path_one_dir_trailing_slash(self): + assert develop._resolve_setup_path('pkgs/', '.', 'pkgs') == '../' + + class TestNamespaces: @staticmethod -- cgit v1.2.1 From 90e95f2367a2325dbf29e5a7a92ef483806ae7b9 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 30 Jan 2017 14:30:55 -0500 Subject: Rely on backports.unittest_mock plugin to make mock available on old Python versions. Ref #949. --- setuptools/tests/test_easy_install.py | 5 +---- setuptools/tests/test_msvc.py | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) (limited to 'setuptools') diff --git a/setuptools/tests/test_easy_install.py b/setuptools/tests/test_easy_install.py index f5c932da..b75e6ff2 100644 --- a/setuptools/tests/test_easy_install.py +++ b/setuptools/tests/test_easy_install.py @@ -14,15 +14,12 @@ import itertools import distutils.errors import io import zipfile +from unittest import mock import time from six.moves import urllib import pytest -try: - from unittest import mock -except ImportError: - import mock from setuptools import sandbox from setuptools.sandbox import run_setup diff --git a/setuptools/tests/test_msvc.py b/setuptools/tests/test_msvc.py index a0c76ea0..fbeed1d5 100644 --- a/setuptools/tests/test_msvc.py +++ b/setuptools/tests/test_msvc.py @@ -5,12 +5,9 @@ Tests for msvc support module. import os import contextlib import distutils.errors +from unittest import mock import pytest -try: - from unittest import mock -except ImportError: - import mock from . import contexts -- cgit v1.2.1 From ed360af83a547d565aea8c20d3086486c4c9491c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 2 Feb 2017 09:00:35 -0500 Subject: Infer the username using getpass.getuser() if no username is resolved from .pypirc. --- setuptools/command/upload.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'setuptools') diff --git a/setuptools/command/upload.py b/setuptools/command/upload.py index 484baa5a..a44173a9 100644 --- a/setuptools/command/upload.py +++ b/setuptools/command/upload.py @@ -10,6 +10,10 @@ class upload(orig.upload): def finalize_options(self): orig.upload.finalize_options(self) + self.username = ( + self.username or + getpass.getuser() + ) # Attempt to obtain password. Short circuit evaluation at the first # sign of success. self.password = ( -- cgit v1.2.1 From 43ba6abb0257dc0a51c9799bd1dea83f0fdc04e0 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 3 Feb 2017 15:13:26 -0500 Subject: Always cast to str on Linux and Python 2, let rmtree handle the rest. Fixes #953. --- setuptools/py27compat.py | 1 - 1 file changed, 1 deletion(-) (limited to 'setuptools') diff --git a/setuptools/py27compat.py b/setuptools/py27compat.py index a71a936e..f0a80a8e 100644 --- a/setuptools/py27compat.py +++ b/setuptools/py27compat.py @@ -21,7 +21,6 @@ if sys.version_info < (3,): linux_py2_ascii = ( platform.system() == 'Linux' and - sys.getfilesystemencoding() == 'ascii' and sys.version_info < (3,) ) -- cgit v1.2.1 From 50830de19302234b6741e2d1b853fbf07fbedf95 Mon Sep 17 00:00:00 2001 From: idle sign Date: Sat, 4 Feb 2017 15:21:12 +0700 Subject: Dropped support for classifiers subsection handling in setup.cfg (see #952). --- setuptools/config.py | 11 ----------- setuptools/tests/test_config.py | 13 ++++++++----- 2 files changed, 8 insertions(+), 16 deletions(-) (limited to 'setuptools') diff --git a/setuptools/config.py b/setuptools/config.py index 19b39629..39a01f88 100644 --- a/setuptools/config.py +++ b/setuptools/config.py @@ -412,17 +412,6 @@ class ConfigMetadataHandler(ConfigHandler): 'version': self._parse_version, } - def parse_section_classifiers(self, section_options): - """Parses configuration file section. - - :param dict section_options: - """ - classifiers = [] - for begin, (_, rest) in section_options.items(): - classifiers.append('%s :%s' % (begin.title(), rest)) - - self['classifiers'] = classifiers - def _parse_version(self, value): """Parses `version` option value. diff --git a/setuptools/tests/test_config.py b/setuptools/tests/test_config.py index fa8d523b..799fb165 100644 --- a/setuptools/tests/test_config.py +++ b/setuptools/tests/test_config.py @@ -257,6 +257,7 @@ class TestMetadata: def test_classifiers(self, tmpdir): expected = set([ 'Framework :: Django', + 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.5', ]) @@ -269,19 +270,21 @@ class TestMetadata: tmpdir.join('classifiers').write( 'Framework :: Django\n' + 'Programming Language :: Python :: 3\n' 'Programming Language :: Python :: 3.5\n' ) with get_dist(tmpdir) as dist: assert set(dist.metadata.classifiers) == expected - # From section. + # From list notation config.write( - '[metadata.classifiers]\n' - 'Framework :: Django\n' - 'Programming Language :: Python :: 3.5\n' + '[metadata]\n' + 'classifiers =\n' + ' Framework :: Django\n' + ' Programming Language :: Python :: 3\n' + ' Programming Language :: Python :: 3.5\n' ) - with get_dist(tmpdir) as dist: assert set(dist.metadata.classifiers) == expected -- cgit v1.2.1 From 5f235ac251a88a1704166f2e2c12903bafad8adb Mon Sep 17 00:00:00 2001 From: Moriyoshi Koizumi Date: Sat, 11 Feb 2017 12:14:09 +0900 Subject: A local version label starts with '+' sign, as per https://www.python.org/dev/peps/pep-0440/#id23 --- setuptools/package_index.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'setuptools') diff --git a/setuptools/package_index.py b/setuptools/package_index.py index 42361058..b19893e8 100755 --- a/setuptools/package_index.py +++ b/setuptools/package_index.py @@ -30,7 +30,7 @@ from fnmatch import translate from setuptools.py26compat import strip_fragment from setuptools.py27compat import get_all_headers -EGG_FRAGMENT = re.compile(r'^egg=([-A-Za-z0-9_.]+)$') +EGG_FRAGMENT = re.compile(r'^egg=([-A-Za-z0-9_.+]+)$') HREF = re.compile("""href\\s*=\\s*['"]?([^'"> ]+)""", re.I) # this is here to fix emacs' cruddy broken syntax highlighting PYPI_MD5 = re.compile( -- cgit v1.2.1 From f1ca29cc9332a1bb59e250aa280ddc5239b5457e Mon Sep 17 00:00:00 2001 From: Moriyoshi Koizumi Date: Mon, 13 Feb 2017 01:44:17 +0900 Subject: An epoch starts with a number followed by '!'. --- setuptools/package_index.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'setuptools') diff --git a/setuptools/package_index.py b/setuptools/package_index.py index b19893e8..3544dd54 100755 --- a/setuptools/package_index.py +++ b/setuptools/package_index.py @@ -30,7 +30,7 @@ from fnmatch import translate from setuptools.py26compat import strip_fragment from setuptools.py27compat import get_all_headers -EGG_FRAGMENT = re.compile(r'^egg=([-A-Za-z0-9_.+]+)$') +EGG_FRAGMENT = re.compile(r'^egg=([-A-Za-z0-9_.+!]+)$') HREF = re.compile("""href\\s*=\\s*['"]?([^'"> ]+)""", re.I) # this is here to fix emacs' cruddy broken syntax highlighting PYPI_MD5 = re.compile( -- cgit v1.2.1 From 57fa89ac2d7461995b7191aeab8a027a86d73120 Mon Sep 17 00:00:00 2001 From: Moriyoshi Koizumi Date: Mon, 13 Feb 2017 04:35:30 +0900 Subject: Add a test. --- setuptools/tests/test_packageindex.py | 42 +++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) (limited to 'setuptools') diff --git a/setuptools/tests/test_packageindex.py b/setuptools/tests/test_packageindex.py index d68867c2..1a66394f 100644 --- a/setuptools/tests/test_packageindex.py +++ b/setuptools/tests/test_packageindex.py @@ -181,6 +181,48 @@ class TestPackageIndex: res = setuptools.package_index.local_open(url) assert 'content' in res.read() + def test_egg_fragment(self): + """ + EGG fragments must comply to PEP 440 + """ + epoch = [ + '', + '1!', + ] + releases = [ + '0', + '0.0', + '0.0.0', + ] + pre = [ + 'a0', + 'b0', + 'rc0', + ] + post = [ + '.post0' + ] + dev = [ + '.dev0', + ] + local = [ + ('', ''), + ('+ubuntu.0', '+ubuntu.0'), + ('+ubuntu-0', '+ubuntu.0'), + ('+ubuntu_0', '+ubuntu.0'), + ] + versions = [ + [''.join([e, r, p, l]) for l in ll] + for e in epoch + for r in releases + for p in sum([pre, post, dev], ['']) + for ll in local] + for v, vc in versions: + dists = list(setuptools.package_index.distros_for_url( + 'http://example.com/example.zip#egg=example-' + v)) + assert dists[0].version == '' + assert dists[1].version == vc + class TestContentCheckers: def test_md5(self): -- cgit v1.2.1 From 9406d8fe002605af0f76f2de6c1e2fa1f44522fa Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 21 Feb 2017 09:53:05 -0500 Subject: Remove redundant test from -nspkg.pth. If m is non-true, then has_mfs must have been False. --- setuptools/namespaces.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'setuptools') diff --git a/setuptools/namespaces.py b/setuptools/namespaces.py index 556b5dd2..7c24a566 100755 --- a/setuptools/namespaces.py +++ b/setuptools/namespaces.py @@ -52,7 +52,7 @@ class Installer: "importlib.util.module_from_spec(" "importlib.machinery.PathFinder.find_spec(%(pkg)r, " "[os.path.dirname(p)])))", - "m = m or not has_mfs and " + "m = m or " "sys.modules.setdefault(%(pkg)r, types.ModuleType(%(pkg)r))", "mp = (m or []) and m.__dict__.setdefault('__path__',[])", "(p not in mp) and mp.append(p)", -- cgit v1.2.1 From 31cbff363a92d872f2daaaf1a24e1f9c0e47ca1f Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 23 Feb 2017 16:53:15 -0500 Subject: Update version match for issue12885. Ref #971. --- setuptools/monkey.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'setuptools') diff --git a/setuptools/monkey.py b/setuptools/monkey.py index 5843c46b..da6ecfe7 100644 --- a/setuptools/monkey.py +++ b/setuptools/monkey.py @@ -52,13 +52,7 @@ def patch_all(): # we can't patch distutils.cmd, alas distutils.core.Command = setuptools.Command - has_issue_12885 = ( - sys.version_info < (3, 4, 6) - or - (3, 5) < sys.version_info <= (3, 5, 3) - or - (3, 6) < sys.version_info - ) + has_issue_12885 = sys.version_info <= (3, 5, 3) if has_issue_12885: # fix findall bug in distutils (http://bugs.python.org/issue12885) -- cgit v1.2.1 From bb4906b775ac64e0ac4184df4f1b4900be5ddae8 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 23 Feb 2017 16:55:20 -0500 Subject: Remove incorrect open bound on 3.6 or later for warehouse patch. --- setuptools/monkey.py | 2 -- 1 file changed, 2 deletions(-) (limited to 'setuptools') diff --git a/setuptools/monkey.py b/setuptools/monkey.py index da6ecfe7..68fad9dd 100644 --- a/setuptools/monkey.py +++ b/setuptools/monkey.py @@ -66,8 +66,6 @@ def patch_all(): (3, 4) < sys.version_info < (3, 4, 6) or (3, 5) < sys.version_info <= (3, 5, 3) - or - (3, 6) < sys.version_info ) if needs_warehouse: -- cgit v1.2.1 From 3d0cc355fb5e8012cb8c72f0e25042a5a44f31d6 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 24 Feb 2017 11:49:51 -0500 Subject: Revert "Merge pull request #933 from pypa/feature/581-depend-not-bundle" This reverts commit 089cdeb489a0fa94d11b7307b54210ef9aa40511, reversing changes made to aaec654d804cb78dbb6391afff721a63f26a71cd. --- setuptools/__init__.py | 2 +- setuptools/command/alias.py | 2 +- setuptools/command/bdist_egg.py | 2 +- setuptools/command/build_ext.py | 2 +- setuptools/command/build_py.py | 4 ++-- setuptools/command/develop.py | 2 +- setuptools/command/easy_install.py | 4 ++-- setuptools/command/egg_info.py | 6 +++--- setuptools/command/py36compat.py | 2 +- setuptools/command/rotate.py | 2 +- setuptools/command/sdist.py | 2 +- setuptools/command/setopt.py | 2 +- setuptools/command/test.py | 4 ++-- setuptools/command/upload_docs.py | 4 ++-- setuptools/config.py | 2 +- setuptools/dist.py | 6 +++--- setuptools/extension.py | 2 +- setuptools/extern/__init__.py | 4 ++++ setuptools/glob.py | 2 +- setuptools/monkey.py | 2 +- setuptools/msvc.py | 6 +++--- setuptools/namespaces.py | 2 +- setuptools/package_index.py | 4 ++-- setuptools/py33compat.py | 2 +- setuptools/sandbox.py | 4 ++-- setuptools/ssl_support.py | 2 +- setuptools/tests/__init__.py | 2 +- setuptools/tests/contexts.py | 2 +- setuptools/tests/server.py | 2 +- setuptools/tests/test_archive_util.py | 2 +- setuptools/tests/test_build_ext.py | 2 +- setuptools/tests/test_develop.py | 2 +- setuptools/tests/test_dist_info.py | 2 +- setuptools/tests/test_easy_install.py | 2 +- setuptools/tests/test_egg_info.py | 2 +- setuptools/tests/test_integration.py | 2 +- setuptools/tests/test_manifest.py | 2 +- setuptools/tests/test_packageindex.py | 4 ++-- setuptools/tests/test_sdist.py | 4 ++-- setuptools/unicode_utils.py | 2 +- 40 files changed, 57 insertions(+), 53 deletions(-) create mode 100644 setuptools/extern/__init__.py (limited to 'setuptools') diff --git a/setuptools/__init__.py b/setuptools/__init__.py index d01918ed..04f76740 100644 --- a/setuptools/__init__.py +++ b/setuptools/__init__.py @@ -7,7 +7,7 @@ import distutils.filelist from distutils.util import convert_path from fnmatch import fnmatchcase -from six.moves import filter, map +from setuptools.extern.six.moves import filter, map import setuptools.version from setuptools.extension import Extension diff --git a/setuptools/command/alias.py b/setuptools/command/alias.py index 35ece78d..4532b1cc 100755 --- a/setuptools/command/alias.py +++ b/setuptools/command/alias.py @@ -1,6 +1,6 @@ from distutils.errors import DistutilsOptionError -from six.moves import map +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 ae344cd0..8cd9dfef 100644 --- a/setuptools/command/bdist_egg.py +++ b/setuptools/command/bdist_egg.py @@ -11,7 +11,7 @@ import os import textwrap import marshal -import six +from setuptools.extern import six from pkg_resources import get_build_platform, Distribution, ensure_directory from pkg_resources import EntryPoint diff --git a/setuptools/command/build_ext.py b/setuptools/command/build_ext.py index c2fd8704..36f53f0d 100644 --- a/setuptools/command/build_ext.py +++ b/setuptools/command/build_ext.py @@ -10,7 +10,7 @@ from distutils.errors import DistutilsError from distutils import log from setuptools.extension import Library -import six +from setuptools.extern import six try: # Attempt to use Cython for building extensions, if available diff --git a/setuptools/command/build_py.py b/setuptools/command/build_py.py index 56daa2bd..b0314fd4 100644 --- a/setuptools/command/build_py.py +++ b/setuptools/command/build_py.py @@ -8,8 +8,8 @@ import io import distutils.errors import itertools -import six -from six.moves import map, filter, filterfalse +from setuptools.extern import six +from setuptools.extern.six.moves import map, filter, filterfalse try: from setuptools.lib2to3_ex import Mixin2to3 diff --git a/setuptools/command/develop.py b/setuptools/command/develop.py index ddfdc662..85b23c60 100755 --- a/setuptools/command/develop.py +++ b/setuptools/command/develop.py @@ -5,7 +5,7 @@ import os import glob import io -import six +from setuptools.extern import six from pkg_resources import Distribution, PathMetadata, normalize_path from setuptools.command.easy_install import easy_install diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py index d3eabfc3..36e7f359 100755 --- a/setuptools/command/easy_install.py +++ b/setuptools/command/easy_install.py @@ -40,8 +40,8 @@ import subprocess import shlex import io -import six -from six.moves import configparser, map +from setuptools.extern import six +from setuptools.extern.six.moves import configparser, map from setuptools import Command from setuptools.sandbox import run_setup diff --git a/setuptools/command/egg_info.py b/setuptools/command/egg_info.py index 1a6ea9cb..a32c42f8 100755 --- a/setuptools/command/egg_info.py +++ b/setuptools/command/egg_info.py @@ -16,8 +16,8 @@ import warnings import time import collections -import six -from six.moves import map +from setuptools.extern import six +from setuptools.extern.six.moves import map from setuptools import Command from setuptools.command.sdist import sdist @@ -30,7 +30,7 @@ from pkg_resources import ( import setuptools.unicode_utils as unicode_utils from setuptools.glob import glob -import packaging +from pkg_resources.extern import packaging def translate_pattern(glob): diff --git a/setuptools/command/py36compat.py b/setuptools/command/py36compat.py index a2c74b2d..61063e75 100644 --- a/setuptools/command/py36compat.py +++ b/setuptools/command/py36compat.py @@ -3,7 +3,7 @@ from glob import glob from distutils.util import convert_path from distutils.command import sdist -from six.moves import filter +from setuptools.extern.six.moves import filter class sdist_add_defaults: diff --git a/setuptools/command/rotate.py b/setuptools/command/rotate.py index 7ea36e96..b89353f5 100755 --- a/setuptools/command/rotate.py +++ b/setuptools/command/rotate.py @@ -4,7 +4,7 @@ from distutils.errors import DistutilsOptionError import os import shutil -import six +from setuptools.extern import six from setuptools import Command diff --git a/setuptools/command/sdist.py b/setuptools/command/sdist.py index 2c2d88af..84e29a1b 100755 --- a/setuptools/command/sdist.py +++ b/setuptools/command/sdist.py @@ -5,7 +5,7 @@ import sys import io import contextlib -import six +from setuptools.extern import six from .py36compat import sdist_add_defaults diff --git a/setuptools/command/setopt.py b/setuptools/command/setopt.py index 6f6298c4..7e57cc02 100755 --- a/setuptools/command/setopt.py +++ b/setuptools/command/setopt.py @@ -4,7 +4,7 @@ from distutils.errors import DistutilsOptionError import distutils import os -from six.moves import configparser +from setuptools.extern.six.moves import configparser from setuptools import Command diff --git a/setuptools/command/test.py b/setuptools/command/test.py index e7a386d1..ef0af12f 100644 --- a/setuptools/command/test.py +++ b/setuptools/command/test.py @@ -7,8 +7,8 @@ from distutils.errors import DistutilsError, DistutilsOptionError from distutils import log from unittest import TestLoader -import six -from six.moves import map, filter +from setuptools.extern import six +from setuptools.extern.six.moves import map, filter from pkg_resources import (resource_listdir, resource_exists, normalize_path, working_set, _namespace_packages, diff --git a/setuptools/command/upload_docs.py b/setuptools/command/upload_docs.py index eeb0718b..269dc2d5 100644 --- a/setuptools/command/upload_docs.py +++ b/setuptools/command/upload_docs.py @@ -16,8 +16,8 @@ import shutil import itertools import functools -import six -from six.moves import http_client, urllib +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 diff --git a/setuptools/config.py b/setuptools/config.py index 39a01f88..0149316c 100644 --- a/setuptools/config.py +++ b/setuptools/config.py @@ -7,7 +7,7 @@ from functools import partial from distutils.errors import DistutilsOptionError, DistutilsFileError from setuptools.py26compat import import_module -from six import string_types +from setuptools.extern.six import string_types def read_configuration( diff --git a/setuptools/dist.py b/setuptools/dist.py index be55dc4e..159464be 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -12,9 +12,9 @@ from distutils.errors import (DistutilsOptionError, DistutilsPlatformError, DistutilsSetupError) from distutils.util import rfc822_escape -import six -from six.moves import map -import packaging +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 import windows_support diff --git a/setuptools/extension.py b/setuptools/extension.py index 34a36dfb..29468894 100644 --- a/setuptools/extension.py +++ b/setuptools/extension.py @@ -4,7 +4,7 @@ import distutils.core import distutils.errors import distutils.extension -from six.moves import map +from setuptools.extern.six.moves import map from .monkey import get_unpatched diff --git a/setuptools/extern/__init__.py b/setuptools/extern/__init__.py new file mode 100644 index 00000000..2cd08b7e --- /dev/null +++ b/setuptools/extern/__init__.py @@ -0,0 +1,4 @@ +from pkg_resources.extern import VendorImporter + +names = 'six', +VendorImporter(__name__, names, 'pkg_resources._vendor').install() diff --git a/setuptools/glob.py b/setuptools/glob.py index f2644026..6c781de3 100644 --- a/setuptools/glob.py +++ b/setuptools/glob.py @@ -10,7 +10,7 @@ Changes include: import os import re import fnmatch -from six import binary_type +from setuptools.extern.six import binary_type __all__ = ["glob", "iglob", "escape"] diff --git a/setuptools/monkey.py b/setuptools/monkey.py index 68fad9dd..94f22a56 100644 --- a/setuptools/monkey.py +++ b/setuptools/monkey.py @@ -10,7 +10,7 @@ import functools import inspect from .py26compat import import_module -import six +from setuptools.extern import six import setuptools diff --git a/setuptools/msvc.py b/setuptools/msvc.py index 97e27303..447ddb38 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -20,14 +20,14 @@ import sys import platform import itertools import distutils.errors -from packaging.version import LegacyVersion +from pkg_resources.extern.packaging.version import LegacyVersion -from six.moves import filterfalse +from setuptools.extern.six.moves import filterfalse from .monkey import get_unpatched if platform.system() == 'Windows': - from six.moves import winreg + from setuptools.extern.six.moves import winreg safe_env = os.environ else: """ diff --git a/setuptools/namespaces.py b/setuptools/namespaces.py index 7c24a566..dc16106d 100755 --- a/setuptools/namespaces.py +++ b/setuptools/namespaces.py @@ -2,7 +2,7 @@ import os from distutils import log import itertools -from six.moves import map +from setuptools.extern.six.moves import map flatten = itertools.chain.from_iterable diff --git a/setuptools/package_index.py b/setuptools/package_index.py index 3544dd54..faef5377 100755 --- a/setuptools/package_index.py +++ b/setuptools/package_index.py @@ -14,8 +14,8 @@ try: except ImportError: from urllib2 import splituser -import six -from six.moves import urllib, http_client, configparser, map +from setuptools.extern import six +from setuptools.extern.six.moves import urllib, http_client, configparser, map import setuptools from pkg_resources import ( diff --git a/setuptools/py33compat.py b/setuptools/py33compat.py index 0caa2003..af64d5d1 100644 --- a/setuptools/py33compat.py +++ b/setuptools/py33compat.py @@ -2,7 +2,7 @@ import dis import array import collections -import six +from setuptools.extern import six OpArg = collections.namedtuple('OpArg', 'opcode arg') diff --git a/setuptools/sandbox.py b/setuptools/sandbox.py index 0ddd2332..817a3afa 100755 --- a/setuptools/sandbox.py +++ b/setuptools/sandbox.py @@ -8,8 +8,8 @@ import re import contextlib import pickle -import six -from six.moves import builtins, map +from setuptools.extern import six +from setuptools.extern.six.moves import builtins, map import pkg_resources diff --git a/setuptools/ssl_support.py b/setuptools/ssl_support.py index fa5e4421..72b18ef2 100644 --- a/setuptools/ssl_support.py +++ b/setuptools/ssl_support.py @@ -4,7 +4,7 @@ import atexit import re import functools -from six.moves import urllib, http_client, map, filter +from setuptools.extern.six.moves import urllib, http_client, map, filter from pkg_resources import ResolutionError, ExtractionError diff --git a/setuptools/tests/__init__.py b/setuptools/tests/__init__.py index f54c478e..dbf16201 100644 --- a/setuptools/tests/__init__.py +++ b/setuptools/tests/__init__.py @@ -8,7 +8,7 @@ from distutils.errors import DistutilsSetupError from distutils.core import Extension from distutils.version import LooseVersion -import six +from setuptools.extern import six import pytest import setuptools.dist diff --git a/setuptools/tests/contexts.py b/setuptools/tests/contexts.py index 77ebecf9..535ae107 100644 --- a/setuptools/tests/contexts.py +++ b/setuptools/tests/contexts.py @@ -5,7 +5,7 @@ import sys import contextlib import site -import six +from setuptools.extern import six import pkg_resources diff --git a/setuptools/tests/server.py b/setuptools/tests/server.py index 5cdde217..35312120 100644 --- a/setuptools/tests/server.py +++ b/setuptools/tests/server.py @@ -4,7 +4,7 @@ import time import threading -from six.moves import BaseHTTPServer, SimpleHTTPServer +from setuptools.extern.six.moves import BaseHTTPServer, SimpleHTTPServer class IndexServer(BaseHTTPServer.HTTPServer): diff --git a/setuptools/tests/test_archive_util.py b/setuptools/tests/test_archive_util.py index 5cdf63f2..b789e9ac 100644 --- a/setuptools/tests/test_archive_util.py +++ b/setuptools/tests/test_archive_util.py @@ -3,7 +3,7 @@ import tarfile import io -import six +from setuptools.extern import six import pytest diff --git a/setuptools/tests/test_build_ext.py b/setuptools/tests/test_build_ext.py index 59a896d8..60257154 100644 --- a/setuptools/tests/test_build_ext.py +++ b/setuptools/tests/test_build_ext.py @@ -2,7 +2,7 @@ import sys import distutils.command.build_ext as orig from distutils.sysconfig import get_config_var -import six +from setuptools.extern import six from setuptools.command.build_ext import build_ext, get_abi3_suffix from setuptools.dist import Distribution diff --git a/setuptools/tests/test_develop.py b/setuptools/tests/test_develop.py index 54e199c3..ad7cfa05 100644 --- a/setuptools/tests/test_develop.py +++ b/setuptools/tests/test_develop.py @@ -9,7 +9,7 @@ import sys import io import subprocess -import six +from setuptools.extern import six from setuptools.command import test import pytest diff --git a/setuptools/tests/test_dist_info.py b/setuptools/tests/test_dist_info.py index 24c5149a..f7e7d2bf 100644 --- a/setuptools/tests/test_dist_info.py +++ b/setuptools/tests/test_dist_info.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals -from six.moves import map +from setuptools.extern.six.moves import map import pytest diff --git a/setuptools/tests/test_easy_install.py b/setuptools/tests/test_easy_install.py index b75e6ff2..52db16f6 100644 --- a/setuptools/tests/test_easy_install.py +++ b/setuptools/tests/test_easy_install.py @@ -17,7 +17,7 @@ import zipfile from unittest import mock import time -from six.moves import urllib +from setuptools.extern.six.moves import urllib import pytest diff --git a/setuptools/tests/test_egg_info.py b/setuptools/tests/test_egg_info.py index c9a4425a..a32b981d 100644 --- a/setuptools/tests/test_egg_info.py +++ b/setuptools/tests/test_egg_info.py @@ -6,7 +6,7 @@ import sys from setuptools.command.egg_info import egg_info, manifest_maker from setuptools.dist import Distribution -from six.moves import map +from setuptools.extern.six.moves import map import pytest diff --git a/setuptools/tests/test_integration.py b/setuptools/tests/test_integration.py index a83d4fe8..78fb0627 100644 --- a/setuptools/tests/test_integration.py +++ b/setuptools/tests/test_integration.py @@ -7,7 +7,7 @@ import glob import os import sys -from six.moves import urllib +from setuptools.extern.six.moves import urllib import pytest from setuptools.command.easy_install import easy_install diff --git a/setuptools/tests/test_manifest.py b/setuptools/tests/test_manifest.py index 3b34c888..f17cf6a6 100644 --- a/setuptools/tests/test_manifest.py +++ b/setuptools/tests/test_manifest.py @@ -11,7 +11,7 @@ from distutils.errors import DistutilsTemplateError from setuptools.command.egg_info import FileList, egg_info, translate_pattern from setuptools.dist import Distribution -import six +from setuptools.extern import six from setuptools.tests.textwrap import DALS import pytest diff --git a/setuptools/tests/test_packageindex.py b/setuptools/tests/test_packageindex.py index 1a66394f..53e20d44 100644 --- a/setuptools/tests/test_packageindex.py +++ b/setuptools/tests/test_packageindex.py @@ -4,8 +4,8 @@ import sys import os import distutils.errors -import six -from six.moves import urllib, http_client +from setuptools.extern import six +from setuptools.extern.six.moves import urllib, http_client import pkg_resources import setuptools.package_index diff --git a/setuptools/tests/test_sdist.py b/setuptools/tests/test_sdist.py index 38fdda24..f34068dc 100644 --- a/setuptools/tests/test_sdist.py +++ b/setuptools/tests/test_sdist.py @@ -9,8 +9,8 @@ import unicodedata import contextlib import io -import six -from six.moves import map +from setuptools.extern import six +from setuptools.extern.six.moves import map import pytest diff --git a/setuptools/unicode_utils.py b/setuptools/unicode_utils.py index 6a84f9be..7c63efd2 100644 --- a/setuptools/unicode_utils.py +++ b/setuptools/unicode_utils.py @@ -1,7 +1,7 @@ import unicodedata import sys -import six +from setuptools.extern import six # HFS Plus uses decomposed UTF-8 -- cgit v1.2.1 From e753cb42481783ac858ceb518aaac1472075063c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Skytt=C3=A4?= Date: Fri, 24 Feb 2017 10:55:44 +0200 Subject: Python 3.6 invalid escape sequence deprecation fixes --- setuptools/command/easy_install.py | 2 +- setuptools/msvc.py | 4 ++-- setuptools/package_index.py | 8 ++++---- setuptools/sandbox.py | 4 ++-- setuptools/tests/test_bdist_egg.py | 2 +- setuptools/tests/test_easy_install.py | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) (limited to 'setuptools') diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py index d3eabfc3..f5ca0754 100755 --- a/setuptools/command/easy_install.py +++ b/setuptools/command/easy_install.py @@ -2013,7 +2013,7 @@ class ScriptWriter(object): gui apps. """ - template = textwrap.dedent(""" + template = textwrap.dedent(r""" # EASY-INSTALL-ENTRY-SCRIPT: %(spec)r,%(group)r,%(name)r __requires__ = %(spec)r import re diff --git a/setuptools/msvc.py b/setuptools/msvc.py index 97e27303..d41daec4 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -272,7 +272,7 @@ class PlatformInfo: ) def target_dir(self, hidex86=False, x64=False): - """ + r""" Target platform specific subfolder. Parameters @@ -294,7 +294,7 @@ class PlatformInfo: ) def cross_dir(self, forcex86=False): - """ + r""" Cross platform specific subfolder. Parameters diff --git a/setuptools/package_index.py b/setuptools/package_index.py index 3544dd54..5d397b67 100755 --- a/setuptools/package_index.py +++ b/setuptools/package_index.py @@ -34,8 +34,8 @@ EGG_FRAGMENT = re.compile(r'^egg=([-A-Za-z0-9_.+!]+)$') HREF = re.compile("""href\\s*=\\s*['"]?([^'"> ]+)""", re.I) # this is here to fix emacs' cruddy broken syntax highlighting PYPI_MD5 = re.compile( - '([^<]+)\n\s+\\(md5\\)' + '([^<]+)\n\\s+\\(md5\\)' ) URL_SCHEME = re.compile('([-+.a-z0-9]{2,}):', re.I).match EXTENSIONS = ".tar.gz .tar.bz2 .tar .zip .tgz".split() @@ -161,7 +161,7 @@ def interpret_distro_name( # versions in distribution archive names (sdist and bdist). parts = basename.split('-') - if not py_version and any(re.match('py\d\.\d$', p) for p in parts[2:]): + if not py_version and any(re.match(r'py\d\.\d$', p) for p in parts[2:]): # it is a bdist_dumb, not an sdist -- bail out return @@ -205,7 +205,7 @@ def unique_values(func): return wrapper -REL = re.compile("""<([^>]*\srel\s*=\s*['"]?([^'">]+)[^>]*)>""", re.I) +REL = re.compile(r"""<([^>]*\srel\s*=\s*['"]?([^'">]+)[^>]*)>""", re.I) # this line is here to fix emacs' cruddy broken syntax highlighting diff --git a/setuptools/sandbox.py b/setuptools/sandbox.py index 0ddd2332..41c1c3b1 100755 --- a/setuptools/sandbox.py +++ b/setuptools/sandbox.py @@ -215,7 +215,7 @@ def _needs_hiding(mod_name): >>> _needs_hiding('Cython') True """ - pattern = re.compile('(setuptools|pkg_resources|distutils|Cython)(\.|$)') + pattern = re.compile(r'(setuptools|pkg_resources|distutils|Cython)(\.|$)') return bool(pattern.match(mod_name)) @@ -391,7 +391,7 @@ class DirectorySandbox(AbstractSandbox): _exception_patterns = [ # Allow lib2to3 to attempt to save a pickled grammar object (#121) - '.*lib2to3.*\.pickle$', + r'.*lib2to3.*\.pickle$', ] "exempt writing to paths that match the pattern" diff --git a/setuptools/tests/test_bdist_egg.py b/setuptools/tests/test_bdist_egg.py index 5aabf404..d24aa366 100644 --- a/setuptools/tests/test_bdist_egg.py +++ b/setuptools/tests/test_bdist_egg.py @@ -41,4 +41,4 @@ class Test: # let's see if we got our egg link at the right place [content] = os.listdir('dist') - assert re.match('foo-0.0.0-py[23].\d.egg$', content) + assert re.match(r'foo-0.0.0-py[23].\d.egg$', content) diff --git a/setuptools/tests/test_easy_install.py b/setuptools/tests/test_easy_install.py index b75e6ff2..fd8300a0 100644 --- a/setuptools/tests/test_easy_install.py +++ b/setuptools/tests/test_easy_install.py @@ -65,7 +65,7 @@ class TestEasyInstallTest: def test_get_script_args(self): header = ei.CommandSpec.best().from_environment().as_header() - expected = header + DALS(""" + expected = header + DALS(r""" # EASY-INSTALL-ENTRY-SCRIPT: 'spec','console_scripts','name' __requires__ = 'spec' import re -- cgit v1.2.1 From d4c215a7c61fb1f94b88bd2aa0b332ebaff18193 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 25 Feb 2017 21:16:34 -0600 Subject: Add a test capturing new proposed expectation that Setuptools' build dependencies should not require setuptools to build in order to avoid inevitable conflicts when bootstrapping from source. Packaging fails this test. Ref #980 --- setuptools/tests/test_integration.py | 50 ++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) (limited to 'setuptools') diff --git a/setuptools/tests/test_integration.py b/setuptools/tests/test_integration.py index a83d4fe8..cb62eb29 100644 --- a/setuptools/tests/test_integration.py +++ b/setuptools/tests/test_integration.py @@ -98,3 +98,53 @@ def test_pbr(install_context): def test_python_novaclient(install_context): _install_one('python-novaclient', install_context, 'novaclient', 'base.py') + +import re +import subprocess +import functools +import tarfile, zipfile + + +build_deps = ['appdirs', 'packaging', 'pyparsing', 'six'] +@pytest.mark.parametrize("build_dep", build_deps) +@pytest.mark.skipif(sys.version_info < (3, 6), reason='run only on late versions') +def test_build_deps_on_distutils(request, tmpdir_factory, build_dep): + """ + All setuptools build dependencies must build without + setuptools. + """ + if 'pyparsing' in build_dep: + pytest.xfail(reason="Project imports setuptools unconditionally") + build_target = tmpdir_factory.mktemp('source') + build_dir = download_and_extract(request, build_dep, build_target) + install_target = tmpdir_factory.mktemp('target') + output = install(build_dir, install_target) + for line in output.splitlines(): + match = re.search('Unknown distribution option: (.*)', line) + allowed_unknowns = [ + 'test_suite', + 'tests_require', + 'install_requires', + ] + assert not match or match.group(1).strip('"\'') in allowed_unknowns + + +def install(pkg_dir, install_dir): + with open(os.path.join(pkg_dir, 'setuptools.py'), 'w') as breaker: + breaker.write('raise ImportError()') + cmd = [sys.executable, 'setup.py', 'install', '--prefix', install_dir] + env = dict(os.environ, PYTHONPATH=pkg_dir) + output = subprocess.check_output(cmd, cwd=pkg_dir, env=env, stderr=subprocess.STDOUT) + return output.decode('utf-8') + + +def download_and_extract(request, req, target): + cmd = [sys.executable, '-m', 'pip', 'download', '--no-deps', + '--no-binary', ':all:', req] + output = subprocess.check_output(cmd, encoding='utf-8') + filename = re.search('Saved (.*)', output).group(1) + request.addfinalizer(functools.partial(os.remove, filename)) + opener = zipfile.ZipFile if filename.endswith('.zip') else tarfile.open + with opener(filename) as archive: + archive.extractall(target) + return os.path.join(target, os.listdir(target)[0]) -- cgit v1.2.1 From 20aca0e37e2003a364098a27189c732197ccbec2 Mon Sep 17 00:00:00 2001 From: Emil Styrke Date: Mon, 27 Feb 2017 14:15:39 +0100 Subject: Fix for auto_chmod behavior Apparently, in (at least) python 3.5.2, the function that is called on Windows to remove files is os.unlink and not os.remove. This results in permission errors when trying to clean up after easy_install has been used to install a package from a Git repository. --- setuptools/command/easy_install.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'setuptools') diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py index f5ca0754..ef83f7ae 100755 --- a/setuptools/command/easy_install.py +++ b/setuptools/command/easy_install.py @@ -1675,7 +1675,7 @@ def _first_line_re(): def auto_chmod(func, arg, exc): - if func is os.remove and os.name == 'nt': + if func in [os.unlink, os.remove] and os.name == 'nt': chmod(arg, stat.S_IWRITE) return func(arg) et, ev, _ = sys.exc_info() -- cgit v1.2.1 From 62fc6681509f04ba7ee12e87d6ac5d6056214fa8 Mon Sep 17 00:00:00 2001 From: Florian Schulze Date: Sat, 11 Mar 2017 14:45:15 +0100 Subject: Fix documentation upload by fixing content_type in _build_multipart on Python 3.x. --- setuptools/command/upload_docs.py | 2 +- setuptools/tests/test_upload_docs.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'setuptools') diff --git a/setuptools/command/upload_docs.py b/setuptools/command/upload_docs.py index eeb0718b..910d5ea7 100644 --- a/setuptools/command/upload_docs.py +++ b/setuptools/command/upload_docs.py @@ -138,7 +138,7 @@ class upload_docs(upload): part_groups = map(builder, data.items()) parts = itertools.chain.from_iterable(part_groups) body_items = itertools.chain(parts, end_items) - content_type = 'multipart/form-data; boundary=%s' % boundary + content_type = 'multipart/form-data; boundary=%s' % boundary.decode('ascii') return b''.join(body_items), content_type def upload_file(self, filename): diff --git a/setuptools/tests/test_upload_docs.py b/setuptools/tests/test_upload_docs.py index 5d50bb0b..a26e32a6 100644 --- a/setuptools/tests/test_upload_docs.py +++ b/setuptools/tests/test_upload_docs.py @@ -64,6 +64,8 @@ class TestUploadDocsTest: ) body, content_type = upload_docs._build_multipart(data) assert 'form-data' in content_type + assert "b'" not in content_type + assert 'b"' not in content_type assert isinstance(body, bytes) assert b'foo' in body assert b'content' in body -- cgit v1.2.1 From da1dcf42236810c76c763ee9be8ba71bf213c297 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 11 Mar 2017 09:12:07 -0500 Subject: Extract variables to remove hanging indents. --- setuptools/command/upload_docs.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) (limited to 'setuptools') diff --git a/setuptools/command/upload_docs.py b/setuptools/command/upload_docs.py index 910d5ea7..468cb377 100644 --- a/setuptools/command/upload_docs.py +++ b/setuptools/command/upload_docs.py @@ -77,9 +77,8 @@ class upload_docs(upload): self.mkpath(self.target_dir) # just in case for root, dirs, files in os.walk(self.target_dir): if root == self.target_dir and not files: - raise DistutilsOptionError( - "no files found in upload directory '%s'" - % self.target_dir) + tmpl = "no files found in upload directory '%s'" + raise DistutilsOptionError(tmpl % self.target_dir) for name in files: full = os.path.join(root, name) relative = root[len(self.target_dir):].lstrip(os.path.sep) @@ -159,8 +158,8 @@ class upload_docs(upload): body, ct = self._build_multipart(data) - self.announce("Submitting documentation to %s" % (self.repository), - log.INFO) + msg = "Submitting documentation to %s" % (self.repository) + self.announce(msg, log.INFO) # build the Request # We can't use urllib2 since we need to send the Basic @@ -191,16 +190,16 @@ class upload_docs(upload): r = conn.getresponse() if r.status == 200: - self.announce('Server response (%s): %s' % (r.status, r.reason), - log.INFO) + msg = 'Server response (%s): %s' % (r.status, r.reason) + self.announce(msg, log.INFO) elif r.status == 301: location = r.getheader('Location') if location is None: location = 'https://pythonhosted.org/%s/' % meta.get_name() - self.announce('Upload successful. Visit %s' % location, - log.INFO) + msg = 'Upload successful. Visit %s' % location + self.announce(msg, log.INFO) else: - self.announce('Upload failed (%s): %s' % (r.status, r.reason), - log.ERROR) + msg = 'Upload failed (%s): %s' % (r.status, r.reason) + self.announce(msg, log.ERROR) if self.show_response: print('-' * 75, r.read(), '-' * 75) -- cgit v1.2.1 From 20b3b3eaa74935d4854b63f75d893def27a4248e Mon Sep 17 00:00:00 2001 From: JGoutin Date: Mon, 20 Mar 2017 17:45:57 +0100 Subject: Update for MS BuildTools 2017 --- setuptools/msvc.py | 96 +++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 81 insertions(+), 15 deletions(-) (limited to 'setuptools') diff --git a/setuptools/msvc.py b/setuptools/msvc.py index d41daec4..71fe24cd 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -13,6 +13,7 @@ Microsoft Visual C++ 10.0: Microsoft Visual C++ 14.0: Microsoft Visual C++ Build Tools 2015 (x86, x64, arm) + Microsoft Visual Studio Build Tools 2017 (x86, x64, arm, arm64) """ import os @@ -150,6 +151,7 @@ def msvc14_get_vc_env(plat_spec): ------------------------- Microsoft Visual C++ 14.0: Microsoft Visual C++ Build Tools 2015 (x86, x64, arm) + Microsoft Visual Studio Build Tools 2017 (x86, x64, arm, arm64) Parameters ---------- @@ -411,7 +413,7 @@ class RegistryInfo: ------ str: value """ - node64 = '' if self.pi.current_is_x86() or x86 else r'\Wow6432Node' + node64 = '' if self.pi.current_is_x86() or x86 else 'Wow6432Node' return os.path.join('Software', node64, 'Microsoft', key) def lookup(self, key, name): @@ -483,12 +485,13 @@ class SystemInfo: """ Find all available Microsoft Visual C++ versions. """ - vckeys = (self.ri.vc, self.ri.vc_for_python) + ms = self.ri.microsoft + vckeys = (self.ri.vc, self.ri.vc_for_python, self.ri.vs) vc_vers = [] for hkey in self.ri.HKEYS: for key in vckeys: try: - bkey = winreg.OpenKey(hkey, key, 0, winreg.KEY_READ) + bkey = winreg.OpenKey(hkey, ms(key), 0, winreg.KEY_READ) except (OSError, IOError): continue subkeys, values, _ = winreg.QueryInfoKey(bkey) @@ -525,9 +528,24 @@ class SystemInfo: """ Microsoft Visual C++ directory. """ - # Default path - default = r'Microsoft Visual Studio %0.1f\VC' % self.vc_ver - guess_vc = os.path.join(self.ProgramFilesx86, default) + self.VSInstallDir + + # Default path starting VS2017 + guess_vc = '' + if self.vc_ver > 14.0: + default = r'VC\Tools\MSVC' + guess_vc = os.path.join(self.VSInstallDir, default) + # Subdir with VC exact version as name + try: + vc_exact_ver = os.listdir(guess_vc)[-1] + guess_vc = os.path.join(guess_vc, vc_exact_ver) + except (OSError, IOError, IndexError): + guess_vc = '' + + # Legacy default path + if not guess_vc: + default = r'Microsoft Visual Studio %0.1f\VC' % self.vc_ver + guess_vc = os.path.join(self.ProgramFilesx86, default) # Try to get "VC++ for Python" path from registry as default path reg_path = os.path.join(self.ri.vc_for_python, '%0.1f' % self.vc_ver) @@ -725,9 +743,19 @@ class SystemInfo: bits: int Platform number of bits: 32 or 64. """ - # Find actual .NET version + # Find actual .NET version in registry ver = self.ri.lookup(self.ri.vc, 'frameworkver%d' % bits) or '' + # If nothing in registry, look in Framework folder + if not ver: + dot_net_dir = (self.FrameworkDir32 if bits == 32 else + self.FrameworkDir64) + for dir_name in reversed(os.listdir(dot_net_dir)): + if (os.path.isdir(os.path.join(dot_net_dir, dir_name)) and + dir_name.startswith('v')): + ver = dir_name + break + # Set .NET versions for specified MSVC++ version if self.vc_ver >= 12.0: frameworkver = (ver, 'v4.0') @@ -810,7 +838,10 @@ class EnvironmentInfo: """ Microsoft Visual C++ & Microsoft Foundation Class Libraries """ - arch_subdir = self.pi.target_dir(hidex86=True) + if self.vc_ver >= 15.0: + arch_subdir = self.pi.target_dir(x64=True) + else: + arch_subdir = self.pi.target_dir(hidex86=True) paths = ['Lib%s' % arch_subdir, r'ATLMFC\Lib%s' % arch_subdir] if self.vc_ver >= 14.0: @@ -840,10 +871,20 @@ class EnvironmentInfo: if arch_subdir: tools += [os.path.join(si.VCInstallDir, 'Bin%s' % arch_subdir)] - if self.vc_ver >= 14.0: + if self.vc_ver == 14.0: path = 'Bin%s' % self.pi.current_dir(hidex86=True) tools += [os.path.join(si.VCInstallDir, path)] + elif self.vc_ver >= 15.0: + host_dir = (r'bin\HostX86%s' if self.pi.current_is_x86() else + r'bin\HostX64%s') + tools += [os.path.join( + si.VCInstallDir, host_dir % self.pi.target_dir(x64=True))] + + if self.pi.current_cpu != self.pi.target_cpu: + tools += [os.path.join( + si.VCInstallDir, host_dir % self.pi.current_dir(x64=True))] + else: tools += [os.path.join(si.VCInstallDir, 'Bin')] @@ -933,8 +974,11 @@ class EnvironmentInfo: """ Microsoft Windows SDK Tools """ - bin_dir = 'Bin' if self.vc_ver <= 11.0 else r'Bin\x86' - tools = [os.path.join(self.si.WindowsSdkDir, bin_dir)] + if self.vc_ver < 15.0: + bin_dir = 'Bin' if self.vc_ver <= 11.0 else r'Bin\x86' + tools = [os.path.join(self.si.WindowsSdkDir, bin_dir)] + else: + tools = [] if not self.pi.current_is_x86(): arch_subdir = self.pi.current_dir(x64=True) @@ -949,6 +993,12 @@ class EnvironmentInfo: path = r'Bin\NETFX 4.0 Tools%s' % arch_subdir tools += [os.path.join(self.si.WindowsSdkDir, path)] + elif self.vc_ver >= 15.0: + path = os.path.join(self.si.WindowsSdkDir, 'Bin') + arch_subdir = self.pi.current_dir(x64=True) + sdkver = self._get_content_dirname(path, slash=False) + tools += [os.path.join(path, r'%s%s' % (sdkver, arch_subdir))] + if self.si.WindowsSDKExecutablePath: tools += [self.si.WindowsSDKExecutablePath] @@ -1023,10 +1073,21 @@ class EnvironmentInfo: """ if self.vc_ver < 12.0: return [] + elif self.vc_ver < 15.0: + base_path = self.si.ProgramFilesx86 + arch_subdir = self.pi.current_dir(hidex86=True) + else: + base_path = self.si.VSInstallDir + arch_subdir = '' - arch_subdir = self.pi.current_dir(hidex86=True) path = r'MSBuild\%0.1f\bin%s' % (self.vc_ver, arch_subdir) - return [os.path.join(self.si.ProgramFilesx86, path)] + build = [os.path.join(base_path, path)] + + if self.vc_ver >= 15.0: + # Add Roslyn C# & Visual Basic Compiler + build += [os.path.join(base_path, path, 'Roslyn')] + + return build @property def HTMLHelpWorkshop(self): @@ -1170,7 +1231,7 @@ class EnvironmentInfo: seen_add(k) yield element - def _get_content_dirname(self, path): + def _get_content_dirname(self, path, slash=True): """ Return name of the first dir in path or '' if no dir found. @@ -1178,6 +1239,8 @@ class EnvironmentInfo: ---------- path: str Path where search dir. + slash: bool + If not True, only return "name" not "name\" Return ------ @@ -1187,7 +1250,10 @@ class EnvironmentInfo: try: name = os.listdir(path) if name: - return '%s\\' % name[0] + name = name[0] + if slash: + return '%s\\' % name + return name return '' except (OSError, IOError): return '' -- cgit v1.2.1 From 282d7d354900dbae5724feb9af1ad7bbf617220e Mon Sep 17 00:00:00 2001 From: Jim Fulton Date: Fri, 24 Mar 2017 12:55:03 -0400 Subject: fixed incomplete import of packaging.specifiers and packaging.version This fixes: #997 --- setuptools/dist.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'setuptools') diff --git a/setuptools/dist.py b/setuptools/dist.py index be55dc4e..71c6c288 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -14,7 +14,8 @@ from distutils.util import rfc822_escape import six from six.moves import map -import packaging +import packaging.specifiers +import packaging.version from setuptools.depends import Require from setuptools import windows_support -- cgit v1.2.1 From abaf7c4dd76c56eb509f6a5f6caa2f8e886801b6 Mon Sep 17 00:00:00 2001 From: Marcel Bargull Date: Fri, 7 Apr 2017 13:42:51 +0200 Subject: Fixes #999: support python_requires, py_modules in configuration files --- setuptools/config.py | 1 + setuptools/dist.py | 4 +++- setuptools/tests/test_config.py | 4 ++++ 3 files changed, 8 insertions(+), 1 deletion(-) (limited to 'setuptools') diff --git a/setuptools/config.py b/setuptools/config.py index 39a01f88..252f2deb 100644 --- a/setuptools/config.py +++ b/setuptools/config.py @@ -462,6 +462,7 @@ class ConfigOptionsHandler(ConfigHandler): 'tests_require': parse_list_semicolon, 'packages': self._parse_packages, 'entry_points': self._parse_file, + 'py_modules': parse_list, } def _parse_packages(self, value): diff --git a/setuptools/dist.py b/setuptools/dist.py index 71c6c288..fd1d28c2 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -166,7 +166,7 @@ def check_specifier(dist, attr, value): packaging.specifiers.SpecifierSet(value) except packaging.specifiers.InvalidSpecifier as error: tmpl = ( - "{attr!r} must be a string or list of strings " + "{attr!r} must be a string " "containing valid version specifiers; {error}" ) raise DistutilsSetupError(tmpl.format(attr=attr, error=error)) @@ -353,6 +353,8 @@ class Distribution(Distribution_parse_config_files, _Distribution): _Distribution.parse_config_files(self, filenames=filenames) parse_configuration(self, self.command_options) + if getattr(self, 'python_requires', None): + self.metadata.python_requires = self.python_requires def parse_command_line(self): """Process features after parsing command line options""" diff --git a/setuptools/tests/test_config.py b/setuptools/tests/test_config.py index 799fb165..8bd2a494 100644 --- a/setuptools/tests/test_config.py +++ b/setuptools/tests/test_config.py @@ -312,6 +312,8 @@ class TestOptions: 'setup_requires = docutils>=0.3; spack ==1.1, ==1.3; there\n' 'dependency_links = http://some.com/here/1, ' 'http://some.com/there/2\n' + 'python_requires = >=1.0, !=2.8\n' + 'py_modules = module1, module2\n' ) with get_dist(tmpdir) as dist: assert dist.zip_safe @@ -340,6 +342,8 @@ class TestOptions: 'there' ]) assert dist.tests_require == ['mock==0.7.2', 'pytest'] + assert dist.python_requires == '>=1.0, !=2.8' + assert dist.py_modules == ['module1', 'module2'] def test_multiline(self, tmpdir): fake_env( -- cgit v1.2.1 From ac59ef12d16c13533ead6401bdb4727016be77e3 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 7 Apr 2017 21:52:18 -0400 Subject: extract two functions for guessing the VC version; ref #995 --- setuptools/msvc.py | 41 +++++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 16 deletions(-) (limited to 'setuptools') diff --git a/setuptools/msvc.py b/setuptools/msvc.py index 71fe24cd..cf556ad3 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -530,22 +530,7 @@ class SystemInfo: """ self.VSInstallDir - # Default path starting VS2017 - guess_vc = '' - if self.vc_ver > 14.0: - default = r'VC\Tools\MSVC' - guess_vc = os.path.join(self.VSInstallDir, default) - # Subdir with VC exact version as name - try: - vc_exact_ver = os.listdir(guess_vc)[-1] - guess_vc = os.path.join(guess_vc, vc_exact_ver) - except (OSError, IOError, IndexError): - guess_vc = '' - - # Legacy default path - if not guess_vc: - default = r'Microsoft Visual Studio %0.1f\VC' % self.vc_ver - guess_vc = os.path.join(self.ProgramFilesx86, default) + guess_vc = self._guess_vc() or self._guess_vc_legacy() # Try to get "VC++ for Python" path from registry as default path reg_path = os.path.join(self.ri.vc_for_python, '%0.1f' % self.vc_ver) @@ -561,6 +546,30 @@ class SystemInfo: return path + def _guess_vc(self): + """ + Locate Visual C for 2017 + """ + + if self.vc_ver <= 14.0: + return + + default = r'VC\Tools\MSVC' + guess_vc = os.path.join(self.VSInstallDir, default) + # Subdir with VC exact version as name + try: + vc_exact_ver = os.listdir(guess_vc)[-1] + return os.path.join(guess_vc, vc_exact_ver) + except (OSError, IOError, IndexError): + pass + + def _guess_vc_legacy(self): + """ + Locate Visual C for versions prior to 2017 + """ + default = r'Microsoft Visual Studio %0.1f\VC' % self.vc_ver + return os.path.join(self.ProgramFilesx86, default) + @property def WindowsSdkVersion(self): """ -- cgit v1.2.1 From 934d6707b1d8c55c930d458e39b11038e9276a4d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 7 Apr 2017 22:07:30 -0400 Subject: Extract method for finding .Net in the framework folder. Ref #995. --- setuptools/msvc.py | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) (limited to 'setuptools') diff --git a/setuptools/msvc.py b/setuptools/msvc.py index cf556ad3..1588cd2e 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -753,17 +753,9 @@ class SystemInfo: Platform number of bits: 32 or 64. """ # Find actual .NET version in registry - ver = self.ri.lookup(self.ri.vc, 'frameworkver%d' % bits) or '' - - # If nothing in registry, look in Framework folder - if not ver: - dot_net_dir = (self.FrameworkDir32 if bits == 32 else - self.FrameworkDir64) - for dir_name in reversed(os.listdir(dot_net_dir)): - if (os.path.isdir(os.path.join(dot_net_dir, dir_name)) and - dir_name.startswith('v')): - ver = dir_name - break + reg_ver = self.ri.lookup(self.ri.vc, 'frameworkver%d' % bits) + dot_net_dir = getattr(self, 'FrameworkDir%d' % bits) + ver = reg_ver or self._find_dot_net_in(dot_net_dir) or '' # Set .NET versions for specified MSVC++ version if self.vc_ver >= 12.0: @@ -777,6 +769,18 @@ class SystemInfo: frameworkver = ('v3.0', 'v2.0.50727') return frameworkver + def _find_dot_net_in(self, dot_net_dir): + """ + Find .Net in the Framework folder + """ + matching_dirs = ( + dir_name + for dir_name in reversed(os.listdir(dot_net_dir)) + if os.path.isdir(os.path.join(dot_net_dir, dir_name)) + and dir_name.startswith('v') + ) + return next(matching_dirs, None) + class EnvironmentInfo: """ -- cgit v1.2.1 From 9430e92f888a7c41f295373bd8a6ef8af967e2e1 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 7 Apr 2017 22:19:06 -0400 Subject: Move initialization into a single location. Ref #995. --- setuptools/msvc.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'setuptools') diff --git a/setuptools/msvc.py b/setuptools/msvc.py index 1588cd2e..baf2021e 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -987,11 +987,11 @@ class EnvironmentInfo: """ Microsoft Windows SDK Tools """ + tools = [] + if self.vc_ver < 15.0: bin_dir = 'Bin' if self.vc_ver <= 11.0 else r'Bin\x86' - tools = [os.path.join(self.si.WindowsSdkDir, bin_dir)] - else: - tools = [] + tools += [os.path.join(self.si.WindowsSdkDir, bin_dir)] if not self.pi.current_is_x86(): arch_subdir = self.pi.current_dir(x64=True) -- cgit v1.2.1 From 3fa9efcbb390b87304ddc64551e9fca823694773 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 7 Apr 2017 22:23:18 -0400 Subject: Extract generator for simpler syntax. Ref #995. --- setuptools/msvc.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) (limited to 'setuptools') diff --git a/setuptools/msvc.py b/setuptools/msvc.py index baf2021e..c5a8aea0 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -987,16 +987,17 @@ class EnvironmentInfo: """ Microsoft Windows SDK Tools """ - tools = [] + return list(self._sdk_tools()) + def _sdk_tools(self): if self.vc_ver < 15.0: bin_dir = 'Bin' if self.vc_ver <= 11.0 else r'Bin\x86' - tools += [os.path.join(self.si.WindowsSdkDir, bin_dir)] + yield os.path.join(self.si.WindowsSdkDir, bin_dir) if not self.pi.current_is_x86(): arch_subdir = self.pi.current_dir(x64=True) path = 'Bin%s' % arch_subdir - tools += [os.path.join(self.si.WindowsSdkDir, path)] + yield os.path.join(self.si.WindowsSdkDir, path) if self.vc_ver == 10.0 or self.vc_ver == 11.0: if self.pi.target_is_x86(): @@ -1004,18 +1005,16 @@ class EnvironmentInfo: else: arch_subdir = self.pi.current_dir(hidex86=True, x64=True) path = r'Bin\NETFX 4.0 Tools%s' % arch_subdir - tools += [os.path.join(self.si.WindowsSdkDir, path)] + yield os.path.join(self.si.WindowsSdkDir, path) elif self.vc_ver >= 15.0: path = os.path.join(self.si.WindowsSdkDir, 'Bin') arch_subdir = self.pi.current_dir(x64=True) sdkver = self._get_content_dirname(path, slash=False) - tools += [os.path.join(path, r'%s%s' % (sdkver, arch_subdir))] + yield os.path.join(path, r'%s%s' % (sdkver, arch_subdir)) if self.si.WindowsSDKExecutablePath: - tools += [self.si.WindowsSDKExecutablePath] - - return tools + yield self.si.WindowsSDKExecutablePath @property def SdkSetup(self): -- cgit v1.2.1 From f357a32fdc9ce8e6a862efcbb9a0229f6f0a685c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 7 Apr 2017 22:30:49 -0400 Subject: Simplify _get_content_dirname by simply removing the trailing backslash. Ref #995. --- setuptools/msvc.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) (limited to 'setuptools') diff --git a/setuptools/msvc.py b/setuptools/msvc.py index c5a8aea0..9a911834 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -1010,7 +1010,7 @@ class EnvironmentInfo: elif self.vc_ver >= 15.0: path = os.path.join(self.si.WindowsSdkDir, 'Bin') arch_subdir = self.pi.current_dir(x64=True) - sdkver = self._get_content_dirname(path, slash=False) + sdkver = self._get_content_dirname(path).rstrip('\\') yield os.path.join(path, r'%s%s' % (sdkver, arch_subdir)) if self.si.WindowsSDKExecutablePath: @@ -1243,7 +1243,7 @@ class EnvironmentInfo: seen_add(k) yield element - def _get_content_dirname(self, path, slash=True): + def _get_content_dirname(self, path): """ Return name of the first dir in path or '' if no dir found. @@ -1251,8 +1251,6 @@ class EnvironmentInfo: ---------- path: str Path where search dir. - slash: bool - If not True, only return "name" not "name\" Return ------ @@ -1262,10 +1260,7 @@ class EnvironmentInfo: try: name = os.listdir(path) if name: - name = name[0] - if slash: - return '%s\\' % name - return name + return '%s\\' % name[0] return '' except (OSError, IOError): return '' -- cgit v1.2.1 From cd13a8c0c4ee765a8bd083863338dec4ba618d08 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 7 Apr 2017 22:33:28 -0400 Subject: Remove unnecessary raw string. Ref #995. --- setuptools/msvc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'setuptools') diff --git a/setuptools/msvc.py b/setuptools/msvc.py index 9a911834..35c02129 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -1011,7 +1011,7 @@ class EnvironmentInfo: path = os.path.join(self.si.WindowsSdkDir, 'Bin') arch_subdir = self.pi.current_dir(x64=True) sdkver = self._get_content_dirname(path).rstrip('\\') - yield os.path.join(path, r'%s%s' % (sdkver, arch_subdir)) + yield os.path.join(path, '%s%s' % (sdkver, arch_subdir)) if self.si.WindowsSDKExecutablePath: yield self.si.WindowsSDKExecutablePath -- cgit v1.2.1 From f420bb2093f85d3aa34e732c42133b0e2e06ecd9 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 8 Apr 2017 10:18:31 -0400 Subject: Let the default vc_min_ver represent the most lenient, degenerate limit. --- setuptools/msvc.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'setuptools') diff --git a/setuptools/msvc.py b/setuptools/msvc.py index 35c02129..75745e67 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -806,15 +806,14 @@ class EnvironmentInfo: # Variables and properties in this class use originals CamelCase variables # names from Microsoft source files for more easy comparaison. - def __init__(self, arch, vc_ver=None, vc_min_ver=None): + def __init__(self, arch, vc_ver=None, vc_min_ver=0): self.pi = PlatformInfo(arch) self.ri = RegistryInfo(self.pi) self.si = SystemInfo(self.ri, vc_ver) - if vc_min_ver: - if self.vc_ver < vc_min_ver: - err = 'No suitable Microsoft Visual C++ version found' - raise distutils.errors.DistutilsPlatformError(err) + if self.vc_ver < vc_min_ver: + err = 'No suitable Microsoft Visual C++ version found' + raise distutils.errors.DistutilsPlatformError(err) @property def vc_ver(self): -- cgit v1.2.1 From 66177c944536aab86994f6df7172c0d9d6acb5cf Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 8 Apr 2017 10:20:47 -0400 Subject: Extract private method for locating latest available vc ver. --- setuptools/msvc.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'setuptools') diff --git a/setuptools/msvc.py b/setuptools/msvc.py index 75745e67..d739178b 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -472,14 +472,14 @@ class SystemInfo: def __init__(self, registry_info, vc_ver=None): self.ri = registry_info self.pi = self.ri.pi - if vc_ver: - self.vc_ver = vc_ver - else: - try: - self.vc_ver = self.find_available_vc_vers()[-1] - except IndexError: - err = 'No Microsoft Visual C++ version found' - raise distutils.errors.DistutilsPlatformError(err) + self.vc_ver = vc_ver or self._find_latest_available_vc_ver() + + def _find_latest_available_vc_ver(self): + try: + return self.find_available_vc_vers()[-1] + except IndexError: + err = 'No Microsoft Visual C++ version found' + raise distutils.errors.DistutilsPlatformError(err) def find_available_vc_vers(self): """ -- cgit v1.2.1 From c86965048dee300bc4d985afc72e5de7267658b3 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 8 Apr 2017 10:29:23 -0400 Subject: spaces for consistency --- setuptools/py27compat.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'setuptools') diff --git a/setuptools/py27compat.py b/setuptools/py27compat.py index f0a80a8e..4b6fae91 100644 --- a/setuptools/py27compat.py +++ b/setuptools/py27compat.py @@ -20,8 +20,8 @@ if sys.version_info < (3,): linux_py2_ascii = ( - platform.system() == 'Linux' and - sys.version_info < (3,) + platform.system() == 'Linux' and + sys.version_info < (3,) ) rmtree_safe = str if linux_py2_ascii else lambda x: x -- cgit v1.2.1 From 1d928cbc7b2cfcf1ffd2ec27f83ee33f0af39dfe Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 8 Apr 2017 10:35:00 -0400 Subject: Use six to detect Python 2 --- setuptools/py27compat.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'setuptools') diff --git a/setuptools/py27compat.py b/setuptools/py27compat.py index 4b6fae91..701283c8 100644 --- a/setuptools/py27compat.py +++ b/setuptools/py27compat.py @@ -2,9 +2,10 @@ Compatibility Support for Python 2.7 and earlier """ -import sys import platform +import six + def get_all_headers(message, key): """ @@ -13,15 +14,14 @@ def get_all_headers(message, key): return message.get_all(key) -if sys.version_info < (3,): - +if six.PY2: def get_all_headers(message, key): return message.getheaders(key) linux_py2_ascii = ( platform.system() == 'Linux' and - sys.version_info < (3,) + six.PY2 ) rmtree_safe = str if linux_py2_ascii else lambda x: x -- cgit v1.2.1 From b50fdf497d6970002a2f7156650d7da21e2e39f5 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 8 Apr 2017 10:44:34 -0400 Subject: In msvc9_query_vcvarsall, ensure dict values are not unicode. Fixes #992. --- setuptools/msvc.py | 5 ++++- setuptools/py27compat.py | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) (limited to 'setuptools') diff --git a/setuptools/msvc.py b/setuptools/msvc.py index d739178b..1e7a3277 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -26,6 +26,7 @@ from packaging.version import LegacyVersion from six.moves import filterfalse from .monkey import get_unpatched +from . import py27compat if platform.system() == 'Windows': from six.moves import winreg @@ -134,11 +135,13 @@ def msvc9_query_vcvarsall(ver, arch='x86', *args, **kwargs): # If error, try to set environment directly try: - return EnvironmentInfo(arch, ver).return_env() + env = EnvironmentInfo(arch, ver).return_env() except distutils.errors.DistutilsPlatformError as exc: _augment_exception(exc, ver, arch) raise + return py27compat.make_dict_values_strings(env) + def msvc14_get_vc_env(plat_spec): """ diff --git a/setuptools/py27compat.py b/setuptools/py27compat.py index 701283c8..0f924889 100644 --- a/setuptools/py27compat.py +++ b/setuptools/py27compat.py @@ -26,3 +26,17 @@ linux_py2_ascii = ( rmtree_safe = str if linux_py2_ascii else lambda x: x """Workaround for http://bugs.python.org/issue24672""" + + +def dict_values_strings(dict_): + """ + Given a dict, make sure the text values are str. + """ + if six.PY3: + return dict_ + + # When dropping Python 2.6 support, use a dict constructor + return dict( + (key, str(value)) + for key, value in dict_.iteritems() + ) -- cgit v1.2.1 From 6ab912d1f676604d71e8e2090d262002f78929d4 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 8 Apr 2017 11:23:32 -0400 Subject: Correct typo. Ref #992. --- setuptools/msvc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'setuptools') diff --git a/setuptools/msvc.py b/setuptools/msvc.py index 1e7a3277..3970bfaf 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -140,7 +140,7 @@ def msvc9_query_vcvarsall(ver, arch='x86', *args, **kwargs): _augment_exception(exc, ver, arch) raise - return py27compat.make_dict_values_strings(env) + return py27compat.dict_values_strings(env) def msvc14_get_vc_env(plat_spec): -- cgit v1.2.1 From fe3deeeebd6d5f01e803ea6bbfcf001834ca45b7 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 10 Apr 2017 11:13:51 -0400 Subject: Nicer syntax for processing PYTHONPATH --- setuptools/command/easy_install.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'setuptools') diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py index ef83f7ae..55d26560 100755 --- a/setuptools/command/easy_install.py +++ b/setuptools/command/easy_install.py @@ -1349,9 +1349,16 @@ class easy_install(Command): def get_site_dirs(): - # return a list of 'site' dirs - sitedirs = [_f for _f in os.environ.get('PYTHONPATH', - '').split(os.pathsep) if _f] + """ + Return a list of 'site' dirs + """ + + sitedirs = [] + + # start with PYTHONPATH + pythonpath_items = os.environ.get('PYTHONPATH', '').split(os.pathsep) + sitedirs.extend(filter(None, pythonpath_items)) + prefixes = [sys.prefix] if sys.exec_prefix != sys.prefix: prefixes.append(sys.exec_prefix) -- cgit v1.2.1 From 3b18e07289d1cd4cdd046d46c244d77938732e5e Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 10 Apr 2017 11:24:25 -0400 Subject: Consolidate technique for reading PYTHONPATH. --- setuptools/command/easy_install.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'setuptools') diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py index 55d26560..e30ca3ac 100755 --- a/setuptools/command/easy_install.py +++ b/setuptools/command/easy_install.py @@ -474,8 +474,7 @@ class easy_install(Command): else: 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, _pythonpath()): # only PYTHONPATH dirs need a site.py, so pretend it's there self.sitepy_installed = True elif self.multi_version and not os.path.exists(pth_file): @@ -1348,6 +1347,11 @@ class easy_install(Command): setattr(self, attr, val) +def _pythonpath(): + items = os.environ.get('PYTHONPATH', '').split(os.pathsep) + return filter(None, items) + + def get_site_dirs(): """ Return a list of 'site' dirs @@ -1356,8 +1360,7 @@ def get_site_dirs(): sitedirs = [] # start with PYTHONPATH - pythonpath_items = os.environ.get('PYTHONPATH', '').split(os.pathsep) - sitedirs.extend(filter(None, pythonpath_items)) + sitedirs.extend(_pythonpath()) prefixes = [sys.prefix] if sys.exec_prefix != sys.prefix: -- cgit v1.2.1 From a1aa1be06bdef8824c8954bb74dc9a57f324f826 Mon Sep 17 00:00:00 2001 From: JGoutin Date: Mon, 10 Apr 2017 17:56:48 +0200 Subject: Fixes for Visual Studio 2017 - VCRuntimeRedist new path since VS2017. - Use always last Windows SDK and UCRT SDK when more than one version are available. - Minors docstrings changes. Tested with "Visual Studio 2017 Community" and "Visual Studio Build Tools 2017". --- setuptools/msvc.py | 130 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 79 insertions(+), 51 deletions(-) (limited to 'setuptools') diff --git a/setuptools/msvc.py b/setuptools/msvc.py index 3970bfaf..3110eaff 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -4,15 +4,16 @@ Improved support for Microsoft Visual C++ compilers. Known supported compilers: -------------------------- Microsoft Visual C++ 9.0: - Microsoft Visual C++ Compiler for Python 2.7 (x86, amd64); - Microsoft Windows SDK 7.0 (x86, x64, ia64); + Microsoft Visual C++ Compiler for Python 2.7 (x86, amd64) Microsoft Windows SDK 6.1 (x86, x64, ia64) + Microsoft Windows SDK 7.0 (x86, x64, ia64) Microsoft Visual C++ 10.0: Microsoft Windows SDK 7.1 (x86, x64, ia64) Microsoft Visual C++ 14.0: Microsoft Visual C++ Build Tools 2015 (x86, x64, arm) + Microsoft Visual Studio 2017 (x86, x64, arm, arm64) Microsoft Visual Studio Build Tools 2017 (x86, x64, arm, arm64) """ @@ -96,7 +97,7 @@ def msvc9_find_vcvarsall(version): def msvc9_query_vcvarsall(ver, arch='x86', *args, **kwargs): """ - Patched "distutils.msvc9compiler.query_vcvarsall" for support standalones + Patched "distutils.msvc9compiler.query_vcvarsall" for support extra compilers. Set environment without use of "vcvarsall.bat". @@ -104,9 +105,9 @@ def msvc9_query_vcvarsall(ver, arch='x86', *args, **kwargs): Known supported compilers ------------------------- Microsoft Visual C++ 9.0: - Microsoft Visual C++ Compiler for Python 2.7 (x86, amd64); - Microsoft Windows SDK 7.0 (x86, x64, ia64); + Microsoft Visual C++ Compiler for Python 2.7 (x86, amd64) Microsoft Windows SDK 6.1 (x86, x64, ia64) + Microsoft Windows SDK 7.0 (x86, x64, ia64) Microsoft Visual C++ 10.0: Microsoft Windows SDK 7.1 (x86, x64, ia64) @@ -145,7 +146,7 @@ def msvc9_query_vcvarsall(ver, arch='x86', *args, **kwargs): def msvc14_get_vc_env(plat_spec): """ - Patched "distutils._msvccompiler._get_vc_env" for support standalones + Patched "distutils._msvccompiler._get_vc_env" for support extra compilers. Set environment without use of "vcvarsall.bat". @@ -154,6 +155,7 @@ def msvc14_get_vc_env(plat_spec): ------------------------- Microsoft Visual C++ 14.0: Microsoft Visual C++ Build Tools 2015 (x86, x64, arm) + Microsoft Visual Studio 2017 (x86, x64, arm, arm64) Microsoft Visual Studio Build Tools 2017 (x86, x64, arm, arm64) Parameters @@ -553,7 +555,6 @@ class SystemInfo: """ Locate Visual C for 2017 """ - if self.vc_ver <= 14.0: return @@ -576,9 +577,8 @@ class SystemInfo: @property def WindowsSdkVersion(self): """ - Microsoft Windows SDK versions. + Microsoft Windows SDK versions for specified MSVC++ version. """ - # Set Windows SDK versions for specified MSVC++ version if self.vc_ver <= 9.0: return ('7.0', '6.1', '6.0a') elif self.vc_ver == 10.0: @@ -590,6 +590,14 @@ class SystemInfo: elif self.vc_ver >= 14.0: return ('10.0', '8.1') + @property + def WindowsSdkLastVersion(self): + """ + Microsoft Windows SDK last version + """ + return self._use_last_dir_name(os.path.join( + self.WindowsSdkDir, 'lib')) + @property def WindowsSdkDir(self): """ @@ -687,6 +695,14 @@ class SystemInfo: break return sdkdir or '' + @property + def UniversalCRTSdkLastVersion(self): + """ + Microsoft Universal C Runtime SDK last version + """ + return self._use_last_dir_name(os.path.join( + self.UniversalCRTSdkDir, 'lib')) + @property def NetFxSdkVersion(self): """ @@ -746,7 +762,7 @@ class SystemInfo: """ return self._find_dot_net_versions(64) - def _find_dot_net_versions(self, bits=32): + def _find_dot_net_versions(self, bits): """ Find Microsoft .NET Framework versions. @@ -758,7 +774,7 @@ class SystemInfo: # Find actual .NET version in registry reg_ver = self.ri.lookup(self.ri.vc, 'frameworkver%d' % bits) dot_net_dir = getattr(self, 'FrameworkDir%d' % bits) - ver = reg_ver or self._find_dot_net_in(dot_net_dir) or '' + ver = reg_ver or self._use_last_dir_name(dot_net_dir, 'v') or '' # Set .NET versions for specified MSVC++ version if self.vc_ver >= 12.0: @@ -772,17 +788,24 @@ class SystemInfo: frameworkver = ('v3.0', 'v2.0.50727') return frameworkver - def _find_dot_net_in(self, dot_net_dir): + def _use_last_dir_name(self, path, prefix=''): """ - Find .Net in the Framework folder + Return name of the last dir in path or '' if no dir found. + + Parameters + ---------- + path: str + Use dirs in this path + prefix: str + Use only dirs startings by this prefix """ matching_dirs = ( dir_name - for dir_name in reversed(os.listdir(dot_net_dir)) - if os.path.isdir(os.path.join(dot_net_dir, dir_name)) - and dir_name.startswith('v') + for dir_name in reversed(os.listdir(path)) + if os.path.isdir(os.path.join(path, dir_name)) and + dir_name.startswith(prefix) ) - return next(matching_dirs, None) + return next(matching_dirs, None) or '' class EnvironmentInfo: @@ -917,8 +940,8 @@ class EnvironmentInfo: else: arch_subdir = self.pi.target_dir(x64=True) lib = os.path.join(self.si.WindowsSdkDir, 'lib') - libver = self._get_content_dirname(lib) - return [os.path.join(lib, '%sum%s' % (libver, arch_subdir))] + libver = self._sdk_subdir + return [os.path.join(lib, '%sum%s' % (libver , arch_subdir))] @property def OSIncludes(self): @@ -932,7 +955,7 @@ class EnvironmentInfo: else: if self.vc_ver >= 14.0: - sdkver = self._get_content_dirname(include) + sdkver = self._sdk_subdir else: sdkver = '' return [os.path.join(include, '%sshared' % sdkver), @@ -992,6 +1015,9 @@ class EnvironmentInfo: return list(self._sdk_tools()) def _sdk_tools(self): + """ + Microsoft Windows SDK Tools paths generator + """ if self.vc_ver < 15.0: bin_dir = 'Bin' if self.vc_ver <= 11.0 else r'Bin\x86' yield os.path.join(self.si.WindowsSdkDir, bin_dir) @@ -1012,12 +1038,20 @@ class EnvironmentInfo: elif self.vc_ver >= 15.0: path = os.path.join(self.si.WindowsSdkDir, 'Bin') arch_subdir = self.pi.current_dir(x64=True) - sdkver = self._get_content_dirname(path).rstrip('\\') + sdkver = self.si.WindowsSdkLastVersion yield os.path.join(path, '%s%s' % (sdkver, arch_subdir)) if self.si.WindowsSDKExecutablePath: yield self.si.WindowsSDKExecutablePath + @property + def _sdk_subdir(self): + """ + Microsoft Windows SDK version subdir + """ + ucrtver = self.si.WindowsSdkLastVersion + return ('%s\\' % ucrtver) if ucrtver else '' + @property def SdkSetup(self): """ @@ -1116,27 +1150,34 @@ class EnvironmentInfo: @property def UCRTLibraries(self): """ - Microsoft Universal CRT Libraries + Microsoft Universal C Runtime SDK Libraries """ if self.vc_ver < 14.0: return [] arch_subdir = self.pi.target_dir(x64=True) lib = os.path.join(self.si.UniversalCRTSdkDir, 'lib') - ucrtver = self._get_content_dirname(lib) + ucrtver = self._ucrt_subdir return [os.path.join(lib, '%sucrt%s' % (ucrtver, arch_subdir))] @property def UCRTIncludes(self): """ - Microsoft Universal CRT Include + Microsoft Universal C Runtime SDK Include """ if self.vc_ver < 14.0: return [] include = os.path.join(self.si.UniversalCRTSdkDir, 'include') - ucrtver = self._get_content_dirname(include) - return [os.path.join(include, '%sucrt' % ucrtver)] + return [os.path.join(include, '%sucrt' % self._ucrt_subdir)] + + @property + def _ucrt_subdir(self): + """ + Microsoft Universal C Runtime SDK version subdir + """ + ucrtver = self.si.UniversalCRTSdkLastVersion + return ('%s\\' % ucrtver) if ucrtver else '' @property def FSharp(self): @@ -1154,9 +1195,18 @@ class EnvironmentInfo: Microsoft Visual C++ runtime redistribuable dll """ arch_subdir = self.pi.target_dir(x64=True) - vcruntime = 'redist%s\\Microsoft.VC%d0.CRT\\vcruntime%d0.dll' - vcruntime = vcruntime % (arch_subdir, self.vc_ver, self.vc_ver) - return os.path.join(self.si.VCInstallDir, vcruntime) + if self.vc_ver < 15: + redist_path = self.si.VCInstallDir + vcruntime = 'redist%s\\Microsoft.VC%d0.CRT\\vcruntime%d0.dll' + else: + redist_path = self.si.VCInstallDir.replace('\\Tools', '\\Redist') + vcruntime = 'onecore%s\\Microsoft.VC%d0.CRT\\vcruntime%d0.dll' + + # Visual Studio 2017 is still Visual C++ 14.0 + dll_ver = 14.0 if self.vc_ver == 15 else self.vc_ver + + vcruntime = vcruntime % (arch_subdir, self.vc_ver, dll_ver) + return os.path.join(redist_path, vcruntime) def return_env(self, exists=True): """ @@ -1244,25 +1294,3 @@ class EnvironmentInfo: if k not in seen: seen_add(k) yield element - - def _get_content_dirname(self, path): - """ - Return name of the first dir in path or '' if no dir found. - - Parameters - ---------- - path: str - Path where search dir. - - Return - ------ - foldername: str - "name\" or "" - """ - try: - name = os.listdir(path) - if name: - return '%s\\' % name[0] - return '' - except (OSError, IOError): - return '' -- cgit v1.2.1 From 1955e5b0df67cc1aa389b8c655199958a6fcc6a0 Mon Sep 17 00:00:00 2001 From: Stefano Miccoli Date: Thu, 13 Apr 2017 22:57:56 +0200 Subject: addresses #436 --- setuptools/command/egg_info.py | 2 -- 1 file changed, 2 deletions(-) (limited to 'setuptools') diff --git a/setuptools/command/egg_info.py b/setuptools/command/egg_info.py index 1a6ea9cb..21bbfb72 100755 --- a/setuptools/command/egg_info.py +++ b/setuptools/command/egg_info.py @@ -564,8 +564,6 @@ class manifest_maker(sdist): rcfiles = list(walk_revctrl()) if rcfiles: self.filelist.extend(rcfiles) - elif os.path.exists(self.manifest): - self.read_manifest() ei_cmd = self.get_finalized_command('egg_info') self.filelist.graft(ei_cmd.egg_info) -- cgit v1.2.1 From 74b46ceaf9affd65da0ba0d2d56e58a85955a652 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 16 Apr 2017 09:00:53 -0500 Subject: Revert "In msvc9_query_vcvarsall, ensure dict values are not unicode. Fixes #992." This reverts commit b50fdf497d6970002a2f7156650d7da21e2e39f5. --- setuptools/msvc.py | 5 +---- setuptools/py27compat.py | 14 -------------- 2 files changed, 1 insertion(+), 18 deletions(-) (limited to 'setuptools') diff --git a/setuptools/msvc.py b/setuptools/msvc.py index 3110eaff..84dcb2a7 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -27,7 +27,6 @@ from packaging.version import LegacyVersion from six.moves import filterfalse from .monkey import get_unpatched -from . import py27compat if platform.system() == 'Windows': from six.moves import winreg @@ -136,13 +135,11 @@ def msvc9_query_vcvarsall(ver, arch='x86', *args, **kwargs): # If error, try to set environment directly try: - env = EnvironmentInfo(arch, ver).return_env() + return EnvironmentInfo(arch, ver).return_env() except distutils.errors.DistutilsPlatformError as exc: _augment_exception(exc, ver, arch) raise - return py27compat.dict_values_strings(env) - def msvc14_get_vc_env(plat_spec): """ diff --git a/setuptools/py27compat.py b/setuptools/py27compat.py index 0f924889..701283c8 100644 --- a/setuptools/py27compat.py +++ b/setuptools/py27compat.py @@ -26,17 +26,3 @@ linux_py2_ascii = ( rmtree_safe = str if linux_py2_ascii else lambda x: x """Workaround for http://bugs.python.org/issue24672""" - - -def dict_values_strings(dict_): - """ - Given a dict, make sure the text values are str. - """ - if six.PY3: - return dict_ - - # When dropping Python 2.6 support, use a dict constructor - return dict( - (key, str(value)) - for key, value in dict_.iteritems() - ) -- cgit v1.2.1 From 58fa3b44e5130b0cf79523c876f46a07734db7ba Mon Sep 17 00:00:00 2001 From: Pi Delport Date: Tue, 18 Apr 2017 17:10:47 +0200 Subject: Add an integration test to install pyuri This test is also a regression test for issue #1016. --- setuptools/tests/test_integration.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'setuptools') diff --git a/setuptools/tests/test_integration.py b/setuptools/tests/test_integration.py index cb62eb29..2acec91b 100644 --- a/setuptools/tests/test_integration.py +++ b/setuptools/tests/test_integration.py @@ -99,6 +99,21 @@ def test_python_novaclient(install_context): _install_one('python-novaclient', install_context, 'novaclient', 'base.py') + +def test_pyuri(install_context): + """ + Install the pyuri package (version 0.3.1 at the time of writing). + + This is also a regression test for issue #1016. + """ + _install_one('pyuri', install_context, 'pyuri', 'uri.py') + + pyuri = install_context.installed_projects['pyuri'] + + # The package data should be installed. + assert os.path.exists(os.path.join(pyuri.location, 'pyuri', 'uri.regex')) + + import re import subprocess import functools -- cgit v1.2.1 From 1163c86b3b62258f28964c2aee8483631a85c892 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 18 Apr 2017 17:52:44 -0500 Subject: Revert "addresses #436". Fixes #1016. This reverts commit 1955e5b0df67cc1aa389b8c655199958a6fcc6a0. --- setuptools/command/egg_info.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'setuptools') diff --git a/setuptools/command/egg_info.py b/setuptools/command/egg_info.py index 21bbfb72..1a6ea9cb 100755 --- a/setuptools/command/egg_info.py +++ b/setuptools/command/egg_info.py @@ -564,6 +564,8 @@ class manifest_maker(sdist): rcfiles = list(walk_revctrl()) if rcfiles: self.filelist.extend(rcfiles) + elif os.path.exists(self.manifest): + self.read_manifest() ei_cmd = self.get_finalized_command('egg_info') self.filelist.graft(ei_cmd.egg_info) -- cgit v1.2.1 From f1a9711815acab7e2d9c77b86b43117f72c5c78f Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 18 Apr 2017 18:32:40 -0500 Subject: Pass flags programmatically, avoiding deprecating trailing pattern flags syntax revealed in #1015. --- setuptools/command/egg_info.py | 3 ++- setuptools/tests/test_manifest.py | 26 +++++++++++++------------- 2 files changed, 15 insertions(+), 14 deletions(-) (limited to 'setuptools') diff --git a/setuptools/command/egg_info.py b/setuptools/command/egg_info.py index 1a6ea9cb..151e495b 100755 --- a/setuptools/command/egg_info.py +++ b/setuptools/command/egg_info.py @@ -112,7 +112,8 @@ def translate_pattern(glob): if not last_chunk: pat += sep - return re.compile(pat + r'\Z(?ms)') + pat += r'\Z' + return re.compile(pat, flags=re.MULTILINE|re.DOTALL) class egg_info(Command): diff --git a/setuptools/tests/test_manifest.py b/setuptools/tests/test_manifest.py index 3b34c888..57347b53 100644 --- a/setuptools/tests/test_manifest.py +++ b/setuptools/tests/test_manifest.py @@ -71,26 +71,26 @@ def get_pattern(glob): def test_translated_pattern_test(): l = make_local_path - assert get_pattern('foo') == r'foo\Z(?ms)' - assert get_pattern(l('foo/bar')) == l(r'foo\/bar\Z(?ms)') + assert get_pattern('foo') == r'foo\Z' + assert get_pattern(l('foo/bar')) == l(r'foo\/bar\Z') # Glob matching - assert get_pattern('*.txt') == l(r'[^\/]*\.txt\Z(?ms)') - assert get_pattern('dir/*.txt') == l(r'dir\/[^\/]*\.txt\Z(?ms)') - assert get_pattern('*/*.py') == l(r'[^\/]*\/[^\/]*\.py\Z(?ms)') + assert get_pattern('*.txt') == l(r'[^\/]*\.txt\Z') + assert get_pattern('dir/*.txt') == l(r'dir\/[^\/]*\.txt\Z') + assert get_pattern('*/*.py') == l(r'[^\/]*\/[^\/]*\.py\Z') assert get_pattern('docs/page-?.txt') \ - == l(r'docs\/page\-[^\/]\.txt\Z(?ms)') + == l(r'docs\/page\-[^\/]\.txt\Z') # Globstars change what they mean depending upon where they are - assert get_pattern(l('foo/**/bar')) == l(r'foo\/(?:[^\/]+\/)*bar\Z(?ms)') - assert get_pattern(l('foo/**')) == l(r'foo\/.*\Z(?ms)') - assert get_pattern(l('**')) == r'.*\Z(?ms)' + assert get_pattern(l('foo/**/bar')) == l(r'foo\/(?:[^\/]+\/)*bar\Z') + assert get_pattern(l('foo/**')) == l(r'foo\/.*\Z') + assert get_pattern(l('**')) == r'.*\Z' # Character classes - assert get_pattern('pre[one]post') == r'pre[one]post\Z(?ms)' - assert get_pattern('hello[!one]world') == r'hello[^one]world\Z(?ms)' - assert get_pattern('[]one].txt') == r'[\]one]\.txt\Z(?ms)' - assert get_pattern('foo[!]one]bar') == r'foo[^\]one]bar\Z(?ms)' + assert get_pattern('pre[one]post') == r'pre[one]post\Z' + assert get_pattern('hello[!one]world') == r'hello[^one]world\Z' + assert get_pattern('[]one].txt') == r'[\]one]\.txt\Z' + assert get_pattern('foo[!]one]bar') == r'foo[^\]one]bar\Z' class TempDirTestCase(object): -- cgit v1.2.1 From d8e1ed570126dfc99ed7f9126df3bfc890e7005d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 18 Apr 2017 21:00:50 -0500 Subject: Rewrite tests to test the actual matching rather than making assertions about the regular expressions. Fixes #1015. --- setuptools/tests/test_manifest.py | 103 ++++++++++++++++++++++++++++++-------- 1 file changed, 83 insertions(+), 20 deletions(-) (limited to 'setuptools') diff --git a/setuptools/tests/test_manifest.py b/setuptools/tests/test_manifest.py index 57347b53..28cbca1a 100644 --- a/setuptools/tests/test_manifest.py +++ b/setuptools/tests/test_manifest.py @@ -6,6 +6,7 @@ import os import shutil import sys import tempfile +import itertools from distutils import log from distutils.errors import DistutilsTemplateError @@ -65,32 +66,94 @@ default_files = frozenset(map(make_local_path, [ ])) -def get_pattern(glob): - return translate_pattern(make_local_path(glob)).pattern - - -def test_translated_pattern_test(): - l = make_local_path - assert get_pattern('foo') == r'foo\Z' - assert get_pattern(l('foo/bar')) == l(r'foo\/bar\Z') +translate_specs = [ + ('foo', ['foo'], ['bar', 'foobar']), + ('foo/bar', ['foo/bar'], ['foo/bar/baz', './foo/bar', 'foo']), # Glob matching - assert get_pattern('*.txt') == l(r'[^\/]*\.txt\Z') - assert get_pattern('dir/*.txt') == l(r'dir\/[^\/]*\.txt\Z') - assert get_pattern('*/*.py') == l(r'[^\/]*\/[^\/]*\.py\Z') - assert get_pattern('docs/page-?.txt') \ - == l(r'docs\/page\-[^\/]\.txt\Z') + ('*.txt', ['foo.txt', 'bar.txt'], ['foo/foo.txt']), + ('dir/*.txt', ['dir/foo.txt', 'dir/bar.txt', 'dir/.txt'], ['notdir/foo.txt']), + ('*/*.py', ['bin/start.py'], []), + ('docs/page-?.txt', ['docs/page-9.txt'], ['docs/page-10.txt']), # Globstars change what they mean depending upon where they are - assert get_pattern(l('foo/**/bar')) == l(r'foo\/(?:[^\/]+\/)*bar\Z') - assert get_pattern(l('foo/**')) == l(r'foo\/.*\Z') - assert get_pattern(l('**')) == r'.*\Z' + ( + 'foo/**/bar', + ['foo/bing/bar', 'foo/bing/bang/bar', 'foo/bar'], + ['foo/abar'], + ), + ( + 'foo/**', + ['foo/bar/bing.py', 'foo/x'], + ['/foo/x'], + ), + ( + '**', + ['x', 'abc/xyz', '@nything'], + [], + ), # Character classes - assert get_pattern('pre[one]post') == r'pre[one]post\Z' - assert get_pattern('hello[!one]world') == r'hello[^one]world\Z' - assert get_pattern('[]one].txt') == r'[\]one]\.txt\Z' - assert get_pattern('foo[!]one]bar') == r'foo[^\]one]bar\Z' + ( + 'pre[one]post', + ['preopost', 'prenpost', 'preepost'], + ['prepost', 'preonepost'], + ), + + ( + 'hello[!one]world', + ['helloxworld', 'helloyworld'], + ['hellooworld', 'helloworld', 'hellooneworld'], + ), + + ( + '[]one].txt', + ['o.txt', '].txt', 'e.txt'], + ['one].txt'], + ), + + ( + 'foo[!]one]bar', + ['fooybar'], + ['foo]bar', 'fooobar', 'fooebar'], + ), + +] +""" +A spec of inputs for 'translate_pattern' and matches and mismatches +for that input. +""" + +match_params = itertools.chain.from_iterable( + zip(itertools.repeat(pattern), matches) + for pattern, matches, mismatches in translate_specs +) + + +@pytest.fixture(params=match_params) +def pattern_match(request): + return map(make_local_path, request.param) + + +mismatch_params = itertools.chain.from_iterable( + zip(itertools.repeat(pattern), mismatches) + for pattern, matches, mismatches in translate_specs +) + + +@pytest.fixture(params=mismatch_params) +def pattern_mismatch(request): + return map(make_local_path, request.param) + + +def test_translated_pattern_match(pattern_match): + pattern, target = pattern_match + assert translate_pattern(pattern).match(target) + + +def test_translated_pattern_mismatch(pattern_mismatch): + pattern, target = pattern_mismatch + assert not translate_pattern(pattern).match(target) class TempDirTestCase(object): -- cgit v1.2.1 From b1cb67c743de2581f9393315b23777011c22ecf7 Mon Sep 17 00:00:00 2001 From: Nick Douma Date: Thu, 27 Apr 2017 11:44:56 +0200 Subject: Use a different method to lookup base classes on Jython Jython seems to implement inspect.getmro differently, which causes any classes with the same name as a class lower in the MRO not to be returned. This patch offloads the MRO lookup to a separate function, which implements different logic for Jython only. Ref #1024 --- setuptools/monkey.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'setuptools') diff --git a/setuptools/monkey.py b/setuptools/monkey.py index 68fad9dd..97ba159d 100644 --- a/setuptools/monkey.py +++ b/setuptools/monkey.py @@ -21,6 +21,19 @@ if you think you need this functionality. """ +def get_mro(cls): + """Returns the bases classes for cls sorted by the MRO. + + Works around an issue on Jython where inspect.getmro will not return all + base classes if multiple classes share the same name. Instead, this + function will return a tuple containing the class itself, and the contents + of cls.__bases__ . + """ + if platform.python_implementation() != "Jython": + return inspect.getmro(cls) + return (cls,) + cls.__bases__ + + def get_unpatched(item): lookup = ( get_unpatched_class if isinstance(item, six.class_types) else @@ -38,7 +51,7 @@ def get_unpatched_class(cls): """ external_bases = ( cls - for cls in inspect.getmro(cls) + for cls in get_mro(cls) if not cls.__module__.startswith('setuptools') ) base = next(external_bases) -- cgit v1.2.1 From 8219fe5211aebbc8de8da9c988cc2a2b85b92829 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 27 Apr 2017 15:28:54 -0400 Subject: Make _get_mro private; Swap logic to put preferred behavior at top level; Update docstring to reference issue. --- setuptools/monkey.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'setuptools') diff --git a/setuptools/monkey.py b/setuptools/monkey.py index 97ba159d..acd0a4f4 100644 --- a/setuptools/monkey.py +++ b/setuptools/monkey.py @@ -21,17 +21,18 @@ if you think you need this functionality. """ -def get_mro(cls): - """Returns the bases classes for cls sorted by the MRO. +def _get_mro(cls): + """ + Returns the bases classes for cls sorted by the MRO. Works around an issue on Jython where inspect.getmro will not return all base classes if multiple classes share the same name. Instead, this function will return a tuple containing the class itself, and the contents - of cls.__bases__ . + of cls.__bases__. See https://github.com/pypa/setuptools/issues/1024. """ - if platform.python_implementation() != "Jython": - return inspect.getmro(cls) - return (cls,) + cls.__bases__ + if platform.python_implementation() == "Jython": + return (cls,) + cls.__bases__ + return inspect.getmro(cls) def get_unpatched(item): @@ -51,7 +52,7 @@ def get_unpatched_class(cls): """ external_bases = ( cls - for cls in get_mro(cls) + for cls in _get_mro(cls) if not cls.__module__.startswith('setuptools') ) base = next(external_bases) -- cgit v1.2.1 From d919999bf8c37b2efad7d6eb57ec2f5ff340799e Mon Sep 17 00:00:00 2001 From: anatoly techtonik Date: Tue, 16 May 2017 11:13:30 +0300 Subject: Document -s to run single test Fixes https://github.com/pypa/setuptools/issues/1032 --- setuptools/command/test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'setuptools') diff --git a/setuptools/command/test.py b/setuptools/command/test.py index e7a386d1..29227d79 100644 --- a/setuptools/command/test.py +++ b/setuptools/command/test.py @@ -67,7 +67,7 @@ class test(Command): user_options = [ ('test-module=', 'm', "Run 'test_suite' in specified module"), ('test-suite=', 's', - "Test suite to run (e.g. 'some_module.test_suite')"), + "Run single test, case or suite (e.g. 'module.test_suite')"), ('test-runner=', 'r', "Test runner to use"), ] -- cgit v1.2.1 From 7474f891cd5f62b0ef2af286096b47f8497e5d0d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 21 May 2017 11:08:53 -0400 Subject: Remove extraneous whitespace and empty comment --- setuptools/sandbox.py | 3 --- 1 file changed, 3 deletions(-) (limited to 'setuptools') diff --git a/setuptools/sandbox.py b/setuptools/sandbox.py index 41c1c3b1..9c4ff336 100755 --- a/setuptools/sandbox.py +++ b/setuptools/sandbox.py @@ -486,6 +486,3 @@ This package cannot be safely installed by EasyInstall, and may not support alternate installation locations even if you run its setup script by hand. Please inform the package's author and the EasyInstall maintainers to find out if a fix or workaround is available.""" % self.args - - -# -- cgit v1.2.1 From d6959fca54137c47abb9c3f7a0cbd1d0fd3704d3 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 21 May 2017 11:12:01 -0400 Subject: Use dedent and left strip to store the template inside the class. --- setuptools/sandbox.py | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) (limited to 'setuptools') diff --git a/setuptools/sandbox.py b/setuptools/sandbox.py index 9c4ff336..4711fec2 100755 --- a/setuptools/sandbox.py +++ b/setuptools/sandbox.py @@ -7,6 +7,7 @@ import itertools import re import contextlib import pickle +import textwrap import six from six.moves import builtins, map @@ -476,13 +477,17 @@ WRITE_FLAGS = functools.reduce( class SandboxViolation(DistutilsError): """A setup script attempted to modify the filesystem outside the sandbox""" - def __str__(self): - return """SandboxViolation: %s%r %s + tmpl = textwrap.dedent(""" + SandboxViolation: %s%r %s + + The package setup script has attempted to modify files on your system + that are not within the EasyInstall build area, and has been aborted. -The package setup script has attempted to modify files on your system -that are not within the EasyInstall build area, and has been aborted. + This package cannot be safely installed by EasyInstall, and may not + support alternate installation locations even if you run its setup + script by hand. Please inform the package's author and the EasyInstall + maintainers to find out if a fix or workaround is available. + """).lstrip() -This package cannot be safely installed by EasyInstall, and may not -support alternate installation locations even if you run its setup -script by hand. Please inform the package's author and the EasyInstall -maintainers to find out if a fix or workaround is available.""" % self.args + def __str__(self): + return self.tmpl % self.args -- cgit v1.2.1 From c96c3ffba9f8a6ab43e6f3d57349a46e3076c374 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 21 May 2017 11:30:58 -0400 Subject: Expand test to cover string rendering of SandboxViolation --- setuptools/tests/test_sandbox.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'setuptools') diff --git a/setuptools/tests/test_sandbox.py b/setuptools/tests/test_sandbox.py index 929f0a5b..2b3d859e 100644 --- a/setuptools/tests/test_sandbox.py +++ b/setuptools/tests/test_sandbox.py @@ -126,3 +126,7 @@ class TestExceptionSaver: assert cmd == 'open' assert args == ('/etc/foo', 'w') assert kwargs == {} + + msg = str(caught.value) + assert 'open' in msg + assert "('/etc/foo', 'w')" in msg -- cgit v1.2.1 From d48cb39b4666477da1d954d3604023095c233869 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 21 May 2017 11:31:55 -0400 Subject: Use new style format strings and expand args to variables for better clarity of purpose. --- setuptools/sandbox.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'setuptools') diff --git a/setuptools/sandbox.py b/setuptools/sandbox.py index 4711fec2..53964f4b 100755 --- a/setuptools/sandbox.py +++ b/setuptools/sandbox.py @@ -478,7 +478,7 @@ class SandboxViolation(DistutilsError): """A setup script attempted to modify the filesystem outside the sandbox""" tmpl = textwrap.dedent(""" - SandboxViolation: %s%r %s + SandboxViolation: {cmd}{args!r} {kwargs} The package setup script has attempted to modify files on your system that are not within the EasyInstall build area, and has been aborted. @@ -490,4 +490,5 @@ class SandboxViolation(DistutilsError): """).lstrip() def __str__(self): - return self.tmpl % self.args + cmd, args, kwargs = self.args + return self.tmpl.format(**locals()) -- cgit v1.2.1 From 20567bef2a36b259d2209cc76fd11a61ad288853 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 21 May 2017 11:42:04 -0400 Subject: Implement AbstractSandbox as a context manager. --- setuptools/sandbox.py | 31 ++++++++++++++++--------------- setuptools/tests/test_sandbox.py | 4 ++-- 2 files changed, 18 insertions(+), 17 deletions(-) (limited to 'setuptools') diff --git a/setuptools/sandbox.py b/setuptools/sandbox.py index 53964f4b..14f18d74 100755 --- a/setuptools/sandbox.py +++ b/setuptools/sandbox.py @@ -249,11 +249,9 @@ def run_setup(setup_script, args): setup_script.encode(sys.getfilesystemencoding()) ) - def runner(): + with DirectorySandbox(setup_dir): ns = dict(__file__=dunder_file, __name__='__main__') _execfile(setup_script, ns) - - DirectorySandbox(setup_dir).run(runner) except SystemExit as v: if v.args and v.args[0]: raise @@ -275,21 +273,24 @@ class AbstractSandbox: for name in self._attrs: setattr(os, name, getattr(source, name)) + def __enter__(self): + self._copy(self) + if _file: + builtins.file = self._file + builtins.open = self._open + self._active = True + + def __exit__(self, exc_type, exc_value, traceback): + self._active = False + if _file: + builtins.file = _file + builtins.open = _open + self._copy(_os) + def run(self, func): """Run 'func' under os sandboxing""" - try: - self._copy(self) - if _file: - builtins.file = self._file - builtins.open = self._open - self._active = True + with self: return func() - finally: - self._active = False - if _file: - builtins.file = _file - builtins.open = _open - self._copy(_os) def _mk_dual_path_wrapper(name): original = getattr(_os, name) diff --git a/setuptools/tests/test_sandbox.py b/setuptools/tests/test_sandbox.py index 2b3d859e..6b0400f3 100644 --- a/setuptools/tests/test_sandbox.py +++ b/setuptools/tests/test_sandbox.py @@ -12,8 +12,8 @@ from setuptools.sandbox import DirectorySandbox class TestSandbox: def test_devnull(self, tmpdir): - sandbox = DirectorySandbox(str(tmpdir)) - sandbox.run(self._file_writer(os.devnull)) + with DirectorySandbox(str(tmpdir)): + self._file_writer(os.devnull) @staticmethod def _file_writer(path): -- cgit v1.2.1 From 8d3ed39696c5932a48b5f4a3768caa9f9e8c54b8 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 21 May 2017 11:47:44 -0400 Subject: Just use class in its namespace --- setuptools/tests/test_sandbox.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'setuptools') diff --git a/setuptools/tests/test_sandbox.py b/setuptools/tests/test_sandbox.py index 6b0400f3..a3f1206d 100644 --- a/setuptools/tests/test_sandbox.py +++ b/setuptools/tests/test_sandbox.py @@ -7,12 +7,11 @@ import pytest import pkg_resources import setuptools.sandbox -from setuptools.sandbox import DirectorySandbox class TestSandbox: def test_devnull(self, tmpdir): - with DirectorySandbox(str(tmpdir)): + with setuptools.sandbox.DirectorySandbox(str(tmpdir)): self._file_writer(os.devnull) @staticmethod @@ -116,11 +115,11 @@ class TestExceptionSaver: with open('/etc/foo', 'w'): pass - sandbox = DirectorySandbox(str(tmpdir)) with pytest.raises(setuptools.sandbox.SandboxViolation) as caught: with setuptools.sandbox.save_modules(): setuptools.sandbox.hide_setuptools() - sandbox.run(write_file) + with setuptools.sandbox.DirectorySandbox(str(tmpdir)): + write_file() cmd, args, kwargs = caught.value.args assert cmd == 'open' -- cgit v1.2.1 From 1cebea7e885c357d8f64a021bec4316b7993c50c Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Wed, 31 May 2017 21:29:05 -0400 Subject: fixed #1042 -- corrected an import --- setuptools/py27compat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'setuptools') diff --git a/setuptools/py27compat.py b/setuptools/py27compat.py index 701283c8..2985011b 100644 --- a/setuptools/py27compat.py +++ b/setuptools/py27compat.py @@ -4,7 +4,7 @@ Compatibility Support for Python 2.7 and earlier import platform -import six +from setuptools.extern import six def get_all_headers(message, key): -- cgit v1.2.1 From 995d309317c6895a123c03df28bc8f51f6ead5f5 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 6 Jun 2017 17:04:45 -0400 Subject: Limit the scope of deprecation of the upload_docs command. --- setuptools/command/upload_docs.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'setuptools') diff --git a/setuptools/command/upload_docs.py b/setuptools/command/upload_docs.py index 24a017cf..07aa564a 100644 --- a/setuptools/command/upload_docs.py +++ b/setuptools/command/upload_docs.py @@ -57,7 +57,6 @@ class upload_docs(upload): self.target_dir = None def finalize_options(self): - log.warn("Upload_docs command is deprecated. Use RTD instead.") upload.finalize_options(self) if self.upload_dir is None: if self.has_sphinx(): @@ -69,6 +68,8 @@ class upload_docs(upload): else: self.ensure_dirname('upload_dir') self.target_dir = self.upload_dir + if 'pypi.python.org' in self.repository: + log.warn("Upload_docs command is deprecated. Use RTD instead.") self.announce('Using upload directory %s' % self.target_dir) def create_zipfile(self, filename): -- cgit v1.2.1 From 04a306fa080e8a71f94ea5198b507c501c621cb6 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 13 Jul 2017 14:13:54 -0400 Subject: Use makedirs with future compatibility throughout setuptools. Ref #1083. --- setuptools/command/easy_install.py | 5 ++--- setuptools/sandbox.py | 6 +++--- setuptools/tests/files.py | 6 ++++-- setuptools/tests/test_manifest.py | 4 ++-- 4 files changed, 11 insertions(+), 10 deletions(-) (limited to 'setuptools') diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py index e319f77c..8fba7b41 100755 --- a/setuptools/command/easy_install.py +++ b/setuptools/command/easy_install.py @@ -59,7 +59,7 @@ from pkg_resources import ( Distribution, PathMetadata, EggMetadata, WorkingSet, DistributionNotFound, VersionConflict, DEVELOP_DIST, ) -import pkg_resources +import pkg_resources.py31compat # Turn on PEP440Warnings warnings.filterwarnings("default", category=pkg_resources.PEP440Warning) @@ -544,8 +544,7 @@ class easy_install(Command): if ok_exists: os.unlink(ok_file) dirname = os.path.dirname(ok_file) - if not os.path.exists(dirname): - os.makedirs(dirname) + pkg_resources.py31compat.makedirs(dirname, exist_ok=True) f = open(pth_file, 'w') except (OSError, IOError): self.cant_write_to_target() diff --git a/setuptools/sandbox.py b/setuptools/sandbox.py index f99c13c4..1d981f49 100755 --- a/setuptools/sandbox.py +++ b/setuptools/sandbox.py @@ -12,7 +12,7 @@ import textwrap from setuptools.extern import six from setuptools.extern.six.moves import builtins, map -import pkg_resources +import pkg_resources.py31compat if sys.platform.startswith('java'): import org.python.modules.posix.PosixModule as _os @@ -26,6 +26,7 @@ _open = open from distutils.errors import DistutilsError from pkg_resources import working_set + __all__ = [ "AbstractSandbox", "DirectorySandbox", "SandboxViolation", "run_setup", ] @@ -73,8 +74,7 @@ def override_temp(replacement): """ Monkey-patch tempfile.tempdir with replacement, ensuring it exists """ - if not os.path.isdir(replacement): - os.makedirs(replacement) + pkg_resources.py31compat.makedirs(replacement, exist_ok=True) saved = tempfile.tempdir diff --git a/setuptools/tests/files.py b/setuptools/tests/files.py index 4364241b..98de9fc3 100644 --- a/setuptools/tests/files.py +++ b/setuptools/tests/files.py @@ -1,6 +1,9 @@ import os +import pkg_resources.py31compat + + def build_files(file_defs, prefix=""): """ Build a set of files/directories, as described by the file_defs dictionary. @@ -24,8 +27,7 @@ def build_files(file_defs, prefix=""): for name, contents in file_defs.items(): full_name = os.path.join(prefix, name) if isinstance(contents, dict): - if not os.path.exists(full_name): - os.makedirs(full_name) + pkg_resources.py31compat.makedirs(full_name, exist_ok=True) build_files(contents, prefix=full_name) else: with open(full_name, 'w') as f: diff --git a/setuptools/tests/test_manifest.py b/setuptools/tests/test_manifest.py index ab9b3469..65eec7d9 100644 --- a/setuptools/tests/test_manifest.py +++ b/setuptools/tests/test_manifest.py @@ -10,6 +10,7 @@ import itertools from distutils import log from distutils.errors import DistutilsTemplateError +import pkg_resources.py31compat from setuptools.command.egg_info import FileList, egg_info, translate_pattern from setuptools.dist import Distribution from setuptools.extern import six @@ -361,8 +362,7 @@ class TestFileListTest(TempDirTestCase): for file in files: file = os.path.join(self.temp_dir, file) dirname, basename = os.path.split(file) - if not os.path.exists(dirname): - os.makedirs(dirname) + pkg_resources.py31compat.makedirs(dirname, exist_ok=True) open(file, 'w').close() def test_process_template_line(self): -- cgit v1.2.1 From e992c1ae2eba0638f5d463da78acf476ec2c04f8 Mon Sep 17 00:00:00 2001 From: Benoit Pierre Date: Thu, 15 Jun 2017 09:45:36 +0200 Subject: tests: fix `fail_on_ascii` fixture In my environment, with: - LANGUAGE=en_US:en_GB:en:C - LC_ALL=en_US.UTF-8 Running the testsuite with: - python3.6 -m pytest: is successful - tox -e py36: fails The later because LC_ALL is unset by tox, and LANGUAGE is not passed through, so `locale.getpreferredencoding()` returns 'ANSI_X3.4-1968'. --- setuptools/tests/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'setuptools') diff --git a/setuptools/tests/__init__.py b/setuptools/tests/__init__.py index dbf16201..8ae4402d 100644 --- a/setuptools/tests/__init__.py +++ b/setuptools/tests/__init__.py @@ -1,4 +1,5 @@ """Tests for the 'setuptools' package""" +import locale import sys import os import distutils.core @@ -16,8 +17,7 @@ import setuptools.depends as dep from setuptools import Feature from setuptools.depends import Require -c_type = os.environ.get("LC_CTYPE", os.environ.get("LC_ALL")) -is_ascii = c_type in ("C", "POSIX") +is_ascii = locale.getpreferredencoding() == 'ANSI_X3.4-1968' fail_on_ascii = pytest.mark.xfail(is_ascii, reason="Test fails in this locale") -- cgit v1.2.1 From 462610859692843e3bb6162befe978f14244eada Mon Sep 17 00:00:00 2001 From: Benoit Pierre Date: Thu, 15 Jun 2017 10:03:16 +0200 Subject: tests: mark test_unicode_filename_in_sdist with fail_on_ascii --- setuptools/tests/test_easy_install.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'setuptools') diff --git a/setuptools/tests/test_easy_install.py b/setuptools/tests/test_easy_install.py index 2d9682a9..1b7178a1 100644 --- a/setuptools/tests/test_easy_install.py +++ b/setuptools/tests/test_easy_install.py @@ -30,6 +30,7 @@ from setuptools.dist import Distribution from pkg_resources import normalize_path, working_set from pkg_resources import Distribution as PRDistribution import setuptools.tests.server +from setuptools.tests import fail_on_ascii import pkg_resources from .py26compat import tarfile_open @@ -166,6 +167,7 @@ class TestEasyInstallTest: sdist_zip.close() return str(sdist) + @fail_on_ascii def test_unicode_filename_in_sdist(self, sdist_unicode, tmpdir, monkeypatch): """ The install command should execute correctly even if -- cgit v1.2.1 From 050808db513e54c12190d64d5ba0a1f6e8ad9590 Mon Sep 17 00:00:00 2001 From: Benoit Pierre Date: Mon, 10 Jul 2017 02:54:29 +0200 Subject: fix handling of environment markers in `install_requires` --- setuptools/dist.py | 34 +++++++++++++++++++++++++++++++++- setuptools/tests/test_egg_info.py | 15 ++++++++++----- 2 files changed, 43 insertions(+), 6 deletions(-) (limited to 'setuptools') diff --git a/setuptools/dist.py b/setuptools/dist.py index 6b97ed33..cb887ea6 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -8,6 +8,7 @@ import distutils.log import distutils.core import distutils.cmd import distutils.dist +from collections import defaultdict from distutils.errors import (DistutilsOptionError, DistutilsPlatformError, DistutilsSetupError) from distutils.util import rfc822_escape @@ -134,7 +135,13 @@ def check_extras(dist, attr, value): k, m = k.split(':', 1) if pkg_resources.invalid_marker(m): raise DistutilsSetupError("Invalid environment marker: " + m) - list(pkg_resources.parse_requirements(v)) + for r in pkg_resources.parse_requirements(v): + if r.marker: + tmpl = ( + "'extras_require' requirements cannot include " + "environment markers, in {section!r}: '{req!s}'" + ) + raise DistutilsSetupError(tmpl.format(section=k, req=r)) except (TypeError, ValueError, AttributeError): raise DistutilsSetupError( "'extras_require' must be a dictionary whose values are " @@ -346,6 +353,31 @@ class Distribution(Distribution_parse_config_files, _Distribution): ) if getattr(self, 'python_requires', None): self.metadata.python_requires = self.python_requires + self._finalize_requires() + + def _finalize_requires(self): + """Move requirements in `install_requires` that + are using environment markers to `extras_require`. + """ + if not self.install_requires: + return + extras_require = defaultdict(list, ( + (k, list(pkg_resources.parse_requirements(v))) + for k, v in (self.extras_require or {}).items() + )) + install_requires = [] + for r in pkg_resources.parse_requirements(self.install_requires): + marker = r.marker + if not marker: + install_requires.append(r) + continue + r.marker = None + extras_require[':'+str(marker)].append(r) + self.extras_require = dict( + (k, [str(r) for r in v]) + for k, v in extras_require.items() + ) + self.install_requires = [str(r) for r in install_requires] def parse_config_files(self, filenames=None): """Parses configuration files from various levels diff --git a/setuptools/tests/test_egg_info.py b/setuptools/tests/test_egg_info.py index a32b981d..55df4489 100644 --- a/setuptools/tests/test_egg_info.py +++ b/setuptools/tests/test_egg_info.py @@ -185,8 +185,12 @@ class TestEggInfo(object): 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') + with open(requires_txt) as fp: + install_requires = fp.read() + assert install_requires.lstrip() == DALS(''' + [:python_version < "2"] + barbazquux + ''') assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == [] def test_setup_requires_with_markers(self, tmpdir_cwd, env): @@ -202,10 +206,11 @@ class TestEggInfo(object): 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): + def test_extras_require_with_markers(self, tmpdir_cwd, env): self._setup_script_with_requires( - """extra_requires={":python_version<'2'": ["barbazquux"]},""") - self._run_install_command(tmpdir_cwd, env) + """extras_require={"extra": ["barbazquux; python_version<'2'"]},""") + with pytest.raises(AssertionError): + self._run_install_command(tmpdir_cwd, env) assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == [] def test_python_requires_egg_info(self, tmpdir_cwd, env): -- cgit v1.2.1 From e82eadd1ff76b9aa3d5a8e472f48c054a296d8fe Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 13 Jul 2017 20:58:19 -0400 Subject: Restore test that includes an environment marker. --- setuptools/tests/test_egg_info.py | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'setuptools') diff --git a/setuptools/tests/test_egg_info.py b/setuptools/tests/test_egg_info.py index 55df4489..51648fff 100644 --- a/setuptools/tests/test_egg_info.py +++ b/setuptools/tests/test_egg_info.py @@ -207,6 +207,12 @@ class TestEggInfo(object): assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == [] def test_extras_require_with_markers(self, tmpdir_cwd, env): + self._setup_script_with_requires( + """extras_require={":python_version<'2'": ["barbazquux"]},""") + self._run_install_command(tmpdir_cwd, env) + assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == [] + + def test_extras_require_with_markers_in_req(self, tmpdir_cwd, env): self._setup_script_with_requires( """extras_require={"extra": ["barbazquux; python_version<'2'"]},""") with pytest.raises(AssertionError): -- cgit v1.2.1 From 44233b1b8cecc77001dec43a2d86e6955d529f82 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 13 Jul 2017 21:17:58 -0400 Subject: Extract the creation of the mismatch marker. --- setuptools/tests/test_egg_info.py | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) (limited to 'setuptools') diff --git a/setuptools/tests/test_egg_info.py b/setuptools/tests/test_egg_info.py index 51648fff..1376075c 100644 --- a/setuptools/tests/test_egg_info.py +++ b/setuptools/tests/test_egg_info.py @@ -179,45 +179,53 @@ class TestEggInfo(object): 'setup.py': setup_script, }) + mismatch_marker = "python_version<'{this_ver}'".format( + this_ver=sys.version_info[0], + ) + def test_install_requires_with_markers(self, tmpdir_cwd, env): - self._setup_script_with_requires( - """install_requires=["barbazquux;python_version<'2'"],""") + tmpl = 'install_requires=["barbazquux;{marker}"],' + req = tmpl.format(marker=self.mismatch_marker) + self._setup_script_with_requires(req) 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') with open(requires_txt) as fp: install_requires = fp.read() assert install_requires.lstrip() == DALS(''' - [:python_version < "2"] + [:python_version < "{sys.version_info[0]}"] barbazquux - ''') + ''').format(sys=sys) assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == [] def test_setup_requires_with_markers(self, tmpdir_cwd, env): - self._setup_script_with_requires( - """setup_requires=["barbazquux;python_version<'2'"],""") + tmpl = 'setup_requires=["barbazquux;{marker}"],' + req = tmpl.format(marker=self.mismatch_marker) + self._setup_script_with_requires(req) self._run_install_command(tmpdir_cwd, env) assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == [] def test_tests_require_with_markers(self, tmpdir_cwd, env): - self._setup_script_with_requires( - """tests_require=["barbazquux;python_version<'2'"],""") + tmpl = 'tests_require=["barbazquux;{marker}"],' + req = tmpl.format(marker=self.mismatch_marker) + self._setup_script_with_requires(req) 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_extras_require_with_markers(self, tmpdir_cwd, env): - self._setup_script_with_requires( - """extras_require={":python_version<'2'": ["barbazquux"]},""") + tmpl = 'extras_require={{":{marker}": ["barbazquux"]}},' + req = tmpl.format(marker=self.mismatch_marker) + self._setup_script_with_requires(req) self._run_install_command(tmpdir_cwd, env) assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == [] def test_extras_require_with_markers_in_req(self, tmpdir_cwd, env): - self._setup_script_with_requires( - """extras_require={"extra": ["barbazquux; python_version<'2'"]},""") + tmpl = 'extras_require={{"extra": ["barbazquux; {marker}"]}},' + req = tmpl.format(marker=self.mismatch_marker) + self._setup_script_with_requires(req) with pytest.raises(AssertionError): self._run_install_command(tmpdir_cwd, env) - assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == [] def test_python_requires_egg_info(self, tmpdir_cwd, env): self._setup_script_with_requires( -- cgit v1.2.1 From 295dbf3043da95165683991d71c187c875daa600 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 13 Jul 2017 21:20:02 -0400 Subject: extract variable for expected_requires. --- setuptools/tests/test_egg_info.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'setuptools') diff --git a/setuptools/tests/test_egg_info.py b/setuptools/tests/test_egg_info.py index 1376075c..6358abf0 100644 --- a/setuptools/tests/test_egg_info.py +++ b/setuptools/tests/test_egg_info.py @@ -192,10 +192,11 @@ class TestEggInfo(object): requires_txt = os.path.join(egg_info_dir, 'requires.txt') with open(requires_txt) as fp: install_requires = fp.read() - assert install_requires.lstrip() == DALS(''' - [:python_version < "{sys.version_info[0]}"] - barbazquux - ''').format(sys=sys) + expected_requires = DALS(''' + [:python_version < "{sys.version_info[0]}"] + barbazquux + ''').format(sys=sys) + assert install_requires.lstrip() == expected_requires assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == [] def test_setup_requires_with_markers(self, tmpdir_cwd, env): -- cgit v1.2.1 From 3cf29ecb38271ae0512273d561adebd03df023b9 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 13 Jul 2017 21:27:43 -0400 Subject: Extract _check_extra function --- setuptools/dist.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'setuptools') diff --git a/setuptools/dist.py b/setuptools/dist.py index cb887ea6..c7f6d371 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -8,6 +8,7 @@ import distutils.log import distutils.core import distutils.cmd import distutils.dist +import itertools from collections import defaultdict from distutils.errors import (DistutilsOptionError, DistutilsPlatformError, DistutilsSetupError) @@ -130,7 +131,16 @@ def check_nsp(dist, attr, value): def check_extras(dist, attr, value): """Verify that extras_require mapping is valid""" try: - for k, v in value.items(): + list(itertools.starmap(_check_extra, value.items())) + except (TypeError, ValueError, AttributeError): + raise DistutilsSetupError( + "'extras_require' must be a dictionary whose values are " + "strings or lists of strings containing valid project/version " + "requirement specifiers." + ) + + +def _check_extra(k, v): if ':' in k: k, m = k.split(':', 1) if pkg_resources.invalid_marker(m): @@ -142,12 +152,6 @@ def check_extras(dist, attr, value): "environment markers, in {section!r}: '{req!s}'" ) raise DistutilsSetupError(tmpl.format(section=k, req=r)) - except (TypeError, ValueError, AttributeError): - raise DistutilsSetupError( - "'extras_require' must be a dictionary whose values are " - "strings or lists of strings containing valid project/version " - "requirement specifiers." - ) def assert_bool(dist, attr, value): -- cgit v1.2.1 From 8c00a37662c6f2ff3814d75d5569c02f83e411a1 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 13 Jul 2017 21:28:13 -0400 Subject: Reindent --- setuptools/dist.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) (limited to 'setuptools') diff --git a/setuptools/dist.py b/setuptools/dist.py index c7f6d371..1a475ed0 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -141,17 +141,18 @@ def check_extras(dist, attr, value): def _check_extra(k, v): - if ':' in k: - k, m = k.split(':', 1) - if pkg_resources.invalid_marker(m): - raise DistutilsSetupError("Invalid environment marker: " + m) - for r in pkg_resources.parse_requirements(v): - if r.marker: - tmpl = ( - "'extras_require' requirements cannot include " - "environment markers, in {section!r}: '{req!s}'" - ) - raise DistutilsSetupError(tmpl.format(section=k, req=r)) + if ':' in k: + k, m = k.split(':', 1) + if pkg_resources.invalid_marker(m): + raise DistutilsSetupError("Invalid environment marker: " + m) + + for r in pkg_resources.parse_requirements(v): + if r.marker: + tmpl = ( + "'extras_require' requirements cannot include " + "environment markers, in {section!r}: '{req!s}'" + ) + raise DistutilsSetupError(tmpl.format(section=k, req=r)) def assert_bool(dist, attr, value): -- cgit v1.2.1 From dfe339ec3b1c2d0f463ed39f86c34c01ff8faedc Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 13 Jul 2017 21:31:31 -0400 Subject: Use better variable names and the partition method for simplicity. --- setuptools/dist.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'setuptools') diff --git a/setuptools/dist.py b/setuptools/dist.py index 1a475ed0..5adbfd4e 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -140,19 +140,18 @@ def check_extras(dist, attr, value): ) -def _check_extra(k, v): - if ':' in k: - k, m = k.split(':', 1) - if pkg_resources.invalid_marker(m): - raise DistutilsSetupError("Invalid environment marker: " + m) +def _check_extra(extra, reqs): + name, sep, marker = extra.partition(':') + if marker and pkg_resources.invalid_marker(marker): + raise DistutilsSetupError("Invalid environment marker: " + marker) - for r in pkg_resources.parse_requirements(v): + for r in pkg_resources.parse_requirements(reqs): if r.marker: tmpl = ( "'extras_require' requirements cannot include " "environment markers, in {section!r}: '{req!s}'" ) - raise DistutilsSetupError(tmpl.format(section=k, req=r)) + raise DistutilsSetupError(tmpl.format(section=name, req=r)) def assert_bool(dist, attr, value): -- cgit v1.2.1 From 1bbb027f369fb7dbf58a939bca57a9c8e9ecf8c7 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 13 Jul 2017 21:43:49 -0400 Subject: Use filter and next to directly extract a single failure. --- setuptools/dist.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) (limited to 'setuptools') diff --git a/setuptools/dist.py b/setuptools/dist.py index 5adbfd4e..619318f3 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -9,13 +9,14 @@ import distutils.core import distutils.cmd import distutils.dist import itertools +import operator from collections import defaultdict from distutils.errors import (DistutilsOptionError, DistutilsPlatformError, DistutilsSetupError) from distutils.util import rfc822_escape from setuptools.extern import six -from setuptools.extern.six.moves import map +from setuptools.extern.six.moves import map, filter from pkg_resources.extern import packaging __import__('pkg_resources.extern.packaging.specifiers') @@ -145,13 +146,16 @@ def _check_extra(extra, reqs): if marker and pkg_resources.invalid_marker(marker): raise DistutilsSetupError("Invalid environment marker: " + marker) - for r in pkg_resources.parse_requirements(reqs): - if r.marker: - tmpl = ( - "'extras_require' requirements cannot include " - "environment markers, in {section!r}: '{req!s}'" - ) - raise DistutilsSetupError(tmpl.format(section=name, req=r)) + # extras requirements cannot themselves have markers + parsed = pkg_resources.parse_requirements(reqs) + marked_reqs = filter(operator.attrgetter('marker'), parsed) + bad_req = next(marked_reqs, None) + if bad_req: + tmpl = ( + "'extras_require' requirements cannot include " + "environment markers, in {name!r}: '{bad_req!s}'" + ) + raise DistutilsSetupError(tmpl.format(**locals())) def assert_bool(dist, attr, value): -- cgit v1.2.1 From 986a8bce395c1a5b9e41a547d02eb09dd432e93e Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 13 Jul 2017 21:51:56 -0400 Subject: Delint --- setuptools/dist.py | 51 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 20 deletions(-) (limited to 'setuptools') diff --git a/setuptools/dist.py b/setuptools/dist.py index 619318f3..68c8747a 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -11,17 +11,15 @@ import distutils.dist import itertools import operator from collections import defaultdict -from distutils.errors import (DistutilsOptionError, DistutilsPlatformError, - DistutilsSetupError) +from distutils.errors import ( + DistutilsOptionError, DistutilsPlatformError, DistutilsSetupError, +) from distutils.util import rfc822_escape from setuptools.extern import six from setuptools.extern.six.moves import map, filter from pkg_resources.extern import packaging -__import__('pkg_resources.extern.packaging.specifiers') -__import__('pkg_resources.extern.packaging.version') - from setuptools.depends import Require from setuptools import windows_support from setuptools.monkey import get_unpatched @@ -29,6 +27,9 @@ from setuptools.config import parse_configuration import pkg_resources from .py36compat import Distribution_parse_config_files +__import__('pkg_resources.extern.packaging.specifiers') +__import__('pkg_resources.extern.packaging.version') + def _get_unpatched(cls): warnings.warn("Do not call this function", DeprecationWarning) @@ -364,7 +365,8 @@ class Distribution(Distribution_parse_config_files, _Distribution): self._finalize_requires() def _finalize_requires(self): - """Move requirements in `install_requires` that + """ + Move requirements in `install_requires` that are using environment markers to `extras_require`. """ if not self.install_requires: @@ -380,7 +382,7 @@ class Distribution(Distribution_parse_config_files, _Distribution): install_requires.append(r) continue r.marker = None - extras_require[':'+str(marker)].append(r) + extras_require[':' + str(marker)].append(r) self.extras_require = dict( (k, [str(r) for r in v]) for k, v in extras_require.items() @@ -432,7 +434,10 @@ class Distribution(Distribution_parse_config_files, _Distribution): ep.load()(self, ep.name, value) if getattr(self, 'convert_2to3_doctests', None): # XXX may convert to set here when we can rely on set being builtin - self.convert_2to3_doctests = [os.path.abspath(p) for p in self.convert_2to3_doctests] + self.convert_2to3_doctests = [ + os.path.abspath(p) + for p in self.convert_2to3_doctests + ] else: self.convert_2to3_doctests = [] @@ -476,7 +481,8 @@ class Distribution(Distribution_parse_config_files, _Distribution): opts['find_links'] = ('setup', links) install_dir = self.get_egg_cache_dir() cmd = easy_install( - dist, args=["x"], install_dir=install_dir, exclude_scripts=True, + dist, args=["x"], install_dir=install_dir, + exclude_scripts=True, always_copy=False, build_directory=None, editable=False, upgrade=False, multi_version=True, no_report=True, user=False ) @@ -501,8 +507,11 @@ class Distribution(Distribution_parse_config_files, _Distribution): if not feature.include_by_default(): excdef, incdef = incdef, excdef - go.append(('with-' + name, None, 'include ' + descr + incdef)) - go.append(('without-' + name, None, 'exclude ' + descr + excdef)) + new = ( + ('with-' + name, None, 'include ' + descr + incdef), + ('without-' + name, None, 'exclude ' + descr + excdef), + ) + go.extend(new) no['without-' + name] = 'with-' + name self.global_options = self.feature_options = go + self.global_options @@ -530,7 +539,8 @@ class Distribution(Distribution_parse_config_files, _Distribution): if command in self.cmdclass: return self.cmdclass[command] - for ep in pkg_resources.iter_entry_points('distutils.commands', command): + eps = pkg_resources.iter_entry_points('distutils.commands', command) + for ep in eps: ep.require(installer=self.fetch_build_egg) self.cmdclass[command] = cmdclass = ep.load() return cmdclass @@ -664,7 +674,8 @@ class Distribution(Distribution_parse_config_files, _Distribution): name + ": this setting cannot be changed via include/exclude" ) else: - setattr(self, name, old + [item for item in value if item not in old]) + new = [item for item in value if item not in old] + setattr(self, name, old + new) def exclude(self, **attrs): """Remove items from distribution that are named in keyword arguments @@ -875,14 +886,14 @@ class Feature: @staticmethod def warn_deprecated(): - warnings.warn( + msg = ( "Features are deprecated and will be removed in a future " - "version. See https://github.com/pypa/setuptools/issues/65.", - DeprecationWarning, - stacklevel=3, + "version. See https://github.com/pypa/setuptools/issues/65." ) + warnings.warn(msg, DeprecationWarning, stacklevel=3) - def __init__(self, description, standard=False, available=True, + def __init__( + self, description, standard=False, available=True, optional=True, require_features=(), remove=(), **extras): self.warn_deprecated() @@ -907,8 +918,8 @@ class Feature: if not remove and not require_features and not extras: raise DistutilsSetupError( - "Feature %s: must define 'require_features', 'remove', or at least one" - " of 'packages', 'py_modules', etc." + "Feature %s: must define 'require_features', 'remove', or " + "at least one of 'packages', 'py_modules', etc." ) def include_by_default(self): -- cgit v1.2.1 From 7257263ba6568ae2868a0eb96f4e36917fb607b1 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 13 Jul 2017 22:10:08 -0400 Subject: Delint --- setuptools/tests/test_egg_info.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'setuptools') diff --git a/setuptools/tests/test_egg_info.py b/setuptools/tests/test_egg_info.py index 6358abf0..07bd8818 100644 --- a/setuptools/tests/test_egg_info.py +++ b/setuptools/tests/test_egg_info.py @@ -64,7 +64,7 @@ class TestEggInfo(object): yield env dict_order_fails = pytest.mark.skipif( - sys.version_info < (2,7), + sys.version_info < (2, 7), reason="Intermittent failures on Python 2.6", ) -- cgit v1.2.1 From bf20d881df662da30d94687efb2ff3d3ba32f55a Mon Sep 17 00:00:00 2001 From: Benoit Pierre Date: Fri, 14 Jul 2017 08:49:23 +0200 Subject: tests: switch back to mock instead of backports.unittest_mock --- setuptools/tests/test_build_clib.py | 2 +- setuptools/tests/test_easy_install.py | 2 +- setuptools/tests/test_msvc.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'setuptools') diff --git a/setuptools/tests/test_build_clib.py b/setuptools/tests/test_build_clib.py index 7e3d1de9..aebcc350 100644 --- a/setuptools/tests/test_build_clib.py +++ b/setuptools/tests/test_build_clib.py @@ -2,7 +2,7 @@ import pytest import os import shutil -from unittest import mock +import mock from distutils.errors import DistutilsSetupError from setuptools.command.build_clib import build_clib from setuptools.dist import Distribution diff --git a/setuptools/tests/test_easy_install.py b/setuptools/tests/test_easy_install.py index 1b7178a1..26cdd90d 100644 --- a/setuptools/tests/test_easy_install.py +++ b/setuptools/tests/test_easy_install.py @@ -14,7 +14,7 @@ import itertools import distutils.errors import io import zipfile -from unittest import mock +import mock import time from setuptools.extern.six.moves import urllib diff --git a/setuptools/tests/test_msvc.py b/setuptools/tests/test_msvc.py index fbeed1d5..32d7a907 100644 --- a/setuptools/tests/test_msvc.py +++ b/setuptools/tests/test_msvc.py @@ -5,7 +5,7 @@ Tests for msvc support module. import os import contextlib import distutils.errors -from unittest import mock +import mock import pytest -- cgit v1.2.1 From 95386da92ec1725a09c5cd8e457be5ff3dc15a3e Mon Sep 17 00:00:00 2001 From: Benoit Pierre Date: Fri, 14 Jul 2017 08:58:14 +0200 Subject: tests: rework clean install test Use pytest-virtualenv so the test can be run no Windows too. --- setuptools/tests/test_virtualenv.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 setuptools/tests/test_virtualenv.py (limited to 'setuptools') diff --git a/setuptools/tests/test_virtualenv.py b/setuptools/tests/test_virtualenv.py new file mode 100644 index 00000000..eb8db7a6 --- /dev/null +++ b/setuptools/tests/test_virtualenv.py @@ -0,0 +1,31 @@ +import os + +from pytest import yield_fixture +from pytest_fixture_config import yield_requires_config + +import pytest_virtualenv + + +@yield_requires_config(pytest_virtualenv.CONFIG, ['virtualenv_executable']) +@yield_fixture(scope='function') +def bare_virtualenv(): + """ Bare virtualenv (no pip/setuptools/wheel). + """ + with pytest_virtualenv.VirtualEnv(args=( + '--no-wheel', + '--no-pip', + '--no-setuptools', + )) as venv: + yield venv + + +SOURCE_DIR = os.path.join(os.path.dirname(__file__), '../..') + +def test_clean_env_install(bare_virtualenv): + """ + Check setuptools can be installed in a clean environment. + """ + bare_virtualenv.run(' && '.join(( + 'cd {source}', + 'python setup.py install', + )).format(source=SOURCE_DIR)) -- cgit v1.2.1 From 7c2df64c8558ac71c20d86d3cb2a05daad99cc87 Mon Sep 17 00:00:00 2001 From: Benoit Pierre Date: Fri, 14 Jul 2017 05:44:35 +0200 Subject: fix possible error when finalizing `install_requires` --- setuptools/dist.py | 5 +++-- setuptools/tests/test_virtualenv.py | 19 +++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) (limited to 'setuptools') diff --git a/setuptools/dist.py b/setuptools/dist.py index 68c8747a..1bd89ddd 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -369,11 +369,12 @@ class Distribution(Distribution_parse_config_files, _Distribution): Move requirements in `install_requires` that are using environment markers to `extras_require`. """ - if not self.install_requires: + if not getattr(self, 'install_requires', None): return + extras_require = getattr(self, 'extras_require', None) extras_require = defaultdict(list, ( (k, list(pkg_resources.parse_requirements(v))) - for k, v in (self.extras_require or {}).items() + for k, v in (extras_require or {}).items() )) install_requires = [] for r in pkg_resources.parse_requirements(self.install_requires): diff --git a/setuptools/tests/test_virtualenv.py b/setuptools/tests/test_virtualenv.py index eb8db7a6..a7f485a4 100644 --- a/setuptools/tests/test_virtualenv.py +++ b/setuptools/tests/test_virtualenv.py @@ -1,3 +1,4 @@ +import glob import os from pytest import yield_fixture @@ -29,3 +30,21 @@ def test_clean_env_install(bare_virtualenv): 'cd {source}', 'python setup.py install', )).format(source=SOURCE_DIR)) + +def test_pip_upgrade_from_source(virtualenv): + """ + Check pip can upgrade setuptools from source. + """ + dist_dir = virtualenv.workspace + # Generate source distribution / wheel. + virtualenv.run(' && '.join(( + 'cd {source}', + 'python setup.py -q sdist -d {dist}', + 'python setup.py -q bdist_wheel -d {dist}', + )).format(source=SOURCE_DIR, dist=dist_dir)) + sdist = glob.glob(os.path.join(dist_dir, '*.zip'))[0] + wheel = glob.glob(os.path.join(dist_dir, '*.whl'))[0] + # Then update from wheel. + virtualenv.run('pip install ' + wheel) + # And finally try to upgrade from source. + virtualenv.run('pip install --no-cache-dir --upgrade ' + sdist) -- cgit v1.2.1 From 2328be3cc556076b91c8ec74da7b85b178dbc574 Mon Sep 17 00:00:00 2001 From: Benoit Pierre Date: Sat, 15 Jul 2017 03:32:57 +0200 Subject: fix `extras_require` handling Allow requirements of the form `"extra": ["barbazquux; {marker}"]` by internally converting them to `"extra:{marker}": ["barbazquux"]`. --- setuptools/dist.py | 46 +++++++++++++++++++------------------- setuptools/tests/test_egg_info.py | 47 +++++++++++++++++++++++++++++++++++---- 2 files changed, 66 insertions(+), 27 deletions(-) (limited to 'setuptools') diff --git a/setuptools/dist.py b/setuptools/dist.py index 1bd89ddd..d77d56b1 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -9,7 +9,6 @@ import distutils.core import distutils.cmd import distutils.dist import itertools -import operator from collections import defaultdict from distutils.errors import ( DistutilsOptionError, DistutilsPlatformError, DistutilsSetupError, @@ -17,7 +16,7 @@ from distutils.errors import ( from distutils.util import rfc822_escape from setuptools.extern import six -from setuptools.extern.six.moves import map, filter +from setuptools.extern.six.moves import map from pkg_resources.extern import packaging from setuptools.depends import Require @@ -146,17 +145,7 @@ def _check_extra(extra, reqs): name, sep, marker = extra.partition(':') if marker and pkg_resources.invalid_marker(marker): raise DistutilsSetupError("Invalid environment marker: " + marker) - - # extras requirements cannot themselves have markers - parsed = pkg_resources.parse_requirements(reqs) - marked_reqs = filter(operator.attrgetter('marker'), parsed) - bad_req = next(marked_reqs, None) - if bad_req: - tmpl = ( - "'extras_require' requirements cannot include " - "environment markers, in {name!r}: '{bad_req!s}'" - ) - raise DistutilsSetupError(tmpl.format(**locals())) + list(pkg_resources.parse_requirements(reqs)) def assert_bool(dist, attr, value): @@ -366,18 +355,29 @@ class Distribution(Distribution_parse_config_files, _Distribution): def _finalize_requires(self): """ - Move requirements in `install_requires` that - are using environment markers to `extras_require`. + Fix environment markers in `install_requires` and `extras_require`. + + - move requirements in `install_requires` that are using environment + markers to `extras_require`. + - convert requirements in `extras_require` of the form + `"extra": ["barbazquux; {marker}"]` to + `"extra:{marker}": ["barbazquux"]`. """ - if not getattr(self, 'install_requires', None): - return - extras_require = getattr(self, 'extras_require', None) - extras_require = defaultdict(list, ( - (k, list(pkg_resources.parse_requirements(v))) - for k, v in (extras_require or {}).items() - )) + extras_require = defaultdict(list) + for k, v in ( + getattr(self, 'extras_require', None) or {} + ).items(): + for r in pkg_resources.parse_requirements(v): + marker = r.marker + if marker: + r.marker = None + extras_require[k + ':' + str(marker)].append(r) + else: + extras_require[k].append(r) install_requires = [] - for r in pkg_resources.parse_requirements(self.install_requires): + for r in pkg_resources.parse_requirements( + getattr(self, 'install_requires', None) or () + ): marker = r.marker if not marker: install_requires.append(r) diff --git a/setuptools/tests/test_egg_info.py b/setuptools/tests/test_egg_info.py index 07bd8818..0b6f06b2 100644 --- a/setuptools/tests/test_egg_info.py +++ b/setuptools/tests/test_egg_info.py @@ -182,6 +182,11 @@ class TestEggInfo(object): mismatch_marker = "python_version<'{this_ver}'".format( this_ver=sys.version_info[0], ) + # Alternate equivalent syntax. + mismatch_marker_alternate = 'python_version < "{this_ver}"'.format( + this_ver=sys.version_info[0], + ) + invalid_marker = "<=>++" def test_install_requires_with_markers(self, tmpdir_cwd, env): tmpl = 'install_requires=["barbazquux;{marker}"],' @@ -193,9 +198,9 @@ class TestEggInfo(object): with open(requires_txt) as fp: install_requires = fp.read() expected_requires = DALS(''' - [:python_version < "{sys.version_info[0]}"] + [:{marker}] barbazquux - ''').format(sys=sys) + ''').format(marker=self.mismatch_marker_alternate) assert install_requires.lstrip() == expected_requires assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == [] @@ -214,19 +219,53 @@ class TestEggInfo(object): tmpdir_cwd, env, cmd=['test'], output="Ran 0 tests in") assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == [] - def test_extras_require_with_markers(self, tmpdir_cwd, env): + def test_extras_require_with_marker(self, tmpdir_cwd, env): tmpl = 'extras_require={{":{marker}": ["barbazquux"]}},' req = tmpl.format(marker=self.mismatch_marker) self._setup_script_with_requires(req) 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') + with open(requires_txt) as fp: + install_requires = fp.read() + expected_requires = DALS(''' + [:{marker}] + barbazquux + ''').format(marker=self.mismatch_marker) + assert install_requires.lstrip() == expected_requires assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == [] - def test_extras_require_with_markers_in_req(self, tmpdir_cwd, env): + def test_extras_require_with_marker_in_req(self, tmpdir_cwd, env): tmpl = 'extras_require={{"extra": ["barbazquux; {marker}"]}},' req = tmpl.format(marker=self.mismatch_marker) self._setup_script_with_requires(req) + 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') + with open(requires_txt) as fp: + install_requires = fp.read() + expected_requires = DALS(''' + [extra:{marker}] + barbazquux + ''').format(marker=self.mismatch_marker_alternate) + assert install_requires.lstrip() == expected_requires + assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == [] + + def test_extras_require_with_invalid_marker(self, tmpdir_cwd, env): + tmpl = 'extras_require={{":{marker}": ["barbazquux"]}},' + req = tmpl.format(marker=self.invalid_marker) + self._setup_script_with_requires(req) with pytest.raises(AssertionError): self._run_install_command(tmpdir_cwd, env) + assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == [] + + def test_extras_require_with_invalid_marker_in_req(self, tmpdir_cwd, env): + tmpl = 'extras_require={{"extra": ["barbazquux; {marker}"]}},' + req = tmpl.format(marker=self.invalid_marker) + self._setup_script_with_requires(req) + with pytest.raises(AssertionError): + self._run_install_command(tmpdir_cwd, env) + assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == [] def test_python_requires_egg_info(self, tmpdir_cwd, env): self._setup_script_with_requires( -- cgit v1.2.1 From a3ec721ec1e70f1f7aec6c3349ad85b446410809 Mon Sep 17 00:00:00 2001 From: Benoit Pierre Date: Fri, 14 Jul 2017 23:56:05 +0200 Subject: fix `install_requires` handling of extras Internally move requirements in `install_requires` that are using extras to `extras_require` so those extras don't get stripped when building wheels. --- setuptools/dist.py | 12 +++++++++--- setuptools/tests/test_egg_info.py | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 41 insertions(+), 4 deletions(-) (limited to 'setuptools') diff --git a/setuptools/dist.py b/setuptools/dist.py index d77d56b1..9a034db5 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -358,7 +358,7 @@ class Distribution(Distribution_parse_config_files, _Distribution): Fix environment markers in `install_requires` and `extras_require`. - move requirements in `install_requires` that are using environment - markers to `extras_require`. + markers or extras to `extras_require`. - convert requirements in `extras_require` of the form `"extra": ["barbazquux; {marker}"]` to `"extra:{marker}": ["barbazquux"]`. @@ -379,11 +379,17 @@ class Distribution(Distribution_parse_config_files, _Distribution): getattr(self, 'install_requires', None) or () ): marker = r.marker - if not marker: + extras = r.extras + if not marker and not extras: install_requires.append(r) continue + r.extras = () r.marker = None - extras_require[':' + str(marker)].append(r) + for e in extras or ('',): + section = e + if marker: + section += ':' + str(marker) + extras_require[section].append(r) self.extras_require = dict( (k, [str(r) for r in v]) for k, v in extras_require.items() diff --git a/setuptools/tests/test_egg_info.py b/setuptools/tests/test_egg_info.py index 0b6f06b2..5ea55d61 100644 --- a/setuptools/tests/test_egg_info.py +++ b/setuptools/tests/test_egg_info.py @@ -188,7 +188,7 @@ class TestEggInfo(object): ) invalid_marker = "<=>++" - def test_install_requires_with_markers(self, tmpdir_cwd, env): + def test_install_requires_with_marker(self, tmpdir_cwd, env): tmpl = 'install_requires=["barbazquux;{marker}"],' req = tmpl.format(marker=self.mismatch_marker) self._setup_script_with_requires(req) @@ -204,6 +204,37 @@ class TestEggInfo(object): assert install_requires.lstrip() == expected_requires assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == [] + def test_install_requires_with_extra(self, tmpdir_cwd, env): + req = 'install_requires=["barbazquux [test]"],' + self._setup_script_with_requires(req) + 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') + with open(requires_txt) as fp: + install_requires = fp.read() + expected_requires = DALS(''' + [test] + barbazquux + ''') + assert install_requires.lstrip() == expected_requires + assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == [] + + def test_install_requires_with_extra_and_marker(self, tmpdir_cwd, env): + tmpl = 'install_requires=["barbazquux [test]; {marker}"],' + req = tmpl.format(marker=self.mismatch_marker) + self._setup_script_with_requires(req) + 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') + with open(requires_txt) as fp: + install_requires = fp.read() + expected_requires = DALS(''' + [test:{marker}] + barbazquux + ''').format(marker=self.mismatch_marker_alternate) + assert install_requires.lstrip() == expected_requires + assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == [] + def test_setup_requires_with_markers(self, tmpdir_cwd, env): tmpl = 'setup_requires=["barbazquux;{marker}"],' req = tmpl.format(marker=self.mismatch_marker) -- cgit v1.2.1 From 4d0b492a7958292ed7df0a28b07198a79033e77e Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 23 Jul 2017 13:34:04 -0400 Subject: Extract variable for nicer indentation --- setuptools/dist.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'setuptools') diff --git a/setuptools/dist.py b/setuptools/dist.py index 9a034db5..602ba500 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -364,9 +364,8 @@ class Distribution(Distribution_parse_config_files, _Distribution): `"extra:{marker}": ["barbazquux"]`. """ extras_require = defaultdict(list) - for k, v in ( - getattr(self, 'extras_require', None) or {} - ).items(): + spec_ext_reqs = getattr(self, 'extras_require', None) or {} + for k, v in spec_ext_reqs.items(): for r in pkg_resources.parse_requirements(v): marker = r.marker if marker: @@ -375,9 +374,8 @@ class Distribution(Distribution_parse_config_files, _Distribution): else: extras_require[k].append(r) install_requires = [] - for r in pkg_resources.parse_requirements( - getattr(self, 'install_requires', None) or () - ): + spec_inst_reqs = getattr(self, 'install_requires', None) or () + for r in pkg_resources.parse_requirements(spec_inst_reqs): marker = r.marker extras = r.extras if not marker and not extras: -- cgit v1.2.1 From 3e6409381946eba193533c9a9a968af9be1231f2 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 23 Jul 2017 13:51:49 -0400 Subject: Consolidate assignment of extras to the key in extras requirements. --- setuptools/dist.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'setuptools') diff --git a/setuptools/dist.py b/setuptools/dist.py index 602ba500..2b720b53 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -367,12 +367,10 @@ class Distribution(Distribution_parse_config_files, _Distribution): spec_ext_reqs = getattr(self, 'extras_require', None) or {} for k, v in spec_ext_reqs.items(): for r in pkg_resources.parse_requirements(v): - marker = r.marker - if marker: + if r.marker: + k += ':' + str(r.marker) r.marker = None - extras_require[k + ':' + str(marker)].append(r) - else: - extras_require[k].append(r) + extras_require[k].append(r) install_requires = [] spec_inst_reqs = getattr(self, 'install_requires', None) or () for r in pkg_resources.parse_requirements(spec_inst_reqs): -- cgit v1.2.1 From 171fc767992f55f10186b6ded6ea2875328d0827 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 23 Jul 2017 14:10:33 -0400 Subject: Extract two methods (still interdependent) for fixing requires --- setuptools/dist.py | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) (limited to 'setuptools') diff --git a/setuptools/dist.py b/setuptools/dist.py index 2b720b53..ad50a8ef 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -356,21 +356,31 @@ class Distribution(Distribution_parse_config_files, _Distribution): def _finalize_requires(self): """ Fix environment markers in `install_requires` and `extras_require`. + """ + self._convert_extras_requirements() + self._move_install_requirements_markers() - - move requirements in `install_requires` that are using environment - markers or extras to `extras_require`. - - convert requirements in `extras_require` of the form - `"extra": ["barbazquux; {marker}"]` to - `"extra:{marker}": ["barbazquux"]`. + def _convert_extras_requirements(self): + """ + Convert requirements in `extras_require` of the form + `"extra": ["barbazquux; {marker}"]` to + `"extra:{marker}": ["barbazquux"]`. """ - extras_require = defaultdict(list) spec_ext_reqs = getattr(self, 'extras_require', None) or {} + self._tmp_extras_require = defaultdict(list) for k, v in spec_ext_reqs.items(): for r in pkg_resources.parse_requirements(v): if r.marker: k += ':' + str(r.marker) r.marker = None - extras_require[k].append(r) + self._tmp_extras_require[k].append(r) + + def _move_install_requirements_markers(self): + """ + Move requirements in `install_requires` that are using environment + markers or extras to `extras_require`. + """ + install_requires = [] spec_inst_reqs = getattr(self, 'install_requires', None) or () for r in pkg_resources.parse_requirements(spec_inst_reqs): @@ -385,10 +395,10 @@ class Distribution(Distribution_parse_config_files, _Distribution): section = e if marker: section += ':' + str(marker) - extras_require[section].append(r) + self._tmp_extras_require[section].append(r) self.extras_require = dict( (k, [str(r) for r in v]) - for k, v in extras_require.items() + for k, v in self._tmp_extras_require.items() ) self.install_requires = [str(r) for r in install_requires] -- cgit v1.2.1 From 81ffba7099ea754a970c2d857bcca2ac88358557 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 23 Jul 2017 14:50:07 -0400 Subject: Use term 'section' consistently --- setuptools/dist.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'setuptools') diff --git a/setuptools/dist.py b/setuptools/dist.py index ad50a8ef..ffa972f3 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -368,12 +368,12 @@ class Distribution(Distribution_parse_config_files, _Distribution): """ spec_ext_reqs = getattr(self, 'extras_require', None) or {} self._tmp_extras_require = defaultdict(list) - for k, v in spec_ext_reqs.items(): + for section, v in spec_ext_reqs.items(): for r in pkg_resources.parse_requirements(v): if r.marker: - k += ':' + str(r.marker) + section += ':' + str(r.marker) r.marker = None - self._tmp_extras_require[k].append(r) + self._tmp_extras_require[section].append(r) def _move_install_requirements_markers(self): """ @@ -391,8 +391,7 @@ class Distribution(Distribution_parse_config_files, _Distribution): continue r.extras = () r.marker = None - for e in extras or ('',): - section = e + for section in extras or ('',): if marker: section += ':' + str(marker) self._tmp_extras_require[section].append(r) -- cgit v1.2.1 From 00a738d4d0d4e84c240e32cebfb54fdc653de00a Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 23 Jul 2017 15:07:33 -0400 Subject: Handle rebuild of install_requires separate from building extras" --- setuptools/dist.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) (limited to 'setuptools') diff --git a/setuptools/dist.py b/setuptools/dist.py index ffa972f3..febb1b0c 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -380,15 +380,21 @@ class Distribution(Distribution_parse_config_files, _Distribution): Move requirements in `install_requires` that are using environment markers or extras to `extras_require`. """ - - install_requires = [] spec_inst_reqs = getattr(self, 'install_requires', None) or () - for r in pkg_resources.parse_requirements(spec_inst_reqs): + self.install_requires = list( + str(req) + for req in pkg_resources.parse_requirements(spec_inst_reqs) + if not req.marker and not req.extras + ) + + markers_or_extras_reqs = ( + req + for req in pkg_resources.parse_requirements(spec_inst_reqs) + if req.marker or req.extras + ) + for r in markers_or_extras_reqs: marker = r.marker extras = r.extras - if not marker and not extras: - install_requires.append(r) - continue r.extras = () r.marker = None for section in extras or ('',): @@ -399,7 +405,6 @@ class Distribution(Distribution_parse_config_files, _Distribution): (k, [str(r) for r in v]) for k, v in self._tmp_extras_require.items() ) - self.install_requires = [str(r) for r in install_requires] def parse_config_files(self, filenames=None): """Parses configuration files from various levels -- cgit v1.2.1 From f464c4b808e74f0c23ff36e4a83722011718ddc0 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 23 Jul 2017 15:26:50 -0400 Subject: Extract a function for removing extras and marker from a requirement. --- setuptools/dist.py | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) (limited to 'setuptools') diff --git a/setuptools/dist.py b/setuptools/dist.py index febb1b0c..d335d92a 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -372,7 +372,6 @@ class Distribution(Distribution_parse_config_files, _Distribution): for r in pkg_resources.parse_requirements(v): if r.marker: section += ':' + str(r.marker) - r.marker = None self._tmp_extras_require[section].append(r) def _move_install_requirements_markers(self): @@ -393,19 +392,26 @@ class Distribution(Distribution_parse_config_files, _Distribution): if req.marker or req.extras ) for r in markers_or_extras_reqs: - marker = r.marker - extras = r.extras - r.extras = () - r.marker = None - for section in extras or ('',): - if marker: - section += ':' + str(marker) + suffix = ':' + str(r.marker) if r.marker else '' + sections = [ + section + suffix + for section in r.extras or ('',) + ] + for section in sections: self._tmp_extras_require[section].append(r) self.extras_require = dict( - (k, [str(r) for r in v]) + (k, [str(r) for r in map(self._clean_req, v)]) for k, v in self._tmp_extras_require.items() ) + def _clean_req(self, req): + """ + Given a Requirement, remove extras and markers and return it. + """ + req.extras = () + req.marker = None + return req + def parse_config_files(self, filenames=None): """Parses configuration files from various levels and loads configuration. -- cgit v1.2.1 From b812935899dec7e7afea3ea0ae0f5a9b3169f741 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 23 Jul 2017 15:45:07 -0400 Subject: Consolidate logic around a 'simple' requirement --- setuptools/dist.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'setuptools') diff --git a/setuptools/dist.py b/setuptools/dist.py index d335d92a..cf25c64d 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -16,7 +16,7 @@ from distutils.errors import ( from distutils.util import rfc822_escape from setuptools.extern import six -from setuptools.extern.six.moves import map +from setuptools.extern.six.moves import map, filter, filterfalse from pkg_resources.extern import packaging from setuptools.depends import Require @@ -379,17 +379,21 @@ class Distribution(Distribution_parse_config_files, _Distribution): Move requirements in `install_requires` that are using environment markers or extras to `extras_require`. """ + def is_simple_req(req): + return not req.marker and not req.extras + spec_inst_reqs = getattr(self, 'install_requires', None) or () self.install_requires = list( str(req) - for req in pkg_resources.parse_requirements(spec_inst_reqs) - if not req.marker and not req.extras + for req in filter( + is_simple_req, + pkg_resources.parse_requirements(spec_inst_reqs), + ) ) - markers_or_extras_reqs = ( - req - for req in pkg_resources.parse_requirements(spec_inst_reqs) - if req.marker or req.extras + markers_or_extras_reqs = filterfalse( + is_simple_req, + pkg_resources.parse_requirements(spec_inst_reqs), ) for r in markers_or_extras_reqs: suffix = ':' + str(r.marker) if r.marker else '' -- cgit v1.2.1 From 77044955def37aa95310224d6b8436a8f8e0c6d3 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 23 Jul 2017 15:49:26 -0400 Subject: Refactor a bit for clarity --- setuptools/dist.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) (limited to 'setuptools') diff --git a/setuptools/dist.py b/setuptools/dist.py index cf25c64d..88bdf5aa 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -379,23 +379,26 @@ class Distribution(Distribution_parse_config_files, _Distribution): Move requirements in `install_requires` that are using environment markers or extras to `extras_require`. """ + + # divide the install_requires into two sets, simple ones still + # handled by install_requires and more complex ones handled + # by extras_require. + def is_simple_req(req): return not req.marker and not req.extras spec_inst_reqs = getattr(self, 'install_requires', None) or () - self.install_requires = list( - str(req) - for req in filter( - is_simple_req, - pkg_resources.parse_requirements(spec_inst_reqs), - ) + simple_reqs = filter( + is_simple_req, + pkg_resources.parse_requirements(spec_inst_reqs), ) - - markers_or_extras_reqs = filterfalse( + complex_reqs = filterfalse( is_simple_req, pkg_resources.parse_requirements(spec_inst_reqs), ) - for r in markers_or_extras_reqs: + self.install_requires = list(map(str, simple_reqs)) + + for r in complex_reqs: suffix = ':' + str(r.marker) if r.marker else '' sections = [ section + suffix -- cgit v1.2.1 From f26bf186bad984b8649a723d795f66c4f8e415f6 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 23 Jul 2017 15:52:38 -0400 Subject: Align suffix calculation for extras sections --- setuptools/dist.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) (limited to 'setuptools') diff --git a/setuptools/dist.py b/setuptools/dist.py index 88bdf5aa..50f5c18f 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -370,9 +370,8 @@ class Distribution(Distribution_parse_config_files, _Distribution): self._tmp_extras_require = defaultdict(list) for section, v in spec_ext_reqs.items(): for r in pkg_resources.parse_requirements(v): - if r.marker: - section += ':' + str(r.marker) - self._tmp_extras_require[section].append(r) + suffix = ':' + str(r.marker) if r.marker else '' + self._tmp_extras_require[section + suffix].append(r) def _move_install_requirements_markers(self): """ @@ -400,12 +399,8 @@ class Distribution(Distribution_parse_config_files, _Distribution): for r in complex_reqs: suffix = ':' + str(r.marker) if r.marker else '' - sections = [ - section + suffix - for section in r.extras or ('',) - ] - for section in sections: - self._tmp_extras_require[section].append(r) + for section in r.extras or ('',): + self._tmp_extras_require[section + suffix].append(r) self.extras_require = dict( (k, [str(r) for r in map(self._clean_req, v)]) for k, v in self._tmp_extras_require.items() -- cgit v1.2.1 From e144d98e8b38ee3440ad4fbf28c085e057858806 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 23 Jul 2017 15:59:11 -0400 Subject: Parse the requirements just once for simplicity and clarity --- setuptools/dist.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) (limited to 'setuptools') diff --git a/setuptools/dist.py b/setuptools/dist.py index 50f5c18f..c4a42183 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -387,14 +387,9 @@ class Distribution(Distribution_parse_config_files, _Distribution): return not req.marker and not req.extras spec_inst_reqs = getattr(self, 'install_requires', None) or () - simple_reqs = filter( - is_simple_req, - pkg_resources.parse_requirements(spec_inst_reqs), - ) - complex_reqs = filterfalse( - is_simple_req, - pkg_resources.parse_requirements(spec_inst_reqs), - ) + inst_reqs = list(pkg_resources.parse_requirements(spec_inst_reqs)) + simple_reqs = filter(is_simple_req, inst_reqs) + complex_reqs = filterfalse(is_simple_req, inst_reqs) self.install_requires = list(map(str, simple_reqs)) for r in complex_reqs: -- cgit v1.2.1 From 880774ac34e43c832f52d64923c670f59b54f07e Mon Sep 17 00:00:00 2001 From: Benoit Pierre Date: Sun, 23 Jul 2017 22:14:47 +0200 Subject: Revert "fix `install_requires` handling of extras" This reverts commit a3ec721ec1e70f1f7aec6c3349ad85b446410809. --- setuptools/dist.py | 11 ++++------ setuptools/tests/test_egg_info.py | 42 +++++++++++++++++++++++++++++++++------ 2 files changed, 40 insertions(+), 13 deletions(-) (limited to 'setuptools') diff --git a/setuptools/dist.py b/setuptools/dist.py index 50f5c18f..f6bf2601 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -376,7 +376,7 @@ class Distribution(Distribution_parse_config_files, _Distribution): def _move_install_requirements_markers(self): """ Move requirements in `install_requires` that are using environment - markers or extras to `extras_require`. + markers `extras_require`. """ # divide the install_requires into two sets, simple ones still @@ -384,7 +384,7 @@ class Distribution(Distribution_parse_config_files, _Distribution): # by extras_require. def is_simple_req(req): - return not req.marker and not req.extras + return not req.marker spec_inst_reqs = getattr(self, 'install_requires', None) or () simple_reqs = filter( @@ -398,9 +398,7 @@ class Distribution(Distribution_parse_config_files, _Distribution): self.install_requires = list(map(str, simple_reqs)) for r in complex_reqs: - suffix = ':' + str(r.marker) if r.marker else '' - for section in r.extras or ('',): - self._tmp_extras_require[section + suffix].append(r) + self._tmp_extras_require[':' + str(r.marker)].append(r) self.extras_require = dict( (k, [str(r) for r in map(self._clean_req, v)]) for k, v in self._tmp_extras_require.items() @@ -408,9 +406,8 @@ class Distribution(Distribution_parse_config_files, _Distribution): def _clean_req(self, req): """ - Given a Requirement, remove extras and markers and return it. + Given a Requirement, remove environment markers and return it. """ - req.extras = () req.marker = None return req diff --git a/setuptools/tests/test_egg_info.py b/setuptools/tests/test_egg_info.py index 5ea55d61..d9d4ec3b 100644 --- a/setuptools/tests/test_egg_info.py +++ b/setuptools/tests/test_egg_info.py @@ -207,14 +207,13 @@ class TestEggInfo(object): def test_install_requires_with_extra(self, tmpdir_cwd, env): req = 'install_requires=["barbazquux [test]"],' self._setup_script_with_requires(req) - self._run_install_command(tmpdir_cwd, env) - egg_info_dir = self._find_egg_info_files(env.paths['lib']).base + self._run_install_command(tmpdir_cwd, env, cmd=['egg_info']) + egg_info_dir = os.path.join('.', 'foo.egg-info') requires_txt = os.path.join(egg_info_dir, 'requires.txt') with open(requires_txt) as fp: install_requires = fp.read() expected_requires = DALS(''' - [test] - barbazquux + barbazquux[test] ''') assert install_requires.lstrip() == expected_requires assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == [] @@ -229,8 +228,8 @@ class TestEggInfo(object): with open(requires_txt) as fp: install_requires = fp.read() expected_requires = DALS(''' - [test:{marker}] - barbazquux + [:{marker}] + barbazquux[test] ''').format(marker=self.mismatch_marker_alternate) assert install_requires.lstrip() == expected_requires assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == [] @@ -250,6 +249,37 @@ class TestEggInfo(object): tmpdir_cwd, env, cmd=['test'], output="Ran 0 tests in") assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == [] + def test_extras_require_with_extra(self, tmpdir_cwd, env): + req = 'extras_require={"extra": ["barbazquux [test]"]},' + self._setup_script_with_requires(req) + self._run_install_command(tmpdir_cwd, env, cmd=['egg_info']) + egg_info_dir = os.path.join('.', 'foo.egg-info') + requires_txt = os.path.join(egg_info_dir, 'requires.txt') + with open(requires_txt) as fp: + install_requires = fp.read() + expected_requires = DALS(''' + [extra] + barbazquux[test] + ''') + assert install_requires.lstrip() == expected_requires + assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == [] + + def test_extras_require_with_extra_and_marker_in_req(self, tmpdir_cwd, env): + tmpl = 'extras_require={{"extra": ["barbazquux [test]; {marker}"]}},' + req = tmpl.format(marker=self.mismatch_marker) + self._setup_script_with_requires(req) + 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') + with open(requires_txt) as fp: + install_requires = fp.read() + expected_requires = DALS(''' + [extra:{marker}] + barbazquux[test] + ''').format(marker=self.mismatch_marker_alternate) + assert install_requires.lstrip() == expected_requires + assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == [] + def test_extras_require_with_marker(self, tmpdir_cwd, env): tmpl = 'extras_require={{":{marker}": ["barbazquux"]}},' req = tmpl.format(marker=self.mismatch_marker) -- cgit v1.2.1 From a440f728c0066f284e0d2246026264c7166a8bf5 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 23 Jul 2017 16:14:55 -0400 Subject: Extract method capturing the 'suffix' for a marker. --- setuptools/dist.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) (limited to 'setuptools') diff --git a/setuptools/dist.py b/setuptools/dist.py index c4a42183..d1472a34 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -370,9 +370,17 @@ class Distribution(Distribution_parse_config_files, _Distribution): self._tmp_extras_require = defaultdict(list) for section, v in spec_ext_reqs.items(): for r in pkg_resources.parse_requirements(v): - suffix = ':' + str(r.marker) if r.marker else '' + suffix = self._suffix_for(r) self._tmp_extras_require[section + suffix].append(r) + @staticmethod + def _suffix_for(req): + """ + For a requirement, return the 'extras_require' suffix for + that requirement. + """ + return ':' + str(req.marker) if req.marker else '' + def _move_install_requirements_markers(self): """ Move requirements in `install_requires` that are using environment @@ -393,9 +401,13 @@ class Distribution(Distribution_parse_config_files, _Distribution): self.install_requires = list(map(str, simple_reqs)) for r in complex_reqs: - suffix = ':' + str(r.marker) if r.marker else '' - for section in r.extras or ('',): - self._tmp_extras_require[section + suffix].append(r) + sections = ( + section + self._suffix_for(r) + for section in r.extras or ('',) + ) + for section in sections: + self._tmp_extras_require[section].append(r) + self.extras_require = dict( (k, [str(r) for r in map(self._clean_req, v)]) for k, v in self._tmp_extras_require.items() -- cgit v1.2.1 From a82fd99f671e6bbcfd753196862d891c0d32d82c Mon Sep 17 00:00:00 2001 From: Benoit Pierre Date: Tue, 25 Jul 2017 20:59:48 +0200 Subject: do not strip empty sections in `extras_require` --- setuptools/dist.py | 2 ++ setuptools/tests/test_egg_info.py | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+) (limited to 'setuptools') diff --git a/setuptools/dist.py b/setuptools/dist.py index 0787261e..dfe700bd 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -369,6 +369,8 @@ class Distribution(Distribution_parse_config_files, _Distribution): spec_ext_reqs = getattr(self, 'extras_require', None) or {} self._tmp_extras_require = defaultdict(list) for section, v in spec_ext_reqs.items(): + # Do not strip empty sections. + self._tmp_extras_require[section] for r in pkg_resources.parse_requirements(v): suffix = self._suffix_for(r) self._tmp_extras_require[section + suffix].append(r) diff --git a/setuptools/tests/test_egg_info.py b/setuptools/tests/test_egg_info.py index d9d4ec3b..9ea7cdce 100644 --- a/setuptools/tests/test_egg_info.py +++ b/setuptools/tests/test_egg_info.py @@ -274,6 +274,8 @@ class TestEggInfo(object): with open(requires_txt) as fp: install_requires = fp.read() expected_requires = DALS(''' + [extra] + [extra:{marker}] barbazquux[test] ''').format(marker=self.mismatch_marker_alternate) @@ -306,6 +308,8 @@ class TestEggInfo(object): with open(requires_txt) as fp: install_requires = fp.read() expected_requires = DALS(''' + [extra] + [extra:{marker}] barbazquux ''').format(marker=self.mismatch_marker_alternate) @@ -328,6 +332,20 @@ class TestEggInfo(object): self._run_install_command(tmpdir_cwd, env) assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == [] + def test_extras_require_with_empty_section(self, tmpdir_cwd, env): + tmpl = 'extras_require={{"empty": []}},' + req = tmpl.format(marker=self.invalid_marker) + self._setup_script_with_requires(req) + 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') + with open(requires_txt) as fp: + install_requires = fp.read() + expected_requires = DALS(''' + [empty] + ''').format(marker=self.mismatch_marker_alternate) + assert install_requires.lstrip() == expected_requires + def test_python_requires_egg_info(self, tmpdir_cwd, env): self._setup_script_with_requires( """python_requires='>=2.7.12',""") -- cgit v1.2.1 From 0c86c6a290c6ddce7732228820482a600f7520ca Mon Sep 17 00:00:00 2001 From: Benoit Pierre Date: Wed, 26 Jul 2017 19:51:24 +0200 Subject: tests: minor cleanup; remove dead code --- setuptools/tests/test_easy_install.py | 12 ------------ 1 file changed, 12 deletions(-) (limited to 'setuptools') diff --git a/setuptools/tests/test_easy_install.py b/setuptools/tests/test_easy_install.py index 26cdd90d..c9d396f4 100644 --- a/setuptools/tests/test_easy_install.py +++ b/setuptools/tests/test_easy_install.py @@ -567,18 +567,6 @@ def create_setup_requires_package(path, distname='foobar', version='0.1', return test_pkg -def make_trivial_sdist(dist_path, setup_py): - """Create a simple sdist tarball at dist_path, containing just a - setup.py, the contents of which are provided by the setup_py string. - """ - - setup_py_file = tarfile.TarInfo(name='setup.py') - 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) - - @pytest.mark.skipif( sys.platform.startswith('java') and ei.is_sh(sys.executable), reason="Test cannot run under java when executable is sh" -- cgit v1.2.1 From 75e88f63cc0308f7933e936b171f9cba2d04e7ad Mon Sep 17 00:00:00 2001 From: Benoit Pierre Date: Fri, 21 Jul 2017 16:39:15 +0200 Subject: fix `test` command handling of `extras_require` Also install platform specific requirements in `extras_require`. --- setuptools/command/test.py | 10 ++++-- setuptools/tests/test_virtualenv.py | 66 +++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 3 deletions(-) (limited to 'setuptools') diff --git a/setuptools/command/test.py b/setuptools/command/test.py index b8863fdc..638d0c56 100644 --- a/setuptools/command/test.py +++ b/setuptools/command/test.py @@ -11,7 +11,7 @@ from setuptools.extern import six from setuptools.extern.six.moves import map, filter from pkg_resources import (resource_listdir, resource_exists, normalize_path, - working_set, _namespace_packages, + working_set, _namespace_packages, evaluate_marker, add_activation_listener, require, EntryPoint) from setuptools import Command from setuptools.py31compat import unittest_main @@ -191,9 +191,13 @@ class test(Command): Install the requirements indicated by self.distribution and return an iterable of the dists that were built. """ - ir_d = dist.fetch_build_eggs(dist.install_requires or []) + ir_d = dist.fetch_build_eggs(dist.install_requires) tr_d = dist.fetch_build_eggs(dist.tests_require or []) - return itertools.chain(ir_d, tr_d) + er_d = dist.fetch_build_eggs( + v for k, v in dist.extras_require.items() + if k.startswith(':') and evaluate_marker(k[1:]) + ) + return itertools.chain(ir_d, tr_d, er_d) def run(self): installed_dists = self.install_dists(self.distribution) diff --git a/setuptools/tests/test_virtualenv.py b/setuptools/tests/test_virtualenv.py index a7f485a4..17b8793c 100644 --- a/setuptools/tests/test_virtualenv.py +++ b/setuptools/tests/test_virtualenv.py @@ -6,6 +6,9 @@ from pytest_fixture_config import yield_requires_config import pytest_virtualenv +from .textwrap import DALS +from .test_easy_install import make_nspkg_sdist + @yield_requires_config(pytest_virtualenv.CONFIG, ['virtualenv_executable']) @yield_fixture(scope='function') @@ -48,3 +51,66 @@ def test_pip_upgrade_from_source(virtualenv): virtualenv.run('pip install ' + wheel) # And finally try to upgrade from source. virtualenv.run('pip install --no-cache-dir --upgrade ' + sdist) + +def test_test_command_install_requirements(bare_virtualenv, tmpdir): + """ + Check the test command will install all required dependencies. + """ + bare_virtualenv.run(' && '.join(( + 'cd {source}', + 'python setup.py develop', + )).format(source=SOURCE_DIR)) + def sdist(distname, version): + dist_path = tmpdir.join('%s-%s.tar.gz' % (distname, version)) + make_nspkg_sdist(str(dist_path), distname, version) + return dist_path + dependency_links = [ + str(dist_path) + for dist_path in ( + sdist('foobar', '2.4'), + sdist('bits', '4.2'), + sdist('bobs', '6.0'), + sdist('pieces', '0.6'), + ) + ] + with tmpdir.join('setup.py').open('w') as fp: + fp.write(DALS( + ''' + from setuptools import setup + + setup( + dependency_links={dependency_links!r}, + install_requires=[ + 'barbazquux1; sys_platform in ""', + 'foobar==2.4', + ], + setup_requires='bits==4.2', + tests_require=""" + bobs==6.0 + """, + extras_require={{ + 'test': ['barbazquux2'], + ':"" in sys_platform': 'pieces==0.6', + ':python_version > "1"': """ + pieces + foobar + """, + }} + ) + '''.format(dependency_links=dependency_links))) + with tmpdir.join('test.py').open('w') as fp: + fp.write(DALS( + ''' + import foobar + import bits + import bobs + import pieces + + open('success', 'w').close() + ''')) + # Run test command for test package. + bare_virtualenv.run(' && '.join(( + 'cd {tmpdir}', + 'python setup.py test -s test', + )).format(tmpdir=tmpdir)) + assert tmpdir.join('success').check() -- cgit v1.2.1 From 096f3287314549ac423f1c1c443f8aefa0b64b4f Mon Sep 17 00:00:00 2001 From: Benoit Pierre Date: Thu, 27 Jul 2017 01:45:54 +0200 Subject: fix requires handling when using setup.cfg --- setuptools/dist.py | 10 +- setuptools/tests/test_config.py | 6 +- setuptools/tests/test_egg_info.py | 334 ++++++++++++++++++++++---------------- 3 files changed, 201 insertions(+), 149 deletions(-) (limited to 'setuptools') diff --git a/setuptools/dist.py b/setuptools/dist.py index dfe700bd..21730f22 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -349,14 +349,15 @@ class Distribution(Distribution_parse_config_files, _Distribution): "setuptools, pip, and PyPI. Please see PEP 440 for more " "details." % self.metadata.version ) - if getattr(self, 'python_requires', None): - self.metadata.python_requires = self.python_requires self._finalize_requires() def _finalize_requires(self): """ - Fix environment markers in `install_requires` and `extras_require`. + Set `metadata.python_requires` and fix environment markers + in `install_requires` and `extras_require`. """ + if getattr(self, 'python_requires', None): + self.metadata.python_requires = self.python_requires self._convert_extras_requirements() self._move_install_requirements_markers() @@ -424,8 +425,7 @@ class Distribution(Distribution_parse_config_files, _Distribution): _Distribution.parse_config_files(self, filenames=filenames) parse_configuration(self, self.command_options) - if getattr(self, 'python_requires', None): - self.metadata.python_requires = self.python_requires + self._finalize_requires() def parse_command_line(self): """Process features after parsing command line options""" diff --git a/setuptools/tests/test_config.py b/setuptools/tests/test_config.py index 8bd2a494..dbabd69e 100644 --- a/setuptools/tests/test_config.py +++ b/setuptools/tests/test_config.py @@ -333,7 +333,7 @@ class TestOptions: ]) assert dist.install_requires == ([ 'docutils>=0.3', - 'pack ==1.1, ==1.3', + 'pack==1.1,==1.3', 'hey' ]) assert dist.setup_requires == ([ @@ -403,7 +403,7 @@ class TestOptions: ]) assert dist.install_requires == ([ 'docutils>=0.3', - 'pack ==1.1, ==1.3', + 'pack==1.1,==1.3', 'hey' ]) assert dist.setup_requires == ([ @@ -508,7 +508,7 @@ class TestOptions: with get_dist(tmpdir) as dist: assert dist.extras_require == { 'pdf': ['ReportLab>=1.2', 'RXP'], - 'rest': ['docutils>=0.3', 'pack ==1.1, ==1.3'] + 'rest': ['docutils>=0.3', 'pack==1.1,==1.3'] } def test_entry_points(self, tmpdir): diff --git a/setuptools/tests/test_egg_info.py b/setuptools/tests/test_egg_info.py index 9ea7cdce..33d6cc52 100644 --- a/setuptools/tests/test_egg_info.py +++ b/setuptools/tests/test_egg_info.py @@ -1,3 +1,4 @@ +import ast import os import glob import re @@ -165,19 +166,17 @@ class TestEggInfo(object): 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(""" + def _setup_script_with_requires(self, requires, use_setup_cfg=False): + setup_script = DALS( + ''' from setuptools import setup - setup( - name='foo', - %s - zip_safe=False, - ) - """ % requires_line) - build_files({ - 'setup.py': setup_script, - }) + setup(name='foo', zip_safe=False, %s) + ''' + ) % ('' if use_setup_cfg else requires) + setup_config = requires if use_setup_cfg else '' + build_files({'setup.py': setup_script, + 'setup.cfg': setup_config}) mismatch_marker = "python_version<'{this_ver}'".format( this_ver=sys.version_info[0], @@ -188,131 +187,198 @@ class TestEggInfo(object): ) invalid_marker = "<=>++" - def test_install_requires_with_marker(self, tmpdir_cwd, env): - tmpl = 'install_requires=["barbazquux;{marker}"],' - req = tmpl.format(marker=self.mismatch_marker) - self._setup_script_with_requires(req) - 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') - with open(requires_txt) as fp: - install_requires = fp.read() - expected_requires = DALS(''' - [:{marker}] - barbazquux - ''').format(marker=self.mismatch_marker_alternate) - assert install_requires.lstrip() == expected_requires - assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == [] + class RequiresTestHelper(object): + + @staticmethod + def parametrize(*test_list, **format_dict): + idlist = [] + argvalues = [] + for test in test_list: + test_params = test.lstrip().split('\n\n', 3) + name_kwargs = test_params.pop(0).split('\n') + if len(name_kwargs) > 1: + install_cmd_kwargs = ast.literal_eval(name_kwargs[1].strip()) + else: + install_cmd_kwargs = {} + name = name_kwargs[0].strip() + setup_py_requires, setup_cfg_requires, expected_requires = ( + DALS(a).format(**format_dict) for a in test_params + ) + for id_, requires, use_cfg in ( + (name, setup_py_requires, False), + (name + '_in_setup_cfg', setup_cfg_requires, True), + ): + idlist.append(id_) + marks = () + if requires.startswith('@xfail\n'): + requires = requires[7:] + marks = pytest.mark.xfail + argvalues.append(pytest.param(requires, use_cfg, + expected_requires, + install_cmd_kwargs, + marks=marks)) + return pytest.mark.parametrize('requires,use_setup_cfg,' + 'expected_requires,install_cmd_kwargs', + argvalues, ids=idlist) + + @RequiresTestHelper.parametrize( + # Format of a test: + # + # id + # install_cmd_kwargs [optional] + # + # requires block (when used in setup.py) + # + # requires block (when used in setup.cfg) + # + # expected contents of requires.txt - def test_install_requires_with_extra(self, tmpdir_cwd, env): - req = 'install_requires=["barbazquux [test]"],' - self._setup_script_with_requires(req) - self._run_install_command(tmpdir_cwd, env, cmd=['egg_info']) - egg_info_dir = os.path.join('.', 'foo.egg-info') - requires_txt = os.path.join(egg_info_dir, 'requires.txt') - with open(requires_txt) as fp: - install_requires = fp.read() - expected_requires = DALS(''' - barbazquux[test] - ''') - assert install_requires.lstrip() == expected_requires - assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == [] + ''' + install_requires_with_marker + + install_requires=["barbazquux;{mismatch_marker}"], + + [options] + install_requires = + barbazquux; {mismatch_marker} - def test_install_requires_with_extra_and_marker(self, tmpdir_cwd, env): - tmpl = 'install_requires=["barbazquux [test]; {marker}"],' - req = tmpl.format(marker=self.mismatch_marker) - self._setup_script_with_requires(req) - 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') - with open(requires_txt) as fp: - install_requires = fp.read() - expected_requires = DALS(''' - [:{marker}] - barbazquux[test] - ''').format(marker=self.mismatch_marker_alternate) - assert install_requires.lstrip() == expected_requires - assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == [] + [:{mismatch_marker_alternate}] + barbazquux + ''', - def test_setup_requires_with_markers(self, tmpdir_cwd, env): - tmpl = 'setup_requires=["barbazquux;{marker}"],' - req = tmpl.format(marker=self.mismatch_marker) - self._setup_script_with_requires(req) - self._run_install_command(tmpdir_cwd, env) - assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == [] + ''' + install_requires_with_extra + {'cmd': ['egg_info']} - def test_tests_require_with_markers(self, tmpdir_cwd, env): - tmpl = 'tests_require=["barbazquux;{marker}"],' - req = tmpl.format(marker=self.mismatch_marker) - self._setup_script_with_requires(req) - self._run_install_command( - tmpdir_cwd, env, cmd=['test'], output="Ran 0 tests in") - assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == [] + install_requires=["barbazquux [test]"], - def test_extras_require_with_extra(self, tmpdir_cwd, env): - req = 'extras_require={"extra": ["barbazquux [test]"]},' - self._setup_script_with_requires(req) - self._run_install_command(tmpdir_cwd, env, cmd=['egg_info']) - egg_info_dir = os.path.join('.', 'foo.egg-info') - requires_txt = os.path.join(egg_info_dir, 'requires.txt') - with open(requires_txt) as fp: - install_requires = fp.read() - expected_requires = DALS(''' - [extra] - barbazquux[test] - ''') - assert install_requires.lstrip() == expected_requires - assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == [] + [options] + install_requires = + barbazquux [test] - def test_extras_require_with_extra_and_marker_in_req(self, tmpdir_cwd, env): - tmpl = 'extras_require={{"extra": ["barbazquux [test]; {marker}"]}},' - req = tmpl.format(marker=self.mismatch_marker) - self._setup_script_with_requires(req) - 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') - with open(requires_txt) as fp: - install_requires = fp.read() - expected_requires = DALS(''' - [extra] - - [extra:{marker}] - barbazquux[test] - ''').format(marker=self.mismatch_marker_alternate) - assert install_requires.lstrip() == expected_requires - assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == [] + barbazquux[test] + ''', - def test_extras_require_with_marker(self, tmpdir_cwd, env): - tmpl = 'extras_require={{":{marker}": ["barbazquux"]}},' - req = tmpl.format(marker=self.mismatch_marker) - self._setup_script_with_requires(req) - 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') - with open(requires_txt) as fp: - install_requires = fp.read() - expected_requires = DALS(''' - [:{marker}] - barbazquux - ''').format(marker=self.mismatch_marker) - assert install_requires.lstrip() == expected_requires - assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == [] + ''' + install_requires_with_extra_and_marker - def test_extras_require_with_marker_in_req(self, tmpdir_cwd, env): - tmpl = 'extras_require={{"extra": ["barbazquux; {marker}"]}},' - req = tmpl.format(marker=self.mismatch_marker) - self._setup_script_with_requires(req) - self._run_install_command(tmpdir_cwd, env) - egg_info_dir = self._find_egg_info_files(env.paths['lib']).base + install_requires=["barbazquux [test]; {mismatch_marker}"], + + [options] + install_requires = + barbazquux [test]; {mismatch_marker} + + [:{mismatch_marker_alternate}] + barbazquux[test] + ''', + + ''' + setup_requires_with_markers + + setup_requires=["barbazquux;{mismatch_marker}"], + + [options] + setup_requires = + barbazquux; {mismatch_marker} + + ''', + + ''' + tests_require_with_markers + {'cmd': ['test'], 'output': "Ran 0 tests in"} + + tests_require=["barbazquux;{mismatch_marker}"], + + [options] + tests_require = + barbazquux; {mismatch_marker} + + ''', + + ''' + extras_require_with_extra + {'cmd': ['egg_info']} + + extras_require={{"extra": ["barbazquux [test]"]}}, + + [options.extras_require] + extra = barbazquux [test] + + [extra] + barbazquux[test] + ''', + + ''' + extras_require_with_extra_and_marker_in_req + + extras_require={{"extra": ["barbazquux [test]; {mismatch_marker}"]}}, + + [options.extras_require] + extra = + barbazquux [test]; {mismatch_marker} + + [extra] + + [extra:{mismatch_marker_alternate}] + barbazquux[test] + ''', + + # FIXME: ConfigParser does not allow : in key names! + ''' + extras_require_with_marker + + extras_require={{":{mismatch_marker}": ["barbazquux"]}}, + + @xfail + [options.extras_require] + :{mismatch_marker} = barbazquux + + [:{mismatch_marker}] + barbazquux + ''', + + ''' + extras_require_with_marker_in_req + + extras_require={{"extra": ["barbazquux; {mismatch_marker}"]}}, + + [options.extras_require] + extra = + barbazquux; {mismatch_marker} + + [extra] + + [extra:{mismatch_marker_alternate}] + barbazquux + ''', + + ''' + extras_require_with_empty_section + + extras_require={{"empty": []}}, + + [options.extras_require] + empty = + + [empty] + ''', + # Format arguments. + invalid_marker=invalid_marker, + mismatch_marker=mismatch_marker, + mismatch_marker_alternate=mismatch_marker_alternate, + ) + def test_requires(self, tmpdir_cwd, env, + requires, use_setup_cfg, + expected_requires, install_cmd_kwargs): + self._setup_script_with_requires(requires, use_setup_cfg) + self._run_install_command(tmpdir_cwd, env, **install_cmd_kwargs) + egg_info_dir = os.path.join('.', 'foo.egg-info') requires_txt = os.path.join(egg_info_dir, 'requires.txt') - with open(requires_txt) as fp: - install_requires = fp.read() - expected_requires = DALS(''' - [extra] - - [extra:{marker}] - barbazquux - ''').format(marker=self.mismatch_marker_alternate) + if os.path.exists(requires_txt): + with open(requires_txt) as fp: + install_requires = fp.read() + else: + install_requires = '' assert install_requires.lstrip() == expected_requires assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == [] @@ -332,20 +398,6 @@ class TestEggInfo(object): self._run_install_command(tmpdir_cwd, env) assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == [] - def test_extras_require_with_empty_section(self, tmpdir_cwd, env): - tmpl = 'extras_require={{"empty": []}},' - req = tmpl.format(marker=self.invalid_marker) - self._setup_script_with_requires(req) - 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') - with open(requires_txt) as fp: - install_requires = fp.read() - expected_requires = DALS(''' - [empty] - ''').format(marker=self.mismatch_marker_alternate) - assert install_requires.lstrip() == expected_requires - def test_python_requires_egg_info(self, tmpdir_cwd, env): self._setup_script_with_requires( """python_requires='>=2.7.12',""") -- cgit v1.2.1 From 54e6a1cd8565c0130024fd68fd628d36fa0ec1c9 Mon Sep 17 00:00:00 2001 From: Segev Finer Date: Wed, 2 Aug 2017 21:44:53 +0300 Subject: Fix exception on mingw built Python 2 The exception is caused by the VCForPython27 monkey patch. It's not needed on mingw built Python. Disable it and avoid the import by using the same function that `distutils` uses to decide it's running on a mingw built Python. Fixes #1118 --- setuptools/monkey.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'setuptools') diff --git a/setuptools/monkey.py b/setuptools/monkey.py index 6d3711ec..62194595 100644 --- a/setuptools/monkey.py +++ b/setuptools/monkey.py @@ -4,6 +4,7 @@ Monkey patching of distutils. import sys import distutils.filelist +from distutils.util import get_platform import platform import types import functools @@ -152,13 +153,13 @@ def patch_for_msvc_specialized_compiler(): Patch functions in distutils to use standalone Microsoft Visual C++ compilers. """ - # import late to avoid circular imports on Python < 3.5 - msvc = import_module('setuptools.msvc') - - if platform.system() != 'Windows': + if not get_platform().startswith('win'): # Compilers only availables on Microsoft Windows return + # import late to avoid circular imports on Python < 3.5 + msvc = import_module('setuptools.msvc') + def patch_params(mod_name, func_name): """ Prepare the parameters for patch_func to patch indicated function. -- cgit v1.2.1 From 300c8802ef4d13d9433af3bce9d22936ff3c610f Mon Sep 17 00:00:00 2001 From: Segev Finer Date: Thu, 3 Aug 2017 21:39:59 +0300 Subject: Fix exception on mingw built Python 2 msvc9compiler doesn't like being imported on mingw built Python. It throws DistutilsPlatformError, so catch it. Fixes #1118 --- setuptools/monkey.py | 9 ++++----- setuptools/msvc.py | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) (limited to 'setuptools') diff --git a/setuptools/monkey.py b/setuptools/monkey.py index 62194595..6d3711ec 100644 --- a/setuptools/monkey.py +++ b/setuptools/monkey.py @@ -4,7 +4,6 @@ Monkey patching of distutils. import sys import distutils.filelist -from distutils.util import get_platform import platform import types import functools @@ -153,13 +152,13 @@ def patch_for_msvc_specialized_compiler(): Patch functions in distutils to use standalone Microsoft Visual C++ compilers. """ - if not get_platform().startswith('win'): - # Compilers only availables on Microsoft Windows - return - # import late to avoid circular imports on Python < 3.5 msvc = import_module('setuptools.msvc') + if platform.system() != 'Windows': + # Compilers only availables on Microsoft Windows + return + def patch_params(mod_name, func_name): """ Prepare the parameters for patch_func to patch indicated function. diff --git a/setuptools/msvc.py b/setuptools/msvc.py index 729021ac..f3917815 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -47,7 +47,7 @@ else: try: from distutils.msvc9compiler import Reg -except ImportError: +except (ImportError, distutils.errors.DistutilsPlatformError): pass -- cgit v1.2.1 From 20d6c6c22d88260669a1aec573d62aabc4052abf Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 3 Aug 2017 14:52:36 -0400 Subject: Extract variable for exceptions to provide explanation --- setuptools/msvc.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'setuptools') diff --git a/setuptools/msvc.py b/setuptools/msvc.py index f3917815..8e3b638f 100644 --- a/setuptools/msvc.py +++ b/setuptools/msvc.py @@ -45,9 +45,18 @@ else: safe_env = dict() +_msvc9_suppress_errors = ( + # msvc9compiler isn't available on some platforms + ImportError, + + # msvc9compiler raises DistutilsPlatformError in some + # environments. See #1118. + distutils.errors.DistutilsPlatformError, +) + try: from distutils.msvc9compiler import Reg -except (ImportError, distutils.errors.DistutilsPlatformError): +except _msvc9_suppress_errors: pass -- cgit v1.2.1 From e5461b6ccc57596c7e9cf7837084f8a20eeec9b7 Mon Sep 17 00:00:00 2001 From: Benoit Pierre Date: Sat, 5 Aug 2017 18:31:53 +0200 Subject: workaround easy_install bug Don't reuse `easy_install` command in `Distribution.fetch_build_egg` implementation. Fix #196. --- setuptools/dist.py | 54 +++++++++++++++++++------------------------ setuptools/tests/test_dist.py | 46 ++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 30 deletions(-) create mode 100644 setuptools/tests/test_dist.py (limited to 'setuptools') diff --git a/setuptools/dist.py b/setuptools/dist.py index 21730f22..e1510b6f 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -485,36 +485,30 @@ class Distribution(Distribution_parse_config_files, _Distribution): def fetch_build_egg(self, req): """Fetch an egg needed for building""" - - try: - cmd = self._egg_fetcher - cmd.package_index.to_scan = [] - except AttributeError: - from setuptools.command.easy_install import easy_install - dist = self.__class__({'script_args': ['easy_install']}) - dist.parse_config_files() - opts = dist.get_option_dict('easy_install') - keep = ( - 'find_links', 'site_dirs', 'index_url', 'optimize', - 'site_dirs', 'allow_hosts' - ) - for key in list(opts): - if key not in keep: - del opts[key] # don't use any other settings - if self.dependency_links: - links = self.dependency_links[:] - if 'find_links' in opts: - links = opts['find_links'][1].split() + links - opts['find_links'] = ('setup', links) - install_dir = self.get_egg_cache_dir() - cmd = easy_install( - dist, args=["x"], install_dir=install_dir, - exclude_scripts=True, - always_copy=False, build_directory=None, editable=False, - upgrade=False, multi_version=True, no_report=True, user=False - ) - cmd.ensure_finalized() - self._egg_fetcher = cmd + from setuptools.command.easy_install import easy_install + dist = self.__class__({'script_args': ['easy_install']}) + dist.parse_config_files() + opts = dist.get_option_dict('easy_install') + keep = ( + 'find_links', 'site_dirs', 'index_url', 'optimize', + 'site_dirs', 'allow_hosts' + ) + for key in list(opts): + if key not in keep: + del opts[key] # don't use any other settings + if self.dependency_links: + links = self.dependency_links[:] + if 'find_links' in opts: + links = opts['find_links'][1].split() + links + opts['find_links'] = ('setup', links) + install_dir = self.get_egg_cache_dir() + cmd = easy_install( + dist, args=["x"], install_dir=install_dir, + exclude_scripts=True, + always_copy=False, build_directory=None, editable=False, + upgrade=False, multi_version=True, no_report=True, user=False + ) + cmd.ensure_finalized() return cmd.easy_install(req) def _set_global_opts_from_features(self): diff --git a/setuptools/tests/test_dist.py b/setuptools/tests/test_dist.py new file mode 100644 index 00000000..435ffec0 --- /dev/null +++ b/setuptools/tests/test_dist.py @@ -0,0 +1,46 @@ +from setuptools import Distribution +from setuptools.extern.six.moves.urllib.request import pathname2url +from setuptools.extern.six.moves.urllib_parse import urljoin + +from .textwrap import DALS +from .test_easy_install import make_nspkg_sdist + + +def test_dist_fetch_build_egg(tmpdir): + """ + Check multiple calls to `Distribution.fetch_build_egg` work as expected. + """ + index = tmpdir.mkdir('index') + index_url = urljoin('file://', pathname2url(str(index))) + def sdist_with_index(distname, version): + dist_dir = index.mkdir(distname) + dist_sdist = '%s-%s.tar.gz' % (distname, version) + make_nspkg_sdist(str(dist_dir.join(dist_sdist)), distname, version) + with dist_dir.join('index.html').open('w') as fp: + fp.write(DALS( + ''' + + {dist_sdist}
+ + ''' + ).format(dist_sdist=dist_sdist)) + sdist_with_index('barbazquux', '3.2.0') + sdist_with_index('barbazquux-runner', '2.11.1') + with tmpdir.join('setup.cfg').open('w') as fp: + fp.write(DALS( + ''' + [easy_install] + index_url = {index_url} + ''' + ).format(index_url=index_url)) + reqs = ''' + barbazquux-runner + barbazquux + '''.split() + with tmpdir.as_cwd(): + dist = Distribution() + resolved_dists = [ + dist.fetch_build_egg(r) + for r in reqs + ] + assert [dist.key for dist in resolved_dists if dist] == reqs -- cgit v1.2.1 From 986ff42f117e8ac3cc952bfc2c043f7de8ba7084 Mon Sep 17 00:00:00 2001 From: Sviatoslav Sydorenko Date: Wed, 9 Aug 2017 23:16:13 +0300 Subject: Allow adding few files @ metadata.long_description --- setuptools/config.py | 6 +++--- setuptools/tests/test_config.py | 24 ++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) (limited to 'setuptools') diff --git a/setuptools/config.py b/setuptools/config.py index 06a61d16..4d3eff93 100644 --- a/setuptools/config.py +++ b/setuptools/config.py @@ -245,8 +245,8 @@ class ConfigHandler(object): directory with setup.py. Examples: - include: LICENSE - include: src/file.txt + file: LICENSE + file: src/file.txt :param str value: :rtype: str @@ -408,7 +408,7 @@ class ConfigMetadataHandler(ConfigHandler): 'classifiers': self._get_parser_compound(parse_file, parse_list), 'license': parse_file, 'description': parse_file, - 'long_description': parse_file, + 'long_description': self._get_parser_compound(parse_list, lambda l: '\n'.join(map(parse_file, l))), 'version': self._parse_version, } diff --git a/setuptools/tests/test_config.py b/setuptools/tests/test_config.py index dbabd69e..95ae60b2 100644 --- a/setuptools/tests/test_config.py +++ b/setuptools/tests/test_config.py @@ -139,6 +139,30 @@ class TestMetadata: assert metadata.download_url == 'http://test.test.com/test/' assert metadata.maintainer_email == 'test@test.com' + def test_file_mixed(self, tmpdir): + + fake_env( + tmpdir, + '[metadata]\n' + 'long_description =\n' + ' Some normal line\n' + ' file: README.rst\n' + ' Another line\n' + ' file: CHANGES.rst\n' + '\n' + ) + + tmpdir.join('README.rst').write('readme contents\nline2') + tmpdir.join('CHANGES.rst').write('changelog contents\nand stuff') + + with get_dist(tmpdir) as dist: + assert dist.metadata.long_description == ( + 'Some normal line\n' + 'readme contents\nline2\n' + 'Another line\n' + 'changelog contents\nand stuff' + ) + def test_file_sandboxed(self, tmpdir): fake_env( -- cgit v1.2.1 From b699f94728c82691bed9b67a48b170951535fa38 Mon Sep 17 00:00:00 2001 From: Sviatoslav Sydorenko Date: Mon, 28 Aug 2017 10:22:21 +0300 Subject: Support list of files passed to `file:` directive * `file:` not accepts comma-separated list of filenames * files' contents are glues with an LF separator --- setuptools/config.py | 30 +++++++++++++++++++----------- setuptools/tests/test_config.py | 10 ++-------- 2 files changed, 21 insertions(+), 19 deletions(-) (limited to 'setuptools') diff --git a/setuptools/config.py b/setuptools/config.py index 4d3eff93..b6627c80 100644 --- a/setuptools/config.py +++ b/setuptools/config.py @@ -246,30 +246,38 @@ class ConfigHandler(object): Examples: file: LICENSE - file: src/file.txt + file: README.rst, CHANGELOG.md, src/file.txt :param str value: :rtype: str """ + include_directive = 'file:' + file_contents = [] + if not isinstance(value, string_types): return value - include_directive = 'file:' if not value.startswith(include_directive): return value + filepaths = value[len(include_directive):] + filepaths = filepaths.split(',') + filepaths = map(str.strip, filepaths) + filepaths = map(os.path.abspath, filepaths) + current_directory = os.getcwd() - filepath = value.replace(include_directive, '').strip() - filepath = os.path.abspath(filepath) + for filepath in filepaths: + if not filepath.startswith(current_directory): + raise DistutilsOptionError( + '`file:` directive can not access %s' % filepath) - if not filepath.startswith(current_directory): - raise DistutilsOptionError( - '`file:` directive can not access %s' % filepath) + if os.path.isfile(filepath): + with io.open(filepath, encoding='utf-8') as f: + file_contents.append(f.read()) - if os.path.isfile(filepath): - with io.open(filepath, encoding='utf-8') as f: - value = f.read() + if file_contents: + value = '\n'.join(file_contents) return value @@ -408,7 +416,7 @@ class ConfigMetadataHandler(ConfigHandler): 'classifiers': self._get_parser_compound(parse_file, parse_list), 'license': parse_file, 'description': parse_file, - 'long_description': self._get_parser_compound(parse_list, lambda l: '\n'.join(map(parse_file, l))), + 'long_description': parse_file, 'version': self._parse_version, } diff --git a/setuptools/tests/test_config.py b/setuptools/tests/test_config.py index 95ae60b2..d81d4288 100644 --- a/setuptools/tests/test_config.py +++ b/setuptools/tests/test_config.py @@ -144,11 +144,7 @@ class TestMetadata: fake_env( tmpdir, '[metadata]\n' - 'long_description =\n' - ' Some normal line\n' - ' file: README.rst\n' - ' Another line\n' - ' file: CHANGES.rst\n' + 'long_description = file: README.rst, CHANGES.rst\n' '\n' ) @@ -157,9 +153,7 @@ class TestMetadata: with get_dist(tmpdir) as dist: assert dist.metadata.long_description == ( - 'Some normal line\n' 'readme contents\nline2\n' - 'Another line\n' 'changelog contents\nand stuff' ) @@ -168,7 +162,7 @@ class TestMetadata: fake_env( tmpdir, '[metadata]\n' - 'long_description = file: ../../README\n' + 'long_description = file: CHANGES.rst, ../../README\n' ) with get_dist(tmpdir, parse=False) as dist: -- cgit v1.2.1 From c5c4304d8c60b6f985c8c7607f6950d31d1b2e33 Mon Sep 17 00:00:00 2001 From: Sviatoslav Sydorenko Date: Mon, 28 Aug 2017 10:39:29 +0300 Subject: Convert path to str, which is needed under Py 2 --- setuptools/config.py | 1 + 1 file changed, 1 insertion(+) (limited to 'setuptools') diff --git a/setuptools/config.py b/setuptools/config.py index b6627c80..6fa50d20 100644 --- a/setuptools/config.py +++ b/setuptools/config.py @@ -262,6 +262,7 @@ class ConfigHandler(object): filepaths = value[len(include_directive):] filepaths = filepaths.split(',') + filepaths = map(str, filepaths) # Needed for Python 2 filepaths = map(str.strip, filepaths) filepaths = map(os.path.abspath, filepaths) -- cgit v1.2.1 From 29688821b381268a0d59c0d26317d88ad518f966 Mon Sep 17 00:00:00 2001 From: "Bernhard M. Wiedemann" Date: Wed, 21 Jun 2017 14:08:35 +0200 Subject: Sort file lists to generate reproducible zip files that do not differ depending on (random) filesystem order See https://reproducible-builds.org/ for why this matters. --- setuptools/command/bdist_egg.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'setuptools') diff --git a/setuptools/command/bdist_egg.py b/setuptools/command/bdist_egg.py index 8cd9dfef..51755d52 100644 --- a/setuptools/command/bdist_egg.py +++ b/setuptools/command/bdist_egg.py @@ -38,6 +38,14 @@ def strip_module(filename): filename = filename[:-6] return filename +def sorted_walk(dir): + """Do os.walk in a reproducible way, + independent of indeterministic filesystem readdir order + """ + for base, dirs, files in os.walk(dir): + dirs.sort() + files.sort() + yield base, dirs, files def write_stub(resource, pyfile): _stub_template = textwrap.dedent(""" @@ -302,7 +310,7 @@ class bdist_egg(Command): ext_outputs = [] paths = {self.bdist_dir: ''} - for base, dirs, files in os.walk(self.bdist_dir): + for base, dirs, files in sorted_walk(self.bdist_dir): for filename in files: if os.path.splitext(filename)[1].lower() in NATIVE_EXTENSIONS: all_outputs.append(paths[base] + filename) @@ -329,7 +337,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) + walker = sorted_walk(egg_dir) base, dirs, files = next(walker) if 'EGG-INFO' in dirs: dirs.remove('EGG-INFO') @@ -463,10 +471,10 @@ def make_zipfile(zip_filename, base_dir, verbose=0, dry_run=0, compress=True, compression = zipfile.ZIP_DEFLATED if compress else zipfile.ZIP_STORED if not dry_run: z = zipfile.ZipFile(zip_filename, mode, compression=compression) - for dirname, dirs, files in os.walk(base_dir): + for dirname, dirs, files in sorted_walk(base_dir): visit(z, dirname, files) z.close() else: - for dirname, dirs, files in os.walk(base_dir): + for dirname, dirs, files in sorted_walk(base_dir): visit(None, dirname, files) return zip_filename -- cgit v1.2.1 From 5da6479d917d77c8091d8b4c32724054ae5adf65 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 28 Aug 2017 08:50:44 -0400 Subject: Avoid interaction of multiple factors in test. If one wishes to test both 'mixed' and 'sandboxed', let's create a separate test for that, but it's probably unnecessary. --- setuptools/tests/test_config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'setuptools') diff --git a/setuptools/tests/test_config.py b/setuptools/tests/test_config.py index d81d4288..cdfa5af4 100644 --- a/setuptools/tests/test_config.py +++ b/setuptools/tests/test_config.py @@ -162,7 +162,7 @@ class TestMetadata: fake_env( tmpdir, '[metadata]\n' - 'long_description = file: CHANGES.rst, ../../README\n' + 'long_description = file: ../../README\n' ) with get_dist(tmpdir, parse=False) as dist: -- cgit v1.2.1 From fc59fe8ea080f7d469c6c388fa878c4ede3e6557 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 28 Aug 2017 08:54:02 -0400 Subject: Using generator comprehension, avoid casting filepath to bytes on Python 2 --- setuptools/config.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'setuptools') diff --git a/setuptools/config.py b/setuptools/config.py index 6fa50d20..23fc25ea 100644 --- a/setuptools/config.py +++ b/setuptools/config.py @@ -260,11 +260,8 @@ class ConfigHandler(object): if not value.startswith(include_directive): return value - filepaths = value[len(include_directive):] - filepaths = filepaths.split(',') - filepaths = map(str, filepaths) # Needed for Python 2 - filepaths = map(str.strip, filepaths) - filepaths = map(os.path.abspath, filepaths) + spec = value[len(include_directive):] + filepaths = (os.path.abspath(path.strip()) for path in spec.split(',')) current_directory = os.getcwd() -- cgit v1.2.1 From 3af152a26ee2ef889b8d7ab4428c975ba0c8e85b Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 28 Aug 2017 09:05:42 -0400 Subject: Extract method for reading local file. Now return results directly instead of for/append loop. --- setuptools/config.py | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) (limited to 'setuptools') diff --git a/setuptools/config.py b/setuptools/config.py index 23fc25ea..2b156269 100644 --- a/setuptools/config.py +++ b/setuptools/config.py @@ -252,7 +252,6 @@ class ConfigHandler(object): :rtype: str """ include_directive = 'file:' - file_contents = [] if not isinstance(value, string_types): return value @@ -262,22 +261,24 @@ class ConfigHandler(object): spec = value[len(include_directive):] filepaths = (os.path.abspath(path.strip()) for path in spec.split(',')) + return '\n'.join( + self._read_local_file(path) + for path in filepath + if os.path.isfile(path) + ) + + @staticmethod + def _read_local_file(filepath): + """ + Read contents of filepath. Raise error if the file + isn't in the current directory. + """ + if not filepath.startswith(os.getcwd()): + raise DistutilsOptionError( + '`file:` directive can not access %s' % filepath) - current_directory = os.getcwd() - - for filepath in filepaths: - if not filepath.startswith(current_directory): - raise DistutilsOptionError( - '`file:` directive can not access %s' % filepath) - - if os.path.isfile(filepath): - with io.open(filepath, encoding='utf-8') as f: - file_contents.append(f.read()) - - if file_contents: - value = '\n'.join(file_contents) - - return value + with io.open(filepath, encoding='utf-8') as f: + return f.read() @classmethod def _parse_attr(cls, value): -- cgit v1.2.1 From 7e549f6eb72e37f50a1183e018ad2f3b2c952285 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 28 Aug 2017 09:09:41 -0400 Subject: Correct typo --- setuptools/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'setuptools') diff --git a/setuptools/config.py b/setuptools/config.py index 2b156269..7ba02184 100644 --- a/setuptools/config.py +++ b/setuptools/config.py @@ -263,7 +263,7 @@ class ConfigHandler(object): filepaths = (os.path.abspath(path.strip()) for path in spec.split(',')) return '\n'.join( self._read_local_file(path) - for path in filepath + for path in filepaths if os.path.isfile(path) ) -- cgit v1.2.1 From 3c25384fce3f6134a342ab32b7afc54cc6066fa3 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 28 Aug 2017 09:13:37 -0400 Subject: Use proper reference. --- setuptools/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'setuptools') diff --git a/setuptools/config.py b/setuptools/config.py index 7ba02184..75f829fb 100644 --- a/setuptools/config.py +++ b/setuptools/config.py @@ -262,7 +262,7 @@ class ConfigHandler(object): spec = value[len(include_directive):] filepaths = (os.path.abspath(path.strip()) for path in spec.split(',')) return '\n'.join( - self._read_local_file(path) + cls._read_local_file(path) for path in filepaths if os.path.isfile(path) ) -- cgit v1.2.1 From 9e708961e7fa64e56c9469ca52163d7e2df31477 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 28 Aug 2017 09:32:25 -0400 Subject: Need to perform the local assertion before checking for existence. --- setuptools/config.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'setuptools') diff --git a/setuptools/config.py b/setuptools/config.py index 75f829fb..9a62e2ec 100644 --- a/setuptools/config.py +++ b/setuptools/config.py @@ -262,21 +262,20 @@ class ConfigHandler(object): spec = value[len(include_directive):] filepaths = (os.path.abspath(path.strip()) for path in spec.split(',')) return '\n'.join( - cls._read_local_file(path) + cls._read_file(path) for path in filepaths - if os.path.isfile(path) + if (cls._assert_local(path) or True) + and os.path.isfile(path) ) @staticmethod - def _read_local_file(filepath): - """ - Read contents of filepath. Raise error if the file - isn't in the current directory. - """ + def _assert_local(filepath): if not filepath.startswith(os.getcwd()): raise DistutilsOptionError( '`file:` directive can not access %s' % filepath) + @staticmethod + def _read_file(filepath): with io.open(filepath, encoding='utf-8') as f: return f.read() -- cgit v1.2.1 From 4fc60ed1e47f725a833dd63656ceec981a58e1a0 Mon Sep 17 00:00:00 2001 From: Marc Abramowitz Date: Sat, 30 Jul 2016 08:15:53 -0700 Subject: Add new long_description_content_type kwarg This is used to populate the new `Description-Content-Type` field. `Description-Content-Type` is described at https://github.com/pypa/python-packaging-user-guide/pull/258 --- setuptools/command/egg_info.py | 4 ++++ setuptools/dist.py | 10 ++++++++++ 2 files changed, 14 insertions(+) (limited to 'setuptools') diff --git a/setuptools/command/egg_info.py b/setuptools/command/egg_info.py index 6c00b0b7..a183d15d 100755 --- a/setuptools/command/egg_info.py +++ b/setuptools/command/egg_info.py @@ -599,6 +599,10 @@ def write_pkg_info(cmd, basename, filename): metadata = cmd.distribution.metadata metadata.version, oldver = cmd.egg_version, metadata.version metadata.name, oldname = cmd.egg_name, metadata.name + metadata.long_description_content_type = getattr( + cmd.distribution, + 'long_description_content_type' + ) try: # write unescaped data to PKG-INFO, so older pkg_resources # can still parse it diff --git a/setuptools/dist.py b/setuptools/dist.py index 21730f22..084641a8 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -58,6 +58,13 @@ def write_pkg_file(self, file): if self.download_url: file.write('Download-URL: %s\n' % self.download_url) + long_desc_content_type = getattr( + self, + 'long_description_content_type', + None + ) or 'UNKNOWN' + file.write('Description-Content-Type: %s\n' % long_desc_content_type) + long_desc = rfc822_escape(self.get_long_description()) file.write('Description: %s\n' % long_desc) @@ -317,6 +324,9 @@ class Distribution(Distribution_parse_config_files, _Distribution): self.dist_files = [] self.src_root = attrs and attrs.pop("src_root", None) self.patch_missing_pkg_info(attrs) + self.long_description_content_type = _attrs_dict.get( + 'long_description_content_type' + ) # Make sure we have any eggs needed to interpret 'attrs' if attrs is not None: self.dependency_links = attrs.pop('dependency_links', []) -- cgit v1.2.1 From ffc5f66bf730106854bd2bf41da494d5a17dcc46 Mon Sep 17 00:00:00 2001 From: Marc Abramowitz Date: Sun, 31 Jul 2016 15:17:12 -0700 Subject: Add test_long_description_content_type Test that specifying a `long_description_content_type` keyword arg to the `setup` function results in writing a `Description-Content-Type` line to the `PKG-INFO` file in the `.egg-info` directory. `Description-Content-Type` is described at https://github.com/pypa/python-packaging-user-guide/pull/258 --- setuptools/tests/test_egg_info.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'setuptools') diff --git a/setuptools/tests/test_egg_info.py b/setuptools/tests/test_egg_info.py index 33d6cc52..e454694d 100644 --- a/setuptools/tests/test_egg_info.py +++ b/setuptools/tests/test_egg_info.py @@ -398,6 +398,31 @@ class TestEggInfo(object): self._run_install_command(tmpdir_cwd, env) assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == [] + def test_long_description_content_type(self, tmpdir_cwd, env): + # Test that specifying a `long_description_content_type` keyword arg to + # the `setup` function results in writing a `Description-Content-Type` + # line to the `PKG-INFO` file in the `.egg-info` + # directory. + # `Description-Content-Type` is described at + # https://github.com/pypa/python-packaging-user-guide/pull/258 + + self._setup_script_with_requires( + """long_description_content_type='text/markdown',""") + environ = os.environ.copy().update( + HOME=env.paths['home'], + ) + code, data = environment.run_setup_py( + cmd=['egg_info'], + pypath=os.pathsep.join([env.paths['lib'], str(tmpdir_cwd)]), + data_stream=1, + env=environ, + ) + egg_info_dir = os.path.join('.', 'foo.egg-info') + with open(os.path.join(egg_info_dir, 'PKG-INFO')) as pkginfo_file: + pkg_info_lines = pkginfo_file.read().split('\n') + expected_line = 'Description-Content-Type: text/markdown' + assert expected_line in pkg_info_lines + def test_python_requires_egg_info(self, tmpdir_cwd, env): self._setup_script_with_requires( """python_requires='>=2.7.12',""") -- cgit v1.2.1 From 500bb9a161e810523c48825491b853c5fbe595cc Mon Sep 17 00:00:00 2001 From: Samuel Gaist Date: Tue, 22 Aug 2017 00:22:54 +0200 Subject: Improve README file list handling and add Markdown to the current list Markdown is a widely used format to write README files and documentation. This patch aims to simplify adding new formats and at the same time adds that one to the list. --- setuptools/command/sdist.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'setuptools') diff --git a/setuptools/command/sdist.py b/setuptools/command/sdist.py index 84e29a1b..7b4d197d 100755 --- a/setuptools/command/sdist.py +++ b/setuptools/command/sdist.py @@ -37,7 +37,8 @@ class sdist(sdist_add_defaults, orig.sdist): negative_opt = {} - READMES = 'README', 'README.rst', 'README.txt' + README_EXTENSIONS = ['', '.rst', '.txt', '.md'] + READMES = tuple('README{}'.format(ext) for ext in README_EXTENSIONS) def run(self): self.run_command('egg_info') -- cgit v1.2.1 From e909e1cb7aff16a02a965fe3ebaa16a65d749d63 Mon Sep 17 00:00:00 2001 From: Benoit Pierre Date: Sun, 3 Sep 2017 20:10:22 +0200 Subject: Fix Python 2.6 support --- setuptools/command/sdist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'setuptools') diff --git a/setuptools/command/sdist.py b/setuptools/command/sdist.py index 7b4d197d..508148e0 100755 --- a/setuptools/command/sdist.py +++ b/setuptools/command/sdist.py @@ -38,7 +38,7 @@ class sdist(sdist_add_defaults, orig.sdist): negative_opt = {} README_EXTENSIONS = ['', '.rst', '.txt', '.md'] - READMES = tuple('README{}'.format(ext) for ext in README_EXTENSIONS) + READMES = tuple('README{0}'.format(ext) for ext in README_EXTENSIONS) def run(self): self.run_command('egg_info') -- cgit v1.2.1