Mercurial > repos > shellac > sam_consensus_v3
comparison env/lib/python3.9/site-packages/setuptools/_distutils/util.py @ 0:4f3585e2f14b draft default tip
"planemo upload commit 60cee0fc7c0cda8592644e1aad72851dec82c959"
author | shellac |
---|---|
date | Mon, 22 Mar 2021 18:12:50 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4f3585e2f14b |
---|---|
1 """distutils.util | |
2 | |
3 Miscellaneous utility functions -- anything that doesn't fit into | |
4 one of the other *util.py modules. | |
5 """ | |
6 | |
7 import os | |
8 import re | |
9 import importlib.util | |
10 import string | |
11 import sys | |
12 from distutils.errors import DistutilsPlatformError | |
13 from distutils.dep_util import newer | |
14 from distutils.spawn import spawn | |
15 from distutils import log | |
16 from distutils.errors import DistutilsByteCompileError | |
17 from .py35compat import _optim_args_from_interpreter_flags | |
18 | |
19 | |
20 def get_host_platform(): | |
21 """Return a string that identifies the current platform. This is used mainly to | |
22 distinguish platform-specific build directories and platform-specific built | |
23 distributions. Typically includes the OS name and version and the | |
24 architecture (as supplied by 'os.uname()'), although the exact information | |
25 included depends on the OS; eg. on Linux, the kernel version isn't | |
26 particularly important. | |
27 | |
28 Examples of returned values: | |
29 linux-i586 | |
30 linux-alpha (?) | |
31 solaris-2.6-sun4u | |
32 | |
33 Windows will return one of: | |
34 win-amd64 (64bit Windows on AMD64 (aka x86_64, Intel64, EM64T, etc) | |
35 win32 (all others - specifically, sys.platform is returned) | |
36 | |
37 For other non-POSIX platforms, currently just returns 'sys.platform'. | |
38 | |
39 """ | |
40 if os.name == 'nt': | |
41 if 'amd64' in sys.version.lower(): | |
42 return 'win-amd64' | |
43 if '(arm)' in sys.version.lower(): | |
44 return 'win-arm32' | |
45 if '(arm64)' in sys.version.lower(): | |
46 return 'win-arm64' | |
47 return sys.platform | |
48 | |
49 # Set for cross builds explicitly | |
50 if "_PYTHON_HOST_PLATFORM" in os.environ: | |
51 return os.environ["_PYTHON_HOST_PLATFORM"] | |
52 | |
53 if os.name != "posix" or not hasattr(os, 'uname'): | |
54 # XXX what about the architecture? NT is Intel or Alpha, | |
55 # Mac OS is M68k or PPC, etc. | |
56 return sys.platform | |
57 | |
58 # Try to distinguish various flavours of Unix | |
59 | |
60 (osname, host, release, version, machine) = os.uname() | |
61 | |
62 # Convert the OS name to lowercase, remove '/' characters, and translate | |
63 # spaces (for "Power Macintosh") | |
64 osname = osname.lower().replace('/', '') | |
65 machine = machine.replace(' ', '_') | |
66 machine = machine.replace('/', '-') | |
67 | |
68 if osname[:5] == "linux": | |
69 # At least on Linux/Intel, 'machine' is the processor -- | |
70 # i386, etc. | |
71 # XXX what about Alpha, SPARC, etc? | |
72 return "%s-%s" % (osname, machine) | |
73 elif osname[:5] == "sunos": | |
74 if release[0] >= "5": # SunOS 5 == Solaris 2 | |
75 osname = "solaris" | |
76 release = "%d.%s" % (int(release[0]) - 3, release[2:]) | |
77 # We can't use "platform.architecture()[0]" because a | |
78 # bootstrap problem. We use a dict to get an error | |
79 # if some suspicious happens. | |
80 bitness = {2147483647:"32bit", 9223372036854775807:"64bit"} | |
81 machine += ".%s" % bitness[sys.maxsize] | |
82 # fall through to standard osname-release-machine representation | |
83 elif osname[:3] == "aix": | |
84 from .py38compat import aix_platform | |
85 return aix_platform(osname, version, release) | |
86 elif osname[:6] == "cygwin": | |
87 osname = "cygwin" | |
88 rel_re = re.compile (r'[\d.]+', re.ASCII) | |
89 m = rel_re.match(release) | |
90 if m: | |
91 release = m.group() | |
92 elif osname[:6] == "darwin": | |
93 import _osx_support, distutils.sysconfig | |
94 osname, release, machine = _osx_support.get_platform_osx( | |
95 distutils.sysconfig.get_config_vars(), | |
96 osname, release, machine) | |
97 | |
98 return "%s-%s-%s" % (osname, release, machine) | |
99 | |
100 def get_platform(): | |
101 if os.name == 'nt': | |
102 TARGET_TO_PLAT = { | |
103 'x86' : 'win32', | |
104 'x64' : 'win-amd64', | |
105 'arm' : 'win-arm32', | |
106 } | |
107 return TARGET_TO_PLAT.get(os.environ.get('VSCMD_ARG_TGT_ARCH')) or get_host_platform() | |
108 else: | |
109 return get_host_platform() | |
110 | |
111 def convert_path (pathname): | |
112 """Return 'pathname' as a name that will work on the native filesystem, | |
113 i.e. split it on '/' and put it back together again using the current | |
114 directory separator. Needed because filenames in the setup script are | |
115 always supplied in Unix style, and have to be converted to the local | |
116 convention before we can actually use them in the filesystem. Raises | |
117 ValueError on non-Unix-ish systems if 'pathname' either starts or | |
118 ends with a slash. | |
119 """ | |
120 if os.sep == '/': | |
121 return pathname | |
122 if not pathname: | |
123 return pathname | |
124 if pathname[0] == '/': | |
125 raise ValueError("path '%s' cannot be absolute" % pathname) | |
126 if pathname[-1] == '/': | |
127 raise ValueError("path '%s' cannot end with '/'" % pathname) | |
128 | |
129 paths = pathname.split('/') | |
130 while '.' in paths: | |
131 paths.remove('.') | |
132 if not paths: | |
133 return os.curdir | |
134 return os.path.join(*paths) | |
135 | |
136 # convert_path () | |
137 | |
138 | |
139 def change_root (new_root, pathname): | |
140 """Return 'pathname' with 'new_root' prepended. If 'pathname' is | |
141 relative, this is equivalent to "os.path.join(new_root,pathname)". | |
142 Otherwise, it requires making 'pathname' relative and then joining the | |
143 two, which is tricky on DOS/Windows and Mac OS. | |
144 """ | |
145 if os.name == 'posix': | |
146 if not os.path.isabs(pathname): | |
147 return os.path.join(new_root, pathname) | |
148 else: | |
149 return os.path.join(new_root, pathname[1:]) | |
150 | |
151 elif os.name == 'nt': | |
152 (drive, path) = os.path.splitdrive(pathname) | |
153 if path[0] == '\\': | |
154 path = path[1:] | |
155 return os.path.join(new_root, path) | |
156 | |
157 else: | |
158 raise DistutilsPlatformError("nothing known about platform '%s'" % os.name) | |
159 | |
160 | |
161 _environ_checked = 0 | |
162 def check_environ (): | |
163 """Ensure that 'os.environ' has all the environment variables we | |
164 guarantee that users can use in config files, command-line options, | |
165 etc. Currently this includes: | |
166 HOME - user's home directory (Unix only) | |
167 PLAT - description of the current platform, including hardware | |
168 and OS (see 'get_platform()') | |
169 """ | |
170 global _environ_checked | |
171 if _environ_checked: | |
172 return | |
173 | |
174 if os.name == 'posix' and 'HOME' not in os.environ: | |
175 try: | |
176 import pwd | |
177 os.environ['HOME'] = pwd.getpwuid(os.getuid())[5] | |
178 except (ImportError, KeyError): | |
179 # bpo-10496: if the current user identifier doesn't exist in the | |
180 # password database, do nothing | |
181 pass | |
182 | |
183 if 'PLAT' not in os.environ: | |
184 os.environ['PLAT'] = get_platform() | |
185 | |
186 _environ_checked = 1 | |
187 | |
188 | |
189 def subst_vars (s, local_vars): | |
190 """Perform shell/Perl-style variable substitution on 'string'. Every | |
191 occurrence of '$' followed by a name is considered a variable, and | |
192 variable is substituted by the value found in the 'local_vars' | |
193 dictionary, or in 'os.environ' if it's not in 'local_vars'. | |
194 'os.environ' is first checked/augmented to guarantee that it contains | |
195 certain values: see 'check_environ()'. Raise ValueError for any | |
196 variables not found in either 'local_vars' or 'os.environ'. | |
197 """ | |
198 check_environ() | |
199 def _subst (match, local_vars=local_vars): | |
200 var_name = match.group(1) | |
201 if var_name in local_vars: | |
202 return str(local_vars[var_name]) | |
203 else: | |
204 return os.environ[var_name] | |
205 | |
206 try: | |
207 return re.sub(r'\$([a-zA-Z_][a-zA-Z_0-9]*)', _subst, s) | |
208 except KeyError as var: | |
209 raise ValueError("invalid variable '$%s'" % var) | |
210 | |
211 # subst_vars () | |
212 | |
213 | |
214 def grok_environment_error (exc, prefix="error: "): | |
215 # Function kept for backward compatibility. | |
216 # Used to try clever things with EnvironmentErrors, | |
217 # but nowadays str(exception) produces good messages. | |
218 return prefix + str(exc) | |
219 | |
220 | |
221 # Needed by 'split_quoted()' | |
222 _wordchars_re = _squote_re = _dquote_re = None | |
223 def _init_regex(): | |
224 global _wordchars_re, _squote_re, _dquote_re | |
225 _wordchars_re = re.compile(r'[^\\\'\"%s ]*' % string.whitespace) | |
226 _squote_re = re.compile(r"'(?:[^'\\]|\\.)*'") | |
227 _dquote_re = re.compile(r'"(?:[^"\\]|\\.)*"') | |
228 | |
229 def split_quoted (s): | |
230 """Split a string up according to Unix shell-like rules for quotes and | |
231 backslashes. In short: words are delimited by spaces, as long as those | |
232 spaces are not escaped by a backslash, or inside a quoted string. | |
233 Single and double quotes are equivalent, and the quote characters can | |
234 be backslash-escaped. The backslash is stripped from any two-character | |
235 escape sequence, leaving only the escaped character. The quote | |
236 characters are stripped from any quoted string. Returns a list of | |
237 words. | |
238 """ | |
239 | |
240 # This is a nice algorithm for splitting up a single string, since it | |
241 # doesn't require character-by-character examination. It was a little | |
242 # bit of a brain-bender to get it working right, though... | |
243 if _wordchars_re is None: _init_regex() | |
244 | |
245 s = s.strip() | |
246 words = [] | |
247 pos = 0 | |
248 | |
249 while s: | |
250 m = _wordchars_re.match(s, pos) | |
251 end = m.end() | |
252 if end == len(s): | |
253 words.append(s[:end]) | |
254 break | |
255 | |
256 if s[end] in string.whitespace: # unescaped, unquoted whitespace: now | |
257 words.append(s[:end]) # we definitely have a word delimiter | |
258 s = s[end:].lstrip() | |
259 pos = 0 | |
260 | |
261 elif s[end] == '\\': # preserve whatever is being escaped; | |
262 # will become part of the current word | |
263 s = s[:end] + s[end+1:] | |
264 pos = end+1 | |
265 | |
266 else: | |
267 if s[end] == "'": # slurp singly-quoted string | |
268 m = _squote_re.match(s, end) | |
269 elif s[end] == '"': # slurp doubly-quoted string | |
270 m = _dquote_re.match(s, end) | |
271 else: | |
272 raise RuntimeError("this can't happen (bad char '%c')" % s[end]) | |
273 | |
274 if m is None: | |
275 raise ValueError("bad string (mismatched %s quotes?)" % s[end]) | |
276 | |
277 (beg, end) = m.span() | |
278 s = s[:beg] + s[beg+1:end-1] + s[end:] | |
279 pos = m.end() - 2 | |
280 | |
281 if pos >= len(s): | |
282 words.append(s) | |
283 break | |
284 | |
285 return words | |
286 | |
287 # split_quoted () | |
288 | |
289 | |
290 def execute (func, args, msg=None, verbose=0, dry_run=0): | |
291 """Perform some action that affects the outside world (eg. by | |
292 writing to the filesystem). Such actions are special because they | |
293 are disabled by the 'dry_run' flag. This method takes care of all | |
294 that bureaucracy for you; all you have to do is supply the | |
295 function to call and an argument tuple for it (to embody the | |
296 "external action" being performed), and an optional message to | |
297 print. | |
298 """ | |
299 if msg is None: | |
300 msg = "%s%r" % (func.__name__, args) | |
301 if msg[-2:] == ',)': # correct for singleton tuple | |
302 msg = msg[0:-2] + ')' | |
303 | |
304 log.info(msg) | |
305 if not dry_run: | |
306 func(*args) | |
307 | |
308 | |
309 def strtobool (val): | |
310 """Convert a string representation of truth to true (1) or false (0). | |
311 | |
312 True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values | |
313 are 'n', 'no', 'f', 'false', 'off', and '0'. Raises ValueError if | |
314 'val' is anything else. | |
315 """ | |
316 val = val.lower() | |
317 if val in ('y', 'yes', 't', 'true', 'on', '1'): | |
318 return 1 | |
319 elif val in ('n', 'no', 'f', 'false', 'off', '0'): | |
320 return 0 | |
321 else: | |
322 raise ValueError("invalid truth value %r" % (val,)) | |
323 | |
324 | |
325 def byte_compile (py_files, | |
326 optimize=0, force=0, | |
327 prefix=None, base_dir=None, | |
328 verbose=1, dry_run=0, | |
329 direct=None): | |
330 """Byte-compile a collection of Python source files to .pyc | |
331 files in a __pycache__ subdirectory. 'py_files' is a list | |
332 of files to compile; any files that don't end in ".py" are silently | |
333 skipped. 'optimize' must be one of the following: | |
334 0 - don't optimize | |
335 1 - normal optimization (like "python -O") | |
336 2 - extra optimization (like "python -OO") | |
337 If 'force' is true, all files are recompiled regardless of | |
338 timestamps. | |
339 | |
340 The source filename encoded in each bytecode file defaults to the | |
341 filenames listed in 'py_files'; you can modify these with 'prefix' and | |
342 'basedir'. 'prefix' is a string that will be stripped off of each | |
343 source filename, and 'base_dir' is a directory name that will be | |
344 prepended (after 'prefix' is stripped). You can supply either or both | |
345 (or neither) of 'prefix' and 'base_dir', as you wish. | |
346 | |
347 If 'dry_run' is true, doesn't actually do anything that would | |
348 affect the filesystem. | |
349 | |
350 Byte-compilation is either done directly in this interpreter process | |
351 with the standard py_compile module, or indirectly by writing a | |
352 temporary script and executing it. Normally, you should let | |
353 'byte_compile()' figure out to use direct compilation or not (see | |
354 the source for details). The 'direct' flag is used by the script | |
355 generated in indirect mode; unless you know what you're doing, leave | |
356 it set to None. | |
357 """ | |
358 | |
359 # Late import to fix a bootstrap issue: _posixsubprocess is built by | |
360 # setup.py, but setup.py uses distutils. | |
361 import subprocess | |
362 | |
363 # nothing is done if sys.dont_write_bytecode is True | |
364 if sys.dont_write_bytecode: | |
365 raise DistutilsByteCompileError('byte-compiling is disabled.') | |
366 | |
367 # First, if the caller didn't force us into direct or indirect mode, | |
368 # figure out which mode we should be in. We take a conservative | |
369 # approach: choose direct mode *only* if the current interpreter is | |
370 # in debug mode and optimize is 0. If we're not in debug mode (-O | |
371 # or -OO), we don't know which level of optimization this | |
372 # interpreter is running with, so we can't do direct | |
373 # byte-compilation and be certain that it's the right thing. Thus, | |
374 # always compile indirectly if the current interpreter is in either | |
375 # optimize mode, or if either optimization level was requested by | |
376 # the caller. | |
377 if direct is None: | |
378 direct = (__debug__ and optimize == 0) | |
379 | |
380 # "Indirect" byte-compilation: write a temporary script and then | |
381 # run it with the appropriate flags. | |
382 if not direct: | |
383 try: | |
384 from tempfile import mkstemp | |
385 (script_fd, script_name) = mkstemp(".py") | |
386 except ImportError: | |
387 from tempfile import mktemp | |
388 (script_fd, script_name) = None, mktemp(".py") | |
389 log.info("writing byte-compilation script '%s'", script_name) | |
390 if not dry_run: | |
391 if script_fd is not None: | |
392 script = os.fdopen(script_fd, "w") | |
393 else: | |
394 script = open(script_name, "w") | |
395 | |
396 with script: | |
397 script.write("""\ | |
398 from distutils.util import byte_compile | |
399 files = [ | |
400 """) | |
401 | |
402 # XXX would be nice to write absolute filenames, just for | |
403 # safety's sake (script should be more robust in the face of | |
404 # chdir'ing before running it). But this requires abspath'ing | |
405 # 'prefix' as well, and that breaks the hack in build_lib's | |
406 # 'byte_compile()' method that carefully tacks on a trailing | |
407 # slash (os.sep really) to make sure the prefix here is "just | |
408 # right". This whole prefix business is rather delicate -- the | |
409 # problem is that it's really a directory, but I'm treating it | |
410 # as a dumb string, so trailing slashes and so forth matter. | |
411 | |
412 #py_files = map(os.path.abspath, py_files) | |
413 #if prefix: | |
414 # prefix = os.path.abspath(prefix) | |
415 | |
416 script.write(",\n".join(map(repr, py_files)) + "]\n") | |
417 script.write(""" | |
418 byte_compile(files, optimize=%r, force=%r, | |
419 prefix=%r, base_dir=%r, | |
420 verbose=%r, dry_run=0, | |
421 direct=1) | |
422 """ % (optimize, force, prefix, base_dir, verbose)) | |
423 | |
424 cmd = [sys.executable] | |
425 cmd.extend(_optim_args_from_interpreter_flags()) | |
426 cmd.append(script_name) | |
427 spawn(cmd, dry_run=dry_run) | |
428 execute(os.remove, (script_name,), "removing %s" % script_name, | |
429 dry_run=dry_run) | |
430 | |
431 # "Direct" byte-compilation: use the py_compile module to compile | |
432 # right here, right now. Note that the script generated in indirect | |
433 # mode simply calls 'byte_compile()' in direct mode, a weird sort of | |
434 # cross-process recursion. Hey, it works! | |
435 else: | |
436 from py_compile import compile | |
437 | |
438 for file in py_files: | |
439 if file[-3:] != ".py": | |
440 # This lets us be lazy and not filter filenames in | |
441 # the "install_lib" command. | |
442 continue | |
443 | |
444 # Terminology from the py_compile module: | |
445 # cfile - byte-compiled file | |
446 # dfile - purported source filename (same as 'file' by default) | |
447 if optimize >= 0: | |
448 opt = '' if optimize == 0 else optimize | |
449 cfile = importlib.util.cache_from_source( | |
450 file, optimization=opt) | |
451 else: | |
452 cfile = importlib.util.cache_from_source(file) | |
453 dfile = file | |
454 if prefix: | |
455 if file[:len(prefix)] != prefix: | |
456 raise ValueError("invalid prefix: filename %r doesn't start with %r" | |
457 % (file, prefix)) | |
458 dfile = dfile[len(prefix):] | |
459 if base_dir: | |
460 dfile = os.path.join(base_dir, dfile) | |
461 | |
462 cfile_base = os.path.basename(cfile) | |
463 if direct: | |
464 if force or newer(file, cfile): | |
465 log.info("byte-compiling %s to %s", file, cfile_base) | |
466 if not dry_run: | |
467 compile(file, cfile, dfile) | |
468 else: | |
469 log.debug("skipping byte-compilation of %s to %s", | |
470 file, cfile_base) | |
471 | |
472 # byte_compile () | |
473 | |
474 def rfc822_escape (header): | |
475 """Return a version of the string escaped for inclusion in an | |
476 RFC-822 header, by ensuring there are 8 spaces space after each newline. | |
477 """ | |
478 lines = header.split('\n') | |
479 sep = '\n' + 8 * ' ' | |
480 return sep.join(lines) | |
481 | |
482 # 2to3 support | |
483 | |
484 def run_2to3(files, fixer_names=None, options=None, explicit=None): | |
485 """Invoke 2to3 on a list of Python files. | |
486 The files should all come from the build area, as the | |
487 modification is done in-place. To reduce the build time, | |
488 only files modified since the last invocation of this | |
489 function should be passed in the files argument.""" | |
490 | |
491 if not files: | |
492 return | |
493 | |
494 # Make this class local, to delay import of 2to3 | |
495 from lib2to3.refactor import RefactoringTool, get_fixers_from_package | |
496 class DistutilsRefactoringTool(RefactoringTool): | |
497 def log_error(self, msg, *args, **kw): | |
498 log.error(msg, *args) | |
499 | |
500 def log_message(self, msg, *args): | |
501 log.info(msg, *args) | |
502 | |
503 def log_debug(self, msg, *args): | |
504 log.debug(msg, *args) | |
505 | |
506 if fixer_names is None: | |
507 fixer_names = get_fixers_from_package('lib2to3.fixes') | |
508 r = DistutilsRefactoringTool(fixer_names, options=options) | |
509 r.refactor(files, write=True) | |
510 | |
511 def copydir_run_2to3(src, dest, template=None, fixer_names=None, | |
512 options=None, explicit=None): | |
513 """Recursively copy a directory, only copying new and changed files, | |
514 running run_2to3 over all newly copied Python modules afterward. | |
515 | |
516 If you give a template string, it's parsed like a MANIFEST.in. | |
517 """ | |
518 from distutils.dir_util import mkpath | |
519 from distutils.file_util import copy_file | |
520 from distutils.filelist import FileList | |
521 filelist = FileList() | |
522 curdir = os.getcwd() | |
523 os.chdir(src) | |
524 try: | |
525 filelist.findall() | |
526 finally: | |
527 os.chdir(curdir) | |
528 filelist.files[:] = filelist.allfiles | |
529 if template: | |
530 for line in template.splitlines(): | |
531 line = line.strip() | |
532 if not line: continue | |
533 filelist.process_template_line(line) | |
534 copied = [] | |
535 for filename in filelist.files: | |
536 outname = os.path.join(dest, filename) | |
537 mkpath(os.path.dirname(outname)) | |
538 res = copy_file(os.path.join(src, filename), outname, update=1) | |
539 if res[1]: copied.append(outname) | |
540 run_2to3([fn for fn in copied if fn.lower().endswith('.py')], | |
541 fixer_names=fixer_names, options=options, explicit=explicit) | |
542 return copied | |
543 | |
544 class Mixin2to3: | |
545 '''Mixin class for commands that run 2to3. | |
546 To configure 2to3, setup scripts may either change | |
547 the class variables, or inherit from individual commands | |
548 to override how 2to3 is invoked.''' | |
549 | |
550 # provide list of fixers to run; | |
551 # defaults to all from lib2to3.fixers | |
552 fixer_names = None | |
553 | |
554 # options dictionary | |
555 options = None | |
556 | |
557 # list of fixers to invoke even though they are marked as explicit | |
558 explicit = None | |
559 | |
560 def run_2to3(self, files): | |
561 return run_2to3(files, self.fixer_names, self.options, self.explicit) |