Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
wiseplat
GitHub Repository: wiseplat/python-code
Path: blob/master/ invest-robot-contest_TinkoffBotTwitch-main/venv/lib/python3.8/site-packages/setuptools/config.py
7763 views
1
from __future__ import absolute_import, unicode_literals
2
import ast
3
import io
4
import os
5
import sys
6
7
import warnings
8
import functools
9
import importlib
10
from collections import defaultdict
11
from functools import partial
12
from functools import wraps
13
import contextlib
14
15
from distutils.errors import DistutilsOptionError, DistutilsFileError
16
from setuptools.extern.packaging.version import LegacyVersion, parse
17
from setuptools.extern.packaging.specifiers import SpecifierSet
18
from setuptools.extern.six import string_types, PY3
19
20
21
__metaclass__ = type
22
23
24
class StaticModule:
25
"""
26
Attempt to load the module by the name
27
"""
28
def __init__(self, name):
29
spec = importlib.util.find_spec(name)
30
with open(spec.origin) as strm:
31
src = strm.read()
32
module = ast.parse(src)
33
vars(self).update(locals())
34
del self.self
35
36
def __getattr__(self, attr):
37
try:
38
return next(
39
ast.literal_eval(statement.value)
40
for statement in self.module.body
41
if isinstance(statement, ast.Assign)
42
for target in statement.targets
43
if isinstance(target, ast.Name) and target.id == attr
44
)
45
except Exception as e:
46
raise AttributeError(
47
"{self.name} has no attribute {attr}".format(**locals())
48
) from e
49
50
51
@contextlib.contextmanager
52
def patch_path(path):
53
"""
54
Add path to front of sys.path for the duration of the context.
55
"""
56
try:
57
sys.path.insert(0, path)
58
yield
59
finally:
60
sys.path.remove(path)
61
62
63
def read_configuration(
64
filepath, find_others=False, ignore_option_errors=False):
65
"""Read given configuration file and returns options from it as a dict.
66
67
:param str|unicode filepath: Path to configuration file
68
to get options from.
69
70
:param bool find_others: Whether to search for other configuration files
71
which could be on in various places.
72
73
:param bool ignore_option_errors: Whether to silently ignore
74
options, values of which could not be resolved (e.g. due to exceptions
75
in directives such as file:, attr:, etc.).
76
If False exceptions are propagated as expected.
77
78
:rtype: dict
79
"""
80
from setuptools.dist import Distribution, _Distribution
81
82
filepath = os.path.abspath(filepath)
83
84
if not os.path.isfile(filepath):
85
raise DistutilsFileError(
86
'Configuration file %s does not exist.' % filepath)
87
88
current_directory = os.getcwd()
89
os.chdir(os.path.dirname(filepath))
90
91
try:
92
dist = Distribution()
93
94
filenames = dist.find_config_files() if find_others else []
95
if filepath not in filenames:
96
filenames.append(filepath)
97
98
_Distribution.parse_config_files(dist, filenames=filenames)
99
100
handlers = parse_configuration(
101
dist, dist.command_options,
102
ignore_option_errors=ignore_option_errors)
103
104
finally:
105
os.chdir(current_directory)
106
107
return configuration_to_dict(handlers)
108
109
110
def _get_option(target_obj, key):
111
"""
112
Given a target object and option key, get that option from
113
the target object, either through a get_{key} method or
114
from an attribute directly.
115
"""
116
getter_name = 'get_{key}'.format(**locals())
117
by_attribute = functools.partial(getattr, target_obj, key)
118
getter = getattr(target_obj, getter_name, by_attribute)
119
return getter()
120
121
122
def configuration_to_dict(handlers):
123
"""Returns configuration data gathered by given handlers as a dict.
124
125
:param list[ConfigHandler] handlers: Handlers list,
126
usually from parse_configuration()
127
128
:rtype: dict
129
"""
130
config_dict = defaultdict(dict)
131
132
for handler in handlers:
133
for option in handler.set_options:
134
value = _get_option(handler.target_obj, option)
135
config_dict[handler.section_prefix][option] = value
136
137
return config_dict
138
139
140
def parse_configuration(
141
distribution, command_options, ignore_option_errors=False):
142
"""Performs additional parsing of configuration options
143
for a distribution.
144
145
Returns a list of used option handlers.
146
147
:param Distribution distribution:
148
:param dict command_options:
149
:param bool ignore_option_errors: Whether to silently ignore
150
options, values of which could not be resolved (e.g. due to exceptions
151
in directives such as file:, attr:, etc.).
152
If False exceptions are propagated as expected.
153
:rtype: list
154
"""
155
options = ConfigOptionsHandler(
156
distribution, command_options, ignore_option_errors)
157
options.parse()
158
159
meta = ConfigMetadataHandler(
160
distribution.metadata, command_options, ignore_option_errors,
161
distribution.package_dir)
162
meta.parse()
163
164
return meta, options
165
166
167
class ConfigHandler:
168
"""Handles metadata supplied in configuration files."""
169
170
section_prefix = None
171
"""Prefix for config sections handled by this handler.
172
Must be provided by class heirs.
173
174
"""
175
176
aliases = {}
177
"""Options aliases.
178
For compatibility with various packages. E.g.: d2to1 and pbr.
179
Note: `-` in keys is replaced with `_` by config parser.
180
181
"""
182
183
def __init__(self, target_obj, options, ignore_option_errors=False):
184
sections = {}
185
186
section_prefix = self.section_prefix
187
for section_name, section_options in options.items():
188
if not section_name.startswith(section_prefix):
189
continue
190
191
section_name = section_name.replace(section_prefix, '').strip('.')
192
sections[section_name] = section_options
193
194
self.ignore_option_errors = ignore_option_errors
195
self.target_obj = target_obj
196
self.sections = sections
197
self.set_options = []
198
199
@property
200
def parsers(self):
201
"""Metadata item name to parser function mapping."""
202
raise NotImplementedError(
203
'%s must provide .parsers property' % self.__class__.__name__)
204
205
def __setitem__(self, option_name, value):
206
unknown = tuple()
207
target_obj = self.target_obj
208
209
# Translate alias into real name.
210
option_name = self.aliases.get(option_name, option_name)
211
212
current_value = getattr(target_obj, option_name, unknown)
213
214
if current_value is unknown:
215
raise KeyError(option_name)
216
217
if current_value:
218
# Already inhabited. Skipping.
219
return
220
221
skip_option = False
222
parser = self.parsers.get(option_name)
223
if parser:
224
try:
225
value = parser(value)
226
227
except Exception:
228
skip_option = True
229
if not self.ignore_option_errors:
230
raise
231
232
if skip_option:
233
return
234
235
setter = getattr(target_obj, 'set_%s' % option_name, None)
236
if setter is None:
237
setattr(target_obj, option_name, value)
238
else:
239
setter(value)
240
241
self.set_options.append(option_name)
242
243
@classmethod
244
def _parse_list(cls, value, separator=','):
245
"""Represents value as a list.
246
247
Value is split either by separator (defaults to comma) or by lines.
248
249
:param value:
250
:param separator: List items separator character.
251
:rtype: list
252
"""
253
if isinstance(value, list): # _get_parser_compound case
254
return value
255
256
if '\n' in value:
257
value = value.splitlines()
258
else:
259
value = value.split(separator)
260
261
return [chunk.strip() for chunk in value if chunk.strip()]
262
263
@classmethod
264
def _parse_dict(cls, value):
265
"""Represents value as a dict.
266
267
:param value:
268
:rtype: dict
269
"""
270
separator = '='
271
result = {}
272
for line in cls._parse_list(value):
273
key, sep, val = line.partition(separator)
274
if sep != separator:
275
raise DistutilsOptionError(
276
'Unable to parse option value to dict: %s' % value)
277
result[key.strip()] = val.strip()
278
279
return result
280
281
@classmethod
282
def _parse_bool(cls, value):
283
"""Represents value as boolean.
284
285
:param value:
286
:rtype: bool
287
"""
288
value = value.lower()
289
return value in ('1', 'true', 'yes')
290
291
@classmethod
292
def _exclude_files_parser(cls, key):
293
"""Returns a parser function to make sure field inputs
294
are not files.
295
296
Parses a value after getting the key so error messages are
297
more informative.
298
299
:param key:
300
:rtype: callable
301
"""
302
def parser(value):
303
exclude_directive = 'file:'
304
if value.startswith(exclude_directive):
305
raise ValueError(
306
'Only strings are accepted for the {0} field, '
307
'files are not accepted'.format(key))
308
return value
309
return parser
310
311
@classmethod
312
def _parse_file(cls, value):
313
"""Represents value as a string, allowing including text
314
from nearest files using `file:` directive.
315
316
Directive is sandboxed and won't reach anything outside
317
directory with setup.py.
318
319
Examples:
320
file: README.rst, CHANGELOG.md, src/file.txt
321
322
:param str value:
323
:rtype: str
324
"""
325
include_directive = 'file:'
326
327
if not isinstance(value, string_types):
328
return value
329
330
if not value.startswith(include_directive):
331
return value
332
333
spec = value[len(include_directive):]
334
filepaths = (os.path.abspath(path.strip()) for path in spec.split(','))
335
return '\n'.join(
336
cls._read_file(path)
337
for path in filepaths
338
if (cls._assert_local(path) or True)
339
and os.path.isfile(path)
340
)
341
342
@staticmethod
343
def _assert_local(filepath):
344
if not filepath.startswith(os.getcwd()):
345
raise DistutilsOptionError(
346
'`file:` directive can not access %s' % filepath)
347
348
@staticmethod
349
def _read_file(filepath):
350
with io.open(filepath, encoding='utf-8') as f:
351
return f.read()
352
353
@classmethod
354
def _parse_attr(cls, value, package_dir=None):
355
"""Represents value as a module attribute.
356
357
Examples:
358
attr: package.attr
359
attr: package.module.attr
360
361
:param str value:
362
:rtype: str
363
"""
364
attr_directive = 'attr:'
365
if not value.startswith(attr_directive):
366
return value
367
368
attrs_path = value.replace(attr_directive, '').strip().split('.')
369
attr_name = attrs_path.pop()
370
371
module_name = '.'.join(attrs_path)
372
module_name = module_name or '__init__'
373
374
parent_path = os.getcwd()
375
if package_dir:
376
if attrs_path[0] in package_dir:
377
# A custom path was specified for the module we want to import
378
custom_path = package_dir[attrs_path[0]]
379
parts = custom_path.rsplit('/', 1)
380
if len(parts) > 1:
381
parent_path = os.path.join(os.getcwd(), parts[0])
382
module_name = parts[1]
383
else:
384
module_name = custom_path
385
elif '' in package_dir:
386
# A custom parent directory was specified for all root modules
387
parent_path = os.path.join(os.getcwd(), package_dir[''])
388
389
with patch_path(parent_path):
390
try:
391
# attempt to load value statically
392
return getattr(StaticModule(module_name), attr_name)
393
except Exception:
394
# fallback to simple import
395
module = importlib.import_module(module_name)
396
397
return getattr(module, attr_name)
398
399
@classmethod
400
def _get_parser_compound(cls, *parse_methods):
401
"""Returns parser function to represents value as a list.
402
403
Parses a value applying given methods one after another.
404
405
:param parse_methods:
406
:rtype: callable
407
"""
408
def parse(value):
409
parsed = value
410
411
for method in parse_methods:
412
parsed = method(parsed)
413
414
return parsed
415
416
return parse
417
418
@classmethod
419
def _parse_section_to_dict(cls, section_options, values_parser=None):
420
"""Parses section options into a dictionary.
421
422
Optionally applies a given parser to values.
423
424
:param dict section_options:
425
:param callable values_parser:
426
:rtype: dict
427
"""
428
value = {}
429
values_parser = values_parser or (lambda val: val)
430
for key, (_, val) in section_options.items():
431
value[key] = values_parser(val)
432
return value
433
434
def parse_section(self, section_options):
435
"""Parses configuration file section.
436
437
:param dict section_options:
438
"""
439
for (name, (_, value)) in section_options.items():
440
try:
441
self[name] = value
442
443
except KeyError:
444
pass # Keep silent for a new option may appear anytime.
445
446
def parse(self):
447
"""Parses configuration file items from one
448
or more related sections.
449
450
"""
451
for section_name, section_options in self.sections.items():
452
453
method_postfix = ''
454
if section_name: # [section.option] variant
455
method_postfix = '_%s' % section_name
456
457
section_parser_method = getattr(
458
self,
459
# Dots in section names are translated into dunderscores.
460
('parse_section%s' % method_postfix).replace('.', '__'),
461
None)
462
463
if section_parser_method is None:
464
raise DistutilsOptionError(
465
'Unsupported distribution option section: [%s.%s]' % (
466
self.section_prefix, section_name))
467
468
section_parser_method(section_options)
469
470
def _deprecated_config_handler(self, func, msg, warning_class):
471
""" this function will wrap around parameters that are deprecated
472
473
:param msg: deprecation message
474
:param warning_class: class of warning exception to be raised
475
:param func: function to be wrapped around
476
"""
477
@wraps(func)
478
def config_handler(*args, **kwargs):
479
warnings.warn(msg, warning_class)
480
return func(*args, **kwargs)
481
482
return config_handler
483
484
485
class ConfigMetadataHandler(ConfigHandler):
486
487
section_prefix = 'metadata'
488
489
aliases = {
490
'home_page': 'url',
491
'summary': 'description',
492
'classifier': 'classifiers',
493
'platform': 'platforms',
494
}
495
496
strict_mode = False
497
"""We need to keep it loose, to be partially compatible with
498
`pbr` and `d2to1` packages which also uses `metadata` section.
499
500
"""
501
502
def __init__(self, target_obj, options, ignore_option_errors=False,
503
package_dir=None):
504
super(ConfigMetadataHandler, self).__init__(target_obj, options,
505
ignore_option_errors)
506
self.package_dir = package_dir
507
508
@property
509
def parsers(self):
510
"""Metadata item name to parser function mapping."""
511
parse_list = self._parse_list
512
parse_file = self._parse_file
513
parse_dict = self._parse_dict
514
exclude_files_parser = self._exclude_files_parser
515
516
return {
517
'platforms': parse_list,
518
'keywords': parse_list,
519
'provides': parse_list,
520
'requires': self._deprecated_config_handler(
521
parse_list,
522
"The requires parameter is deprecated, please use "
523
"install_requires for runtime dependencies.",
524
DeprecationWarning),
525
'obsoletes': parse_list,
526
'classifiers': self._get_parser_compound(parse_file, parse_list),
527
'license': exclude_files_parser('license'),
528
'license_files': parse_list,
529
'description': parse_file,
530
'long_description': parse_file,
531
'version': self._parse_version,
532
'project_urls': parse_dict,
533
}
534
535
def _parse_version(self, value):
536
"""Parses `version` option value.
537
538
:param value:
539
:rtype: str
540
541
"""
542
version = self._parse_file(value)
543
544
if version != value:
545
version = version.strip()
546
# Be strict about versions loaded from file because it's easy to
547
# accidentally include newlines and other unintended content
548
if isinstance(parse(version), LegacyVersion):
549
tmpl = (
550
'Version loaded from {value} does not '
551
'comply with PEP 440: {version}'
552
)
553
raise DistutilsOptionError(tmpl.format(**locals()))
554
555
return version
556
557
version = self._parse_attr(value, self.package_dir)
558
559
if callable(version):
560
version = version()
561
562
if not isinstance(version, string_types):
563
if hasattr(version, '__iter__'):
564
version = '.'.join(map(str, version))
565
else:
566
version = '%s' % version
567
568
return version
569
570
571
class ConfigOptionsHandler(ConfigHandler):
572
573
section_prefix = 'options'
574
575
@property
576
def parsers(self):
577
"""Metadata item name to parser function mapping."""
578
parse_list = self._parse_list
579
parse_list_semicolon = partial(self._parse_list, separator=';')
580
parse_bool = self._parse_bool
581
parse_dict = self._parse_dict
582
583
return {
584
'zip_safe': parse_bool,
585
'use_2to3': parse_bool,
586
'include_package_data': parse_bool,
587
'package_dir': parse_dict,
588
'use_2to3_fixers': parse_list,
589
'use_2to3_exclude_fixers': parse_list,
590
'convert_2to3_doctests': parse_list,
591
'scripts': parse_list,
592
'eager_resources': parse_list,
593
'dependency_links': parse_list,
594
'namespace_packages': parse_list,
595
'install_requires': parse_list_semicolon,
596
'setup_requires': parse_list_semicolon,
597
'tests_require': parse_list_semicolon,
598
'packages': self._parse_packages,
599
'entry_points': self._parse_file,
600
'py_modules': parse_list,
601
'python_requires': SpecifierSet,
602
}
603
604
def _parse_packages(self, value):
605
"""Parses `packages` option value.
606
607
:param value:
608
:rtype: list
609
"""
610
find_directives = ['find:', 'find_namespace:']
611
trimmed_value = value.strip()
612
613
if trimmed_value not in find_directives:
614
return self._parse_list(value)
615
616
findns = trimmed_value == find_directives[1]
617
if findns and not PY3:
618
raise DistutilsOptionError(
619
'find_namespace: directive is unsupported on Python < 3.3')
620
621
# Read function arguments from a dedicated section.
622
find_kwargs = self.parse_section_packages__find(
623
self.sections.get('packages.find', {}))
624
625
if findns:
626
from setuptools import find_namespace_packages as find_packages
627
else:
628
from setuptools import find_packages
629
630
return find_packages(**find_kwargs)
631
632
def parse_section_packages__find(self, section_options):
633
"""Parses `packages.find` configuration file section.
634
635
To be used in conjunction with _parse_packages().
636
637
:param dict section_options:
638
"""
639
section_data = self._parse_section_to_dict(
640
section_options, self._parse_list)
641
642
valid_keys = ['where', 'include', 'exclude']
643
644
find_kwargs = dict(
645
[(k, v) for k, v in section_data.items() if k in valid_keys and v])
646
647
where = find_kwargs.get('where')
648
if where is not None:
649
find_kwargs['where'] = where[0] # cast list to single val
650
651
return find_kwargs
652
653
def parse_section_entry_points(self, section_options):
654
"""Parses `entry_points` configuration file section.
655
656
:param dict section_options:
657
"""
658
parsed = self._parse_section_to_dict(section_options, self._parse_list)
659
self['entry_points'] = parsed
660
661
def _parse_package_data(self, section_options):
662
parsed = self._parse_section_to_dict(section_options, self._parse_list)
663
664
root = parsed.get('*')
665
if root:
666
parsed[''] = root
667
del parsed['*']
668
669
return parsed
670
671
def parse_section_package_data(self, section_options):
672
"""Parses `package_data` configuration file section.
673
674
:param dict section_options:
675
"""
676
self['package_data'] = self._parse_package_data(section_options)
677
678
def parse_section_exclude_package_data(self, section_options):
679
"""Parses `exclude_package_data` configuration file section.
680
681
:param dict section_options:
682
"""
683
self['exclude_package_data'] = self._parse_package_data(
684
section_options)
685
686
def parse_section_extras_require(self, section_options):
687
"""Parses `extras_require` configuration file section.
688
689
:param dict section_options:
690
"""
691
parse_list = partial(self._parse_list, separator=';')
692
self['extras_require'] = self._parse_section_to_dict(
693
section_options, parse_list)
694
695
def parse_section_data_files(self, section_options):
696
"""Parses `data_files` configuration file section.
697
698
:param dict section_options:
699
"""
700
parsed = self._parse_section_to_dict(section_options, self._parse_list)
701
self['data_files'] = [(k, v) for k, v in parsed.items()]
702
703