Path: blob/master/ invest-robot-contest_TinkoffBotTwitch-main/venv/lib/python3.8/site-packages/setuptools/config.py
7763 views
from __future__ import absolute_import, unicode_literals1import ast2import io3import os4import sys56import warnings7import functools8import importlib9from collections import defaultdict10from functools import partial11from functools import wraps12import contextlib1314from distutils.errors import DistutilsOptionError, DistutilsFileError15from setuptools.extern.packaging.version import LegacyVersion, parse16from setuptools.extern.packaging.specifiers import SpecifierSet17from setuptools.extern.six import string_types, PY3181920__metaclass__ = type212223class StaticModule:24"""25Attempt to load the module by the name26"""27def __init__(self, name):28spec = importlib.util.find_spec(name)29with open(spec.origin) as strm:30src = strm.read()31module = ast.parse(src)32vars(self).update(locals())33del self.self3435def __getattr__(self, attr):36try:37return next(38ast.literal_eval(statement.value)39for statement in self.module.body40if isinstance(statement, ast.Assign)41for target in statement.targets42if isinstance(target, ast.Name) and target.id == attr43)44except Exception as e:45raise AttributeError(46"{self.name} has no attribute {attr}".format(**locals())47) from e484950@contextlib.contextmanager51def patch_path(path):52"""53Add path to front of sys.path for the duration of the context.54"""55try:56sys.path.insert(0, path)57yield58finally:59sys.path.remove(path)606162def read_configuration(63filepath, find_others=False, ignore_option_errors=False):64"""Read given configuration file and returns options from it as a dict.6566:param str|unicode filepath: Path to configuration file67to get options from.6869:param bool find_others: Whether to search for other configuration files70which could be on in various places.7172:param bool ignore_option_errors: Whether to silently ignore73options, values of which could not be resolved (e.g. due to exceptions74in directives such as file:, attr:, etc.).75If False exceptions are propagated as expected.7677:rtype: dict78"""79from setuptools.dist import Distribution, _Distribution8081filepath = os.path.abspath(filepath)8283if not os.path.isfile(filepath):84raise DistutilsFileError(85'Configuration file %s does not exist.' % filepath)8687current_directory = os.getcwd()88os.chdir(os.path.dirname(filepath))8990try:91dist = Distribution()9293filenames = dist.find_config_files() if find_others else []94if filepath not in filenames:95filenames.append(filepath)9697_Distribution.parse_config_files(dist, filenames=filenames)9899handlers = parse_configuration(100dist, dist.command_options,101ignore_option_errors=ignore_option_errors)102103finally:104os.chdir(current_directory)105106return configuration_to_dict(handlers)107108109def _get_option(target_obj, key):110"""111Given a target object and option key, get that option from112the target object, either through a get_{key} method or113from an attribute directly.114"""115getter_name = 'get_{key}'.format(**locals())116by_attribute = functools.partial(getattr, target_obj, key)117getter = getattr(target_obj, getter_name, by_attribute)118return getter()119120121def configuration_to_dict(handlers):122"""Returns configuration data gathered by given handlers as a dict.123124:param list[ConfigHandler] handlers: Handlers list,125usually from parse_configuration()126127:rtype: dict128"""129config_dict = defaultdict(dict)130131for handler in handlers:132for option in handler.set_options:133value = _get_option(handler.target_obj, option)134config_dict[handler.section_prefix][option] = value135136return config_dict137138139def parse_configuration(140distribution, command_options, ignore_option_errors=False):141"""Performs additional parsing of configuration options142for a distribution.143144Returns a list of used option handlers.145146:param Distribution distribution:147:param dict command_options:148:param bool ignore_option_errors: Whether to silently ignore149options, values of which could not be resolved (e.g. due to exceptions150in directives such as file:, attr:, etc.).151If False exceptions are propagated as expected.152:rtype: list153"""154options = ConfigOptionsHandler(155distribution, command_options, ignore_option_errors)156options.parse()157158meta = ConfigMetadataHandler(159distribution.metadata, command_options, ignore_option_errors,160distribution.package_dir)161meta.parse()162163return meta, options164165166class ConfigHandler:167"""Handles metadata supplied in configuration files."""168169section_prefix = None170"""Prefix for config sections handled by this handler.171Must be provided by class heirs.172173"""174175aliases = {}176"""Options aliases.177For compatibility with various packages. E.g.: d2to1 and pbr.178Note: `-` in keys is replaced with `_` by config parser.179180"""181182def __init__(self, target_obj, options, ignore_option_errors=False):183sections = {}184185section_prefix = self.section_prefix186for section_name, section_options in options.items():187if not section_name.startswith(section_prefix):188continue189190section_name = section_name.replace(section_prefix, '').strip('.')191sections[section_name] = section_options192193self.ignore_option_errors = ignore_option_errors194self.target_obj = target_obj195self.sections = sections196self.set_options = []197198@property199def parsers(self):200"""Metadata item name to parser function mapping."""201raise NotImplementedError(202'%s must provide .parsers property' % self.__class__.__name__)203204def __setitem__(self, option_name, value):205unknown = tuple()206target_obj = self.target_obj207208# Translate alias into real name.209option_name = self.aliases.get(option_name, option_name)210211current_value = getattr(target_obj, option_name, unknown)212213if current_value is unknown:214raise KeyError(option_name)215216if current_value:217# Already inhabited. Skipping.218return219220skip_option = False221parser = self.parsers.get(option_name)222if parser:223try:224value = parser(value)225226except Exception:227skip_option = True228if not self.ignore_option_errors:229raise230231if skip_option:232return233234setter = getattr(target_obj, 'set_%s' % option_name, None)235if setter is None:236setattr(target_obj, option_name, value)237else:238setter(value)239240self.set_options.append(option_name)241242@classmethod243def _parse_list(cls, value, separator=','):244"""Represents value as a list.245246Value is split either by separator (defaults to comma) or by lines.247248:param value:249:param separator: List items separator character.250:rtype: list251"""252if isinstance(value, list): # _get_parser_compound case253return value254255if '\n' in value:256value = value.splitlines()257else:258value = value.split(separator)259260return [chunk.strip() for chunk in value if chunk.strip()]261262@classmethod263def _parse_dict(cls, value):264"""Represents value as a dict.265266:param value:267:rtype: dict268"""269separator = '='270result = {}271for line in cls._parse_list(value):272key, sep, val = line.partition(separator)273if sep != separator:274raise DistutilsOptionError(275'Unable to parse option value to dict: %s' % value)276result[key.strip()] = val.strip()277278return result279280@classmethod281def _parse_bool(cls, value):282"""Represents value as boolean.283284:param value:285:rtype: bool286"""287value = value.lower()288return value in ('1', 'true', 'yes')289290@classmethod291def _exclude_files_parser(cls, key):292"""Returns a parser function to make sure field inputs293are not files.294295Parses a value after getting the key so error messages are296more informative.297298:param key:299:rtype: callable300"""301def parser(value):302exclude_directive = 'file:'303if value.startswith(exclude_directive):304raise ValueError(305'Only strings are accepted for the {0} field, '306'files are not accepted'.format(key))307return value308return parser309310@classmethod311def _parse_file(cls, value):312"""Represents value as a string, allowing including text313from nearest files using `file:` directive.314315Directive is sandboxed and won't reach anything outside316directory with setup.py.317318Examples:319file: README.rst, CHANGELOG.md, src/file.txt320321:param str value:322:rtype: str323"""324include_directive = 'file:'325326if not isinstance(value, string_types):327return value328329if not value.startswith(include_directive):330return value331332spec = value[len(include_directive):]333filepaths = (os.path.abspath(path.strip()) for path in spec.split(','))334return '\n'.join(335cls._read_file(path)336for path in filepaths337if (cls._assert_local(path) or True)338and os.path.isfile(path)339)340341@staticmethod342def _assert_local(filepath):343if not filepath.startswith(os.getcwd()):344raise DistutilsOptionError(345'`file:` directive can not access %s' % filepath)346347@staticmethod348def _read_file(filepath):349with io.open(filepath, encoding='utf-8') as f:350return f.read()351352@classmethod353def _parse_attr(cls, value, package_dir=None):354"""Represents value as a module attribute.355356Examples:357attr: package.attr358attr: package.module.attr359360:param str value:361:rtype: str362"""363attr_directive = 'attr:'364if not value.startswith(attr_directive):365return value366367attrs_path = value.replace(attr_directive, '').strip().split('.')368attr_name = attrs_path.pop()369370module_name = '.'.join(attrs_path)371module_name = module_name or '__init__'372373parent_path = os.getcwd()374if package_dir:375if attrs_path[0] in package_dir:376# A custom path was specified for the module we want to import377custom_path = package_dir[attrs_path[0]]378parts = custom_path.rsplit('/', 1)379if len(parts) > 1:380parent_path = os.path.join(os.getcwd(), parts[0])381module_name = parts[1]382else:383module_name = custom_path384elif '' in package_dir:385# A custom parent directory was specified for all root modules386parent_path = os.path.join(os.getcwd(), package_dir[''])387388with patch_path(parent_path):389try:390# attempt to load value statically391return getattr(StaticModule(module_name), attr_name)392except Exception:393# fallback to simple import394module = importlib.import_module(module_name)395396return getattr(module, attr_name)397398@classmethod399def _get_parser_compound(cls, *parse_methods):400"""Returns parser function to represents value as a list.401402Parses a value applying given methods one after another.403404:param parse_methods:405:rtype: callable406"""407def parse(value):408parsed = value409410for method in parse_methods:411parsed = method(parsed)412413return parsed414415return parse416417@classmethod418def _parse_section_to_dict(cls, section_options, values_parser=None):419"""Parses section options into a dictionary.420421Optionally applies a given parser to values.422423:param dict section_options:424:param callable values_parser:425:rtype: dict426"""427value = {}428values_parser = values_parser or (lambda val: val)429for key, (_, val) in section_options.items():430value[key] = values_parser(val)431return value432433def parse_section(self, section_options):434"""Parses configuration file section.435436:param dict section_options:437"""438for (name, (_, value)) in section_options.items():439try:440self[name] = value441442except KeyError:443pass # Keep silent for a new option may appear anytime.444445def parse(self):446"""Parses configuration file items from one447or more related sections.448449"""450for section_name, section_options in self.sections.items():451452method_postfix = ''453if section_name: # [section.option] variant454method_postfix = '_%s' % section_name455456section_parser_method = getattr(457self,458# Dots in section names are translated into dunderscores.459('parse_section%s' % method_postfix).replace('.', '__'),460None)461462if section_parser_method is None:463raise DistutilsOptionError(464'Unsupported distribution option section: [%s.%s]' % (465self.section_prefix, section_name))466467section_parser_method(section_options)468469def _deprecated_config_handler(self, func, msg, warning_class):470""" this function will wrap around parameters that are deprecated471472:param msg: deprecation message473:param warning_class: class of warning exception to be raised474:param func: function to be wrapped around475"""476@wraps(func)477def config_handler(*args, **kwargs):478warnings.warn(msg, warning_class)479return func(*args, **kwargs)480481return config_handler482483484class ConfigMetadataHandler(ConfigHandler):485486section_prefix = 'metadata'487488aliases = {489'home_page': 'url',490'summary': 'description',491'classifier': 'classifiers',492'platform': 'platforms',493}494495strict_mode = False496"""We need to keep it loose, to be partially compatible with497`pbr` and `d2to1` packages which also uses `metadata` section.498499"""500501def __init__(self, target_obj, options, ignore_option_errors=False,502package_dir=None):503super(ConfigMetadataHandler, self).__init__(target_obj, options,504ignore_option_errors)505self.package_dir = package_dir506507@property508def parsers(self):509"""Metadata item name to parser function mapping."""510parse_list = self._parse_list511parse_file = self._parse_file512parse_dict = self._parse_dict513exclude_files_parser = self._exclude_files_parser514515return {516'platforms': parse_list,517'keywords': parse_list,518'provides': parse_list,519'requires': self._deprecated_config_handler(520parse_list,521"The requires parameter is deprecated, please use "522"install_requires for runtime dependencies.",523DeprecationWarning),524'obsoletes': parse_list,525'classifiers': self._get_parser_compound(parse_file, parse_list),526'license': exclude_files_parser('license'),527'license_files': parse_list,528'description': parse_file,529'long_description': parse_file,530'version': self._parse_version,531'project_urls': parse_dict,532}533534def _parse_version(self, value):535"""Parses `version` option value.536537:param value:538:rtype: str539540"""541version = self._parse_file(value)542543if version != value:544version = version.strip()545# Be strict about versions loaded from file because it's easy to546# accidentally include newlines and other unintended content547if isinstance(parse(version), LegacyVersion):548tmpl = (549'Version loaded from {value} does not '550'comply with PEP 440: {version}'551)552raise DistutilsOptionError(tmpl.format(**locals()))553554return version555556version = self._parse_attr(value, self.package_dir)557558if callable(version):559version = version()560561if not isinstance(version, string_types):562if hasattr(version, '__iter__'):563version = '.'.join(map(str, version))564else:565version = '%s' % version566567return version568569570class ConfigOptionsHandler(ConfigHandler):571572section_prefix = 'options'573574@property575def parsers(self):576"""Metadata item name to parser function mapping."""577parse_list = self._parse_list578parse_list_semicolon = partial(self._parse_list, separator=';')579parse_bool = self._parse_bool580parse_dict = self._parse_dict581582return {583'zip_safe': parse_bool,584'use_2to3': parse_bool,585'include_package_data': parse_bool,586'package_dir': parse_dict,587'use_2to3_fixers': parse_list,588'use_2to3_exclude_fixers': parse_list,589'convert_2to3_doctests': parse_list,590'scripts': parse_list,591'eager_resources': parse_list,592'dependency_links': parse_list,593'namespace_packages': parse_list,594'install_requires': parse_list_semicolon,595'setup_requires': parse_list_semicolon,596'tests_require': parse_list_semicolon,597'packages': self._parse_packages,598'entry_points': self._parse_file,599'py_modules': parse_list,600'python_requires': SpecifierSet,601}602603def _parse_packages(self, value):604"""Parses `packages` option value.605606:param value:607:rtype: list608"""609find_directives = ['find:', 'find_namespace:']610trimmed_value = value.strip()611612if trimmed_value not in find_directives:613return self._parse_list(value)614615findns = trimmed_value == find_directives[1]616if findns and not PY3:617raise DistutilsOptionError(618'find_namespace: directive is unsupported on Python < 3.3')619620# Read function arguments from a dedicated section.621find_kwargs = self.parse_section_packages__find(622self.sections.get('packages.find', {}))623624if findns:625from setuptools import find_namespace_packages as find_packages626else:627from setuptools import find_packages628629return find_packages(**find_kwargs)630631def parse_section_packages__find(self, section_options):632"""Parses `packages.find` configuration file section.633634To be used in conjunction with _parse_packages().635636:param dict section_options:637"""638section_data = self._parse_section_to_dict(639section_options, self._parse_list)640641valid_keys = ['where', 'include', 'exclude']642643find_kwargs = dict(644[(k, v) for k, v in section_data.items() if k in valid_keys and v])645646where = find_kwargs.get('where')647if where is not None:648find_kwargs['where'] = where[0] # cast list to single val649650return find_kwargs651652def parse_section_entry_points(self, section_options):653"""Parses `entry_points` configuration file section.654655:param dict section_options:656"""657parsed = self._parse_section_to_dict(section_options, self._parse_list)658self['entry_points'] = parsed659660def _parse_package_data(self, section_options):661parsed = self._parse_section_to_dict(section_options, self._parse_list)662663root = parsed.get('*')664if root:665parsed[''] = root666del parsed['*']667668return parsed669670def parse_section_package_data(self, section_options):671"""Parses `package_data` configuration file section.672673:param dict section_options:674"""675self['package_data'] = self._parse_package_data(section_options)676677def parse_section_exclude_package_data(self, section_options):678"""Parses `exclude_package_data` configuration file section.679680:param dict section_options:681"""682self['exclude_package_data'] = self._parse_package_data(683section_options)684685def parse_section_extras_require(self, section_options):686"""Parses `extras_require` configuration file section.687688:param dict section_options:689"""690parse_list = partial(self._parse_list, separator=';')691self['extras_require'] = self._parse_section_to_dict(692section_options, parse_list)693694def parse_section_data_files(self, section_options):695"""Parses `data_files` configuration file section.696697:param dict section_options:698"""699parsed = self._parse_section_to_dict(section_options, self._parse_list)700self['data_files'] = [(k, v) for k, v in parsed.items()]701702703