Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
keewenaw
GitHub Repository: keewenaw/ethereum-wallet-cracker
Path: blob/main/test/lib/python3.9/site-packages/setuptools/command/egg_info.py
4799 views
1
"""setuptools.command.egg_info
2
3
Create a distribution's .egg-info directory and contents"""
4
5
from distutils.filelist import FileList as _FileList
6
from distutils.errors import DistutilsInternalError
7
from distutils.util import convert_path
8
from distutils import log
9
import distutils.errors
10
import distutils.filelist
11
import functools
12
import os
13
import re
14
import sys
15
import io
16
import warnings
17
import time
18
import collections
19
20
from .._importlib import metadata
21
from .. import _entry_points
22
23
from setuptools import Command
24
from setuptools.command.sdist import sdist
25
from setuptools.command.sdist import walk_revctrl
26
from setuptools.command.setopt import edit_config
27
from setuptools.command import bdist_egg
28
from pkg_resources import (
29
Requirement, safe_name, parse_version,
30
safe_version, to_filename)
31
import setuptools.unicode_utils as unicode_utils
32
from setuptools.glob import glob
33
34
from setuptools.extern import packaging
35
from setuptools.extern.jaraco.text import yield_lines
36
from setuptools import SetuptoolsDeprecationWarning
37
38
39
def translate_pattern(glob): # noqa: C901 # is too complex (14) # FIXME
40
"""
41
Translate a file path glob like '*.txt' in to a regular expression.
42
This differs from fnmatch.translate which allows wildcards to match
43
directory separators. It also knows about '**/' which matches any number of
44
directories.
45
"""
46
pat = ''
47
48
# This will split on '/' within [character classes]. This is deliberate.
49
chunks = glob.split(os.path.sep)
50
51
sep = re.escape(os.sep)
52
valid_char = '[^%s]' % (sep,)
53
54
for c, chunk in enumerate(chunks):
55
last_chunk = c == len(chunks) - 1
56
57
# Chunks that are a literal ** are globstars. They match anything.
58
if chunk == '**':
59
if last_chunk:
60
# Match anything if this is the last component
61
pat += '.*'
62
else:
63
# Match '(name/)*'
64
pat += '(?:%s+%s)*' % (valid_char, sep)
65
continue # Break here as the whole path component has been handled
66
67
# Find any special characters in the remainder
68
i = 0
69
chunk_len = len(chunk)
70
while i < chunk_len:
71
char = chunk[i]
72
if char == '*':
73
# Match any number of name characters
74
pat += valid_char + '*'
75
elif char == '?':
76
# Match a name character
77
pat += valid_char
78
elif char == '[':
79
# Character class
80
inner_i = i + 1
81
# Skip initial !/] chars
82
if inner_i < chunk_len and chunk[inner_i] == '!':
83
inner_i = inner_i + 1
84
if inner_i < chunk_len and chunk[inner_i] == ']':
85
inner_i = inner_i + 1
86
87
# Loop till the closing ] is found
88
while inner_i < chunk_len and chunk[inner_i] != ']':
89
inner_i = inner_i + 1
90
91
if inner_i >= chunk_len:
92
# Got to the end of the string without finding a closing ]
93
# Do not treat this as a matching group, but as a literal [
94
pat += re.escape(char)
95
else:
96
# Grab the insides of the [brackets]
97
inner = chunk[i + 1:inner_i]
98
char_class = ''
99
100
# Class negation
101
if inner[0] == '!':
102
char_class = '^'
103
inner = inner[1:]
104
105
char_class += re.escape(inner)
106
pat += '[%s]' % (char_class,)
107
108
# Skip to the end ]
109
i = inner_i
110
else:
111
pat += re.escape(char)
112
i += 1
113
114
# Join each chunk with the dir separator
115
if not last_chunk:
116
pat += sep
117
118
pat += r'\Z'
119
return re.compile(pat, flags=re.MULTILINE | re.DOTALL)
120
121
122
class InfoCommon:
123
tag_build = None
124
tag_date = None
125
126
@property
127
def name(self):
128
return safe_name(self.distribution.get_name())
129
130
def tagged_version(self):
131
return safe_version(self._maybe_tag(self.distribution.get_version()))
132
133
def _maybe_tag(self, version):
134
"""
135
egg_info may be called more than once for a distribution,
136
in which case the version string already contains all tags.
137
"""
138
return (
139
version if self.vtags and self._already_tagged(version)
140
else version + self.vtags
141
)
142
143
def _already_tagged(self, version: str) -> bool:
144
# Depending on their format, tags may change with version normalization.
145
# So in addition the regular tags, we have to search for the normalized ones.
146
return version.endswith(self.vtags) or version.endswith(self._safe_tags())
147
148
def _safe_tags(self) -> str:
149
# To implement this we can rely on `safe_version` pretending to be version 0
150
# followed by tags. Then we simply discard the starting 0 (fake version number)
151
return safe_version(f"0{self.vtags}")[1:]
152
153
def tags(self) -> str:
154
version = ''
155
if self.tag_build:
156
version += self.tag_build
157
if self.tag_date:
158
version += time.strftime("-%Y%m%d")
159
return version
160
vtags = property(tags)
161
162
163
class egg_info(InfoCommon, Command):
164
description = "create a distribution's .egg-info directory"
165
166
user_options = [
167
('egg-base=', 'e', "directory containing .egg-info directories"
168
" (default: top of the source tree)"),
169
('tag-date', 'd', "Add date stamp (e.g. 20050528) to version number"),
170
('tag-build=', 'b', "Specify explicit tag to add to version number"),
171
('no-date', 'D', "Don't include date stamp [default]"),
172
]
173
174
boolean_options = ['tag-date']
175
negative_opt = {
176
'no-date': 'tag-date',
177
}
178
179
def initialize_options(self):
180
self.egg_base = None
181
self.egg_name = None
182
self.egg_info = None
183
self.egg_version = None
184
self.broken_egg_info = False
185
186
####################################
187
# allow the 'tag_svn_revision' to be detected and
188
# set, supporting sdists built on older Setuptools.
189
@property
190
def tag_svn_revision(self):
191
pass
192
193
@tag_svn_revision.setter
194
def tag_svn_revision(self, value):
195
pass
196
####################################
197
198
def save_version_info(self, filename):
199
"""
200
Materialize the value of date into the
201
build tag. Install build keys in a deterministic order
202
to avoid arbitrary reordering on subsequent builds.
203
"""
204
egg_info = collections.OrderedDict()
205
# follow the order these keys would have been added
206
# when PYTHONHASHSEED=0
207
egg_info['tag_build'] = self.tags()
208
egg_info['tag_date'] = 0
209
edit_config(filename, dict(egg_info=egg_info))
210
211
def finalize_options(self):
212
# Note: we need to capture the current value returned
213
# by `self.tagged_version()`, so we can later update
214
# `self.distribution.metadata.version` without
215
# repercussions.
216
self.egg_name = self.name
217
self.egg_version = self.tagged_version()
218
parsed_version = parse_version(self.egg_version)
219
220
try:
221
is_version = isinstance(parsed_version, packaging.version.Version)
222
spec = "%s==%s" if is_version else "%s===%s"
223
Requirement(spec % (self.egg_name, self.egg_version))
224
except ValueError as e:
225
raise distutils.errors.DistutilsOptionError(
226
"Invalid distribution name or version syntax: %s-%s" %
227
(self.egg_name, self.egg_version)
228
) from e
229
230
if self.egg_base is None:
231
dirs = self.distribution.package_dir
232
self.egg_base = (dirs or {}).get('', os.curdir)
233
234
self.ensure_dirname('egg_base')
235
self.egg_info = to_filename(self.egg_name) + '.egg-info'
236
if self.egg_base != os.curdir:
237
self.egg_info = os.path.join(self.egg_base, self.egg_info)
238
if '-' in self.egg_name:
239
self.check_broken_egg_info()
240
241
# Set package version for the benefit of dumber commands
242
# (e.g. sdist, bdist_wininst, etc.)
243
#
244
self.distribution.metadata.version = self.egg_version
245
246
# If we bootstrapped around the lack of a PKG-INFO, as might be the
247
# case in a fresh checkout, make sure that any special tags get added
248
# to the version info
249
#
250
pd = self.distribution._patched_dist
251
if pd is not None and pd.key == self.egg_name.lower():
252
pd._version = self.egg_version
253
pd._parsed_version = parse_version(self.egg_version)
254
self.distribution._patched_dist = None
255
256
def write_or_delete_file(self, what, filename, data, force=False):
257
"""Write `data` to `filename` or delete if empty
258
259
If `data` is non-empty, this routine is the same as ``write_file()``.
260
If `data` is empty but not ``None``, this is the same as calling
261
``delete_file(filename)`. If `data` is ``None``, then this is a no-op
262
unless `filename` exists, in which case a warning is issued about the
263
orphaned file (if `force` is false), or deleted (if `force` is true).
264
"""
265
if data:
266
self.write_file(what, filename, data)
267
elif os.path.exists(filename):
268
if data is None and not force:
269
log.warn(
270
"%s not set in setup(), but %s exists", what, filename
271
)
272
return
273
else:
274
self.delete_file(filename)
275
276
def write_file(self, what, filename, data):
277
"""Write `data` to `filename` (if not a dry run) after announcing it
278
279
`what` is used in a log message to identify what is being written
280
to the file.
281
"""
282
log.info("writing %s to %s", what, filename)
283
data = data.encode("utf-8")
284
if not self.dry_run:
285
f = open(filename, 'wb')
286
f.write(data)
287
f.close()
288
289
def delete_file(self, filename):
290
"""Delete `filename` (if not a dry run) after announcing it"""
291
log.info("deleting %s", filename)
292
if not self.dry_run:
293
os.unlink(filename)
294
295
def run(self):
296
self.mkpath(self.egg_info)
297
os.utime(self.egg_info, None)
298
for ep in metadata.entry_points(group='egg_info.writers'):
299
self.distribution._install_dependencies(ep)
300
writer = ep.load()
301
writer(self, ep.name, os.path.join(self.egg_info, ep.name))
302
303
# Get rid of native_libs.txt if it was put there by older bdist_egg
304
nl = os.path.join(self.egg_info, "native_libs.txt")
305
if os.path.exists(nl):
306
self.delete_file(nl)
307
308
self.find_sources()
309
310
def find_sources(self):
311
"""Generate SOURCES.txt manifest file"""
312
manifest_filename = os.path.join(self.egg_info, "SOURCES.txt")
313
mm = manifest_maker(self.distribution)
314
mm.manifest = manifest_filename
315
mm.run()
316
self.filelist = mm.filelist
317
318
def check_broken_egg_info(self):
319
bei = self.egg_name + '.egg-info'
320
if self.egg_base != os.curdir:
321
bei = os.path.join(self.egg_base, bei)
322
if os.path.exists(bei):
323
log.warn(
324
"-" * 78 + '\n'
325
"Note: Your current .egg-info directory has a '-' in its name;"
326
'\nthis will not work correctly with "setup.py develop".\n\n'
327
'Please rename %s to %s to correct this problem.\n' + '-' * 78,
328
bei, self.egg_info
329
)
330
self.broken_egg_info = self.egg_info
331
self.egg_info = bei # make it work for now
332
333
334
class FileList(_FileList):
335
# Implementations of the various MANIFEST.in commands
336
337
def process_template_line(self, line):
338
# Parse the line: split it up, make sure the right number of words
339
# is there, and return the relevant words. 'action' is always
340
# defined: it's the first word of the line. Which of the other
341
# three are defined depends on the action; it'll be either
342
# patterns, (dir and patterns), or (dir_pattern).
343
(action, patterns, dir, dir_pattern) = self._parse_template_line(line)
344
345
action_map = {
346
'include': self.include,
347
'exclude': self.exclude,
348
'global-include': self.global_include,
349
'global-exclude': self.global_exclude,
350
'recursive-include': functools.partial(
351
self.recursive_include, dir,
352
),
353
'recursive-exclude': functools.partial(
354
self.recursive_exclude, dir,
355
),
356
'graft': self.graft,
357
'prune': self.prune,
358
}
359
log_map = {
360
'include': "warning: no files found matching '%s'",
361
'exclude': (
362
"warning: no previously-included files found "
363
"matching '%s'"
364
),
365
'global-include': (
366
"warning: no files found matching '%s' "
367
"anywhere in distribution"
368
),
369
'global-exclude': (
370
"warning: no previously-included files matching "
371
"'%s' found anywhere in distribution"
372
),
373
'recursive-include': (
374
"warning: no files found matching '%s' "
375
"under directory '%s'"
376
),
377
'recursive-exclude': (
378
"warning: no previously-included files matching "
379
"'%s' found under directory '%s'"
380
),
381
'graft': "warning: no directories found matching '%s'",
382
'prune': "no previously-included directories found matching '%s'",
383
}
384
385
try:
386
process_action = action_map[action]
387
except KeyError:
388
raise DistutilsInternalError(
389
"this cannot happen: invalid action '{action!s}'".
390
format(action=action),
391
)
392
393
# OK, now we know that the action is valid and we have the
394
# right number of words on the line for that action -- so we
395
# can proceed with minimal error-checking.
396
397
action_is_recursive = action.startswith('recursive-')
398
if action in {'graft', 'prune'}:
399
patterns = [dir_pattern]
400
extra_log_args = (dir, ) if action_is_recursive else ()
401
log_tmpl = log_map[action]
402
403
self.debug_print(
404
' '.join(
405
[action] +
406
([dir] if action_is_recursive else []) +
407
patterns,
408
)
409
)
410
for pattern in patterns:
411
if not process_action(pattern):
412
log.warn(log_tmpl, pattern, *extra_log_args)
413
414
def _remove_files(self, predicate):
415
"""
416
Remove all files from the file list that match the predicate.
417
Return True if any matching files were removed
418
"""
419
found = False
420
for i in range(len(self.files) - 1, -1, -1):
421
if predicate(self.files[i]):
422
self.debug_print(" removing " + self.files[i])
423
del self.files[i]
424
found = True
425
return found
426
427
def include(self, pattern):
428
"""Include files that match 'pattern'."""
429
found = [f for f in glob(pattern) if not os.path.isdir(f)]
430
self.extend(found)
431
return bool(found)
432
433
def exclude(self, pattern):
434
"""Exclude files that match 'pattern'."""
435
match = translate_pattern(pattern)
436
return self._remove_files(match.match)
437
438
def recursive_include(self, dir, pattern):
439
"""
440
Include all files anywhere in 'dir/' that match the pattern.
441
"""
442
full_pattern = os.path.join(dir, '**', pattern)
443
found = [f for f in glob(full_pattern, recursive=True)
444
if not os.path.isdir(f)]
445
self.extend(found)
446
return bool(found)
447
448
def recursive_exclude(self, dir, pattern):
449
"""
450
Exclude any file anywhere in 'dir/' that match the pattern.
451
"""
452
match = translate_pattern(os.path.join(dir, '**', pattern))
453
return self._remove_files(match.match)
454
455
def graft(self, dir):
456
"""Include all files from 'dir/'."""
457
found = [
458
item
459
for match_dir in glob(dir)
460
for item in distutils.filelist.findall(match_dir)
461
]
462
self.extend(found)
463
return bool(found)
464
465
def prune(self, dir):
466
"""Filter out files from 'dir/'."""
467
match = translate_pattern(os.path.join(dir, '**'))
468
return self._remove_files(match.match)
469
470
def global_include(self, pattern):
471
"""
472
Include all files anywhere in the current directory that match the
473
pattern. This is very inefficient on large file trees.
474
"""
475
if self.allfiles is None:
476
self.findall()
477
match = translate_pattern(os.path.join('**', pattern))
478
found = [f for f in self.allfiles if match.match(f)]
479
self.extend(found)
480
return bool(found)
481
482
def global_exclude(self, pattern):
483
"""
484
Exclude all files anywhere that match the pattern.
485
"""
486
match = translate_pattern(os.path.join('**', pattern))
487
return self._remove_files(match.match)
488
489
def append(self, item):
490
if item.endswith('\r'): # Fix older sdists built on Windows
491
item = item[:-1]
492
path = convert_path(item)
493
494
if self._safe_path(path):
495
self.files.append(path)
496
497
def extend(self, paths):
498
self.files.extend(filter(self._safe_path, paths))
499
500
def _repair(self):
501
"""
502
Replace self.files with only safe paths
503
504
Because some owners of FileList manipulate the underlying
505
``files`` attribute directly, this method must be called to
506
repair those paths.
507
"""
508
self.files = list(filter(self._safe_path, self.files))
509
510
def _safe_path(self, path):
511
enc_warn = "'%s' not %s encodable -- skipping"
512
513
# To avoid accidental trans-codings errors, first to unicode
514
u_path = unicode_utils.filesys_decode(path)
515
if u_path is None:
516
log.warn("'%s' in unexpected encoding -- skipping" % path)
517
return False
518
519
# Must ensure utf-8 encodability
520
utf8_path = unicode_utils.try_encode(u_path, "utf-8")
521
if utf8_path is None:
522
log.warn(enc_warn, path, 'utf-8')
523
return False
524
525
try:
526
# accept is either way checks out
527
if os.path.exists(u_path) or os.path.exists(utf8_path):
528
return True
529
# this will catch any encode errors decoding u_path
530
except UnicodeEncodeError:
531
log.warn(enc_warn, path, sys.getfilesystemencoding())
532
533
534
class manifest_maker(sdist):
535
template = "MANIFEST.in"
536
537
def initialize_options(self):
538
self.use_defaults = 1
539
self.prune = 1
540
self.manifest_only = 1
541
self.force_manifest = 1
542
543
def finalize_options(self):
544
pass
545
546
def run(self):
547
self.filelist = FileList()
548
if not os.path.exists(self.manifest):
549
self.write_manifest() # it must exist so it'll get in the list
550
self.add_defaults()
551
if os.path.exists(self.template):
552
self.read_template()
553
self.add_license_files()
554
self.prune_file_list()
555
self.filelist.sort()
556
self.filelist.remove_duplicates()
557
self.write_manifest()
558
559
def _manifest_normalize(self, path):
560
path = unicode_utils.filesys_decode(path)
561
return path.replace(os.sep, '/')
562
563
def write_manifest(self):
564
"""
565
Write the file list in 'self.filelist' to the manifest file
566
named by 'self.manifest'.
567
"""
568
self.filelist._repair()
569
570
# Now _repairs should encodability, but not unicode
571
files = [self._manifest_normalize(f) for f in self.filelist.files]
572
msg = "writing manifest file '%s'" % self.manifest
573
self.execute(write_file, (self.manifest, files), msg)
574
575
def warn(self, msg):
576
if not self._should_suppress_warning(msg):
577
sdist.warn(self, msg)
578
579
@staticmethod
580
def _should_suppress_warning(msg):
581
"""
582
suppress missing-file warnings from sdist
583
"""
584
return re.match(r"standard file .*not found", msg)
585
586
def add_defaults(self):
587
sdist.add_defaults(self)
588
self.filelist.append(self.template)
589
self.filelist.append(self.manifest)
590
rcfiles = list(walk_revctrl())
591
if rcfiles:
592
self.filelist.extend(rcfiles)
593
elif os.path.exists(self.manifest):
594
self.read_manifest()
595
596
if os.path.exists("setup.py"):
597
# setup.py should be included by default, even if it's not
598
# the script called to create the sdist
599
self.filelist.append("setup.py")
600
601
ei_cmd = self.get_finalized_command('egg_info')
602
self.filelist.graft(ei_cmd.egg_info)
603
604
def add_license_files(self):
605
license_files = self.distribution.metadata.license_files or []
606
for lf in license_files:
607
log.info("adding license file '%s'", lf)
608
pass
609
self.filelist.extend(license_files)
610
611
def prune_file_list(self):
612
build = self.get_finalized_command('build')
613
base_dir = self.distribution.get_fullname()
614
self.filelist.prune(build.build_base)
615
self.filelist.prune(base_dir)
616
sep = re.escape(os.sep)
617
self.filelist.exclude_pattern(r'(^|' + sep + r')(RCS|CVS|\.svn)' + sep,
618
is_regex=1)
619
620
def _safe_data_files(self, build_py):
621
"""
622
The parent class implementation of this method
623
(``sdist``) will try to include data files, which
624
might cause recursion problems when
625
``include_package_data=True``.
626
627
Therefore, avoid triggering any attempt of
628
analyzing/building the manifest again.
629
"""
630
if hasattr(build_py, 'get_data_files_without_manifest'):
631
return build_py.get_data_files_without_manifest()
632
633
warnings.warn(
634
"Custom 'build_py' does not implement "
635
"'get_data_files_without_manifest'.\nPlease extend command classes"
636
" from setuptools instead of distutils.",
637
SetuptoolsDeprecationWarning
638
)
639
return build_py.get_data_files()
640
641
642
def write_file(filename, contents):
643
"""Create a file with the specified name and write 'contents' (a
644
sequence of strings without line terminators) to it.
645
"""
646
contents = "\n".join(contents)
647
648
# assuming the contents has been vetted for utf-8 encoding
649
contents = contents.encode("utf-8")
650
651
with open(filename, "wb") as f: # always write POSIX-style manifest
652
f.write(contents)
653
654
655
def write_pkg_info(cmd, basename, filename):
656
log.info("writing %s", filename)
657
if not cmd.dry_run:
658
metadata = cmd.distribution.metadata
659
metadata.version, oldver = cmd.egg_version, metadata.version
660
metadata.name, oldname = cmd.egg_name, metadata.name
661
662
try:
663
# write unescaped data to PKG-INFO, so older pkg_resources
664
# can still parse it
665
metadata.write_pkg_info(cmd.egg_info)
666
finally:
667
metadata.name, metadata.version = oldname, oldver
668
669
safe = getattr(cmd.distribution, 'zip_safe', None)
670
671
bdist_egg.write_safety_flag(cmd.egg_info, safe)
672
673
674
def warn_depends_obsolete(cmd, basename, filename):
675
if os.path.exists(filename):
676
log.warn(
677
"WARNING: 'depends.txt' is not used by setuptools 0.6!\n"
678
"Use the install_requires/extras_require setup() args instead."
679
)
680
681
682
def _write_requirements(stream, reqs):
683
lines = yield_lines(reqs or ())
684
685
def append_cr(line):
686
return line + '\n'
687
lines = map(append_cr, lines)
688
stream.writelines(lines)
689
690
691
def write_requirements(cmd, basename, filename):
692
dist = cmd.distribution
693
data = io.StringIO()
694
_write_requirements(data, dist.install_requires)
695
extras_require = dist.extras_require or {}
696
for extra in sorted(extras_require):
697
data.write('\n[{extra}]\n'.format(**vars()))
698
_write_requirements(data, extras_require[extra])
699
cmd.write_or_delete_file("requirements", filename, data.getvalue())
700
701
702
def write_setup_requirements(cmd, basename, filename):
703
data = io.StringIO()
704
_write_requirements(data, cmd.distribution.setup_requires)
705
cmd.write_or_delete_file("setup-requirements", filename, data.getvalue())
706
707
708
def write_toplevel_names(cmd, basename, filename):
709
pkgs = dict.fromkeys(
710
[
711
k.split('.', 1)[0]
712
for k in cmd.distribution.iter_distribution_names()
713
]
714
)
715
cmd.write_file("top-level names", filename, '\n'.join(sorted(pkgs)) + '\n')
716
717
718
def overwrite_arg(cmd, basename, filename):
719
write_arg(cmd, basename, filename, True)
720
721
722
def write_arg(cmd, basename, filename, force=False):
723
argname = os.path.splitext(basename)[0]
724
value = getattr(cmd.distribution, argname, None)
725
if value is not None:
726
value = '\n'.join(value) + '\n'
727
cmd.write_or_delete_file(argname, filename, value, force)
728
729
730
def write_entries(cmd, basename, filename):
731
eps = _entry_points.load(cmd.distribution.entry_points)
732
defn = _entry_points.render(eps)
733
cmd.write_or_delete_file('entry points', filename, defn, True)
734
735
736
def get_pkg_info_revision():
737
"""
738
Get a -r### off of PKG-INFO Version in case this is an sdist of
739
a subversion revision.
740
"""
741
warnings.warn(
742
"get_pkg_info_revision is deprecated.", EggInfoDeprecationWarning)
743
if os.path.exists('PKG-INFO'):
744
with io.open('PKG-INFO') as f:
745
for line in f:
746
match = re.match(r"Version:.*-r(\d+)\s*$", line)
747
if match:
748
return int(match.group(1))
749
return 0
750
751
752
class EggInfoDeprecationWarning(SetuptoolsDeprecationWarning):
753
"""Deprecated behavior warning for EggInfo, bypassing suppression."""
754
755