From 8423e1ed14ac1691c2863c6e8cac9230cf558d7b Mon Sep 17 00:00:00 2001 From: PJ Eby Date: Fri, 19 Mar 2004 20:53:14 +0000 Subject: Initial checkin of setuptools 0.0.1. --HG-- branch : setuptools extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/trunk/setuptools%4040869 --- setuptools/dist.py | 453 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 453 insertions(+) create mode 100644 setuptools/dist.py (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py new file mode 100644 index 00000000..2941f26e --- /dev/null +++ b/setuptools/dist.py @@ -0,0 +1,453 @@ +__all__ = ['Distribution', 'Feature'] + +from distutils.core import Distribution as _Distribution +from distutils.core import Extension +from setuptools.command.build_py import build_py +from setuptools.command.build_ext import build_ext +from setuptools.command.install import install +from setuptools.command.install_lib import install_lib +from distutils.errors import DistutilsOptionError, DistutilsPlatformError +from distutils.errors import DistutilsSetupError +sequence = tuple, list + +class Distribution(_Distribution): + + """Distribution with support for features, tests, and package data + + This is an enhanced version of 'distutils.dist.Distribution' that + effectively adds the following new optional keyword arguments to 'setup()': + + 'features' -- a dictionary mapping option names to 'setuptools.Feature' + objects. Features are a portion of the distribution that can be + included or excluded based on user options, inter-feature dependencies, + and availability on the current system. Excluded features are omitted + from all setup commands, including source and binary distributions, so + you can create multiple distributions from the same source tree. + + Feature names should be valid Python identifiers, except that they may + contain the '-' (minus) sign. Features can be included or excluded + via the command line options '--with-X' and '--without-X', where 'X' is + the name of the feature. Whether a feature is included by default, and + whether you are allowed to control this from the command line, is + determined by the Feature object. See the 'Feature' class for more + information. + + 'test_suite' -- the name of a test suite to run for the 'test' command. + If the user runs 'python setup.py test', the package will be installed, + and the named test suite will be run. The format is the same as + would be used on a 'unittest.py' command line. That is, it is the + dotted name of an object to import and call to generate a test suite. + + 'package_data' -- a dictionary mapping package names to lists of filenames + or globs to use to find data files contained in the named packages. + If the dictionary has filenames or globs listed under '""' (the empty + string), those names will be searched for in every package, in addition + to any names for the specific package. Data files found using these + names/globs will be installed along with the package, in the same + location as the package. Note that globs are allowed to reference + the contents of non-package subdirectories, as long as you use '/' as + a path separator. (Globs are automatically converted to + platform-specific paths at runtime.) + + In addition to these new keywords, this class also has several new methods + for manipulating the distribution's contents. For example, the 'include()' + and 'exclude()' methods can be thought of as in-place add and subtract + commands that add or remove packages, modules, extensions, and so on from + the distribution. They are used by the feature subsystem to configure the + distribution for the included and excluded features. + """ + + def __init__ (self, attrs=None): + self.features = {} + self.package_data = {} + self.test_suite = None + self.requires = [] + _Distribution.__init__(self,attrs) + self.cmdclass.setdefault('build_py',build_py) + self.cmdclass.setdefault('build_ext',build_ext) + self.cmdclass.setdefault('install',install) + self.cmdclass.setdefault('install_lib',install_lib) + + if self.features: + self._set_global_opts_from_features() + + def parse_command_line(self): + """Process features after parsing command line options""" + result = _Distribution.parse_command_line(self) + if self.features: + self._finalize_features() + return result + + def _feature_attrname(self,name): + """Convert feature name to corresponding option attribute name""" + return 'with_'+name.replace('-','_') + + def _set_global_opts_from_features(self): + """Add --with-X/--without-X options based on optional features""" + + go = [] + no = self.negative_opt.copy() + + for name,feature in self.features.items(): + self._set_feature(name,None) + feature.validate(self) + + if feature.optional: + descr = feature.description + incdef = ' (default)' + excdef='' + 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)) + no['without-'+name] = 'with-'+name + + self.global_options = self.feature_options = go + self.global_options + self.negative_opt = self.feature_negopt = no + + def _finalize_features(self): + """Add/remove features and resolve dependencies between them""" + + # First, flag all the enabled items (and thus their dependencies) + for name,feature in self.features.items(): + enabled = self.feature_is_included(name) + if enabled or (enabled is None and feature.include_by_default()): + feature.include_in(self) + self._set_feature(name,1) + + # Then disable the rest, so that off-by-default features don't + # get flagged as errors when they're required by an enabled feature + for name,feature in self.features.items(): + if not self.feature_is_included(name): + feature.exclude_from(self) + self._set_feature(name,0) + + def _set_feature(self,name,status): + """Set feature's inclusion status""" + setattr(self,self._feature_attrname(name),status) + + def feature_is_included(self,name): + """Return 1 if feature is included, 0 if excluded, 'None' if unknown""" + return getattr(self,self._feature_attrname(name)) + + def include_feature(self,name): + """Request inclusion of feature named 'name'""" + + if self.feature_is_included(name)==0: + descr = self.features[name].description + raise DistutilsOptionError( + descr + " is required, but was excluded or is not available" + ) + self.features[name].include_in(self) + self._set_feature(name,1) + + def include(self,**attrs): + """Add items to distribution that are named in keyword arguments + + For example, 'dist.exclude(py_modules=["x"])' would add 'x' to + the distribution's 'py_modules' attribute, if it was not already + there. + + Currently, this method only supports inclusion for attributes that are + lists or tuples. If you need to add support for adding to other + attributes in this or a subclass, you can add an '_include_X' method, + where 'X' is the name of the attribute. The method will be called with + the value passed to 'include()'. So, 'dist.include(foo={"bar":"baz"})' + will try to call 'dist._include_foo({"bar":"baz"})', which can then + handle whatever special inclusion logic is needed. + """ + for k,v in attrs.items(): + include = getattr(self, '_include_'+k, None) + if include: + include(v) + else: + self._include_misc(k,v) + + def exclude_package(self,package): + """Remove packages, modules, and extensions in named package""" + + pfx = package+'.' + if self.packages: + self.packages = [ + p for p in self.packages + if p<>package and not p.startswith(pfx) + ] + + if self.py_modules: + self.py_modules = [ + p for p in self.py_modules + if p<>package and not p.startswith(pfx) + ] + + if self.ext_modules: + self.ext_modules = [ + p for p in self.ext_modules + if p.name<>package and not p.name.startswith(pfx) + ] + + + def has_contents_for(self,package): + """Return true if 'exclude_package(package)' would do something""" + + pfx = package+'.' + + for p in self.packages or (): + if p==package or p.startswith(pfx): + return True + + for p in self.py_modules or (): + if p==package or p.startswith(pfx): + return True + + for p in self.ext_modules or (): + if p.name==package or p.name.startswith(pfx): + return True + + + def _exclude_misc(self,name,value): + """Handle 'exclude()' for list/tuple attrs without a special handler""" + if not isinstance(value,sequence): + raise DistutilsSetupError( + "%s: setting must be a list or tuple (%r)" % (name, value) + ) + try: + old = getattr(self,name) + except AttributeError: + raise DistutilsSetupError( + "%s: No such distribution setting" % name + ) + if old is not None and not isinstance(old,sequence): + raise DistutilsSetupError( + name+": this setting cannot be changed via include/exclude" + ) + elif old: + setattr(self,name,[item for item in old if item not in value]) + + def _include_misc(self,name,value): + """Handle 'include()' for list/tuple attrs without a special handler""" + + if not isinstance(value,sequence): + raise DistutilsSetupError( + "%s: setting must be a list (%r)" % (name, value) + ) + try: + old = getattr(self,name) + except AttributeError: + raise DistutilsSetupError( + "%s: No such distribution setting" % name + ) + if old is None: + setattr(self,name,value) + elif not isinstance(old,sequence): + raise DistutilsSetupError( + name+": this setting cannot be changed via include/exclude" + ) + else: + setattr(self,name,old+[item for item in value if item not in old]) + + def exclude(self,**attrs): + """Remove items from distribution that are named in keyword arguments + + For example, 'dist.exclude(py_modules=["x"])' would remove 'x' from + the distribution's 'py_modules' attribute. Excluding packages uses + the 'exclude_package()' method, so all of the package's contained + packages, modules, and extensions are also excluded. + + Currently, this method only supports exclusion from attributes that are + lists or tuples. If you need to add support for excluding from other + attributes in this or a subclass, you can add an '_exclude_X' method, + where 'X' is the name of the attribute. The method will be called with + the value passed to 'exclude()'. So, 'dist.exclude(foo={"bar":"baz"})' + will try to call 'dist._exclude_foo({"bar":"baz"})', which can then + handle whatever special exclusion logic is needed. + """ + for k,v in attrs.items(): + exclude = getattr(self, '_exclude_'+k, None) + if exclude: + exclude(v) + else: + self._exclude_misc(k,v) + + def _exclude_packages(self,packages): + if not isinstance(packages,sequence): + raise DistutilsSetupError( + "packages: setting must be a list or tuple (%r)" % (packages,) + ) + map(self.exclude_package, packages) + + def _parse_command_opts(self, parser, args): + # Remove --with-X/--without-X options when processing command args + self.global_options = self.__class__.global_options + self.negative_opt = self.__class__.negative_opt + return _Distribution._parse_command_opts(self, parser, args) + + def has_dependencies(self): + return not not self.requires + + + +class Feature: + + """A subset of the distribution that can be excluded if unneeded/wanted + + Features are created using these keyword arguments: + + 'description' -- a short, human readable description of the feature, to + be used in error messages, and option help messages. + + 'standard' -- if true, the feature is included by default if it is + available on the current system. Otherwise, the feature is only + included if requested via a command line '--with-X' option, or if + another included feature requires it. The default setting is 'False'. + + 'available' -- if true, the feature is available for installation on the + current system. The default setting is 'True'. + + 'optional' -- if true, the feature's inclusion can be controlled from the + command line, using the '--with-X' or '--without-X' options. If + false, the feature's inclusion status is determined automatically, + based on 'availabile', 'standard', and whether any other feature + requires it. The default setting is 'True'. + + 'requires' -- a string or sequence of strings naming features that should + also be included if this feature is included. Defaults to empty list. + + 'remove' -- a string or list of strings naming packages to be removed + from the distribution if this feature is *not* included. If the + feature *is* included, this argument is ignored. This argument exists + to support removing features that "crosscut" a distribution, such as + defining a 'tests' feature that removes all the 'tests' subpackages + provided by other features. The default for this argument is an empty + list. (Note: the named package(s) or modules must exist in the base + distribution when the 'setup()' function is initially called.) + + other keywords -- any other keyword arguments are saved, and passed to + the distribution's 'include()' and 'exclude()' methods when the + feature is included or excluded, respectively. So, for example, you + could pass 'packages=["a","b"]' to cause packages 'a' and 'b' to be + added or removed from the distribution as appropriate. + + A feature must include at least one 'requires', 'remove', or other + keyword argument. Otherwise, it can't affect the distribution in any way. + Note also that you can subclass 'Feature' to create your own specialized + feature types that modify the distribution in other ways when included or + excluded. See the docstrings for the various methods here for more detail. + Aside from the methods, the only feature attributes that distributions look + at are 'description' and 'optional'. + """ + + def __init__(self, description, standard=False, available=True, + optional=True, requires=(), remove=(), **extras + ): + + self.description = description + self.standard = standard + self.available = available + self.optional = optional + + if isinstance(requires,str): + requires = requires, + + self.requires = requires + + if isinstance(remove,str): + remove = remove, + + self.remove = remove + self.extras = extras + + if not remove and not requires and not extras: + raise DistutilsSetupError( + "Feature %s: must define 'requires', 'remove', or at least one" + " of 'packages', 'py_modules', etc." + ) + + + def include_by_default(self): + """Should this feature be included by default?""" + return self.available and self.standard + + + def include_in(self,dist): + + """Ensure feature and its requirements are included in distribution + + You may override this in a subclass to perform additional operations on + the distribution. Note that this method may be called more than once + per feature, and so should be idempotent. + + """ + + if not self.available: + raise DistutilsPlatformError( + self.description+" is required," + "but is not available on this platform" + ) + + dist.include(**self.extras) + + for f in self.requires: + dist.include_feature(f) + + + + def exclude_from(self,dist): + + """Ensure feature is excluded from distribution + + You may override this in a subclass to perform additional operations on + the distribution. This method will be called at most once per + feature, and only after all included features have been asked to + include themselves. + """ + + dist.exclude(**self.extras) + + if self.remove: + for item in self.remove: + dist.exclude_package(item) + + + + def validate(self,dist): + + """Verify that feature makes sense in context of distribution + + This method is called by the distribution just before it parses its + command line. It checks to ensure that the 'remove' attribute, if any, + contains only valid package/module names that are present in the base + distribution when 'setup()' is called. You may override it in a + subclass to perform any other required validation of the feature + against a target distribution. + """ + + for item in self.remove: + if not dist.has_contents_for(item): + raise DistutilsSetupError( + "%s wants to be able to remove %s, but the distribution" + " doesn't contain any packages or modules under %s" + % (self.description, item, item) + ) + + + + + + + + + + + + + + + + + + + + + + -- cgit v1.2.1 From 7ce55cabc53fe2c1378446ba0557e5f716c0cd31 Mon Sep 17 00:00:00 2001 From: PJ Eby Date: Sat, 20 Mar 2004 20:52:12 +0000 Subject: Flesh out 'depends' command to display dependencies' status, and halt if all requirements aren't met. (Also, check planned install location for the dependencies, as well as checking sys.path.) Also: * Allow 'Feature()' objects to include 'Require()' objects, so that dependencies can be optional * 'Require()' objects can set a homepage, whose URL will be displayed by the 'depends' command if the dependency needs to be installed. * Misc. fixes/refactoring of version validation to properly handle "unknown" versions, and to decouple version fetching from version checking. * Updated TODO to remove various completed items. --HG-- branch : setuptools extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/trunk/setuptools%4040876 --- setuptools/dist.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 2941f26e..cd3af266 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -1,7 +1,7 @@ __all__ = ['Distribution', 'Feature'] - from distutils.core import Distribution as _Distribution from distutils.core import Extension +from setuptools.depends import Require from setuptools.command.build_py import build_py from setuptools.command.build_ext import build_ext from setuptools.command.install import install @@ -11,7 +11,6 @@ from distutils.errors import DistutilsSetupError sequence = tuple, list class Distribution(_Distribution): - """Distribution with support for features, tests, and package data This is an enhanced version of 'distutils.dist.Distribution' that @@ -67,7 +66,6 @@ class Distribution(_Distribution): self.cmdclass.setdefault('build_ext',build_ext) self.cmdclass.setdefault('install',install) self.cmdclass.setdefault('install_lib',install_lib) - if self.features: self._set_global_opts_from_features() @@ -288,7 +286,6 @@ class Distribution(_Distribution): class Feature: - """A subset of the distribution that can be excluded if unneeded/wanted Features are created using these keyword arguments: @@ -312,6 +309,8 @@ class Feature: 'requires' -- a string or sequence of strings naming features that should also be included if this feature is included. Defaults to empty list. + May also contain 'Require' objects that should be added/removed from + the distribution. 'remove' -- a string or list of strings naming packages to be removed from the distribution if this feature is *not* included. If the @@ -345,15 +344,15 @@ class Feature: self.standard = standard self.available = available self.optional = optional - - if isinstance(requires,str): + if isinstance(requires,(str,Require)): requires = requires, - self.requires = requires + self.requires = [r for r in requires if isinstance(r,str)] + er = [r for r in requires if not isinstance(r,str)] + if er: extras['requires'] = er if isinstance(remove,str): remove = remove, - self.remove = remove self.extras = extras @@ -368,7 +367,6 @@ class Feature: """Should this feature be included by default?""" return self.available and self.standard - def include_in(self,dist): """Ensure feature and its requirements are included in distribution -- cgit v1.2.1 From 2e83526e517ecb61170f061af4cbc213dfa1ef0b Mon Sep 17 00:00:00 2001 From: PJ Eby Date: Mon, 22 Mar 2004 01:12:31 +0000 Subject: Compute command line that should be passed to child setup scripts. Warn user if unsupported options are supplied, and cancel unless 'depends -i' (aka '--ignore-extra-args') was used. --HG-- branch : setuptools extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/trunk/setuptools%4040880 --- setuptools/dist.py | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index cd3af266..cb5a906b 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -285,6 +285,47 @@ class Distribution(_Distribution): + def get_cmdline_options(self): + """Return a '{cmd: {opt:val}}' map of all command-line options + + Option names are all long, but do not include the leading '--', and + contain dashes rather than underscores. If the option doesn't take + an argument (e.g. '--quiet'), the 'val' is 'None'. + + Note that options provided by config files are intentionally excluded. + """ + + d = {} + + for cmd,opts in self.command_options.items(): + + for opt,(src,val) in opts.items(): + + if src != "command line": + continue + + opt = opt.replace('_','-') + + if val==0: + cmdobj = self.get_command_obj(cmd) + neg_opt = self.negative_opt.copy() + neg_opt.update(getattr(cmdobj,'negative_opt',{})) + for neg,pos in neg_opt.items(): + if pos==opt: + opt=neg + val=None + break + else: + raise AssertionError("Shouldn't be able to get here") + + elif val==1: + val = None + + d.setdefault(cmd,{})[opt] = val + + return d + + class Feature: """A subset of the distribution that can be excluded if unneeded/wanted -- cgit v1.2.1 From dfc00cc146f9d8c387ec61c05bad3f716532ee2a Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Mon, 14 Jun 2004 14:15:34 +0000 Subject: Deal with the distutils on the head; package_data may already be supported. In this case, setuptools need not override the build_py command. --HG-- branch : setuptools extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/trunk/setuptools%4040900 --- setuptools/dist.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index cb5a906b..1c61924d 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -2,7 +2,6 @@ __all__ = ['Distribution', 'Feature'] from distutils.core import Distribution as _Distribution from distutils.core import Extension from setuptools.depends import Require -from setuptools.command.build_py import build_py from setuptools.command.build_ext import build_ext from setuptools.command.install import install from setuptools.command.install_lib import install_lib @@ -58,11 +57,13 @@ class Distribution(_Distribution): def __init__ (self, attrs=None): self.features = {} - self.package_data = {} self.test_suite = None self.requires = [] _Distribution.__init__(self,attrs) - self.cmdclass.setdefault('build_py',build_py) + if not hasattr(self, "package_data"): + from setuptools.command.build_py import build_py + self.package_data = {} + self.cmdclass.setdefault('build_py',build_py) self.cmdclass.setdefault('build_ext',build_ext) self.cmdclass.setdefault('install',install) self.cmdclass.setdefault('install_lib',install_lib) -- cgit v1.2.1 From 0f429b9bc6894b7a8fbb6cfa587a33a9b9d8ac8f Mon Sep 17 00:00:00 2001 From: Fred Drake Date: Tue, 15 Jun 2004 19:00:03 +0000 Subject: fix to make things continue to work when we're *not* running under Python 2.4; this was broken by my earlier change to avoid stomping on Python 2.4's build_py command --HG-- branch : setuptools extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/trunk/setuptools%4040902 --- setuptools/dist.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 1c61924d..4d297c93 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -56,13 +56,15 @@ class Distribution(_Distribution): """ def __init__ (self, attrs=None): + have_package_data = hasattr(self, "package_data") + if not have_package_data: + self.package_data = {} self.features = {} self.test_suite = None self.requires = [] _Distribution.__init__(self,attrs) - if not hasattr(self, "package_data"): + if not have_package_data: from setuptools.command.build_py import build_py - self.package_data = {} self.cmdclass.setdefault('build_py',build_py) self.cmdclass.setdefault('build_ext',build_ext) self.cmdclass.setdefault('install',install) -- cgit v1.2.1 From 18b9ae1e5df8c0c141970c23c9aa0589928655c6 Mon Sep 17 00:00:00 2001 From: PJ Eby Date: Sun, 12 Jun 2005 15:49:54 +0000 Subject: Restructure easy_install as a distutils "Command" object, so that it can access the distutils configuration and logging infrastructure, and can "inherit" options from a distutils setup script that wants to use it to install its own dependencies. --HG-- branch : setuptools extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/trunk/setuptools%4041052 --- setuptools/dist.py | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 81 insertions(+), 2 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 4d297c93..a39b4a13 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -1,4 +1,5 @@ __all__ = ['Distribution', 'Feature'] + from distutils.core import Distribution as _Distribution from distutils.core import Extension from setuptools.depends import Require @@ -7,8 +8,37 @@ from setuptools.command.install import install from setuptools.command.install_lib import install_lib from distutils.errors import DistutilsOptionError, DistutilsPlatformError from distutils.errors import DistutilsSetupError + sequence = tuple, list + + + + + + + + + + + + + + + + + + + + + + + + + + + + class Distribution(_Distribution): """Distribution with support for features, tests, and package data @@ -54,7 +84,6 @@ class Distribution(_Distribution): the distribution. They are used by the feature subsystem to configure the distribution for the included and excluded features. """ - def __init__ (self, attrs=None): have_package_data = hasattr(self, "package_data") if not have_package_data: @@ -83,6 +112,15 @@ class Distribution(_Distribution): """Convert feature name to corresponding option attribute name""" return 'with_'+name.replace('-','_') + + + + + + + + + def _set_global_opts_from_features(self): """Add --with-X/--without-X options based on optional features""" @@ -277,14 +315,55 @@ class Distribution(_Distribution): ) map(self.exclude_package, packages) + + + + + + + + + + + def _parse_command_opts(self, parser, args): # Remove --with-X/--without-X options when processing command args self.global_options = self.__class__.global_options self.negative_opt = self.__class__.negative_opt - return _Distribution._parse_command_opts(self, parser, args) + + # Handle commands that want to consume all remaining arguments + command = args[0] + nargs = _Distribution._parse_command_opts(self, parser, args) + + cmd_class = self.get_command_class(command) + if getattr(cmd_class,'command_consumes_arguments',None): + self.get_option_dict(command)['args'] = ("command line", nargs) + if nargs is not None: + return [] + + return nargs + def has_dependencies(self): return not not self.requires + + + + + + + + + + + + + + + + + + -- cgit v1.2.1 From 5a9445cd57c60bb47451d8a88ec12a7a865013b7 Mon Sep 17 00:00:00 2001 From: PJ Eby Date: Wed, 15 Jun 2005 02:19:42 +0000 Subject: Add bootstrap installation support that "hitches a ride" on other packages being installed via the normal distutils "setup.py install". Also, don't repeatedly download the setuptools egg if it's already in the target location. --HG-- branch : setuptools extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/trunk/setuptools%4041069 --- setuptools/dist.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index a39b4a13..f46a02f8 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -8,6 +8,7 @@ from setuptools.command.install import install from setuptools.command.install_lib import install_lib from distutils.errors import DistutilsOptionError, DistutilsPlatformError from distutils.errors import DistutilsSetupError +import setuptools sequence = tuple, list @@ -29,7 +30,6 @@ sequence = tuple, list - @@ -348,18 +348,18 @@ class Distribution(_Distribution): return not not self.requires + def run_commands(self): + if setuptools.bootstrap_install_from and 'install' in self.commands: + # Bootstrap self-installation of setuptools + from easy_install import easy_install + cmd = easy_install( + self, args=[setuptools.bootstrap_install_from], zip_ok=1 + ) + cmd.ensure_finalized() + cmd.run() + setuptools.bootstrap_install_from = None - - - - - - - - - - - + _Distribution.run_commands(self) -- cgit v1.2.1 From 643acd6ad1eb4aeebac199c91af001181c7786f3 Mon Sep 17 00:00:00 2001 From: PJ Eby Date: Mon, 27 Jun 2005 00:31:03 +0000 Subject: EasyInstall/setuptools 0.5a4: significant new features, including automatic installation of dependencies, the ability to specify dependencies in a setup script, and several new options to control EasyInstall's behavior. --HG-- branch : setuptools extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/trunk/setuptools%4041073 --- setuptools/dist.py | 151 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 96 insertions(+), 55 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index f46a02f8..742ce7a1 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -8,50 +8,44 @@ from setuptools.command.install import install from setuptools.command.install_lib import install_lib from distutils.errors import DistutilsOptionError, DistutilsPlatformError from distutils.errors import DistutilsSetupError -import setuptools +import setuptools, pkg_resources sequence = tuple, list - - - - - - - - - - - - - - - - - - - - - - - - - - - class Distribution(_Distribution): """Distribution with support for features, tests, and package data This is an enhanced version of 'distutils.dist.Distribution' that effectively adds the following new optional keyword arguments to 'setup()': + 'install_requires' -- a string or sequence of strings specifying project + versions that the distribution requires when installed, in the format + used by 'pkg_resources.require()'. They will be installed + automatically when the package is installed. If you wish to use + packages that are not available in PyPI, or want to give your users an + alternate download location, you can add a 'find_links' option to the + '[easy_install]' section of your project's 'setup.cfg' file, and then + setuptools will scan the listed web pages for links that satisfy the + requirements. + + 'extras_require' -- a dictionary mapping names of optional "extras" to the + additional requirement(s) that using those extras incurs. For example, + this:: + + extras_require = dict(reST = ["docutils>=0.3", "reSTedit"]) + + indicates that the distribution can optionally provide an extra + capability called "reST", but it can only be used if docutils and + reSTedit are installed. If the user installs your package using + EasyInstall and requests one of your extras, the corresponding + additional requirements will be installed if needed. + 'features' -- a dictionary mapping option names to 'setuptools.Feature' objects. Features are a portion of the distribution that can be included or excluded based on user options, inter-feature dependencies, and availability on the current system. Excluded features are omitted from all setup commands, including source and binary distributions, so you can create multiple distributions from the same source tree. - Feature names should be valid Python identifiers, except that they may contain the '-' (minus) sign. Features can be included or excluded via the command line options '--with-X' and '--without-X', where 'X' is @@ -84,6 +78,8 @@ class Distribution(_Distribution): the distribution. They are used by the feature subsystem to configure the distribution for the included and excluded features. """ + + def __init__ (self, attrs=None): have_package_data = hasattr(self, "package_data") if not have_package_data: @@ -91,16 +87,68 @@ class Distribution(_Distribution): self.features = {} self.test_suite = None self.requires = [] + self.install_requires = [] + self.extras_require = {} _Distribution.__init__(self,attrs) if not have_package_data: from setuptools.command.build_py import build_py self.cmdclass.setdefault('build_py',build_py) + self.cmdclass.setdefault('build_ext',build_ext) self.cmdclass.setdefault('install',install) self.cmdclass.setdefault('install_lib',install_lib) + + + + + + + + + + + + + + + + + + + + + + + + + def finalize_options(self): + _Distribution.finalize_options(self) + if self.features: self._set_global_opts_from_features() + if self.extra_path: + raise DistutilsSetupError( + "The 'extra_path' parameter is not needed when using " + "setuptools. Please remove it from your setup script." + ) + try: + list(pkg_resources.parse_requirements(self.install_requires)) + except (TypeError,ValueError): + raise DistutilsSetupError( + "'install_requires' must be a string or list of strings " + "containing valid project/version requirement specifiers" + ) + try: + for k,v in self.extras_require.items(): + list(pkg_resources.parse_requirements(v)) + 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 parse_command_line(self): """Process features after parsing command line options""" result = _Distribution.parse_command_line(self) @@ -108,19 +156,12 @@ class Distribution(_Distribution): self._finalize_features() return result + def _feature_attrname(self,name): """Convert feature name to corresponding option attribute name""" return 'with_'+name.replace('-','_') - - - - - - - - def _set_global_opts_from_features(self): """Add --with-X/--without-X options based on optional features""" @@ -343,29 +384,29 @@ class Distribution(_Distribution): return nargs - def has_dependencies(self): return not not self.requires - def run_commands(self): - if setuptools.bootstrap_install_from and 'install' in self.commands: + for cmd in self.commands: + if cmd=='install' and not cmd in self.have_run: + self.install_eggs() + else: + self.run_command(cmd) + + def install_eggs(self): + from easy_install import easy_install + cmd = easy_install(self, args="x") + cmd.ensure_finalized() # finalize before bdist_egg munges install cmd + self.run_command('bdist_egg') + args = [self.get_command_obj('bdist_egg').egg_output] + if setuptools.bootstrap_install_from: # Bootstrap self-installation of setuptools - from easy_install import easy_install - cmd = easy_install( - self, args=[setuptools.bootstrap_install_from], zip_ok=1 - ) - cmd.ensure_finalized() - cmd.run() - setuptools.bootstrap_install_from = None - - _Distribution.run_commands(self) - - - - - - + args.insert(0, setuptools.bootstrap_install_from) + cmd.args = args + cmd.run() + self.have_run['install'] = 1 + setuptools.bootstrap_install_from = None def get_cmdline_options(self): """Return a '{cmd: {opt:val}}' map of all command-line options -- cgit v1.2.1 From 52928dfe183fcd7e7e5684914522b1da47938ae1 Mon Sep 17 00:00:00 2001 From: PJ Eby Date: Mon, 4 Jul 2005 18:45:41 +0000 Subject: Fix a problem using bdist_egg with non-setuptools distributions. --HG-- branch : setuptools extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/trunk/setuptools%4041076 --- setuptools/dist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 742ce7a1..ebcc12f9 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -89,6 +89,7 @@ class Distribution(_Distribution): self.requires = [] self.install_requires = [] self.extras_require = {} + self.dist_files = [] _Distribution.__init__(self,attrs) if not have_package_data: from setuptools.command.build_py import build_py @@ -116,7 +117,6 @@ class Distribution(_Distribution): - -- cgit v1.2.1 From 8468e915d354af475ef47363cccafdc9d458b7a8 Mon Sep 17 00:00:00 2001 From: PJ Eby Date: Wed, 6 Jul 2005 02:10:48 +0000 Subject: Made ``easy_install`` a standard ``setuptools`` command, moving it from the ``easy_install`` module to ``setuptools.command.easy_install``. Note that if you were importing or extending it, you must now change your imports accordingly. ``easy_install.py`` is still installed as a script, but not as a module. --HG-- branch : setuptools extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/trunk/setuptools%4041079 --- setuptools/dist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index ebcc12f9..590ea165 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -395,7 +395,7 @@ class Distribution(_Distribution): self.run_command(cmd) def install_eggs(self): - from easy_install import easy_install + from setuptools.command.easy_install import easy_install cmd = easy_install(self, args="x") cmd.ensure_finalized() # finalize before bdist_egg munges install cmd self.run_command('bdist_egg') -- cgit v1.2.1 From d3add44007ad72fc795297dd208d9a37ac4b54a3 Mon Sep 17 00:00:00 2001 From: PJ Eby Date: Thu, 7 Jul 2005 02:03:25 +0000 Subject: Beefed up the "sdist" command so that if you don't have a MANIFEST.in, it will include all files under revision control (CVS or Subversion) in the current directory, and it will regenerate the list every time you create a source distribution, not just when you tell it to. This should make the default "do what you mean" more often than the distutils' default behavior did, while still retaining the old behavior in the presence of MANIFEST.in. --HG-- branch : setuptools extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/trunk/setuptools%4041087 --- setuptools/dist.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 590ea165..fc0f3eba 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -5,6 +5,7 @@ from distutils.core import Extension from setuptools.depends import Require from setuptools.command.build_ext import build_ext from setuptools.command.install import install +from setuptools.command.sdist import sdist from setuptools.command.install_lib import install_lib from distutils.errors import DistutilsOptionError, DistutilsPlatformError from distutils.errors import DistutilsSetupError @@ -79,7 +80,6 @@ class Distribution(_Distribution): distribution for the included and excluded features. """ - def __init__ (self, attrs=None): have_package_data = hasattr(self, "package_data") if not have_package_data: @@ -98,7 +98,7 @@ class Distribution(_Distribution): self.cmdclass.setdefault('build_ext',build_ext) self.cmdclass.setdefault('install',install) self.cmdclass.setdefault('install_lib',install_lib) - + self.cmdclass.setdefault('sdist',sdist) -- cgit v1.2.1 From 6050f5361738831a12debd373b9016a077e930df Mon Sep 17 00:00:00 2001 From: PJ Eby Date: Fri, 8 Jul 2005 05:09:23 +0000 Subject: Added support for defining command aliases in distutils configuration files, under the "[aliases]" section. To prevent recursion and to allow aliases to call the command of the same name, a given alias can be expanded only once per command-line invocation. You can define new aliases with the "alias" command, either for the local, global, or per-user configuration. --HG-- branch : setuptools extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/trunk/setuptools%4041094 --- setuptools/dist.py | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index fc0f3eba..8fcd19ea 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -372,10 +372,19 @@ class Distribution(_Distribution): self.global_options = self.__class__.global_options self.negative_opt = self.__class__.negative_opt - # Handle commands that want to consume all remaining arguments + # First, expand any aliases command = args[0] + aliases = self.get_option_dict('aliases') + while command in aliases: + src,alias = aliases[command] + del aliases[command] # ensure each alias can expand only once! + import shlex + args[:1] = shlex.split(alias,True) + command = args[0] + nargs = _Distribution._parse_command_opts(self, parser, args) + # Handle commands that want to consume all remaining arguments cmd_class = self.get_command_class(command) if getattr(cmd_class,'command_consumes_arguments',None): self.get_option_dict(command)['args'] = ("command line", nargs) @@ -384,6 +393,21 @@ class Distribution(_Distribution): return nargs + + + + + + + + + + + + + + + def has_dependencies(self): return not not self.requires @@ -408,6 +432,23 @@ class Distribution(_Distribution): self.have_run['install'] = 1 setuptools.bootstrap_install_from = None + + + + + + + + + + + + + + + + + def get_cmdline_options(self): """Return a '{cmd: {opt:val}}' map of all command-line options -- cgit v1.2.1 From 451377d0e877fc610d1bdf8181ba70a90e4c14cc Mon Sep 17 00:00:00 2001 From: PJ Eby Date: Sun, 10 Jul 2005 04:49:31 +0000 Subject: Detect and handle conflicts with "unmanaged" packages when installing packages managed by EasyInstall. Also, add an option to exclude source files from .egg distributions. --HG-- branch : setuptools extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/trunk/setuptools%4041109 --- setuptools/dist.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 8fcd19ea..4fef454f 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -420,13 +420,16 @@ class Distribution(_Distribution): def install_eggs(self): from setuptools.command.easy_install import easy_install - cmd = easy_install(self, args="x") + cmd = easy_install(self, args="x", ignore_conflicts_at_my_risk=1) cmd.ensure_finalized() # finalize before bdist_egg munges install cmd + self.run_command('bdist_egg') args = [self.get_command_obj('bdist_egg').egg_output] + if setuptools.bootstrap_install_from: # Bootstrap self-installation of setuptools args.insert(0, setuptools.bootstrap_install_from) + cmd.args = args cmd.run() self.have_run['install'] = 1 @@ -446,9 +449,6 @@ class Distribution(_Distribution): - - - def get_cmdline_options(self): """Return a '{cmd: {opt:val}}' map of all command-line options -- cgit v1.2.1 From 39fc2a15c4a236ae7b378ebb98aee545776d2ec1 Mon Sep 17 00:00:00 2001 From: PJ Eby Date: Sun, 10 Jul 2005 15:43:08 +0000 Subject: Implement ``namespace_packages`` keyword to ``setup()``. Added keyword summary to setuptools doc. Begin work on ``zip_safe`` flag. --HG-- branch : setuptools extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/trunk/setuptools%4041113 --- setuptools/dist.py | 93 +++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 67 insertions(+), 26 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 4fef454f..73627752 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -90,6 +90,8 @@ class Distribution(_Distribution): self.install_requires = [] self.extras_require = {} self.dist_files = [] + self.zip_safe = None + self.namespace_packages = None _Distribution.__init__(self,attrs) if not have_package_data: from setuptools.command.build_py import build_py @@ -100,19 +102,17 @@ class Distribution(_Distribution): self.cmdclass.setdefault('install_lib',install_lib) self.cmdclass.setdefault('sdist',sdist) + def parse_command_line(self): + """Process features after parsing command line options""" + result = _Distribution.parse_command_line(self) + if self.features: + self._finalize_features() + return result - - - - - - - - - - - + def _feature_attrname(self,name): + """Convert feature name to corresponding option attribute name""" + return 'with_'+name.replace('-','_') @@ -123,10 +123,8 @@ class Distribution(_Distribution): def finalize_options(self): _Distribution.finalize_options(self) - if self.features: self._set_global_opts_from_features() - if self.extra_path: raise DistutilsSetupError( "The 'extra_path' parameter is not needed when using " @@ -148,19 +146,21 @@ class Distribution(_Distribution): "strings or lists of strings containing valid project/version " "requirement specifiers." ) - - def parse_command_line(self): - """Process features after parsing command line options""" - result = _Distribution.parse_command_line(self) - if self.features: - self._finalize_features() - return result - - - def _feature_attrname(self,name): - """Convert feature name to corresponding option attribute name""" - return 'with_'+name.replace('-','_') - + if self.namespace_packages is not None: + try: + assert ''.join(self.namespace_packages)!=self.namespace_packages + except (TypeError,ValueError,AttributeError,AssertionError): + raise DistutilsSetupError( + "'namespace_packages' must be a sequence of strings" + ) + for nsp in self.namespace_packages: + for name in iter_distribution_names(self): + if name.startswith(nsp+'.'): break + else: + raise DistutilsSetupError( + "Distribution contains no modules or packages for " + + "namespace package %r" % nsp + ) def _set_global_opts_from_features(self): """Add --with-X/--without-X options based on optional features""" @@ -488,6 +488,47 @@ class Distribution(_Distribution): d.setdefault(cmd,{})[opt] = val return d + + +def iter_distribution_names(distribution): + """Yield all packages, modules, and extensions declared by distribution""" + + for pkg in distribution.packages or (): + yield pkg + + for module in distribution.py_modules or (): + yield module + + for ext in distribution.ext_modules or (): + if isinstance(ext,tuple): + name,buildinfo = ext + yield name + else: + yield ext.name + + + + + + + + + + + + + + + + + + + + + + + + class Feature: -- cgit v1.2.1 From 8618cfa8ac93431ffcede4f3987b559449bbbcb8 Mon Sep 17 00:00:00 2001 From: PJ Eby Date: Sun, 24 Jul 2005 17:59:27 +0000 Subject: Fix eager resource extraction. Add eager_resources setup() argument. Add support for obtaining project-level resources by making get_provider() accept Requirement objects. --HG-- branch : setuptools extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/trunk/setuptools%4041151 --- setuptools/dist.py | 69 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 55 insertions(+), 14 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 73627752..3c7ff852 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -92,6 +92,7 @@ class Distribution(_Distribution): self.dist_files = [] self.zip_safe = None self.namespace_packages = None + self.eager_resources = None _Distribution.__init__(self,attrs) if not have_package_data: from setuptools.command.build_py import build_py @@ -120,16 +121,18 @@ class Distribution(_Distribution): - def finalize_options(self): _Distribution.finalize_options(self) + if self.features: self._set_global_opts_from_features() + if self.extra_path: raise DistutilsSetupError( "The 'extra_path' parameter is not needed when using " "setuptools. Please remove it from your setup script." ) + try: list(pkg_resources.parse_requirements(self.install_requires)) except (TypeError,ValueError): @@ -137,6 +140,7 @@ class Distribution(_Distribution): "'install_requires' must be a string or list of strings " "containing valid project/version requirement specifiers" ) + try: for k,v in self.extras_require.items(): list(pkg_resources.parse_requirements(v)) @@ -146,22 +150,27 @@ class Distribution(_Distribution): "strings or lists of strings containing valid project/version " "requirement specifiers." ) - if self.namespace_packages is not None: - try: - assert ''.join(self.namespace_packages)!=self.namespace_packages - except (TypeError,ValueError,AttributeError,AssertionError): - raise DistutilsSetupError( - "'namespace_packages' must be a sequence of strings" - ) - for nsp in self.namespace_packages: - for name in iter_distribution_names(self): - if name.startswith(nsp+'.'): break - else: + + for attr in 'namespace_packages','eager_resources': + value = getattr(self,attr,None) + if value is not None: + try: + assert ''.join(value)!=value + except (TypeError,ValueError,AttributeError,AssertionError): raise DistutilsSetupError( - "Distribution contains no modules or packages for " + - "namespace package %r" % nsp + "%r must be a list of strings (got %r)" % (attr,value) ) + + for nsp in self.namespace_packages or (): + for name in iter_distribution_names(self): + if name.startswith(nsp+'.'): break + else: + raise DistutilsSetupError( + "Distribution contains no modules or packages for " + + "namespace package %r" % nsp + ) + def _set_global_opts_from_features(self): """Add --with-X/--without-X options based on optional features""" @@ -186,6 +195,14 @@ class Distribution(_Distribution): self.global_options = self.feature_options = go + self.global_options self.negative_opt = self.feature_negopt = no + + + + + + + + def _finalize_features(self): """Add/remove features and resolve dependencies between them""" @@ -203,6 +220,30 @@ class Distribution(_Distribution): feature.exclude_from(self) self._set_feature(name,0) + + + + + + + + + + + + + + + + + + + + + + + + def _set_feature(self,name,status): """Set feature's inclusion status""" setattr(self,self._feature_attrname(name),status) -- cgit v1.2.1 From 1c40632b88d76aea178e751483645ec3d32c81d9 Mon Sep 17 00:00:00 2001 From: PJ Eby Date: Sun, 24 Jul 2005 22:47:06 +0000 Subject: Implement "entry points" for dynamic discovery of drivers and plugins. Change setuptools to discover setup commands using an entry point group called "distutils.commands". Thanks to Ian Bicking for the suggestion that led to designing this super-cool feature. --HG-- branch : setuptools extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/trunk/setuptools%4041152 --- setuptools/dist.py | 69 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 55 insertions(+), 14 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 3c7ff852..a603ade0 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -11,6 +11,32 @@ from distutils.errors import DistutilsOptionError, DistutilsPlatformError from distutils.errors import DistutilsSetupError import setuptools, pkg_resources +def get_command_class(self, command): + """Pluggable version of get_command_class()""" + if command in self.cmdclass: + return self.cmdclass[command] + + for dist in pkg_resources.working_set: + if dist.get_entry_info('distutils.commands',command): + cmdclass = dist.load_entry_point('distutils.commands',command) + self.cmdclass[command] = cmdclass + return cmdclass + else: + return _old_get_command_class(self, command) + +def print_commands(self): + for dist in pkg_resources.working_set: + for cmd,ep in dist.get_entry_map('distutils.commands').items(): + if cmd not in self.cmdclass: + cmdclass = ep.load() # don't require extras, we're not running + self.cmdclass[cmd] = cmdclass + return _old_print_commands(self) + +for meth in 'print_commands', 'get_command_class': + if getattr(_Distribution,meth).im_func.func_globals is not globals(): + globals()['_old_'+meth] = getattr(_Distribution,meth) + setattr(_Distribution, meth, globals()[meth]) + sequence = tuple, list class Distribution(_Distribution): @@ -80,6 +106,21 @@ class Distribution(_Distribution): distribution for the included and excluded features. """ + + + + + + + + + + + + + + + def __init__ (self, attrs=None): have_package_data = hasattr(self, "package_data") if not have_package_data: @@ -93,15 +134,9 @@ class Distribution(_Distribution): self.zip_safe = None self.namespace_packages = None self.eager_resources = None + self.entry_points = None _Distribution.__init__(self,attrs) - if not have_package_data: - from setuptools.command.build_py import build_py - self.cmdclass.setdefault('build_py',build_py) - self.cmdclass.setdefault('build_ext',build_ext) - self.cmdclass.setdefault('install',install) - self.cmdclass.setdefault('install_lib',install_lib) - self.cmdclass.setdefault('sdist',sdist) def parse_command_line(self): """Process features after parsing command line options""" @@ -121,6 +156,12 @@ class Distribution(_Distribution): + + + + + + def finalize_options(self): _Distribution.finalize_options(self) @@ -171,6 +212,12 @@ class Distribution(_Distribution): "namespace package %r" % nsp ) + if self.entry_points is not None: + try: + pkg_resources.EntryPoint.parse_map(self.entry_points) + except ValueError, e: + raise DistutilsSetupError(e) + def _set_global_opts_from_features(self): """Add --with-X/--without-X options based on optional features""" @@ -197,12 +244,6 @@ class Distribution(_Distribution): - - - - - - def _finalize_features(self): """Add/remove features and resolve dependencies between them""" @@ -420,7 +461,7 @@ class Distribution(_Distribution): src,alias = aliases[command] del aliases[command] # ensure each alias can expand only once! import shlex - args[:1] = shlex.split(alias,True) + args[:1] = shlex.split(alias,True) command = args[0] nargs = _Distribution._parse_command_opts(self, parser, args) -- cgit v1.2.1 From 54bb8e4003534a9af54028ef719eda5ba6088bac Mon Sep 17 00:00:00 2001 From: PJ Eby Date: Mon, 25 Jul 2005 03:12:51 +0000 Subject: Misc. bug fixes and doc additions. Add 'iter_entry_points()' API. --HG-- branch : setuptools extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/trunk/setuptools%4041153 --- setuptools/dist.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index a603ade0..40234b4e 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -16,20 +16,17 @@ def get_command_class(self, command): if command in self.cmdclass: return self.cmdclass[command] - for dist in pkg_resources.working_set: - if dist.get_entry_info('distutils.commands',command): - cmdclass = dist.load_entry_point('distutils.commands',command) - self.cmdclass[command] = cmdclass - return cmdclass + for ep in pkg_resources.iter_entry_points('distutils.commands',command): + self.cmdclass[command] = cmdclass = ep.load() + return cmdclass else: return _old_get_command_class(self, command) def print_commands(self): - for dist in pkg_resources.working_set: - for cmd,ep in dist.get_entry_map('distutils.commands').items(): - if cmd not in self.cmdclass: - cmdclass = ep.load() # don't require extras, we're not running - self.cmdclass[cmd] = cmdclass + for ep in pkg_resources.iter_entry_points('distutils.commands'): + if ep.name not in self.cmdclass: + cmdclass = ep.load(False) # don't require extras, we're not running + self.cmdclass[ep.name] = cmdclass return _old_print_commands(self) for meth in 'print_commands', 'get_command_class': @@ -39,6 +36,9 @@ for meth in 'print_commands', 'get_command_class': sequence = tuple, list + + + class Distribution(_Distribution): """Distribution with support for features, tests, and package data -- cgit v1.2.1 From 8a29467d941a7983d5f6eadc5c0e1624417944b6 Mon Sep 17 00:00:00 2001 From: PJ Eby Date: Sat, 6 Aug 2005 18:46:28 +0000 Subject: Enhanced setuptools infrastructure to support distutils extensions that can be plugged in at setup() time to define new setup() arguments or distutils commands. This allows modularization and reuse of distutils extensions in a way that was previously not possible. --HG-- branch : setuptools extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/trunk/setuptools%4041180 --- setuptools/dist.py | 296 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 189 insertions(+), 107 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 40234b4e..6d226d68 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -9,36 +9,118 @@ from setuptools.command.sdist import sdist from setuptools.command.install_lib import install_lib from distutils.errors import DistutilsOptionError, DistutilsPlatformError from distutils.errors import DistutilsSetupError -import setuptools, pkg_resources - -def get_command_class(self, command): - """Pluggable version of get_command_class()""" - if command in self.cmdclass: - return self.cmdclass[command] - - for ep in pkg_resources.iter_entry_points('distutils.commands',command): - self.cmdclass[command] = cmdclass = ep.load() - return cmdclass - else: - return _old_get_command_class(self, command) - -def print_commands(self): - for ep in pkg_resources.iter_entry_points('distutils.commands'): - if ep.name not in self.cmdclass: - cmdclass = ep.load(False) # don't require extras, we're not running - self.cmdclass[ep.name] = cmdclass - return _old_print_commands(self) - -for meth in 'print_commands', 'get_command_class': - if getattr(_Distribution,meth).im_func.func_globals is not globals(): - globals()['_old_'+meth] = getattr(_Distribution,meth) - setattr(_Distribution, meth, globals()[meth]) +import setuptools, pkg_resources, distutils.core, distutils.dist, distutils.cmd +import os + +def _get_unpatched(cls): + """Protect against re-patching the distutils if reloaded + + Also ensures that no other distutils extension monkeypatched the distutils + first. + """ + while cls.__module__.startswith('setuptools'): + cls, = cls.__bases__ + if not cls.__module__.startswith('distutils'): + raise AssertionError( + "distutils has already been patched by %r" % cls + ) + return cls + +_Distribution = _get_unpatched(_Distribution) sequence = tuple, list + + + + + + +def assert_string_list(dist, attr, value): + """Verify that value is a string list or None""" + try: + assert ''.join(value)!=value + except (TypeError,ValueError,AttributeError,AssertionError): + raise DistutilsSetupError( + "%r must be a list of strings (got %r)" % (attr,value) + ) + +def check_nsp(dist, attr, value): + """Verify that namespace packages are valid""" + assert_string_list(dist,attr,value) + + for nsp in value: + for name in dist.iter_distribution_names(): + if name.startswith(nsp+'.'): break + else: + raise DistutilsSetupError( + "Distribution contains no modules or packages for " + + "namespace package %r" % nsp + ) + +def check_extras(dist, attr, value): + """Verify that extras_require mapping is valid""" + try: + for k,v in value.items(): + list(pkg_resources.parse_requirements(v)) + 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): + """Verify that value is True, False, 0, or 1""" + if bool(value) != value: + raise DistutilsSetupError( + "%r must be a boolean value (got %r)" % (attr,value) + ) + +def check_install_requires(dist, attr, value): + """Verify that install_requires is a valid requirements list""" + try: + list(pkg_resources.parse_requirements(value)) + except (TypeError,ValueError): + raise DistutilsSetupError( + "'install_requires' must be a string or list of strings " + "containing valid project/version requirement specifiers" + ) + +def check_entry_points(dist, attr, value): + """Verify that entry_points map is parseable""" + try: + pkg_resources.EntryPoint.parse_map(value) + except ValueError, e: + raise DistutilsSetupError(e) + + +def check_test_suite(dist, attr, value): + if not isinstance(value,basestring): + raise DistutilsSetupError("test_suite must be a string") + + + + + + + + + + + + + + + + + + + + class Distribution(_Distribution): """Distribution with support for features, tests, and package data @@ -125,16 +207,19 @@ class Distribution(_Distribution): have_package_data = hasattr(self, "package_data") if not have_package_data: self.package_data = {} + self.features = {} - self.test_suite = None self.requires = [] - self.install_requires = [] - self.extras_require = {} self.dist_files = [] - self.zip_safe = None - self.namespace_packages = None - self.eager_resources = None - self.entry_points = None + + if attrs and 'setup_requires' in attrs: + # Make sure we have any eggs needed to interpret 'attrs' + self.fetch_build_eggs(attrs.pop('setup_requires')) + + for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'): + if not hasattr(self,ep.name): + setattr(self,ep.name,None) + _Distribution.__init__(self,attrs) @@ -145,20 +230,17 @@ class Distribution(_Distribution): self._finalize_features() return result - def _feature_attrname(self,name): """Convert feature name to corresponding option attribute name""" return 'with_'+name.replace('-','_') - - - - - - - - - + def fetch_build_eggs(self, requires): + """Resolve pre-setup requirements""" + from pkg_resources import working_set, parse_requirements + for dist in working_set.resolve( + parse_requirements(requires), installer=self.fetch_build_egg + ): + working_set.add(dist) @@ -174,49 +256,34 @@ class Distribution(_Distribution): "setuptools. Please remove it from your setup script." ) - try: - list(pkg_resources.parse_requirements(self.install_requires)) - except (TypeError,ValueError): - raise DistutilsSetupError( - "'install_requires' must be a string or list of strings " - "containing valid project/version requirement specifiers" - ) + for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'): + value = getattr(self,ep.name,None) + if value is not None: + ep.require(installer=self.fetch_build_egg) + ep.load()(self, ep.name, value) + + def fetch_build_egg(self, req): + """Fetch an egg needed for building""" try: - for k,v in self.extras_require.items(): - list(pkg_resources.parse_requirements(v)) - 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." + cmd = self._egg_fetcher + except AttributeError: + from setuptools.command.easy_install import easy_install + cmd = easy_install( + self.__class__({'script_args':['easy_install']}), + args="x", install_dir=os.curdir, exclude_scripts=True, + always_copy=False, build_directory=None, editable=False, + upgrade=False ) + cmd.ensure_finalized() + cmd.zip_ok = None # override any setup.cfg setting for these + cmd.build_directory = None + self._egg_fetcher = cmd - for attr in 'namespace_packages','eager_resources': - value = getattr(self,attr,None) - if value is not None: - try: - assert ''.join(value)!=value - except (TypeError,ValueError,AttributeError,AssertionError): - raise DistutilsSetupError( - "%r must be a list of strings (got %r)" % (attr,value) - ) + return cmd.easy_install(req) - for nsp in self.namespace_packages or (): - for name in iter_distribution_names(self): - if name.startswith(nsp+'.'): break - else: - raise DistutilsSetupError( - "Distribution contains no modules or packages for " + - "namespace package %r" % nsp - ) - if self.entry_points is not None: - try: - pkg_resources.EntryPoint.parse_map(self.entry_points) - except ValueError, e: - raise DistutilsSetupError(e) def _set_global_opts_from_features(self): """Add --with-X/--without-X options based on optional features""" @@ -244,22 +311,7 @@ class Distribution(_Distribution): - def _finalize_features(self): - """Add/remove features and resolve dependencies between them""" - - # First, flag all the enabled items (and thus their dependencies) - for name,feature in self.features.items(): - enabled = self.feature_is_included(name) - if enabled or (enabled is None and feature.include_by_default()): - feature.include_in(self) - self._set_feature(name,1) - # Then disable the rest, so that off-by-default features don't - # get flagged as errors when they're required by an enabled feature - for name,feature in self.features.items(): - if not self.feature_is_included(name): - feature.exclude_from(self) - self._set_feature(name,0) @@ -274,12 +326,42 @@ class Distribution(_Distribution): + def _finalize_features(self): + """Add/remove features and resolve dependencies between them""" + # First, flag all the enabled items (and thus their dependencies) + for name,feature in self.features.items(): + enabled = self.feature_is_included(name) + if enabled or (enabled is None and feature.include_by_default()): + feature.include_in(self) + self._set_feature(name,1) + # Then disable the rest, so that off-by-default features don't + # get flagged as errors when they're required by an enabled feature + for name,feature in self.features.items(): + if not self.feature_is_included(name): + feature.exclude_from(self) + self._set_feature(name,0) + def get_command_class(self, command): + """Pluggable version of get_command_class()""" + if command in self.cmdclass: + return self.cmdclass[command] + for ep in pkg_resources.iter_entry_points('distutils.commands',command): + ep.require(installer=self.fetch_build_egg) + self.cmdclass[command] = cmdclass = ep.load() + return cmdclass + else: + return _Distribution.get_command_class(self, command) + def print_commands(self): + for ep in pkg_resources.iter_entry_points('distutils.commands'): + if ep.name not in self.cmdclass: + cmdclass = ep.load(False) # don't require extras, we're not running + self.cmdclass[ep.name] = cmdclass + return _Distribution.print_commands(self) @@ -572,25 +654,25 @@ class Distribution(_Distribution): return d -def iter_distribution_names(distribution): - """Yield all packages, modules, and extensions declared by distribution""" - - for pkg in distribution.packages or (): - yield pkg - - for module in distribution.py_modules or (): - yield module - - for ext in distribution.ext_modules or (): - if isinstance(ext,tuple): - name,buildinfo = ext - yield name - else: - yield ext.name + def iter_distribution_names(self): + """Yield all packages, modules, and extension names in distribution""" + for pkg in self.packages or (): + yield pkg + for module in self.py_modules or (): + yield module + for ext in self.ext_modules or (): + if isinstance(ext,tuple): + name,buildinfo = ext + yield name + else: + yield ext.name +# Install it throughout the distutils +for module in distutils.dist, distutils.core, distutils.cmd: + module.Distribution = Distribution -- cgit v1.2.1 From 8afe820f59c3b63795bd235f2800b0e1329eb7e1 Mon Sep 17 00:00:00 2001 From: PJ Eby Date: Sat, 6 Aug 2005 19:29:49 +0000 Subject: Got rid of the no-longer meaningful "depends" command. Consolidated the replacement of the "install" command so that installation is always via easy_install, but doesn't use the previous kludgy intereception technique. Allow ``extra_path`` to be set, but ignore it, so that when easy_install wraps a package that uses it, there won't be any confusion as to the desired installation location. --HG-- branch : setuptools extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/trunk/setuptools%4041181 --- setuptools/dist.py | 57 ++++++++---------------------------------------------- 1 file changed, 8 insertions(+), 49 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 6d226d68..02458858 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -207,9 +207,8 @@ class Distribution(_Distribution): have_package_data = hasattr(self, "package_data") if not have_package_data: self.package_data = {} - + self.requires = [] # XXX self.features = {} - self.requires = [] self.dist_files = [] if attrs and 'setup_requires' in attrs: @@ -244,18 +243,13 @@ class Distribution(_Distribution): + def finalize_options(self): _Distribution.finalize_options(self) if self.features: self._set_global_opts_from_features() - if self.extra_path: - raise DistutilsSetupError( - "The 'extra_path' parameter is not needed when using " - "setuptools. Please remove it from your setup script." - ) - for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'): value = getattr(self,ep.name,None) if value is not None: @@ -285,6 +279,12 @@ class Distribution(_Distribution): + + + + + + def _set_global_opts_from_features(self): """Add --with-X/--without-X options based on optional features""" @@ -572,47 +572,6 @@ class Distribution(_Distribution): - def has_dependencies(self): - return not not self.requires - - def run_commands(self): - for cmd in self.commands: - if cmd=='install' and not cmd in self.have_run: - self.install_eggs() - else: - self.run_command(cmd) - - def install_eggs(self): - from setuptools.command.easy_install import easy_install - cmd = easy_install(self, args="x", ignore_conflicts_at_my_risk=1) - cmd.ensure_finalized() # finalize before bdist_egg munges install cmd - - self.run_command('bdist_egg') - args = [self.get_command_obj('bdist_egg').egg_output] - - if setuptools.bootstrap_install_from: - # Bootstrap self-installation of setuptools - args.insert(0, setuptools.bootstrap_install_from) - - cmd.args = args - cmd.run() - self.have_run['install'] = 1 - setuptools.bootstrap_install_from = None - - - - - - - - - - - - - - - def get_cmdline_options(self): """Return a '{cmd: {opt:val}}' map of all command-line options -- cgit v1.2.1 From a3ae3c46f0619d1273ed651f50352f5bb0425a80 Mon Sep 17 00:00:00 2001 From: PJ Eby Date: Sun, 14 Aug 2005 20:16:28 +0000 Subject: Allow distributing an empty namespace package. --HG-- branch : setuptools extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/trunk/setuptools%4041199 --- setuptools/dist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 02458858..1285c340 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -54,7 +54,7 @@ def check_nsp(dist, attr, value): for nsp in value: for name in dist.iter_distribution_names(): - if name.startswith(nsp+'.'): break + if name==nsp or name.startswith(nsp+'.'): break else: raise DistutilsSetupError( "Distribution contains no modules or packages for " + -- cgit v1.2.1 From a20ad75bb1be6af2e06ad05956fc6001fcc3437b Mon Sep 17 00:00:00 2001 From: PJ Eby Date: Sun, 14 Aug 2005 20:18:56 +0000 Subject: On second thought, don't. :( Walter Doerwald's situation isn't really compatible with namespace packages, even if I do manage to hack up a way to make it work. --HG-- branch : setuptools extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/trunk/setuptools%4041200 --- setuptools/dist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 1285c340..02458858 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -54,7 +54,7 @@ def check_nsp(dist, attr, value): for nsp in value: for name in dist.iter_distribution_names(): - if name==nsp or name.startswith(nsp+'.'): break + if name.startswith(nsp+'.'): break else: raise DistutilsSetupError( "Distribution contains no modules or packages for " + -- cgit v1.2.1 From 0993e96461d584ff9abdc888dda4081cf5fe8ac7 Mon Sep 17 00:00:00 2001 From: PJ Eby Date: Sun, 14 Aug 2005 21:14:46 +0000 Subject: Minor refactoring of code that checks a distribution's contents. --HG-- branch : setuptools extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/trunk/setuptools%4041203 --- setuptools/dist.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 02458858..31a07827 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -53,9 +53,7 @@ def check_nsp(dist, attr, value): assert_string_list(dist,attr,value) for nsp in value: - for name in dist.iter_distribution_names(): - if name.startswith(nsp+'.'): break - else: + if not dist.has_contents_for(nsp): raise DistutilsSetupError( "Distribution contains no modules or packages for " + "namespace package %r" % nsp @@ -80,6 +78,8 @@ def assert_bool(dist, attr, value): "%r must be a boolean value (got %r)" % (attr,value) ) + + def check_install_requires(dist, attr, value): """Verify that install_requires is a valid requirements list""" try: @@ -436,17 +436,17 @@ class Distribution(_Distribution): pfx = package+'.' - for p in self.packages or (): + for p in self.iter_distribution_names(): if p==package or p.startswith(pfx): return True - for p in self.py_modules or (): - if p==package or p.startswith(pfx): - return True - for p in self.ext_modules or (): - if p.name==package or p.name.startswith(pfx): - return True + + + + + + def _exclude_misc(self,name,value): -- cgit v1.2.1 From f9aa7e9e50d68830954facf280a02b551357c524 Mon Sep 17 00:00:00 2001 From: PJ Eby Date: Sat, 5 Nov 2005 05:50:26 +0000 Subject: Fixed some problems with fresh checkouts of projects that don't include ``.egg-info/PKG-INFO`` under revision control and put the project's source code directly in the project directory. If such a package had any requirements that get processed before the ``egg_info`` command can be run, the setup scripts would fail with a "Missing 'Version:' header and/or PKG-INFO file" error, because the egg runtime interpreted the unbuilt metadata in a directory on ``sys.path`` (i.e. the current directory) as being a corrupted egg. Setuptools now monkeypatches the distribution metadata cache to pretend that the egg has valid version information, until it has a chance to make it actually be so (via the ``egg_info`` command). --HG-- branch : setuptools extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/trunk/setuptools%4041401 --- setuptools/dist.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 31a07827..f0ad6f8b 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -188,20 +188,20 @@ class Distribution(_Distribution): distribution for the included and excluded features. """ - - - - - - - - - - - - - - + _patched_dist = None + + def patch_missing_pkg_info(self, attrs): + # Fake up a replacement for the data that would normally come from + # PKG-INFO, but which might not yet be built if this is a fresh + # checkout. + # + if not attrs or 'name' not in attrs or 'version' not in attrs: + return + key = pkg_resources.safe_name(str(attrs['name'])).lower() + dist = pkg_resources.working_set.by_key.get(key) + if dist is not None and not dist.has_metadata('PKG-INFO'): + dist._version = pkg_resources.safe_version(str(attrs['version'])) + self._patched_dist = dist def __init__ (self, attrs=None): have_package_data = hasattr(self, "package_data") @@ -210,7 +210,7 @@ class Distribution(_Distribution): self.requires = [] # XXX self.features = {} self.dist_files = [] - + self.patch_missing_pkg_info(attrs) if attrs and 'setup_requires' in attrs: # Make sure we have any eggs needed to interpret 'attrs' self.fetch_build_eggs(attrs.pop('setup_requires')) -- cgit v1.2.1 From 016ae6c42a868bc36c950cd3dc04e75b6ecce7dc Mon Sep 17 00:00:00 2001 From: PJ Eby Date: Sat, 19 Nov 2005 20:38:40 +0000 Subject: Added ``tests_require`` keyword to ``setup()``, so that e.g. packages requiring ``nose`` to run unit tests can make this dependency optional unless the ``test`` command is run. --HG-- branch : setuptools extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/trunk/setuptools%4041483 --- setuptools/dist.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index f0ad6f8b..17c9f149 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -80,14 +80,14 @@ def assert_bool(dist, attr, value): -def check_install_requires(dist, attr, value): +def check_requirements(dist, attr, value): """Verify that install_requires is a valid requirements list""" try: list(pkg_resources.parse_requirements(value)) except (TypeError,ValueError): raise DistutilsSetupError( - "'install_requires' must be a string or list of strings " - "containing valid project/version requirement specifiers" + "%r must be a string or list of strings " + "containing valid project/version requirement specifiers" % (attr,) ) def check_entry_points(dist, attr, value): -- cgit v1.2.1 From d989230127c6e16c505096f6150db977e61478b3 Mon Sep 17 00:00:00 2001 From: PJ Eby Date: Thu, 15 Dec 2005 02:45:03 +0000 Subject: Added the ``exclude_package_data`` keyword to ``setup()``, allowing you to trim back files included via the ``package_data`` and ``include_package_data`` options. --HG-- branch : setuptools extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/trunk/setuptools%4041693 --- setuptools/dist.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 17c9f149..fb7df8ce 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -103,24 +103,24 @@ def check_test_suite(dist, attr, value): raise DistutilsSetupError("test_suite must be a string") +def check_package_data(dist, attr, value): + """Verify that value is a dictionary of package names to glob lists""" + if isinstance(value,dict): + for k,v in value.items(): + if not isinstance(k,str): break + try: iter(v) + except TypeError: + break + else: + return + raise DistutilsSetupError( + attr+" must be a dictionary mapping package names to lists of " + "wildcard patterns" + ) - - - - - - - - - - - - - - class Distribution(_Distribution): """Distribution with support for features, tests, and package data -- cgit v1.2.1 From bda5b372b2b631048897ec5ecee6eee98b3b06a9 Mon Sep 17 00:00:00 2001 From: PJ Eby Date: Thu, 5 Jan 2006 23:14:21 +0000 Subject: First draft of shared library build support. See tests/shlib_test for a trivial example. This has only been tested on Windows with a MinGW compiler, and the Mac OS support isn't finished. Testing w/other platforms+compilers would be helpful. --HG-- branch : setuptools extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/trunk/setuptools%4041927 --- setuptools/dist.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index fb7df8ce..4a0ae146 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -1,9 +1,7 @@ __all__ = ['Distribution', 'Feature'] from distutils.core import Distribution as _Distribution -from distutils.core import Extension from setuptools.depends import Require -from setuptools.command.build_ext import build_ext from setuptools.command.install import install from setuptools.command.sdist import sdist from setuptools.command.install_lib import install_lib @@ -39,6 +37,8 @@ sequence = tuple, list + + def assert_string_list(dist, attr, value): """Verify that value is a string list or None""" try: -- cgit v1.2.1 From a24a73e7be4e82afe5d0c96439fdc5432350987f Mon Sep 17 00:00:00 2001 From: PJ Eby Date: Sun, 12 Feb 2006 19:32:17 +0000 Subject: Workaround for packages that think 'version' is a number. --HG-- branch : setuptools extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/trunk/setuptools%4042337 --- setuptools/dist.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 4a0ae146..273ffe00 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -118,7 +118,7 @@ def check_package_data(dist, attr, value): "wildcard patterns" ) - + class Distribution(_Distribution): @@ -221,6 +221,9 @@ class Distribution(_Distribution): _Distribution.__init__(self,attrs) + if isinstance(self.metadata.version, (int,long,float)): + # Some people apparently take "version number" too literally :) + self.metadata.version = str(self.metadata.version) def parse_command_line(self): """Process features after parsing command line options""" @@ -241,9 +244,6 @@ class Distribution(_Distribution): ): working_set.add(dist) - - - def finalize_options(self): _Distribution.finalize_options(self) -- cgit v1.2.1 From 0e3d174d58cba20737bbb8fa4184755dd17c4b83 Mon Sep 17 00:00:00 2001 From: PJ Eby Date: Tue, 28 Feb 2006 20:59:27 +0000 Subject: When installing setup_requires/tests_require packages, use --multi-version so that '.' doesn't have to support .pth files. --HG-- branch : setuptools extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/trunk/setuptools%4042681 --- setuptools/dist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 273ffe00..d906eb72 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -267,7 +267,7 @@ class Distribution(_Distribution): self.__class__({'script_args':['easy_install']}), args="x", install_dir=os.curdir, exclude_scripts=True, always_copy=False, build_directory=None, editable=False, - upgrade=False + upgrade=False, multi_version=True ) cmd.ensure_finalized() cmd.zip_ok = None # override any setup.cfg setting for these -- cgit v1.2.1 From 81ad30d96508ee69a3e7ec43ca8fa9856428e114 Mon Sep 17 00:00:00 2001 From: PJ Eby Date: Tue, 28 Feb 2006 21:22:53 +0000 Subject: Fix setup_requires/tests_require/etc. not using setup.cfg or other config files for option defaults. --HG-- branch : setuptools extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/trunk/setuptools%4042684 --- setuptools/dist.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index d906eb72..06e7a71a 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -263,28 +263,28 @@ class Distribution(_Distribution): cmd = self._egg_fetcher 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 opts.keys(): + if key not in keep: + del opts[key] # don't use any other settings cmd = easy_install( - self.__class__({'script_args':['easy_install']}), - args="x", install_dir=os.curdir, exclude_scripts=True, + dist, args=["x"], install_dir=os.curdir, exclude_scripts=True, always_copy=False, build_directory=None, editable=False, upgrade=False, multi_version=True ) cmd.ensure_finalized() - cmd.zip_ok = None # override any setup.cfg setting for these - cmd.build_directory = None self._egg_fetcher = cmd return cmd.easy_install(req) - - - - - - - def _set_global_opts_from_features(self): """Add --with-X/--without-X options based on optional features""" -- cgit v1.2.1 From 4da195dc370305433ac4f448c647af8d9fa3691d Mon Sep 17 00:00:00 2001 From: PJ Eby Date: Wed, 29 Mar 2006 19:23:55 +0000 Subject: Implement dependency_links feature, courtesy of Tres Seaver's rough draft of a patch. --HG-- branch : setuptools extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/trunk/setuptools%4043423 --- setuptools/dist.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 06e7a71a..8e34f35d 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -211,16 +211,16 @@ class Distribution(_Distribution): self.features = {} self.dist_files = [] self.patch_missing_pkg_info(attrs) + # Make sure we have any eggs needed to interpret 'attrs' + if attrs and 'dependency_links' in attrs: + self.dependency_links = attrs.pop('dependency_links') + assert_string_list(self,'dependency_links',self.dependency_links) if attrs and 'setup_requires' in attrs: - # Make sure we have any eggs needed to interpret 'attrs' self.fetch_build_eggs(attrs.pop('setup_requires')) - for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'): if not hasattr(self,ep.name): setattr(self,ep.name,None) - _Distribution.__init__(self,attrs) - if isinstance(self.metadata.version, (int,long,float)): # Some people apparently take "version number" too literally :) self.metadata.version = str(self.metadata.version) @@ -246,7 +246,6 @@ class Distribution(_Distribution): def finalize_options(self): _Distribution.finalize_options(self) - if self.features: self._set_global_opts_from_features() @@ -256,7 +255,6 @@ class Distribution(_Distribution): ep.require(installer=self.fetch_build_egg) ep.load()(self, ep.name, value) - def fetch_build_egg(self, req): """Fetch an egg needed for building""" try: @@ -273,18 +271,20 @@ class Distribution(_Distribution): for key in opts.keys(): 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) cmd = easy_install( dist, args=["x"], install_dir=os.curdir, exclude_scripts=True, always_copy=False, build_directory=None, editable=False, - upgrade=False, multi_version=True + upgrade=False, multi_version=True, no_report = True ) cmd.ensure_finalized() self._egg_fetcher = cmd - return cmd.easy_install(req) - - def _set_global_opts_from_features(self): """Add --with-X/--without-X options based on optional features""" -- cgit v1.2.1 From 696739a1fc24ccb3ace56a2156b7199f6b85e194 Mon Sep 17 00:00:00 2001 From: PJ Eby Date: Wed, 29 Mar 2006 23:32:41 +0000 Subject: Added ``test_loader`` keyword to support custom test loaders. --HG-- branch : setuptools extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/trunk/setuptools%4043430 --- setuptools/dist.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 8e34f35d..b1ebeb47 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -28,15 +28,15 @@ _Distribution = _get_unpatched(_Distribution) sequence = tuple, list - - - - - - - - - +def check_importable(dist, attr, value): + try: + ep = pkg_resources.EntryPoint.parse('x='+value) + assert not ep.extras + except (TypeError,ValueError,AttributeError,AssertionError): + raise DistutilsSetupError( + "%r must be importable 'module:attrs' string (got %r)" + % (attr,value) + ) def assert_string_list(dist, attr, value): -- cgit v1.2.1 From 847ce20a9390a14d3c415d9127db4bf9b86af3cb Mon Sep 17 00:00:00 2001 From: PJ Eby Date: Mon, 24 Apr 2006 20:52:59 +0000 Subject: Backport 'module' fixes to 0.6 --HG-- branch : setuptools-0.6 extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/branches/setuptools-0.6%4045696 --- setuptools/dist.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index b1ebeb47..724d5c5d 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -624,10 +624,12 @@ class Distribution(_Distribution): for ext in self.ext_modules or (): if isinstance(ext,tuple): - name,buildinfo = ext - yield name + name, buildinfo = ext else: - yield ext.name + name = ext.name + if name.endswith('module'): + name = name[:-6] + yield name # Install it throughout the distutils for module in distutils.dist, distutils.core, distutils.cmd: @@ -650,8 +652,6 @@ for module in distutils.dist, distutils.core, distutils.cmd: - - class Feature: -- cgit v1.2.1 From 403e6845234a3eb83b5e2858edb5e204f6641842 Mon Sep 17 00:00:00 2001 From: PJ Eby Date: Fri, 14 Jul 2006 23:37:52 +0000 Subject: * Fixed ``AttributeError`` when trying to download a ``setup_requires`` dependency when a distribution lacks a ``dependency_links`` setting. * Made ``zip-safe`` and ``not-zip-safe`` flag files contain a single byte, so as to play better with packaging tools that complain about zero-length files. * Made ``setup.py develop`` respect the ``--no-deps`` option, which it previously was ignoring. (bug fixes backported from trunk) --HG-- branch : setuptools-0.6 extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/branches/setuptools-0.6%4050659 --- setuptools/dist.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 724d5c5d..73269574 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -212,8 +212,8 @@ class Distribution(_Distribution): self.dist_files = [] self.patch_missing_pkg_info(attrs) # Make sure we have any eggs needed to interpret 'attrs' - if attrs and 'dependency_links' in attrs: - self.dependency_links = attrs.pop('dependency_links') + if attrs is not None: + self.dependency_links = attrs.pop('dependency_links', []) assert_string_list(self,'dependency_links',self.dependency_links) if attrs and 'setup_requires' in attrs: self.fetch_build_eggs(attrs.pop('setup_requires')) -- cgit v1.2.1 From 3bf8c4fa854d9cbb9af700b3852b1fded03fea54 Mon Sep 17 00:00:00 2001 From: PJ Eby Date: Wed, 20 Sep 2006 20:48:18 +0000 Subject: Backport all known 2.5-compatibility fixes --HG-- branch : setuptools-0.6 extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/branches/setuptools-0.6%4051935 --- setuptools/dist.py | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 73269574..700c57dc 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -1,4 +1,4 @@ -__all__ = ['Distribution', 'Feature'] +__all__ = ['Distribution'] from distutils.core import Distribution as _Distribution from setuptools.depends import Require @@ -207,7 +207,7 @@ class Distribution(_Distribution): have_package_data = hasattr(self, "package_data") if not have_package_data: self.package_data = {} - self.requires = [] # XXX + self.require_features = [] self.features = {} self.dist_files = [] self.patch_missing_pkg_info(attrs) @@ -676,10 +676,10 @@ class Feature: based on 'availabile', 'standard', and whether any other feature requires it. The default setting is 'True'. - 'requires' -- a string or sequence of strings naming features that should - also be included if this feature is included. Defaults to empty list. - May also contain 'Require' objects that should be added/removed from - the distribution. + 'require_features' -- a string or sequence of strings naming features + that should also be included if this feature is included. Defaults to + empty list. May also contain 'Require' objects that should be + added/removed from the distribution. 'remove' -- a string or list of strings naming packages to be removed from the distribution if this feature is *not* included. If the @@ -704,34 +704,34 @@ class Feature: Aside from the methods, the only feature attributes that distributions look at are 'description' and 'optional'. """ - def __init__(self, description, standard=False, available=True, - optional=True, requires=(), remove=(), **extras + optional=True, require_features=(), remove=(), **extras ): self.description = description self.standard = standard self.available = available self.optional = optional - if isinstance(requires,(str,Require)): - requires = requires, + if isinstance(require_features,(str,Require)): + require_features = require_features, - self.requires = [r for r in requires if isinstance(r,str)] - er = [r for r in requires if not isinstance(r,str)] - if er: extras['requires'] = er + self.require_features = [ + r for r in require_features if isinstance(r,str) + ] + er = [r for r in require_features if not isinstance(r,str)] + if er: extras['require_features'] = er if isinstance(remove,str): remove = remove, self.remove = remove self.extras = extras - if not remove and not requires and not extras: + if not remove and not require_features and not extras: raise DistutilsSetupError( - "Feature %s: must define 'requires', 'remove', or at least one" + "Feature %s: must define 'require_features', 'remove', or at least one" " of 'packages', 'py_modules', etc." ) - def include_by_default(self): """Should this feature be included by default?""" return self.available and self.standard @@ -754,7 +754,7 @@ class Feature: dist.include(**self.extras) - for f in self.requires: + for f in self.require_features: dist.include_feature(f) -- cgit v1.2.1 From 798b6f9b3aeba38aa4701a3d3ccf2599b053dc95 Mon Sep 17 00:00:00 2001 From: PJ Eby Date: Thu, 14 Feb 2008 21:23:15 +0000 Subject: Added a warning if a namespace package is declared, but its parent package is not also declared as a namespace. (backport from trunk) --HG-- branch : setuptools-0.6 extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/branches/setuptools-0.6%4060817 --- setuptools/dist.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 700c57dc..30ff35e3 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -8,7 +8,7 @@ from setuptools.command.install_lib import install_lib from distutils.errors import DistutilsOptionError, DistutilsPlatformError from distutils.errors import DistutilsSetupError import setuptools, pkg_resources, distutils.core, distutils.dist, distutils.cmd -import os +import os, distutils.log def _get_unpatched(cls): """Protect against re-patching the distutils if reloaded @@ -51,13 +51,19 @@ def assert_string_list(dist, attr, value): def check_nsp(dist, attr, value): """Verify that namespace packages are valid""" assert_string_list(dist,attr,value) - for nsp in value: if not dist.has_contents_for(nsp): raise DistutilsSetupError( "Distribution contains no modules or packages for " + "namespace package %r" % nsp ) + if '.' in nsp: + parent = '.'.join(nsp.split('.')[:-1]) + if parent not in value: + distutils.log.warn( + "%r is declared as a package namespace, but %r is not:" + " please correct this in setup.py", nsp, parent + ) def check_extras(dist, attr, value): """Verify that extras_require mapping is valid""" @@ -71,15 +77,15 @@ def check_extras(dist, attr, value): "requirement specifiers." ) + + + def assert_bool(dist, attr, value): """Verify that value is True, False, 0, or 1""" if bool(value) != value: raise DistutilsSetupError( "%r must be a boolean value (got %r)" % (attr,value) ) - - - def check_requirements(dist, attr, value): """Verify that install_requires is a valid requirements list""" try: @@ -89,7 +95,6 @@ def check_requirements(dist, attr, value): "%r must be a string or list of strings " "containing valid project/version requirement specifiers" % (attr,) ) - def check_entry_points(dist, attr, value): """Verify that entry_points map is parseable""" try: @@ -97,12 +102,10 @@ def check_entry_points(dist, attr, value): except ValueError, e: raise DistutilsSetupError(e) - def check_test_suite(dist, attr, value): if not isinstance(value,basestring): raise DistutilsSetupError("test_suite must be a string") - def check_package_data(dist, attr, value): """Verify that value is a dictionary of package names to glob lists""" if isinstance(value,dict): @@ -118,9 +121,6 @@ def check_package_data(dist, attr, value): "wildcard patterns" ) - - - class Distribution(_Distribution): """Distribution with support for features, tests, and package data -- cgit v1.2.1 From 41854a0e9535b9142811e7815937d15c1a7c5b1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Sat, 12 Sep 2009 12:36:46 +0200 Subject: Add src_root attribute to support installing from a copy. --HG-- branch : distribute extra : rebase_source : 95242b20ab228862aeef205f399869f79e342f0e --- setuptools/dist.py | 1 + 1 file changed, 1 insertion(+) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 30ff35e3..7e9ab5c9 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -210,6 +210,7 @@ class Distribution(_Distribution): self.require_features = [] self.features = {} self.dist_files = [] + self.src_root = attrs.pop("src_root") self.patch_missing_pkg_info(attrs) # Make sure we have any eggs needed to interpret 'attrs' if attrs is not None: -- cgit v1.2.1 From 42b976684874ad63f962946a67d03eae51884a15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Sat, 12 Sep 2009 18:06:05 +0200 Subject: Add default for pop. --HG-- branch : distribute extra : rebase_source : a4655cb42947b8ea089e72b3d4c2ff2482a31e6a --- setuptools/dist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 7e9ab5c9..c2e57f4b 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -210,7 +210,7 @@ class Distribution(_Distribution): self.require_features = [] self.features = {} self.dist_files = [] - self.src_root = attrs.pop("src_root") + self.src_root = attrs.pop("src_root", None) self.patch_missing_pkg_info(attrs) # Make sure we have any eggs needed to interpret 'attrs' if attrs is not None: -- cgit v1.2.1 From 1a8b650faf34b6205a8a26168ab7ef886833f85c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Sun, 13 Sep 2009 13:50:29 +0200 Subject: Fix processing of convert_doctests_2to3. --HG-- branch : distribute extra : rebase_source : 101f51e5f7c364407e27b742aec5e02336936d8c --- setuptools/dist.py | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index c2e57f4b..f295ca4e 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -255,6 +255,11 @@ class Distribution(_Distribution): if value is not None: ep.require(installer=self.fetch_build_egg) ep.load()(self, ep.name, value) + if self.convert_doctests_2to3: + # XXX may convert to set here when we can rely on set being builtin + self.convert_doctests_2to3 = [os.path.abspath(p) for p in self.convert_doctests_2to3] + else: + self.convert_doctests_2to3 = [] def fetch_build_egg(self, req): """Fetch an egg needed for building""" -- cgit v1.2.1 From 15a0f215d79be0bd7cbf521395377fd7c3168dfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Sun, 13 Sep 2009 14:40:15 +0200 Subject: Deal with convert_doctests_2to3 not being set. --HG-- branch : distribute extra : rebase_source : c1952763f89af6f9c5e25e67982b96756d7336e3 --- setuptools/dist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index f295ca4e..fd23d9ba 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -255,7 +255,7 @@ class Distribution(_Distribution): if value is not None: ep.require(installer=self.fetch_build_egg) ep.load()(self, ep.name, value) - if self.convert_doctests_2to3: + if getattr(self, 'convert_doctests_2to3', None): # XXX may convert to set here when we can rely on set being builtin self.convert_doctests_2to3 = [os.path.abspath(p) for p in self.convert_doctests_2to3] else: -- cgit v1.2.1 From cfa83c08e6c6248f0085dfaab5c4f7edad6f8ec1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Sun, 13 Sep 2009 16:20:45 +0200 Subject: Support attrs is None. --HG-- branch : distribute extra : rebase_source : 7ab3d57b13cb2ec2d7d1b9aafc99f4ec0ea49b5a --- setuptools/dist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index fd23d9ba..531c94f4 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -210,7 +210,7 @@ class Distribution(_Distribution): self.require_features = [] self.features = {} self.dist_files = [] - self.src_root = attrs.pop("src_root", None) + self.src_root = attrs and attrs.pop("src_root", None) self.patch_missing_pkg_info(attrs) # Make sure we have any eggs needed to interpret 'attrs' if attrs is not None: -- cgit v1.2.1 From 7596dc0cf93965df85e36dcbca683270f5e9ded2 Mon Sep 17 00:00:00 2001 From: Lennart Regebro Date: Tue, 22 Sep 2009 17:25:53 +0200 Subject: Name changes of the parameters. --HG-- branch : distribute extra : rebase_source : fc921b526cda13b02a4bb0215f91ee04d03dca57 --- setuptools/dist.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 531c94f4..2246ab96 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -255,11 +255,11 @@ class Distribution(_Distribution): if value is not None: ep.require(installer=self.fetch_build_egg) ep.load()(self, ep.name, value) - if getattr(self, 'convert_doctests_2to3', None): + if getattr(self, 'convert_2to3_doctests', None): # XXX may convert to set here when we can rely on set being builtin - self.convert_doctests_2to3 = [os.path.abspath(p) for p in self.convert_doctests_2to3] + self.convert_2to3_doctests = [os.path.abspath(p) for p in self.convert_2to3_doctests] else: - self.convert_doctests_2to3 = [] + self.convert_2to3_doctests = [] def fetch_build_egg(self, req): """Fetch an egg needed for building""" -- cgit v1.2.1 From 9294929b0028f551a54dd48cc3325581933b3c5f Mon Sep 17 00:00:00 2001 From: PJ Eby Date: Mon, 12 Oct 2009 20:00:02 +0000 Subject: Major updates and fixes include: * Fix for the Python 2.6.3 build_ext API change * Support for the most recent Sourceforge download link insanity * Support for SVN 1.6 * Stop crashing on certain types of HTTP error * Stop re-trying URLs that already failed retrieval once * Fixes for various dependency management problems such as looping builds, re-downloading packages already present on sys.path (but not in a registered "site" directory), and randomly preferring local -f packages over local installed packages * Prevent lots of spurious "already imported from another path" warnings (e.g. when pkg_resources is imported late) * Ensure C libraries (as opposed to extensions) are also built when doing bdist_egg Other changes: * Misc. documentation fixes * Improved Jython support * Fewer warnings under Python 2.6+ * Warn when 'packages' uses paths instead of package names (because it causes other problems, like spurious "already imported" warnings) * Stop using /usr/bin/sw_vers on Mac OS (replaced w/'platform' module calls) Note: This is NOT a merge from Distribute; upon review, many of the tracker-submitted patches used as a basis for forking were incorrect, incomplete, introduced new bugs, or were not addressing the root causes. (E.g., one of the changes in this patch fixes three superficially unrelated issues in the setuptools bug tracker.) Careful review will be required if you want to merge this work back into Distribute. --HG-- branch : setuptools-0.6 extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/branches/setuptools-0.6%4075385 --- setuptools/dist.py | 53 +++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 47 insertions(+), 6 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 30ff35e3..c1218ef2 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -8,7 +8,7 @@ from setuptools.command.install_lib import install_lib from distutils.errors import DistutilsOptionError, DistutilsPlatformError from distutils.errors import DistutilsSetupError import setuptools, pkg_resources, distutils.core, distutils.dist, distutils.cmd -import os, distutils.log +import os, distutils.log, re def _get_unpatched(cls): """Protect against re-patching the distutils if reloaded @@ -61,8 +61,8 @@ def check_nsp(dist, attr, value): parent = '.'.join(nsp.split('.')[:-1]) if parent not in value: distutils.log.warn( - "%r is declared as a package namespace, but %r is not:" - " please correct this in setup.py", nsp, parent + "WARNING: %r is declared as a package namespace, but %r" + " is not: please correct this in setup.py", nsp, parent ) def check_extras(dist, attr, value): @@ -120,6 +120,47 @@ def check_package_data(dist, attr, value): attr+" must be a dictionary mapping package names to lists of " "wildcard patterns" ) + +def check_packages(dist, attr, value): + for pkgname in value: + if not re.match(r'\w+(\.\w+)*', pkgname): + distutils.log.warn( + "WARNING: %r not a valid package name; please use only" + ".-separated package names in setup.py", pkgname + ) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + class Distribution(_Distribution): """Distribution with support for features, tests, and package data @@ -415,19 +456,19 @@ class Distribution(_Distribution): if self.packages: self.packages = [ p for p in self.packages - if p<>package and not p.startswith(pfx) + if p!=package and not p.startswith(pfx) ] if self.py_modules: self.py_modules = [ p for p in self.py_modules - if p<>package and not p.startswith(pfx) + if p!=package and not p.startswith(pfx) ] if self.ext_modules: self.ext_modules = [ p for p in self.ext_modules - if p.name<>package and not p.name.startswith(pfx) + if p.name!=package and not p.name.startswith(pfx) ] -- cgit v1.2.1 From f0464d99bbc4d5e5b7d0a244d5f96befdabd1273 Mon Sep 17 00:00:00 2001 From: tarek Date: Sun, 1 Nov 2009 15:01:40 +0100 Subject: backporting the API Eby added in 0.6c11 --HG-- branch : distribute extra : rebase_source : 7e34c308ad98259f9344e10117750aeae3e8d2ea --- setuptools/dist.py | 32 +++++++++++--------------------- 1 file changed, 11 insertions(+), 21 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 2246ab96..fd4ca66b 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -1,5 +1,6 @@ __all__ = ['Distribution'] +import re from distutils.core import Distribution as _Distribution from setuptools.depends import Require from setuptools.command.install import install @@ -421,19 +422,19 @@ class Distribution(_Distribution): if self.packages: self.packages = [ p for p in self.packages - if p<>package and not p.startswith(pfx) + if p != package and not p.startswith(pfx) ] if self.py_modules: self.py_modules = [ p for p in self.py_modules - if p<>package and not p.startswith(pfx) + if p != package and not p.startswith(pfx) ] if self.ext_modules: self.ext_modules = [ p for p in self.ext_modules - if p.name<>package and not p.name.startswith(pfx) + if p.name != package and not p.name.startswith(pfx) ] @@ -805,22 +806,11 @@ class Feature: - - - - - - - - - - - - - - - - - - +def check_packages(dist, attr, value): + for pkgname in value: + if not re.match(r'\w+(\.\w+)*', pkgname): + distutils.log.warn( + "WARNING: %r not a valid package name; please use only" + ".-separated package names in setup.py", pkgname + ) -- cgit v1.2.1 From 6ad276bf9c860d9d62af0ec45c537cef03b3fc93 Mon Sep 17 00:00:00 2001 From: Erik Bray Date: Mon, 23 May 2011 10:52:19 -0400 Subject: setting to_scan to [] instead of None ensures (somewhat confusingly) that find_links are used, when available, to install each dist listed in setup_requires --HG-- branch : distribute extra : rebase_source : 4efd4ba05e527df60a33c55da30c19586a9cecdb --- setuptools/dist.py | 1 + 1 file changed, 1 insertion(+) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index fd4ca66b..0ad18122 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -266,6 +266,7 @@ class Distribution(_Distribution): """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']}) -- cgit v1.2.1 From 58a658b26d1c95b31d02050dcccd648d2e4ce27b Mon Sep 17 00:00:00 2001 From: Vinay Sajip Date: Mon, 20 Jun 2011 22:55:16 +0100 Subject: Changes to support 2.x and 3.x in the same codebase. --HG-- branch : distribute extra : rebase_source : 7d3608edee54a43789f0574d702fb839628b5071 --- setuptools/dist.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 0ad18122..ebe02065 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -1,11 +1,13 @@ __all__ = ['Distribution'] import re +import sys from distutils.core import Distribution as _Distribution from setuptools.depends import Require from setuptools.command.install import install from setuptools.command.sdist import sdist from setuptools.command.install_lib import install_lib +from setuptools.compat import numeric_types, basestring from distutils.errors import DistutilsOptionError, DistutilsPlatformError from distutils.errors import DistutilsSetupError import setuptools, pkg_resources, distutils.core, distutils.dist, distutils.cmd @@ -100,7 +102,8 @@ def check_entry_points(dist, attr, value): """Verify that entry_points map is parseable""" try: pkg_resources.EntryPoint.parse_map(value) - except ValueError, e: + except ValueError: + e = sys.exc_info()[1] raise DistutilsSetupError(e) def check_test_suite(dist, attr, value): @@ -223,7 +226,7 @@ class Distribution(_Distribution): if not hasattr(self,ep.name): setattr(self,ep.name,None) _Distribution.__init__(self,attrs) - if isinstance(self.metadata.version, (int,long,float)): + if isinstance(self.metadata.version, numeric_types): # Some people apparently take "version number" too literally :) self.metadata.version = str(self.metadata.version) @@ -526,7 +529,7 @@ class Distribution(_Distribution): raise DistutilsSetupError( "packages: setting must be a list or tuple (%r)" % (packages,) ) - map(self.exclude_package, packages) + list(map(self.exclude_package, packages)) -- cgit v1.2.1 From d847dbfeee2b2c85d7bc0a66813c33836a9850fa Mon Sep 17 00:00:00 2001 From: Erik Bray Date: Thu, 6 Sep 2012 15:51:09 -0400 Subject: Adds a fix for issue #318, including a regression test. This also fixes another test that was passing trivially due to *bug* in yet another test, ugh --HG-- branch : distribute extra : rebase_source : 476550766b7b756dced040ad4356b7685d6f062a --- setuptools/dist.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 0ad18122..6607cf7b 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -264,6 +264,7 @@ class Distribution(_Distribution): def fetch_build_egg(self, req): """Fetch an egg needed for building""" + try: cmd = self._egg_fetcher cmd.package_index.to_scan = [] @@ -287,7 +288,7 @@ class Distribution(_Distribution): cmd = easy_install( dist, args=["x"], install_dir=os.curdir, exclude_scripts=True, always_copy=False, build_directory=None, editable=False, - upgrade=False, multi_version=True, no_report = True + upgrade=False, multi_version=True, no_report=True, user=False ) cmd.ensure_finalized() self._egg_fetcher = cmd -- cgit v1.2.1 From dbbfab8fe8a8c146c2353f751d617c8761f6ddc4 Mon Sep 17 00:00:00 2001 From: Erik Bray Date: Tue, 11 Sep 2012 13:53:29 -0400 Subject: Fixes and adds a regression test for #323; required adding some new keyword arguments to existing pkg_resources methods. Also had to update how __path__ is handled for namespace packages to ensure that when a new egg distribution containing a namespace package is placed on sys.path, the entries in __path__ are in the same order they would have been in had that egg been on the path when pkg_resources was first imported --HG-- branch : distribute extra : rebase_source : 63a120c9397f6619d2768ec982e5c6b664c97e40 --- setuptools/dist.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 6607cf7b..2061c0a2 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -242,9 +242,10 @@ class Distribution(_Distribution): """Resolve pre-setup requirements""" from pkg_resources import working_set, parse_requirements for dist in working_set.resolve( - parse_requirements(requires), installer=self.fetch_build_egg + parse_requirements(requires), installer=self.fetch_build_egg, + replace_conflicting=True ): - working_set.add(dist) + working_set.add(dist, replace=True) def finalize_options(self): _Distribution.finalize_options(self) -- cgit v1.2.1 From 11270be66d228aacca12a211e3fdcb38988fb193 Mon Sep 17 00:00:00 2001 From: "Stefan H. Holek" Date: Mon, 8 Oct 2012 20:32:46 +0200 Subject: Print metadata in UTF-8 independent of platform. Fixes #311. --HG-- branch : distribute extra : rebase_source : 4ff0df4ad7d9ea8cee6342f9c642e4fe634b7f18 --- setuptools/dist.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 6607cf7b..afac180e 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -640,6 +640,38 @@ class Distribution(_Distribution): name = name[:-6] yield name + + def handle_display_options(self, option_order): + """If there were any non-global "display-only" options + (--help-commands or the metadata display options) on the command + line, display the requested info and return true; else return + false. + """ + import sys + + if sys.version_info < (3,) or self.help_commands: + return _Distribution.handle_display_options(self, option_order) + + # Stdout may be StringIO (e.g. in tests) + import io + if not isinstance(sys.stdout, io.TextIOWrapper): + return _Distribution.handle_display_options(self, option_order) + + # Print metadata in UTF-8 no matter the platform + encoding = sys.stdout.encoding + errors = sys.stdout.errors + newline = sys.platform != 'win32' and '\n' or None + line_buffering = sys.stdout.line_buffering + + sys.stdout = io.TextIOWrapper( + sys.stdout.detach(), 'utf-8', errors, newline, line_buffering) + try: + return _Distribution.handle_display_options(self, option_order) + finally: + sys.stdout = io.TextIOWrapper( + sys.stdout.detach(), encoding, errors, newline, line_buffering) + + # Install it throughout the distutils for module in distutils.dist, distutils.core, distutils.cmd: module.Distribution = Distribution -- cgit v1.2.1 From 7fbee015f7d31185ccfc890c59dbfab1c699db82 Mon Sep 17 00:00:00 2001 From: Gabriel Date: Fri, 23 Nov 2012 22:38:55 +0100 Subject: Don't wrap sys.stdout if it's in the correct encoding already. --HG-- branch : distribute extra : rebase_source : 314a8be1a2e63ceaf501ecb047a29f62302be0a0 --- setuptools/dist.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index afac180e..3e9e0254 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -657,6 +657,9 @@ class Distribution(_Distribution): if not isinstance(sys.stdout, io.TextIOWrapper): return _Distribution.handle_display_options(self, option_order) + if sys.stdout.encoding.lower() in ('utf-8', 'utf8'): + return _Distribution.handle_display_options(self, option_order) + # Print metadata in UTF-8 no matter the platform encoding = sys.stdout.encoding errors = sys.stdout.errors -- cgit v1.2.1 From 9d1e4a7a1f340147dca2f215dc33485c31f1834e Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 24 Nov 2012 10:23:09 -0500 Subject: Added comment and updated CHANGES --HG-- branch : distribute extra : rebase_source : 4c828b71eced1215219f5b16d881fa1f35972744 --- setuptools/dist.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 3e9e0254..998a4dbe 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -657,6 +657,8 @@ class Distribution(_Distribution): if not isinstance(sys.stdout, io.TextIOWrapper): return _Distribution.handle_display_options(self, option_order) + # Don't wrap stdout if utf-8 is already the encoding. Provides + # workaround for #334. if sys.stdout.encoding.lower() in ('utf-8', 'utf8'): return _Distribution.handle_display_options(self, option_order) -- cgit v1.2.1 From ceb03af882740ae02ef4be004a30b45c7b25b9ce Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 26 Nov 2012 09:20:11 -0500 Subject: Backed out changeset: 98a9f9dcce0e; Fixes #335. --HG-- branch : distribute extra : rebase_source : 3f4ff1c880688e6dd72d2fa8fab3c07e7f486a7e --- setuptools/dist.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 6236d5be..998a4dbe 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -242,10 +242,9 @@ class Distribution(_Distribution): """Resolve pre-setup requirements""" from pkg_resources import working_set, parse_requirements for dist in working_set.resolve( - parse_requirements(requires), installer=self.fetch_build_egg, - replace_conflicting=True + parse_requirements(requires), installer=self.fetch_build_egg ): - working_set.add(dist, replace=True) + working_set.add(dist) def finalize_options(self): _Distribution.finalize_options(self) -- cgit v1.2.1 From 737fe613840baf2e10246ffb83f86690a0b1561b Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 3 May 2013 07:43:28 -0400 Subject: Copy changes to dist.py from 1aae1efe5733 --HG-- branch : Setuptools-Distribute merge extra : source : a79dd619dc8637e5c9b7de32bd8c5389c995dcb9 --- setuptools/dist.py | 53 +++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 47 insertions(+), 6 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 30ff35e3..c1218ef2 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -8,7 +8,7 @@ from setuptools.command.install_lib import install_lib from distutils.errors import DistutilsOptionError, DistutilsPlatformError from distutils.errors import DistutilsSetupError import setuptools, pkg_resources, distutils.core, distutils.dist, distutils.cmd -import os, distutils.log +import os, distutils.log, re def _get_unpatched(cls): """Protect against re-patching the distutils if reloaded @@ -61,8 +61,8 @@ def check_nsp(dist, attr, value): parent = '.'.join(nsp.split('.')[:-1]) if parent not in value: distutils.log.warn( - "%r is declared as a package namespace, but %r is not:" - " please correct this in setup.py", nsp, parent + "WARNING: %r is declared as a package namespace, but %r" + " is not: please correct this in setup.py", nsp, parent ) def check_extras(dist, attr, value): @@ -120,6 +120,47 @@ def check_package_data(dist, attr, value): attr+" must be a dictionary mapping package names to lists of " "wildcard patterns" ) + +def check_packages(dist, attr, value): + for pkgname in value: + if not re.match(r'\w+(\.\w+)*', pkgname): + distutils.log.warn( + "WARNING: %r not a valid package name; please use only" + ".-separated package names in setup.py", pkgname + ) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + class Distribution(_Distribution): """Distribution with support for features, tests, and package data @@ -415,19 +456,19 @@ class Distribution(_Distribution): if self.packages: self.packages = [ p for p in self.packages - if p<>package and not p.startswith(pfx) + if p!=package and not p.startswith(pfx) ] if self.py_modules: self.py_modules = [ p for p in self.py_modules - if p<>package and not p.startswith(pfx) + if p!=package and not p.startswith(pfx) ] if self.ext_modules: self.ext_modules = [ p for p in self.ext_modules - if p.name<>package and not p.name.startswith(pfx) + if p.name!=package and not p.name.startswith(pfx) ] -- cgit v1.2.1 From e7d341af8987659ae1fa79701515c16238fe22f6 Mon Sep 17 00:00:00 2001 From: pje Date: Sat, 4 May 2013 16:21:50 -0400 Subject: Backport experimental environment marker support from the trunk --HG-- branch : setuptools-0.6 --- setuptools/dist.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index c1218ef2..582cc557 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -47,7 +47,6 @@ def assert_string_list(dist, attr, value): raise DistutilsSetupError( "%r must be a list of strings (got %r)" % (attr,value) ) - def check_nsp(dist, attr, value): """Verify that namespace packages are valid""" assert_string_list(dist,attr,value) @@ -69,6 +68,10 @@ def check_extras(dist, attr, value): """Verify that extras_require mapping is valid""" try: for k,v in value.items(): + if ':' in k: + k,m = k.split(':',1) + if pkg_resources.invalid_marker(m): + raise DistutilsSetupError("Invalid environment marker: "+m) list(pkg_resources.parse_requirements(v)) except (TypeError,ValueError,AttributeError): raise DistutilsSetupError( @@ -77,9 +80,6 @@ def check_extras(dist, attr, value): "requirement specifiers." ) - - - def assert_bool(dist, attr, value): """Verify that value is True, False, 0, or 1""" if bool(value) != value: -- cgit v1.2.1 From 8a33154bb30c9c0ca193d13d8b8ce3818e045d71 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 11 Aug 2013 08:24:29 -0400 Subject: Officially deprecated Features functionality (per #65). --- setuptools/dist.py | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 01889215..e9d0b5a2 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -2,6 +2,7 @@ __all__ = ['Distribution'] import re import sys +import warnings from distutils.core import Distribution as _Distribution from setuptools.depends import Require from setuptools.command.install import install @@ -132,7 +133,7 @@ def check_packages(dist, attr, value): "WARNING: %r not a valid package name; please use only" ".-separated package names in setup.py", pkgname ) - + @@ -194,7 +195,8 @@ class Distribution(_Distribution): EasyInstall and requests one of your extras, the corresponding additional requirements will be installed if needed. - 'features' -- a dictionary mapping option names to 'setuptools.Feature' + 'features' **deprecated** -- a dictionary mapping option names to + 'setuptools.Feature' objects. Features are a portion of the distribution that can be included or excluded based on user options, inter-feature dependencies, and availability on the current system. Excluded features are omitted @@ -252,6 +254,8 @@ class Distribution(_Distribution): have_package_data = hasattr(self, "package_data") if not have_package_data: self.package_data = {} + if 'features' in attrs or 'require_features' in attrs: + Feature.warn_deprecated() self.require_features = [] self.features = {} self.dist_files = [] @@ -745,7 +749,13 @@ for module in distutils.dist, distutils.core, distutils.cmd: class Feature: - """A subset of the distribution that can be excluded if unneeded/wanted + """ + **deprecated** -- The `Feature` facility was never completely implemented + or supported, `has reported issues + `_ and will be removed in + a future version. + + A subset of the distribution that can be excluded if unneeded/wanted Features are created using these keyword arguments: @@ -794,9 +804,17 @@ class Feature: Aside from the methods, the only feature attributes that distributions look at are 'description' and 'optional'. """ + + @staticmethod + def warn_deprecated(): + warnings.warn("Features are deprecated and will be removed in " + "a future version. See http://bitbucket.org/pypa/setuptools/65.", + DeprecationWarning) + def __init__(self, description, standard=False, available=True, optional=True, require_features=(), remove=(), **extras ): + self.warn_deprecated() self.description = description self.standard = standard -- cgit v1.2.1 From e621ce6f3099b83548cede7425a5af25b0f0ec8c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 11 Aug 2013 08:54:56 -0400 Subject: Fix TypeError when no attrs were passed --- setuptools/dist.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index e9d0b5a2..9dbf04ba 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -254,7 +254,8 @@ class Distribution(_Distribution): have_package_data = hasattr(self, "package_data") if not have_package_data: self.package_data = {} - if 'features' in attrs or 'require_features' in attrs: + _attrs_dict = attrs or {} + if 'features' in _attrs_dict or 'require_features' in _attrs_dict: Feature.warn_deprecated() self.require_features = [] self.features = {} -- cgit v1.2.1 From 25f13ea57a53b04866f1c3a0a961c951812bdb43 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 15 Aug 2013 19:58:54 -0400 Subject: Set stacklevel on DeprecationWarning for more relevant locality. --- setuptools/dist.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 9dbf04ba..5c84b8d4 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -808,9 +808,12 @@ class Feature: @staticmethod def warn_deprecated(): - warnings.warn("Features are deprecated and will be removed in " - "a future version. See http://bitbucket.org/pypa/setuptools/65.", - DeprecationWarning) + warnings.warn( + "Features are deprecated and will be removed in a future " + "version. See http://bitbucket.org/pypa/setuptools/65.", + DeprecationWarning, + stacklevel=3, + ) def __init__(self, description, standard=False, available=True, optional=True, require_features=(), remove=(), **extras -- cgit v1.2.1 From 0c3e350abfd455c6bf2678308cc26bef015b8d48 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 25 Aug 2013 14:39:24 -0400 Subject: Reorganize imports --- setuptools/dist.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 5c84b8d4..dfce015d 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -1,18 +1,23 @@ __all__ = ['Distribution'] import re +import os import sys import warnings +import distutils.log +import distutils.core +import distutils.cmd from distutils.core import Distribution as _Distribution +from distutils.errors import (DistutilsOptionError, DistutilsPlatformError, + DistutilsSetupError) + from setuptools.depends import Require from setuptools.command.install import install from setuptools.command.sdist import sdist from setuptools.command.install_lib import install_lib from setuptools.compat import numeric_types, basestring -from distutils.errors import DistutilsOptionError, DistutilsPlatformError -from distutils.errors import DistutilsSetupError -import setuptools, pkg_resources, distutils.core, distutils.dist, distutils.cmd -import os, distutils.log +import setuptools +import pkg_resources def _get_unpatched(cls): """Protect against re-patching the distutils if reloaded -- cgit v1.2.1 From aece98c7d09ab1107ad77d2d4bfbec8d91fca127 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 25 Aug 2013 14:40:08 -0400 Subject: Remove unused imports --- setuptools/dist.py | 4 ---- 1 file changed, 4 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index dfce015d..c90f73c2 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -12,11 +12,7 @@ from distutils.errors import (DistutilsOptionError, DistutilsPlatformError, DistutilsSetupError) from setuptools.depends import Require -from setuptools.command.install import install -from setuptools.command.sdist import sdist -from setuptools.command.install_lib import install_lib from setuptools.compat import numeric_types, basestring -import setuptools import pkg_resources def _get_unpatched(cls): -- cgit v1.2.1 From 2ec8add337856baa2fba48b161ef380d5d007a02 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 25 Aug 2013 14:42:33 -0400 Subject: Trim excess whitespace --- setuptools/dist.py | 127 ++--------------------------------------------------- 1 file changed, 4 insertions(+), 123 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index c90f73c2..c5b02f99 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -136,38 +136,6 @@ def check_packages(dist, attr, value): ) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - class Distribution(_Distribution): """Distribution with support for features, tests, and package data @@ -251,7 +219,7 @@ class Distribution(_Distribution): dist._version = pkg_resources.safe_version(str(attrs['version'])) self._patched_dist = dist - def __init__ (self, attrs=None): + def __init__(self, attrs=None): have_package_data = hasattr(self, "package_data") if not have_package_data: self.package_data = {} @@ -368,23 +336,6 @@ class Distribution(_Distribution): self.global_options = self.feature_options = go + self.global_options self.negative_opt = self.feature_negopt = no - - - - - - - - - - - - - - - - - def _finalize_features(self): """Add/remove features and resolve dependencies between them""" @@ -402,7 +353,6 @@ class Distribution(_Distribution): feature.exclude_from(self) self._set_feature(name,0) - def get_command_class(self, command): """Pluggable version of get_command_class()""" if command in self.cmdclass: @@ -422,10 +372,6 @@ class Distribution(_Distribution): self.cmdclass[ep.name] = cmdclass return _Distribution.print_commands(self) - - - - def _set_feature(self,name,status): """Set feature's inclusion status""" setattr(self,self._feature_attrname(name),status) @@ -440,8 +386,8 @@ class Distribution(_Distribution): if self.feature_is_included(name)==0: descr = self.features[name].description raise DistutilsOptionError( - descr + " is required, but was excluded or is not available" - ) + descr + " is required, but was excluded or is not available" + ) self.features[name].include_in(self) self._set_feature(name,1) @@ -489,7 +435,6 @@ class Distribution(_Distribution): if p.name != package and not p.name.startswith(pfx) ] - def has_contents_for(self,package): """Return true if 'exclude_package(package)' would do something""" @@ -499,15 +444,6 @@ class Distribution(_Distribution): if p==package or p.startswith(pfx): return True - - - - - - - - - def _exclude_misc(self,name,value): """Handle 'exclude()' for list/tuple attrs without a special handler""" if not isinstance(value,sequence): @@ -579,17 +515,6 @@ class Distribution(_Distribution): ) list(map(self.exclude_package, packages)) - - - - - - - - - - - def _parse_command_opts(self, parser, args): # Remove --with-X/--without-X options when processing command args self.global_options = self.__class__.global_options @@ -616,21 +541,6 @@ class Distribution(_Distribution): return nargs - - - - - - - - - - - - - - - def get_cmdline_options(self): """Return a '{cmd: {opt:val}}' map of all command-line options @@ -671,7 +581,6 @@ class Distribution(_Distribution): return d - def iter_distribution_names(self): """Yield all packages, modules, and extension names in distribution""" @@ -690,7 +599,6 @@ class Distribution(_Distribution): name = name[:-6] yield name - def handle_display_options(self, option_order): """If there were any non-global "display-only" options (--help-commands or the metadata display options) on the command @@ -732,24 +640,6 @@ for module in distutils.dist, distutils.core, distutils.cmd: module.Distribution = Distribution - - - - - - - - - - - - - - - - - - class Feature: """ **deprecated** -- The `Feature` facility was never completely implemented @@ -817,8 +707,7 @@ class Feature: ) def __init__(self, description, standard=False, available=True, - optional=True, require_features=(), remove=(), **extras - ): + optional=True, require_features=(), remove=(), **extras): self.warn_deprecated() self.description = description @@ -870,8 +759,6 @@ class Feature: for f in self.require_features: dist.include_feature(f) - - def exclude_from(self,dist): """Ensure feature is excluded from distribution @@ -888,8 +775,6 @@ class Feature: for item in self.remove: dist.exclude_package(item) - - def validate(self,dist): """Verify that feature makes sense in context of distribution @@ -909,7 +794,3 @@ class Feature: " doesn't contain any packages or modules under %s" % (self.description, item, item) ) - - - - -- cgit v1.2.1 From 818e80e00093764b261ac4c2595aedb9f194cf0d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 5 Jan 2014 12:31:44 -0500 Subject: Fix Python 3 failure when constructing an egg_fetcher. Fixes #131. --- setuptools/dist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index c5b02f99..3126cb96 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -295,7 +295,7 @@ class Distribution(_Distribution): 'find_links', 'site_dirs', 'index_url', 'optimize', 'site_dirs', 'allow_hosts' ) - for key in opts.keys(): + for key in list(opts): if key not in keep: del opts[key] # don't use any other settings if self.dependency_links: -- cgit v1.2.1 From 2c66805878d8f2b46241d8944e8b5af20eeeb9e1 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 27 Jan 2014 11:02:52 -0500 Subject: Backed out changeset: ef949e6e6de1, which was itself a backout of the fix for Distribute #323, so this backout restores that fix and also Fixes #141. --- setuptools/dist.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 3126cb96..0801ae74 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -260,9 +260,10 @@ class Distribution(_Distribution): """Resolve pre-setup requirements""" from pkg_resources import working_set, parse_requirements for dist in working_set.resolve( - parse_requirements(requires), installer=self.fetch_build_egg + parse_requirements(requires), installer=self.fetch_build_egg, + replace_conflicting=True ): - working_set.add(dist) + working_set.add(dist, replace=True) def finalize_options(self): _Distribution.finalize_options(self) -- cgit v1.2.1 From fbf26248da1664d103df596bcbf11acfd638516f Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 9 Feb 2014 17:17:32 -0500 Subject: Removed Features functionality. Fixes #65. --- setuptools/dist.py | 259 +---------------------------------------------------- 1 file changed, 3 insertions(+), 256 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 0801ae74..dcb9ba4e 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -3,15 +3,12 @@ __all__ = ['Distribution'] import re import os import sys -import warnings import distutils.log import distutils.core import distutils.cmd from distutils.core import Distribution as _Distribution -from distutils.errors import (DistutilsOptionError, DistutilsPlatformError, - DistutilsSetupError) +from distutils.errors import DistutilsSetupError -from setuptools.depends import Require from setuptools.compat import numeric_types, basestring import pkg_resources @@ -137,7 +134,7 @@ def check_packages(dist, attr, value): class Distribution(_Distribution): - """Distribution with support for features, tests, and package data + """Distribution with support for tests and package data This is an enhanced version of 'distutils.dist.Distribution' that effectively adds the following new optional keyword arguments to 'setup()': @@ -164,21 +161,6 @@ class Distribution(_Distribution): EasyInstall and requests one of your extras, the corresponding additional requirements will be installed if needed. - 'features' **deprecated** -- a dictionary mapping option names to - 'setuptools.Feature' - objects. Features are a portion of the distribution that can be - included or excluded based on user options, inter-feature dependencies, - and availability on the current system. Excluded features are omitted - from all setup commands, including source and binary distributions, so - you can create multiple distributions from the same source tree. - Feature names should be valid Python identifiers, except that they may - contain the '-' (minus) sign. Features can be included or excluded - via the command line options '--with-X' and '--without-X', where 'X' is - the name of the feature. Whether a feature is included by default, and - whether you are allowed to control this from the command line, is - determined by the Feature object. See the 'Feature' class for more - information. - 'test_suite' -- the name of a test suite to run for the 'test' command. If the user runs 'python setup.py test', the package will be installed, and the named test suite will be run. The format is the same as @@ -200,8 +182,7 @@ class Distribution(_Distribution): for manipulating the distribution's contents. For example, the 'include()' and 'exclude()' methods can be thought of as in-place add and subtract commands that add or remove packages, modules, extensions, and so on from - the distribution. They are used by the feature subsystem to configure the - distribution for the included and excluded features. + the distribution. """ _patched_dist = None @@ -223,11 +204,6 @@ class Distribution(_Distribution): have_package_data = hasattr(self, "package_data") if not have_package_data: self.package_data = {} - _attrs_dict = attrs or {} - if 'features' in _attrs_dict or 'require_features' in _attrs_dict: - Feature.warn_deprecated() - self.require_features = [] - self.features = {} self.dist_files = [] self.src_root = attrs and attrs.pop("src_root", None) self.patch_missing_pkg_info(attrs) @@ -245,17 +221,6 @@ class Distribution(_Distribution): # Some people apparently take "version number" too literally :) self.metadata.version = str(self.metadata.version) - def parse_command_line(self): - """Process features after parsing command line options""" - result = _Distribution.parse_command_line(self) - if self.features: - self._finalize_features() - return result - - def _feature_attrname(self,name): - """Convert feature name to corresponding option attribute name""" - return 'with_'+name.replace('-','_') - def fetch_build_eggs(self, requires): """Resolve pre-setup requirements""" from pkg_resources import working_set, parse_requirements @@ -267,8 +232,6 @@ class Distribution(_Distribution): def finalize_options(self): _Distribution.finalize_options(self) - if self.features: - self._set_global_opts_from_features() for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'): value = getattr(self,ep.name,None) @@ -313,47 +276,6 @@ class Distribution(_Distribution): self._egg_fetcher = cmd return cmd.easy_install(req) - def _set_global_opts_from_features(self): - """Add --with-X/--without-X options based on optional features""" - - go = [] - no = self.negative_opt.copy() - - for name,feature in self.features.items(): - self._set_feature(name,None) - feature.validate(self) - - if feature.optional: - descr = feature.description - incdef = ' (default)' - excdef='' - 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)) - no['without-'+name] = 'with-'+name - - self.global_options = self.feature_options = go + self.global_options - self.negative_opt = self.feature_negopt = no - - def _finalize_features(self): - """Add/remove features and resolve dependencies between them""" - - # First, flag all the enabled items (and thus their dependencies) - for name,feature in self.features.items(): - enabled = self.feature_is_included(name) - if enabled or (enabled is None and feature.include_by_default()): - feature.include_in(self) - self._set_feature(name,1) - - # Then disable the rest, so that off-by-default features don't - # get flagged as errors when they're required by an enabled feature - for name,feature in self.features.items(): - if not self.feature_is_included(name): - feature.exclude_from(self) - self._set_feature(name,0) - def get_command_class(self, command): """Pluggable version of get_command_class()""" if command in self.cmdclass: @@ -373,25 +295,6 @@ class Distribution(_Distribution): self.cmdclass[ep.name] = cmdclass return _Distribution.print_commands(self) - def _set_feature(self,name,status): - """Set feature's inclusion status""" - setattr(self,self._feature_attrname(name),status) - - def feature_is_included(self,name): - """Return 1 if feature is included, 0 if excluded, 'None' if unknown""" - return getattr(self,self._feature_attrname(name)) - - def include_feature(self,name): - """Request inclusion of feature named 'name'""" - - if self.feature_is_included(name)==0: - descr = self.features[name].description - raise DistutilsOptionError( - descr + " is required, but was excluded or is not available" - ) - self.features[name].include_in(self) - self._set_feature(name,1) - def include(self,**attrs): """Add items to distribution that are named in keyword arguments @@ -639,159 +542,3 @@ class Distribution(_Distribution): # Install it throughout the distutils for module in distutils.dist, distutils.core, distutils.cmd: module.Distribution = Distribution - - -class Feature: - """ - **deprecated** -- The `Feature` facility was never completely implemented - or supported, `has reported issues - `_ and will be removed in - a future version. - - A subset of the distribution that can be excluded if unneeded/wanted - - Features are created using these keyword arguments: - - 'description' -- a short, human readable description of the feature, to - be used in error messages, and option help messages. - - 'standard' -- if true, the feature is included by default if it is - available on the current system. Otherwise, the feature is only - included if requested via a command line '--with-X' option, or if - another included feature requires it. The default setting is 'False'. - - 'available' -- if true, the feature is available for installation on the - current system. The default setting is 'True'. - - 'optional' -- if true, the feature's inclusion can be controlled from the - command line, using the '--with-X' or '--without-X' options. If - false, the feature's inclusion status is determined automatically, - based on 'availabile', 'standard', and whether any other feature - requires it. The default setting is 'True'. - - 'require_features' -- a string or sequence of strings naming features - that should also be included if this feature is included. Defaults to - empty list. May also contain 'Require' objects that should be - added/removed from the distribution. - - 'remove' -- a string or list of strings naming packages to be removed - from the distribution if this feature is *not* included. If the - feature *is* included, this argument is ignored. This argument exists - to support removing features that "crosscut" a distribution, such as - defining a 'tests' feature that removes all the 'tests' subpackages - provided by other features. The default for this argument is an empty - list. (Note: the named package(s) or modules must exist in the base - distribution when the 'setup()' function is initially called.) - - other keywords -- any other keyword arguments are saved, and passed to - the distribution's 'include()' and 'exclude()' methods when the - feature is included or excluded, respectively. So, for example, you - could pass 'packages=["a","b"]' to cause packages 'a' and 'b' to be - added or removed from the distribution as appropriate. - - A feature must include at least one 'requires', 'remove', or other - keyword argument. Otherwise, it can't affect the distribution in any way. - Note also that you can subclass 'Feature' to create your own specialized - feature types that modify the distribution in other ways when included or - excluded. See the docstrings for the various methods here for more detail. - Aside from the methods, the only feature attributes that distributions look - at are 'description' and 'optional'. - """ - - @staticmethod - def warn_deprecated(): - warnings.warn( - "Features are deprecated and will be removed in a future " - "version. See http://bitbucket.org/pypa/setuptools/65.", - DeprecationWarning, - stacklevel=3, - ) - - def __init__(self, description, standard=False, available=True, - optional=True, require_features=(), remove=(), **extras): - self.warn_deprecated() - - self.description = description - self.standard = standard - self.available = available - self.optional = optional - if isinstance(require_features,(str,Require)): - require_features = require_features, - - self.require_features = [ - r for r in require_features if isinstance(r,str) - ] - er = [r for r in require_features if not isinstance(r,str)] - if er: extras['require_features'] = er - - if isinstance(remove,str): - remove = remove, - self.remove = remove - self.extras = extras - - 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." - ) - - def include_by_default(self): - """Should this feature be included by default?""" - return self.available and self.standard - - def include_in(self,dist): - - """Ensure feature and its requirements are included in distribution - - You may override this in a subclass to perform additional operations on - the distribution. Note that this method may be called more than once - per feature, and so should be idempotent. - - """ - - if not self.available: - raise DistutilsPlatformError( - self.description+" is required," - "but is not available on this platform" - ) - - dist.include(**self.extras) - - for f in self.require_features: - dist.include_feature(f) - - def exclude_from(self,dist): - - """Ensure feature is excluded from distribution - - You may override this in a subclass to perform additional operations on - the distribution. This method will be called at most once per - feature, and only after all included features have been asked to - include themselves. - """ - - dist.exclude(**self.extras) - - if self.remove: - for item in self.remove: - dist.exclude_package(item) - - def validate(self,dist): - - """Verify that feature makes sense in context of distribution - - This method is called by the distribution just before it parses its - command line. It checks to ensure that the 'remove' attribute, if any, - contains only valid package/module names that are present in the base - distribution when 'setup()' is called. You may override it in a - subclass to perform any other required validation of the feature - against a target distribution. - """ - - for item in self.remove: - if not dist.has_contents_for(item): - raise DistutilsSetupError( - "%s wants to be able to remove %s, but the distribution" - " doesn't contain any packages or modules under %s" - % (self.description, item, item) - ) -- cgit v1.2.1 From e4460fad043f4fa0edc7b7e1eef0b209f4588fe5 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 8 Mar 2014 08:24:33 -0500 Subject: Backout b17e9a0ea116 and 50725de303ef, restoring Feature model. Fixes #161 and re-opens #65. --HG-- extra : amend_source : f14bc0bf6c9f04e16d30ce0abf7bcb944f41ebea --- setuptools/dist.py | 259 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 256 insertions(+), 3 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index dcb9ba4e..0801ae74 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -3,12 +3,15 @@ __all__ = ['Distribution'] import re import os import sys +import warnings import distutils.log import distutils.core import distutils.cmd from distutils.core import Distribution as _Distribution -from distutils.errors import DistutilsSetupError +from distutils.errors import (DistutilsOptionError, DistutilsPlatformError, + DistutilsSetupError) +from setuptools.depends import Require from setuptools.compat import numeric_types, basestring import pkg_resources @@ -134,7 +137,7 @@ def check_packages(dist, attr, value): class Distribution(_Distribution): - """Distribution with support for tests and package data + """Distribution with support for features, tests, and package data This is an enhanced version of 'distutils.dist.Distribution' that effectively adds the following new optional keyword arguments to 'setup()': @@ -161,6 +164,21 @@ class Distribution(_Distribution): EasyInstall and requests one of your extras, the corresponding additional requirements will be installed if needed. + 'features' **deprecated** -- a dictionary mapping option names to + 'setuptools.Feature' + objects. Features are a portion of the distribution that can be + included or excluded based on user options, inter-feature dependencies, + and availability on the current system. Excluded features are omitted + from all setup commands, including source and binary distributions, so + you can create multiple distributions from the same source tree. + Feature names should be valid Python identifiers, except that they may + contain the '-' (minus) sign. Features can be included or excluded + via the command line options '--with-X' and '--without-X', where 'X' is + the name of the feature. Whether a feature is included by default, and + whether you are allowed to control this from the command line, is + determined by the Feature object. See the 'Feature' class for more + information. + 'test_suite' -- the name of a test suite to run for the 'test' command. If the user runs 'python setup.py test', the package will be installed, and the named test suite will be run. The format is the same as @@ -182,7 +200,8 @@ class Distribution(_Distribution): for manipulating the distribution's contents. For example, the 'include()' and 'exclude()' methods can be thought of as in-place add and subtract commands that add or remove packages, modules, extensions, and so on from - the distribution. + the distribution. They are used by the feature subsystem to configure the + distribution for the included and excluded features. """ _patched_dist = None @@ -204,6 +223,11 @@ class Distribution(_Distribution): have_package_data = hasattr(self, "package_data") if not have_package_data: self.package_data = {} + _attrs_dict = attrs or {} + if 'features' in _attrs_dict or 'require_features' in _attrs_dict: + Feature.warn_deprecated() + self.require_features = [] + self.features = {} self.dist_files = [] self.src_root = attrs and attrs.pop("src_root", None) self.patch_missing_pkg_info(attrs) @@ -221,6 +245,17 @@ class Distribution(_Distribution): # Some people apparently take "version number" too literally :) self.metadata.version = str(self.metadata.version) + def parse_command_line(self): + """Process features after parsing command line options""" + result = _Distribution.parse_command_line(self) + if self.features: + self._finalize_features() + return result + + def _feature_attrname(self,name): + """Convert feature name to corresponding option attribute name""" + return 'with_'+name.replace('-','_') + def fetch_build_eggs(self, requires): """Resolve pre-setup requirements""" from pkg_resources import working_set, parse_requirements @@ -232,6 +267,8 @@ class Distribution(_Distribution): def finalize_options(self): _Distribution.finalize_options(self) + if self.features: + self._set_global_opts_from_features() for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'): value = getattr(self,ep.name,None) @@ -276,6 +313,47 @@ class Distribution(_Distribution): self._egg_fetcher = cmd return cmd.easy_install(req) + def _set_global_opts_from_features(self): + """Add --with-X/--without-X options based on optional features""" + + go = [] + no = self.negative_opt.copy() + + for name,feature in self.features.items(): + self._set_feature(name,None) + feature.validate(self) + + if feature.optional: + descr = feature.description + incdef = ' (default)' + excdef='' + 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)) + no['without-'+name] = 'with-'+name + + self.global_options = self.feature_options = go + self.global_options + self.negative_opt = self.feature_negopt = no + + def _finalize_features(self): + """Add/remove features and resolve dependencies between them""" + + # First, flag all the enabled items (and thus their dependencies) + for name,feature in self.features.items(): + enabled = self.feature_is_included(name) + if enabled or (enabled is None and feature.include_by_default()): + feature.include_in(self) + self._set_feature(name,1) + + # Then disable the rest, so that off-by-default features don't + # get flagged as errors when they're required by an enabled feature + for name,feature in self.features.items(): + if not self.feature_is_included(name): + feature.exclude_from(self) + self._set_feature(name,0) + def get_command_class(self, command): """Pluggable version of get_command_class()""" if command in self.cmdclass: @@ -295,6 +373,25 @@ class Distribution(_Distribution): self.cmdclass[ep.name] = cmdclass return _Distribution.print_commands(self) + def _set_feature(self,name,status): + """Set feature's inclusion status""" + setattr(self,self._feature_attrname(name),status) + + def feature_is_included(self,name): + """Return 1 if feature is included, 0 if excluded, 'None' if unknown""" + return getattr(self,self._feature_attrname(name)) + + def include_feature(self,name): + """Request inclusion of feature named 'name'""" + + if self.feature_is_included(name)==0: + descr = self.features[name].description + raise DistutilsOptionError( + descr + " is required, but was excluded or is not available" + ) + self.features[name].include_in(self) + self._set_feature(name,1) + def include(self,**attrs): """Add items to distribution that are named in keyword arguments @@ -542,3 +639,159 @@ class Distribution(_Distribution): # Install it throughout the distutils for module in distutils.dist, distutils.core, distutils.cmd: module.Distribution = Distribution + + +class Feature: + """ + **deprecated** -- The `Feature` facility was never completely implemented + or supported, `has reported issues + `_ and will be removed in + a future version. + + A subset of the distribution that can be excluded if unneeded/wanted + + Features are created using these keyword arguments: + + 'description' -- a short, human readable description of the feature, to + be used in error messages, and option help messages. + + 'standard' -- if true, the feature is included by default if it is + available on the current system. Otherwise, the feature is only + included if requested via a command line '--with-X' option, or if + another included feature requires it. The default setting is 'False'. + + 'available' -- if true, the feature is available for installation on the + current system. The default setting is 'True'. + + 'optional' -- if true, the feature's inclusion can be controlled from the + command line, using the '--with-X' or '--without-X' options. If + false, the feature's inclusion status is determined automatically, + based on 'availabile', 'standard', and whether any other feature + requires it. The default setting is 'True'. + + 'require_features' -- a string or sequence of strings naming features + that should also be included if this feature is included. Defaults to + empty list. May also contain 'Require' objects that should be + added/removed from the distribution. + + 'remove' -- a string or list of strings naming packages to be removed + from the distribution if this feature is *not* included. If the + feature *is* included, this argument is ignored. This argument exists + to support removing features that "crosscut" a distribution, such as + defining a 'tests' feature that removes all the 'tests' subpackages + provided by other features. The default for this argument is an empty + list. (Note: the named package(s) or modules must exist in the base + distribution when the 'setup()' function is initially called.) + + other keywords -- any other keyword arguments are saved, and passed to + the distribution's 'include()' and 'exclude()' methods when the + feature is included or excluded, respectively. So, for example, you + could pass 'packages=["a","b"]' to cause packages 'a' and 'b' to be + added or removed from the distribution as appropriate. + + A feature must include at least one 'requires', 'remove', or other + keyword argument. Otherwise, it can't affect the distribution in any way. + Note also that you can subclass 'Feature' to create your own specialized + feature types that modify the distribution in other ways when included or + excluded. See the docstrings for the various methods here for more detail. + Aside from the methods, the only feature attributes that distributions look + at are 'description' and 'optional'. + """ + + @staticmethod + def warn_deprecated(): + warnings.warn( + "Features are deprecated and will be removed in a future " + "version. See http://bitbucket.org/pypa/setuptools/65.", + DeprecationWarning, + stacklevel=3, + ) + + def __init__(self, description, standard=False, available=True, + optional=True, require_features=(), remove=(), **extras): + self.warn_deprecated() + + self.description = description + self.standard = standard + self.available = available + self.optional = optional + if isinstance(require_features,(str,Require)): + require_features = require_features, + + self.require_features = [ + r for r in require_features if isinstance(r,str) + ] + er = [r for r in require_features if not isinstance(r,str)] + if er: extras['require_features'] = er + + if isinstance(remove,str): + remove = remove, + self.remove = remove + self.extras = extras + + 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." + ) + + def include_by_default(self): + """Should this feature be included by default?""" + return self.available and self.standard + + def include_in(self,dist): + + """Ensure feature and its requirements are included in distribution + + You may override this in a subclass to perform additional operations on + the distribution. Note that this method may be called more than once + per feature, and so should be idempotent. + + """ + + if not self.available: + raise DistutilsPlatformError( + self.description+" is required," + "but is not available on this platform" + ) + + dist.include(**self.extras) + + for f in self.require_features: + dist.include_feature(f) + + def exclude_from(self,dist): + + """Ensure feature is excluded from distribution + + You may override this in a subclass to perform additional operations on + the distribution. This method will be called at most once per + feature, and only after all included features have been asked to + include themselves. + """ + + dist.exclude(**self.extras) + + if self.remove: + for item in self.remove: + dist.exclude_package(item) + + def validate(self,dist): + + """Verify that feature makes sense in context of distribution + + This method is called by the distribution just before it parses its + command line. It checks to ensure that the 'remove' attribute, if any, + contains only valid package/module names that are present in the base + distribution when 'setup()' is called. You may override it in a + subclass to perform any other required validation of the feature + against a target distribution. + """ + + for item in self.remove: + if not dist.has_contents_for(item): + raise DistutilsSetupError( + "%s wants to be able to remove %s, but the distribution" + " doesn't contain any packages or modules under %s" + % (self.description, item, item) + ) -- cgit v1.2.1 From 36addf1ea62cde1ab5884f91e18ac4d1954529cc Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 3 May 2014 11:46:21 -0400 Subject: Monkey-patch the write_pkg_info method on Python 3.1 DistributionMetadata. Fixes #197 --- setuptools/dist.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 0801ae74..59a89236 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -7,6 +7,7 @@ import warnings import distutils.log import distutils.core import distutils.cmd +import distutils.dist from distutils.core import Distribution as _Distribution from distutils.errors import (DistutilsOptionError, DistutilsPlatformError, DistutilsSetupError) @@ -31,6 +32,26 @@ def _get_unpatched(cls): _Distribution = _get_unpatched(_Distribution) +def _patch_distribution_metadata_write_pkg_info(): + """ + Workaround issue #197 - Python 3.1 uses an environment-local encoding to + save the pkg_info. Monkey-patch its write_pkg_info method to correct + this undesirable behavior. + """ + if sys.version_info[:2] != (3,1): + return + + # from Python 3.4 + def write_pkg_info(self, base_dir): + """Write the PKG-INFO file into the release tree. + """ + with open(os.path.join(base_dir, 'PKG-INFO'), 'w', + encoding='UTF-8') as pkg_info: + self.write_pkg_file(pkg_info) + + distutils.dist.DistributionMetadata.write_pkg_info = write_pkg_info +_patch_distribution_metadata_write_pkg_info() + sequence = tuple, list def check_importable(dist, attr, value): -- cgit v1.2.1 From 8567ca65adbf927a0af5c9b7314688dfbc46ab66 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 17 May 2014 12:25:31 -0400 Subject: Use PY3 and PY2 throughout --- setuptools/dist.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 59a89236..b8bd7490 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -13,7 +13,7 @@ from distutils.errors import (DistutilsOptionError, DistutilsPlatformError, DistutilsSetupError) from setuptools.depends import Require -from setuptools.compat import numeric_types, basestring +from setuptools.compat import numeric_types, basestring, PY2 import pkg_resources def _get_unpatched(cls): @@ -629,7 +629,7 @@ class Distribution(_Distribution): """ import sys - if sys.version_info < (3,) or self.help_commands: + if PY2 or self.help_commands: return _Distribution.handle_display_options(self, option_order) # Stdout may be StringIO (e.g. in tests) -- cgit v1.2.1 From 40232ffa1983be8149a68cb969f28b92200204b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jurko=20Gospodneti=C4=87?= Date: Mon, 12 May 2014 18:57:50 +0200 Subject: generalize fix for issue #197 from 1cd816bb7c933eecd9d8464e054b21c7d5daf2df Now the same fix gets applied for Python versions [3.0 - 3.2.2> instead of just 3.1. The underlying distutils issue affected all those vesions and has been fixed in the Python 3.2.2 release. --HG-- extra : rebase_source : fe4dc458edbf83e13b275e02fb90939d9061e206 --- setuptools/dist.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index b8bd7490..69e04099 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -34,11 +34,11 @@ _Distribution = _get_unpatched(_Distribution) def _patch_distribution_metadata_write_pkg_info(): """ - Workaround issue #197 - Python 3.1 uses an environment-local encoding to - save the pkg_info. Monkey-patch its write_pkg_info method to correct - this undesirable behavior. + Workaround issue #197 - Python [3.0 - 3.2.2> uses an environment-local + encoding to save the pkg_info. Monkey-patch its write_pkg_info method to + correct this undesirable behavior. """ - if sys.version_info[:2] != (3,1): + if not ((3,) <= sys.version_info[:3] < (3, 2, 2)): return # from Python 3.4 -- cgit v1.2.1 From 11b6828342b5f34bc593d49b0b13dc23a2eb7a2d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 1 Jun 2014 21:14:20 +0100 Subject: Use a variable for less busy syntax --- setuptools/dist.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 69e04099..7713bf02 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -34,11 +34,12 @@ _Distribution = _get_unpatched(_Distribution) def _patch_distribution_metadata_write_pkg_info(): """ - Workaround issue #197 - Python [3.0 - 3.2.2> uses an environment-local + Workaround issue #197 - Python 3 prior to 3.2.2 uses an environment-local encoding to save the pkg_info. Monkey-patch its write_pkg_info method to correct this undesirable behavior. """ - if not ((3,) <= sys.version_info[:3] < (3, 2, 2)): + environment_local = (3,) <= sys.version_info[:3] < (3, 2, 2) + if not environment_local: return # from Python 3.4 -- cgit v1.2.1 From 96efb0e69b7caf488f73b1713b35fd442332ef00 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 18 Jun 2014 11:02:35 -0400 Subject: Remove superfluous import --- setuptools/dist.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 7713bf02..4fcae978 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -280,12 +280,11 @@ class Distribution(_Distribution): def fetch_build_eggs(self, requires): """Resolve pre-setup requirements""" - from pkg_resources import working_set, parse_requirements - for dist in working_set.resolve( - parse_requirements(requires), installer=self.fetch_build_egg, + for dist in pkg_resources.working_set.resolve( + pkg_resources.parse_requirements(requires), installer=self.fetch_build_egg, replace_conflicting=True ): - working_set.add(dist, replace=True) + pkg_resources.working_set.add(dist, replace=True) def finalize_options(self): _Distribution.finalize_options(self) -- cgit v1.2.1 From 25a0f6733a92425e55830f8169a38fc9b0d3350e Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 18 Jun 2014 11:03:30 -0400 Subject: Extract variable for clarity. --- setuptools/dist.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 4fcae978..8de95a38 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -280,10 +280,12 @@ class Distribution(_Distribution): def fetch_build_eggs(self, requires): """Resolve pre-setup requirements""" - for dist in pkg_resources.working_set.resolve( - pkg_resources.parse_requirements(requires), installer=self.fetch_build_egg, - replace_conflicting=True - ): + resolved_dists = pkg_resources.working_set.resolve( + pkg_resources.parse_requirements(requires), + installer=self.fetch_build_egg, + replace_conflicting=True, + ) + for dist in resolved_dists: pkg_resources.working_set.add(dist, replace=True) def finalize_options(self): -- cgit v1.2.1 From f3829d8bd0cd852be133f8822aa4493a7a12fd51 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 5 Jul 2014 13:34:34 -0400 Subject: Use numbers.Number to detect numeric type --- setuptools/dist.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 8de95a38..dac4dfa8 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -4,6 +4,7 @@ import re import os import sys import warnings +import numbers import distutils.log import distutils.core import distutils.cmd @@ -13,7 +14,7 @@ from distutils.errors import (DistutilsOptionError, DistutilsPlatformError, DistutilsSetupError) from setuptools.depends import Require -from setuptools.compat import numeric_types, basestring, PY2 +from setuptools.compat import basestring, PY2 import pkg_resources def _get_unpatched(cls): @@ -263,7 +264,7 @@ class Distribution(_Distribution): if not hasattr(self,ep.name): setattr(self,ep.name,None) _Distribution.__init__(self,attrs) - if isinstance(self.metadata.version, numeric_types): + if isinstance(self.metadata.version, numbers.Number): # Some people apparently take "version number" too literally :) self.metadata.version = str(self.metadata.version) -- cgit v1.2.1 From b49435397a5094f94678adf3549cc8941aa469b7 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 5 Jul 2014 15:06:51 -0400 Subject: Use six for Python 2 compatibility --HG-- branch : feature/issue-229 extra : source : 7b1997ececc5772798ce33a0f8e77387cb55a977 --- setuptools/dist.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index dac4dfa8..c6ac1946 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -13,8 +13,9 @@ from distutils.core import Distribution as _Distribution from distutils.errors import (DistutilsOptionError, DistutilsPlatformError, DistutilsSetupError) +import six + from setuptools.depends import Require -from setuptools.compat import basestring, PY2 import pkg_resources def _get_unpatched(cls): @@ -132,7 +133,7 @@ def check_entry_points(dist, attr, value): raise DistutilsSetupError(e) def check_test_suite(dist, attr, value): - if not isinstance(value,basestring): + if not isinstance(value, six.string_types): raise DistutilsSetupError("test_suite must be a string") def check_package_data(dist, attr, value): @@ -632,7 +633,7 @@ class Distribution(_Distribution): """ import sys - if PY2 or self.help_commands: + if six.PY2 or self.help_commands: return _Distribution.handle_display_options(self, option_order) # Stdout may be StringIO (e.g. in tests) -- cgit v1.2.1 From 50cd15aef3a657ee9cf86326e18e3bc2919b4cf6 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 10 Aug 2014 14:06:15 -0400 Subject: Include setup_requires directive in Distribution attributes and metadata. Fixes #239. --- setuptools/dist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index dac4dfa8..8b36f67c 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -259,7 +259,7 @@ class Distribution(_Distribution): self.dependency_links = attrs.pop('dependency_links', []) assert_string_list(self,'dependency_links',self.dependency_links) if attrs and 'setup_requires' in attrs: - self.fetch_build_eggs(attrs.pop('setup_requires')) + self.fetch_build_eggs(attrs['setup_requires']) for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'): if not hasattr(self,ep.name): setattr(self,ep.name,None) -- cgit v1.2.1 From 9382fa0c05e533400613e1c7c0a777cabb463390 Mon Sep 17 00:00:00 2001 From: Donald Stufft Date: Thu, 4 Sep 2014 21:04:06 -0400 Subject: Implement PEP 440 by using the packaging library --- setuptools/dist.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 8b36f67c..ae4ff554 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -15,6 +15,7 @@ from distutils.errors import (DistutilsOptionError, DistutilsPlatformError, from setuptools.depends import Require from setuptools.compat import basestring, PY2 +from setuptools._vendor.packaging.version import Version, InvalidVersion import pkg_resources def _get_unpatched(cls): @@ -268,6 +269,26 @@ class Distribution(_Distribution): # Some people apparently take "version number" too literally :) self.metadata.version = str(self.metadata.version) + if self.metadata.version is not None: + try: + normalized_version = str(Version(self.metadata.version)) + if self.metadata.version != normalized_version: + warnings.warn( + "The version specified requires normalization, " + "consider using '%s' instead of '%s'." % ( + normalized_version, + self.metadata.version, + ) + ) + self.metadata.version = normalized_version + except (InvalidVersion, TypeError): + warnings.warn( + "The version specified (%r) is an invalid version, this " + "may not work as expected with newer versions of " + "setuptools, pip, and PyPI. Please see PEP 440 for more " + "details." % self.metadata.version + ) + def parse_command_line(self): """Process features after parsing command line options""" result = _Distribution.parse_command_line(self) -- cgit v1.2.1 From 7d9c21a893431798ba77edd62b5490ff4ce47ecf Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 27 Sep 2014 16:13:48 -0400 Subject: Prefer packaging library if available. --- setuptools/dist.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index ae4ff554..a3a37ee4 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -13,11 +13,18 @@ from distutils.core import Distribution as _Distribution from distutils.errors import (DistutilsOptionError, DistutilsPlatformError, DistutilsSetupError) +try: + import packaging.version +except ImportError: + # fallback to vendored version + import setuptools._vendor.packaging.version + packaging = setuptools._vendor.packaging + from setuptools.depends import Require from setuptools.compat import basestring, PY2 -from setuptools._vendor.packaging.version import Version, InvalidVersion import pkg_resources + def _get_unpatched(cls): """Protect against re-patching the distutils if reloaded @@ -271,7 +278,8 @@ class Distribution(_Distribution): if self.metadata.version is not None: try: - normalized_version = str(Version(self.metadata.version)) + ver = packaging.version.Version(self.metadata.version) + normalized_version = str(ver) if self.metadata.version != normalized_version: warnings.warn( "The version specified requires normalization, " @@ -281,7 +289,7 @@ class Distribution(_Distribution): ) ) self.metadata.version = normalized_version - except (InvalidVersion, TypeError): + except (packaging.version.InvalidVersion, TypeError): warnings.warn( "The version specified (%r) is an invalid version, this " "may not work as expected with newer versions of " -- cgit v1.2.1 From c64524729fbd21cf5ad430acad82bf399b6f723d Mon Sep 17 00:00:00 2001 From: Marc Abramowitz Date: Mon, 13 Oct 2014 10:37:00 -0700 Subject: Cache eggs required for building in .eggs dir This makes it so that these eggs don't prevent `install_requires` from installing these packages. Fixes ticket #80; workaround for ticket #209 --HG-- branch : put_setup_requires_egg_in_cache_dir_4 --- setuptools/dist.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 8b36f67c..2d9da8c4 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -14,7 +14,8 @@ from distutils.errors import (DistutilsOptionError, DistutilsPlatformError, DistutilsSetupError) from setuptools.depends import Require -from setuptools.compat import basestring, PY2 +from setuptools.compat import basestring, PY2, unicode +from setuptools import win32 import pkg_resources def _get_unpatched(cls): @@ -305,6 +306,21 @@ class Distribution(_Distribution): else: self.convert_2to3_doctests = [] + def get_egg_cache_dir(self): + egg_cache_dir = os.path.join(os.curdir, '.eggs') + if not os.path.exists(egg_cache_dir): + os.mkdir(egg_cache_dir) + win32.hide_file(unicode(egg_cache_dir)) + readme_txt_filename = os.path.join(egg_cache_dir, 'README.txt') + with open(readme_txt_filename, 'w') as f: + f.write('This directory contains eggs that were downloaded ' + 'by setuptools to build, test, and run plug-ins.\n\n') + f.write('This directory caches those eggs to prevent ' + 'repeated downloads.\n\n') + f.write('However, it is safe to delete this directory.\n\n') + + return egg_cache_dir + def fetch_build_egg(self, req): """Fetch an egg needed for building""" @@ -328,8 +344,9 @@ class Distribution(_Distribution): 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=os.curdir, 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 ) -- cgit v1.2.1 From 5c68f3db41766e24d5fe6f7adbfd713bc83786a3 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 19 Oct 2014 12:52:08 +0100 Subject: The name win32 is a misnomer. Use 'windows_support' instead. --- setuptools/dist.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 2d9da8c4..4f302232 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -15,7 +15,7 @@ from distutils.errors import (DistutilsOptionError, DistutilsPlatformError, from setuptools.depends import Require from setuptools.compat import basestring, PY2, unicode -from setuptools import win32 +from setuptools import windows_support import pkg_resources def _get_unpatched(cls): @@ -310,7 +310,7 @@ class Distribution(_Distribution): egg_cache_dir = os.path.join(os.curdir, '.eggs') if not os.path.exists(egg_cache_dir): os.mkdir(egg_cache_dir) - win32.hide_file(unicode(egg_cache_dir)) + windows_support.hide_file(unicode(egg_cache_dir)) readme_txt_filename = os.path.join(egg_cache_dir, 'README.txt') with open(readme_txt_filename, 'w') as f: f.write('This directory contains eggs that were downloaded ' -- cgit v1.2.1 From ae6eb3131a935824d1aca43c6ac5ac6bb4907078 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 19 Oct 2014 13:02:37 +0100 Subject: Declare argtypes and restype on SetFileAttributesW so that it will cast Python 2 bytestrings to Unicode automatically. --- setuptools/dist.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 4f302232..6b9d350e 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -14,7 +14,7 @@ from distutils.errors import (DistutilsOptionError, DistutilsPlatformError, DistutilsSetupError) from setuptools.depends import Require -from setuptools.compat import basestring, PY2, unicode +from setuptools.compat import basestring, PY2 from setuptools import windows_support import pkg_resources @@ -310,7 +310,7 @@ class Distribution(_Distribution): egg_cache_dir = os.path.join(os.curdir, '.eggs') if not os.path.exists(egg_cache_dir): os.mkdir(egg_cache_dir) - windows_support.hide_file(unicode(egg_cache_dir)) + windows_support.hide_file(egg_cache_dir) readme_txt_filename = os.path.join(egg_cache_dir, 'README.txt') with open(readme_txt_filename, 'w') as f: f.write('This directory contains eggs that were downloaded ' -- cgit v1.2.1 From 9063c163e105545bacb67865f5d35056eb342a49 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 24 Dec 2014 17:02:04 -0500 Subject: Move vendored packaging module into pkg_resources._vendor, restoring independence of pkg_resources from setuptools. Fixes #311. --- setuptools/dist.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index e44796fd..2daa2835 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -17,8 +17,8 @@ try: import packaging.version except ImportError: # fallback to vendored version - import setuptools._vendor.packaging.version - packaging = setuptools._vendor.packaging + import pkg_resources._vendor.packaging.version + packaging = pkg_resources._vendor.packaging from setuptools.depends import Require from setuptools.compat import basestring, PY2 -- cgit v1.2.1 From 170657b68f4b92e7e1bf82f5e19a831f5744af67 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 24 Dec 2014 17:11:49 -0500 Subject: Setuptools now uses the 'packaging' package from pkg_resources, unifying the behavior around resolution of that package. --- setuptools/dist.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 2daa2835..7a94d4b3 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -13,18 +13,13 @@ from distutils.core import Distribution as _Distribution from distutils.errors import (DistutilsOptionError, DistutilsPlatformError, DistutilsSetupError) -try: - import packaging.version -except ImportError: - # fallback to vendored version - import pkg_resources._vendor.packaging.version - packaging = pkg_resources._vendor.packaging - from setuptools.depends import Require from setuptools.compat import basestring, PY2 from setuptools import windows_support import pkg_resources +packaging = pkg_resources.packaging + def _get_unpatched(cls): """Protect against re-patching the distutils if reloaded -- cgit v1.2.1 From 80a28fa8c044ccb74e4ae54544be8c449ebd03e8 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 31 Dec 2014 12:35:32 -0500 Subject: Use underlying invocation of ._load directly --- setuptools/dist.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 7a94d4b3..eb146444 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -434,7 +434,8 @@ class Distribution(_Distribution): def print_commands(self): for ep in pkg_resources.iter_entry_points('distutils.commands'): if ep.name not in self.cmdclass: - cmdclass = ep.load(False) # don't require extras, we're not running + # don't require extras as the commands won't be invoked + cmdclass = ep._load() self.cmdclass[ep.name] = cmdclass return _Distribution.print_commands(self) -- cgit v1.2.1 From 9d6a6e5927ae0e67164383e419f3145fc154467e Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 4 Jan 2015 11:35:16 -0500 Subject: Use except/as, now supported by Python 2.6 --- setuptools/dist.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index eb146444..1917a610 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -131,8 +131,7 @@ def check_entry_points(dist, attr, value): """Verify that entry_points map is parseable""" try: pkg_resources.EntryPoint.parse_map(value) - except ValueError: - e = sys.exc_info()[1] + except ValueError as e: raise DistutilsSetupError(e) def check_test_suite(dist, attr, value): -- cgit v1.2.1 From 92a553d3adeb431cdf92b136ac9ccc3f2ef98bf1 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 5 Jan 2015 14:21:41 -0500 Subject: Add EntryPoint.resolve and deprecate most usage of EntryPoint.load. Removed EntryPoint._load. --- setuptools/dist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 1917a610..bc29b131 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -434,7 +434,7 @@ class Distribution(_Distribution): for ep in pkg_resources.iter_entry_points('distutils.commands'): if ep.name not in self.cmdclass: # don't require extras as the commands won't be invoked - cmdclass = ep._load() + cmdclass = ep.resolve() self.cmdclass[ep.name] = cmdclass return _Distribution.print_commands(self) -- cgit v1.2.1 From 0478d8fa223ca94e5c9a3f42dd5f92ed8be1270f Mon Sep 17 00:00:00 2001 From: Min RK Date: Tue, 20 Jan 2015 11:24:18 -0800 Subject: remove warning on normalization It seems inappropriate to show a warning on schemes officially supported in PEP 440. --HG-- branch : no-normalize-warning --- setuptools/dist.py | 7 ------- 1 file changed, 7 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index bc29b131..03d78520 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -276,13 +276,6 @@ class Distribution(_Distribution): ver = packaging.version.Version(self.metadata.version) normalized_version = str(ver) if self.metadata.version != normalized_version: - warnings.warn( - "The version specified requires normalization, " - "consider using '%s' instead of '%s'." % ( - normalized_version, - self.metadata.version, - ) - ) self.metadata.version = normalized_version except (packaging.version.InvalidVersion, TypeError): warnings.warn( -- cgit v1.2.1 From 88f62bfe62531bed69f871425d21d8c4e3203ecf Mon Sep 17 00:00:00 2001 From: MinRK Date: Thu, 5 Feb 2015 12:11:23 -0800 Subject: soften normalized version warning indicate that normalization is happening, but don't be pushy about changing valid versions. --HG-- branch : no-normalize-warning --- setuptools/dist.py | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 03d78520..ffbc7c48 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -276,6 +276,12 @@ class Distribution(_Distribution): ver = packaging.version.Version(self.metadata.version) normalized_version = str(ver) if self.metadata.version != normalized_version: + warnings.warn( + "Normalizing '%s' to '%s'" % ( + self.metadata.version, + normalized_version, + ) + ) self.metadata.version = normalized_version except (packaging.version.InvalidVersion, TypeError): warnings.warn( -- cgit v1.2.1 From 33544f0bf806d73495b00b9f8a0e45c25742fbc1 Mon Sep 17 00:00:00 2001 From: Marc Abramowitz Date: Fri, 1 May 2015 08:23:57 -0700 Subject: Nicer error when problem in install_requires Instead of: $ python setup.py egg_info error in adminweb setup command: 'install_requires' must be a string or list of strings containing valid project/version requirement specifiers We now have the more helpful: $ python setup.py egg_info error in adminweb setup command: 'install_requires' must be a string or list of strings containing valid project/version requirement specifiers. Error: Expected version spec in smsdk.authsvc>=0.0.8,2.0 at 2.0 It took me longer than it should to find the problem with the old error message. The new error message would've helped greatly. --- setuptools/dist.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index ffbc7c48..220bcf8c 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -122,11 +122,15 @@ def check_requirements(dist, attr, value): """Verify that install_requires is a valid requirements list""" try: list(pkg_resources.parse_requirements(value)) - except (TypeError,ValueError): + except (TypeError, ValueError) as e: raise DistutilsSetupError( "%r must be a string or list of strings " - "containing valid project/version requirement specifiers" % (attr,) + "containing valid project/version requirement specifiers.\n" + "Error: %s" + % (attr, ' '.join(e.args)) ) + + def check_entry_points(dist, attr, value): """Verify that entry_points map is parseable""" try: -- cgit v1.2.1 From fb27525a41a3f941aac906564d144d7177d35a79 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 3 May 2015 11:59:38 -0400 Subject: Let the exception render itself. --- setuptools/dist.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 220bcf8c..892044dd 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -126,8 +126,8 @@ def check_requirements(dist, attr, value): raise DistutilsSetupError( "%r must be a string or list of strings " "containing valid project/version requirement specifiers.\n" - "Error: %s" - % (attr, ' '.join(e.args)) + "%s" + % (attr, e) ) -- cgit v1.2.1 From 95a68afdf62f63e044f600ab87a4410db6bdf128 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 3 May 2015 12:34:02 -0400 Subject: Render the error message as a single line without a period (for consistency with other usage). --- setuptools/dist.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 892044dd..05669366 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -123,13 +123,11 @@ def check_requirements(dist, attr, value): try: list(pkg_resources.parse_requirements(value)) except (TypeError, ValueError) as e: - raise DistutilsSetupError( + tmpl = ( "%r must be a string or list of strings " - "containing valid project/version requirement specifiers.\n" - "%s" - % (attr, e) + "containing valid project/version requirement specifiers; %s" ) - + raise DistutilsSetupError(tmpl % (attr, e)) def check_entry_points(dist, attr, value): """Verify that entry_points map is parseable""" -- cgit v1.2.1 From 0cc8362038f6feb3ede00ee9ce8ba8c7d04940d9 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 3 May 2015 12:39:16 -0400 Subject: Use new string formatting --- setuptools/dist.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 05669366..d7ad4655 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -115,19 +115,20 @@ def check_extras(dist, attr, value): def assert_bool(dist, attr, value): """Verify that value is True, False, 0, or 1""" if bool(value) != value: - raise DistutilsSetupError( - "%r must be a boolean value (got %r)" % (attr,value) - ) + tmpl = "{attr!r} must be a boolean value (got {value!r})" + raise DistutilsSetupError(tmpl.format(attr=attr, value=value)) + + def check_requirements(dist, attr, value): """Verify that install_requires is a valid requirements list""" try: list(pkg_resources.parse_requirements(value)) - except (TypeError, ValueError) as e: + except (TypeError, ValueError) as error: tmpl = ( - "%r must be a string or list of strings " - "containing valid project/version requirement specifiers; %s" + "{attr!r} must be a string or list of strings " + "containing valid project/version requirement specifiers; {error}" ) - raise DistutilsSetupError(tmpl % (attr, e)) + raise DistutilsSetupError(tmpl.format(attr=attr, error=error)) def check_entry_points(dist, attr, value): """Verify that entry_points map is parseable""" -- cgit v1.2.1 From 9a86e5c638a16afc092c9895ae24c4669248448e Mon Sep 17 00:00:00 2001 From: Stanislaw Pitucha Date: Mon, 7 Dec 2015 16:13:26 +1100 Subject: Fix multiline strings with missing spaces --- setuptools/dist.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index d7ad4655..03369da8 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -160,7 +160,7 @@ def check_packages(dist, attr, value): for pkgname in value: if not re.match(r'\w+(\.\w+)*', pkgname): distutils.log.warn( - "WARNING: %r not a valid package name; please use only" + "WARNING: %r not a valid package name; please use only " ".-separated package names in setup.py", pkgname ) @@ -818,7 +818,7 @@ class Feature: if not self.available: raise DistutilsPlatformError( - self.description+" is required," + self.description+" is required, " "but is not available on this platform" ) -- cgit v1.2.1 From fc916c8ea1f7ac0ee5cc56dfaa0ab4ef6aee1cfb Mon Sep 17 00:00:00 2001 From: Sachi King Date: Mon, 7 Dec 2015 19:52:21 +1300 Subject: Add get_command_list to dist and process distutils.commands entry points --HG-- branch : get_command_list --- setuptools/dist.py | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index d7ad4655..c5f04b33 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -440,6 +440,14 @@ class Distribution(_Distribution): self.cmdclass[ep.name] = cmdclass return _Distribution.print_commands(self) + def get_command_list(self): + for ep in pkg_resources.iter_entry_points('distutils.commands'): + if ep.name not in self.cmdclass: + # don't require extras as the commands won't be invoked + cmdclass = ep.resolve() + self.cmdclass[ep.name] = cmdclass + return _Distribution.get_command_list(self) + def _set_feature(self,name,status): """Set feature's inclusion status""" setattr(self,self._feature_attrname(name),status) -- cgit v1.2.1 From 5eafe7644d18eddd77a5573ecc30b4c98efbb2c6 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 14 Dec 2015 04:12:53 -0500 Subject: Prefer setdefault to hasattr/setattr --- setuptools/dist.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index d7ad4655..b0c52838 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -267,8 +267,7 @@ class Distribution(_Distribution): if attrs and 'setup_requires' in attrs: self.fetch_build_eggs(attrs['setup_requires']) for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'): - if not hasattr(self,ep.name): - setattr(self,ep.name,None) + vars(self).setdefault(ep.name, None) _Distribution.__init__(self,attrs) if isinstance(self.metadata.version, numbers.Number): # Some people apparently take "version number" too literally :) -- cgit v1.2.1 From 06872bb0bbbeb953e90bd0941444b0d499056557 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 31 Dec 2015 11:51:01 -0500 Subject: Update vendoring technique to match that used for packaging. Ref #229. --HG-- branch : feature/issue-229 --- setuptools/dist.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 7335c967..11b42d19 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -13,7 +13,12 @@ from distutils.core import Distribution as _Distribution from distutils.errors import (DistutilsOptionError, DistutilsPlatformError, DistutilsSetupError) -import six +try: + from setuptools._vendor import six +except ImportError: + # fallback to naturally-installed version; allows system packagers to + # omit vendored packages. + import six from setuptools.depends import Require from setuptools import windows_support -- cgit v1.2.1 From 952c1bafda1929c74c737646aa025e6ffad6632e Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 31 Dec 2015 16:30:47 -0500 Subject: Modeling after Astropy's technique for bundling libraries, the imports are now much cleaner. Thanks @embray. Ref #229. --HG-- branch : feature/issue-229 --- setuptools/dist.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 11b42d19..70731225 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -13,12 +13,7 @@ from distutils.core import Distribution as _Distribution from distutils.errors import (DistutilsOptionError, DistutilsPlatformError, DistutilsSetupError) -try: - from setuptools._vendor import six -except ImportError: - # fallback to naturally-installed version; allows system packagers to - # omit vendored packages. - import six +from setuptools.extern import six from setuptools.depends import Require from setuptools import windows_support -- cgit v1.2.1 From 9b985a9112d9be396adca6a1948076378c70cc34 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 31 Dec 2015 16:47:55 -0500 Subject: Use the same technique in pkg_resources, relying on an 'extern' module to resolve the conditional import. --HG-- branch : feature/issue-229 --- setuptools/dist.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 70731225..4964a9e8 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -14,13 +14,12 @@ from distutils.errors import (DistutilsOptionError, DistutilsPlatformError, DistutilsSetupError) from setuptools.extern import six +from pkg_resources.extern import packaging from setuptools.depends import Require from setuptools import windows_support import pkg_resources -packaging = pkg_resources.packaging - def _get_unpatched(cls): """Protect against re-patching the distutils if reloaded -- cgit v1.2.1 From 8af3b6ef5b4173a0d0d6735147c98c882ae98344 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 16 Jan 2016 06:54:00 -0500 Subject: Always use Python 3 version of map --- setuptools/dist.py | 1 + 1 file changed, 1 insertion(+) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 4964a9e8..77855415 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -14,6 +14,7 @@ from distutils.errors import (DistutilsOptionError, DistutilsPlatformError, DistutilsSetupError) from setuptools.extern import six +from setuptools.extern.six.moves import map from pkg_resources.extern import packaging from setuptools.depends import Require -- cgit v1.2.1 From 3b90be7bb6323eb44d0f28864509c1d47aa098de Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 29 Mar 2016 12:17:06 -0400 Subject: Update most bitbucket references to point to Github now. Fixes #422. --- setuptools/dist.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 77855415..086e0a58 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -720,7 +720,7 @@ class Feature: """ **deprecated** -- The `Feature` facility was never completely implemented or supported, `has reported issues - `_ and will be removed in + `_ and will be removed in a future version. A subset of the distribution that can be excluded if unneeded/wanted @@ -777,7 +777,7 @@ class Feature: def warn_deprecated(): warnings.warn( "Features are deprecated and will be removed in a future " - "version. See http://bitbucket.org/pypa/setuptools/65.", + "version. See https://github.com/pypa/setuptools/issues/65.", DeprecationWarning, stacklevel=3, ) -- cgit v1.2.1 From 6d11e88f938f09ef16db4c6064b6e74acba4db1d Mon Sep 17 00:00:00 2001 From: stepshal Date: Tue, 12 Jul 2016 22:00:43 +0700 Subject: Fix quantity of blank lines after code object. --- setuptools/dist.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 086e0a58..ee85cf52 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -38,6 +38,7 @@ def _get_unpatched(cls): _Distribution = _get_unpatched(_Distribution) + def _patch_distribution_metadata_write_pkg_info(): """ Workaround issue #197 - Python 3 prior to 3.2.2 uses an environment-local @@ -61,6 +62,7 @@ _patch_distribution_metadata_write_pkg_info() sequence = tuple, list + def check_importable(dist, attr, value): try: ep = pkg_resources.EntryPoint.parse('x='+value) @@ -80,6 +82,8 @@ def assert_string_list(dist, attr, value): raise DistutilsSetupError( "%r must be a list of strings (got %r)" % (attr,value) ) + + def check_nsp(dist, attr, value): """Verify that namespace packages are valid""" assert_string_list(dist,attr,value) @@ -97,6 +101,7 @@ def check_nsp(dist, attr, value): " is not: please correct this in setup.py", nsp, parent ) + def check_extras(dist, attr, value): """Verify that extras_require mapping is valid""" try: @@ -113,6 +118,7 @@ def check_extras(dist, attr, value): "requirement specifiers." ) + def assert_bool(dist, attr, value): """Verify that value is True, False, 0, or 1""" if bool(value) != value: @@ -131,6 +137,7 @@ def check_requirements(dist, attr, value): ) raise DistutilsSetupError(tmpl.format(attr=attr, error=error)) + def check_entry_points(dist, attr, value): """Verify that entry_points map is parseable""" try: @@ -138,10 +145,12 @@ def check_entry_points(dist, attr, value): except ValueError as e: raise DistutilsSetupError(e) + def check_test_suite(dist, attr, value): if not isinstance(value, six.string_types): raise DistutilsSetupError("test_suite must be a string") + def check_package_data(dist, attr, value): """Verify that value is a dictionary of package names to glob lists""" if isinstance(value,dict): @@ -157,6 +166,7 @@ def check_package_data(dist, attr, value): "wildcard patterns" ) + def check_packages(dist, attr, value): for pkgname in value: if not re.match(r'\w+(\.\w+)*', pkgname): @@ -815,7 +825,6 @@ class Feature: return self.available and self.standard def include_in(self,dist): - """Ensure feature and its requirements are included in distribution You may override this in a subclass to perform additional operations on @@ -836,7 +845,6 @@ class Feature: dist.include_feature(f) def exclude_from(self,dist): - """Ensure feature is excluded from distribution You may override this in a subclass to perform additional operations on @@ -852,7 +860,6 @@ class Feature: dist.exclude_package(item) def validate(self,dist): - """Verify that feature makes sense in context of distribution This method is called by the distribution just before it parses its -- cgit v1.2.1 From f749ccab1e55723848946c9aba5c3eddebe0b24e Mon Sep 17 00:00:00 2001 From: stepshal Date: Thu, 14 Jul 2016 09:26:06 +0700 Subject: Add missing whitespace. --- setuptools/dist.py | 136 ++++++++++++++++++++++++++--------------------------- 1 file changed, 68 insertions(+), 68 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index ee85cf52..f229d726 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -67,10 +67,10 @@ def check_importable(dist, attr, value): try: ep = pkg_resources.EntryPoint.parse('x='+value) assert not ep.extras - except (TypeError,ValueError,AttributeError,AssertionError): + except (TypeError, ValueError, AttributeError, AssertionError): raise DistutilsSetupError( "%r must be importable 'module:attrs' string (got %r)" - % (attr,value) + % (attr, value) ) @@ -78,15 +78,15 @@ def assert_string_list(dist, attr, value): """Verify that value is a string list or None""" try: assert ''.join(value)!=value - except (TypeError,ValueError,AttributeError,AssertionError): + except (TypeError, ValueError, AttributeError, AssertionError): raise DistutilsSetupError( - "%r must be a list of strings (got %r)" % (attr,value) + "%r must be a list of strings (got %r)" % (attr, value) ) def check_nsp(dist, attr, value): """Verify that namespace packages are valid""" - assert_string_list(dist,attr,value) + assert_string_list(dist, attr, value) for nsp in value: if not dist.has_contents_for(nsp): raise DistutilsSetupError( @@ -105,13 +105,13 @@ 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(): + for k, v in value.items(): if ':' in k: - k,m = k.split(':',1) + k, m = k.split(':', 1) if pkg_resources.invalid_marker(m): raise DistutilsSetupError("Invalid environment marker: "+m) list(pkg_resources.parse_requirements(v)) - except (TypeError,ValueError,AttributeError): + except (TypeError, ValueError, AttributeError): raise DistutilsSetupError( "'extras_require' must be a dictionary whose values are " "strings or lists of strings containing valid project/version " @@ -153,9 +153,9 @@ def check_test_suite(dist, attr, value): def check_package_data(dist, attr, value): """Verify that value is a dictionary of package names to glob lists""" - if isinstance(value,dict): - for k,v in value.items(): - if not isinstance(k,str): break + if isinstance(value, dict): + for k, v in value.items(): + if not isinstance(k, str): break try: iter(v) except TypeError: break @@ -274,12 +274,12 @@ class Distribution(_Distribution): # Make sure we have any eggs needed to interpret 'attrs' if attrs is not None: self.dependency_links = attrs.pop('dependency_links', []) - assert_string_list(self,'dependency_links',self.dependency_links) + assert_string_list(self, 'dependency_links', self.dependency_links) if attrs and 'setup_requires' in attrs: self.fetch_build_eggs(attrs['setup_requires']) for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'): vars(self).setdefault(ep.name, None) - _Distribution.__init__(self,attrs) + _Distribution.__init__(self, attrs) if isinstance(self.metadata.version, numbers.Number): # Some people apparently take "version number" too literally :) self.metadata.version = str(self.metadata.version) @@ -311,9 +311,9 @@ class Distribution(_Distribution): self._finalize_features() return result - def _feature_attrname(self,name): + def _feature_attrname(self, name): """Convert feature name to corresponding option attribute name""" - return 'with_'+name.replace('-','_') + return 'with_'+name.replace('-', '_') def fetch_build_eggs(self, requires): """Resolve pre-setup requirements""" @@ -331,7 +331,7 @@ class Distribution(_Distribution): self._set_global_opts_from_features() for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'): - value = getattr(self,ep.name,None) + value = getattr(self, ep.name, None) if value is not None: ep.require(installer=self.fetch_build_egg) ep.load()(self, ep.name, value) @@ -364,7 +364,7 @@ class Distribution(_Distribution): cmd.package_index.to_scan = [] except AttributeError: from setuptools.command.easy_install import easy_install - dist = self.__class__({'script_args':['easy_install']}) + dist = self.__class__({'script_args': ['easy_install']}) dist.parse_config_files() opts = dist.get_option_dict('easy_install') keep = ( @@ -395,8 +395,8 @@ class Distribution(_Distribution): go = [] no = self.negative_opt.copy() - for name,feature in self.features.items(): - self._set_feature(name,None) + for name, feature in self.features.items(): + self._set_feature(name, None) feature.validate(self) if feature.optional: @@ -417,25 +417,25 @@ class Distribution(_Distribution): """Add/remove features and resolve dependencies between them""" # First, flag all the enabled items (and thus their dependencies) - for name,feature in self.features.items(): + for name, feature in self.features.items(): enabled = self.feature_is_included(name) if enabled or (enabled is None and feature.include_by_default()): feature.include_in(self) - self._set_feature(name,1) + self._set_feature(name, 1) # Then disable the rest, so that off-by-default features don't # get flagged as errors when they're required by an enabled feature - for name,feature in self.features.items(): + for name, feature in self.features.items(): if not self.feature_is_included(name): feature.exclude_from(self) - self._set_feature(name,0) + self._set_feature(name, 0) def get_command_class(self, command): """Pluggable version of get_command_class()""" if command in self.cmdclass: return self.cmdclass[command] - for ep in pkg_resources.iter_entry_points('distutils.commands',command): + for ep in pkg_resources.iter_entry_points('distutils.commands', command): ep.require(installer=self.fetch_build_egg) self.cmdclass[command] = cmdclass = ep.load() return cmdclass @@ -458,15 +458,15 @@ class Distribution(_Distribution): self.cmdclass[ep.name] = cmdclass return _Distribution.get_command_list(self) - def _set_feature(self,name,status): + def _set_feature(self, name, status): """Set feature's inclusion status""" - setattr(self,self._feature_attrname(name),status) + setattr(self, self._feature_attrname(name), status) - def feature_is_included(self,name): + def feature_is_included(self, name): """Return 1 if feature is included, 0 if excluded, 'None' if unknown""" - return getattr(self,self._feature_attrname(name)) + return getattr(self, self._feature_attrname(name)) - def include_feature(self,name): + def include_feature(self, name): """Request inclusion of feature named 'name'""" if self.feature_is_included(name)==0: @@ -475,9 +475,9 @@ class Distribution(_Distribution): descr + " is required, but was excluded or is not available" ) self.features[name].include_in(self) - self._set_feature(name,1) + self._set_feature(name, 1) - def include(self,**attrs): + def include(self, **attrs): """Add items to distribution that are named in keyword arguments For example, 'dist.exclude(py_modules=["x"])' would add 'x' to @@ -492,14 +492,14 @@ class Distribution(_Distribution): will try to call 'dist._include_foo({"bar":"baz"})', which can then handle whatever special inclusion logic is needed. """ - for k,v in attrs.items(): + for k, v in attrs.items(): include = getattr(self, '_include_'+k, None) if include: include(v) else: - self._include_misc(k,v) + self._include_misc(k, v) - def exclude_package(self,package): + def exclude_package(self, package): """Remove packages, modules, and extensions in named package""" pfx = package+'.' @@ -521,7 +521,7 @@ class Distribution(_Distribution): if p.name != package and not p.name.startswith(pfx) ] - def has_contents_for(self,package): + def has_contents_for(self, package): """Return true if 'exclude_package(package)' would do something""" pfx = package+'.' @@ -530,48 +530,48 @@ class Distribution(_Distribution): if p==package or p.startswith(pfx): return True - def _exclude_misc(self,name,value): + def _exclude_misc(self, name, value): """Handle 'exclude()' for list/tuple attrs without a special handler""" - if not isinstance(value,sequence): + if not isinstance(value, sequence): raise DistutilsSetupError( "%s: setting must be a list or tuple (%r)" % (name, value) ) try: - old = getattr(self,name) + old = getattr(self, name) except AttributeError: raise DistutilsSetupError( "%s: No such distribution setting" % name ) - if old is not None and not isinstance(old,sequence): + if old is not None and not isinstance(old, sequence): raise DistutilsSetupError( name+": this setting cannot be changed via include/exclude" ) elif old: - setattr(self,name,[item for item in old if item not in value]) + setattr(self, name, [item for item in old if item not in value]) - def _include_misc(self,name,value): + def _include_misc(self, name, value): """Handle 'include()' for list/tuple attrs without a special handler""" - if not isinstance(value,sequence): + if not isinstance(value, sequence): raise DistutilsSetupError( "%s: setting must be a list (%r)" % (name, value) ) try: - old = getattr(self,name) + old = getattr(self, name) except AttributeError: raise DistutilsSetupError( "%s: No such distribution setting" % name ) if old is None: - setattr(self,name,value) - elif not isinstance(old,sequence): + setattr(self, name, value) + elif not isinstance(old, sequence): raise DistutilsSetupError( name+": this setting cannot be changed via include/exclude" ) else: - setattr(self,name,old+[item for item in value if item not in old]) + setattr(self, name, old+[item for item in value if item not in old]) - def exclude(self,**attrs): + def exclude(self, **attrs): """Remove items from distribution that are named in keyword arguments For example, 'dist.exclude(py_modules=["x"])' would remove 'x' from @@ -587,15 +587,15 @@ class Distribution(_Distribution): will try to call 'dist._exclude_foo({"bar":"baz"})', which can then handle whatever special exclusion logic is needed. """ - for k,v in attrs.items(): + for k, v in attrs.items(): exclude = getattr(self, '_exclude_'+k, None) if exclude: exclude(v) else: - self._exclude_misc(k,v) + self._exclude_misc(k, v) - def _exclude_packages(self,packages): - if not isinstance(packages,sequence): + def _exclude_packages(self, packages): + if not isinstance(packages, sequence): raise DistutilsSetupError( "packages: setting must be a list or tuple (%r)" % (packages,) ) @@ -610,17 +610,17 @@ class Distribution(_Distribution): command = args[0] aliases = self.get_option_dict('aliases') while command in aliases: - src,alias = aliases[command] + src, alias = aliases[command] del aliases[command] # ensure each alias can expand only once! import shlex - args[:1] = shlex.split(alias,True) + args[:1] = shlex.split(alias, True) command = args[0] nargs = _Distribution._parse_command_opts(self, parser, args) # Handle commands that want to consume all remaining arguments cmd_class = self.get_command_class(command) - if getattr(cmd_class,'command_consumes_arguments',None): + if getattr(cmd_class, 'command_consumes_arguments', None): self.get_option_dict(command)['args'] = ("command line", nargs) if nargs is not None: return [] @@ -639,20 +639,20 @@ class Distribution(_Distribution): d = {} - for cmd,opts in self.command_options.items(): + for cmd, opts in self.command_options.items(): - for opt,(src,val) in opts.items(): + for opt, (src, val) in opts.items(): if src != "command line": continue - opt = opt.replace('_','-') + opt = opt.replace('_', '-') if val==0: cmdobj = self.get_command_obj(cmd) neg_opt = self.negative_opt.copy() - neg_opt.update(getattr(cmdobj,'negative_opt',{})) - for neg,pos in neg_opt.items(): + neg_opt.update(getattr(cmdobj, 'negative_opt', {})) + for neg, pos in neg_opt.items(): if pos==opt: opt=neg val=None @@ -663,7 +663,7 @@ class Distribution(_Distribution): elif val==1: val = None - d.setdefault(cmd,{})[opt] = val + d.setdefault(cmd, {})[opt] = val return d @@ -677,7 +677,7 @@ class Distribution(_Distribution): yield module for ext in self.ext_modules or (): - if isinstance(ext,tuple): + if isinstance(ext, tuple): name, buildinfo = ext else: name = ext.name @@ -800,16 +800,16 @@ class Feature: self.standard = standard self.available = available self.optional = optional - if isinstance(require_features,(str,Require)): + if isinstance(require_features, (str, Require)): require_features = require_features, self.require_features = [ - r for r in require_features if isinstance(r,str) + r for r in require_features if isinstance(r, str) ] - er = [r for r in require_features if not isinstance(r,str)] + er = [r for r in require_features if not isinstance(r, str)] if er: extras['require_features'] = er - if isinstance(remove,str): + if isinstance(remove, str): remove = remove, self.remove = remove self.extras = extras @@ -824,7 +824,7 @@ class Feature: """Should this feature be included by default?""" return self.available and self.standard - def include_in(self,dist): + def include_in(self, dist): """Ensure feature and its requirements are included in distribution You may override this in a subclass to perform additional operations on @@ -844,7 +844,7 @@ class Feature: for f in self.require_features: dist.include_feature(f) - def exclude_from(self,dist): + def exclude_from(self, dist): """Ensure feature is excluded from distribution You may override this in a subclass to perform additional operations on @@ -859,7 +859,7 @@ class Feature: for item in self.remove: dist.exclude_package(item) - def validate(self,dist): + def validate(self, dist): """Verify that feature makes sense in context of distribution This method is called by the distribution just before it parses its -- cgit v1.2.1 From dc2d1dc249bec8e3a864e2aa6002a8e27adc4b7c Mon Sep 17 00:00:00 2001 From: stepshal Date: Thu, 14 Jul 2016 12:11:49 +0700 Subject: Fix missing whitespace around operator. --- setuptools/dist.py | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index f229d726..b4ff3861 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -65,7 +65,7 @@ sequence = tuple, list def check_importable(dist, attr, value): try: - ep = pkg_resources.EntryPoint.parse('x='+value) + ep = pkg_resources.EntryPoint.parse('x=' + value) assert not ep.extras except (TypeError, ValueError, AttributeError, AssertionError): raise DistutilsSetupError( @@ -77,7 +77,7 @@ def check_importable(dist, attr, value): def assert_string_list(dist, attr, value): """Verify that value is a string list or None""" try: - assert ''.join(value)!=value + assert ''.join(value) != value except (TypeError, ValueError, AttributeError, AssertionError): raise DistutilsSetupError( "%r must be a list of strings (got %r)" % (attr, value) @@ -109,7 +109,7 @@ def check_extras(dist, attr, value): if ':' in k: k, m = k.split(':', 1) if pkg_resources.invalid_marker(m): - raise DistutilsSetupError("Invalid environment marker: "+m) + raise DistutilsSetupError("Invalid environment marker: " + m) list(pkg_resources.parse_requirements(v)) except (TypeError, ValueError, AttributeError): raise DistutilsSetupError( @@ -162,7 +162,7 @@ def check_package_data(dist, attr, value): else: return raise DistutilsSetupError( - attr+" must be a dictionary mapping package names to lists of " + attr + " must be a dictionary mapping package names to lists of " "wildcard patterns" ) @@ -313,7 +313,7 @@ class Distribution(_Distribution): def _feature_attrname(self, name): """Convert feature name to corresponding option attribute name""" - return 'with_'+name.replace('-', '_') + return 'with_' + name.replace('-', '_') def fetch_build_eggs(self, requires): """Resolve pre-setup requirements""" @@ -402,13 +402,13 @@ class Distribution(_Distribution): if feature.optional: descr = feature.description incdef = ' (default)' - excdef='' + excdef = '' 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)) - no['without-'+name] = 'with-'+name + go.append(('with-' + name, None, 'include ' + descr + incdef)) + go.append(('without-' + name, None, 'exclude ' + descr + excdef)) + no['without-' + name] = 'with-' + name self.global_options = self.feature_options = go + self.global_options self.negative_opt = self.feature_negopt = no @@ -469,7 +469,7 @@ class Distribution(_Distribution): def include_feature(self, name): """Request inclusion of feature named 'name'""" - if self.feature_is_included(name)==0: + if self.feature_is_included(name) == 0: descr = self.features[name].description raise DistutilsOptionError( descr + " is required, but was excluded or is not available" @@ -493,7 +493,7 @@ class Distribution(_Distribution): handle whatever special inclusion logic is needed. """ for k, v in attrs.items(): - include = getattr(self, '_include_'+k, None) + include = getattr(self, '_include_' + k, None) if include: include(v) else: @@ -502,7 +502,7 @@ class Distribution(_Distribution): def exclude_package(self, package): """Remove packages, modules, and extensions in named package""" - pfx = package+'.' + pfx = package + '.' if self.packages: self.packages = [ p for p in self.packages @@ -524,10 +524,10 @@ class Distribution(_Distribution): def has_contents_for(self, package): """Return true if 'exclude_package(package)' would do something""" - pfx = package+'.' + pfx = package + '.' for p in self.iter_distribution_names(): - if p==package or p.startswith(pfx): + if p == package or p.startswith(pfx): return True def _exclude_misc(self, name, value): @@ -544,7 +544,7 @@ class Distribution(_Distribution): ) if old is not None and not isinstance(old, sequence): raise DistutilsSetupError( - name+": this setting cannot be changed via include/exclude" + name + ": this setting cannot be changed via include/exclude" ) elif old: setattr(self, name, [item for item in old if item not in value]) @@ -566,10 +566,10 @@ class Distribution(_Distribution): setattr(self, name, value) elif not isinstance(old, sequence): raise DistutilsSetupError( - name+": this setting cannot be changed via include/exclude" + name + ": this setting cannot be changed via include/exclude" ) else: - setattr(self, name, old+[item for item in value if item not in old]) + setattr(self, name, old + [item for item in value if item not in old]) def exclude(self, **attrs): """Remove items from distribution that are named in keyword arguments @@ -588,7 +588,7 @@ class Distribution(_Distribution): handle whatever special exclusion logic is needed. """ for k, v in attrs.items(): - exclude = getattr(self, '_exclude_'+k, None) + exclude = getattr(self, '_exclude_' + k, None) if exclude: exclude(v) else: @@ -648,19 +648,19 @@ class Distribution(_Distribution): opt = opt.replace('_', '-') - if val==0: + if val == 0: cmdobj = self.get_command_obj(cmd) neg_opt = self.negative_opt.copy() neg_opt.update(getattr(cmdobj, 'negative_opt', {})) for neg, pos in neg_opt.items(): - if pos==opt: - opt=neg - val=None + if pos == opt: + opt = neg + val = None break else: raise AssertionError("Shouldn't be able to get here") - elif val==1: + elif val == 1: val = None d.setdefault(cmd, {})[opt] = val @@ -835,7 +835,7 @@ class Feature: if not self.available: raise DistutilsPlatformError( - self.description+" is required, " + self.description + " is required, " "but is not available on this platform" ) -- cgit v1.2.1 From 98477464ed0d9ffd0b39e8baac15dfcd568dcb22 Mon Sep 17 00:00:00 2001 From: Xavier Fernandez Date: Fri, 8 Jul 2016 20:39:10 +0200 Subject: Add python/external_requires keywords to setup This should allow setuptools to write the metadata Requires-Python and Requires-External from PEP345 to the PKGINFO file --- setuptools/dist.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index f229d726..29b0f266 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -39,6 +39,22 @@ def _get_unpatched(cls): _Distribution = _get_unpatched(_Distribution) +def _patch_distribution_metadata_write_pkg_file(): + """Patch write_pkg_file to also write Requires-Python/Requires-External""" + original_write = distutils.dist.DistributionMetadata.write_pkg_file + def write_pkg_file(self, file): + """Write the PKG-INFO format data to a file object. + """ + original_write(self, file) + if hasattr(self, 'python_requires'): + file.write('Requires-Python: %s\n' % self.python_requires) + if getattr(self, 'external_requires', []): + self._write_list(file, 'Requires-External', self.external_requires) + + distutils.dist.DistributionMetadata.write_pkg_file = write_pkg_file +_patch_distribution_metadata_write_pkg_file() + + def _patch_distribution_metadata_write_pkg_info(): """ Workaround issue #197 - Python 3 prior to 3.2.2 uses an environment-local @@ -138,6 +154,18 @@ def check_requirements(dist, attr, value): raise DistutilsSetupError(tmpl.format(attr=attr, error=error)) +def check_specifier(dist, attr, value): + """Verify that value is a valid version specifier""" + try: + packaging.specifiers.SpecifierSet(value) + except packaging.specifiers.InvalidSpecifier as error: + tmpl = ( + "{attr!r} must be a string or list of strings " + "containing valid version specifiers; {error}" + ) + raise DistutilsSetupError(tmpl.format(attr=attr, error=error)) + + def check_entry_points(dist, attr, value): """Verify that entry_points map is parseable""" try: @@ -303,6 +331,10 @@ class Distribution(_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 + if getattr(self, 'external_requires', None): + self.metadata.external_requires = self.external_requires def parse_command_line(self): """Process features after parsing command line options""" -- cgit v1.2.1 From d9a251b704b87e9a0e5090213e7799c468c4bbf5 Mon Sep 17 00:00:00 2001 From: Xavier Fernandez Date: Sun, 10 Jul 2016 20:51:37 +0200 Subject: Drop external_requires keyword --- setuptools/dist.py | 4 ---- 1 file changed, 4 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 29b0f266..27cf8501 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -48,8 +48,6 @@ def _patch_distribution_metadata_write_pkg_file(): original_write(self, file) if hasattr(self, 'python_requires'): file.write('Requires-Python: %s\n' % self.python_requires) - if getattr(self, 'external_requires', []): - self._write_list(file, 'Requires-External', self.external_requires) distutils.dist.DistributionMetadata.write_pkg_file = write_pkg_file _patch_distribution_metadata_write_pkg_file() @@ -333,8 +331,6 @@ class Distribution(_Distribution): ) if getattr(self, 'python_requires', None): self.metadata.python_requires = self.python_requires - if getattr(self, 'external_requires', None): - self.metadata.external_requires = self.external_requires def parse_command_line(self): """Process features after parsing command line options""" -- cgit v1.2.1 From 64335b63f9e03e71d0acd885b8bfd0b4b7a60aa8 Mon Sep 17 00:00:00 2001 From: stepshal Date: Thu, 21 Jul 2016 04:13:28 +0700 Subject: Put colon-separated compound statement on separate lines. --- setuptools/dist.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index b4ff3861..b8ada9b9 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -155,8 +155,10 @@ def check_package_data(dist, attr, value): """Verify that value is a dictionary of package names to glob lists""" if isinstance(value, dict): for k, v in value.items(): - if not isinstance(k, str): break - try: iter(v) + if not isinstance(k, str): + break + try: + iter(v) except TypeError: break else: @@ -807,7 +809,8 @@ class Feature: r for r in require_features if isinstance(r, str) ] er = [r for r in require_features if not isinstance(r, str)] - if er: extras['require_features'] = er + if er: + extras['require_features'] = er if isinstance(remove, str): remove = remove, -- cgit v1.2.1 From 39bf3155d47c0024240be414a611dcb6d549f53c Mon Sep 17 00:00:00 2001 From: stepshal Date: Thu, 21 Jul 2016 09:37:34 +0700 Subject: Add missing blank lines after class or function definition. --- setuptools/dist.py | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index bfdbb3b5..0a192553 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -36,12 +36,14 @@ def _get_unpatched(cls): ) return cls + _Distribution = _get_unpatched(_Distribution) def _patch_distribution_metadata_write_pkg_file(): """Patch write_pkg_file to also write Requires-Python/Requires-External""" original_write = distutils.dist.DistributionMetadata.write_pkg_file + def write_pkg_file(self, file): """Write the PKG-INFO format data to a file object. """ @@ -50,6 +52,8 @@ def _patch_distribution_metadata_write_pkg_file(): file.write('Requires-Python: %s\n' % self.python_requires) distutils.dist.DistributionMetadata.write_pkg_file = write_pkg_file + + _patch_distribution_metadata_write_pkg_file() @@ -72,6 +76,8 @@ def _patch_distribution_metadata_write_pkg_info(): self.write_pkg_file(pkg_info) distutils.dist.DistributionMetadata.write_pkg_info = write_pkg_info + + _patch_distribution_metadata_write_pkg_info() sequence = tuple, list -- cgit v1.2.1 From d079163e74166860fedbfe0abcfcb820507368fd Mon Sep 17 00:00:00 2001 From: Xavier Fernandez Date: Sun, 17 Jul 2016 14:52:16 +0200 Subject: Also update the Metadata-Version when adding Requires-Python --- setuptools/dist.py | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 0a192553..705ce9a3 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -12,6 +12,7 @@ import distutils.dist from distutils.core import Distribution as _Distribution 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 @@ -44,10 +45,45 @@ def _patch_distribution_metadata_write_pkg_file(): """Patch write_pkg_file to also write Requires-Python/Requires-External""" original_write = distutils.dist.DistributionMetadata.write_pkg_file + # Based on Python 3.5 version def write_pkg_file(self, file): """Write the PKG-INFO format data to a file object. """ - original_write(self, file) + version = '1.0' + if (self.provides or self.requires or self.obsoletes or + self.classifiers or self.download_url): + version = '1.1' + # Setuptools specific for PEP 345 + if hasattr(self, 'python_requires'): + version = '1.2' + + file.write('Metadata-Version: %s\n' % version) + file.write('Name: %s\n' % self.get_name()) + file.write('Version: %s\n' % self.get_version()) + file.write('Summary: %s\n' % self.get_description()) + file.write('Home-page: %s\n' % self.get_url()) + file.write('Author: %s\n' % self.get_contact()) + file.write('Author-email: %s\n' % self.get_contact_email()) + file.write('License: %s\n' % self.get_license()) + if self.download_url: + file.write('Download-URL: %s\n' % self.download_url) + + long_desc = rfc822_escape(self.get_long_description()) + file.write('Description: %s\n' % long_desc) + + keywords = ','.join(self.get_keywords()) + if keywords: + file.write('Keywords: %s\n' % keywords) + + self._write_list(file, 'Platform', self.get_platforms()) + self._write_list(file, 'Classifier', self.get_classifiers()) + + # PEP 314 + self._write_list(file, 'Requires', self.get_requires()) + self._write_list(file, 'Provides', self.get_provides()) + self._write_list(file, 'Obsoletes', self.get_obsoletes()) + + # Setuptools specific for PEP 345 if hasattr(self, 'python_requires'): file.write('Requires-Python: %s\n' % self.python_requires) -- cgit v1.2.1 From 6a628a665c968ea0b5d08cd4f769436e0aba15f2 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 21 Jul 2016 07:26:52 -0400 Subject: Remove unused variable --- setuptools/dist.py | 1 - 1 file changed, 1 deletion(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 705ce9a3..59a5ecf2 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -43,7 +43,6 @@ _Distribution = _get_unpatched(_Distribution) def _patch_distribution_metadata_write_pkg_file(): """Patch write_pkg_file to also write Requires-Python/Requires-External""" - original_write = distutils.dist.DistributionMetadata.write_pkg_file # Based on Python 3.5 version def write_pkg_file(self, file): -- cgit v1.2.1 From 7f118aaae6a49171a91248cc413dc78eb497a054 Mon Sep 17 00:00:00 2001 From: stepshal Date: Thu, 21 Jul 2016 10:17:11 +0700 Subject: Fix continuation line unaligned for hanging indent. --- setuptools/dist.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 0a192553..c539f6ef 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -542,19 +542,19 @@ class Distribution(_Distribution): if self.packages: self.packages = [ p for p in self.packages - if p != package and not p.startswith(pfx) + if p != package and not p.startswith(pfx) ] if self.py_modules: self.py_modules = [ p for p in self.py_modules - if p != package and not p.startswith(pfx) + if p != package and not p.startswith(pfx) ] if self.ext_modules: self.ext_modules = [ p for p in self.ext_modules - if p.name != package and not p.name.startswith(pfx) + if p.name != package and not p.name.startswith(pfx) ] def has_contents_for(self, package): -- cgit v1.2.1 From d6efc9424328b42a3c7aeae758bab35bc7df5014 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 4 Sep 2016 19:09:42 -0400 Subject: Introduce a new monkey module to encapsulate the monkeypatching. --- setuptools/dist.py | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 820df6d5..380b9436 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -9,7 +9,6 @@ import distutils.log import distutils.core import distutils.cmd import distutils.dist -from distutils.core import Distribution as _Distribution from distutils.errors import (DistutilsOptionError, DistutilsPlatformError, DistutilsSetupError) from distutils.util import rfc822_escape @@ -20,25 +19,11 @@ from pkg_resources.extern import packaging from setuptools.depends import Require from setuptools import windows_support +from setuptools.monkey import _get_unpatched import pkg_resources -def _get_unpatched(cls): - """Protect against re-patching the distutils if reloaded - - Also ensures that no other distutils extension monkeypatched the distutils - first. - """ - while cls.__module__.startswith('setuptools'): - cls, = cls.__bases__ - if not cls.__module__.startswith('distutils'): - raise AssertionError( - "distutils has already been patched by %r" % cls - ) - return cls - - -_Distribution = _get_unpatched(_Distribution) +_Distribution = _get_unpatched(distutils.core.Distribution) def _patch_distribution_metadata_write_pkg_file(): -- cgit v1.2.1 From 443cabec148460b3a688923df1a63f689d1164c7 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 4 Sep 2016 19:30:10 -0400 Subject: Remove private prefix from monkey as monkey module explicitly declares that all functions are private. --- setuptools/dist.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 380b9436..7a4249f1 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -19,11 +19,11 @@ from pkg_resources.extern import packaging from setuptools.depends import Require from setuptools import windows_support -from setuptools.monkey import _get_unpatched +from setuptools.monkey import get_unpatched import pkg_resources -_Distribution = _get_unpatched(distutils.core.Distribution) +_Distribution = get_unpatched(distutils.core.Distribution) def _patch_distribution_metadata_write_pkg_file(): -- cgit v1.2.1 From cd22ba427f9b201d6bc48586ddf4595312b9e19e Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 4 Sep 2016 19:50:27 -0400 Subject: Move (much of?) the rest of the monkey patching into the monkey module --- setuptools/dist.py | 129 +++++++++++++++++++++-------------------------------- 1 file changed, 51 insertions(+), 78 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 7a4249f1..d321e6c5 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -23,83 +23,58 @@ from setuptools.monkey import get_unpatched import pkg_resources -_Distribution = get_unpatched(distutils.core.Distribution) - - -def _patch_distribution_metadata_write_pkg_file(): - """Patch write_pkg_file to also write Requires-Python/Requires-External""" - - # Based on Python 3.5 version - def write_pkg_file(self, file): - """Write the PKG-INFO format data to a file object. - """ - version = '1.0' - if (self.provides or self.requires or self.obsoletes or - self.classifiers or self.download_url): - version = '1.1' - # Setuptools specific for PEP 345 - if hasattr(self, 'python_requires'): - version = '1.2' - - file.write('Metadata-Version: %s\n' % version) - file.write('Name: %s\n' % self.get_name()) - file.write('Version: %s\n' % self.get_version()) - file.write('Summary: %s\n' % self.get_description()) - file.write('Home-page: %s\n' % self.get_url()) - file.write('Author: %s\n' % self.get_contact()) - file.write('Author-email: %s\n' % self.get_contact_email()) - file.write('License: %s\n' % self.get_license()) - if self.download_url: - file.write('Download-URL: %s\n' % self.download_url) - - long_desc = rfc822_escape(self.get_long_description()) - file.write('Description: %s\n' % long_desc) - - keywords = ','.join(self.get_keywords()) - if keywords: - file.write('Keywords: %s\n' % keywords) - - self._write_list(file, 'Platform', self.get_platforms()) - self._write_list(file, 'Classifier', self.get_classifiers()) - - # PEP 314 - self._write_list(file, 'Requires', self.get_requires()) - self._write_list(file, 'Provides', self.get_provides()) - self._write_list(file, 'Obsoletes', self.get_obsoletes()) - - # Setuptools specific for PEP 345 - if hasattr(self, 'python_requires'): - file.write('Requires-Python: %s\n' % self.python_requires) - - distutils.dist.DistributionMetadata.write_pkg_file = write_pkg_file - - -_patch_distribution_metadata_write_pkg_file() - - -def _patch_distribution_metadata_write_pkg_info(): +# Based on Python 3.5 version +def write_pkg_file(self, file): + """Write the PKG-INFO format data to a file object. """ - Workaround issue #197 - Python 3 prior to 3.2.2 uses an environment-local - encoding to save the pkg_info. Monkey-patch its write_pkg_info method to - correct this undesirable behavior. + version = '1.0' + if (self.provides or self.requires or self.obsoletes or + self.classifiers or self.download_url): + version = '1.1' + # Setuptools specific for PEP 345 + if hasattr(self, 'python_requires'): + version = '1.2' + + file.write('Metadata-Version: %s\n' % version) + file.write('Name: %s\n' % self.get_name()) + file.write('Version: %s\n' % self.get_version()) + file.write('Summary: %s\n' % self.get_description()) + file.write('Home-page: %s\n' % self.get_url()) + file.write('Author: %s\n' % self.get_contact()) + file.write('Author-email: %s\n' % self.get_contact_email()) + file.write('License: %s\n' % self.get_license()) + if self.download_url: + file.write('Download-URL: %s\n' % self.download_url) + + long_desc = rfc822_escape(self.get_long_description()) + file.write('Description: %s\n' % long_desc) + + keywords = ','.join(self.get_keywords()) + if keywords: + file.write('Keywords: %s\n' % keywords) + + self._write_list(file, 'Platform', self.get_platforms()) + self._write_list(file, 'Classifier', self.get_classifiers()) + + # PEP 314 + self._write_list(file, 'Requires', self.get_requires()) + self._write_list(file, 'Provides', self.get_provides()) + self._write_list(file, 'Obsoletes', self.get_obsoletes()) + + # Setuptools specific for PEP 345 + if hasattr(self, 'python_requires'): + file.write('Requires-Python: %s\n' % self.python_requires) + + +# from Python 3.4 +def write_pkg_info(self, base_dir): + """Write the PKG-INFO file into the release tree. """ - environment_local = (3,) <= sys.version_info[:3] < (3, 2, 2) - if not environment_local: - return - - # from Python 3.4 - def write_pkg_info(self, base_dir): - """Write the PKG-INFO file into the release tree. - """ - with open(os.path.join(base_dir, 'PKG-INFO'), 'w', - encoding='UTF-8') as pkg_info: - self.write_pkg_file(pkg_info) - - distutils.dist.DistributionMetadata.write_pkg_info = write_pkg_info + with open(os.path.join(base_dir, 'PKG-INFO'), 'w', + encoding='UTF-8') as pkg_info: + self.write_pkg_file(pkg_info) -_patch_distribution_metadata_write_pkg_info() - sequence = tuple, list @@ -230,6 +205,9 @@ def check_packages(dist, attr, value): ) +_Distribution = get_unpatched(distutils.core.Distribution) + + class Distribution(_Distribution): """Distribution with support for features, tests, and package data @@ -777,11 +755,6 @@ class Distribution(_Distribution): sys.stdout.detach(), encoding, errors, newline, line_buffering) -# Install it throughout the distutils -for module in distutils.dist, distutils.core, distutils.cmd: - module.Distribution = Distribution - - class Feature: """ **deprecated** -- The `Feature` facility was never completely implemented -- cgit v1.2.1 From c5645385252c45575569a01622405115d49cd1b9 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 9 Sep 2016 10:13:36 -0400 Subject: Remove unused import --- setuptools/dist.py | 1 - 1 file changed, 1 deletion(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index d321e6c5..2f7bb59a 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -2,7 +2,6 @@ __all__ = ['Distribution'] import re import os -import sys import warnings import numbers import distutils.log -- cgit v1.2.1 From 20a2f628283a0af476020cc394e4dca1dcdeaadd Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 9 Sep 2016 10:27:32 -0400 Subject: Add Deprecation warning for _get_unpatched. --- setuptools/dist.py | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 2f7bb59a..b004f928 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -22,6 +22,11 @@ from setuptools.monkey import get_unpatched import pkg_resources +def _get_unpatched(cls): + warnings.warn("Do not call this function", DeprecationWarning) + return get_unpatched(cls) + + # Based on Python 3.5 version def write_pkg_file(self, file): """Write the PKG-INFO format data to a file object. -- cgit v1.2.1 From 5e4eea7d600f44321e76689890f9f885669f34c9 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 17 Sep 2016 17:05:07 -0400 Subject: In test command, add installed eggs to PYTHONPATH when invoking tests so that subprocesses will also have the dependencies available. Fixes #794. --- setuptools/dist.py | 1 + 1 file changed, 1 insertion(+) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index b004f928..364f2b4d 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -362,6 +362,7 @@ class Distribution(_Distribution): ) for dist in resolved_dists: pkg_resources.working_set.add(dist, replace=True) + return resolved_dists def finalize_options(self): _Distribution.finalize_options(self) -- cgit v1.2.1 From df1bd4e17a082b9b634f62d799807a18e526a7c0 Mon Sep 17 00:00:00 2001 From: stepshal Date: Wed, 19 Oct 2016 00:10:40 +0700 Subject: Fix spacing after comment hash. --- setuptools/dist.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 364f2b4d..367c26ea 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -412,7 +412,7 @@ class Distribution(_Distribution): ) for key in list(opts): if key not in keep: - del opts[key] # don't use any other settings + del opts[key] # don't use any other settings if self.dependency_links: links = self.dependency_links[:] if 'find_links' in opts: @@ -650,7 +650,7 @@ class Distribution(_Distribution): aliases = self.get_option_dict('aliases') while command in aliases: src, alias = aliases[command] - del aliases[command] # ensure each alias can expand only once! + del aliases[command] # ensure each alias can expand only once! import shlex args[:1] = shlex.split(alias, True) command = args[0] -- cgit v1.2.1 From f19e5732da07d22ea0d7c7a64ba7e736ba2d1a52 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 19 Oct 2016 14:02:08 -0400 Subject: Use rpartition for simplicity --- setuptools/dist.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 367c26ea..a3099fcd 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -112,9 +112,8 @@ def check_nsp(dist, attr, value): "Distribution contains no modules or packages for " + "namespace package %r" % nsp ) - if '.' in nsp: - parent = '.'.join(nsp.split('.')[:-1]) - if parent not in value: + parent, sep, child = nsp.rpartition('.') + if parent and parent not in value: distutils.log.warn( "WARNING: %r is declared as a package namespace, but %r" " is not: please correct this in setup.py", nsp, parent -- cgit v1.2.1 From b5299c316942b61cbc98750c0ed93f3a4c69b16a Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 19 Oct 2016 14:02:16 -0400 Subject: Reindent --- setuptools/dist.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index a3099fcd..dbb083bb 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -114,10 +114,10 @@ def check_nsp(dist, attr, value): ) parent, sep, child = nsp.rpartition('.') if parent and parent not in value: - distutils.log.warn( - "WARNING: %r is declared as a package namespace, but %r" - " is not: please correct this in setup.py", nsp, parent - ) + distutils.log.warn( + "WARNING: %r is declared as a package namespace, but %r" + " is not: please correct this in setup.py", nsp, parent + ) def check_extras(dist, attr, value): -- cgit v1.2.1 From d382def1367e9d9b288e9b04b6f2da5987052b5b Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 19 Oct 2016 14:03:56 -0400 Subject: Use a meaningful variable name --- setuptools/dist.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index dbb083bb..612040c8 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -105,15 +105,16 @@ def assert_string_list(dist, attr, value): def check_nsp(dist, attr, value): """Verify that namespace packages are valid""" - assert_string_list(dist, attr, value) - for nsp in value: + ns_packages = value + assert_string_list(dist, attr, ns_packages) + for nsp in ns_packages: if not dist.has_contents_for(nsp): raise DistutilsSetupError( "Distribution contains no modules or packages for " + "namespace package %r" % nsp ) parent, sep, child = nsp.rpartition('.') - if parent and parent not in value: + if parent and parent not in ns_packages: distutils.log.warn( "WARNING: %r is declared as a package namespace, but %r" " is not: please correct this in setup.py", nsp, parent -- cgit v1.2.1 From 69130241500d78735375e36eca1b3dc6a7048dd6 Mon Sep 17 00:00:00 2001 From: idle sign Date: Sat, 26 Nov 2016 21:42:08 +0700 Subject: Metadata and options are now could be set in setup.cfg (see #394). --- setuptools/dist.py | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 612040c8..c975abe0 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -19,6 +19,7 @@ from pkg_resources.extern import packaging from setuptools.depends import Require from setuptools import windows_support from setuptools.monkey import get_unpatched +from setuptools.config import ConfigMetadataHandler, ConfigOptionsHandler import pkg_resources @@ -342,6 +343,16 @@ class Distribution(_Distribution): if getattr(self, 'python_requires', None): self.metadata.python_requires = self.python_requires + def parse_config_files(self, filenames=None): + """Parses configuration files from various levels + and loads configuration. + + """ + _Distribution.parse_config_files(self, filenames=filenames) + + ConfigMetadataHandler(self.metadata, self.command_options).parse() + ConfigOptionsHandler(self, self.command_options).parse() + def parse_command_line(self): """Process features after parsing command line options""" result = _Distribution.parse_command_line(self) -- cgit v1.2.1 From 163f36449c2b8c19c272414bff0bf80c9f3f2c7d Mon Sep 17 00:00:00 2001 From: idle sign Date: Mon, 5 Dec 2016 23:13:35 +0700 Subject: Added API functions. --- setuptools/dist.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index c975abe0..c04e6426 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -19,7 +19,7 @@ from pkg_resources.extern import packaging from setuptools.depends import Require from setuptools import windows_support from setuptools.monkey import get_unpatched -from setuptools.config import ConfigMetadataHandler, ConfigOptionsHandler +from setuptools.config import parse_configuration import pkg_resources @@ -350,8 +350,7 @@ class Distribution(_Distribution): """ _Distribution.parse_config_files(self, filenames=filenames) - ConfigMetadataHandler(self.metadata, self.command_options).parse() - ConfigOptionsHandler(self, self.command_options).parse() + parse_configuration(self, self.command_options) def parse_command_line(self): """Process features after parsing command line options""" -- 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 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'setuptools/dist.py') 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 -- 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/dist.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'setuptools/dist.py') 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 -- 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/dist.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'setuptools/dist.py') 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 -- 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/dist.py') 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/dist.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'setuptools/dist.py') 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""" -- 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 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) (limited to 'setuptools/dist.py') 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 -- 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/dist.py') 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/dist.py') 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/dist.py') 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/dist.py') 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/dist.py') 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 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 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'setuptools/dist.py') 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): -- 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 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) (limited to 'setuptools/dist.py') 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) -- 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 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'setuptools/dist.py') 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() -- 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/dist.py') 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/dist.py') 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/dist.py') 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/dist.py') 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/dist.py') 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/dist.py') 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/dist.py') 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/dist.py') 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/dist.py') 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/dist.py') 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 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'setuptools/dist.py') 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 -- 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/dist.py') 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 ++ 1 file changed, 2 insertions(+) (limited to 'setuptools/dist.py') 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) -- 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 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'setuptools/dist.py') 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""" -- 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 ++++++++++++++++++++++++------------------------------ 1 file changed, 24 insertions(+), 30 deletions(-) (limited to 'setuptools/dist.py') 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): -- 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/dist.py | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'setuptools/dist.py') 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 472c79f95cc41a3e85d6b212347d6b006c9c3c26 Mon Sep 17 00:00:00 2001 From: Benoit Pierre Date: Thu, 14 Sep 2017 22:07:13 +0200 Subject: support `setup_requires` in setup.cfg --- setuptools/dist.py | 40 +++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 21 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index a2ca8795..aa304500 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -316,23 +316,19 @@ class Distribution(Distribution_parse_config_files, _Distribution): have_package_data = hasattr(self, "package_data") if not have_package_data: self.package_data = {} - _attrs_dict = attrs or {} - if 'features' in _attrs_dict or 'require_features' in _attrs_dict: + attrs = attrs or {} + if 'features' in attrs or 'require_features' in attrs: Feature.warn_deprecated() self.require_features = [] self.features = {} self.dist_files = [] - self.src_root = attrs and attrs.pop("src_root", None) + self.src_root = attrs.pop("src_root", None) self.patch_missing_pkg_info(attrs) - self.long_description_content_type = _attrs_dict.get( + self.long_description_content_type = attrs.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', []) - assert_string_list(self, 'dependency_links', self.dependency_links) - if attrs and 'setup_requires' in attrs: - self.fetch_build_eggs(attrs['setup_requires']) + self.dependency_links = attrs.pop('dependency_links', []) + self.setup_requires = attrs.pop('setup_requires', []) for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'): vars(self).setdefault(ep.name, None) _Distribution.__init__(self, attrs) @@ -427,14 +423,15 @@ class Distribution(Distribution_parse_config_files, _Distribution): req.marker = None return req - def parse_config_files(self, filenames=None): + def parse_config_files(self, filenames=None, ignore_option_errors=False): """Parses configuration files from various levels and loads configuration. """ _Distribution.parse_config_files(self, filenames=filenames) - parse_configuration(self, self.command_options) + parse_configuration(self, self.command_options, + ignore_option_errors=ignore_option_errors) self._finalize_requires() def parse_command_line(self): @@ -497,19 +494,20 @@ class Distribution(Distribution_parse_config_files, _Distribution): """Fetch an egg needed for building""" 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 + opts.clear() + opts.update( + (k, v) + for k, v in self.get_option_dict('easy_install').items() + if k in ( + # don't use any other settings + 'find_links', 'site_dirs', 'index_url', + 'optimize', 'site_dirs', 'allow_hosts', + )) if self.dependency_links: links = self.dependency_links[:] if 'find_links' in opts: - links = opts['find_links'][1].split() + links + links = opts['find_links'][1] + links opts['find_links'] = ('setup', links) install_dir = self.get_egg_cache_dir() cmd = easy_install( -- cgit v1.2.1 From 2c897b5b877d401e13b661f2a0a14e99a1aabdc8 Mon Sep 17 00:00:00 2001 From: Benoit Pierre Date: Wed, 25 Oct 2017 17:55:26 +0200 Subject: improve encoding handling for `setup.cfg` Support the same mechanism as for Python sources for declaring the encoding to be used when reading `setup.cfg` (see PEP 263), and return the results of reading it as Unicode. Fix #1062 and #1136. --- setuptools/dist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index a2ca8795..b10bd6f7 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -432,7 +432,7 @@ class Distribution(Distribution_parse_config_files, _Distribution): and loads configuration. """ - _Distribution.parse_config_files(self, filenames=filenames) + Distribution_parse_config_files.parse_config_files(self, filenames=filenames) parse_configuration(self, self.command_options) self._finalize_requires() -- cgit v1.2.1 From 553e21e12ca2ebe6ab0f597e69594b71271faecc Mon Sep 17 00:00:00 2001 From: Henk-Jaap Wagenaar Date: Tue, 21 Nov 2017 22:16:53 +0000 Subject: Add setup.cfg support for long_description_content_type (in line with docs). --- setuptools/dist.py | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index aa304500..48c7a456 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -332,6 +332,13 @@ class Distribution(Distribution_parse_config_files, _Distribution): for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'): vars(self).setdefault(ep.name, None) _Distribution.__init__(self, attrs) + + try: + self.metadata.long_description_content_type + except AttributeError: + self.metadata.long_description_content_type = \ + self.long_description_content_type + if isinstance(self.metadata.version, numbers.Number): # Some people apparently take "version number" too literally :) self.metadata.version = str(self.metadata.version) -- cgit v1.2.1 From df2246449c271c07586bcecc3eaa36e9b0e94e3d Mon Sep 17 00:00:00 2001 From: Jeremy Stanley Date: Tue, 21 Nov 2017 00:48:44 +0000 Subject: Support PEP 345 Project-URL metadata By including one or more Project-URL entries in PKG-INFO metadata, PyPI can display helpful hyperlinks in a generic manner. Add support here to be able to pass it through setup.cfg and setup.py with a project_urls dict. See the corresponding section of the Core Metadata Specifications from the Python Packaging User Guide for details: https://packaging.python.org/specifications/core-metadata/#project-url-multiple-use --- setuptools/dist.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index aa304500..fa03cd27 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -44,7 +44,7 @@ def write_pkg_file(self, file): self.classifiers or self.download_url): version = '1.1' # Setuptools specific for PEP 345 - if hasattr(self, 'python_requires'): + if hasattr(self, 'python_requires') or self.project_urls: version = '1.2' file.write('Metadata-Version: %s\n' % version) @@ -57,6 +57,8 @@ def write_pkg_file(self, file): file.write('License: %s\n' % self.get_license()) if self.download_url: file.write('Download-URL: %s\n' % self.download_url) + for project_url in self.project_urls.items(): + file.write('Project-URL: %s, %s\n' % project_url) long_desc_content_type = getattr( self, @@ -327,11 +329,18 @@ class Distribution(Distribution_parse_config_files, _Distribution): self.long_description_content_type = attrs.get( 'long_description_content_type' ) + self.project_urls = attrs.get('project_urls', {}) self.dependency_links = attrs.pop('dependency_links', []) self.setup_requires = attrs.pop('setup_requires', []) for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'): vars(self).setdefault(ep.name, None) _Distribution.__init__(self, attrs) + + # The project_urls attribute may not be supported in distutils, so + # prime it here from our value if not automatically set + self.metadata.project_urls = getattr( + self.metadata, 'project_urls', self.project_urls) + if isinstance(self.metadata.version, numbers.Number): # Some people apparently take "version number" too literally :) self.metadata.version = str(self.metadata.version) -- cgit v1.2.1 From f012485e4767f3be81493c93ad534a02c6f14f14 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 25 Nov 2017 09:45:05 -0500 Subject: Disallow unordered sequences for specifying install_requires. Fixes #458. --- setuptools/dist.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index aa304500..477f93dd 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -166,6 +166,8 @@ def check_requirements(dist, attr, value): """Verify that install_requires is a valid requirements list""" try: list(pkg_resources.parse_requirements(value)) + if isinstance(value, (dict, set)): + raise TypeError("Unordered types are not allowed") except (TypeError, ValueError) as error: tmpl = ( "{attr!r} must be a string or list of strings " -- cgit v1.2.1 From 780a6f161ed4ce1026f5279c53c196d3bfdcec37 Mon Sep 17 00:00:00 2001 From: Henk-Jaap Wagenaar Date: Sat, 25 Nov 2017 18:55:58 +0000 Subject: Rework how to handle long_description_content_type --- setuptools/dist.py | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 48c7a456..8148df95 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -58,11 +58,8 @@ 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' + long_desc_content_type = \ + self.long_description_content_type or 'UNKNOWN' file.write('Description-Content-Type: %s\n' % long_desc_content_type) long_desc = rfc822_escape(self.get_long_description()) @@ -324,20 +321,15 @@ class Distribution(Distribution_parse_config_files, _Distribution): self.dist_files = [] self.src_root = attrs.pop("src_root", None) self.patch_missing_pkg_info(attrs) - self.long_description_content_type = attrs.get( - 'long_description_content_type' - ) self.dependency_links = attrs.pop('dependency_links', []) self.setup_requires = attrs.pop('setup_requires', []) for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'): vars(self).setdefault(ep.name, None) _Distribution.__init__(self, attrs) - try: - self.metadata.long_description_content_type - except AttributeError: - self.metadata.long_description_content_type = \ - self.long_description_content_type + self.metadata.long_description_content_type = attrs.get( + 'long_description_content_type' + ) if isinstance(self.metadata.version, numbers.Number): # Some people apparently take "version number" too literally :) -- cgit v1.2.1 From 82c7b798928cf2f508585b33616f454ef0b21983 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 4 Jan 2018 20:46:17 -0500 Subject: Remove extraneous attribute copy, artifact of bad merge. --- setuptools/dist.py | 3 --- 1 file changed, 3 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index fc5db5d1..c2bfdbc7 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -325,9 +325,6 @@ class Distribution(Distribution_parse_config_files, _Distribution): self.dist_files = [] self.src_root = attrs.pop("src_root", None) self.patch_missing_pkg_info(attrs) - self.long_description_content_type = attrs.get( - 'long_description_content_type' - ) self.project_urls = attrs.get('project_urls', {}) self.dependency_links = attrs.pop('dependency_links', []) self.setup_requires = attrs.pop('setup_requires', []) -- cgit v1.2.1 From b2ea3c4a20d008622caec445f5b6916ddd420d16 Mon Sep 17 00:00:00 2001 From: Dustin Ingram Date: Mon, 5 Mar 2018 21:57:06 -0600 Subject: Updates for PEP 566 (Metadata 2.1) --- setuptools/dist.py | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index c2bfdbc7..33ceb404 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -46,6 +46,8 @@ def write_pkg_file(self, file): # Setuptools specific for PEP 345 if hasattr(self, 'python_requires') or self.project_urls: version = '1.2' + if self.long_description_content_type or self.provides_extras: + version = '2.1' file.write('Metadata-Version: %s\n' % version) file.write('Name: %s\n' % self.get_name()) @@ -60,10 +62,6 @@ def write_pkg_file(self, file): for project_url in self.project_urls.items(): file.write('Project-URL: %s, %s\n' % project_url) - long_desc_content_type = \ - self.long_description_content_type 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) @@ -83,6 +81,16 @@ def write_pkg_file(self, file): if hasattr(self, 'python_requires'): file.write('Requires-Python: %s\n' % self.python_requires) + # PEP 566 + if self.long_description_content_type: + file.write( + 'Description-Content-Type: %s\n' % + self.long_description_content_type + ) + if self.provides_extras: + for extra in self.provides_extras: + file.write('Provides-Extra: %s\n' % extra) + # from Python 3.4 def write_pkg_info(self, base_dir): @@ -339,6 +347,9 @@ class Distribution(Distribution_parse_config_files, _Distribution): self.metadata.long_description_content_type = attrs.get( 'long_description_content_type' ) + self.metadata.provides_extras = getattr( + self.metadata, 'provides_extras', set() + ) if isinstance(self.metadata.version, numbers.Number): # Some people apparently take "version number" too literally :) @@ -372,6 +383,14 @@ class Distribution(Distribution_parse_config_files, _Distribution): """ if getattr(self, 'python_requires', None): self.metadata.python_requires = self.python_requires + + if getattr(self, 'extras_require', None): + for extra in self.extras_require.keys(): + # Since this gets called multiple times at points where the + # keys have become 'converted' extras, ensure that we are only + # truly adding extras we haven't seen before here. + self.metadata.provides_extras.add(extra.split(':')[0]) + self._convert_extras_requirements() self._move_install_requirements_markers() -- cgit v1.2.1 From 696afcad2d750f55c66ffc2a504fa2ebed1b6a59 Mon Sep 17 00:00:00 2001 From: wim glenn Date: Fri, 16 Mar 2018 01:17:06 -0500 Subject: fix for regression - spurious "Provides-Extra:" generated in metadata --- setuptools/dist.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 33ceb404..38ab4044 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -89,7 +89,8 @@ def write_pkg_file(self, file): ) if self.provides_extras: for extra in self.provides_extras: - file.write('Provides-Extra: %s\n' % extra) + if extra: + file.write('Provides-Extra: %s\n' % extra) # from Python 3.4 -- cgit v1.2.1 From 1fffc0eff9892c7c9bc3b177dd3483a41e85e40e Mon Sep 17 00:00:00 2001 From: wim glenn Date: Fri, 16 Mar 2018 14:27:42 -0500 Subject: address review comments and squash the empty string extra earlier --- setuptools/dist.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 38ab4044..d24958da 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -89,8 +89,7 @@ def write_pkg_file(self, file): ) if self.provides_extras: for extra in self.provides_extras: - if extra: - file.write('Provides-Extra: %s\n' % extra) + file.write('Provides-Extra: %s\n' % extra) # from Python 3.4 @@ -390,7 +389,9 @@ class Distribution(Distribution_parse_config_files, _Distribution): # Since this gets called multiple times at points where the # keys have become 'converted' extras, ensure that we are only # truly adding extras we haven't seen before here. - self.metadata.provides_extras.add(extra.split(':')[0]) + extra = extra.split(':')[0] + if extra: + self.metadata.provides_extras.add(extra) self._convert_extras_requirements() self._move_install_requirements_markers() -- cgit v1.2.1 From d8170d79a1059b6c58e1b54d94c6600f85354bf6 Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Fri, 16 Mar 2018 18:23:24 -0400 Subject: Add support for maintainer in PKG-INFO Per PEP 345, metadata Version 1.2 should support the Author, Maintainer, Author-Email and Maintainer-Email fields. --- setuptools/dist.py | 48 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 36 insertions(+), 12 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 33ceb404..a6928c49 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -14,6 +14,7 @@ from distutils.errors import ( DistutilsOptionError, DistutilsPlatformError, DistutilsSetupError, ) from distutils.util import rfc822_escape +from distutils.version import StrictVersion from setuptools.extern import six from setuptools.extern.six.moves import map, filter, filterfalse @@ -34,28 +35,46 @@ def _get_unpatched(cls): warnings.warn("Do not call this function", DeprecationWarning) return get_unpatched(cls) +def get_metadata_version(dist_md): + if dist_md.long_description_content_type or dist_md.provides_extras: + return StrictVersion('2.1') + elif getattr(dist_md, 'python_requires', None) is not None: + return StrictVersion('1.2') + elif (dist_md.provides or dist_md.requires or dist_md.obsoletes or + dist_md.classifiers or dist_md.download_url): + return StrictVersion('1.1') + + return StrictVersion('1.0') + # Based on Python 3.5 version def write_pkg_file(self, file): """Write the PKG-INFO format data to a file object. """ - version = '1.0' - if (self.provides or self.requires or self.obsoletes or - self.classifiers or self.download_url): - version = '1.1' - # Setuptools specific for PEP 345 - if hasattr(self, 'python_requires') or self.project_urls: - version = '1.2' - if self.long_description_content_type or self.provides_extras: - version = '2.1' + version = get_metadata_version(self) file.write('Metadata-Version: %s\n' % version) file.write('Name: %s\n' % self.get_name()) file.write('Version: %s\n' % self.get_version()) file.write('Summary: %s\n' % self.get_description()) file.write('Home-page: %s\n' % self.get_url()) - file.write('Author: %s\n' % self.get_contact()) - file.write('Author-email: %s\n' % self.get_contact_email()) + + if version == '1.2': + file.write('Author: %s\n' % self.get_contact()) + file.write('Author-email: %s\n' % self.get_contact_email()) + else: + optional_fields = ( + ('Author', 'author'), + ('Author-email', 'author_email'), + ('Maintainer', 'maintainer'), + ('Maintainer-email', 'maintainer_email'), + ) + + for field, attr in optional_fields: + attr_val = getattr(self, attr) + if attr_val is not None: + file.write('%s: %s\n' % (field, attr_val)) + file.write('License: %s\n' % self.get_license()) if self.download_url: file.write('Download-URL: %s\n' % self.download_url) @@ -69,7 +88,12 @@ def write_pkg_file(self, file): if keywords: file.write('Keywords: %s\n' % keywords) - self._write_list(file, 'Platform', self.get_platforms()) + if version == '1.2': + for platform in self.get_platforms(): + file.write('Platform: %s\n' % platform) + else: + self._write_list(file, 'Platform', self.get_platforms()) + self._write_list(file, 'Classifier', self.get_classifiers()) # PEP 314 -- cgit v1.2.1 From 929acc4e551448a68411968fb50336ad51ed4d3c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 17 Mar 2018 14:10:32 -0400 Subject: Setuptools now vendors its own direct dependencies (packaging, six, pyparsing). Ref #1296. --- setuptools/dist.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index b74dd0f5..cd34eef6 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -17,8 +17,8 @@ from distutils.util import rfc822_escape from distutils.version import StrictVersion from setuptools.extern import six +from setuptools.extern import packaging from setuptools.extern.six.moves import map, filter, filterfalse -from pkg_resources.extern import packaging from setuptools.depends import Require from setuptools import windows_support @@ -27,8 +27,8 @@ 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') +__import__('setuptools.extern.packaging.specifiers') +__import__('setuptools.extern.packaging.version') def _get_unpatched(cls): -- cgit v1.2.1 From f78c489384f9a87ec73b515cf94949f3427d6210 Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Sat, 17 Mar 2018 19:59:45 -0400 Subject: Fix issue with unicode author/maintainer on PY2 --- setuptools/dist.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index cd34eef6..0fb3d29c 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- __all__ = ['Distribution'] import re @@ -38,7 +39,9 @@ def _get_unpatched(cls): def get_metadata_version(dist_md): if dist_md.long_description_content_type or dist_md.provides_extras: return StrictVersion('2.1') - elif getattr(dist_md, 'python_requires', None) is not None: + elif (dist_md.maintainer is not None or + dist_md.maintainer_email is not None or + getattr(dist_md, 'python_requires', None) is not None): return StrictVersion('1.2') elif (dist_md.provides or dist_md.requires or dist_md.obsoletes or dist_md.classifiers or dist_md.download_url): @@ -48,7 +51,7 @@ def get_metadata_version(dist_md): # Based on Python 3.5 version -def write_pkg_file(self, file): +def write_pkg_file(self, file, is_test=False): """Write the PKG-INFO format data to a file object. """ version = get_metadata_version(self) @@ -59,7 +62,7 @@ def write_pkg_file(self, file): file.write('Summary: %s\n' % self.get_description()) file.write('Home-page: %s\n' % self.get_url()) - if version == '1.2': + if version < StrictVersion('1.2'): file.write('Author: %s\n' % self.get_contact()) file.write('Author-email: %s\n' % self.get_contact_email()) else: @@ -72,6 +75,9 @@ def write_pkg_file(self, file): for field, attr in optional_fields: attr_val = getattr(self, attr) + if six.PY2: + attr_val = self._encode_field(attr_val) + if attr_val is not None: file.write('%s: %s\n' % (field, attr_val)) @@ -88,7 +94,7 @@ def write_pkg_file(self, file): if keywords: file.write('Keywords: %s\n' % keywords) - if version == '1.2': + if version >= StrictVersion('1.2'): for platform in self.get_platforms(): file.write('Platform: %s\n' % platform) else: -- cgit v1.2.1 From c14a674e89fa5fc6661a35ca4eb468afa886c775 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 18 Mar 2018 09:41:28 -0400 Subject: When possible, avoid test-specific interfaces in production code. --- setuptools/dist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 0fb3d29c..61cf5807 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -51,7 +51,7 @@ def get_metadata_version(dist_md): # Based on Python 3.5 version -def write_pkg_file(self, file, is_test=False): +def write_pkg_file(self, file): """Write the PKG-INFO format data to a file object. """ version = get_metadata_version(self) -- cgit v1.2.1 From d92bc63ddd88729ad0bd892eacce29439b821fd2 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 18 Mar 2018 09:55:59 -0400 Subject: Delint --- setuptools/dist.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 61cf5807..284d922d 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -36,6 +36,7 @@ def _get_unpatched(cls): warnings.warn("Do not call this function", DeprecationWarning) return get_unpatched(cls) + def get_metadata_version(dist_md): if dist_md.long_description_content_type or dist_md.provides_extras: return StrictVersion('2.1') @@ -76,7 +77,7 @@ def write_pkg_file(self, file): for field, attr in optional_fields: attr_val = getattr(self, attr) if six.PY2: - attr_val = self._encode_field(attr_val) + attr_val = self._encode_field(attr_val) if attr_val is not None: file.write('%s: %s\n' % (field, attr_val)) @@ -562,7 +563,7 @@ class Distribution(Distribution_parse_config_files, _Distribution): # don't use any other settings 'find_links', 'site_dirs', 'index_url', 'optimize', 'site_dirs', 'allow_hosts', - )) + )) if self.dependency_links: links = self.dependency_links[:] if 'find_links' in opts: -- cgit v1.2.1 From a0723a66bf7950ee470971ac9931d751a7dd76f3 Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Sun, 18 Mar 2018 11:43:00 -0400 Subject: Stop patching write_pkg_info --- setuptools/dist.py | 9 --------- 1 file changed, 9 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 284d922d..321ab6b7 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -123,15 +123,6 @@ def write_pkg_file(self, file): file.write('Provides-Extra: %s\n' % extra) -# from Python 3.4 -def write_pkg_info(self, base_dir): - """Write the PKG-INFO file into the release tree. - """ - with open(os.path.join(base_dir, 'PKG-INFO'), 'w', - encoding='UTF-8') as pkg_info: - self.write_pkg_file(pkg_info) - - sequence = tuple, list -- cgit v1.2.1 From 07cd2e4e716264fd51aededcb77cefe37aafb25a Mon Sep 17 00:00:00 2001 From: Ian Wienand Date: Mon, 30 Apr 2018 19:08:58 +1000 Subject: Allow setting long_description_content_type externally Some tools, such as PBR, might want to set long_description_content_type during the parent object's Distribution.__init__() call (during distutils setup_keywords entry points). However, that field is currently unconditionally overwritten after these calls, erasing the value. We would rather not duplicate the existing method of copying into dist.metadata as done with project_urls. This preserves the fields within Distribution.metadata described by self._DISTUTIULS_UNUPPORTED_METADATA, or otherwise takes it from arguments. A test case that simulates setting the long description and overriding the arguments is added. --- setuptools/dist.py | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 321ab6b7..6ee4a97f 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -328,6 +328,12 @@ class Distribution(Distribution_parse_config_files, _Distribution): distribution for the included and excluded features. """ + _DISTUTILS_UNSUPPORTED_METADATA = { + 'long_description_content_type': None, + 'project_urls': dict, + 'provides_extras': set, + } + _patched_dist = None def patch_missing_pkg_info(self, attrs): @@ -353,25 +359,29 @@ class Distribution(Distribution_parse_config_files, _Distribution): self.require_features = [] self.features = {} self.dist_files = [] + # Filter-out setuptools' specific options. self.src_root = attrs.pop("src_root", None) self.patch_missing_pkg_info(attrs) - self.project_urls = attrs.get('project_urls', {}) self.dependency_links = attrs.pop('dependency_links', []) self.setup_requires = attrs.pop('setup_requires', []) for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'): vars(self).setdefault(ep.name, None) - _Distribution.__init__(self, attrs) - - # The project_urls attribute may not be supported in distutils, so - # prime it here from our value if not automatically set - self.metadata.project_urls = getattr( - self.metadata, 'project_urls', self.project_urls) - self.metadata.long_description_content_type = attrs.get( - 'long_description_content_type' - ) - self.metadata.provides_extras = getattr( - self.metadata, 'provides_extras', set() - ) + _Distribution.__init__(self, { + k: v for k, v in attrs.items() + if k not in self._DISTUTILS_UNSUPPORTED_METADATA + }) + + # Fill-in missing metadata fields not supported by distutils. + # Note some fields may have been set by other tools (e.g. pbr) + # above; they are taken preferrentially to setup() arguments + for option, default in self._DISTUTILS_UNSUPPORTED_METADATA.items(): + for source in self.metadata.__dict__, attrs: + if option in source: + value = source[option] + break + else: + value = default() if default else None + setattr(self.metadata, option, value) if isinstance(self.metadata.version, numbers.Number): # Some people apparently take "version number" too literally :) -- cgit v1.2.1 From 5854b0eba03dd257e30efff68f1632bdec5f0416 Mon Sep 17 00:00:00 2001 From: Junhan Huang Date: Sat, 27 Oct 2018 16:26:51 -0400 Subject: Add custom deprecation warning classes `DeprecationWarning` is not visible by default in the latest versions of CPython, so this switches the deprecation warnings in setuptools and pkg_resources over to custom classes derived from `Warning` instead. Fixes issue github issue #159 Co-authored-by: Junhan Huang Co-authored-by: Marton Pono --- setuptools/dist.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 6ee4a97f..f6078dbe 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -21,6 +21,8 @@ from setuptools.extern import six from setuptools.extern import packaging from setuptools.extern.six.moves import map, filter, filterfalse +from . import SetuptoolsDeprecationWarning + from setuptools.depends import Require from setuptools import windows_support from setuptools.monkey import get_unpatched @@ -33,7 +35,7 @@ __import__('setuptools.extern.packaging.version') def _get_unpatched(cls): - warnings.warn("Do not call this function", DeprecationWarning) + warnings.warn("Do not call this function", DistDeprecationWarning) return get_unpatched(cls) @@ -980,7 +982,7 @@ class Feature: "Features are deprecated and will be removed in a future " "version. See https://github.com/pypa/setuptools/issues/65." ) - warnings.warn(msg, DeprecationWarning, stacklevel=3) + warnings.warn(msg, DistDeprecationWarning, stacklevel=3) def __init__( self, description, standard=False, available=True, @@ -1069,3 +1071,7 @@ class Feature: " doesn't contain any packages or modules under %s" % (self.description, item, item) ) + + +class DistDeprecationWarning(SetuptoolsDeprecationWarning): + """Class for warning about deprecations in dist in setuptools. Not ignored by default, unlike DeprecationWarning.""" -- cgit v1.2.1 From 83fb2385518f7cf7885144f12db80b9364e67c7f Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Tue, 6 Nov 2018 09:18:05 -0500 Subject: Add DistributionMetadata.read_pkg_file This is the baseline, unchanged from the version in distutils.dist, to be modified before patching. --- setuptools/dist.py | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index f6078dbe..44e9aa89 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -53,6 +53,59 @@ def get_metadata_version(dist_md): return StrictVersion('1.0') +def read_pkg_file(self, file): + """Reads the metadata values from a file object.""" + msg = message_from_file(file) + + def _read_field(name): + value = msg[name] + if value == 'UNKNOWN': + return None + return value + + def _read_list(name): + values = msg.get_all(name, None) + if values == []: + return None + return values + + metadata_version = msg['metadata-version'] + self.name = _read_field('name') + self.version = _read_field('version') + self.description = _read_field('summary') + # we are filling author only. + self.author = _read_field('author') + self.maintainer = None + self.author_email = _read_field('author-email') + self.maintainer_email = None + self.url = _read_field('home-page') + self.license = _read_field('license') + + if 'download-url' in msg: + self.download_url = _read_field('download-url') + else: + self.download_url = None + + self.long_description = _read_field('description') + self.description = _read_field('summary') + + if 'keywords' in msg: + self.keywords = _read_field('keywords').split(',') + + self.platforms = _read_list('platform') + self.classifiers = _read_list('classifier') + + # PEP 314 - these fields only exist in 1.1 + if metadata_version == '1.1': + self.requires = _read_list('requires') + self.provides = _read_list('provides') + self.obsoletes = _read_list('obsoletes') + else: + self.requires = None + self.provides = None + self.obsoletes = None + + # Based on Python 3.5 version def write_pkg_file(self, file): """Write the PKG-INFO format data to a file object. -- cgit v1.2.1 From e5d362d3736bc5972835a55bcb165d8c55913547 Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Tue, 6 Nov 2018 09:30:24 -0500 Subject: Store metadata version on metadata object --- setuptools/dist.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 44e9aa89..5b750b62 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -10,7 +10,11 @@ import distutils.core import distutils.cmd import distutils.dist import itertools + + from collections import defaultdict +from email import message_from_file + from distutils.errors import ( DistutilsOptionError, DistutilsPlatformError, DistutilsSetupError, ) @@ -69,7 +73,7 @@ def read_pkg_file(self, file): return None return values - metadata_version = msg['metadata-version'] + self.metadata_version = StrictVersion(msg['metadata-version']) self.name = _read_field('name') self.version = _read_field('version') self.description = _read_field('summary') @@ -96,7 +100,7 @@ def read_pkg_file(self, file): self.classifiers = _read_list('classifier') # PEP 314 - these fields only exist in 1.1 - if metadata_version == '1.1': + if metadata_version == StrictVersion('1.1'): self.requires = _read_list('requires') self.provides = _read_list('provides') self.obsoletes = _read_list('obsoletes') -- cgit v1.2.1 From 386fcdbe02c4170a560d67742f2ac1319430ff43 Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Mon, 5 Nov 2018 10:23:15 -0500 Subject: Start patching DistributionMetadata.read_pkg_file This turns get_metadata_version into a method on DistributionMetadata, populated either by inferrence (in the case of package metadata specified in `setup`) or from the data in a specified PKG-INFO file. To populate metadata_version from PKG-INFO, we need to monkey patch read_pkg_file in addition to write_pkg_file. --- setuptools/dist.py | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 5b750b62..ae9816fd 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -43,18 +43,25 @@ def _get_unpatched(cls): return get_unpatched(cls) -def get_metadata_version(dist_md): - if dist_md.long_description_content_type or dist_md.provides_extras: - return StrictVersion('2.1') - elif (dist_md.maintainer is not None or - dist_md.maintainer_email is not None or - getattr(dist_md, 'python_requires', None) is not None): - return StrictVersion('1.2') - elif (dist_md.provides or dist_md.requires or dist_md.obsoletes or - dist_md.classifiers or dist_md.download_url): - return StrictVersion('1.1') +def get_metadata_version(self): + mv = getattr(self, 'metadata_version', None) + + if mv is None: + if self.long_description_content_type or self.provides_extras: + mv = StrictVersion('2.1') + elif (self.maintainer is not None or + self.maintainer_email is not None or + getattr(self, 'python_requires', None) is not None): + mv = StrictVersion('1.2') + elif (self.provides or self.requires or self.obsoletes or + self.classifiers or self.download_url): + mv = StrictVersion('1.1') + else: + mv = StrictVersion('1.0') + + self.metadata_version = mv - return StrictVersion('1.0') + return mv def read_pkg_file(self, file): @@ -100,7 +107,7 @@ def read_pkg_file(self, file): self.classifiers = _read_list('classifier') # PEP 314 - these fields only exist in 1.1 - if metadata_version == StrictVersion('1.1'): + if self.metadata_version == StrictVersion('1.1'): self.requires = _read_list('requires') self.provides = _read_list('provides') self.obsoletes = _read_list('obsoletes') @@ -114,7 +121,7 @@ def read_pkg_file(self, file): def write_pkg_file(self, file): """Write the PKG-INFO format data to a file object. """ - version = get_metadata_version(self) + version = self.get_metadata_version() file.write('Metadata-Version: %s\n' % version) file.write('Name: %s\n' % self.get_name()) -- cgit v1.2.1 From c34962d08168e0149a71485f1f71ddfd22146140 Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Tue, 6 Nov 2018 11:48:47 -0500 Subject: Use write_field in write_pkg_file This creates a wrapper function for writing fields in the PKG-INFO file, both to simplify the syntax and to add a point where we can inject an encoding function in order to support Python 2.7 compatibility. --- setuptools/dist.py | 46 ++++++++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 20 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index ae9816fd..b741c648 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -123,15 +123,23 @@ def write_pkg_file(self, file): """ version = self.get_metadata_version() - file.write('Metadata-Version: %s\n' % version) - file.write('Name: %s\n' % self.get_name()) - file.write('Version: %s\n' % self.get_version()) - file.write('Summary: %s\n' % self.get_description()) - file.write('Home-page: %s\n' % self.get_url()) + if six.PY2: + def write_field(key, value): + file.write("%s: %s\n" % (key, self._encode_field(value))) + else: + def write_field(key, value): + file.write("%s: %s\n" % (key, value)) + + + write_field('Metadata-Version', str(version)) + write_field('Name', self.get_name()) + write_field('Version', self.get_version()) + write_field('Summary', self.get_description()) + write_field('Home-page', self.get_url()) if version < StrictVersion('1.2'): - file.write('Author: %s\n' % self.get_contact()) - file.write('Author-email: %s\n' % self.get_contact_email()) + write_field('Author:', self.get_contact()) + write_field('Author-email:', self.get_contact_email()) else: optional_fields = ( ('Author', 'author'), @@ -142,28 +150,26 @@ def write_pkg_file(self, file): for field, attr in optional_fields: attr_val = getattr(self, attr) - if six.PY2: - attr_val = self._encode_field(attr_val) if attr_val is not None: - file.write('%s: %s\n' % (field, attr_val)) + write_field(field, attr_val) - file.write('License: %s\n' % self.get_license()) + write_field('License', self.get_license()) if self.download_url: - file.write('Download-URL: %s\n' % self.download_url) + write_field('Download-URL', self.download_url) for project_url in self.project_urls.items(): - file.write('Project-URL: %s, %s\n' % project_url) + write_field('Project-URL', '%s, %s' % project_url) long_desc = rfc822_escape(self.get_long_description()) - file.write('Description: %s\n' % long_desc) + write_field('Description', long_desc) keywords = ','.join(self.get_keywords()) if keywords: - file.write('Keywords: %s\n' % keywords) + write_field('Keywords', keywords) if version >= StrictVersion('1.2'): for platform in self.get_platforms(): - file.write('Platform: %s\n' % platform) + write_field('Platform', platform) else: self._write_list(file, 'Platform', self.get_platforms()) @@ -176,17 +182,17 @@ def write_pkg_file(self, file): # Setuptools specific for PEP 345 if hasattr(self, 'python_requires'): - file.write('Requires-Python: %s\n' % self.python_requires) + write_field('Requires-Python', self.python_requires) # PEP 566 if self.long_description_content_type: - file.write( - 'Description-Content-Type: %s\n' % + write_field( + 'Description-Content-Type', self.long_description_content_type ) if self.provides_extras: for extra in self.provides_extras: - file.write('Provides-Extra: %s\n' % extra) + write_field('Provides-Extra', extra) sequence = tuple, list -- cgit v1.2.1 From ac3cee396ff93f66afa86bc6e3aa3da3a2667514 Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Mon, 12 Nov 2018 22:32:51 -0500 Subject: Fix issue with missing author metadata Prior to this patch, if the author or author_email were omitted from `setup`, a malformed `PKG-INFO` would be created. --- setuptools/dist.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index b741c648..7062ae8d 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -138,8 +138,8 @@ def write_pkg_file(self, file): write_field('Home-page', self.get_url()) if version < StrictVersion('1.2'): - write_field('Author:', self.get_contact()) - write_field('Author-email:', self.get_contact_email()) + write_field('Author', self.get_contact()) + write_field('Author-email', self.get_contact_email()) else: optional_fields = ( ('Author', 'author'), -- cgit v1.2.1 From 24be5abd4cbd9d84537c457456f841522d626e14 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 25 Jan 2019 16:11:07 -0500 Subject: Given that the config file parsing functionality is unlikely to change upstream, just incorporate the functionality directly. --- setuptools/dist.py | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 74 insertions(+), 4 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index a2600711..4cc3bdfe 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- __all__ = ['Distribution'] +import io +import sys import re import os import warnings @@ -9,9 +11,11 @@ import distutils.log import distutils.core import distutils.cmd import distutils.dist +from distutils.errors import DistutilsOptionError +from distutils.util import strtobool +from distutils.debug import DEBUG import itertools - from collections import defaultdict from email import message_from_file @@ -31,8 +35,8 @@ from setuptools.depends import Require from setuptools import windows_support from setuptools.monkey import get_unpatched from setuptools.config import parse_configuration +from .unicode_utils import detect_encoding import pkg_resources -from .py36compat import Distribution_parse_config_files __import__('setuptools.extern.packaging.specifiers') __import__('setuptools.extern.packaging.version') @@ -332,7 +336,7 @@ def check_packages(dist, attr, value): _Distribution = get_unpatched(distutils.core.Distribution) -class Distribution(Distribution_parse_config_files, _Distribution): +class Distribution(_Distribution): """Distribution with support for features, tests, and package data This is an enhanced version of 'distutils.dist.Distribution' that @@ -556,12 +560,78 @@ class Distribution(Distribution_parse_config_files, _Distribution): req.marker = None return req + def _parse_config_files(self, filenames=None): + """ + Adapted from distutils.dist.Distribution.parse_config_files, + this method provides the same functionality in subtly-improved + ways. + """ + from setuptools.extern.six.moves.configparser import ConfigParser + + # Ignore install directory options if we have a venv + if six.PY3 and 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: + with io.open(filename, 'rb') as fp: + encoding = detect_encoding(fp) + if DEBUG: + self.announce(" reading %s [%s]" % ( + filename, encoding or 'locale') + ) + reader = io.TextIOWrapper(fp, encoding=encoding) + (parser.read_file if six.PY3 else parser.readfp)(reader) + 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) + def parse_config_files(self, filenames=None, ignore_option_errors=False): """Parses configuration files from various levels and loads configuration. """ - Distribution_parse_config_files.parse_config_files(self, filenames=filenames) + self._parse_config_files(filenames=filenames) parse_configuration(self, self.command_options, ignore_option_errors=ignore_option_errors) -- cgit v1.2.1 From b1615d12435289c21853be2f4f40e523317998da Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 26 Jan 2019 21:45:33 -0500 Subject: Adopt distutils.dist.Distribution._set_command_options to support better string detection. --- setuptools/dist.py | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 4cc3bdfe..a7ebe73b 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -14,6 +14,7 @@ import distutils.dist from distutils.errors import DistutilsOptionError from distutils.util import strtobool from distutils.debug import DEBUG +from distutils.fancy_getopt import translate_longopt import itertools from collections import defaultdict @@ -626,6 +627,53 @@ class Distribution(_Distribution): except ValueError as msg: raise DistutilsOptionError(msg) + def _set_command_options(self, command_obj, option_dict=None): + """ + Set the options for 'command_obj' from 'option_dict'. Basically + this means copying elements of a dictionary ('option_dict') to + attributes of an instance ('command'). + + 'command_obj' must be a Command instance. If 'option_dict' is not + supplied, uses the standard option dictionary for this command + (from 'self.command_options'). + + (Adopted from distutils.dist.Distribution._set_command_options) + """ + command_name = command_obj.get_command_name() + if option_dict is None: + option_dict = self.get_option_dict(command_name) + + if DEBUG: + self.announce(" setting options for '%s' command:" % command_name) + for (option, (source, value)) in option_dict.items(): + if DEBUG: + self.announce(" %s = %s (from %s)" % (option, value, + source)) + try: + bool_opts = [translate_longopt(o) + for o in command_obj.boolean_options] + except AttributeError: + bool_opts = [] + try: + neg_opt = command_obj.negative_opt + except AttributeError: + neg_opt = {} + + try: + is_string = isinstance(value, str) + if option in neg_opt and is_string: + setattr(command_obj, neg_opt[option], not strtobool(value)) + elif option in bool_opts and is_string: + setattr(command_obj, option, strtobool(value)) + elif hasattr(command_obj, option): + setattr(command_obj, option, value) + else: + raise DistutilsOptionError( + "error in %s: command '%s' has no such option '%s'" + % (source, command_name, option)) + except ValueError as msg: + raise DistutilsOptionError(msg) + def parse_config_files(self, filenames=None, ignore_option_errors=False): """Parses configuration files from various levels and loads configuration. -- cgit v1.2.1 From 249f24a1f04ce390a9e48a4d8a5bff7982714c86 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 26 Jan 2019 21:48:38 -0500 Subject: Fix test failure by better detecting string options from an updated ConfigParser. --- setuptools/dist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index a7ebe73b..b8551228 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -660,7 +660,7 @@ class Distribution(_Distribution): neg_opt = {} try: - is_string = isinstance(value, str) + is_string = isinstance(value, six.string_types) if option in neg_opt and is_string: setattr(command_obj, neg_opt[option], not strtobool(value)) elif option in bool_opts and is_string: -- cgit v1.2.1 From 9150b6b7272130f11a71190905e6bd3db31afd81 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 28 Jan 2019 17:10:37 -0500 Subject: Prefer native strings on Python 2 when reading config files. Fixes #1653. --- setuptools/dist.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index b8551228..ddb1787a 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -603,7 +603,7 @@ class Distribution(_Distribution): for opt in options: if opt != '__name__' and opt not in ignore_options: - val = parser.get(section, opt) + val = self._try_str(parser.get(section, opt)) opt = opt.replace('-', '_') opt_dict[opt] = (filename, val) @@ -627,6 +627,26 @@ class Distribution(_Distribution): except ValueError as msg: raise DistutilsOptionError(msg) + @staticmethod + def _try_str(val): + """ + On Python 2, much of distutils relies on string values being of + type 'str' (bytes) and not unicode text. If the value can be safely + encoded to bytes using the default encoding, prefer that. + + Why the default encoding? Because that value can be implicitly + decoded back to text if needed. + + Ref #1653 + """ + if six.PY3: + return val + try: + return val.encode() + except UnicodeEncodeError: + pass + return val + def _set_command_options(self, command_obj, option_dict=None): """ Set the options for 'command_obj' from 'option_dict'. Basically -- cgit v1.2.1 From 636070d9922f1443ae98b0fdd45b6c74c3c9af11 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 17 Feb 2019 17:09:26 -0500 Subject: Use an ordered set when constructing 'extras provided'. Ref #1305. --- setuptools/dist.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index ddb1787a..ce37761e 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -28,6 +28,7 @@ from distutils.version import StrictVersion from setuptools.extern import six from setuptools.extern import packaging +from setuptools.extern import ordered_set from setuptools.extern.six.moves import map, filter, filterfalse from . import SetuptoolsDeprecationWarning @@ -408,7 +409,7 @@ class Distribution(_Distribution): _DISTUTILS_UNSUPPORTED_METADATA = { 'long_description_content_type': None, 'project_urls': dict, - 'provides_extras': set, + 'provides_extras': ordered_set.OrderedSet, } _patched_dist = None -- cgit v1.2.1 From 16e452a42a3dbbb0ab3d3146ffa3b743cdca2539 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 6 Mar 2019 15:22:28 +0100 Subject: Remove duplicate import io (#1713) Found by lgtm --- setuptools/dist.py | 1 - 1 file changed, 1 deletion(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index ddb1787a..6233d5dc 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -1103,7 +1103,6 @@ class Distribution(_Distribution): return _Distribution.handle_display_options(self, option_order) # Stdout may be StringIO (e.g. in tests) - import io if not isinstance(sys.stdout, io.TextIOWrapper): return _Distribution.handle_display_options(self, option_order) -- cgit v1.2.1 From c27c705f6a326e4820f1a34d6ce1db101dad3a30 Mon Sep 17 00:00:00 2001 From: Dustin Ingram Date: Sat, 16 Mar 2019 10:06:53 -0700 Subject: Fix typo in docstring (#1718) --- setuptools/dist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 6233d5dc..e6d08b92 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -885,7 +885,7 @@ class Distribution(_Distribution): def include(self, **attrs): """Add items to distribution that are named in keyword arguments - For example, 'dist.exclude(py_modules=["x"])' would add 'x' to + For example, 'dist.include(py_modules=["x"])' would add 'x' to the distribution's 'py_modules' attribute, if it was not already there. -- cgit v1.2.1 From 393809a02ed4d0f07faec5c1f23384233e6cd68e Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 5 Apr 2019 09:27:23 -0400 Subject: Feed the hobgoblins (delint). --- setuptools/dist.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index e6d08b92..ae380290 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -11,7 +11,6 @@ import distutils.log import distutils.core import distutils.cmd import distutils.dist -from distutils.errors import DistutilsOptionError from distutils.util import strtobool from distutils.debug import DEBUG from distutils.fancy_getopt import translate_longopt @@ -135,7 +134,6 @@ def write_pkg_file(self, file): def write_field(key, value): file.write("%s: %s\n" % (key, value)) - write_field('Metadata-Version', str(version)) write_field('Name', self.get_name()) write_field('Version', self.get_version()) @@ -1281,4 +1279,5 @@ class Feature: class DistDeprecationWarning(SetuptoolsDeprecationWarning): - """Class for warning about deprecations in dist in setuptools. Not ignored by default, unlike DeprecationWarning.""" + """Class for warning about deprecations in dist in + setuptools. Not ignored by default, unlike DeprecationWarning.""" -- cgit v1.2.1 From 85fa4a6bc506be12fdbd0f4cff139e7c4e3bc6a8 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 5 Apr 2019 10:04:50 -0400 Subject: When reading config files, require them to be encoded with UTF-8. Fixes #1702. --- setuptools/dist.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index ae380290..9a165de0 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -35,7 +35,6 @@ from setuptools.depends import Require from setuptools import windows_support from setuptools.monkey import get_unpatched from setuptools.config import parse_configuration -from .unicode_utils import detect_encoding import pkg_resources __import__('setuptools.extern.packaging.specifiers') @@ -587,13 +586,9 @@ class Distribution(_Distribution): parser = ConfigParser() for filename in filenames: - with io.open(filename, 'rb') as fp: - encoding = detect_encoding(fp) + with io.open(filename, encoding='utf-8') as reader: if DEBUG: - self.announce(" reading %s [%s]" % ( - filename, encoding or 'locale') - ) - reader = io.TextIOWrapper(fp, encoding=encoding) + self.announce(" reading {filename}".format(**locals())) (parser.read_file if six.PY3 else parser.readfp)(reader) for section in parser.sections(): options = parser.options(section) -- cgit v1.2.1 From 880ff4a3579bac7839f8f038085381ae14155f31 Mon Sep 17 00:00:00 2001 From: Bastian Venthur Date: Thu, 16 May 2019 12:46:50 +0200 Subject: Force metadata-version = 1.2 when project urls are present. Closes: #1756 --- setuptools/dist.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 9a165de0..ea6411b1 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -54,7 +54,8 @@ def get_metadata_version(self): mv = StrictVersion('2.1') elif (self.maintainer is not None or self.maintainer_email is not None or - getattr(self, 'python_requires', None) is not None): + getattr(self, 'python_requires', None) is not None or + self.project_urls): mv = StrictVersion('1.2') elif (self.provides or self.requires or self.obsoletes or self.classifiers or self.download_url): -- cgit v1.2.1 From 305bb1cefc3251c67b55149139a768ddf474f7b6 Mon Sep 17 00:00:00 2001 From: Daniel Himmelstein Date: Thu, 23 May 2019 14:15:19 -0400 Subject: fix assert_string_list docstring value=None raises TypeError DistutilsSetupError: 2 must be a list of strings (got None) --- setuptools/dist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index ea6411b1..1e1b83be 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -213,7 +213,7 @@ def check_importable(dist, attr, value): def assert_string_list(dist, attr, value): - """Verify that value is a string list or None""" + """Verify that value is a string list""" try: assert ''.join(value) != value except (TypeError, ValueError, AttributeError, AssertionError): -- cgit v1.2.1 From 8f848bd777278fc8dcb42dc45751cd8b95ec2a02 Mon Sep 17 00:00:00 2001 From: Daniel Himmelstein Date: Wed, 22 May 2019 17:45:44 -0400 Subject: improve `package_data` check Ensure the dictionary values are lists/tuples of strings. Fix #1459. --- setuptools/dist.py | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 1e1b83be..f0f030b5 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -215,6 +215,10 @@ def check_importable(dist, attr, value): def assert_string_list(dist, attr, value): """Verify that value is a string list""" try: + # verify that value is a list or tuple to exclude unordered + # or single-use iterables + assert isinstance(value, (list, tuple)) + # verify that elements of value are strings assert ''.join(value) != value except (TypeError, ValueError, AttributeError, AssertionError): raise DistutilsSetupError( @@ -307,20 +311,17 @@ def check_test_suite(dist, attr, value): def check_package_data(dist, attr, value): """Verify that value is a dictionary of package names to glob lists""" - if isinstance(value, dict): - for k, v in value.items(): - if not isinstance(k, str): - break - try: - iter(v) - except TypeError: - break - else: - return - raise DistutilsSetupError( - attr + " must be a dictionary mapping package names to lists of " - "wildcard patterns" - ) + if not isinstance(value, dict): + raise DistutilsSetupError( + "{!r} must be a dictionary mapping package names to lists of " + "string wildcard patterns".format(attr)) + for k, v in value.items(): + if not isinstance(k, six.string_types): + raise DistutilsSetupError( + "keys of {!r} dict must be strings (got {!r})" + .format(attr, k) + ) + assert_string_list(dist, 'values of {!r} dict'.format(attr), v) def check_packages(dist, attr, value): -- cgit v1.2.1 From 4c22a6ca57753d3b5604a90b61a0c6c5efe53a1d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 18 Oct 2019 20:37:42 -0400 Subject: Add new hook 'setuptools.finalize_distribution_options' for plugins like 'setuptools_scm' to alter distribution options. --- setuptools/dist.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 2e5ad4bd..987d684e 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -728,6 +728,10 @@ class Distribution(_Distribution): if self.features: self._set_global_opts_from_features() + hook_key = 'setuptools.finalize_distribution_options' + for ep in pkg_resources.iter_entry_points(hook_key): + ep.load()(self) + for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'): value = getattr(self, ep.name, None) if value is not None: -- cgit v1.2.1 From 823ab9d2ec4ab89f90c0a781d872c9071b4afc13 Mon Sep 17 00:00:00 2001 From: Mick Koch Date: Mon, 20 May 2019 18:25:19 -0400 Subject: Add support for `license_files` option in metadata --- setuptools/dist.py | 1 + 1 file changed, 1 insertion(+) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 2e5ad4bd..fb379a20 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -409,6 +409,7 @@ class Distribution(_Distribution): 'long_description_content_type': None, 'project_urls': dict, 'provides_extras': ordered_set.OrderedSet, + 'license_files': list, } _patched_dist = None -- cgit v1.2.1 From 4a31168e517134529c229b310e89039323fdb02f Mon Sep 17 00:00:00 2001 From: Mick Koch Date: Mon, 28 Oct 2019 18:45:42 -0400 Subject: Use an OrderedSet for accumulating license files --- setuptools/dist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index fb379a20..0f3f7322 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -409,7 +409,7 @@ class Distribution(_Distribution): 'long_description_content_type': None, 'project_urls': dict, 'provides_extras': ordered_set.OrderedSet, - 'license_files': list, + 'license_files': ordered_set.OrderedSet, } _patched_dist = None -- cgit v1.2.1 From d6948c636f5e657ac56911b71b7a459d326d8389 Mon Sep 17 00:00:00 2001 From: Benoit Pierre Date: Sun, 29 Apr 2018 19:47:42 +0200 Subject: dist: re-implement `fetch_build_egg` to use pip --- setuptools/dist.py | 28 ++-------------------------- 1 file changed, 2 insertions(+), 26 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 2e5ad4bd..4a76b52b 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -759,32 +759,8 @@ class Distribution(_Distribution): def fetch_build_egg(self, req): """Fetch an egg needed for building""" - from setuptools.command.easy_install import easy_install - dist = self.__class__({'script_args': ['easy_install']}) - opts = dist.get_option_dict('easy_install') - opts.clear() - opts.update( - (k, v) - for k, v in self.get_option_dict('easy_install').items() - if k in ( - # don't use any other settings - 'find_links', 'site_dirs', 'index_url', - 'optimize', 'site_dirs', 'allow_hosts', - )) - if self.dependency_links: - links = self.dependency_links[:] - if 'find_links' in opts: - links = opts['find_links'][1] + 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) + from setuptools.installer import fetch_build_egg + return fetch_build_egg(self, req) def _set_global_opts_from_features(self): """Add --with-X/--without-X options based on optional features""" -- cgit v1.2.1 From 6b210c65938527a4bbcea34942fe43971be3c014 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 16 Nov 2019 12:06:47 -0500 Subject: Move all finalization of distribution options into hooks. Allow hooks to specify an index for ordering. --- setuptools/dist.py | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 987d684e..44990431 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -724,19 +724,28 @@ class Distribution(_Distribution): return resolved_dists def finalize_options(self): - _Distribution.finalize_options(self) - if self.features: - self._set_global_opts_from_features() - + """ + Allow plugins to apply arbitrary operations to the + distribution. Each hook may optionally define a 'order' + to influence the order of execution. Smaller numbers + go first and the default is 0. + """ hook_key = 'setuptools.finalize_distribution_options' - for ep in pkg_resources.iter_entry_points(hook_key): + + def by_order(hook): + return getattr(hook, 'order', 0) + eps = pkg_resources.iter_entry_points(hook_key) + for ep in sorted(eps, key=by_order): ep.load()(self) + def _finalize_setup_keywords(self): for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'): value = getattr(self, ep.name, None) if value is not None: ep.require(installer=self.fetch_build_egg) ep.load()(self, ep.name, value) + + def _finalize_2to3_doctests(self): 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 = [ @@ -790,9 +799,12 @@ class Distribution(_Distribution): cmd.ensure_finalized() return cmd.easy_install(req) - def _set_global_opts_from_features(self): + def _finalize_feature_opts(self): """Add --with-X/--without-X options based on optional features""" + if not self.features: + return + go = [] no = self.negative_opt.copy() -- cgit v1.2.1 From 796abd8dbec884cedf326cb5f85512a5d5648c4e Mon Sep 17 00:00:00 2001 From: Hugo Date: Wed, 8 Jan 2020 19:10:11 +0200 Subject: Fix for Python 4: replace unsafe six.PY3 with PY2 --- setuptools/dist.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 1ba262ec..fe5adf46 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -571,7 +571,7 @@ class Distribution(_Distribution): from setuptools.extern.six.moves.configparser import ConfigParser # Ignore install directory options if we have a venv - if six.PY3 and sys.prefix != sys.base_prefix: + if not six.PY2 and sys.prefix != sys.base_prefix: ignore_options = [ 'install-base', 'install-platbase', 'install-lib', 'install-platlib', 'install-purelib', 'install-headers', @@ -593,7 +593,7 @@ class Distribution(_Distribution): with io.open(filename, encoding='utf-8') as reader: if DEBUG: self.announce(" reading {filename}".format(**locals())) - (parser.read_file if six.PY3 else parser.readfp)(reader) + (parser.readfp if six.PY2 else parser.read_file)(reader) for section in parser.sections(): options = parser.options(section) opt_dict = self.get_option_dict(section) @@ -636,7 +636,7 @@ class Distribution(_Distribution): Ref #1653 """ - if six.PY3: + if not six.PY2: return val try: return val.encode() -- cgit v1.2.1 From c71969013d726c5cbd06dc81770ab064f3e783ff Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 19 Jan 2020 09:53:27 -0500 Subject: Remove the Features feature. Fixes #65. This commit reverts e4460fad043f4fa0edc7b7e1eef0b209f4588fe5. --- setuptools/dist.py | 260 +---------------------------------------------------- 1 file changed, 3 insertions(+), 257 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index fe5adf46..ff5a9ff3 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -19,9 +19,7 @@ import itertools from collections import defaultdict from email import message_from_file -from distutils.errors import ( - DistutilsOptionError, DistutilsPlatformError, DistutilsSetupError, -) +from distutils.errors import DistutilsOptionError, DistutilsSetupError from distutils.util import rfc822_escape from distutils.version import StrictVersion @@ -32,7 +30,6 @@ from setuptools.extern.six.moves import map, filter, filterfalse from . import SetuptoolsDeprecationWarning -from setuptools.depends import Require from setuptools import windows_support from setuptools.monkey import get_unpatched from setuptools.config import parse_configuration @@ -338,7 +335,7 @@ _Distribution = get_unpatched(distutils.core.Distribution) class Distribution(_Distribution): - """Distribution with support for features, tests, and package data + """Distribution with support for tests and package data This is an enhanced version of 'distutils.dist.Distribution' that effectively adds the following new optional keyword arguments to 'setup()': @@ -365,21 +362,6 @@ class Distribution(_Distribution): EasyInstall and requests one of your extras, the corresponding additional requirements will be installed if needed. - 'features' **deprecated** -- a dictionary mapping option names to - 'setuptools.Feature' - objects. Features are a portion of the distribution that can be - included or excluded based on user options, inter-feature dependencies, - and availability on the current system. Excluded features are omitted - from all setup commands, including source and binary distributions, so - you can create multiple distributions from the same source tree. - Feature names should be valid Python identifiers, except that they may - contain the '-' (minus) sign. Features can be included or excluded - via the command line options '--with-X' and '--without-X', where 'X' is - the name of the feature. Whether a feature is included by default, and - whether you are allowed to control this from the command line, is - determined by the Feature object. See the 'Feature' class for more - information. - 'test_suite' -- the name of a test suite to run for the 'test' command. If the user runs 'python setup.py test', the package will be installed, and the named test suite will be run. The format is the same as @@ -401,8 +383,7 @@ class Distribution(_Distribution): for manipulating the distribution's contents. For example, the 'include()' and 'exclude()' methods can be thought of as in-place add and subtract commands that add or remove packages, modules, extensions, and so on from - the distribution. They are used by the feature subsystem to configure the - distribution for the included and excluded features. + the distribution. """ _DISTUTILS_UNSUPPORTED_METADATA = { @@ -432,10 +413,6 @@ class Distribution(_Distribution): if not have_package_data: self.package_data = {} attrs = attrs or {} - if 'features' in attrs or 'require_features' in attrs: - Feature.warn_deprecated() - self.require_features = [] - self.features = {} self.dist_files = [] # Filter-out setuptools' specific options. self.src_root = attrs.pop("src_root", None) @@ -702,17 +679,6 @@ class Distribution(_Distribution): ignore_option_errors=ignore_option_errors) self._finalize_requires() - def parse_command_line(self): - """Process features after parsing command line options""" - result = _Distribution.parse_command_line(self) - if self.features: - self._finalize_features() - return result - - def _feature_attrname(self, name): - """Convert feature name to corresponding option attribute name""" - return 'with_' + name.replace('-', '_') - def fetch_build_eggs(self, requires): """Resolve pre-setup requirements""" resolved_dists = pkg_resources.working_set.resolve( @@ -776,53 +742,6 @@ class Distribution(_Distribution): from setuptools.installer import fetch_build_egg return fetch_build_egg(self, req) - def _finalize_feature_opts(self): - """Add --with-X/--without-X options based on optional features""" - - if not self.features: - return - - go = [] - no = self.negative_opt.copy() - - for name, feature in self.features.items(): - self._set_feature(name, None) - feature.validate(self) - - if feature.optional: - descr = feature.description - incdef = ' (default)' - excdef = '' - if not feature.include_by_default(): - excdef, incdef = incdef, 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 - self.negative_opt = self.feature_negopt = no - - def _finalize_features(self): - """Add/remove features and resolve dependencies between them""" - - # First, flag all the enabled items (and thus their dependencies) - for name, feature in self.features.items(): - enabled = self.feature_is_included(name) - if enabled or (enabled is None and feature.include_by_default()): - feature.include_in(self) - self._set_feature(name, 1) - - # Then disable the rest, so that off-by-default features don't - # get flagged as errors when they're required by an enabled feature - for name, feature in self.features.items(): - if not self.feature_is_included(name): - feature.exclude_from(self) - self._set_feature(name, 0) - def get_command_class(self, command): """Pluggable version of get_command_class()""" if command in self.cmdclass: @@ -852,25 +771,6 @@ class Distribution(_Distribution): self.cmdclass[ep.name] = cmdclass return _Distribution.get_command_list(self) - def _set_feature(self, name, status): - """Set feature's inclusion status""" - setattr(self, self._feature_attrname(name), status) - - def feature_is_included(self, name): - """Return 1 if feature is included, 0 if excluded, 'None' if unknown""" - return getattr(self, self._feature_attrname(name)) - - def include_feature(self, name): - """Request inclusion of feature named 'name'""" - - if self.feature_is_included(name) == 0: - descr = self.features[name].description - raise DistutilsOptionError( - descr + " is required, but was excluded or is not available" - ) - self.features[name].include_in(self) - self._set_feature(name, 1) - def include(self, **attrs): """Add items to distribution that are named in keyword arguments @@ -1115,160 +1015,6 @@ class Distribution(_Distribution): sys.stdout.detach(), encoding, errors, newline, line_buffering) -class Feature: - """ - **deprecated** -- The `Feature` facility was never completely implemented - or supported, `has reported issues - `_ and will be removed in - a future version. - - A subset of the distribution that can be excluded if unneeded/wanted - - Features are created using these keyword arguments: - - 'description' -- a short, human readable description of the feature, to - be used in error messages, and option help messages. - - 'standard' -- if true, the feature is included by default if it is - available on the current system. Otherwise, the feature is only - included if requested via a command line '--with-X' option, or if - another included feature requires it. The default setting is 'False'. - - 'available' -- if true, the feature is available for installation on the - current system. The default setting is 'True'. - - 'optional' -- if true, the feature's inclusion can be controlled from the - command line, using the '--with-X' or '--without-X' options. If - false, the feature's inclusion status is determined automatically, - based on 'availabile', 'standard', and whether any other feature - requires it. The default setting is 'True'. - - 'require_features' -- a string or sequence of strings naming features - that should also be included if this feature is included. Defaults to - empty list. May also contain 'Require' objects that should be - added/removed from the distribution. - - 'remove' -- a string or list of strings naming packages to be removed - from the distribution if this feature is *not* included. If the - feature *is* included, this argument is ignored. This argument exists - to support removing features that "crosscut" a distribution, such as - defining a 'tests' feature that removes all the 'tests' subpackages - provided by other features. The default for this argument is an empty - list. (Note: the named package(s) or modules must exist in the base - distribution when the 'setup()' function is initially called.) - - other keywords -- any other keyword arguments are saved, and passed to - the distribution's 'include()' and 'exclude()' methods when the - feature is included or excluded, respectively. So, for example, you - could pass 'packages=["a","b"]' to cause packages 'a' and 'b' to be - added or removed from the distribution as appropriate. - - A feature must include at least one 'requires', 'remove', or other - keyword argument. Otherwise, it can't affect the distribution in any way. - Note also that you can subclass 'Feature' to create your own specialized - feature types that modify the distribution in other ways when included or - excluded. See the docstrings for the various methods here for more detail. - Aside from the methods, the only feature attributes that distributions look - at are 'description' and 'optional'. - """ - - @staticmethod - def warn_deprecated(): - msg = ( - "Features are deprecated and will be removed in a future " - "version. See https://github.com/pypa/setuptools/issues/65." - ) - warnings.warn(msg, DistDeprecationWarning, stacklevel=3) - - def __init__( - self, description, standard=False, available=True, - optional=True, require_features=(), remove=(), **extras): - self.warn_deprecated() - - self.description = description - self.standard = standard - self.available = available - self.optional = optional - if isinstance(require_features, (str, Require)): - require_features = require_features, - - self.require_features = [ - r for r in require_features if isinstance(r, str) - ] - er = [r for r in require_features if not isinstance(r, str)] - if er: - extras['require_features'] = er - - if isinstance(remove, str): - remove = remove, - self.remove = remove - self.extras = extras - - 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." - ) - - def include_by_default(self): - """Should this feature be included by default?""" - return self.available and self.standard - - def include_in(self, dist): - """Ensure feature and its requirements are included in distribution - - You may override this in a subclass to perform additional operations on - the distribution. Note that this method may be called more than once - per feature, and so should be idempotent. - - """ - - if not self.available: - raise DistutilsPlatformError( - self.description + " is required, " - "but is not available on this platform" - ) - - dist.include(**self.extras) - - for f in self.require_features: - dist.include_feature(f) - - def exclude_from(self, dist): - """Ensure feature is excluded from distribution - - You may override this in a subclass to perform additional operations on - the distribution. This method will be called at most once per - feature, and only after all included features have been asked to - include themselves. - """ - - dist.exclude(**self.extras) - - if self.remove: - for item in self.remove: - dist.exclude_package(item) - - def validate(self, dist): - """Verify that feature makes sense in context of distribution - - This method is called by the distribution just before it parses its - command line. It checks to ensure that the 'remove' attribute, if any, - contains only valid package/module names that are present in the base - distribution when 'setup()' is called. You may override it in a - subclass to perform any other required validation of the feature - against a target distribution. - """ - - for item in self.remove: - if not dist.has_contents_for(item): - raise DistutilsSetupError( - "%s wants to be able to remove %s, but the distribution" - " doesn't contain any packages or modules under %s" - % (self.description, item, item) - ) - - class DistDeprecationWarning(SetuptoolsDeprecationWarning): """Class for warning about deprecations in dist in setuptools. Not ignored by default, unlike DeprecationWarning.""" -- cgit v1.2.1 From 5ce9e5f343ca14f9875106f37f16ad498b294183 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 19 Jan 2020 13:25:45 -0500 Subject: =?UTF-8?q?=F0=9F=91=B9=20Feed=20the=20hobgoblins=20(delint).?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setuptools/dist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index fe5adf46..ad54839b 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -162,7 +162,7 @@ def write_pkg_file(self, file): if self.download_url: write_field('Download-URL', self.download_url) for project_url in self.project_urls.items(): - write_field('Project-URL', '%s, %s' % project_url) + write_field('Project-URL', '%s, %s' % project_url) long_desc = rfc822_escape(self.get_long_description()) write_field('Description', long_desc) -- cgit v1.2.1 From 4835f01c41f784e1bcd24752dfecb96eb44a1a16 Mon Sep 17 00:00:00 2001 From: con-f-use Date: Fri, 14 Feb 2020 11:23:43 +0100 Subject: use finalize_distribution_options entrypoint order fixes #1993 --- setuptools/dist.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index fe5adf46..f6453a08 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -731,13 +731,13 @@ class Distribution(_Distribution): to influence the order of execution. Smaller numbers go first and the default is 0. """ - hook_key = 'setuptools.finalize_distribution_options' + group = 'setuptools.finalize_distribution_options' def by_order(hook): return getattr(hook, 'order', 0) - eps = pkg_resources.iter_entry_points(hook_key) + eps = map(lambda e: e.load(), pkg_resources.iter_entry_points(group)) for ep in sorted(eps, key=by_order): - ep.load()(self) + ep(self) def _finalize_setup_keywords(self): for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'): -- cgit v1.2.1 From 24fa288a98a14e6ec57b996b151a48203322c936 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 15 Mar 2020 14:57:18 -0400 Subject: Extract method for validating version. --- setuptools/dist.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 7ffe0ba1..0f71dd98 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -438,30 +438,35 @@ class Distribution(_Distribution): value = default() if default else None setattr(self.metadata, option, value) - if isinstance(self.metadata.version, numbers.Number): + self.metadata.version = self._validate_version(self.metadata.version) + self._finalize_requires() + + @staticmethod + def _validate_version(version): + if isinstance(version, numbers.Number): # Some people apparently take "version number" too literally :) - self.metadata.version = str(self.metadata.version) + version = str(version) - if self.metadata.version is not None: + if version is not None: try: - ver = packaging.version.Version(self.metadata.version) + ver = packaging.version.Version(version) normalized_version = str(ver) - if self.metadata.version != normalized_version: + if version != normalized_version: warnings.warn( "Normalizing '%s' to '%s'" % ( - self.metadata.version, + version, normalized_version, ) ) - self.metadata.version = normalized_version + version = normalized_version except (packaging.version.InvalidVersion, TypeError): warnings.warn( "The version specified (%r) is an invalid version, this " "may not work as expected with newer versions of " "setuptools, pip, and PyPI. Please see PEP 440 for more " - "details." % self.metadata.version + "details." % version ) - self._finalize_requires() + return version def _finalize_requires(self): """ -- cgit v1.2.1 From a323a4962eef39b6af7c5d07cdeb88bb0c307ce4 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 15 Mar 2020 15:31:09 -0400 Subject: Extract method for normalization, allowing for bypass when the version is wrapped in 'sic'. Fixes #308. --- setuptools/dist.py | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 0f71dd98..a2f8ea0d 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -30,6 +30,7 @@ from setuptools.extern.six.moves import map, filter, filterfalse from . import SetuptoolsDeprecationWarning +import setuptools from setuptools import windows_support from setuptools.monkey import get_unpatched from setuptools.config import parse_configuration @@ -438,9 +439,22 @@ class Distribution(_Distribution): value = default() if default else None setattr(self.metadata, option, value) - self.metadata.version = self._validate_version(self.metadata.version) + self.metadata.version = self._normalize_version( + self._validate_version(self.metadata.version)) self._finalize_requires() + @staticmethod + def _normalize_version(version): + if isinstance(version, setuptools.sic) or version is None: + return version + + normalized = str(packaging.version.Version(version)) + if version != normalized: + tmpl = "Normalizing '{version}' to '{normalized}'" + warnings.warn(tmpl.format(**locals())) + return normalized + return version + @staticmethod def _validate_version(version): if isinstance(version, numbers.Number): @@ -449,16 +463,7 @@ class Distribution(_Distribution): if version is not None: try: - ver = packaging.version.Version(version) - normalized_version = str(ver) - if version != normalized_version: - warnings.warn( - "Normalizing '%s' to '%s'" % ( - version, - normalized_version, - ) - ) - version = normalized_version + packaging.version.Version(version) except (packaging.version.InvalidVersion, TypeError): warnings.warn( "The version specified (%r) is an invalid version, this " @@ -466,6 +471,7 @@ class Distribution(_Distribution): "setuptools, pip, and PyPI. Please see PEP 440 for more " "details." % version ) + return setuptools.sic(version) return version def _finalize_requires(self): -- cgit v1.2.1 From a9eb9e73def8ca6c469e59f1b008746e368ad4c1 Mon Sep 17 00:00:00 2001 From: Ram Rachum Date: Tue, 16 Jun 2020 13:31:12 +0300 Subject: Fix exception causes all over the codebase --- setuptools/dist.py | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index fe64afa9..e813b11c 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -204,11 +204,11 @@ def check_importable(dist, attr, value): try: ep = pkg_resources.EntryPoint.parse('x=' + value) assert not ep.extras - except (TypeError, ValueError, AttributeError, AssertionError): + except (TypeError, ValueError, AttributeError, AssertionError) as e: raise DistutilsSetupError( "%r must be importable 'module:attrs' string (got %r)" % (attr, value) - ) + ) from e def assert_string_list(dist, attr, value): @@ -219,10 +219,10 @@ def assert_string_list(dist, attr, value): assert isinstance(value, (list, tuple)) # verify that elements of value are strings assert ''.join(value) != value - except (TypeError, ValueError, AttributeError, AssertionError): + except (TypeError, ValueError, AttributeError, AssertionError) as e: raise DistutilsSetupError( "%r must be a list of strings (got %r)" % (attr, value) - ) + ) from e def check_nsp(dist, attr, value): @@ -247,12 +247,12 @@ def check_extras(dist, attr, value): """Verify that extras_require mapping is valid""" try: list(itertools.starmap(_check_extra, value.items())) - except (TypeError, ValueError, AttributeError): + except (TypeError, ValueError, AttributeError) as e: raise DistutilsSetupError( "'extras_require' must be a dictionary whose values are " "strings or lists of strings containing valid project/version " "requirement specifiers." - ) + ) from e def _check_extra(extra, reqs): @@ -280,7 +280,9 @@ def check_requirements(dist, attr, value): "{attr!r} must be a string or list of strings " "containing valid project/version requirement specifiers; {error}" ) - raise DistutilsSetupError(tmpl.format(attr=attr, error=error)) + raise DistutilsSetupError( + tmpl.format(attr=attr, error=error) + ) from error def check_specifier(dist, attr, value): @@ -292,7 +294,9 @@ def check_specifier(dist, attr, value): "{attr!r} must be a string " "containing valid version specifiers; {error}" ) - raise DistutilsSetupError(tmpl.format(attr=attr, error=error)) + raise DistutilsSetupError( + tmpl.format(attr=attr, error=error) + ) from error def check_entry_points(dist, attr, value): @@ -300,7 +304,7 @@ def check_entry_points(dist, attr, value): try: pkg_resources.EntryPoint.parse_map(value) except ValueError as e: - raise DistutilsSetupError(e) + raise DistutilsSetupError(e) from e def check_test_suite(dist, attr, value): @@ -609,8 +613,8 @@ class Distribution(_Distribution): setattr(self, opt, strtobool(val)) else: setattr(self, opt, val) - except ValueError as msg: - raise DistutilsOptionError(msg) + except ValueError as e: + raise DistutilsOptionError(e) from e @staticmethod def _try_str(val): @@ -676,8 +680,8 @@ class Distribution(_Distribution): raise DistutilsOptionError( "error in %s: command '%s' has no such option '%s'" % (source, command_name, option)) - except ValueError as msg: - raise DistutilsOptionError(msg) + except ValueError as e: + raise DistutilsOptionError(e) from e def parse_config_files(self, filenames=None, ignore_option_errors=False): """Parses configuration files from various levels @@ -843,10 +847,10 @@ class Distribution(_Distribution): ) try: old = getattr(self, name) - except AttributeError: + except AttributeError as e: raise DistutilsSetupError( "%s: No such distribution setting" % name - ) + ) from e if old is not None and not isinstance(old, sequence): raise DistutilsSetupError( name + ": this setting cannot be changed via include/exclude" @@ -863,10 +867,10 @@ class Distribution(_Distribution): ) try: old = getattr(self, name) - except AttributeError: + except AttributeError as e: raise DistutilsSetupError( "%s: No such distribution setting" % name - ) + ) from e if old is None: setattr(self, name, value) elif not isinstance(old, sequence): -- cgit v1.2.1 From fb7ab81a3d080422687bad71f9ae9d36eeefbee2 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 16 Aug 2020 00:29:24 -0400 Subject: Remove Python 2 compatibility --- setuptools/dist.py | 48 +++++++++++------------------------------------- 1 file changed, 11 insertions(+), 37 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index e813b11c..2c088ef8 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -23,10 +23,8 @@ from distutils.errors import DistutilsOptionError, DistutilsSetupError from distutils.util import rfc822_escape from distutils.version import StrictVersion -from setuptools.extern import six from setuptools.extern import packaging from setuptools.extern import ordered_set -from setuptools.extern.six.moves import map, filter, filterfalse from . import SetuptoolsDeprecationWarning @@ -126,12 +124,8 @@ def write_pkg_file(self, file): """ version = self.get_metadata_version() - if six.PY2: - def write_field(key, value): - file.write("%s: %s\n" % (key, self._encode_field(value))) - else: - def write_field(key, value): - file.write("%s: %s\n" % (key, value)) + def write_field(key, value): + file.write("%s: %s\n" % (key, value)) write_field('Metadata-Version', str(version)) write_field('Name', self.get_name()) @@ -308,7 +302,7 @@ def check_entry_points(dist, attr, value): def check_test_suite(dist, attr, value): - if not isinstance(value, six.string_types): + if not isinstance(value, str): raise DistutilsSetupError("test_suite must be a string") @@ -319,7 +313,7 @@ def check_package_data(dist, attr, value): "{!r} must be a dictionary mapping package names to lists of " "string wildcard patterns".format(attr)) for k, v in value.items(): - if not isinstance(k, six.string_types): + if not isinstance(k, str): raise DistutilsSetupError( "keys of {!r} dict must be strings (got {!r})" .format(attr, k) @@ -537,7 +531,7 @@ class Distribution(_Distribution): spec_inst_reqs = getattr(self, 'install_requires', None) or () 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) + complex_reqs = itertools.filterfalse(is_simple_req, inst_reqs) self.install_requires = list(map(str, simple_reqs)) for r in complex_reqs: @@ -560,10 +554,10 @@ class Distribution(_Distribution): this method provides the same functionality in subtly-improved ways. """ - from setuptools.extern.six.moves.configparser import ConfigParser + from configparser import ConfigParser # Ignore install directory options if we have a venv - if not six.PY2 and sys.prefix != sys.base_prefix: + if sys.prefix != sys.base_prefix: ignore_options = [ 'install-base', 'install-platbase', 'install-lib', 'install-platlib', 'install-purelib', 'install-headers', @@ -585,14 +579,14 @@ class Distribution(_Distribution): with io.open(filename, encoding='utf-8') as reader: if DEBUG: self.announce(" reading {filename}".format(**locals())) - (parser.readfp if six.PY2 else parser.read_file)(reader) + parser.read_file(reader) 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 = self._try_str(parser.get(section, opt)) + val = parser.get(section, opt) opt = opt.replace('-', '_') opt_dict[opt] = (filename, val) @@ -616,26 +610,6 @@ class Distribution(_Distribution): except ValueError as e: raise DistutilsOptionError(e) from e - @staticmethod - def _try_str(val): - """ - On Python 2, much of distutils relies on string values being of - type 'str' (bytes) and not unicode text. If the value can be safely - encoded to bytes using the default encoding, prefer that. - - Why the default encoding? Because that value can be implicitly - decoded back to text if needed. - - Ref #1653 - """ - if not six.PY2: - return val - try: - return val.encode() - except UnicodeEncodeError: - pass - return val - def _set_command_options(self, command_obj, option_dict=None): """ Set the options for 'command_obj' from 'option_dict'. Basically @@ -669,7 +643,7 @@ class Distribution(_Distribution): neg_opt = {} try: - is_string = isinstance(value, six.string_types) + is_string = isinstance(value, str) if option in neg_opt and is_string: setattr(command_obj, neg_opt[option], not strtobool(value)) elif option in bool_opts and is_string: @@ -1003,7 +977,7 @@ class Distribution(_Distribution): """ import sys - if six.PY2 or self.help_commands: + if self.help_commands: return _Distribution.handle_display_options(self, option_order) # Stdout may be StringIO (e.g. in tests) -- cgit v1.2.1 From 08984781ec38f9ba8b35c70c6a5fff38e00b55aa Mon Sep 17 00:00:00 2001 From: Sviatoslav Sydorenko Date: Thu, 31 Dec 2020 17:58:45 +0100 Subject: Simplify `dist.Distribution._parse_config_files` --- setuptools/dist.py | 50 ++++++++++++++++++++++++++------------------------ 1 file changed, 26 insertions(+), 24 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 2c088ef8..186a407c 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -557,14 +557,12 @@ class Distribution(_Distribution): 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 = [] if sys.prefix == sys.base_prefix else [ + 'install-base', 'install-platbase', 'install-lib', + 'install-platlib', 'install-purelib', 'install-headers', + 'install-scripts', 'install-data', 'prefix', 'exec-prefix', + 'home', 'user', 'root', + ] ignore_options = frozenset(ignore_options) @@ -585,30 +583,34 @@ class Distribution(_Distribution): 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) + if opt == '__name__' or opt in ignore_options: + continue + + 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 'global' not in self.command_options: + return + # 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 e: - raise DistutilsOptionError(e) from e + for (opt, (src, val)) in self.command_options['global'].items(): + alias = self.negative_opt.get(opt) + if alias: + val = not strtobool(val) + elif opt in ('verbose', 'dry_run'): # ugh! + val = strtobool(val) + + try: + setattr(self, alias or opt, val) + except ValueError as e: + raise DistutilsOptionError(e) from e def _set_command_options(self, command_obj, option_dict=None): """ -- cgit v1.2.1 From fc891f5cf6d93ad533e2afb5e15a2952408ab358 Mon Sep 17 00:00:00 2001 From: Sviatoslav Sydorenko Date: Thu, 31 Dec 2020 13:08:18 +0100 Subject: Apply noqa C901 comments to overly complex code --- setuptools/dist.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 186a407c..662fbe67 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -119,7 +119,7 @@ def read_pkg_file(self, file): # Based on Python 3.5 version -def write_pkg_file(self, file): +def write_pkg_file(self, file): # noqa: C901 # is too complex (14) # FIXME """Write the PKG-INFO format data to a file object. """ version = self.get_metadata_version() @@ -548,7 +548,8 @@ class Distribution(_Distribution): req.marker = None return req - def _parse_config_files(self, filenames=None): + # FIXME: 'Distribution._parse_config_files' is too complex (14) + def _parse_config_files(self, filenames=None): # noqa: C901 """ Adapted from distutils.dist.Distribution.parse_config_files, this method provides the same functionality in subtly-improved @@ -612,7 +613,8 @@ class Distribution(_Distribution): except ValueError as e: raise DistutilsOptionError(e) from e - def _set_command_options(self, command_obj, option_dict=None): + # FIXME: 'Distribution._set_command_options' is too complex (14) + def _set_command_options(self, command_obj, option_dict=None): # noqa: C901 """ Set the options for 'command_obj' from 'option_dict'. Basically this means copying elements of a dictionary ('option_dict') to -- cgit v1.2.1 From d01d3ee286d4aab0ca0d78bd7a47b2556f81f499 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 6 Dec 2020 11:21:30 -0500 Subject: Quick fix for #1390. Now description cannot contain a newline. --- setuptools/dist.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 662fbe67..2d0aac33 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -118,6 +118,13 @@ def read_pkg_file(self, file): self.obsoletes = None +def single_line(val): + # quick and dirty validation for description pypa/setuptools#1390 + if '\n' in val: + raise ValueError("newlines not allowed") + return val + + # Based on Python 3.5 version def write_pkg_file(self, file): # noqa: C901 # is too complex (14) # FIXME """Write the PKG-INFO format data to a file object. @@ -130,7 +137,7 @@ def write_pkg_file(self, file): # noqa: C901 # is too complex (14) # FIXME write_field('Metadata-Version', str(version)) write_field('Name', self.get_name()) write_field('Version', self.get_version()) - write_field('Summary', self.get_description()) + write_field('Summary', single_line(self.get_description())) write_field('Home-page', self.get_url()) if version < StrictVersion('1.2'): -- cgit v1.2.1 From 2f891b433c42eb1554644199058c9190790ebf85 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 17 Jan 2021 12:14:25 -0500 Subject: Repair Descriptions with newlines and emit a warning that the value will be disallowed in the future. --- setuptools/dist.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 2d0aac33..172d66b1 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -121,7 +121,9 @@ def read_pkg_file(self, file): def single_line(val): # quick and dirty validation for description pypa/setuptools#1390 if '\n' in val: - raise ValueError("newlines not allowed") + # TODO after 2021-07-31: Replace with `raise ValueError("newlines not allowed")` + warnings.UserWarning("newlines not allowed and will break in the future") + val = val.replace('\n', ' ') return val -- cgit v1.2.1 From 49364a9eb82a986da1a1e6ad24022f7aac6e10d3 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 17 Jan 2021 17:13:37 -0500 Subject: Fix AttributeError in Description validation. Fixes #2539. --- setuptools/dist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 172d66b1..050388de 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -122,7 +122,7 @@ def single_line(val): # quick and dirty validation for description pypa/setuptools#1390 if '\n' in val: # TODO after 2021-07-31: Replace with `raise ValueError("newlines not allowed")` - warnings.UserWarning("newlines not allowed and will break in the future") + warnings.warn("newlines not allowed and will break in the future") val = val.replace('\n', ' ') return val -- cgit v1.2.1 From 21b122e06969a9d85c65ce8276519d34da7dc747 Mon Sep 17 00:00:00 2001 From: Melissa Li Date: Tue, 23 Feb 2021 21:23:35 -0500 Subject: Preserve case-sensitive keys in setup.cfg --- setuptools/dist.py | 1 + 1 file changed, 1 insertion(+) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 050388de..c31020f0 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -583,6 +583,7 @@ class Distribution(_Distribution): self.announce("Distribution.parse_config_files():") parser = ConfigParser() + parser.optionxform = str for filename in filenames: with io.open(filename, encoding='utf-8') as reader: if DEBUG: -- cgit v1.2.1 From 85f824f49d69177f68245b9788acaf5ace97afb7 Mon Sep 17 00:00:00 2001 From: Melissa Li Date: Sat, 27 Feb 2021 21:31:09 -0500 Subject: handle AttributeError by raising DisutilsSetupError in check_specifier --- setuptools/dist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index c31020f0..6ae3886b 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -292,7 +292,7 @@ def check_specifier(dist, attr, value): """Verify that value is a valid version specifier""" try: packaging.specifiers.SpecifierSet(value) - except packaging.specifiers.InvalidSpecifier as error: + except (packaging.specifiers.InvalidSpecifier, AttributeError) as error: tmpl = ( "{attr!r} must be a string " "containing valid version specifiers; {error}" -- cgit v1.2.1 From a2e9ae4cb75f9b00ddf37713ec307e5f00869737 Mon Sep 17 00:00:00 2001 From: Melissa Li Date: Tue, 2 Mar 2021 19:36:41 -0500 Subject: Add compatibility method to warn for future underscore change --- setuptools/dist.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 6ae3886b..fe5bd6f8 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -598,7 +598,7 @@ class Distribution(_Distribution): continue val = parser.get(section, opt) - opt = opt.replace('-', '_') + opt = self.dash_to_underscore_warning(opt) opt_dict[opt] = (filename, val) # Make the ConfigParser forget everything (so we retain @@ -623,6 +623,21 @@ class Distribution(_Distribution): except ValueError as e: raise DistutilsOptionError(e) from e + def dash_to_underscore_warning(self, opt): + if opt in ( + 'home-page', 'download-url', 'author-email', + 'maintainer-email', 'long-description', 'build-base', + 'project-urls', 'license-file', 'license-files', + 'long-description-content-type', + ): + underscore_opt = opt.replace('-', '_') + warnings.warn( + "Usage of dash-separated '%s' will not be supported in future " + "versions. Please use the underscore name '%s' instead" + % (opt, underscore_opt)) + return underscore_opt + return opt + # FIXME: 'Distribution._set_command_options' is too complex (14) def _set_command_options(self, command_obj, option_dict=None): # noqa: C901 """ -- cgit v1.2.1 From 9c2b717818046a0d8679b4a97c63db2dc1e32d54 Mon Sep 17 00:00:00 2001 From: Melissa Li Date: Wed, 3 Mar 2021 16:42:16 -0500 Subject: Update warning for dash to underscore conversion --- setuptools/dist.py | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index fe5bd6f8..40440a40 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -598,7 +598,7 @@ class Distribution(_Distribution): continue val = parser.get(section, opt) - opt = self.dash_to_underscore_warning(opt) + opt = self.dash_to_underscore_warning(opt, section) opt_dict[opt] = (filename, val) # Make the ConfigParser forget everything (so we retain @@ -622,21 +622,19 @@ class Distribution(_Distribution): setattr(self, alias or opt, val) except ValueError as e: raise DistutilsOptionError(e) from e - - def dash_to_underscore_warning(self, opt): - if opt in ( - 'home-page', 'download-url', 'author-email', - 'maintainer-email', 'long-description', 'build-base', - 'project-urls', 'license-file', 'license-files', - 'long-description-content-type', + + def dash_to_underscore_warning(self, opt, section): + if section in ( + 'options.extras_require', 'options.data_files', ): - underscore_opt = opt.replace('-', '_') + return opt + underscore_opt = opt.replace('-', '_') + if '-' in opt: warnings.warn( "Usage of dash-separated '%s' will not be supported in future " "versions. Please use the underscore name '%s' instead" % (opt, underscore_opt)) - return underscore_opt - return opt + return underscore_opt # FIXME: 'Distribution._set_command_options' is too complex (14) def _set_command_options(self, command_obj, option_dict=None): # noqa: C901 -- cgit v1.2.1 From 1af7000887487cfe1aa4d127a15e7802656f1e8a Mon Sep 17 00:00:00 2001 From: Melissa Li Date: Wed, 3 Mar 2021 17:02:45 -0500 Subject: Dash to underscore compatibility --- setuptools/dist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 40440a40..c074468b 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -622,7 +622,7 @@ class Distribution(_Distribution): setattr(self, alias or opt, val) except ValueError as e: raise DistutilsOptionError(e) from e - + def dash_to_underscore_warning(self, opt, section): if section in ( 'options.extras_require', 'options.data_files', -- cgit v1.2.1 From 132a6cde2a47f34680527258a3753a692e23b266 Mon Sep 17 00:00:00 2001 From: Melissa Li Date: Fri, 5 Mar 2021 23:03:15 -0500 Subject: Warn for uppercase usage in setup.cfg metadata --- setuptools/dist.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index c074468b..43a51ad8 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -599,6 +599,7 @@ class Distribution(_Distribution): val = parser.get(section, opt) opt = self.dash_to_underscore_warning(opt, section) + opt = self.uppercase_warning(opt, section) opt_dict[opt] = (filename, val) # Make the ConfigParser forget everything (so we retain @@ -636,6 +637,17 @@ class Distribution(_Distribution): % (opt, underscore_opt)) return underscore_opt + def uppercase_warning(self, opt, section): + if section in ('metadata',) and (any(c.isupper() for c in opt)): + lowercase_opt = opt.lower() + warnings.warn( + "Usage of uppercase key '%s' in '%s' will be deprecated in future " + "versions. Please use lowercase '%s' instead" + % (opt, section, lowercase_opt) + ) + return lowercase_opt + return opt + # FIXME: 'Distribution._set_command_options' is too complex (14) def _set_command_options(self, command_obj, option_dict=None): # noqa: C901 """ -- cgit v1.2.1 From 0bffc7c1c673f9735bdac71a2949fae809ec07a3 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 6 Mar 2021 21:47:15 -0500 Subject: Apply suggestions in code review. Co-authored-by: Sviatoslav Sydorenko --- setuptools/dist.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 43a51ad8..70c0e6be 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -599,7 +599,7 @@ class Distribution(_Distribution): val = parser.get(section, opt) opt = self.dash_to_underscore_warning(opt, section) - opt = self.uppercase_warning(opt, section) + opt = self.make_option_lowercase(opt, section) opt_dict[opt] = (filename, val) # Make the ConfigParser forget everything (so we retain @@ -637,16 +637,17 @@ class Distribution(_Distribution): % (opt, underscore_opt)) return underscore_opt - def uppercase_warning(self, opt, section): - if section in ('metadata',) and (any(c.isupper() for c in opt)): - lowercase_opt = opt.lower() - warnings.warn( - "Usage of uppercase key '%s' in '%s' will be deprecated in future " - "versions. Please use lowercase '%s' instead" - % (opt, section, lowercase_opt) - ) - return lowercase_opt - return opt + def make_option_lowercase(self, opt, section): + if section != 'metadata' or opt.islower(): + return opt + + lowercase_opt = opt.lower() + warnings.warn( + "Usage of uppercase key '%s' in '%s' will be deprecated in future " + "versions. Please use lowercase '%s' instead" + % (opt, section, lowercase_opt) + ) + return lowercase_opt # FIXME: 'Distribution._set_command_options' is too complex (14) def _set_command_options(self, command_obj, option_dict=None): # noqa: C901 -- cgit v1.2.1 From 938a33922c8cba3bbff6dfd1c2f723e5f929d6ce Mon Sep 17 00:00:00 2001 From: Melissa Li Date: Mon, 8 Mar 2021 02:59:16 -0500 Subject: Reduce scope of dash deprecation warning to Setuptools and distutils --- setuptools/dist.py | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 70c0e6be..d1587e34 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -11,6 +11,7 @@ import distutils.log import distutils.core import distutils.cmd import distutils.dist +import distutils.command from distutils.util import strtobool from distutils.debug import DEBUG from distutils.fancy_getopt import translate_longopt @@ -29,6 +30,7 @@ from setuptools.extern import ordered_set from . import SetuptoolsDeprecationWarning import setuptools +import setuptools.command from setuptools import windows_support from setuptools.monkey import get_unpatched from setuptools.config import parse_configuration @@ -629,7 +631,13 @@ class Distribution(_Distribution): 'options.extras_require', 'options.data_files', ): return opt + underscore_opt = opt.replace('-', '_') + commands = distutils.command.__all__ + setuptools.command.__all__ + if (not section.startswith('options') and section != 'metadata' + and section not in commands): + return underscore_opt + if '-' in opt: warnings.warn( "Usage of dash-separated '%s' will not be supported in future " -- cgit v1.2.1 From 7129ad1107f7015fe16f275eec17bf36a8badd84 Mon Sep 17 00:00:00 2001 From: Melissa Li Date: Mon, 8 Mar 2021 03:25:14 -0500 Subject: Fix formatting of tests and change dash deprecation method name --- setuptools/dist.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index d1587e34..7cebcb37 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -600,7 +600,7 @@ class Distribution(_Distribution): continue val = parser.get(section, opt) - opt = self.dash_to_underscore_warning(opt, section) + opt = self.warn_dash_deprecation(opt, section) opt = self.make_option_lowercase(opt, section) opt_dict[opt] = (filename, val) @@ -626,7 +626,7 @@ class Distribution(_Distribution): except ValueError as e: raise DistutilsOptionError(e) from e - def dash_to_underscore_warning(self, opt, section): + def warn_dash_deprecation(self, opt, section): if section in ( 'options.extras_require', 'options.data_files', ): -- cgit v1.2.1 From 78434a30184949c95bac3fab8b4c910e9f7266bb Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Sat, 10 Apr 2021 11:29:16 +0200 Subject: Refactor dist.read_pkg_file --- setuptools/dist.py | 64 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 36 insertions(+), 28 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 7cebcb37..8b687af7 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -16,6 +16,7 @@ from distutils.util import strtobool from distutils.debug import DEBUG from distutils.fancy_getopt import translate_longopt import itertools +from typing import List, Optional, TYPE_CHECKING from collections import defaultdict from email import message_from_file @@ -36,6 +37,9 @@ from setuptools.monkey import get_unpatched from setuptools.config import parse_configuration import pkg_resources +if TYPE_CHECKING: + from email.message import Message + __import__('setuptools.extern.packaging.specifiers') __import__('setuptools.extern.packaging.version') @@ -67,53 +71,57 @@ def get_metadata_version(self): return mv +def _read_field_from_msg(msg: "Message", field: str) -> Optional[str]: + """Read Message header field.""" + value = msg[field] + if value == 'UNKNOWN': + return None + return value + + +def _read_list_from_msg(msg: "Message", field: str) -> Optional[List[str]]: + """Read Message header field and return all results as list.""" + values = msg.get_all(field, None) + if values == []: + return None + return values + + def read_pkg_file(self, file): """Reads the metadata values from a file object.""" msg = message_from_file(file) - def _read_field(name): - value = msg[name] - if value == 'UNKNOWN': - return None - return value - - def _read_list(name): - values = msg.get_all(name, None) - if values == []: - return None - return values - self.metadata_version = StrictVersion(msg['metadata-version']) - self.name = _read_field('name') - self.version = _read_field('version') - self.description = _read_field('summary') + self.name = _read_field_from_msg(msg, 'name') + self.version = _read_field_from_msg(msg, 'version') + self.description = _read_field_from_msg(msg, 'summary') # we are filling author only. - self.author = _read_field('author') + self.author = _read_field_from_msg(msg, 'author') self.maintainer = None - self.author_email = _read_field('author-email') + self.author_email = _read_field_from_msg(msg, 'author-email') self.maintainer_email = None - self.url = _read_field('home-page') - self.license = _read_field('license') + self.url = _read_field_from_msg(msg, 'home-page') + self.license = _read_field_from_msg(msg, 'license') if 'download-url' in msg: - self.download_url = _read_field('download-url') + self.download_url = _read_field_from_msg(msg, 'download-url') else: self.download_url = None - self.long_description = _read_field('description') - self.description = _read_field('summary') + self.long_description = _read_field_from_msg(msg, 'description') + self.description = _read_field_from_msg(msg, 'summary') if 'keywords' in msg: - self.keywords = _read_field('keywords').split(',') + self.keywords = _read_field_from_msg(msg, 'keywords').split(',') - self.platforms = _read_list('platform') - self.classifiers = _read_list('classifier') + self.platforms = _read_list_from_msg(msg, 'platform') + self.classifiers = _read_list_from_msg(msg, 'classifier') # PEP 314 - these fields only exist in 1.1 if self.metadata_version == StrictVersion('1.1'): - self.requires = _read_list('requires') - self.provides = _read_list('provides') - self.obsoletes = _read_list('obsoletes') + self.requires = _read_list_from_msg(msg, 'requires') + self.provides = _read_list_from_msg(msg, 'provides') + self.obsoletes = _read_list_from_msg(msg, 'obsoletes') else: self.requires = None self.provides = None -- cgit v1.2.1 From c18ed8706d759e11d7bb7e9070a1897ee2f1e979 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Sat, 10 Apr 2021 11:30:10 +0200 Subject: Add rfc822_unescape --- setuptools/dist.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 8b687af7..c7af35dc 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -16,6 +16,7 @@ from distutils.util import strtobool from distutils.debug import DEBUG from distutils.fancy_getopt import translate_longopt import itertools +import textwrap from typing import List, Optional, TYPE_CHECKING from collections import defaultdict @@ -71,6 +72,16 @@ def get_metadata_version(self): return mv +def rfc822_unescape(content: str) -> str: + """Reverse RFC-822 escaping by removing leading whitespaces from content.""" + lines = content.splitlines() + if len(lines) == 1: + return lines[0].lstrip() + return '\n'.join( + (lines[0].lstrip(), + textwrap.dedent('\n'.join(lines[1:])))) + + def _read_field_from_msg(msg: "Message", field: str) -> Optional[str]: """Read Message header field.""" value = msg[field] @@ -79,6 +90,14 @@ def _read_field_from_msg(msg: "Message", field: str) -> Optional[str]: return value +def _read_field_unescaped_from_msg(msg: "Message", field: str) -> Optional[str]: + """Read Message header field and apply rfc822_unescape.""" + value = _read_field_from_msg(msg, field) + if value is None: + return value + return rfc822_unescape(value) + + def _read_list_from_msg(msg: "Message", field: str) -> Optional[List[str]]: """Read Message header field and return all results as list.""" values = msg.get_all(field, None) @@ -108,7 +127,7 @@ def read_pkg_file(self, file): else: self.download_url = None - self.long_description = _read_field_from_msg(msg, 'description') + self.long_description = _read_field_unescaped_from_msg(msg, 'description') self.description = _read_field_from_msg(msg, 'summary') if 'keywords' in msg: -- cgit v1.2.1 From c36033859ec2f7d9034a93c363ffc858ffbae172 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Thu, 15 Apr 2021 22:32:45 +0200 Subject: Add escaping to license field --- setuptools/dist.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index c7af35dc..49501263 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -120,7 +120,7 @@ def read_pkg_file(self, file): self.author_email = _read_field_from_msg(msg, 'author-email') self.maintainer_email = None self.url = _read_field_from_msg(msg, 'home-page') - self.license = _read_field_from_msg(msg, 'license') + self.license = _read_field_unescaped_from_msg(msg, 'license') if 'download-url' in msg: self.download_url = _read_field_from_msg(msg, 'download-url') @@ -188,7 +188,8 @@ def write_pkg_file(self, file): # noqa: C901 # is too complex (14) # FIXME if attr_val is not None: write_field(field, attr_val) - write_field('License', self.get_license()) + license = rfc822_escape(self.get_license()) + write_field('License', license) if self.download_url: write_field('Download-URL', self.download_url) for project_url in self.project_urls.items(): -- cgit v1.2.1 From 417b02b79d8b29c6af85cbaedcab05c47c159e30 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Fri, 16 Apr 2021 14:18:43 +0200 Subject: Always use latest mv version for PKG-INFO --- setuptools/dist.py | 48 +++++++++++++----------------------------------- 1 file changed, 13 insertions(+), 35 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index c7af35dc..ecbc723a 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -52,23 +52,9 @@ def _get_unpatched(cls): def get_metadata_version(self): mv = getattr(self, 'metadata_version', None) - if mv is None: - if self.long_description_content_type or self.provides_extras: - mv = StrictVersion('2.1') - elif (self.maintainer is not None or - self.maintainer_email is not None or - getattr(self, 'python_requires', None) is not None or - self.project_urls): - mv = StrictVersion('1.2') - elif (self.provides or self.requires or self.obsoletes or - self.classifiers or self.download_url): - mv = StrictVersion('1.1') - else: - mv = StrictVersion('1.0') - + mv = StrictVersion('2.1') self.metadata_version = mv - return mv @@ -171,22 +157,17 @@ def write_pkg_file(self, file): # noqa: C901 # is too complex (14) # FIXME write_field('Summary', single_line(self.get_description())) write_field('Home-page', self.get_url()) - if version < StrictVersion('1.2'): - write_field('Author', self.get_contact()) - write_field('Author-email', self.get_contact_email()) - else: - optional_fields = ( - ('Author', 'author'), - ('Author-email', 'author_email'), - ('Maintainer', 'maintainer'), - ('Maintainer-email', 'maintainer_email'), - ) + optional_fields = ( + ('Author', 'author'), + ('Author-email', 'author_email'), + ('Maintainer', 'maintainer'), + ('Maintainer-email', 'maintainer_email'), + ) - for field, attr in optional_fields: - attr_val = getattr(self, attr) - - if attr_val is not None: - write_field(field, attr_val) + for field, attr in optional_fields: + attr_val = getattr(self, attr, None) + if attr_val is not None: + write_field(field, attr_val) write_field('License', self.get_license()) if self.download_url: @@ -201,11 +182,8 @@ def write_pkg_file(self, file): # noqa: C901 # is too complex (14) # FIXME if keywords: write_field('Keywords', keywords) - if version >= StrictVersion('1.2'): - for platform in self.get_platforms(): - write_field('Platform', platform) - else: - self._write_list(file, 'Platform', self.get_platforms()) + for platform in self.get_platforms(): + write_field('Platform', platform) self._write_list(file, 'Classifier', self.get_classifiers()) -- cgit v1.2.1 From 0c8a1cafe753fa44392b6afe8f056d2d84943cab Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 19 May 2021 20:16:09 -0400 Subject: Move list of commands out of command package and into package metadata. --- setuptools/dist.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 24aef1bb..bab6b444 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -639,7 +639,7 @@ class Distribution(_Distribution): return opt underscore_opt = opt.replace('-', '_') - commands = distutils.command.__all__ + setuptools.command.__all__ + commands = distutils.command.__all__ + self._setuptools_commands() if (not section.startswith('options') and section != 'metadata' and section not in commands): return underscore_opt @@ -651,6 +651,10 @@ class Distribution(_Distribution): % (opt, underscore_opt)) return underscore_opt + def _setuptools_commands(self): + dist = pkg_resources.get_distribution('setuptools') + return list(dist.get_entry_map('distutils.commands')) + def make_option_lowercase(self, opt, section): if section != 'metadata' or opt.islower(): return opt -- cgit v1.2.1 From 1023e656f94124753e913bc070d82710a804b751 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 19 May 2021 20:59:55 -0400 Subject: When discovering commands and distribution does not yet exist, return the empty list. --- setuptools/dist.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index bab6b444..ba810d1a 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -652,8 +652,12 @@ class Distribution(_Distribution): return underscore_opt def _setuptools_commands(self): - dist = pkg_resources.get_distribution('setuptools') - return list(dist.get_entry_map('distutils.commands')) + try: + dist = pkg_resources.get_distribution('setuptools') + return list(dist.get_entry_map('distutils.commands')) + except pkg_resources.DistributionNotFound: + # during bootstrapping, distribution doesn't exist + return [] def make_option_lowercase(self, opt, section): if section != 'metadata' or opt.islower(): -- cgit v1.2.1 From 57002305daec1d31622927c0aea0d4e7df79ec40 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 21 May 2021 17:04:45 -0400 Subject: Extract _set_metadata_defaults method and collapse the implementation to a much simpler form. --- setuptools/dist.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 24aef1bb..96c82409 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -403,7 +403,7 @@ class Distribution(_Distribution): """ _DISTUTILS_UNSUPPORTED_METADATA = { - 'long_description_content_type': None, + 'long_description_content_type': lambda: None, 'project_urls': dict, 'provides_extras': ordered_set.OrderedSet, 'license_files': ordered_set.OrderedSet, @@ -442,22 +442,22 @@ class Distribution(_Distribution): if k not in self._DISTUTILS_UNSUPPORTED_METADATA }) - # Fill-in missing metadata fields not supported by distutils. - # Note some fields may have been set by other tools (e.g. pbr) - # above; they are taken preferrentially to setup() arguments - for option, default in self._DISTUTILS_UNSUPPORTED_METADATA.items(): - for source in self.metadata.__dict__, attrs: - if option in source: - value = source[option] - break - else: - value = default() if default else None - setattr(self.metadata, option, value) + self._set_metadata_defaults(attrs) self.metadata.version = self._normalize_version( self._validate_version(self.metadata.version)) self._finalize_requires() + def _set_metadata_defaults(self, attrs): + """ + Fill-in missing metadata fields not supported by distutils. + Some fields may have been set by other tools (e.g. pbr). + Those fields (vars(self.metadata)) take precedence to + supplied attrs. + """ + for option, default in self._DISTUTILS_UNSUPPORTED_METADATA.items(): + vars(self.metadata).setdefault(option, attrs.get(option, default())) + @staticmethod def _normalize_version(version): if isinstance(version, setuptools.sic) or version is None: -- cgit v1.2.1 From badfe739c61dd6f3609b4e3854519cfbe663c5a2 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Tue, 30 Mar 2021 16:51:52 +0200 Subject: Write long description in message payload --- setuptools/dist.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index a1b7e832..71b31873 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -176,8 +176,9 @@ def write_pkg_file(self, file): # noqa: C901 # is too complex (14) # FIXME for project_url in self.project_urls.items(): write_field('Project-URL', '%s, %s' % project_url) - long_desc = rfc822_escape(self.get_long_description()) - write_field('Description', long_desc) + if version < StrictVersion('2.1'): + long_desc = rfc822_escape(self.get_long_description()) + write_field('Description', long_desc) keywords = ','.join(self.get_keywords()) if keywords: @@ -207,6 +208,9 @@ def write_pkg_file(self, file): # noqa: C901 # is too complex (14) # FIXME for extra in self.provides_extras: write_field('Provides-Extra', extra) + if version >= StrictVersion('2.1'): + file.write("\n%s\n\n" % self.get_long_description()) + sequence = tuple, list -- cgit v1.2.1 From 56bd73b26d379f1423ae8816941018c6a48c0ef7 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Tue, 30 Mar 2021 19:11:21 +0200 Subject: Fix tests --- setuptools/dist.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 71b31873..1eb51ba4 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -96,6 +96,24 @@ def read_pkg_file(self, file): """Reads the metadata values from a file object.""" msg = message_from_file(file) + def _read_long_description(): + value = msg['description'] + if value in ('UNKNOWN', None): + return None + description_lines = value.splitlines() + if len(description_lines) == 1: + return description_lines[0].lstrip() + description_dedent = '\n'.join( + (description_lines[0].lstrip(), + textwrap.dedent('\n'.join(description_lines[1:])))) + return description_dedent + + def _read_payload(): + value = msg.get_payload().strip() + if value == 'UNKNOWN': + return None + return value + self.metadata_version = StrictVersion(msg['metadata-version']) self.name = _read_field_from_msg(msg, 'name') self.version = _read_field_from_msg(msg, 'version') @@ -114,6 +132,8 @@ def read_pkg_file(self, file): self.download_url = None self.long_description = _read_field_unescaped_from_msg(msg, 'description') + if self.long_description is None and self.metadata_version >= StrictVersion('2.1'): + self.long_description = _read_payload() self.description = _read_field_from_msg(msg, 'summary') if 'keywords' in msg: -- cgit v1.2.1 From d1a491ec8144ba856b74467b551eab359e86b659 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Thu, 15 Apr 2021 02:01:17 +0200 Subject: Changes after rebase --- setuptools/dist.py | 27 ++++++++------------------- 1 file changed, 8 insertions(+), 19 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 1eb51ba4..961b3cbd 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -92,28 +92,17 @@ def _read_list_from_msg(msg: "Message", field: str) -> Optional[List[str]]: return values +def _read_payload_from_msg(msg: "Message") -> Optional[str]: + value = msg.get_payload().strip() + if value == 'UNKNOWN': + return None + return value + + def read_pkg_file(self, file): """Reads the metadata values from a file object.""" msg = message_from_file(file) - def _read_long_description(): - value = msg['description'] - if value in ('UNKNOWN', None): - return None - description_lines = value.splitlines() - if len(description_lines) == 1: - return description_lines[0].lstrip() - description_dedent = '\n'.join( - (description_lines[0].lstrip(), - textwrap.dedent('\n'.join(description_lines[1:])))) - return description_dedent - - def _read_payload(): - value = msg.get_payload().strip() - if value == 'UNKNOWN': - return None - return value - self.metadata_version = StrictVersion(msg['metadata-version']) self.name = _read_field_from_msg(msg, 'name') self.version = _read_field_from_msg(msg, 'version') @@ -133,7 +122,7 @@ def read_pkg_file(self, file): self.long_description = _read_field_unescaped_from_msg(msg, 'description') if self.long_description is None and self.metadata_version >= StrictVersion('2.1'): - self.long_description = _read_payload() + self.long_description = _read_payload_from_msg(msg) self.description = _read_field_from_msg(msg, 'summary') if 'keywords' in msg: -- cgit v1.2.1 From 34c31ebe437edf9d6d4ee5010461668156793569 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Sun, 9 May 2021 20:16:37 +0200 Subject: Changes after rebase --- setuptools/dist.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 961b3cbd..bf3b9461 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -185,10 +185,6 @@ def write_pkg_file(self, file): # noqa: C901 # is too complex (14) # FIXME for project_url in self.project_urls.items(): write_field('Project-URL', '%s, %s' % project_url) - if version < StrictVersion('2.1'): - long_desc = rfc822_escape(self.get_long_description()) - write_field('Description', long_desc) - keywords = ','.join(self.get_keywords()) if keywords: write_field('Keywords', keywords) @@ -217,8 +213,7 @@ def write_pkg_file(self, file): # noqa: C901 # is too complex (14) # FIXME for extra in self.provides_extras: write_field('Provides-Extra', extra) - if version >= StrictVersion('2.1'): - file.write("\n%s\n\n" % self.get_long_description()) + file.write("\n%s\n\n" % self.get_long_description()) sequence = tuple, list -- cgit v1.2.1 From 3544de73b3662a27fac14d8eb9f5c841668d66de Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Sat, 17 Apr 2021 20:48:12 +0200 Subject: Add License-File field to package metadata --- setuptools/dist.py | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index bf3b9461..9d7fb751 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -15,9 +15,10 @@ import distutils.command from distutils.util import strtobool from distutils.debug import DEBUG from distutils.fancy_getopt import translate_longopt +from glob import iglob import itertools import textwrap -from typing import List, Optional, TYPE_CHECKING +from typing import List, Optional, Set, TYPE_CHECKING from collections import defaultdict from email import message_from_file @@ -141,6 +142,8 @@ def read_pkg_file(self, file): self.provides = None self.obsoletes = None + self.license_files_computed = _read_list_from_msg(msg, 'license-file') + def single_line(val): # quick and dirty validation for description pypa/setuptools#1390 @@ -213,6 +216,8 @@ def write_pkg_file(self, file): # noqa: C901 # is too complex (14) # FIXME for extra in self.provides_extras: write_field('Provides-Extra', extra) + self._write_list(file, 'License-File', self.license_files_computed) + file.write("\n%s\n\n" % self.get_long_description()) @@ -414,7 +419,9 @@ class Distribution(_Distribution): 'long_description_content_type': lambda: None, 'project_urls': dict, 'provides_extras': ordered_set.OrderedSet, - 'license_files': ordered_set.OrderedSet, + 'license_file': None, + 'license_files': None, + 'license_files_computed': list, } _patched_dist = None @@ -573,6 +580,31 @@ class Distribution(_Distribution): req.marker = None return req + def _finalize_license_files(self): + """Compute names of all license files which should be included.""" + files = set() + license_files: Optional[List[str]] = self.metadata.license_files + patterns: Set[str] = set(license_files) if license_files else set() + + license_file: Optional[str] = self.metadata.license_file + if license_file: + patterns.add(license_file) + + if license_files is None and license_file is None: + # Default patterns match the ones wheel uses + # See https://wheel.readthedocs.io/en/stable/user_guide.html + # -> 'Including license files in the generated wheel file' + patterns = ('LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*') + + for pattern in patterns: + for path in iglob(pattern): + if path.endswith('~'): + continue + if path not in files and os.path.isfile(path): + files.add(path) + + self.metadata.license_files_computed = sorted(files) + # FIXME: 'Distribution._parse_config_files' is too complex (14) def _parse_config_files(self, filenames=None): # noqa: C901 """ @@ -737,6 +769,7 @@ class Distribution(_Distribution): parse_configuration(self, self.command_options, ignore_option_errors=ignore_option_errors) self._finalize_requires() + self._finalize_license_files() def fetch_build_eggs(self, requires): """Resolve pre-setup requirements""" -- cgit v1.2.1 From b16725ac388b6a0bab6c0ff79d809559589be248 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Thu, 20 May 2021 01:24:01 +0200 Subject: Remove license_files_computed field --- setuptools/dist.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 9d7fb751..5be83d70 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -142,7 +142,7 @@ def read_pkg_file(self, file): self.provides = None self.obsoletes = None - self.license_files_computed = _read_list_from_msg(msg, 'license-file') + self.license_files = _read_list_from_msg(msg, 'license-file') def single_line(val): @@ -216,7 +216,7 @@ def write_pkg_file(self, file): # noqa: C901 # is too complex (14) # FIXME for extra in self.provides_extras: write_field('Provides-Extra', extra) - self._write_list(file, 'License-File', self.license_files_computed) + self._write_list(file, 'License-File', self.license_files or []) file.write("\n%s\n\n" % self.get_long_description()) @@ -421,7 +421,6 @@ class Distribution(_Distribution): 'provides_extras': ordered_set.OrderedSet, 'license_file': None, 'license_files': None, - 'license_files_computed': list, } _patched_dist = None @@ -603,7 +602,7 @@ class Distribution(_Distribution): if path not in files and os.path.isfile(path): files.add(path) - self.metadata.license_files_computed = sorted(files) + self.metadata.license_files = sorted(files) # FIXME: 'Distribution._parse_config_files' is too complex (14) def _parse_config_files(self, filenames=None): # noqa: C901 -- cgit v1.2.1 From 3d1cce5c6961c2dafc0dc0300c06a0af03e42842 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Sat, 22 May 2021 12:23:18 +0200 Subject: Keep user sorting for license files --- setuptools/dist.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 5be83d70..4877d36b 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -581,13 +581,13 @@ class Distribution(_Distribution): def _finalize_license_files(self): """Compute names of all license files which should be included.""" - files = set() + files: List[str] = [] license_files: Optional[List[str]] = self.metadata.license_files - patterns: Set[str] = set(license_files) if license_files else set() + patterns: List[str] = license_files if license_files else [] license_file: Optional[str] = self.metadata.license_file - if license_file: - patterns.add(license_file) + if license_file and license_file not in patterns: + patterns.append(license_file) if license_files is None and license_file is None: # Default patterns match the ones wheel uses @@ -596,13 +596,15 @@ class Distribution(_Distribution): patterns = ('LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*') for pattern in patterns: + files_pattern: Set[str] = set() for path in iglob(pattern): if path.endswith('~'): continue if path not in files and os.path.isfile(path): - files.add(path) + files_pattern.add(path) + files.extend(sorted(files_pattern)) - self.metadata.license_files = sorted(files) + self.metadata.license_files = files # FIXME: 'Distribution._parse_config_files' is too complex (14) def _parse_config_files(self, filenames=None): # noqa: C901 -- cgit v1.2.1 From e8d7f8861be6acc6f62057604fc2313b16d16b9f Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Sat, 22 May 2021 12:32:10 +0200 Subject: Fix after rebase --- setuptools/dist.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 4877d36b..65e6d8b2 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -419,8 +419,8 @@ class Distribution(_Distribution): 'long_description_content_type': lambda: None, 'project_urls': dict, 'provides_extras': ordered_set.OrderedSet, - 'license_file': None, - 'license_files': None, + 'license_file': lambda: None, + 'license_files': lambda: None, } _patched_dist = None -- cgit v1.2.1 From 540a912cce79aa3aed2fcb34c68d4433ad1d096e Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Sat, 22 May 2021 13:07:28 +0200 Subject: Remove license_file --- setuptools/dist.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 65e6d8b2..5b901801 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -419,7 +419,6 @@ class Distribution(_Distribution): 'long_description_content_type': lambda: None, 'project_urls': dict, 'provides_extras': ordered_set.OrderedSet, - 'license_file': lambda: None, 'license_files': lambda: None, } @@ -585,7 +584,10 @@ class Distribution(_Distribution): license_files: Optional[List[str]] = self.metadata.license_files patterns: List[str] = license_files if license_files else [] - license_file: Optional[str] = self.metadata.license_file + license_file: Optional[str] = None + opts = self.get_option_dict('metadata') + if 'license_file' in opts: + license_file = opts['license_file'][1] if license_file and license_file not in patterns: patterns.append(license_file) -- cgit v1.2.1 From c837956cd9f623815df6b2155efac6e13b5e1f7b Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Sat, 22 May 2021 13:45:14 +0200 Subject: Revert removal of license_file --- setuptools/dist.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 5b901801..65e6d8b2 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -419,6 +419,7 @@ class Distribution(_Distribution): 'long_description_content_type': lambda: None, 'project_urls': dict, 'provides_extras': ordered_set.OrderedSet, + 'license_file': lambda: None, 'license_files': lambda: None, } @@ -584,10 +585,7 @@ class Distribution(_Distribution): license_files: Optional[List[str]] = self.metadata.license_files patterns: List[str] = license_files if license_files else [] - license_file: Optional[str] = None - opts = self.get_option_dict('metadata') - if 'license_file' in opts: - license_file = opts['license_file'][1] + license_file: Optional[str] = self.metadata.license_file if license_file and license_file not in patterns: patterns.append(license_file) -- cgit v1.2.1 From 4429ffbb700c1838ddf57c47c378bd3c617eb8c3 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 22 May 2021 18:54:37 -0400 Subject: Replace for/if/add/extend with generator on patterns. Use unique_everseen to dedupe. --- setuptools/dist.py | 41 ++++++++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 11 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 65e6d8b2..067d7f5b 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -18,7 +18,7 @@ from distutils.fancy_getopt import translate_longopt from glob import iglob import itertools import textwrap -from typing import List, Optional, Set, TYPE_CHECKING +from typing import List, Optional, TYPE_CHECKING from collections import defaultdict from email import message_from_file @@ -581,7 +581,6 @@ class Distribution(_Distribution): def _finalize_license_files(self): """Compute names of all license files which should be included.""" - files: List[str] = [] license_files: Optional[List[str]] = self.metadata.license_files patterns: List[str] = license_files if license_files else [] @@ -595,16 +594,18 @@ class Distribution(_Distribution): # -> 'Including license files in the generated wheel file' patterns = ('LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*') - for pattern in patterns: - files_pattern: Set[str] = set() - for path in iglob(pattern): - if path.endswith('~'): - continue - if path not in files and os.path.isfile(path): - files_pattern.add(path) - files.extend(sorted(files_pattern)) + self.metadata.license_files = list( + unique_everseen(self._expand_patterns(patterns))) - self.metadata.license_files = files + @staticmethod + def _expand_patterns(patterns): + return ( + path + for pattern in patterns + for path in iglob(pattern) + if not path.endswith('~') + and os.path.isfile(path) + ) # FIXME: 'Distribution._parse_config_files' is too complex (14) def _parse_config_files(self, filenames=None): # noqa: C901 @@ -1111,3 +1112,21 @@ class Distribution(_Distribution): class DistDeprecationWarning(SetuptoolsDeprecationWarning): """Class for warning about deprecations in dist in setuptools. Not ignored by default, unlike DeprecationWarning.""" + + +def unique_everseen(iterable, key=None): + "List unique elements, preserving order. Remember all elements ever seen." + # unique_everseen('AAAABBBCCDAABBB') --> A B C D + # unique_everseen('ABBCcAD', str.lower) --> A B C D + seen = set() + seen_add = seen.add + if key is None: + for element in itertools.filterfalse(seen.__contains__, iterable): + seen_add(element) + yield element + else: + for element in iterable: + k = key(element) + if k not in seen: + seen_add(k) + yield element -- cgit v1.2.1 From 5a0404fa3875a069f7a6436f508116e852909cf2 Mon Sep 17 00:00:00 2001 From: Richard Purdie Date: Mon, 31 May 2021 16:34:03 +0100 Subject: setuptools/dist: Fix reproducibility issue by sorting globbing globbing uses os.listdir() which returns order on disk which is unsorted. Ultimately this leads to random ordering of License-File entries in PKG-INFO and means the resulting output isn't deterministic. Adding a sort fixes that. Switch to glob instead ot iglob since sorted() removes the benefit of an iterator. https://github.com/pypa/setuptools/issues/2691 Signed-off-by: Richard Purdie --- setuptools/dist.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 6e3f25f9..04db6843 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -15,7 +15,7 @@ import distutils.command from distutils.util import strtobool from distutils.debug import DEBUG from distutils.fancy_getopt import translate_longopt -from glob import iglob +from glob import glob import itertools import textwrap from typing import List, Optional, TYPE_CHECKING @@ -603,7 +603,7 @@ class Distribution(_Distribution): return ( path for pattern in patterns - for path in iglob(pattern) + for path in sorted(glob(pattern)) if not path.endswith('~') and os.path.isfile(path) ) -- cgit v1.2.1 From 51d1d8bd2b6e0dc379df6f6d72f60a95d983de95 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 4 Jul 2021 17:10:41 -0400 Subject: Restore the iterator --- setuptools/dist.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 04db6843..12ff308d 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -15,7 +15,7 @@ import distutils.command from distutils.util import strtobool from distutils.debug import DEBUG from distutils.fancy_getopt import translate_longopt -from glob import glob +from glob import iglob import itertools import textwrap from typing import List, Optional, TYPE_CHECKING @@ -603,7 +603,7 @@ class Distribution(_Distribution): return ( path for pattern in patterns - for path in sorted(glob(pattern)) + for path in sorted(iglob(pattern)) if not path.endswith('~') and os.path.isfile(path) ) -- cgit v1.2.1 From ea2e55bf305585bb04ea91a5db7c780b9b9a5ec9 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 4 Jul 2021 17:16:48 -0400 Subject: Add test --- setuptools/dist.py | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 12ff308d..df071c16 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -600,6 +600,12 @@ class Distribution(_Distribution): @staticmethod def _expand_patterns(patterns): + """ + >>> list(Distribution._expand_patterns(['LICENSE'])) + ['LICENSE'] + >>> list(Distribution._expand_patterns(['setup.cfg', 'LIC*'])) + ['setup.cfg', 'LICENSE'] + """ return ( path for pattern in patterns -- cgit v1.2.1 From ca296ca8663a376f3c36c9f8fd86b10ba81366c2 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Sun, 18 Jul 2021 09:27:21 +0100 Subject: remove lib2to3 usage --- setuptools/dist.py | 168 +++++++++++++++++++++++++++-------------------------- 1 file changed, 87 insertions(+), 81 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index df071c16..eec0b27e 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -65,9 +65,7 @@ def rfc822_unescape(content: str) -> str: lines = content.splitlines() if len(lines) == 1: return lines[0].lstrip() - return '\n'.join( - (lines[0].lstrip(), - textwrap.dedent('\n'.join(lines[1:])))) + return '\n'.join((lines[0].lstrip(), textwrap.dedent('\n'.join(lines[1:])))) def _read_field_from_msg(msg: "Message", field: str) -> Optional[str]: @@ -157,8 +155,7 @@ def single_line(val): # Based on Python 3.5 version def write_pkg_file(self, file): # noqa: C901 # is too complex (14) # FIXME - """Write the PKG-INFO format data to a file object. - """ + """Write the PKG-INFO format data to a file object.""" version = self.get_metadata_version() def write_field(key, value): @@ -209,10 +206,7 @@ def write_pkg_file(self, file): # noqa: C901 # is too complex (14) # FIXME # PEP 566 if self.long_description_content_type: - write_field( - 'Description-Content-Type', - self.long_description_content_type - ) + write_field('Description-Content-Type', self.long_description_content_type) if self.provides_extras: for extra in self.provides_extras: write_field('Provides-Extra', extra) @@ -231,8 +225,7 @@ def check_importable(dist, attr, value): assert not ep.extras except (TypeError, ValueError, AttributeError, AssertionError) as e: raise DistutilsSetupError( - "%r must be importable 'module:attrs' string (got %r)" - % (attr, value) + "%r must be importable 'module:attrs' string (got %r)" % (attr, value) ) from e @@ -257,14 +250,16 @@ def check_nsp(dist, attr, value): for nsp in ns_packages: if not dist.has_contents_for(nsp): raise DistutilsSetupError( - "Distribution contains no modules or packages for " + - "namespace package %r" % nsp + "Distribution contains no modules or packages for " + + "namespace package %r" % nsp ) parent, sep, child = nsp.rpartition('.') if parent and parent not in ns_packages: distutils.log.warn( "WARNING: %r is declared as a package namespace, but %r" - " is not: please correct this in setup.py", nsp, parent + " is not: please correct this in setup.py", + nsp, + parent, ) @@ -305,9 +300,7 @@ def check_requirements(dist, attr, value): "{attr!r} must be a string or list of strings " "containing valid project/version requirement specifiers; {error}" ) - raise DistutilsSetupError( - tmpl.format(attr=attr, error=error) - ) from error + raise DistutilsSetupError(tmpl.format(attr=attr, error=error)) from error def check_specifier(dist, attr, value): @@ -316,12 +309,9 @@ def check_specifier(dist, attr, value): packaging.specifiers.SpecifierSet(value) except (packaging.specifiers.InvalidSpecifier, AttributeError) as error: tmpl = ( - "{attr!r} must be a string " - "containing valid version specifiers; {error}" + "{attr!r} must be a string " "containing valid version specifiers; {error}" ) - raise DistutilsSetupError( - tmpl.format(attr=attr, error=error) - ) from error + raise DistutilsSetupError(tmpl.format(attr=attr, error=error)) from error def check_entry_points(dist, attr, value): @@ -342,12 +332,12 @@ def check_package_data(dist, attr, value): if not isinstance(value, dict): raise DistutilsSetupError( "{!r} must be a dictionary mapping package names to lists of " - "string wildcard patterns".format(attr)) + "string wildcard patterns".format(attr) + ) for k, v in value.items(): if not isinstance(k, str): raise DistutilsSetupError( - "keys of {!r} dict must be strings (got {!r})" - .format(attr, k) + "keys of {!r} dict must be strings (got {!r})".format(attr, k) ) assert_string_list(dist, 'values of {!r} dict'.format(attr), v) @@ -357,7 +347,8 @@ def check_packages(dist, attr, value): if not re.match(r'\w+(\.\w+)*', pkgname): distutils.log.warn( "WARNING: %r not a valid package name; please use only " - ".-separated package names in setup.py", pkgname + ".-separated package names in setup.py", + pkgname, ) @@ -452,15 +443,20 @@ class Distribution(_Distribution): self.setup_requires = attrs.pop('setup_requires', []) for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'): vars(self).setdefault(ep.name, None) - _Distribution.__init__(self, { - k: v for k, v in attrs.items() - if k not in self._DISTUTILS_UNSUPPORTED_METADATA - }) + _Distribution.__init__( + self, + { + k: v + for k, v in attrs.items() + if k not in self._DISTUTILS_UNSUPPORTED_METADATA + }, + ) self._set_metadata_defaults(attrs) self.metadata.version = self._normalize_version( - self._validate_version(self.metadata.version)) + self._validate_version(self.metadata.version) + ) self._finalize_requires() def _set_metadata_defaults(self, attrs): @@ -596,7 +592,8 @@ class Distribution(_Distribution): patterns = ('LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*') self.metadata.license_files = list( - unique_everseen(self._expand_patterns(patterns))) + unique_everseen(self._expand_patterns(patterns)) + ) @staticmethod def _expand_patterns(patterns): @@ -610,8 +607,7 @@ class Distribution(_Distribution): path for pattern in patterns for path in sorted(iglob(pattern)) - if not path.endswith('~') - and os.path.isfile(path) + if not path.endswith('~') and os.path.isfile(path) ) # FIXME: 'Distribution._parse_config_files' is too complex (14) @@ -624,12 +620,25 @@ class Distribution(_Distribution): from configparser import ConfigParser # Ignore install directory options if we have a venv - ignore_options = [] if sys.prefix == sys.base_prefix else [ - 'install-base', 'install-platbase', 'install-lib', - 'install-platlib', 'install-purelib', 'install-headers', - 'install-scripts', 'install-data', 'prefix', 'exec-prefix', - 'home', 'user', 'root', - ] + ignore_options = ( + [] + if sys.prefix == sys.base_prefix + else [ + 'install-base', + 'install-platbase', + 'install-lib', + 'install-platlib', + 'install-purelib', + 'install-headers', + 'install-scripts', + 'install-data', + 'prefix', + 'exec-prefix', + 'home', + 'user', + 'root', + ] + ) ignore_options = frozenset(ignore_options) @@ -683,21 +692,26 @@ class Distribution(_Distribution): def warn_dash_deprecation(self, opt, section): if section in ( - 'options.extras_require', 'options.data_files', + 'options.extras_require', + 'options.data_files', ): return opt underscore_opt = opt.replace('-', '_') commands = distutils.command.__all__ + self._setuptools_commands() - if (not section.startswith('options') and section != 'metadata' - and section not in commands): + if ( + not section.startswith('options') + and section != 'metadata' + and section not in commands + ): return underscore_opt if '-' in opt: warnings.warn( "Usage of dash-separated '%s' will not be supported in future " "versions. Please use the underscore name '%s' instead" - % (opt, underscore_opt)) + % (opt, underscore_opt) + ) return underscore_opt def _setuptools_commands(self): @@ -741,11 +755,9 @@ class Distribution(_Distribution): self.announce(" setting options for '%s' command:" % command_name) for (option, (source, value)) in option_dict.items(): if DEBUG: - self.announce(" %s = %s (from %s)" % (option, value, - source)) + self.announce(" %s = %s (from %s)" % (option, value, source)) try: - bool_opts = [translate_longopt(o) - for o in command_obj.boolean_options] + bool_opts = [translate_longopt(o) for o in command_obj.boolean_options] except AttributeError: bool_opts = [] try: @@ -764,7 +776,8 @@ class Distribution(_Distribution): else: raise DistutilsOptionError( "error in %s: command '%s' has no such option '%s'" - % (source, command_name, option)) + % (source, command_name, option) + ) except ValueError as e: raise DistutilsOptionError(e) from e @@ -775,8 +788,9 @@ class Distribution(_Distribution): """ self._parse_config_files(filenames=filenames) - parse_configuration(self, self.command_options, - ignore_option_errors=ignore_option_errors) + parse_configuration( + self, self.command_options, ignore_option_errors=ignore_option_errors + ) self._finalize_requires() self._finalize_license_files() @@ -802,6 +816,7 @@ class Distribution(_Distribution): def by_order(hook): return getattr(hook, 'order', 0) + eps = map(lambda e: e.load(), pkg_resources.iter_entry_points(group)) for ep in sorted(eps, key=by_order): ep(self) @@ -813,16 +828,6 @@ class Distribution(_Distribution): ep.require(installer=self.fetch_build_egg) ep.load()(self, ep.name, value) - def _finalize_2to3_doctests(self): - 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 - ] - else: - self.convert_2to3_doctests = [] - def get_egg_cache_dir(self): egg_cache_dir = os.path.join(os.curdir, '.eggs') if not os.path.exists(egg_cache_dir): @@ -830,10 +835,14 @@ class Distribution(_Distribution): windows_support.hide_file(egg_cache_dir) readme_txt_filename = os.path.join(egg_cache_dir, 'README.txt') with open(readme_txt_filename, 'w') as f: - f.write('This directory contains eggs that were downloaded ' - 'by setuptools to build, test, and run plug-ins.\n\n') - f.write('This directory caches those eggs to prevent ' - 'repeated downloads.\n\n') + f.write( + 'This directory contains eggs that were downloaded ' + 'by setuptools to build, test, and run plug-ins.\n\n' + ) + f.write( + 'This directory caches those eggs to prevent ' + 'repeated downloads.\n\n' + ) f.write('However, it is safe to delete this directory.\n\n') return egg_cache_dir @@ -841,6 +850,7 @@ class Distribution(_Distribution): def fetch_build_egg(self, req): """Fetch an egg needed for building""" from setuptools.installer import fetch_build_egg + return fetch_build_egg(self, req) def get_command_class(self, command): @@ -900,19 +910,18 @@ class Distribution(_Distribution): pfx = package + '.' if self.packages: self.packages = [ - p for p in self.packages - if p != package and not p.startswith(pfx) + p for p in self.packages if p != package and not p.startswith(pfx) ] if self.py_modules: self.py_modules = [ - p for p in self.py_modules - if p != package and not p.startswith(pfx) + p for p in self.py_modules if p != package and not p.startswith(pfx) ] if self.ext_modules: self.ext_modules = [ - p for p in self.ext_modules + p + for p in self.ext_modules if p.name != package and not p.name.startswith(pfx) ] @@ -934,9 +943,7 @@ class Distribution(_Distribution): try: old = getattr(self, name) except AttributeError as e: - raise DistutilsSetupError( - "%s: No such distribution setting" % name - ) from e + raise DistutilsSetupError("%s: No such distribution setting" % name) from e if old is not None and not isinstance(old, sequence): raise DistutilsSetupError( name + ": this setting cannot be changed via include/exclude" @@ -948,15 +955,11 @@ class Distribution(_Distribution): """Handle 'include()' for list/tuple attrs without a special handler""" if not isinstance(value, sequence): - raise DistutilsSetupError( - "%s: setting must be a list (%r)" % (name, value) - ) + raise DistutilsSetupError("%s: setting must be a list (%r)" % (name, value)) try: old = getattr(self, name) except AttributeError as e: - raise DistutilsSetupError( - "%s: No such distribution setting" % name - ) from e + raise DistutilsSetupError("%s: No such distribution setting" % name) from e if old is None: setattr(self, name, value) elif not isinstance(old, sequence): @@ -1009,6 +1012,7 @@ class Distribution(_Distribution): src, alias = aliases[command] del aliases[command] # ensure each alias can expand only once! import shlex + args[:1] = shlex.split(alias, True) command = args[0] @@ -1108,12 +1112,14 @@ class Distribution(_Distribution): line_buffering = sys.stdout.line_buffering sys.stdout = io.TextIOWrapper( - sys.stdout.detach(), 'utf-8', errors, newline, line_buffering) + sys.stdout.detach(), 'utf-8', errors, newline, line_buffering + ) try: return _Distribution.handle_display_options(self, option_order) finally: sys.stdout = io.TextIOWrapper( - sys.stdout.detach(), encoding, errors, newline, line_buffering) + sys.stdout.detach(), encoding, errors, newline, line_buffering + ) class DistDeprecationWarning(SetuptoolsDeprecationWarning): -- cgit v1.2.1 From 2a5fa490ddf541c0d25bade7bb5e8de3e6d6f981 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 6 Sep 2021 11:53:04 -0400 Subject: In Distribution.finalize_options, suppress known removed entry points for a year to avoid issues with older Setuptools. Fixes #2765. --- setuptools/dist.py | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index eec0b27e..f20ba912 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -5,6 +5,7 @@ import io import sys import re import os +import datetime import warnings import numbers import distutils.log @@ -817,10 +818,32 @@ class Distribution(_Distribution): def by_order(hook): return getattr(hook, 'order', 0) - eps = map(lambda e: e.load(), pkg_resources.iter_entry_points(group)) - for ep in sorted(eps, key=by_order): + defined = pkg_resources.iter_entry_points(group) + filtered = self._suppress_removed_finalization_eps(defined) + loaded = map(lambda e: e.load(), filtered) + for ep in sorted(loaded, key=by_order): ep(self) + @staticmethod + def _suppress_removed_finalization_eps(defined): + """ + When removing an entry point, if metadata is loaded + from an older version of Setuptools, that removed + entry point will attempt to be loaded and will fail. + See #2765 for more details. Remove these known + removed entry points for a year to limit the + disruption. + """ + removed = { + '2to3_doctests': datetime.date(2021, 9, 5), + } + duration = datetime.timedelta(days=365) + today = datetime.date.today() + + def suppress(ep): + return ep.name in removed and today - removed[ep.name] < duration + return itertools.filterfalse(suppress, defined) + def _finalize_setup_keywords(self): for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'): value = getattr(self, ep.name, None) -- cgit v1.2.1 From 60413539ba0556b960d37a7979ca9f9a915581c2 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 6 Sep 2021 12:03:38 -0400 Subject: Remove time-based horizon. --- setuptools/dist.py | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index f20ba912..02ebd635 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -5,7 +5,6 @@ import io import sys import re import os -import datetime import warnings import numbers import distutils.log @@ -819,30 +818,24 @@ class Distribution(_Distribution): return getattr(hook, 'order', 0) defined = pkg_resources.iter_entry_points(group) - filtered = self._suppress_removed_finalization_eps(defined) + filtered = itertools.filterfalse(self._removed, defined) loaded = map(lambda e: e.load(), filtered) for ep in sorted(loaded, key=by_order): ep(self) @staticmethod - def _suppress_removed_finalization_eps(defined): + def _removed(ep): """ When removing an entry point, if metadata is loaded from an older version of Setuptools, that removed entry point will attempt to be loaded and will fail. - See #2765 for more details. Remove these known - removed entry points for a year to limit the - disruption. + See #2765 for more details. """ removed = { - '2to3_doctests': datetime.date(2021, 9, 5), + # removed 2021-09-05 + '2to3_doctests', } - duration = datetime.timedelta(days=365) - today = datetime.date.today() - - def suppress(ep): - return ep.name in removed and today - removed[ep.name] < duration - return itertools.filterfalse(suppress, defined) + return ep.name in removed def _finalize_setup_keywords(self): for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'): -- cgit v1.2.1 From 9f75850ec2455718c4c470c16c4c15139deef624 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 6 Sep 2021 13:03:24 -0400 Subject: Fail fast when use_2to3 is supplied. --- setuptools/dist.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 02ebd635..3363495c 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -289,6 +289,10 @@ def assert_bool(dist, attr, value): raise DistutilsSetupError(tmpl.format(attr=attr, value=value)) +def invalid(dist, attr, value): + raise DistutilsSetupError(f"{attr} is invalid.") + + def check_requirements(dist, attr, value): """Verify that install_requires is a valid requirements list""" try: -- cgit v1.2.1 From 646aa958297f858e04c947cc4577dfaaba4c703a Mon Sep 17 00:00:00 2001 From: Andrew Plummer Date: Tue, 7 Sep 2021 22:11:08 +0100 Subject: Do not error if use_2to3 is set to a false value --- setuptools/dist.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 3363495c..3fa30485 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -289,8 +289,11 @@ def assert_bool(dist, attr, value): raise DistutilsSetupError(tmpl.format(attr=attr, value=value)) -def invalid(dist, attr, value): - raise DistutilsSetupError(f"{attr} is invalid.") +def invalid_ignored_if_false(dist, attr, value): + if not value: + warnings.warn("{attr} is ignored") + return + raise DistutilsSetupError(f"{attr} is invalid if it is set to a true value.") def check_requirements(dist, attr, value): -- cgit v1.2.1 From f11b87f894ae8ced8a91f57ac42f0195d7e515f1 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 7 Sep 2021 19:46:39 -0400 Subject: Make warning a DistDeprecationWarning Co-authored-by: Sviatoslav Sydorenko --- setuptools/dist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 3fa30485..5f114d4c 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -291,7 +291,7 @@ def assert_bool(dist, attr, value): def invalid_ignored_if_false(dist, attr, value): if not value: - warnings.warn("{attr} is ignored") + warnings.warn("{attr} is ignored", DistDeprecationWarning) return raise DistutilsSetupError(f"{attr} is invalid if it is set to a true value.") -- cgit v1.2.1 From 00c0ef57a1aee9cd35623936e756292a21e8d684 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 7 Sep 2021 20:04:18 -0400 Subject: Rename to 'ignore_unless_false'. --- setuptools/dist.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 5f114d4c..8e2111a5 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -289,11 +289,11 @@ def assert_bool(dist, attr, value): raise DistutilsSetupError(tmpl.format(attr=attr, value=value)) -def invalid_ignored_if_false(dist, attr, value): +def invalid_unless_false(dist, attr, value): if not value: - warnings.warn("{attr} is ignored", DistDeprecationWarning) + warnings.warn(f"{attr} is ignored.", DistDeprecationWarning) return - raise DistutilsSetupError(f"{attr} is invalid if it is set to a true value.") + raise DistutilsSetupError(f"{attr} is invalid.") def check_requirements(dist, attr, value): -- cgit v1.2.1 From f8fe4a873c5d3d89a939c0576e1c14a8e82c73c8 Mon Sep 17 00:00:00 2001 From: Sviatoslav Sydorenko Date: Thu, 11 Nov 2021 03:43:03 +0100 Subject: Fail on a multiline distribution package summary --- setuptools/dist.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index 8e2111a5..e61733d8 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -144,12 +144,14 @@ def read_pkg_file(self, file): self.license_files = _read_list_from_msg(msg, 'license-file') -def single_line(val): - # quick and dirty validation for description pypa/setuptools#1390 +def ensure_summary_single_line(val): + """Validate that the summary does not have line breaks.""" + # Ref: https://github.com/pypa/setuptools/issues/1390 if '\n' in val: - # TODO after 2021-07-31: Replace with `raise ValueError("newlines not allowed")` - warnings.warn("newlines not allowed and will break in the future") - val = val.replace('\n', ' ') + raise ValueError( + 'Newlines in the package distribution summary are not allowed', + ) + return val @@ -164,7 +166,7 @@ def write_pkg_file(self, file): # noqa: C901 # is too complex (14) # FIXME write_field('Metadata-Version', str(version)) write_field('Name', self.get_name()) write_field('Version', self.get_version()) - write_field('Summary', single_line(self.get_description())) + write_field('Summary', ensure_summary_single_line(self.get_description())) write_field('Home-page', self.get_url()) optional_fields = ( -- cgit v1.2.1 From a4b7caeaa653116b60a5231e9019ab250293c331 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 12 Nov 2021 19:01:55 -0500 Subject: Restore single_line as a simple, universal validator. --- setuptools/dist.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'setuptools/dist.py') diff --git a/setuptools/dist.py b/setuptools/dist.py index e61733d8..848d6b0f 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -144,13 +144,11 @@ def read_pkg_file(self, file): self.license_files = _read_list_from_msg(msg, 'license-file') -def ensure_summary_single_line(val): - """Validate that the summary does not have line breaks.""" +def single_line(val): + """Validate that the value does not have line breaks.""" # Ref: https://github.com/pypa/setuptools/issues/1390 if '\n' in val: - raise ValueError( - 'Newlines in the package distribution summary are not allowed', - ) + raise ValueError('Newlines are not allowed') return val @@ -166,7 +164,7 @@ def write_pkg_file(self, file): # noqa: C901 # is too complex (14) # FIXME write_field('Metadata-Version', str(version)) write_field('Name', self.get_name()) write_field('Version', self.get_version()) - write_field('Summary', ensure_summary_single_line(self.get_description())) + write_field('Summary', single_line(self.get_description())) write_field('Home-page', self.get_url()) optional_fields = ( -- cgit v1.2.1