import importlib.util
import os
import re
import string
import subprocess
import sys
import sysconfig
import functools
from distutils.errors import DistutilsPlatformError, DistutilsByteCompileError
from distutils.dep_util import newer
from distutils.spawn import spawn
from distutils import log
def get_host_platform():
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
return aix_platform(osname, version, release)
return sysconfig.get_platform()
def get_platform():
if os.name == 'nt':
TARGET_TO_PLAT = {
'x86': 'win32',
'x64': 'win-amd64',
'arm': 'win-arm32',
'arm64': 'win-arm64',
}
target = os.environ.get('VSCMD_ARG_TGT_ARCH')
return TARGET_TO_PLAT.get(target) or get_host_platform()
return get_host_platform()
if sys.platform == 'darwin':
_syscfg_macosx_ver = None MACOSX_VERSION_VAR = 'MACOSX_DEPLOYMENT_TARGET'
def _clear_cached_macosx_ver():
global _syscfg_macosx_ver
_syscfg_macosx_ver = None
def get_macosx_target_ver_from_syscfg():
global _syscfg_macosx_ver
if _syscfg_macosx_ver is None:
from distutils import sysconfig
ver = sysconfig.get_config_var(MACOSX_VERSION_VAR) or ''
if ver:
_syscfg_macosx_ver = ver
return _syscfg_macosx_ver
def get_macosx_target_ver():
syscfg_ver = get_macosx_target_ver_from_syscfg()
env_ver = os.environ.get(MACOSX_VERSION_VAR)
if env_ver:
if (
syscfg_ver
and split_version(syscfg_ver) >= [10, 3]
and split_version(env_ver) < [10, 3]
):
my_msg = (
'$' + MACOSX_VERSION_VAR + ' mismatch: '
'now "%s" but "%s" during configure; '
'must use 10.3 or later' % (env_ver, syscfg_ver)
)
raise DistutilsPlatformError(my_msg)
return env_ver
return syscfg_ver
def split_version(s):
return [int(n) for n in s.split('.')]
def convert_path(pathname):
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)
def change_root(new_root, pathname):
if os.name == 'posix':
if not os.path.isabs(pathname):
return os.path.join(new_root, pathname)
else:
return os.path.join(new_root, pathname[1:])
elif os.name == 'nt':
(drive, path) = os.path.splitdrive(pathname)
if path[0] == '\\':
path = path[1:]
return os.path.join(new_root, path)
raise DistutilsPlatformError(f"nothing known about platform '{os.name}'")
@functools.lru_cache()
def check_environ():
if os.name == 'posix' and 'HOME' not in os.environ:
try:
import pwd
os.environ['HOME'] = pwd.getpwuid(os.getuid())[5]
except (ImportError, KeyError):
pass
if 'PLAT' not in os.environ:
os.environ['PLAT'] = get_platform()
def subst_vars(s, local_vars):
check_environ()
lookup = dict(os.environ)
lookup.update((name, str(value)) for name, value in local_vars.items())
try:
return _subst_compat(s).format_map(lookup)
except KeyError as var:
raise ValueError(f"invalid variable {var}")
def _subst_compat(s):
def _subst(match):
return f'{{{match.group(1)}}}'
repl = re.sub(r'\$([a-zA-Z_][a-zA-Z_0-9]*)', _subst, s)
if repl != s:
import warnings
warnings.warn(
"shell/Perl-style substitions are deprecated",
DeprecationWarning,
)
return repl
def grok_environment_error(exc, prefix="error: "):
return prefix + str(exc)
_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)
_squote_re = re.compile(r"'(?:[^'\\]|\\.)*'")
_dquote_re = re.compile(r'"(?:[^"\\]|\\.)*"')
def split_quoted(s):
if _wordchars_re is None:
_init_regex()
s = s.strip()
words = []
pos = 0
while s:
m = _wordchars_re.match(s, pos)
end = m.end()
if end == len(s):
words.append(s[:end])
break
if s[end] in string.whitespace:
words.append(s[:end])
s = s[end:].lstrip()
pos = 0
elif s[end] == '\\':
s = s[:end] + s[end + 1 :]
pos = end + 1
else:
if s[end] == "'": m = _squote_re.match(s, end)
elif s[end] == '"': m = _dquote_re.match(s, end)
else:
raise RuntimeError("this can't happen (bad char '%c')" % s[end])
if m is None:
raise ValueError("bad string (mismatched %s quotes?)" % s[end])
(beg, end) = m.span()
s = s[:beg] + s[beg + 1 : end - 1] + s[end:]
pos = m.end() - 2
if pos >= len(s):
words.append(s)
break
return words
def execute(func, args, msg=None, verbose=0, dry_run=0):
if msg is None:
msg = "{}{!r}".format(func.__name__, args)
if msg[-2:] == ',)': msg = msg[0:-2] + ')'
log.info(msg)
if not dry_run:
func(*args)
def strtobool(val):
val = val.lower()
if val in ('y', 'yes', 't', 'true', 'on', '1'):
return 1
elif val in ('n', 'no', 'f', 'false', 'off', '0'):
return 0
else:
raise ValueError("invalid truth value {!r}".format(val))
def byte_compile( py_files,
optimize=0,
force=0,
prefix=None,
base_dir=None,
verbose=1,
dry_run=0,
direct=None,
):
if sys.dont_write_bytecode:
raise DistutilsByteCompileError('byte-compiling is disabled.')
if direct is None:
direct = __debug__ and optimize == 0
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")
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")
with script:
script.write(
"""\
from distutils.util import byte_compile
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)
"""
% (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)
else:
from py_compile import compile
for file in py_files:
if file[-3:] != ".py":
continue
if optimize >= 0:
opt = '' if optimize == 0 else optimize
cfile = importlib.util.cache_from_source(file, optimization=opt)
else:
cfile = importlib.util.cache_from_source(file)
dfile = file
if prefix:
if file[: len(prefix)] != prefix:
raise ValueError(
"invalid prefix: filename %r doesn't start with %r"
% (file, prefix)
)
dfile = dfile[len(prefix) :]
if base_dir:
dfile = os.path.join(base_dir, dfile)
cfile_base = os.path.basename(cfile)
if direct:
if force or newer(file, cfile):
log.info("byte-compiling %s to %s", file, cfile_base)
if not dry_run:
compile(file, cfile, dfile)
else:
log.debug("skipping byte-compilation of %s to %s", file, cfile_base)
def rfc822_escape(header):
lines = header.split('\n')
sep = '\n' + 8 * ' '
return sep.join(lines)