aboutsummaryrefslogtreecommitdiffstats
path: root/setuptools/config.py
diff options
context:
space:
mode:
authorJason R. Coombs <jaraco@jaraco.com>2017-09-03 19:57:54 -0400
committerJason R. Coombs <jaraco@jaraco.com>2017-09-03 20:01:45 -0400
commitdcb24ad15465c266a3f258471766fbbe8fc8a42e (patch)
tree13123440610d78e398476a8ce1e8cc3d9f9ec72e /setuptools/config.py
parentf14930e66601b462699c44384c482cd966f53b8f (diff)
parent1b192005562d5cf0de30c02154c58fd1dca577c8 (diff)
downloadexternal_python_setuptools-dcb24ad15465c266a3f258471766fbbe8fc8a42e.tar.gz
external_python_setuptools-dcb24ad15465c266a3f258471766fbbe8fc8a42e.tar.bz2
external_python_setuptools-dcb24ad15465c266a3f258471766fbbe8fc8a42e.zip
Merge branch 'master' into drop-py26
Diffstat (limited to 'setuptools/config.py')
-rw-r--r--setuptools/config.py133
1 files changed, 93 insertions, 40 deletions
diff --git a/setuptools/config.py b/setuptools/config.py
index eb19c895..53828447 100644
--- a/setuptools/config.py
+++ b/setuptools/config.py
@@ -10,7 +10,8 @@ from distutils.errors import DistutilsOptionError, DistutilsFileError
from setuptools.extern.six import string_types
-def read_configuration(filepath, find_others=False):
+def read_configuration(
+ filepath, find_others=False, ignore_option_errors=False):
"""Read given configuration file and returns options from it as a dict.
:param str|unicode filepath: Path to configuration file
@@ -19,6 +20,11 @@ def read_configuration(filepath, find_others=False):
:param bool find_others: Whether to search for other configuration files
which could be on in various places.
+ :param bool ignore_option_errors: Whether to silently ignore
+ options, values of which could not be resolved (e.g. due to exceptions
+ in directives such as file:, attr:, etc.).
+ If False exceptions are propagated as expected.
+
:rtype: dict
"""
from setuptools.dist import Distribution, _Distribution
@@ -32,17 +38,21 @@ def read_configuration(filepath, find_others=False):
current_directory = os.getcwd()
os.chdir(os.path.dirname(filepath))
- dist = Distribution()
+ try:
+ dist = Distribution()
- filenames = dist.find_config_files() if find_others else []
- if filepath not in filenames:
- filenames.append(filepath)
+ filenames = dist.find_config_files() if find_others else []
+ if filepath not in filenames:
+ filenames.append(filepath)
- _Distribution.parse_config_files(dist, filenames=filenames)
+ _Distribution.parse_config_files(dist, filenames=filenames)
- handlers = parse_configuration(dist, dist.command_options)
+ handlers = parse_configuration(
+ dist, dist.command_options,
+ ignore_option_errors=ignore_option_errors)
- os.chdir(current_directory)
+ finally:
+ os.chdir(current_directory)
return configuration_to_dict(handlers)
@@ -76,7 +86,8 @@ def configuration_to_dict(handlers):
return config_dict
-def parse_configuration(distribution, command_options):
+def parse_configuration(
+ distribution, command_options, ignore_option_errors=False):
"""Performs additional parsing of configuration options
for a distribution.
@@ -84,12 +95,18 @@ def parse_configuration(distribution, command_options):
:param Distribution distribution:
:param dict command_options:
+ :param bool ignore_option_errors: Whether to silently ignore
+ options, values of which could not be resolved (e.g. due to exceptions
+ in directives such as file:, attr:, etc.).
+ If False exceptions are propagated as expected.
:rtype: list
"""
- meta = ConfigMetadataHandler(distribution.metadata, command_options)
+ meta = ConfigMetadataHandler(
+ distribution.metadata, command_options, ignore_option_errors)
meta.parse()
- options = ConfigOptionsHandler(distribution, command_options)
+ options = ConfigOptionsHandler(
+ distribution, command_options, ignore_option_errors)
options.parse()
return [meta, options]
@@ -111,7 +128,7 @@ class ConfigHandler(object):
"""
- def __init__(self, target_obj, options):
+ def __init__(self, target_obj, options, ignore_option_errors=False):
sections = {}
section_prefix = self.section_prefix
@@ -122,6 +139,7 @@ class ConfigHandler(object):
section_name = section_name.replace(section_prefix, '').strip('.')
sections[section_name] = section_options
+ self.ignore_option_errors = ignore_option_errors
self.target_obj = target_obj
self.sections = sections
self.set_options = []
@@ -148,9 +166,19 @@ class ConfigHandler(object):
# Already inhabited. Skipping.
return
+ skip_option = False
parser = self.parsers.get(option_name)
if parser:
- value = parser(value)
+ try:
+ value = parser(value)
+
+ except Exception:
+ skip_option = True
+ if not self.ignore_option_errors:
+ raise
+
+ if skip_option:
+ return
setter = getattr(target_obj, 'set_%s' % option_name, None)
if setter is None:
@@ -217,33 +245,39 @@ class ConfigHandler(object):
directory with setup.py.
Examples:
- include: LICENSE
- include: src/file.txt
+ file: LICENSE
+ file: README.rst, CHANGELOG.md, src/file.txt
:param str value:
:rtype: str
"""
+ include_directive = 'file:'
+
if not isinstance(value, string_types):
return value
- include_directive = 'file:'
if not value.startswith(include_directive):
return value
- current_directory = os.getcwd()
-
- filepath = value.replace(include_directive, '').strip()
- filepath = os.path.abspath(filepath)
-
- if not filepath.startswith(current_directory):
+ spec = value[len(include_directive):]
+ filepaths = (os.path.abspath(path.strip()) for path in spec.split(','))
+ return '\n'.join(
+ cls._read_file(path)
+ for path in filepaths
+ if (cls._assert_local(path) or True)
+ and os.path.isfile(path)
+ )
+
+ @staticmethod
+ def _assert_local(filepath):
+ if not filepath.startswith(os.getcwd()):
raise DistutilsOptionError(
'`file:` directive can not access %s' % filepath)
- if os.path.isfile(filepath):
- with io.open(filepath, encoding='utf-8') as f:
- value = f.read()
-
- return value
+ @staticmethod
+ def _read_file(filepath):
+ with io.open(filepath, encoding='utf-8') as f:
+ return f.read()
@classmethod
def _parse_attr(cls, value):
@@ -335,7 +369,10 @@ class ConfigHandler(object):
method_postfix = '_%s' % section_name
section_parser_method = getattr(
- self, 'parse_section%s' % method_postfix, None)
+ self,
+ # Dots in section names are tranlsated into dunderscores.
+ ('parse_section%s' % method_postfix).replace('.', '__'),
+ None)
if section_parser_method is None:
raise DistutilsOptionError(
@@ -381,17 +418,6 @@ class ConfigMetadataHandler(ConfigHandler):
'version': self._parse_version,
}
- def parse_section_classifiers(self, section_options):
- """Parses configuration file section.
-
- :param dict section_options:
- """
- classifiers = []
- for begin, (_, rest) in section_options.items():
- classifiers.append('%s :%s' % (begin.title(), rest))
-
- self['classifiers'] = classifiers
-
def _parse_version(self, value):
"""Parses `version` option value.
@@ -442,6 +468,7 @@ class ConfigOptionsHandler(ConfigHandler):
'tests_require': parse_list_semicolon,
'packages': self._parse_packages,
'entry_points': self._parse_file,
+ 'py_modules': parse_list,
}
def _parse_packages(self, value):
@@ -455,8 +482,34 @@ class ConfigOptionsHandler(ConfigHandler):
if not value.startswith(find_directive):
return self._parse_list(value)
+ # Read function arguments from a dedicated section.
+ find_kwargs = self.parse_section_packages__find(
+ self.sections.get('packages.find', {}))
+
from setuptools import find_packages
- return find_packages()
+
+ return find_packages(**find_kwargs)
+
+ def parse_section_packages__find(self, section_options):
+ """Parses `packages.find` configuration file section.
+
+ To be used in conjunction with _parse_packages().
+
+ :param dict section_options:
+ """
+ section_data = self._parse_section_to_dict(
+ section_options, self._parse_list)
+
+ valid_keys = ['where', 'include', 'exclude']
+
+ find_kwargs = dict(
+ [(k, v) for k, v in section_data.items() if k in valid_keys and v])
+
+ where = find_kwargs.get('where')
+ if where is not None:
+ find_kwargs['where'] = where[0] # cast list to single val
+
+ return find_kwargs
def parse_section_entry_points(self, section_options):
"""Parses `entry_points` configuration file section.