Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hhhrrrttt222111
GitHub Repository: hhhrrrttt222111/Dorkify
Path: blob/master/venv/Lib/site-packages/setuptools/command/bdist_egg.py
811 views
1
"""setuptools.command.bdist_egg
2
3
Build .egg distributions"""
4
5
from distutils.errors import DistutilsSetupError
6
from distutils.dir_util import remove_tree, mkpath
7
from distutils import log
8
from types import CodeType
9
import sys
10
import os
11
import re
12
import textwrap
13
import marshal
14
import warnings
15
16
from setuptools.extern import six
17
18
from pkg_resources import get_build_platform, Distribution, ensure_directory
19
from pkg_resources import EntryPoint
20
from setuptools.extension import Library
21
from setuptools import Command, SetuptoolsDeprecationWarning
22
23
try:
24
# Python 2.7 or >=3.2
25
from sysconfig import get_path, get_python_version
26
27
def _get_purelib():
28
return get_path("purelib")
29
except ImportError:
30
from distutils.sysconfig import get_python_lib, get_python_version
31
32
def _get_purelib():
33
return get_python_lib(False)
34
35
36
def strip_module(filename):
37
if '.' in filename:
38
filename = os.path.splitext(filename)[0]
39
if filename.endswith('module'):
40
filename = filename[:-6]
41
return filename
42
43
44
def sorted_walk(dir):
45
"""Do os.walk in a reproducible way,
46
independent of indeterministic filesystem readdir order
47
"""
48
for base, dirs, files in os.walk(dir):
49
dirs.sort()
50
files.sort()
51
yield base, dirs, files
52
53
54
def write_stub(resource, pyfile):
55
_stub_template = textwrap.dedent("""
56
def __bootstrap__():
57
global __bootstrap__, __loader__, __file__
58
import sys, pkg_resources
59
from importlib.machinery import ExtensionFileLoader
60
__file__ = pkg_resources.resource_filename(__name__, %r)
61
__loader__ = None; del __bootstrap__, __loader__
62
ExtensionFileLoader(__name__,__file__).exec_module()
63
__bootstrap__()
64
""").lstrip()
65
with open(pyfile, 'w') as f:
66
f.write(_stub_template % resource)
67
68
69
class bdist_egg(Command):
70
description = "create an \"egg\" distribution"
71
72
user_options = [
73
('bdist-dir=', 'b',
74
"temporary directory for creating the distribution"),
75
('plat-name=', 'p', "platform name to embed in generated filenames "
76
"(default: %s)" % get_build_platform()),
77
('exclude-source-files', None,
78
"remove all .py files from the generated egg"),
79
('keep-temp', 'k',
80
"keep the pseudo-installation tree around after " +
81
"creating the distribution archive"),
82
('dist-dir=', 'd',
83
"directory to put final built distributions in"),
84
('skip-build', None,
85
"skip rebuilding everything (for testing/debugging)"),
86
]
87
88
boolean_options = [
89
'keep-temp', 'skip-build', 'exclude-source-files'
90
]
91
92
def initialize_options(self):
93
self.bdist_dir = None
94
self.plat_name = None
95
self.keep_temp = 0
96
self.dist_dir = None
97
self.skip_build = 0
98
self.egg_output = None
99
self.exclude_source_files = None
100
101
def finalize_options(self):
102
ei_cmd = self.ei_cmd = self.get_finalized_command("egg_info")
103
self.egg_info = ei_cmd.egg_info
104
105
if self.bdist_dir is None:
106
bdist_base = self.get_finalized_command('bdist').bdist_base
107
self.bdist_dir = os.path.join(bdist_base, 'egg')
108
109
if self.plat_name is None:
110
self.plat_name = get_build_platform()
111
112
self.set_undefined_options('bdist', ('dist_dir', 'dist_dir'))
113
114
if self.egg_output is None:
115
116
# Compute filename of the output egg
117
basename = Distribution(
118
None, None, ei_cmd.egg_name, ei_cmd.egg_version,
119
get_python_version(),
120
self.distribution.has_ext_modules() and self.plat_name
121
).egg_name()
122
123
self.egg_output = os.path.join(self.dist_dir, basename + '.egg')
124
125
def do_install_data(self):
126
# Hack for packages that install data to install's --install-lib
127
self.get_finalized_command('install').install_lib = self.bdist_dir
128
129
site_packages = os.path.normcase(os.path.realpath(_get_purelib()))
130
old, self.distribution.data_files = self.distribution.data_files, []
131
132
for item in old:
133
if isinstance(item, tuple) and len(item) == 2:
134
if os.path.isabs(item[0]):
135
realpath = os.path.realpath(item[0])
136
normalized = os.path.normcase(realpath)
137
if normalized == site_packages or normalized.startswith(
138
site_packages + os.sep
139
):
140
item = realpath[len(site_packages) + 1:], item[1]
141
# XXX else: raise ???
142
self.distribution.data_files.append(item)
143
144
try:
145
log.info("installing package data to %s", self.bdist_dir)
146
self.call_command('install_data', force=0, root=None)
147
finally:
148
self.distribution.data_files = old
149
150
def get_outputs(self):
151
return [self.egg_output]
152
153
def call_command(self, cmdname, **kw):
154
"""Invoke reinitialized command `cmdname` with keyword args"""
155
for dirname in INSTALL_DIRECTORY_ATTRS:
156
kw.setdefault(dirname, self.bdist_dir)
157
kw.setdefault('skip_build', self.skip_build)
158
kw.setdefault('dry_run', self.dry_run)
159
cmd = self.reinitialize_command(cmdname, **kw)
160
self.run_command(cmdname)
161
return cmd
162
163
def run(self):
164
# Generate metadata first
165
self.run_command("egg_info")
166
# We run install_lib before install_data, because some data hacks
167
# pull their data path from the install_lib command.
168
log.info("installing library code to %s", self.bdist_dir)
169
instcmd = self.get_finalized_command('install')
170
old_root = instcmd.root
171
instcmd.root = None
172
if self.distribution.has_c_libraries() and not self.skip_build:
173
self.run_command('build_clib')
174
cmd = self.call_command('install_lib', warn_dir=0)
175
instcmd.root = old_root
176
177
all_outputs, ext_outputs = self.get_ext_outputs()
178
self.stubs = []
179
to_compile = []
180
for (p, ext_name) in enumerate(ext_outputs):
181
filename, ext = os.path.splitext(ext_name)
182
pyfile = os.path.join(self.bdist_dir, strip_module(filename) +
183
'.py')
184
self.stubs.append(pyfile)
185
log.info("creating stub loader for %s", ext_name)
186
if not self.dry_run:
187
write_stub(os.path.basename(ext_name), pyfile)
188
to_compile.append(pyfile)
189
ext_outputs[p] = ext_name.replace(os.sep, '/')
190
191
if to_compile:
192
cmd.byte_compile(to_compile)
193
if self.distribution.data_files:
194
self.do_install_data()
195
196
# Make the EGG-INFO directory
197
archive_root = self.bdist_dir
198
egg_info = os.path.join(archive_root, 'EGG-INFO')
199
self.mkpath(egg_info)
200
if self.distribution.scripts:
201
script_dir = os.path.join(egg_info, 'scripts')
202
log.info("installing scripts to %s", script_dir)
203
self.call_command('install_scripts', install_dir=script_dir,
204
no_ep=1)
205
206
self.copy_metadata_to(egg_info)
207
native_libs = os.path.join(egg_info, "native_libs.txt")
208
if all_outputs:
209
log.info("writing %s", native_libs)
210
if not self.dry_run:
211
ensure_directory(native_libs)
212
libs_file = open(native_libs, 'wt')
213
libs_file.write('\n'.join(all_outputs))
214
libs_file.write('\n')
215
libs_file.close()
216
elif os.path.isfile(native_libs):
217
log.info("removing %s", native_libs)
218
if not self.dry_run:
219
os.unlink(native_libs)
220
221
write_safety_flag(
222
os.path.join(archive_root, 'EGG-INFO'), self.zip_safe()
223
)
224
225
if os.path.exists(os.path.join(self.egg_info, 'depends.txt')):
226
log.warn(
227
"WARNING: 'depends.txt' will not be used by setuptools 0.6!\n"
228
"Use the install_requires/extras_require setup() args instead."
229
)
230
231
if self.exclude_source_files:
232
self.zap_pyfiles()
233
234
# Make the archive
235
make_zipfile(self.egg_output, archive_root, verbose=self.verbose,
236
dry_run=self.dry_run, mode=self.gen_header())
237
if not self.keep_temp:
238
remove_tree(self.bdist_dir, dry_run=self.dry_run)
239
240
# Add to 'Distribution.dist_files' so that the "upload" command works
241
getattr(self.distribution, 'dist_files', []).append(
242
('bdist_egg', get_python_version(), self.egg_output))
243
244
def zap_pyfiles(self):
245
log.info("Removing .py files from temporary directory")
246
for base, dirs, files in walk_egg(self.bdist_dir):
247
for name in files:
248
path = os.path.join(base, name)
249
250
if name.endswith('.py'):
251
log.debug("Deleting %s", path)
252
os.unlink(path)
253
254
if base.endswith('__pycache__'):
255
path_old = path
256
257
pattern = r'(?P<name>.+)\.(?P<magic>[^.]+)\.pyc'
258
m = re.match(pattern, name)
259
path_new = os.path.join(
260
base, os.pardir, m.group('name') + '.pyc')
261
log.info(
262
"Renaming file from [%s] to [%s]"
263
% (path_old, path_new))
264
try:
265
os.remove(path_new)
266
except OSError:
267
pass
268
os.rename(path_old, path_new)
269
270
def zip_safe(self):
271
safe = getattr(self.distribution, 'zip_safe', None)
272
if safe is not None:
273
return safe
274
log.warn("zip_safe flag not set; analyzing archive contents...")
275
return analyze_egg(self.bdist_dir, self.stubs)
276
277
def gen_header(self):
278
epm = EntryPoint.parse_map(self.distribution.entry_points or '')
279
ep = epm.get('setuptools.installation', {}).get('eggsecutable')
280
if ep is None:
281
return 'w' # not an eggsecutable, do it the usual way.
282
283
warnings.warn(
284
"Eggsecutables are deprecated and will be removed in a future "
285
"version.",
286
SetuptoolsDeprecationWarning
287
)
288
289
if not ep.attrs or ep.extras:
290
raise DistutilsSetupError(
291
"eggsecutable entry point (%r) cannot have 'extras' "
292
"or refer to a module" % (ep,)
293
)
294
295
pyver = '{}.{}'.format(*sys.version_info)
296
pkg = ep.module_name
297
full = '.'.join(ep.attrs)
298
base = ep.attrs[0]
299
basename = os.path.basename(self.egg_output)
300
301
header = (
302
"#!/bin/sh\n"
303
'if [ `basename $0` = "%(basename)s" ]\n'
304
'then exec python%(pyver)s -c "'
305
"import sys, os; sys.path.insert(0, os.path.abspath('$0')); "
306
"from %(pkg)s import %(base)s; sys.exit(%(full)s())"
307
'" "$@"\n'
308
'else\n'
309
' echo $0 is not the correct name for this egg file.\n'
310
' echo Please rename it back to %(basename)s and try again.\n'
311
' exec false\n'
312
'fi\n'
313
) % locals()
314
315
if not self.dry_run:
316
mkpath(os.path.dirname(self.egg_output), dry_run=self.dry_run)
317
f = open(self.egg_output, 'w')
318
f.write(header)
319
f.close()
320
return 'a'
321
322
def copy_metadata_to(self, target_dir):
323
"Copy metadata (egg info) to the target_dir"
324
# normalize the path (so that a forward-slash in egg_info will
325
# match using startswith below)
326
norm_egg_info = os.path.normpath(self.egg_info)
327
prefix = os.path.join(norm_egg_info, '')
328
for path in self.ei_cmd.filelist.files:
329
if path.startswith(prefix):
330
target = os.path.join(target_dir, path[len(prefix):])
331
ensure_directory(target)
332
self.copy_file(path, target)
333
334
def get_ext_outputs(self):
335
"""Get a list of relative paths to C extensions in the output distro"""
336
337
all_outputs = []
338
ext_outputs = []
339
340
paths = {self.bdist_dir: ''}
341
for base, dirs, files in sorted_walk(self.bdist_dir):
342
for filename in files:
343
if os.path.splitext(filename)[1].lower() in NATIVE_EXTENSIONS:
344
all_outputs.append(paths[base] + filename)
345
for filename in dirs:
346
paths[os.path.join(base, filename)] = (paths[base] +
347
filename + '/')
348
349
if self.distribution.has_ext_modules():
350
build_cmd = self.get_finalized_command('build_ext')
351
for ext in build_cmd.extensions:
352
if isinstance(ext, Library):
353
continue
354
fullname = build_cmd.get_ext_fullname(ext.name)
355
filename = build_cmd.get_ext_filename(fullname)
356
if not os.path.basename(filename).startswith('dl-'):
357
if os.path.exists(os.path.join(self.bdist_dir, filename)):
358
ext_outputs.append(filename)
359
360
return all_outputs, ext_outputs
361
362
363
NATIVE_EXTENSIONS = dict.fromkeys('.dll .so .dylib .pyd'.split())
364
365
366
def walk_egg(egg_dir):
367
"""Walk an unpacked egg's contents, skipping the metadata directory"""
368
walker = sorted_walk(egg_dir)
369
base, dirs, files = next(walker)
370
if 'EGG-INFO' in dirs:
371
dirs.remove('EGG-INFO')
372
yield base, dirs, files
373
for bdf in walker:
374
yield bdf
375
376
377
def analyze_egg(egg_dir, stubs):
378
# check for existing flag in EGG-INFO
379
for flag, fn in safety_flags.items():
380
if os.path.exists(os.path.join(egg_dir, 'EGG-INFO', fn)):
381
return flag
382
if not can_scan():
383
return False
384
safe = True
385
for base, dirs, files in walk_egg(egg_dir):
386
for name in files:
387
if name.endswith('.py') or name.endswith('.pyw'):
388
continue
389
elif name.endswith('.pyc') or name.endswith('.pyo'):
390
# always scan, even if we already know we're not safe
391
safe = scan_module(egg_dir, base, name, stubs) and safe
392
return safe
393
394
395
def write_safety_flag(egg_dir, safe):
396
# Write or remove zip safety flag file(s)
397
for flag, fn in safety_flags.items():
398
fn = os.path.join(egg_dir, fn)
399
if os.path.exists(fn):
400
if safe is None or bool(safe) != flag:
401
os.unlink(fn)
402
elif safe is not None and bool(safe) == flag:
403
f = open(fn, 'wt')
404
f.write('\n')
405
f.close()
406
407
408
safety_flags = {
409
True: 'zip-safe',
410
False: 'not-zip-safe',
411
}
412
413
414
def scan_module(egg_dir, base, name, stubs):
415
"""Check whether module possibly uses unsafe-for-zipfile stuff"""
416
417
filename = os.path.join(base, name)
418
if filename[:-1] in stubs:
419
return True # Extension module
420
pkg = base[len(egg_dir) + 1:].replace(os.sep, '.')
421
module = pkg + (pkg and '.' or '') + os.path.splitext(name)[0]
422
if six.PY2:
423
skip = 8 # skip magic & date
424
elif sys.version_info < (3, 7):
425
skip = 12 # skip magic & date & file size
426
else:
427
skip = 16 # skip magic & reserved? & date & file size
428
f = open(filename, 'rb')
429
f.read(skip)
430
code = marshal.load(f)
431
f.close()
432
safe = True
433
symbols = dict.fromkeys(iter_symbols(code))
434
for bad in ['__file__', '__path__']:
435
if bad in symbols:
436
log.warn("%s: module references %s", module, bad)
437
safe = False
438
if 'inspect' in symbols:
439
for bad in [
440
'getsource', 'getabsfile', 'getsourcefile', 'getfile'
441
'getsourcelines', 'findsource', 'getcomments', 'getframeinfo',
442
'getinnerframes', 'getouterframes', 'stack', 'trace'
443
]:
444
if bad in symbols:
445
log.warn("%s: module MAY be using inspect.%s", module, bad)
446
safe = False
447
return safe
448
449
450
def iter_symbols(code):
451
"""Yield names and strings used by `code` and its nested code objects"""
452
for name in code.co_names:
453
yield name
454
for const in code.co_consts:
455
if isinstance(const, six.string_types):
456
yield const
457
elif isinstance(const, CodeType):
458
for name in iter_symbols(const):
459
yield name
460
461
462
def can_scan():
463
if not sys.platform.startswith('java') and sys.platform != 'cli':
464
# CPython, PyPy, etc.
465
return True
466
log.warn("Unable to analyze compiled code on this platform.")
467
log.warn("Please ask the author to include a 'zip_safe'"
468
" setting (either True or False) in the package's setup.py")
469
470
471
# Attribute names of options for commands that might need to be convinced to
472
# install to the egg build directory
473
474
INSTALL_DIRECTORY_ATTRS = [
475
'install_lib', 'install_dir', 'install_data', 'install_base'
476
]
477
478
479
def make_zipfile(zip_filename, base_dir, verbose=0, dry_run=0, compress=True,
480
mode='w'):
481
"""Create a zip file from all the files under 'base_dir'. The output
482
zip file will be named 'base_dir' + ".zip". Uses either the "zipfile"
483
Python module (if available) or the InfoZIP "zip" utility (if installed
484
and found on the default search path). If neither tool is available,
485
raises DistutilsExecError. Returns the name of the output zip file.
486
"""
487
import zipfile
488
489
mkpath(os.path.dirname(zip_filename), dry_run=dry_run)
490
log.info("creating '%s' and adding '%s' to it", zip_filename, base_dir)
491
492
def visit(z, dirname, names):
493
for name in names:
494
path = os.path.normpath(os.path.join(dirname, name))
495
if os.path.isfile(path):
496
p = path[len(base_dir) + 1:]
497
if not dry_run:
498
z.write(path, p)
499
log.debug("adding '%s'", p)
500
501
compression = zipfile.ZIP_DEFLATED if compress else zipfile.ZIP_STORED
502
if not dry_run:
503
z = zipfile.ZipFile(zip_filename, mode, compression=compression)
504
for dirname, dirs, files in sorted_walk(base_dir):
505
visit(z, dirname, files)
506
z.close()
507
else:
508
for dirname, dirs, files in sorted_walk(base_dir):
509
visit(None, dirname, files)
510
return zip_filename
511
512