This commit is contained in:
ton
2024-10-07 10:13:40 +07:00
parent aa1631742f
commit 3a7d696db6
9729 changed files with 1832837 additions and 161742 deletions

View File

@@ -4,19 +4,25 @@ Miscellaneous utility functions -- anything that doesn't fit into
one of the other *util.py modules.
"""
from __future__ import annotations
import functools
import importlib.util
import os
import pathlib
import re
import string
import subprocess
import sys
import sysconfig
import functools
import tempfile
from jaraco.functools import pass_none
from .errors import DistutilsPlatformError, DistutilsByteCompileError
from .dep_util import newer
from .spawn import spawn
from ._log import log
from ._modified import newer
from .errors import DistutilsByteCompileError, DistutilsPlatformError
from .spawn import spawn
def get_host_platform():
@@ -30,18 +36,11 @@ def get_host_platform():
# even with older Python versions when distutils was split out.
# Now it delegates to stdlib sysconfig, but maintains compatibility.
if sys.version_info < (3, 8):
if os.name == 'nt':
if '(arm)' in sys.version.lower():
return 'win-arm32'
if '(arm64)' in sys.version.lower():
return 'win-arm64'
if sys.version_info < (3, 9):
if os.name == "posix" and hasattr(os, 'uname'):
osname, host, release, version, machine = os.uname()
if osname[:3] == "aix":
from .py38compat import aix_platform
from .compat.py38 import aix_platform
return aix_platform(osname, version, release)
@@ -109,8 +108,8 @@ def get_macosx_target_ver():
):
my_msg = (
'$' + MACOSX_VERSION_VAR + ' mismatch: '
'now "%s" but "%s" during configure; '
'must use 10.3 or later' % (env_ver, syscfg_ver)
f'now "{env_ver}" but "{syscfg_ver}" during configure; '
'must use 10.3 or later'
)
raise DistutilsPlatformError(my_msg)
return env_ver
@@ -122,33 +121,23 @@ def split_version(s):
return [int(n) for n in s.split('.')]
def convert_path(pathname):
"""Return 'pathname' as a name that will work on the native filesystem,
i.e. split it on '/' and put it back together again using the current
directory separator. Needed because filenames in the setup script are
always supplied in Unix style, and have to be converted to the local
convention before we can actually use them in the filesystem. Raises
ValueError on non-Unix-ish systems if 'pathname' either starts or
ends with a slash.
@pass_none
def convert_path(pathname: str | os.PathLike) -> str:
r"""
Allow for pathlib.Path inputs, coax to a native path string.
If None is passed, will just pass it through as
Setuptools relies on this behavior.
>>> convert_path(None) is None
True
Removes empty paths.
>>> convert_path('foo/./bar').replace('\\', '/')
'foo/bar'
"""
if os.sep == '/':
return pathname
if not pathname:
return pathname
if pathname[0] == '/':
raise ValueError("path '%s' cannot be absolute" % pathname)
if pathname[-1] == '/':
raise ValueError("path '%s' cannot end with '/'" % pathname)
paths = pathname.split('/')
while '.' in paths:
paths.remove('.')
if not paths:
return os.curdir
return os.path.join(*paths)
# convert_path ()
return os.fspath(pathlib.PurePath(pathname))
def change_root(new_root, pathname):
@@ -165,14 +154,14 @@ def change_root(new_root, pathname):
elif os.name == 'nt':
(drive, path) = os.path.splitdrive(pathname)
if path[0] == '\\':
if path[0] == os.sep:
path = path[1:]
return os.path.join(new_root, path)
raise DistutilsPlatformError(f"nothing known about platform '{os.name}'")
@functools.lru_cache()
@functools.lru_cache
def check_environ():
"""Ensure that 'os.environ' has all the environment variables we
guarantee that users can use in config files, command-line options,
@@ -247,7 +236,7 @@ _wordchars_re = _squote_re = _dquote_re = None
def _init_regex():
global _wordchars_re, _squote_re, _dquote_re
_wordchars_re = re.compile(r'[^\\\'\"%s ]*' % string.whitespace)
_wordchars_re = re.compile(rf'[^\\\'\"{string.whitespace} ]*')
_squote_re = re.compile(r"'(?:[^'\\]|\\.)*'")
_dquote_re = re.compile(r'"(?:[^"\\]|\\.)*"')
@@ -302,7 +291,7 @@ def split_quoted(s):
raise RuntimeError("this can't happen (bad char '%c')" % s[end])
if m is None:
raise ValueError("bad string (mismatched %s quotes?)" % s[end])
raise ValueError(f"bad string (mismatched {s[end]} quotes?)")
(beg, end) = m.span()
s = s[:beg] + s[beg + 1 : end - 1] + s[end:]
@@ -318,7 +307,7 @@ def split_quoted(s):
# split_quoted ()
def execute(func, args, msg=None, verbose=0, dry_run=0):
def execute(func, args, msg=None, verbose=False, dry_run=False):
"""Perform some action that affects the outside world (eg. by
writing to the filesystem). Such actions are special because they
are disabled by the 'dry_run' flag. This method takes care of all
@@ -328,7 +317,7 @@ def execute(func, args, msg=None, verbose=0, dry_run=0):
print.
"""
if msg is None:
msg = "{}{!r}".format(func.__name__, args)
msg = f"{func.__name__}{args!r}"
if msg[-2:] == ',)': # correct for singleton tuple
msg = msg[0:-2] + ')'
@@ -350,17 +339,17 @@ def strtobool(val):
elif val in ('n', 'no', 'f', 'false', 'off', '0'):
return 0
else:
raise ValueError("invalid truth value {!r}".format(val))
raise ValueError(f"invalid truth value {val!r}")
def byte_compile( # noqa: C901
py_files,
optimize=0,
force=0,
force=False,
prefix=None,
base_dir=None,
verbose=1,
dry_run=0,
verbose=True,
dry_run=False,
direct=None,
):
"""Byte-compile a collection of Python source files to .pyc
@@ -412,20 +401,10 @@ def byte_compile( # noqa: C901
# "Indirect" byte-compilation: write a temporary script and then
# run it with the appropriate flags.
if not direct:
try:
from tempfile import mkstemp
(script_fd, script_name) = mkstemp(".py")
except ImportError:
from tempfile import mktemp
(script_fd, script_name) = None, mktemp(".py")
(script_fd, script_name) = tempfile.mkstemp(".py")
log.info("writing byte-compilation script '%s'", script_name)
if not dry_run:
if script_fd is not None:
script = os.fdopen(script_fd, "w")
else:
script = open(script_name, "w")
script = os.fdopen(script_fd, "w", encoding='utf-8')
with script:
script.write(
@@ -447,20 +426,19 @@ files = [
script.write(",\n".join(map(repr, py_files)) + "]\n")
script.write(
"""
byte_compile(files, optimize=%r, force=%r,
prefix=%r, base_dir=%r,
verbose=%r, dry_run=0,
direct=1)
f"""
byte_compile(files, optimize={optimize!r}, force={force!r},
prefix={prefix!r}, base_dir={base_dir!r},
verbose={verbose!r}, dry_run=False,
direct=True)
"""
% (optimize, force, prefix, base_dir, verbose)
)
cmd = [sys.executable]
cmd.extend(subprocess._optim_args_from_interpreter_flags())
cmd.append(script_name)
spawn(cmd, dry_run=dry_run)
execute(os.remove, (script_name,), "removing %s" % script_name, dry_run=dry_run)
execute(os.remove, (script_name,), f"removing {script_name}", dry_run=dry_run)
# "Direct" byte-compilation: use the py_compile module to compile
# right here, right now. Note that the script generated in indirect
@@ -487,8 +465,7 @@ byte_compile(files, optimize=%r, force=%r,
if prefix:
if file[: len(prefix)] != prefix:
raise ValueError(
"invalid prefix: filename %r doesn't start with %r"
% (file, prefix)
f"invalid prefix: filename {file!r} doesn't start with {prefix!r}"
)
dfile = dfile[len(prefix) :]
if base_dir:
@@ -508,6 +485,21 @@ def rfc822_escape(header):
"""Return a version of the string escaped for inclusion in an
RFC-822 header, by ensuring there are 8 spaces space after each newline.
"""
lines = header.split('\n')
sep = '\n' + 8 * ' '
return sep.join(lines)
indent = 8 * " "
lines = header.splitlines(keepends=True)
# Emulate the behaviour of `str.split`
# (the terminal line break in `splitlines` does not result in an extra line):
ends_in_newline = lines and lines[-1].splitlines()[0] != lines[-1]
suffix = indent if ends_in_newline else ""
return indent.join(lines) + suffix
def is_mingw():
"""Returns True if the current platform is mingw.
Python compiled with Mingw-w64 has sys.platform == 'win32' and
get_platform() starts with 'mingw'.
"""
return sys.platform == 'win32' and get_platform().startswith('mingw')