Index: third_party/google-endpoints/setuptools/config.py |
diff --git a/third_party/google-endpoints/setuptools/config.py b/third_party/google-endpoints/setuptools/config.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..19b39629c707a6238a756cd7c79b0b7c1236bde9 |
--- /dev/null |
+++ b/third_party/google-endpoints/setuptools/config.py |
@@ -0,0 +1,558 @@ |
+from __future__ import absolute_import, unicode_literals |
+import io |
+import os |
+import sys |
+from collections import defaultdict |
+from functools import partial |
+ |
+from distutils.errors import DistutilsOptionError, DistutilsFileError |
+from setuptools.py26compat import import_module |
+from six import string_types |
+ |
+ |
+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 |
+ to get options from. |
+ |
+ :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 |
+ |
+ filepath = os.path.abspath(filepath) |
+ |
+ if not os.path.isfile(filepath): |
+ raise DistutilsFileError( |
+ 'Configuration file %s does not exist.' % filepath) |
+ |
+ current_directory = os.getcwd() |
+ os.chdir(os.path.dirname(filepath)) |
+ |
+ try: |
+ dist = Distribution() |
+ |
+ filenames = dist.find_config_files() if find_others else [] |
+ if filepath not in filenames: |
+ filenames.append(filepath) |
+ |
+ _Distribution.parse_config_files(dist, filenames=filenames) |
+ |
+ handlers = parse_configuration( |
+ dist, dist.command_options, |
+ ignore_option_errors=ignore_option_errors) |
+ |
+ finally: |
+ os.chdir(current_directory) |
+ |
+ return configuration_to_dict(handlers) |
+ |
+ |
+def configuration_to_dict(handlers): |
+ """Returns configuration data gathered by given handlers as a dict. |
+ |
+ :param list[ConfigHandler] handlers: Handlers list, |
+ usually from parse_configuration() |
+ |
+ :rtype: dict |
+ """ |
+ config_dict = defaultdict(dict) |
+ |
+ for handler in handlers: |
+ |
+ obj_alias = handler.section_prefix |
+ target_obj = handler.target_obj |
+ |
+ for option in handler.set_options: |
+ getter = getattr(target_obj, 'get_%s' % option, None) |
+ |
+ if getter is None: |
+ value = getattr(target_obj, option) |
+ |
+ else: |
+ value = getter() |
+ |
+ config_dict[obj_alias][option] = value |
+ |
+ return config_dict |
+ |
+ |
+def parse_configuration( |
+ distribution, command_options, ignore_option_errors=False): |
+ """Performs additional parsing of configuration options |
+ for a distribution. |
+ |
+ Returns a list of used option handlers. |
+ |
+ :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, ignore_option_errors) |
+ meta.parse() |
+ |
+ options = ConfigOptionsHandler( |
+ distribution, command_options, ignore_option_errors) |
+ options.parse() |
+ |
+ return [meta, options] |
+ |
+ |
+class ConfigHandler(object): |
+ """Handles metadata supplied in configuration files.""" |
+ |
+ section_prefix = None |
+ """Prefix for config sections handled by this handler. |
+ Must be provided by class heirs. |
+ |
+ """ |
+ |
+ aliases = {} |
+ """Options aliases. |
+ For compatibility with various packages. E.g.: d2to1 and pbr. |
+ Note: `-` in keys is replaced with `_` by config parser. |
+ |
+ """ |
+ |
+ def __init__(self, target_obj, options, ignore_option_errors=False): |
+ sections = {} |
+ |
+ section_prefix = self.section_prefix |
+ for section_name, section_options in options.items(): |
+ if not section_name.startswith(section_prefix): |
+ continue |
+ |
+ 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 = [] |
+ |
+ @property |
+ def parsers(self): |
+ """Metadata item name to parser function mapping.""" |
+ raise NotImplementedError( |
+ '%s must provide .parsers property' % self.__class__.__name__) |
+ |
+ def __setitem__(self, option_name, value): |
+ unknown = tuple() |
+ target_obj = self.target_obj |
+ |
+ # Translate alias into real name. |
+ option_name = self.aliases.get(option_name, option_name) |
+ |
+ current_value = getattr(target_obj, option_name, unknown) |
+ |
+ if current_value is unknown: |
+ raise KeyError(option_name) |
+ |
+ if current_value: |
+ # Already inhabited. Skipping. |
+ return |
+ |
+ skip_option = False |
+ parser = self.parsers.get(option_name) |
+ if parser: |
+ 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: |
+ setattr(target_obj, option_name, value) |
+ else: |
+ setter(value) |
+ |
+ self.set_options.append(option_name) |
+ |
+ @classmethod |
+ def _parse_list(cls, value, separator=','): |
+ """Represents value as a list. |
+ |
+ Value is split either by separator (defaults to comma) or by lines. |
+ |
+ :param value: |
+ :param separator: List items separator character. |
+ :rtype: list |
+ """ |
+ if isinstance(value, list): # _get_parser_compound case |
+ return value |
+ |
+ if '\n' in value: |
+ value = value.splitlines() |
+ else: |
+ value = value.split(separator) |
+ |
+ return [chunk.strip() for chunk in value if chunk.strip()] |
+ |
+ @classmethod |
+ def _parse_dict(cls, value): |
+ """Represents value as a dict. |
+ |
+ :param value: |
+ :rtype: dict |
+ """ |
+ separator = '=' |
+ result = {} |
+ for line in cls._parse_list(value): |
+ key, sep, val = line.partition(separator) |
+ if sep != separator: |
+ raise DistutilsOptionError( |
+ 'Unable to parse option value to dict: %s' % value) |
+ result[key.strip()] = val.strip() |
+ |
+ return result |
+ |
+ @classmethod |
+ def _parse_bool(cls, value): |
+ """Represents value as boolean. |
+ |
+ :param value: |
+ :rtype: bool |
+ """ |
+ value = value.lower() |
+ return value in ('1', 'true', 'yes') |
+ |
+ @classmethod |
+ def _parse_file(cls, value): |
+ """Represents value as a string, allowing including text |
+ from nearest files using `file:` directive. |
+ |
+ Directive is sandboxed and won't reach anything outside |
+ directory with setup.py. |
+ |
+ Examples: |
+ include: LICENSE |
+ include: src/file.txt |
+ |
+ :param str value: |
+ :rtype: str |
+ """ |
+ 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): |
+ 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 |
+ |
+ @classmethod |
+ def _parse_attr(cls, value): |
+ """Represents value as a module attribute. |
+ |
+ Examples: |
+ attr: package.attr |
+ attr: package.module.attr |
+ |
+ :param str value: |
+ :rtype: str |
+ """ |
+ attr_directive = 'attr:' |
+ if not value.startswith(attr_directive): |
+ return value |
+ |
+ attrs_path = value.replace(attr_directive, '').strip().split('.') |
+ attr_name = attrs_path.pop() |
+ |
+ module_name = '.'.join(attrs_path) |
+ module_name = module_name or '__init__' |
+ |
+ sys.path.insert(0, os.getcwd()) |
+ try: |
+ module = import_module(module_name) |
+ value = getattr(module, attr_name) |
+ |
+ finally: |
+ sys.path = sys.path[1:] |
+ |
+ return value |
+ |
+ @classmethod |
+ def _get_parser_compound(cls, *parse_methods): |
+ """Returns parser function to represents value as a list. |
+ |
+ Parses a value applying given methods one after another. |
+ |
+ :param parse_methods: |
+ :rtype: callable |
+ """ |
+ def parse(value): |
+ parsed = value |
+ |
+ for method in parse_methods: |
+ parsed = method(parsed) |
+ |
+ return parsed |
+ |
+ return parse |
+ |
+ @classmethod |
+ def _parse_section_to_dict(cls, section_options, values_parser=None): |
+ """Parses section options into a dictionary. |
+ |
+ Optionally applies a given parser to values. |
+ |
+ :param dict section_options: |
+ :param callable values_parser: |
+ :rtype: dict |
+ """ |
+ value = {} |
+ values_parser = values_parser or (lambda val: val) |
+ for key, (_, val) in section_options.items(): |
+ value[key] = values_parser(val) |
+ return value |
+ |
+ def parse_section(self, section_options): |
+ """Parses configuration file section. |
+ |
+ :param dict section_options: |
+ """ |
+ for (name, (_, value)) in section_options.items(): |
+ try: |
+ self[name] = value |
+ |
+ except KeyError: |
+ pass # Keep silent for a new option may appear anytime. |
+ |
+ def parse(self): |
+ """Parses configuration file items from one |
+ or more related sections. |
+ |
+ """ |
+ for section_name, section_options in self.sections.items(): |
+ |
+ method_postfix = '' |
+ if section_name: # [section.option] variant |
+ method_postfix = '_%s' % section_name |
+ |
+ section_parser_method = getattr( |
+ self, |
+ # Dots in section names are tranlsated into dunderscores. |
+ ('parse_section%s' % method_postfix).replace('.', '__'), |
+ None) |
+ |
+ if section_parser_method is None: |
+ raise DistutilsOptionError( |
+ 'Unsupported distribution option section: [%s.%s]' % ( |
+ self.section_prefix, section_name)) |
+ |
+ section_parser_method(section_options) |
+ |
+ |
+class ConfigMetadataHandler(ConfigHandler): |
+ |
+ section_prefix = 'metadata' |
+ |
+ aliases = { |
+ 'home_page': 'url', |
+ 'summary': 'description', |
+ 'classifier': 'classifiers', |
+ 'platform': 'platforms', |
+ } |
+ |
+ strict_mode = False |
+ """We need to keep it loose, to be partially compatible with |
+ `pbr` and `d2to1` packages which also uses `metadata` section. |
+ |
+ """ |
+ |
+ @property |
+ def parsers(self): |
+ """Metadata item name to parser function mapping.""" |
+ parse_list = self._parse_list |
+ parse_file = self._parse_file |
+ |
+ return { |
+ 'platforms': parse_list, |
+ 'keywords': parse_list, |
+ 'provides': parse_list, |
+ 'requires': parse_list, |
+ 'obsoletes': parse_list, |
+ 'classifiers': self._get_parser_compound(parse_file, parse_list), |
+ 'license': parse_file, |
+ 'description': parse_file, |
+ 'long_description': parse_file, |
+ '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. |
+ |
+ :param value: |
+ :rtype: str |
+ |
+ """ |
+ version = self._parse_attr(value) |
+ |
+ if callable(version): |
+ version = version() |
+ |
+ if not isinstance(version, string_types): |
+ if hasattr(version, '__iter__'): |
+ version = '.'.join(map(str, version)) |
+ else: |
+ version = '%s' % version |
+ |
+ return version |
+ |
+ |
+class ConfigOptionsHandler(ConfigHandler): |
+ |
+ section_prefix = 'options' |
+ |
+ @property |
+ def parsers(self): |
+ """Metadata item name to parser function mapping.""" |
+ parse_list = self._parse_list |
+ parse_list_semicolon = partial(self._parse_list, separator=';') |
+ parse_bool = self._parse_bool |
+ parse_dict = self._parse_dict |
+ |
+ return { |
+ 'zip_safe': parse_bool, |
+ 'use_2to3': parse_bool, |
+ 'include_package_data': parse_bool, |
+ 'package_dir': parse_dict, |
+ 'use_2to3_fixers': parse_list, |
+ 'use_2to3_exclude_fixers': parse_list, |
+ 'convert_2to3_doctests': parse_list, |
+ 'scripts': parse_list, |
+ 'eager_resources': parse_list, |
+ 'dependency_links': parse_list, |
+ 'namespace_packages': parse_list, |
+ 'install_requires': parse_list_semicolon, |
+ 'setup_requires': parse_list_semicolon, |
+ 'tests_require': parse_list_semicolon, |
+ 'packages': self._parse_packages, |
+ 'entry_points': self._parse_file, |
+ } |
+ |
+ def _parse_packages(self, value): |
+ """Parses `packages` option value. |
+ |
+ :param value: |
+ :rtype: list |
+ """ |
+ find_directive = 'find:' |
+ |
+ 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(**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. |
+ |
+ :param dict section_options: |
+ """ |
+ parsed = self._parse_section_to_dict(section_options, self._parse_list) |
+ self['entry_points'] = parsed |
+ |
+ def _parse_package_data(self, section_options): |
+ parsed = self._parse_section_to_dict(section_options, self._parse_list) |
+ |
+ root = parsed.get('*') |
+ if root: |
+ parsed[''] = root |
+ del parsed['*'] |
+ |
+ return parsed |
+ |
+ def parse_section_package_data(self, section_options): |
+ """Parses `package_data` configuration file section. |
+ |
+ :param dict section_options: |
+ """ |
+ self['package_data'] = self._parse_package_data(section_options) |
+ |
+ def parse_section_exclude_package_data(self, section_options): |
+ """Parses `exclude_package_data` configuration file section. |
+ |
+ :param dict section_options: |
+ """ |
+ self['exclude_package_data'] = self._parse_package_data( |
+ section_options) |
+ |
+ def parse_section_extras_require(self, section_options): |
+ """Parses `extras_require` configuration file section. |
+ |
+ :param dict section_options: |
+ """ |
+ parse_list = partial(self._parse_list, separator=';') |
+ self['extras_require'] = self._parse_section_to_dict( |
+ section_options, parse_list) |