[pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci
This commit is contained in:
pre-commit-ci[bot] 2024-04-13 00:00:18 +00:00
parent 72ad6dc953
commit f4cd1ba0d6
813 changed files with 66015 additions and 58839 deletions

View file

@ -1,23 +1,22 @@
"""Extensions to the 'distutils' for large or complex distributions"""
from __future__ import annotations
from fnmatch import fnmatchcase
import functools
import os
import re
from fnmatch import fnmatchcase
import _distutils_hack.override # noqa: F401
import distutils.core
import setuptools.version
from distutils.errors import DistutilsOptionError
from distutils.util import convert_path
from ._deprecation_warning import SetuptoolsDeprecationWarning
import setuptools.version
from setuptools.extension import Extension
from setuptools.dist import Distribution
from setuptools.depends import Require
from setuptools.dist import Distribution
from setuptools.extension import Extension
from . import monkey
from ._deprecation_warning import SetuptoolsDeprecationWarning
__all__ = [
@ -64,7 +63,7 @@ class PackageFinder:
convert_path(where),
cls._build_filter('ez_setup', '*__pycache__', *exclude),
cls._build_filter(*include),
)
),
)
@classmethod
@ -179,7 +178,7 @@ class Command(_Command):
return default
elif not isinstance(val, str):
raise DistutilsOptionError(
"'%s' must be a %s (got `%s`)" % (option, what, val)
"'{}' must be a {} (got `{}`)".format(option, what, val),
)
return val
@ -201,7 +200,7 @@ class Command(_Command):
ok = False
if not ok:
raise DistutilsOptionError(
"'%s' must be a list of strings (got %r)" % (option, val)
"'{}' must be a list of strings (got {!r})".format(option, val),
)
def reinitialize_command(self, command, reinit_subcommands=0, **kw):

View file

@ -1,3 +1,6 @@
from __future__ import annotations
class SetuptoolsDeprecationWarning(Warning):
"""
Base class for warning deprecations in ``setuptools``

View file

@ -7,6 +7,7 @@ used from a setup script as
setup (...)
"""
from __future__ import annotations
import sys

View file

@ -6,38 +6,39 @@ for Microsoft Visual Studio 2015.
The module is compatible with VS 2015 and later. You can find legacy support
for older versions in distutils.msvc9compiler and distutils.msvccompiler.
"""
# Written by Perry Stoll
# hacked by Robin Becker and Thomas Heller to do a better job of
# finding DevStudio (through the registry)
# ported to VS 2005 and VS 2008 by Christian Heimes
# ported to VS 2015 by Steve Dower
from __future__ import annotations
import contextlib
import os
import subprocess
import contextlib
import warnings
import unittest.mock
import warnings
with contextlib.suppress(ImportError):
import winreg
from distutils.errors import DistutilsExecError, DistutilsPlatformError, \
CompileError, LibError, LinkError
CompileError, LibError, LinkError
from distutils.ccompiler import CCompiler, gen_lib_options
from distutils import log
from distutils.util import get_platform
from itertools import count
def _find_vc2015():
try:
key = winreg.OpenKeyEx(
winreg.HKEY_LOCAL_MACHINE,
r"Software\Microsoft\VisualStudio\SxS\VC7",
access=winreg.KEY_READ | winreg.KEY_WOW64_32KEY
r'Software\Microsoft\VisualStudio\SxS\VC7',
access=winreg.KEY_READ | winreg.KEY_WOW64_32KEY,
)
except OSError:
log.debug("Visual C++ is not registered")
log.debug('Visual C++ is not registered')
return None, None
best_version = 0
@ -57,6 +58,7 @@ def _find_vc2015():
best_version, best_dir = version, vc_dir
return best_version, best_dir
def _find_vc2017():
"""Returns "15, path" based on the result of invoking vswhere.exe
If no install is found, returns "None, None"
@ -67,35 +69,39 @@ def _find_vc2017():
If vswhere.exe is not available, by definition, VS 2017 is not
installed.
"""
root = os.environ.get("ProgramFiles(x86)") or os.environ.get("ProgramFiles")
root = os.environ.get('ProgramFiles(x86)') or os.environ.get('ProgramFiles')
if not root:
return None, None
try:
path = subprocess.check_output([
os.path.join(root, "Microsoft Visual Studio", "Installer", "vswhere.exe"),
"-latest",
"-prerelease",
"-requires", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
"-property", "installationPath",
"-products", "*",
], encoding="mbcs", errors="strict").strip()
path = subprocess.check_output(
[
os.path.join(root, 'Microsoft Visual Studio', 'Installer', 'vswhere.exe'),
'-latest',
'-prerelease',
'-requires', 'Microsoft.VisualStudio.Component.VC.Tools.x86.x64',
'-property', 'installationPath',
'-products', '*',
], encoding='mbcs', errors='strict',
).strip()
except (subprocess.CalledProcessError, OSError, UnicodeDecodeError):
return None, None
path = os.path.join(path, "VC", "Auxiliary", "Build")
path = os.path.join(path, 'VC', 'Auxiliary', 'Build')
if os.path.isdir(path):
return 15, path
return None, None
PLAT_SPEC_TO_RUNTIME = {
'x86' : 'x86',
'x86_amd64' : 'x64',
'x86_arm' : 'arm',
'x86_arm64' : 'arm64'
'x86': 'x86',
'x86_amd64': 'x64',
'x86_arm': 'arm',
'x86_arm64': 'arm64',
}
def _find_vcvarsall(plat_spec):
# bpo-38597: Removed vcruntime return value
_, best_dir = _find_vc2017()
@ -104,18 +110,19 @@ def _find_vcvarsall(plat_spec):
best_version, best_dir = _find_vc2015()
if not best_dir:
log.debug("No suitable Visual C++ version found")
log.debug('No suitable Visual C++ version found')
return None, None
vcvarsall = os.path.join(best_dir, "vcvarsall.bat")
vcvarsall = os.path.join(best_dir, 'vcvarsall.bat')
if not os.path.isfile(vcvarsall):
log.debug("%s cannot be found", vcvarsall)
log.debug('%s cannot be found', vcvarsall)
return None, None
return vcvarsall, None
def _get_vc_env(plat_spec):
if os.getenv("DISTUTILS_USE_SDK"):
if os.getenv('DISTUTILS_USE_SDK'):
return {
key.lower(): value
for key, value in os.environ.items()
@ -123,17 +130,19 @@ def _get_vc_env(plat_spec):
vcvarsall, _ = _find_vcvarsall(plat_spec)
if not vcvarsall:
raise DistutilsPlatformError("Unable to find vcvarsall.bat")
raise DistutilsPlatformError('Unable to find vcvarsall.bat')
try:
out = subprocess.check_output(
'cmd /u /c "{}" {} && set'.format(vcvarsall, plat_spec),
f'cmd /u /c "{vcvarsall}" {plat_spec} && set',
stderr=subprocess.STDOUT,
).decode('utf-16le', errors='replace')
except subprocess.CalledProcessError as exc:
log.error(exc.output)
raise DistutilsPlatformError("Error executing {}"
.format(exc.cmd))
raise DistutilsPlatformError(
'Error executing {}'
.format(exc.cmd),
)
env = {
key.lower(): value
@ -144,6 +153,7 @@ def _get_vc_env(plat_spec):
return env
def _find_exe(exe, paths=None):
"""Return path to an MSVC executable program.
@ -161,17 +171,19 @@ def _find_exe(exe, paths=None):
return fn
return exe
# A map keyed by get_platform() return values to values accepted by
# 'vcvarsall.bat'. Always cross-compile from x86 to work with the
# lighter-weight MSVC installs that do not include native 64-bit tools.
PLAT_TO_VCVARS = {
'win32' : 'x86',
'win-amd64' : 'x86_amd64',
'win-arm32' : 'x86_arm',
'win-arm64' : 'x86_arm64'
'win32': 'x86',
'win-amd64': 'x86_amd64',
'win-arm32': 'x86_arm',
'win-arm64': 'x86_arm64',
}
class MSVCCompiler(CCompiler) :
class MSVCCompiler(CCompiler):
"""Concrete class that implements an interface to Microsoft Visual C++,
as defined by the CCompiler abstract class."""
@ -192,8 +204,10 @@ class MSVCCompiler(CCompiler) :
# Needed for the filename generation methods provided by the
# base class, CCompiler.
src_extensions = (_c_extensions + _cpp_extensions +
_rc_extensions + _mc_extensions)
src_extensions = (
_c_extensions + _cpp_extensions +
_rc_extensions + _mc_extensions
)
res_extension = '.res'
obj_extension = '.obj'
static_lib_extension = '.lib'
@ -201,9 +215,8 @@ class MSVCCompiler(CCompiler) :
static_lib_format = shared_lib_format = '%s%s'
exe_extension = '.exe'
def __init__(self, verbose=0, dry_run=0, force=0):
CCompiler.__init__ (self, verbose, dry_run, force)
CCompiler.__init__(self, verbose, dry_run, force)
# target platform (.plat_name is consistent with 'bdist')
self.plat_name = None
self.initialized = False
@ -215,25 +228,29 @@ class MSVCCompiler(CCompiler) :
plat_name = get_platform()
# sanity check for platforms to prevent obscure errors later.
if plat_name not in PLAT_TO_VCVARS:
raise DistutilsPlatformError("--plat-name must be one of {}"
.format(tuple(PLAT_TO_VCVARS)))
raise DistutilsPlatformError(
'--plat-name must be one of {}'
.format(tuple(PLAT_TO_VCVARS)),
)
# Get the vcvarsall.bat spec for the requested platform.
plat_spec = PLAT_TO_VCVARS[plat_name]
vc_env = _get_vc_env(plat_spec)
if not vc_env:
raise DistutilsPlatformError("Unable to find a compatible "
"Visual Studio installation.")
raise DistutilsPlatformError(
'Unable to find a compatible '
'Visual Studio installation.',
)
self._paths = vc_env.get('path', '')
paths = self._paths.split(os.pathsep)
self.cc = _find_exe("cl.exe", paths)
self.linker = _find_exe("link.exe", paths)
self.lib = _find_exe("lib.exe", paths)
self.rc = _find_exe("rc.exe", paths) # resource compiler
self.mc = _find_exe("mc.exe", paths) # message compiler
self.mt = _find_exe("mt.exe", paths) # message compiler
self.cc = _find_exe('cl.exe', paths)
self.linker = _find_exe('link.exe', paths)
self.lib = _find_exe('lib.exe', paths)
self.rc = _find_exe('rc.exe', paths) # resource compiler
self.mc = _find_exe('mc.exe', paths) # message compiler
self.mt = _find_exe('mt.exe', paths) # message compiler
for dir in vc_env.get('include', '').split(os.pathsep):
if dir:
@ -248,19 +265,19 @@ class MSVCCompiler(CCompiler) :
# Future releases of Python 3.x will include all past
# versions of vcruntime*.dll for compatibility.
self.compile_options = [
'/nologo', '/O2', '/W3', '/GL', '/DNDEBUG', '/MD'
'/nologo', '/O2', '/W3', '/GL', '/DNDEBUG', '/MD',
]
self.compile_options_debug = [
'/nologo', '/Od', '/MDd', '/Zi', '/W3', '/D_DEBUG'
'/nologo', '/Od', '/MDd', '/Zi', '/W3', '/D_DEBUG',
]
ldflags = [
'/nologo', '/INCREMENTAL:NO', '/LTCG'
'/nologo', '/INCREMENTAL:NO', '/LTCG',
]
ldflags_debug = [
'/nologo', '/INCREMENTAL:NO', '/LTCG', '/DEBUG:FULL'
'/nologo', '/INCREMENTAL:NO', '/LTCG', '/DEBUG:FULL',
]
self.ldflags_exe = [*ldflags, '/MANIFEST:EMBED,ID=1']
@ -286,10 +303,12 @@ class MSVCCompiler(CCompiler) :
# -- Worker methods ------------------------------------------------
def object_filenames(self,
source_filenames,
strip_dir=0,
output_dir=''):
def object_filenames(
self,
source_filenames,
strip_dir=0,
output_dir='',
):
ext_map = {
**{ext: self.obj_extension for ext in self.src_extensions},
**{ext: self.res_extension for ext in self._rc_extensions + self._mc_extensions},
@ -314,19 +333,22 @@ class MSVCCompiler(CCompiler) :
# Better to raise an exception instead of silently continuing
# and later complain about sources and targets having
# different lengths
raise CompileError("Don't know how to compile {}".format(p))
raise CompileError(f"Don't know how to compile {p}")
return list(map(make_out_path, source_filenames))
def compile(self, sources,
output_dir=None, macros=None, include_dirs=None, debug=0,
extra_preargs=None, extra_postargs=None, depends=None):
def compile(
self, sources,
output_dir=None, macros=None, include_dirs=None, debug=0,
extra_preargs=None, extra_postargs=None, depends=None,
):
if not self.initialized:
self.initialize()
compile_info = self._setup_compile(output_dir, macros, include_dirs,
sources, depends, extra_postargs)
compile_info = self._setup_compile(
output_dir, macros, include_dirs,
sources, depends, extra_postargs,
)
macros, objects, extra_postargs, pp_opts, build = compile_info
compile_opts = extra_preargs or []
@ -336,7 +358,6 @@ class MSVCCompiler(CCompiler) :
else:
compile_opts.extend(self.compile_options)
add_cpp_opts = False
for obj in objects:
@ -351,14 +372,14 @@ class MSVCCompiler(CCompiler) :
src = os.path.abspath(src)
if ext in self._c_extensions:
input_opt = "/Tc" + src
input_opt = '/Tc' + src
elif ext in self._cpp_extensions:
input_opt = "/Tp" + src
input_opt = '/Tp' + src
add_cpp_opts = True
elif ext in self._rc_extensions:
# compile .RC to .RES file
input_opt = src
output_opt = "/fo" + obj
output_opt = '/fo' + obj
try:
self.spawn([self.rc] + pp_opts + [output_opt, input_opt])
except DistutilsExecError as msg:
@ -381,24 +402,26 @@ class MSVCCompiler(CCompiler) :
try:
# first compile .MC to .RC and .H file
self.spawn([self.mc, '-h', h_dir, '-r', rc_dir, src])
base, _ = os.path.splitext(os.path.basename (src))
base, _ = os.path.splitext(os.path.basename(src))
rc_file = os.path.join(rc_dir, base + '.rc')
# then compile .RC to .RES file
self.spawn([self.rc, "/fo" + obj, rc_file])
self.spawn([self.rc, '/fo' + obj, rc_file])
except DistutilsExecError as msg:
raise CompileError(msg)
continue
else:
# how to handle this file?
raise CompileError("Don't know how to compile {} to {}"
.format(src, obj))
raise CompileError(
"Don't know how to compile {} to {}"
.format(src, obj),
)
args = [self.cc] + compile_opts + pp_opts
if add_cpp_opts:
args.append('/EHsc')
args.append(input_opt)
args.append("/Fo" + obj)
args.append('/Fo' + obj)
args.extend(extra_postargs)
try:
@ -408,72 +431,84 @@ class MSVCCompiler(CCompiler) :
return objects
def create_static_lib(self,
objects,
output_libname,
output_dir=None,
debug=0,
target_lang=None):
def create_static_lib(
self,
objects,
output_libname,
output_dir=None,
debug=0,
target_lang=None,
):
if not self.initialized:
self.initialize()
objects, output_dir = self._fix_object_args(objects, output_dir)
output_filename = self.library_filename(output_libname,
output_dir=output_dir)
output_filename = self.library_filename(
output_libname,
output_dir=output_dir,
)
if self._need_link(objects, output_filename):
lib_args = objects + ['/OUT:' + output_filename]
if debug:
pass # XXX what goes here?
pass # XXX what goes here?
try:
log.debug('Executing "%s" %s', self.lib, ' '.join(lib_args))
self.spawn([self.lib] + lib_args)
except DistutilsExecError as msg:
raise LibError(msg)
else:
log.debug("skipping %s (up-to-date)", output_filename)
log.debug('skipping %s (up-to-date)', output_filename)
def link(self,
target_desc,
objects,
output_filename,
output_dir=None,
libraries=None,
library_dirs=None,
runtime_library_dirs=None,
export_symbols=None,
debug=0,
extra_preargs=None,
extra_postargs=None,
build_temp=None,
target_lang=None):
def link(
self,
target_desc,
objects,
output_filename,
output_dir=None,
libraries=None,
library_dirs=None,
runtime_library_dirs=None,
export_symbols=None,
debug=0,
extra_preargs=None,
extra_postargs=None,
build_temp=None,
target_lang=None,
):
if not self.initialized:
self.initialize()
objects, output_dir = self._fix_object_args(objects, output_dir)
fixed_args = self._fix_lib_args(libraries, library_dirs,
runtime_library_dirs)
fixed_args = self._fix_lib_args(
libraries, library_dirs,
runtime_library_dirs,
)
libraries, library_dirs, runtime_library_dirs = fixed_args
if runtime_library_dirs:
self.warn("I don't know what to do with 'runtime_library_dirs': "
+ str(runtime_library_dirs))
self.warn(
"I don't know what to do with 'runtime_library_dirs': " +
str(runtime_library_dirs),
)
lib_opts = gen_lib_options(self,
library_dirs, runtime_library_dirs,
libraries)
lib_opts = gen_lib_options(
self,
library_dirs, runtime_library_dirs,
libraries,
)
if output_dir is not None:
output_filename = os.path.join(output_dir, output_filename)
if self._need_link(objects, output_filename):
ldflags = self._ldflags[target_desc, debug]
export_opts = ["/EXPORT:" + sym for sym in (export_symbols or [])]
export_opts = ['/EXPORT:' + sym for sym in (export_symbols or [])]
ld_args = (ldflags + lib_opts + export_opts +
objects + ['/OUT:' + output_filename])
ld_args = (
ldflags + lib_opts + export_opts +
objects + ['/OUT:' + output_filename]
)
# The MSVC linker generates .lib and .exp files, which cannot be
# suppressed by any linker switches. The .lib files may even be
@ -483,11 +518,13 @@ class MSVCCompiler(CCompiler) :
build_temp = os.path.dirname(objects[0])
if export_symbols is not None:
(dll_name, dll_ext) = os.path.splitext(
os.path.basename(output_filename))
os.path.basename(output_filename),
)
implib_file = os.path.join(
build_temp,
self.library_filename(dll_name))
ld_args.append ('/IMPLIB:' + implib_file)
self.library_filename(dll_name),
)
ld_args.append('/IMPLIB:' + implib_file)
if extra_preargs:
ld_args[:0] = extra_preargs
@ -502,7 +539,7 @@ class MSVCCompiler(CCompiler) :
except DistutilsExecError as msg:
raise LinkError(msg)
else:
log.debug("skipping %s (up-to-date)", output_filename)
log.debug('skipping %s (up-to-date)', output_filename)
def spawn(self, cmd):
env = dict(os.environ, PATH=self._paths)
@ -526,7 +563,8 @@ class MSVCCompiler(CCompiler) :
else:
return
warnings.warn(
"Fallback spawn triggered. Please update distutils monkeypatch.")
'Fallback spawn triggered. Please update distutils monkeypatch.',
)
with unittest.mock.patch('os.environ', env):
bag.value = super().spawn(cmd)
@ -535,11 +573,12 @@ class MSVCCompiler(CCompiler) :
# ccompiler.py.
def library_dir_option(self, dir):
return "/LIBPATH:" + dir
return '/LIBPATH:' + dir
def runtime_library_dir_option(self, dir):
raise DistutilsPlatformError(
"don't know how to set runtime library search path for MSVC")
"don't know how to set runtime library search path for MSVC",
)
def library_option(self, lib):
return self.library_filename(lib)
@ -548,7 +587,7 @@ class MSVCCompiler(CCompiler) :
# Prefer a debugging library if found (and requested), but deal
# with it if we don't have one.
if debug:
try_names = [lib + "_d", lib]
try_names = [lib + '_d', lib]
else:
try_names = [lib]
for dir in dirs:

View file

@ -2,10 +2,11 @@
Utility functions for creating archive files (tarballs, zip files,
that sort of thing)."""
from __future__ import annotations
import os
from warnings import warn
import sys
from warnings import warn
try:
import zipfile
@ -28,6 +29,7 @@ try:
except ImportError:
getgrnam = None
def _get_gid(name):
"""Returns a gid, given a group name."""
if getgrnam is None or name is None:
@ -40,6 +42,7 @@ def _get_gid(name):
return result[2]
return None
def _get_uid(name):
"""Returns an uid, given a user name."""
if getpwnam is None or name is None:
@ -52,8 +55,11 @@ def _get_uid(name):
return result[2]
return None
def make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0,
owner=None, group=None):
def make_tarball(
base_name, base_dir, compress='gzip', verbose=0, dry_run=0,
owner=None, group=None,
):
"""Create a (possibly compressed) tar file from all the files under
'base_dir'.
@ -69,16 +75,21 @@ def make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0,
Returns the output filename.
"""
tar_compression = {'gzip': 'gz', 'bzip2': 'bz2', 'xz': 'xz', None: '',
'compress': ''}
compress_ext = {'gzip': '.gz', 'bzip2': '.bz2', 'xz': '.xz',
'compress': '.Z'}
tar_compression = {
'gzip': 'gz', 'bzip2': 'bz2', 'xz': 'xz', None: '',
'compress': '',
}
compress_ext = {
'gzip': '.gz', 'bzip2': '.bz2', 'xz': '.xz',
'compress': '.Z',
}
# flags for compression program, each element of list will be an argument
if compress is not None and compress not in compress_ext.keys():
raise ValueError(
"bad value for 'compress': must be None, 'gzip', 'bzip2', "
"'xz' or 'compress'")
"bad value for 'compress': must be None, 'gzip', 'bzip2', "
"'xz' or 'compress'",
)
archive_name = base_name + '.tar'
if compress != 'compress':
@ -124,6 +135,7 @@ def make_tarball(base_name, base_dir, compress="gzip", verbose=0, dry_run=0,
return archive_name
def make_zipfile(base_name, base_dir, verbose=0, dry_run=0):
"""Create a zip file from all the files under 'base_dir'.
@ -133,38 +145,50 @@ def make_zipfile(base_name, base_dir, verbose=0, dry_run=0):
available, raises DistutilsExecError. Returns the name of the output zip
file.
"""
zip_filename = base_name + ".zip"
zip_filename = base_name + '.zip'
mkpath(os.path.dirname(zip_filename), dry_run=dry_run)
# If zipfile module is not available, try spawning an external
# 'zip' command.
if zipfile is None:
if verbose:
zipoptions = "-r"
zipoptions = '-r'
else:
zipoptions = "-rq"
zipoptions = '-rq'
try:
spawn(["zip", zipoptions, zip_filename, base_dir],
dry_run=dry_run)
spawn(
['zip', zipoptions, zip_filename, base_dir],
dry_run=dry_run,
)
except DistutilsExecError:
# XXX really should distinguish between "couldn't find
# external 'zip' command" and "zip failed".
raise DistutilsExecError(("unable to create zip file '%s': "
"could neither import the 'zipfile' module nor "
"find a standalone zip utility") % zip_filename)
raise DistutilsExecError(
(
"unable to create zip file '%s': "
"could neither import the 'zipfile' module nor "
'find a standalone zip utility'
) % zip_filename,
)
else:
log.info("creating '%s' and adding '%s' to it",
zip_filename, base_dir)
log.info(
"creating '%s' and adding '%s' to it",
zip_filename, base_dir,
)
if not dry_run:
try:
zip = zipfile.ZipFile(zip_filename, "w",
compression=zipfile.ZIP_DEFLATED)
zip = zipfile.ZipFile(
zip_filename, 'w',
compression=zipfile.ZIP_DEFLATED,
)
except RuntimeError:
zip = zipfile.ZipFile(zip_filename, "w",
compression=zipfile.ZIP_STORED)
zip = zipfile.ZipFile(
zip_filename, 'w',
compression=zipfile.ZIP_STORED,
)
with zip:
if base_dir != os.curdir:
@ -184,14 +208,16 @@ def make_zipfile(base_name, base_dir, verbose=0, dry_run=0):
return zip_filename
ARCHIVE_FORMATS = {
'gztar': (make_tarball, [('compress', 'gzip')], "gzip'ed tar-file"),
'bztar': (make_tarball, [('compress', 'bzip2')], "bzip2'ed tar-file"),
'xztar': (make_tarball, [('compress', 'xz')], "xz'ed tar-file"),
'ztar': (make_tarball, [('compress', 'compress')], "compressed tar file"),
'tar': (make_tarball, [('compress', None)], "uncompressed tar file"),
'zip': (make_zipfile, [],"ZIP file")
}
'ztar': (make_tarball, [('compress', 'compress')], 'compressed tar file'),
'tar': (make_tarball, [('compress', None)], 'uncompressed tar file'),
'zip': (make_zipfile, [], 'ZIP file'),
}
def check_archive_formats(formats):
"""Returns the first format from the 'format' list that is unknown.
@ -203,8 +229,11 @@ def check_archive_formats(formats):
return format
return None
def make_archive(base_name, format, root_dir=None, base_dir=None, verbose=0,
dry_run=0, owner=None, group=None):
def make_archive(
base_name, format, root_dir=None, base_dir=None, verbose=0,
dry_run=0, owner=None, group=None,
):
"""Create an archive file (eg. zip or tar).
'base_name' is the name of the file to create, minus any format-specific

View file

@ -3,26 +3,28 @@
Contains BorlandCCompiler, an implementation of the abstract CCompiler class
for the Borland C++ compiler.
"""
# This implementation by Lyle Johnson, based on the original msvccompiler.py
# module and using the directions originally published by Gordon Williams.
# XXX looks like there's a LOT of overlap between these two classes:
# someone should sit down and factor out the common code as
# WindowsCCompiler! --GPW
from __future__ import annotations
import os
from distutils.errors import \
DistutilsExecError, \
CompileError, LibError, LinkError, UnknownFileError
from distutils.ccompiler import \
CCompiler, gen_preprocess_options
from distutils.file_util import write_file
from distutils.dep_util import newer
from distutils import log
class BCPPCompiler(CCompiler) :
from distutils import log
from distutils.ccompiler import CCompiler
from distutils.ccompiler import gen_preprocess_options
from distutils.dep_util import newer
from distutils.errors import CompileError
from distutils.errors import DistutilsExecError
from distutils.errors import LibError
from distutils.errors import LinkError
from distutils.errors import UnknownFileError
from distutils.file_util import write_file
class BCPPCompiler(CCompiler):
"""Concrete class that implements an interface to the Borland C/C++
compiler, as defined by the CCompiler abstract class.
"""
@ -49,21 +51,22 @@ class BCPPCompiler(CCompiler) :
static_lib_format = shared_lib_format = '%s%s'
exe_extension = '.exe'
def __init__(
self,
verbose=0,
dry_run=0,
force=0,
):
def __init__ (self,
verbose=0,
dry_run=0,
force=0):
CCompiler.__init__ (self, verbose, dry_run, force)
CCompiler.__init__(self, verbose, dry_run, force)
# These executables are assumed to all be in the path.
# Borland doesn't seem to use any special registry settings to
# indicate their installation locations.
self.cc = "bcc32.exe"
self.linker = "ilink32.exe"
self.lib = "tlib.exe"
self.cc = 'bcc32.exe'
self.linker = 'ilink32.exe'
self.lib = 'tlib.exe'
self.preprocess_options = None
self.compile_options = ['/tWM', '/O2', '/q', '/g0']
@ -73,24 +76,27 @@ class BCPPCompiler(CCompiler) :
self.ldflags_shared_debug = ['/Tpd', '/Gn', '/q', '/x']
self.ldflags_static = []
self.ldflags_exe = ['/Gn', '/q', '/x']
self.ldflags_exe_debug = ['/Gn', '/q', '/x','/r']
self.ldflags_exe_debug = ['/Gn', '/q', '/x', '/r']
# -- Worker methods ------------------------------------------------
def compile(self, sources,
output_dir=None, macros=None, include_dirs=None, debug=0,
extra_preargs=None, extra_postargs=None, depends=None):
def compile(
self, sources,
output_dir=None, macros=None, include_dirs=None, debug=0,
extra_preargs=None, extra_postargs=None, depends=None,
):
macros, objects, extra_postargs, pp_opts, build = \
self._setup_compile(output_dir, macros, include_dirs, sources,
depends, extra_postargs)
self._setup_compile(
output_dir, macros, include_dirs, sources,
depends, extra_postargs,
)
compile_opts = extra_preargs or []
compile_opts.append ('-c')
compile_opts.append('-c')
if debug:
compile_opts.extend (self.compile_options_debug)
compile_opts.extend(self.compile_options_debug)
else:
compile_opts.extend (self.compile_options)
compile_opts.extend(self.compile_options)
for obj in objects:
try:
@ -106,35 +112,35 @@ class BCPPCompiler(CCompiler) :
if ext == '.res':
# This is already a binary file -- skip it.
continue # the 'for' loop
continue # the 'for' loop
if ext == '.rc':
# This needs to be compiled to a .res file -- do it now.
try:
self.spawn (["brcc32", "-fo", obj, src])
self.spawn(['brcc32', '-fo', obj, src])
except DistutilsExecError as msg:
raise CompileError(msg)
continue # the 'for' loop
continue # the 'for' loop
# The next two are both for the real compiler.
if ext in self._c_extensions:
input_opt = ""
input_opt = ''
elif ext in self._cpp_extensions:
input_opt = "-P"
input_opt = '-P'
else:
# Unknown file type -- no extra options. The compiler
# will probably fail, but let it just in case this is a
# file the compiler recognizes even if we don't.
input_opt = ""
input_opt = ''
output_opt = "-o" + obj
output_opt = '-o' + obj
# Compiler command line syntax is: "bcc32 [options] file(s)".
# Note that the source file names must appear at the end of
# the command line.
try:
self.spawn ([self.cc] + compile_opts + pp_opts +
[input_opt, output_opt] +
extra_postargs + [src])
self.spawn([self.cc] + compile_opts + pp_opts +
[input_opt, output_opt] +
extra_postargs + [src])
except DistutilsExecError as msg:
raise CompileError(msg)
@ -142,62 +148,66 @@ class BCPPCompiler(CCompiler) :
# compile ()
def create_static_lib(
self,
objects,
output_libname,
output_dir=None,
debug=0,
target_lang=None,
):
def create_static_lib (self,
objects,
output_libname,
output_dir=None,
debug=0,
target_lang=None):
(objects, output_dir) = self._fix_object_args (objects, output_dir)
(objects, output_dir) = self._fix_object_args(objects, output_dir)
output_filename = \
self.library_filename (output_libname, output_dir=output_dir)
self.library_filename(output_libname, output_dir=output_dir)
if self._need_link (objects, output_filename):
if self._need_link(objects, output_filename):
lib_args = [output_filename, '/u'] + objects
if debug:
pass # XXX what goes here?
try:
self.spawn ([self.lib] + lib_args)
self.spawn([self.lib] + lib_args)
except DistutilsExecError as msg:
raise LibError(msg)
else:
log.debug("skipping %s (up-to-date)", output_filename)
log.debug('skipping %s (up-to-date)', output_filename)
# create_static_lib ()
def link (self,
target_desc,
objects,
output_filename,
output_dir=None,
libraries=None,
library_dirs=None,
runtime_library_dirs=None,
export_symbols=None,
debug=0,
extra_preargs=None,
extra_postargs=None,
build_temp=None,
target_lang=None):
def link(
self,
target_desc,
objects,
output_filename,
output_dir=None,
libraries=None,
library_dirs=None,
runtime_library_dirs=None,
export_symbols=None,
debug=0,
extra_preargs=None,
extra_postargs=None,
build_temp=None,
target_lang=None,
):
# XXX this ignores 'build_temp'! should follow the lead of
# msvccompiler.py
(objects, output_dir) = self._fix_object_args (objects, output_dir)
(objects, output_dir) = self._fix_object_args(objects, output_dir)
(libraries, library_dirs, runtime_library_dirs) = \
self._fix_lib_args (libraries, library_dirs, runtime_library_dirs)
self._fix_lib_args(libraries, library_dirs, runtime_library_dirs)
if runtime_library_dirs:
log.warn("I don't know what to do with 'runtime_library_dirs': %s",
str(runtime_library_dirs))
log.warn(
"I don't know what to do with 'runtime_library_dirs': %s",
str(runtime_library_dirs),
)
if output_dir is not None:
output_filename = os.path.join (output_dir, output_filename)
output_filename = os.path.join(output_dir, output_filename)
if self._need_link (objects, output_filename):
if self._need_link(objects, output_filename):
# Figure out linker args based on type of target.
if target_desc == CCompiler.EXECUTABLE:
@ -213,20 +223,21 @@ class BCPPCompiler(CCompiler) :
else:
ld_args = self.ldflags_shared[:]
# Create a temporary exports file for use by the linker
if export_symbols is None:
def_file = ''
else:
head, tail = os.path.split (output_filename)
modname, ext = os.path.splitext (tail)
temp_dir = os.path.dirname(objects[0]) # preserve tree structure
def_file = os.path.join (temp_dir, '%s.def' % modname)
head, tail = os.path.split(output_filename)
modname, ext = os.path.splitext(tail)
temp_dir = os.path.dirname(objects[0]) # preserve tree structure
def_file = os.path.join(temp_dir, '%s.def' % modname)
contents = ['EXPORTS']
for sym in (export_symbols or []):
contents.append(' %s=_%s' % (sym, sym))
self.execute(write_file, (def_file, contents),
"writing %s" % def_file)
contents.append(' {}=_{}'.format(sym, sym))
self.execute(
write_file, (def_file, contents),
'writing %s' % def_file,
)
# Borland C++ has problems with '/' in paths
objects2 = map(os.path.normpath, objects)
@ -241,10 +252,9 @@ class BCPPCompiler(CCompiler) :
else:
objects.append(file)
for l in library_dirs:
ld_args.append("/L%s" % os.path.normpath(l))
ld_args.append("/L.") # we sometimes use relative paths
ld_args.append('/L%s' % os.path.normpath(l))
ld_args.append('/L.') # we sometimes use relative paths
# list of object files
ld_args.extend(objects)
@ -260,7 +270,7 @@ class BCPPCompiler(CCompiler) :
# them. Arghghh!. Apparently it works fine as coded...
# name of dll/exe file
ld_args.extend([',',output_filename])
ld_args.extend([',', output_filename])
# no map file and start libraries
ld_args.append(',,')
@ -276,36 +286,34 @@ class BCPPCompiler(CCompiler) :
ld_args.append(libfile)
# some default libraries
ld_args.append ('import32')
ld_args.append ('cw32mt')
ld_args.append('import32')
ld_args.append('cw32mt')
# def file for export symbols
ld_args.extend([',',def_file])
ld_args.extend([',', def_file])
# add resource files
ld_args.append(',')
ld_args.extend(resources)
if extra_preargs:
ld_args[:0] = extra_preargs
if extra_postargs:
ld_args.extend(extra_postargs)
self.mkpath (os.path.dirname (output_filename))
self.mkpath(os.path.dirname(output_filename))
try:
self.spawn ([self.linker] + ld_args)
self.spawn([self.linker] + ld_args)
except DistutilsExecError as msg:
raise LinkError(msg)
else:
log.debug("skipping %s (up-to-date)", output_filename)
log.debug('skipping %s (up-to-date)', output_filename)
# link ()
# -- Miscellaneous methods -----------------------------------------
def find_library_file (self, dirs, lib, debug=0):
def find_library_file(self, dirs, lib, debug=0):
# List of effective library names to try, in order of preference:
# xxx_bcpp.lib is better than xxx.lib
# and xxx_d.lib is better than xxx.lib if debug is set
@ -316,10 +324,10 @@ class BCPPCompiler(CCompiler) :
# compiler they care about, since (almost?) every Windows compiler
# seems to have a different format for static libraries.
if debug:
dlib = (lib + "_d")
try_names = (dlib + "_bcpp", lib + "_bcpp", dlib, lib)
dlib = (lib + '_d')
try_names = (dlib + '_bcpp', lib + '_bcpp', dlib, lib)
else:
try_names = (lib + "_bcpp", lib)
try_names = (lib + '_bcpp', lib)
for dir in dirs:
for name in try_names:
@ -331,40 +339,51 @@ class BCPPCompiler(CCompiler) :
return None
# overwrite the one from CCompiler to support rc and res-files
def object_filenames (self,
source_filenames,
strip_dir=0,
output_dir=''):
if output_dir is None: output_dir = ''
def object_filenames(
self,
source_filenames,
strip_dir=0,
output_dir='',
):
if output_dir is None:
output_dir = ''
obj_names = []
for src_name in source_filenames:
# use normcase to make sure '.rc' is really '.rc' and not '.RC'
(base, ext) = os.path.splitext (os.path.normcase(src_name))
if ext not in (self.src_extensions + ['.rc','.res']):
raise UnknownFileError("unknown file type '%s' (from '%s')" % \
(ext, src_name))
(base, ext) = os.path.splitext(os.path.normcase(src_name))
if ext not in (self.src_extensions + ['.rc', '.res']):
raise UnknownFileError(
"unknown file type '%s' (from '%s')" %
(ext, src_name),
)
if strip_dir:
base = os.path.basename (base)
base = os.path.basename(base)
if ext == '.res':
# these can go unchanged
obj_names.append (os.path.join (output_dir, base + ext))
obj_names.append(os.path.join(output_dir, base + ext))
elif ext == '.rc':
# these need to be compiled to .res-files
obj_names.append (os.path.join (output_dir, base + '.res'))
obj_names.append(os.path.join(output_dir, base + '.res'))
else:
obj_names.append (os.path.join (output_dir,
base + self.obj_extension))
obj_names.append(
os.path.join(
output_dir,
base + self.obj_extension,
),
)
return obj_names
# object_filenames ()
def preprocess (self,
source,
output_file=None,
macros=None,
include_dirs=None,
extra_preargs=None,
extra_postargs=None):
def preprocess(
self,
source,
output_file=None,
macros=None,
include_dirs=None,
extra_preargs=None,
extra_postargs=None,
):
(_, macros, include_dirs) = \
self._fix_compile_args(None, macros, include_dirs)

View file

@ -2,15 +2,21 @@
Contains CCompiler, an abstract base class that defines the interface
for the Distutils compiler abstraction model."""
from __future__ import annotations
import os
import re
import sys
import sys, os, re
from distutils.errors import *
from distutils.spawn import spawn
from distutils.file_util import move_file
from distutils.dir_util import mkpath
from distutils.dep_util import newer_group
from distutils.util import split_quoted, execute
from distutils import log
from distutils.dep_util import newer_group
from distutils.dir_util import mkpath
from distutils.errors import *
from distutils.file_util import move_file
from distutils.spawn import spawn
from distutils.util import execute
from distutils.util import split_quoted
class CCompiler:
"""Abstract base class to define the interface that must be implemented
@ -56,7 +62,6 @@ class CCompiler:
# think this is useless without the ability to null out the
# library search path anyways.
# Subclasses that rely on the standard filename generation methods
# implemented below should override these; see the comment near
# those methods ('object_filenames()' et. al.) for details:
@ -74,13 +79,14 @@ class CCompiler:
# what language to use when mixing source types. For example, if some
# extension has two files with ".c" extension, and one with ".cpp", it
# is still linked as c++.
language_map = {".c" : "c",
".cc" : "c++",
".cpp" : "c++",
".cxx" : "c++",
".m" : "objc",
}
language_order = ["c++", "objc", "c"]
language_map = {
'.c': 'c',
'.cc': 'c++',
'.cpp': 'c++',
'.cxx': 'c++',
'.m': 'objc',
}
language_order = ['c++', 'objc', 'c']
def __init__(self, verbose=0, dry_run=0, force=0):
self.dry_run = dry_run
@ -146,8 +152,10 @@ class CCompiler:
for key in kwargs:
if key not in self.executables:
raise ValueError("unknown executable '%s' for class %s" %
(key, self.__class__.__name__))
raise ValueError(
"unknown executable '%s' for class %s" %
(key, self.__class__.__name__),
)
self.set_executable(key, kwargs[key])
def set_executable(self, key, value):
@ -170,14 +178,19 @@ class CCompiler:
nothing if all definitions are OK, raise TypeError otherwise.
"""
for defn in definitions:
if not (isinstance(defn, tuple) and
(len(defn) in (1, 2) and
(isinstance (defn[1], str) or defn[1] is None)) and
isinstance (defn[0], str)):
raise TypeError(("invalid macro definition '%s': " % defn) + \
"must be tuple (string,), (string, string), or " + \
"(string, None)")
if not (
isinstance(defn, tuple) and
(
len(defn) in (1, 2) and
(isinstance(defn[1], str) or defn[1] is None)
) and
isinstance(defn[0], str)
):
raise TypeError(
("invalid macro definition '%s': " % defn) +
'must be tuple (string,), (string, string), or ' +
'(string, None)',
)
# -- Bookkeeping methods -------------------------------------------
@ -190,7 +203,7 @@ class CCompiler:
"""
# Delete from the list of macro definitions/undefinitions if
# already there (so that this one will take precedence).
i = self._find_macro (name)
i = self._find_macro(name)
if i is not None:
del self.macros[i]
@ -207,7 +220,7 @@ class CCompiler:
"""
# Delete from the list of macro definitions/undefinitions if
# already there (so that this one will take precedence).
i = self._find_macro (name)
i = self._find_macro(name)
if i is not None:
del self.macros[i]
@ -301,14 +314,15 @@ class CCompiler:
"""
self.objects = objects[:]
# -- Private utility methods --------------------------------------
# (here for the convenience of subclasses)
# Helper method to prep compiler in subclass compile() methods
def _setup_compile(self, outdir, macros, incdirs, sources, depends,
extra):
def _setup_compile(
self, outdir, macros, incdirs, sources, depends,
extra,
):
"""Process arguments and decide which source files to compile."""
if outdir is None:
outdir = self.output_dir
@ -328,14 +342,17 @@ class CCompiler:
incdirs = list(incdirs) + (self.include_dirs or [])
else:
raise TypeError(
"'include_dirs' (if supplied) must be a list of strings")
"'include_dirs' (if supplied) must be a list of strings",
)
if extra is None:
extra = []
# Get the list of expected output (object) files
objects = self.object_filenames(sources, strip_dir=0,
output_dir=outdir)
objects = self.object_filenames(
sources, strip_dir=0,
output_dir=outdir,
)
assert len(objects) == len(sources)
pp_opts = gen_preprocess_options(macros, incdirs)
@ -387,7 +404,8 @@ class CCompiler:
include_dirs = list(include_dirs) + (self.include_dirs or [])
else:
raise TypeError(
"'include_dirs' (if supplied) must be a list of strings")
"'include_dirs' (if supplied) must be a list of strings",
)
return output_dir, macros, include_dirs
@ -434,27 +452,33 @@ class CCompiler:
if libraries is None:
libraries = self.libraries
elif isinstance(libraries, (list, tuple)):
libraries = list (libraries) + (self.libraries or [])
libraries = list(libraries) + (self.libraries or [])
else:
raise TypeError(
"'libraries' (if supplied) must be a list of strings")
"'libraries' (if supplied) must be a list of strings",
)
if library_dirs is None:
library_dirs = self.library_dirs
elif isinstance(library_dirs, (list, tuple)):
library_dirs = list (library_dirs) + (self.library_dirs or [])
library_dirs = list(library_dirs) + (self.library_dirs or [])
else:
raise TypeError(
"'library_dirs' (if supplied) must be a list of strings")
"'library_dirs' (if supplied) must be a list of strings",
)
if runtime_library_dirs is None:
runtime_library_dirs = self.runtime_library_dirs
elif isinstance(runtime_library_dirs, (list, tuple)):
runtime_library_dirs = (list(runtime_library_dirs) +
(self.runtime_library_dirs or []))
runtime_library_dirs = (
list(runtime_library_dirs) +
(self.runtime_library_dirs or [])
)
else:
raise TypeError("'runtime_library_dirs' (if supplied) "
"must be a list of strings")
raise TypeError(
"'runtime_library_dirs' (if supplied) "
'must be a list of strings',
)
return (libraries, library_dirs, runtime_library_dirs)
@ -466,9 +490,9 @@ class CCompiler:
return True
else:
if self.dry_run:
newer = newer_group (objects, output_file, missing='newer')
newer = newer_group(objects, output_file, missing='newer')
else:
newer = newer_group (objects, output_file)
newer = newer_group(objects, output_file)
return newer
def detect_language(self, sources):
@ -491,12 +515,13 @@ class CCompiler:
pass
return lang
# -- Worker methods ------------------------------------------------
# (must be implemented by subclasses)
def preprocess(self, source, output_file=None, macros=None,
include_dirs=None, extra_preargs=None, extra_postargs=None):
def preprocess(
self, source, output_file=None, macros=None,
include_dirs=None, extra_preargs=None, extra_postargs=None,
):
"""Preprocess a single C/C++ source file, named in 'source'.
Output will be written to file named 'output_file', or stdout if
'output_file' not supplied. 'macros' is a list of macro
@ -508,9 +533,11 @@ class CCompiler:
"""
pass
def compile(self, sources, output_dir=None, macros=None,
include_dirs=None, debug=0, extra_preargs=None,
extra_postargs=None, depends=None):
def compile(
self, sources, output_dir=None, macros=None,
include_dirs=None, debug=0, extra_preargs=None,
extra_postargs=None, depends=None,
):
"""Compile one or more source files.
'sources' must be a list of filenames, most likely C/C++
@ -562,8 +589,10 @@ class CCompiler:
# A concrete compiler class can either override this method
# entirely or implement _compile().
macros, objects, extra_postargs, pp_opts, build = \
self._setup_compile(output_dir, macros, include_dirs, sources,
depends, extra_postargs)
self._setup_compile(
output_dir, macros, include_dirs, sources,
depends, extra_postargs,
)
cc_args = self._get_cc_args(pp_opts, debug, extra_preargs)
for obj in objects:
@ -582,8 +611,10 @@ class CCompiler:
# should implement _compile().
pass
def create_static_lib(self, objects, output_libname, output_dir=None,
debug=0, target_lang=None):
def create_static_lib(
self, objects, output_libname, output_dir=None,
debug=0, target_lang=None,
):
"""Link a bunch of stuff together to create a static library file.
The "bunch of stuff" consists of the list of object files supplied
as 'objects', the extra object files supplied to
@ -608,26 +639,27 @@ class CCompiler:
"""
pass
# values for target_desc parameter in link()
SHARED_OBJECT = "shared_object"
SHARED_LIBRARY = "shared_library"
EXECUTABLE = "executable"
SHARED_OBJECT = 'shared_object'
SHARED_LIBRARY = 'shared_library'
EXECUTABLE = 'executable'
def link(self,
target_desc,
objects,
output_filename,
output_dir=None,
libraries=None,
library_dirs=None,
runtime_library_dirs=None,
export_symbols=None,
debug=0,
extra_preargs=None,
extra_postargs=None,
build_temp=None,
target_lang=None):
def link(
self,
target_desc,
objects,
output_filename,
output_dir=None,
libraries=None,
library_dirs=None,
runtime_library_dirs=None,
export_symbols=None,
debug=0,
extra_preargs=None,
extra_postargs=None,
build_temp=None,
target_lang=None,
):
"""Link a bunch of stuff together to create an executable or
shared library file.
@ -673,66 +705,74 @@ class CCompiler:
"""
raise NotImplementedError
# Old 'link_*()' methods, rewritten to use the new 'link()' method.
def link_shared_lib(self,
objects,
output_libname,
output_dir=None,
libraries=None,
library_dirs=None,
runtime_library_dirs=None,
export_symbols=None,
debug=0,
extra_preargs=None,
extra_postargs=None,
build_temp=None,
target_lang=None):
self.link(CCompiler.SHARED_LIBRARY, objects,
self.library_filename(output_libname, lib_type='shared'),
output_dir,
libraries, library_dirs, runtime_library_dirs,
export_symbols, debug,
extra_preargs, extra_postargs, build_temp, target_lang)
def link_shared_lib(
self,
objects,
output_libname,
output_dir=None,
libraries=None,
library_dirs=None,
runtime_library_dirs=None,
export_symbols=None,
debug=0,
extra_preargs=None,
extra_postargs=None,
build_temp=None,
target_lang=None,
):
self.link(
CCompiler.SHARED_LIBRARY, objects,
self.library_filename(output_libname, lib_type='shared'),
output_dir,
libraries, library_dirs, runtime_library_dirs,
export_symbols, debug,
extra_preargs, extra_postargs, build_temp, target_lang,
)
def link_shared_object(
self,
objects,
output_filename,
output_dir=None,
libraries=None,
library_dirs=None,
runtime_library_dirs=None,
export_symbols=None,
debug=0,
extra_preargs=None,
extra_postargs=None,
build_temp=None,
target_lang=None,
):
self.link(
CCompiler.SHARED_OBJECT, objects,
output_filename, output_dir,
libraries, library_dirs, runtime_library_dirs,
export_symbols, debug,
extra_preargs, extra_postargs, build_temp, target_lang,
)
def link_shared_object(self,
objects,
output_filename,
output_dir=None,
libraries=None,
library_dirs=None,
runtime_library_dirs=None,
export_symbols=None,
debug=0,
extra_preargs=None,
extra_postargs=None,
build_temp=None,
target_lang=None):
self.link(CCompiler.SHARED_OBJECT, objects,
output_filename, output_dir,
libraries, library_dirs, runtime_library_dirs,
export_symbols, debug,
extra_preargs, extra_postargs, build_temp, target_lang)
def link_executable(self,
objects,
output_progname,
output_dir=None,
libraries=None,
library_dirs=None,
runtime_library_dirs=None,
debug=0,
extra_preargs=None,
extra_postargs=None,
target_lang=None):
self.link(CCompiler.EXECUTABLE, objects,
self.executable_filename(output_progname), output_dir,
libraries, library_dirs, runtime_library_dirs, None,
debug, extra_preargs, extra_postargs, None, target_lang)
def link_executable(
self,
objects,
output_progname,
output_dir=None,
libraries=None,
library_dirs=None,
runtime_library_dirs=None,
debug=0,
extra_preargs=None,
extra_postargs=None,
target_lang=None,
):
self.link(
CCompiler.EXECUTABLE, objects,
self.executable_filename(output_progname), output_dir,
libraries, library_dirs, runtime_library_dirs, None,
debug, extra_preargs, extra_postargs, None, target_lang,
)
# -- Miscellaneous methods -----------------------------------------
# These are all used by the 'gen_lib_options() function; there is
@ -757,8 +797,10 @@ class CCompiler:
"""
raise NotImplementedError
def has_function(self, funcname, includes=None, include_dirs=None,
libraries=None, library_dirs=None):
def has_function(
self, funcname, includes=None, include_dirs=None,
libraries=None, library_dirs=None,
):
"""Return a boolean indicating whether funcname is supported on
the current platform. The optional arguments can be used to
augment the compilation environment.
@ -775,17 +817,19 @@ class CCompiler:
libraries = []
if library_dirs is None:
library_dirs = []
fd, fname = tempfile.mkstemp(".c", funcname, text=True)
f = os.fdopen(fd, "w")
fd, fname = tempfile.mkstemp('.c', funcname, text=True)
f = os.fdopen(fd, 'w')
try:
for incl in includes:
f.write("""#include "%s"\n""" % incl)
f.write("""\
f.write(
"""\
int main (int argc, char **argv) {
%s();
return 0;
}
""" % funcname)
""" % funcname,
)
finally:
f.close()
try:
@ -796,19 +840,21 @@ int main (int argc, char **argv) {
os.remove(fname)
try:
self.link_executable(objects, "a.out",
libraries=libraries,
library_dirs=library_dirs)
self.link_executable(
objects, 'a.out',
libraries=libraries,
library_dirs=library_dirs,
)
except (LinkError, TypeError):
return False
else:
os.remove("a.out")
os.remove('a.out')
finally:
for fn in objects:
os.remove(fn)
return True
def find_library_file (self, dirs, lib, debug=0):
def find_library_file(self, dirs, lib, debug=0):
"""Search the specified list of directories for a static or shared
library file 'lib' and return the full path to that file. If
'debug' true, look for a debugging version (if that makes sense on
@ -857,15 +903,20 @@ int main (int argc, char **argv) {
obj_names = []
for src_name in source_filenames:
base, ext = os.path.splitext(src_name)
base = os.path.splitdrive(base)[1] # Chop off the drive
base = os.path.splitdrive(base)[1] # Chop off the drive
base = base[os.path.isabs(base):] # If abs, chop off leading /
if ext not in self.src_extensions:
raise UnknownFileError(
"unknown file type '%s' (from '%s')" % (ext, src_name))
"unknown file type '{}' (from '{}')".format(ext, src_name),
)
if strip_dir:
base = os.path.basename(base)
obj_names.append(os.path.join(output_dir,
base + self.obj_extension))
obj_names.append(
os.path.join(
output_dir,
base + self.obj_extension,
),
)
return obj_names
def shared_object_filename(self, basename, strip_dir=0, output_dir=''):
@ -880,14 +931,17 @@ int main (int argc, char **argv) {
basename = os.path.basename(basename)
return os.path.join(output_dir, basename + (self.exe_extension or ''))
def library_filename(self, libname, lib_type='static', # or 'shared'
strip_dir=0, output_dir=''):
def library_filename(
self, libname, lib_type='static', # or 'shared'
strip_dir=0, output_dir='',
):
assert output_dir is not None
if lib_type not in ("static", "shared", "dylib", "xcode_stub"):
if lib_type not in ('static', 'shared', 'dylib', 'xcode_stub'):
raise ValueError(
"'lib_type' must be \"static\", \"shared\", \"dylib\", or \"xcode_stub\"")
fmt = getattr(self, lib_type + "_lib_format")
ext = getattr(self, lib_type + "_lib_extension")
"'lib_type' must be \"static\", \"shared\", \"dylib\", or \"xcode_stub\"",
)
fmt = getattr(self, lib_type + '_lib_format')
ext = getattr(self, lib_type + '_lib_extension')
dir, base = os.path.split(libname)
filename = fmt % (base, ext)
@ -896,7 +950,6 @@ int main (int argc, char **argv) {
return os.path.join(output_dir, dir, filename)
# -- Utility methods -----------------------------------------------
def announce(self, msg, level=1):
@ -908,7 +961,7 @@ int main (int argc, char **argv) {
print(msg)
def warn(self, msg):
sys.stderr.write("warning: %s\n" % msg)
sys.stderr.write('warning: %s\n' % msg)
def execute(self, func, args, msg=None, level=1):
execute(func, args, msg, self.dry_run)
@ -919,7 +972,7 @@ int main (int argc, char **argv) {
def move_file(self, src, dst):
return move_file(src, dst, dry_run=self.dry_run)
def mkpath (self, name, mode=0o777):
def mkpath(self, name, mode=0o777):
mkpath(name, mode, dry_run=self.dry_run)
@ -939,7 +992,8 @@ _default_compilers = (
('posix', 'unix'),
('nt', 'msvc'),
)
)
def get_default_compiler(osname=None, platform=None):
"""Determine the default compiler to use for the given platform.
@ -962,20 +1016,32 @@ def get_default_compiler(osname=None, platform=None):
# Default to Unix compiler
return 'unix'
# Map compiler types to (module_name, class_name) pairs -- ie. where to
# find the code that implements an interface to this compiler. (The module
# is assumed to be in the 'distutils' package.)
compiler_class = { 'unix': ('unixccompiler', 'UnixCCompiler',
"standard UNIX-style compiler"),
'msvc': ('_msvccompiler', 'MSVCCompiler',
"Microsoft Visual C++"),
'cygwin': ('cygwinccompiler', 'CygwinCCompiler',
"Cygwin port of GNU C Compiler for Win32"),
'mingw32': ('cygwinccompiler', 'Mingw32CCompiler',
"Mingw32 port of GNU C Compiler for Win32"),
'bcpp': ('bcppcompiler', 'BCPPCompiler',
"Borland C++ Compiler"),
}
compiler_class = {'unix': (
'unixccompiler', 'UnixCCompiler',
'standard UNIX-style compiler',
),
'msvc': (
'_msvccompiler', 'MSVCCompiler',
'Microsoft Visual C++',
),
'cygwin': (
'cygwinccompiler', 'CygwinCCompiler',
'Cygwin port of GNU C Compiler for Win32',
),
'mingw32': (
'cygwinccompiler', 'Mingw32CCompiler',
'Mingw32 port of GNU C Compiler for Win32',
),
'bcpp': (
'bcppcompiler', 'BCPPCompiler',
'Borland C++ Compiler',
),
}
def show_compilers():
"""Print list of available compilers (used by the "--help-compiler"
@ -987,11 +1053,13 @@ def show_compilers():
from distutils.fancy_getopt import FancyGetopt
compilers = []
for compiler in compiler_class.keys():
compilers.append(("compiler="+compiler, None,
compiler_class[compiler][2]))
compilers.append((
'compiler=' + compiler, None,
compiler_class[compiler][2],
))
compilers.sort()
pretty_printer = FancyGetopt(compilers)
pretty_printer.print_help("List of available compilers:")
pretty_printer.print_help('List of available compilers:')
def new_compiler(plat=None, compiler=None, verbose=0, dry_run=0, force=0):
@ -1020,18 +1088,20 @@ def new_compiler(plat=None, compiler=None, verbose=0, dry_run=0, force=0):
raise DistutilsPlatformError(msg)
try:
module_name = "distutils." + module_name
__import__ (module_name)
module_name = 'distutils.' + module_name
__import__(module_name)
module = sys.modules[module_name]
klass = vars(module)[class_name]
except ImportError:
raise DistutilsModuleError(
"can't compile C/C++ code: unable to load module '%s'" % \
module_name)
"can't compile C/C++ code: unable to load module '%s'" %
module_name,
)
except KeyError:
raise DistutilsModuleError(
"can't compile C/C++ code: unable to find class '%s' "
"in module '%s'" % (class_name, module_name))
"can't compile C/C++ code: unable to find class '%s' "
"in module '%s'" % (class_name, module_name),
)
# XXX The None is necessary to preserve backwards compatibility
# with classes that expect verbose to be the first positional
@ -1064,27 +1134,28 @@ def gen_preprocess_options(macros, include_dirs):
for macro in macros:
if not (isinstance(macro, tuple) and 1 <= len(macro) <= 2):
raise TypeError(
"bad macro definition '%s': "
"each element of 'macros' list must be a 1- or 2-tuple"
% macro)
"bad macro definition '%s': "
"each element of 'macros' list must be a 1- or 2-tuple"
% macro,
)
if len(macro) == 1: # undefine this macro
pp_opts.append("-U%s" % macro[0])
pp_opts.append('-U%s' % macro[0])
elif len(macro) == 2:
if macro[1] is None: # define with no explicit value
pp_opts.append("-D%s" % macro[0])
pp_opts.append('-D%s' % macro[0])
else:
# XXX *don't* need to be clever about quoting the
# macro value here, because we're going to avoid the
# shell at all costs when we spawn the command!
pp_opts.append("-D%s=%s" % macro)
pp_opts.append('-D%s=%s' % macro)
for dir in include_dirs:
pp_opts.append("-I%s" % dir)
pp_opts.append('-I%s' % dir)
return pp_opts
def gen_lib_options (compiler, library_dirs, runtime_library_dirs, libraries):
def gen_lib_options(compiler, library_dirs, runtime_library_dirs, libraries):
"""Generate linker options for searching library directories and
linking with specific libraries. 'libraries' and 'library_dirs' are,
respectively, lists of library names (not filenames!) and search
@ -1116,8 +1187,10 @@ def gen_lib_options (compiler, library_dirs, runtime_library_dirs, libraries):
if lib_file:
lib_opts.append(lib_file)
else:
compiler.warn("no library file corresponding to "
"'%s' found (skipping)" % lib)
compiler.warn(
'no library file corresponding to '
"'%s' found (skipping)" % lib,
)
else:
lib_opts.append(compiler.library_option (lib))
lib_opts.append(compiler.library_option(lib))
return lib_opts

View file

@ -3,11 +3,20 @@
Provides the Command class, the base class for the command classes
in the distutils.command package.
"""
from __future__ import annotations
import sys, os, re
from distutils.errors import DistutilsOptionError
from distutils import util, dir_util, file_util, archive_util, dep_util
import os
import re
import sys
from distutils import archive_util
from distutils import dep_util
from distutils import dir_util
from distutils import file_util
from distutils import log
from distutils import util
from distutils.errors import DistutilsOptionError
class Command:
"""Abstract base class for defining command classes, the "worker bees"
@ -41,7 +50,6 @@ class Command:
# defined. The canonical example is the "install" command.
sub_commands = []
# -- Creation/initialization methods -------------------------------
def __init__(self, dist):
@ -54,9 +62,9 @@ class Command:
from distutils.dist import Distribution
if not isinstance(dist, Distribution):
raise TypeError("dist must be a Distribution instance")
raise TypeError('dist must be a Distribution instance')
if self.__class__ is Command:
raise RuntimeError("Command is an abstract class")
raise RuntimeError('Command is an abstract class')
self.distribution = dist
self.initialize_options()
@ -94,7 +102,7 @@ class Command:
# XXX A more explicit way to customize dry_run would be better.
def __getattr__(self, attr):
if attr == 'dry_run':
myval = getattr(self, "_" + attr)
myval = getattr(self, '_' + attr)
if myval is None:
return getattr(self.distribution, attr)
else:
@ -130,8 +138,10 @@ class Command:
This method must be implemented by all command classes.
"""
raise RuntimeError("abstract method -- subclass %s must override"
% self.__class__)
raise RuntimeError(
'abstract method -- subclass %s must override'
% self.__class__,
)
def finalize_options(self):
"""Set final values for all the options that this command supports.
@ -144,23 +154,26 @@ class Command:
This method must be implemented by all command classes.
"""
raise RuntimeError("abstract method -- subclass %s must override"
% self.__class__)
raise RuntimeError(
'abstract method -- subclass %s must override'
% self.__class__,
)
def dump_options(self, header=None, indent=""):
def dump_options(self, header=None, indent=''):
from distutils.fancy_getopt import longopt_xlate
if header is None:
header = "command options for '%s':" % self.get_command_name()
self.announce(indent + header, level=log.INFO)
indent = indent + " "
indent = indent + ' '
for (option, _, _) in self.user_options:
option = option.translate(longopt_xlate)
if option[-1] == "=":
if option[-1] == '=':
option = option[:-1]
value = getattr(self, option)
self.announce(indent + "%s = %s" % (option, value),
level=log.INFO)
self.announce(
indent + '{} = {}'.format(option, value),
level=log.INFO,
)
def run(self):
"""A command's raison d'etre: carry out the action it exists to
@ -172,8 +185,10 @@ class Command:
This method must be implemented by all command classes.
"""
raise RuntimeError("abstract method -- subclass %s must override"
% self.__class__)
raise RuntimeError(
'abstract method -- subclass %s must override'
% self.__class__,
)
def announce(self, msg, level=1):
"""If the current verbosity level is of greater than or equal to
@ -190,7 +205,6 @@ class Command:
print(msg)
sys.stdout.flush()
# -- Option validation methods -------------------------------------
# (these are very handy in writing the 'finalize_options()' method)
#
@ -210,15 +224,17 @@ class Command:
setattr(self, option, default)
return default
elif not isinstance(val, str):
raise DistutilsOptionError("'%s' must be a %s (got `%s`)"
% (option, what, val))
raise DistutilsOptionError(
"'%s' must be a %s (got `%s`)"
% (option, what, val),
)
return val
def ensure_string(self, option, default=None):
"""Ensure that 'option' is a string; if not defined, set it to
'default'.
"""
self._ensure_stringlike(option, "string", default)
self._ensure_stringlike(option, 'string', default)
def ensure_string_list(self, option):
r"""Ensure that 'option' is a list of strings. If 'option' is
@ -238,11 +254,14 @@ class Command:
ok = False
if not ok:
raise DistutilsOptionError(
"'%s' must be a list of strings (got %r)"
% (option, val))
"'%s' must be a list of strings (got %r)"
% (option, val),
)
def _ensure_tested_string(self, option, tester, what, error_fmt,
default=None):
def _ensure_tested_string(
self, option, tester, what, error_fmt,
default=None,
):
val = self._ensure_stringlike(option, what, default)
if val is not None and not tester(val):
raise DistutilsOptionError(("error in '%s' option: " + error_fmt)
@ -250,15 +269,18 @@ class Command:
def ensure_filename(self, option):
"""Ensure that 'option' is the name of an existing file."""
self._ensure_tested_string(option, os.path.isfile,
"filename",
"'%s' does not exist or is not a file")
self._ensure_tested_string(
option, os.path.isfile,
'filename',
"'%s' does not exist or is not a file",
)
def ensure_dirname(self, option):
self._ensure_tested_string(option, os.path.isdir,
"directory name",
"'%s' does not exist or is not a directory")
self._ensure_tested_string(
option, os.path.isdir,
'directory name',
"'%s' does not exist or is not a directory",
)
# -- Convenience methods for commands ------------------------------
@ -302,8 +324,10 @@ class Command:
# XXX rename to 'get_reinitialized_command()'? (should do the
# same in dist.py, if so)
def reinitialize_command(self, command, reinit_subcommands=0):
return self.distribution.reinitialize_command(command,
reinit_subcommands)
return self.distribution.reinitialize_command(
command,
reinit_subcommands,
)
def run_command(self, command):
"""Run some other command: uses the 'run_command()' method of
@ -325,11 +349,10 @@ class Command:
commands.append(cmd_name)
return commands
# -- External world manipulation -----------------------------------
def warn(self, msg):
log.warn("warning: %s: %s\n", self.get_command_name(), msg)
log.warn('warning: %s: %s\n', self.get_command_name(), msg)
def execute(self, func, args, msg=None, level=1):
util.execute(func, args, msg, dry_run=self.dry_run)
@ -337,25 +360,33 @@ class Command:
def mkpath(self, name, mode=0o777):
dir_util.mkpath(name, mode, dry_run=self.dry_run)
def copy_file(self, infile, outfile, preserve_mode=1, preserve_times=1,
link=None, level=1):
def copy_file(
self, infile, outfile, preserve_mode=1, preserve_times=1,
link=None, level=1,
):
"""Copy a file respecting verbose, dry-run and force flags. (The
former two default to whatever is in the Distribution object, and
the latter defaults to false for commands that don't define it.)"""
return file_util.copy_file(infile, outfile, preserve_mode,
preserve_times, not self.force, link,
dry_run=self.dry_run)
return file_util.copy_file(
infile, outfile, preserve_mode,
preserve_times, not self.force, link,
dry_run=self.dry_run,
)
def copy_tree(self, infile, outfile, preserve_mode=1, preserve_times=1,
preserve_symlinks=0, level=1):
def copy_tree(
self, infile, outfile, preserve_mode=1, preserve_times=1,
preserve_symlinks=0, level=1,
):
"""Copy an entire directory tree respecting verbose, dry-run,
and force flags.
"""
return dir_util.copy_tree(infile, outfile, preserve_mode,
preserve_times, preserve_symlinks,
not self.force, dry_run=self.dry_run)
return dir_util.copy_tree(
infile, outfile, preserve_mode,
preserve_times, preserve_symlinks,
not self.force, dry_run=self.dry_run,
)
def move_file (self, src, dst, level=1):
def move_file(self, src, dst, level=1):
"""Move a file respecting dry-run flag."""
return file_util.move_file(src, dst, dry_run=self.dry_run)
@ -364,14 +395,20 @@ class Command:
from distutils.spawn import spawn
spawn(cmd, search_path, dry_run=self.dry_run)
def make_archive(self, base_name, format, root_dir=None, base_dir=None,
owner=None, group=None):
return archive_util.make_archive(base_name, format, root_dir, base_dir,
dry_run=self.dry_run,
owner=owner, group=group)
def make_archive(
self, base_name, format, root_dir=None, base_dir=None,
owner=None, group=None,
):
return archive_util.make_archive(
base_name, format, root_dir, base_dir,
dry_run=self.dry_run,
owner=owner, group=group,
)
def make_file(self, infiles, outfile, func, args,
exec_msg=None, skip_msg=None, level=1):
def make_file(
self, infiles, outfile, func, args,
exec_msg=None, skip_msg=None, level=1,
):
"""Special case of 'execute()' for operations that process one or
more input files and generate one output file. Works just like
'execute()', except the operation is skipped and a different
@ -381,17 +418,18 @@ class Command:
timestamp checks.
"""
if skip_msg is None:
skip_msg = "skipping %s (inputs unchanged)" % outfile
skip_msg = 'skipping %s (inputs unchanged)' % outfile
# Allow 'infiles' to be a single string
if isinstance(infiles, str):
infiles = (infiles,)
elif not isinstance(infiles, (list, tuple)):
raise TypeError(
"'infiles' must be a string, or a list or tuple of strings")
"'infiles' must be a string, or a list or tuple of strings",
)
if exec_msg is None:
exec_msg = "generating %s from %s" % (outfile, ', '.join(infiles))
exec_msg = 'generating {} from {}'.format(outfile, ', '.join(infiles))
# If 'outfile' must be regenerated (either because it doesn't
# exist, is out-of-date, or the 'force' flag is true) then

View file

@ -2,30 +2,32 @@
Package containing implementation of all the standard Distutils
commands."""
from __future__ import annotations
__all__ = ['build',
'build_py',
'build_ext',
'build_clib',
'build_scripts',
'clean',
'install',
'install_lib',
'install_headers',
'install_scripts',
'install_data',
'sdist',
'register',
'bdist',
'bdist_dumb',
'bdist_rpm',
'bdist_wininst',
'check',
'upload',
# These two are reserved for future use:
#'bdist_sdux',
#'bdist_pkgtool',
# Note:
# bdist_packager is not included because it only provides
# an abstract base class
]
__all__ = [
'build',
'build_py',
'build_ext',
'build_clib',
'build_scripts',
'clean',
'install',
'install_lib',
'install_headers',
'install_scripts',
'install_data',
'sdist',
'register',
'bdist',
'bdist_dumb',
'bdist_rpm',
'bdist_wininst',
'check',
'upload',
# These two are reserved for future use:
#'bdist_sdux',
#'bdist_pkgtool',
# Note:
# bdist_packager is not included because it only provides
# an abstract base class
]

View file

@ -2,8 +2,10 @@
Implements the Distutils 'bdist' command (create a built [binary]
distribution)."""
from __future__ import annotations
import os
from distutils.core import Command
from distutils.errors import *
from distutils.util import get_platform
@ -15,68 +17,93 @@ def show_formats():
from distutils.fancy_getopt import FancyGetopt
formats = []
for format in bdist.format_commands:
formats.append(("formats=" + format, None,
bdist.format_command[format][1]))
formats.append((
'formats=' + format, None,
bdist.format_command[format][1],
))
pretty_printer = FancyGetopt(formats)
pretty_printer.print_help("List of available distribution formats:")
pretty_printer.print_help('List of available distribution formats:')
class bdist(Command):
description = "create a built (binary) distribution"
description = 'create a built (binary) distribution'
user_options = [('bdist-base=', 'b',
"temporary directory for creating built distributions"),
('plat-name=', 'p',
"platform name to embed in generated filenames "
"(default: %s)" % get_platform()),
('formats=', None,
"formats for distribution (comma-separated list)"),
('dist-dir=', 'd',
"directory to put final built distributions in "
"[default: dist]"),
('skip-build', None,
"skip rebuilding everything (for testing/debugging)"),
('owner=', 'u',
"Owner name used when creating a tar file"
" [default: current user]"),
('group=', 'g',
"Group name used when creating a tar file"
" [default: current group]"),
]
user_options = [
(
'bdist-base=', 'b',
'temporary directory for creating built distributions',
),
(
'plat-name=', 'p',
'platform name to embed in generated filenames '
'(default: %s)' % get_platform(),
),
(
'formats=', None,
'formats for distribution (comma-separated list)',
),
(
'dist-dir=', 'd',
'directory to put final built distributions in '
'[default: dist]',
),
(
'skip-build', None,
'skip rebuilding everything (for testing/debugging)',
),
(
'owner=', 'u',
'Owner name used when creating a tar file'
' [default: current user]',
),
(
'group=', 'g',
'Group name used when creating a tar file'
' [default: current group]',
),
]
boolean_options = ['skip-build']
help_options = [
('help-formats', None,
"lists available distribution formats", show_formats),
]
(
'help-formats', None,
'lists available distribution formats', show_formats,
),
]
# The following commands do not take a format option from bdist
no_format_option = ('bdist_rpm',)
# This won't do in reality: will need to distinguish RPM-ish Linux,
# Debian-ish Linux, Solaris, FreeBSD, ..., Windows, Mac OS.
default_format = {'posix': 'gztar',
'nt': 'zip'}
default_format = {
'posix': 'gztar',
'nt': 'zip',
}
# Establish the preferred order (for the --help-formats option).
format_commands = ['rpm', 'gztar', 'bztar', 'xztar', 'ztar', 'tar',
'wininst', 'zip', 'msi']
format_commands = [
'rpm', 'gztar', 'bztar', 'xztar', 'ztar', 'tar',
'wininst', 'zip', 'msi',
]
# And the real information.
format_command = {'rpm': ('bdist_rpm', "RPM distribution"),
'gztar': ('bdist_dumb', "gzip'ed tar file"),
'bztar': ('bdist_dumb', "bzip2'ed tar file"),
'xztar': ('bdist_dumb', "xz'ed tar file"),
'ztar': ('bdist_dumb', "compressed tar file"),
'tar': ('bdist_dumb', "tar file"),
'wininst': ('bdist_wininst',
"Windows executable installer"),
'zip': ('bdist_dumb', "ZIP file"),
'msi': ('bdist_msi', "Microsoft Installer")
}
format_command = {
'rpm': ('bdist_rpm', 'RPM distribution'),
'gztar': ('bdist_dumb', "gzip'ed tar file"),
'bztar': ('bdist_dumb', "bzip2'ed tar file"),
'xztar': ('bdist_dumb', "xz'ed tar file"),
'ztar': ('bdist_dumb', 'compressed tar file'),
'tar': ('bdist_dumb', 'tar file'),
'wininst': (
'bdist_wininst',
'Windows executable installer',
),
'zip': ('bdist_dumb', 'ZIP file'),
'msi': ('bdist_msi', 'Microsoft Installer'),
}
def initialize_options(self):
self.bdist_base = None
@ -100,8 +127,10 @@ class bdist(Command):
# "build/bdist.<plat>/dumb", "build/bdist.<plat>/rpm", etc.)
if self.bdist_base is None:
build_base = self.get_finalized_command('build').build_base
self.bdist_base = os.path.join(build_base,
'bdist.' + self.plat_name)
self.bdist_base = os.path.join(
build_base,
'bdist.' + self.plat_name,
)
self.ensure_string_list('formats')
if self.formats is None:
@ -109,11 +138,12 @@ class bdist(Command):
self.formats = [self.default_format[os.name]]
except KeyError:
raise DistutilsPlatformError(
"don't know how to create built distributions "
"on platform %s" % os.name)
"don't know how to create built distributions "
'on platform %s' % os.name,
)
if self.dist_dir is None:
self.dist_dir = "dist"
self.dist_dir = 'dist'
def run(self):
# Figure out which sub-commands we need to run.
@ -138,6 +168,6 @@ class bdist(Command):
# If we're going to need to run this command again, tell it to
# keep its temporary files around so subsequent runs go faster.
if cmd_name in commands[i+1:]:
if cmd_name in commands[i + 1:]:
sub_cmd.keep_temp = 1
self.run_command(cmd_name)

View file

@ -3,49 +3,72 @@
Implements the Distutils 'bdist_dumb' command (create a "dumb" built
distribution -- i.e., just an archive to be unpacked under $prefix or
$exec_prefix)."""
from __future__ import annotations
import os
from distutils import log
from distutils.core import Command
from distutils.util import get_platform
from distutils.dir_util import remove_tree, ensure_relative
from distutils.dir_util import ensure_relative
from distutils.dir_util import remove_tree
from distutils.errors import *
from distutils.sysconfig import get_python_version
from distutils import log
from distutils.util import get_platform
class bdist_dumb(Command):
description = "create a \"dumb\" built distribution"
user_options = [('bdist-dir=', 'd',
"temporary directory for creating the distribution"),
('plat-name=', 'p',
"platform name to embed in generated filenames "
"(default: %s)" % get_platform()),
('format=', 'f',
"archive format to create (tar, gztar, bztar, xztar, "
"ztar, zip)"),
('keep-temp', 'k',
"keep the pseudo-installation tree around after " +
"creating the distribution archive"),
('dist-dir=', 'd',
"directory to put final built distributions in"),
('skip-build', None,
"skip rebuilding everything (for testing/debugging)"),
('relative', None,
"build the archive using relative paths "
"(default: false)"),
('owner=', 'u',
"Owner name used when creating a tar file"
" [default: current user]"),
('group=', 'g',
"Group name used when creating a tar file"
" [default: current group]"),
]
user_options = [
(
'bdist-dir=', 'd',
'temporary directory for creating the distribution',
),
(
'plat-name=', 'p',
'platform name to embed in generated filenames '
'(default: %s)' % get_platform(),
),
(
'format=', 'f',
'archive format to create (tar, gztar, bztar, xztar, '
'ztar, zip)',
),
(
'keep-temp', 'k',
'keep the pseudo-installation tree around after ' +
'creating the distribution archive',
),
(
'dist-dir=', 'd',
'directory to put final built distributions in',
),
(
'skip-build', None,
'skip rebuilding everything (for testing/debugging)',
),
(
'relative', None,
'build the archive using relative paths '
'(default: false)',
),
(
'owner=', 'u',
'Owner name used when creating a tar file'
' [default: current user]',
),
(
'group=', 'g',
'Group name used when creating a tar file'
' [default: current group]',
),
]
boolean_options = ['keep-temp', 'skip-build', 'relative']
default_format = { 'posix': 'gztar',
'nt': 'zip' }
default_format = {'posix': 'gztar',
'nt': 'zip', }
def initialize_options(self):
self.bdist_dir = None
@ -68,13 +91,16 @@ class bdist_dumb(Command):
self.format = self.default_format[os.name]
except KeyError:
raise DistutilsPlatformError(
"don't know how to create dumb built distributions "
"on platform %s" % os.name)
"don't know how to create dumb built distributions "
'on platform %s' % os.name,
)
self.set_undefined_options('bdist',
('dist_dir', 'dist_dir'),
('plat_name', 'plat_name'),
('skip_build', 'skip_build'))
self.set_undefined_options(
'bdist',
('dist_dir', 'dist_dir'),
('plat_name', 'plat_name'),
('skip_build', 'skip_build'),
)
def run(self):
if not self.skip_build:
@ -85,39 +111,52 @@ class bdist_dumb(Command):
install.skip_build = self.skip_build
install.warn_dir = 0
log.info("installing to %s", self.bdist_dir)
log.info('installing to %s', self.bdist_dir)
self.run_command('install')
# And make an archive relative to the root of the
# pseudo-installation tree.
archive_basename = "%s.%s" % (self.distribution.get_fullname(),
self.plat_name)
archive_basename = '{}.{}'.format(
self.distribution.get_fullname(),
self.plat_name,
)
pseudoinstall_root = os.path.join(self.dist_dir, archive_basename)
if not self.relative:
archive_root = self.bdist_dir
else:
if (self.distribution.has_ext_modules() and
(install.install_base != install.install_platbase)):
if (
self.distribution.has_ext_modules() and
(install.install_base != install.install_platbase)
):
raise DistutilsPlatformError(
"can't make a dumb built distribution where "
"base and platbase are different (%s, %s)"
% (repr(install.install_base),
repr(install.install_platbase)))
"can't make a dumb built distribution where "
'base and platbase are different (%s, %s)'
% (
repr(install.install_base),
repr(install.install_platbase),
),
)
else:
archive_root = os.path.join(self.bdist_dir,
ensure_relative(install.install_base))
archive_root = os.path.join(
self.bdist_dir,
ensure_relative(install.install_base),
)
# Make the archive
filename = self.make_archive(pseudoinstall_root,
self.format, root_dir=archive_root,
owner=self.owner, group=self.group)
filename = self.make_archive(
pseudoinstall_root,
self.format, root_dir=archive_root,
owner=self.owner, group=self.group,
)
if self.distribution.has_ext_modules():
pyversion = get_python_version()
else:
pyversion = 'any'
self.distribution.dist_files.append(('bdist_dumb', pyversion,
filename))
self.distribution.dist_files.append((
'bdist_dumb', pyversion,
filename,
))
if not self.keep_temp:
remove_tree(self.bdist_dir, dry_run=self.dry_run)

View file

@ -2,135 +2,225 @@
Implements the Distutils 'bdist_rpm' command (create RPM source and binary
distributions)."""
from __future__ import annotations
import subprocess, sys, os
import os
import subprocess
import sys
from distutils import log
from distutils.core import Command
from distutils.debug import DEBUG
from distutils.file_util import write_file
from distutils.errors import *
from distutils.file_util import write_file
from distutils.sysconfig import get_python_version
from distutils import log
class bdist_rpm(Command):
description = "create an RPM distribution"
description = 'create an RPM distribution'
user_options = [
('bdist-base=', None,
"base directory for creating built distributions"),
('rpm-base=', None,
"base directory for creating RPMs (defaults to \"rpm\" under "
"--bdist-base; must be specified for RPM 2)"),
('dist-dir=', 'd',
"directory to put final RPM files in "
"(and .spec files if --spec-only)"),
('python=', None,
"path to Python interpreter to hard-code in the .spec file "
"(default: \"python\")"),
('fix-python', None,
"hard-code the exact path to the current Python interpreter in "
"the .spec file"),
('spec-only', None,
"only regenerate spec file"),
('source-only', None,
"only generate source RPM"),
('binary-only', None,
"only generate binary RPM"),
('use-bzip2', None,
"use bzip2 instead of gzip to create source distribution"),
(
'bdist-base=', None,
'base directory for creating built distributions',
),
(
'rpm-base=', None,
"base directory for creating RPMs (defaults to \"rpm\" under "
'--bdist-base; must be specified for RPM 2)',
),
(
'dist-dir=', 'd',
'directory to put final RPM files in '
'(and .spec files if --spec-only)',
),
(
'python=', None,
'path to Python interpreter to hard-code in the .spec file '
"(default: \"python\")",
),
(
'fix-python', None,
'hard-code the exact path to the current Python interpreter in '
'the .spec file',
),
(
'spec-only', None,
'only regenerate spec file',
),
(
'source-only', None,
'only generate source RPM',
),
(
'binary-only', None,
'only generate binary RPM',
),
(
'use-bzip2', None,
'use bzip2 instead of gzip to create source distribution',
),
# More meta-data: too RPM-specific to put in the setup script,
# but needs to go in the .spec file -- so we make these options
# to "bdist_rpm". The idea is that packagers would put this
# info in setup.cfg, although they are of course free to
# supply it on the command line.
('distribution-name=', None,
"name of the (Linux) distribution to which this "
"RPM applies (*not* the name of the module distribution!)"),
('group=', None,
"package classification [default: \"Development/Libraries\"]"),
('release=', None,
"RPM release number"),
('serial=', None,
"RPM serial number"),
('vendor=', None,
"RPM \"vendor\" (eg. \"Joe Blow <joe@example.com>\") "
"[default: maintainer or author from setup script]"),
('packager=', None,
"RPM packager (eg. \"Jane Doe <jane@example.net>\") "
"[default: vendor]"),
('doc-files=', None,
"list of documentation files (space or comma-separated)"),
('changelog=', None,
"RPM changelog"),
('icon=', None,
"name of icon file"),
('provides=', None,
"capabilities provided by this package"),
('requires=', None,
"capabilities required by this package"),
('conflicts=', None,
"capabilities which conflict with this package"),
('build-requires=', None,
"capabilities required to build this package"),
('obsoletes=', None,
"capabilities made obsolete by this package"),
('no-autoreq', None,
"do not automatically calculate dependencies"),
(
'distribution-name=', None,
'name of the (Linux) distribution to which this '
'RPM applies (*not* the name of the module distribution!)',
),
(
'group=', None,
"package classification [default: \"Development/Libraries\"]",
),
(
'release=', None,
'RPM release number',
),
(
'serial=', None,
'RPM serial number',
),
(
'vendor=', None,
"RPM \"vendor\" (eg. \"Joe Blow <joe@example.com>\") "
'[default: maintainer or author from setup script]',
),
(
'packager=', None,
"RPM packager (eg. \"Jane Doe <jane@example.net>\") "
'[default: vendor]',
),
(
'doc-files=', None,
'list of documentation files (space or comma-separated)',
),
(
'changelog=', None,
'RPM changelog',
),
(
'icon=', None,
'name of icon file',
),
(
'provides=', None,
'capabilities provided by this package',
),
(
'requires=', None,
'capabilities required by this package',
),
(
'conflicts=', None,
'capabilities which conflict with this package',
),
(
'build-requires=', None,
'capabilities required to build this package',
),
(
'obsoletes=', None,
'capabilities made obsolete by this package',
),
(
'no-autoreq', None,
'do not automatically calculate dependencies',
),
# Actions to take when building RPM
('keep-temp', 'k',
"don't clean up RPM build directory"),
('no-keep-temp', None,
"clean up RPM build directory [default]"),
('use-rpm-opt-flags', None,
"compile with RPM_OPT_FLAGS when building from source RPM"),
('no-rpm-opt-flags', None,
"do not pass any RPM CFLAGS to compiler"),
('rpm3-mode', None,
"RPM 3 compatibility mode (default)"),
('rpm2-mode', None,
"RPM 2 compatibility mode"),
(
'keep-temp', 'k',
"don't clean up RPM build directory",
),
(
'no-keep-temp', None,
'clean up RPM build directory [default]',
),
(
'use-rpm-opt-flags', None,
'compile with RPM_OPT_FLAGS when building from source RPM',
),
(
'no-rpm-opt-flags', None,
'do not pass any RPM CFLAGS to compiler',
),
(
'rpm3-mode', None,
'RPM 3 compatibility mode (default)',
),
(
'rpm2-mode', None,
'RPM 2 compatibility mode',
),
# Add the hooks necessary for specifying custom scripts
('prep-script=', None,
"Specify a script for the PREP phase of RPM building"),
('build-script=', None,
"Specify a script for the BUILD phase of RPM building"),
(
'prep-script=', None,
'Specify a script for the PREP phase of RPM building',
),
(
'build-script=', None,
'Specify a script for the BUILD phase of RPM building',
),
('pre-install=', None,
"Specify a script for the pre-INSTALL phase of RPM building"),
('install-script=', None,
"Specify a script for the INSTALL phase of RPM building"),
('post-install=', None,
"Specify a script for the post-INSTALL phase of RPM building"),
(
'pre-install=', None,
'Specify a script for the pre-INSTALL phase of RPM building',
),
(
'install-script=', None,
'Specify a script for the INSTALL phase of RPM building',
),
(
'post-install=', None,
'Specify a script for the post-INSTALL phase of RPM building',
),
('pre-uninstall=', None,
"Specify a script for the pre-UNINSTALL phase of RPM building"),
('post-uninstall=', None,
"Specify a script for the post-UNINSTALL phase of RPM building"),
(
'pre-uninstall=', None,
'Specify a script for the pre-UNINSTALL phase of RPM building',
),
(
'post-uninstall=', None,
'Specify a script for the post-UNINSTALL phase of RPM building',
),
('clean-script=', None,
"Specify a script for the CLEAN phase of RPM building"),
(
'clean-script=', None,
'Specify a script for the CLEAN phase of RPM building',
),
('verify-script=', None,
"Specify a script for the VERIFY phase of the RPM build"),
(
'verify-script=', None,
'Specify a script for the VERIFY phase of the RPM build',
),
# Allow a packager to explicitly force an architecture
('force-arch=', None,
"Force an architecture onto the RPM build process"),
(
'force-arch=', None,
'Force an architecture onto the RPM build process',
),
('quiet', 'q',
"Run the INSTALL phase of RPM building in quiet mode"),
]
(
'quiet', 'q',
'Run the INSTALL phase of RPM building in quiet mode',
),
]
boolean_options = ['keep-temp', 'use-rpm-opt-flags', 'rpm3-mode',
'no-autoreq', 'quiet']
negative_opt = {'no-keep-temp': 'keep-temp',
'no-rpm-opt-flags': 'use-rpm-opt-flags',
'rpm2-mode': 'rpm3-mode'}
boolean_options = [
'keep-temp', 'use-rpm-opt-flags', 'rpm3-mode',
'no-autoreq', 'quiet',
]
negative_opt = {
'no-keep-temp': 'keep-temp',
'no-rpm-opt-flags': 'use-rpm-opt-flags',
'rpm2-mode': 'rpm3-mode',
}
def initialize_options(self):
self.bdist_base = None
@ -182,24 +272,29 @@ class bdist_rpm(Command):
if self.rpm_base is None:
if not self.rpm3_mode:
raise DistutilsOptionError(
"you must specify --rpm-base in RPM 2 mode")
self.rpm_base = os.path.join(self.bdist_base, "rpm")
'you must specify --rpm-base in RPM 2 mode',
)
self.rpm_base = os.path.join(self.bdist_base, 'rpm')
if self.python is None:
if self.fix_python:
self.python = sys.executable
else:
self.python = "python3"
self.python = 'python3'
elif self.fix_python:
raise DistutilsOptionError(
"--python and --fix-python are mutually exclusive options")
'--python and --fix-python are mutually exclusive options',
)
if os.name != 'posix':
raise DistutilsPlatformError("don't know how to create RPM "
"distributions on platform %s" % os.name)
raise DistutilsPlatformError(
"don't know how to create RPM "
'distributions on platform %s' % os.name,
)
if self.binary_only and self.source_only:
raise DistutilsOptionError(
"cannot supply both '--source-only' and '--binary-only'")
"cannot supply both '--source-only' and '--binary-only'",
)
# don't pass CFLAGS to pure python distributions
if not self.distribution.has_ext_modules():
@ -209,10 +304,14 @@ class bdist_rpm(Command):
self.finalize_package_data()
def finalize_package_data(self):
self.ensure_string('group', "Development/Libraries")
self.ensure_string('vendor',
"%s <%s>" % (self.distribution.get_contact(),
self.distribution.get_contact_email()))
self.ensure_string('group', 'Development/Libraries')
self.ensure_string(
'vendor',
'{} <{}>'.format(
self.distribution.get_contact(),
self.distribution.get_contact_email(),
),
)
self.ensure_string('packager')
self.ensure_string_list('doc_files')
if isinstance(self.doc_files, list):
@ -220,13 +319,13 @@ class bdist_rpm(Command):
if os.path.exists(readme) and readme not in self.doc_files:
self.doc_files.append(readme)
self.ensure_string('release', "1")
self.ensure_string('release', '1')
self.ensure_string('serial') # should it be an int?
self.ensure_string('distribution_name')
self.ensure_string('changelog')
# Format changelog correctly
# Format changelog correctly
self.changelog = self._format_changelog(self.changelog)
self.ensure_filename('icon')
@ -255,11 +354,11 @@ class bdist_rpm(Command):
def run(self):
if DEBUG:
print("before _get_package_data():")
print("vendor =", self.vendor)
print("packager =", self.packager)
print("doc_files =", self.doc_files)
print("changelog =", self.changelog)
print('before _get_package_data():')
print('vendor =', self.vendor)
print('packager =', self.packager)
print('doc_files =', self.doc_files)
print('changelog =', self.changelog)
# make directories
if self.spec_only:
@ -274,14 +373,20 @@ class bdist_rpm(Command):
# Spec file goes into 'dist_dir' if '--spec-only specified',
# build/rpm.<plat> otherwise.
spec_path = os.path.join(spec_dir,
"%s.spec" % self.distribution.get_name())
self.execute(write_file,
(spec_path,
self._make_spec_file()),
"writing '%s'" % spec_path)
spec_path = os.path.join(
spec_dir,
'%s.spec' % self.distribution.get_name(),
)
self.execute(
write_file,
(
spec_path,
self._make_spec_file(),
),
"writing '%s'" % spec_path,
)
if self.spec_only: # stop if requested
if self.spec_only: # stop if requested
return
# Make a source distribution and copy to SOURCES directory with
@ -304,13 +409,14 @@ class bdist_rpm(Command):
self.copy_file(self.icon, source_dir)
else:
raise DistutilsFileError(
"icon file '%s' does not exist" % self.icon)
"icon file '%s' does not exist" % self.icon,
)
# build package
log.info("building RPMs")
log.info('building RPMs')
rpm_cmd = ['rpmbuild']
if self.source_only: # what kind of RPMs?
if self.source_only: # what kind of RPMs?
rpm_cmd.append('-bs')
elif self.binary_only:
rpm_cmd.append('-bb')
@ -318,8 +424,10 @@ class bdist_rpm(Command):
rpm_cmd.append('-ba')
rpm_cmd.extend(['--define', '__python %s' % self.python])
if self.rpm3_mode:
rpm_cmd.extend(['--define',
'_topdir %s' % os.path.abspath(self.rpm_base)])
rpm_cmd.extend([
'--define',
'_topdir %s' % os.path.abspath(self.rpm_base),
])
if not self.keep_temp:
rpm_cmd.append('--clean')
@ -331,11 +439,12 @@ class bdist_rpm(Command):
# file
# Note that some of these may not be really built (if the file
# list is empty)
nvr_string = "%{name}-%{version}-%{release}"
src_rpm = nvr_string + ".src.rpm"
non_src_rpm = "%{arch}/" + nvr_string + ".%{arch}.rpm"
q_cmd = r"rpm -q --qf '%s %s\n' --specfile '%s'" % (
src_rpm, non_src_rpm, spec_path)
nvr_string = '%{name}-%{version}-%{release}'
src_rpm = nvr_string + '.src.rpm'
non_src_rpm = '%{arch}/' + nvr_string + '.%{arch}.rpm'
q_cmd = r"rpm -q --qf '{} {}\n' --specfile '{}'".format(
src_rpm, non_src_rpm, spec_path,
)
out = os.popen(q_cmd)
try:
@ -346,7 +455,7 @@ class bdist_rpm(Command):
if not line:
break
l = line.strip().split()
assert(len(l) == 2)
assert (len(l) == 2)
binary_rpms.append(l[1])
# The source rpm is named after the first entry in the spec file
if source_rpm is None:
@ -354,7 +463,7 @@ class bdist_rpm(Command):
status = out.close()
if status:
raise DistutilsExecError("Failed to execute: %s" % repr(q_cmd))
raise DistutilsExecError('Failed to execute: %s' % repr(q_cmd))
finally:
out.close()
@ -369,21 +478,25 @@ class bdist_rpm(Command):
if not self.binary_only:
srpm = os.path.join(rpm_dir['SRPMS'], source_rpm)
assert(os.path.exists(srpm))
assert (os.path.exists(srpm))
self.move_file(srpm, self.dist_dir)
filename = os.path.join(self.dist_dir, source_rpm)
self.distribution.dist_files.append(
('bdist_rpm', pyversion, filename))
('bdist_rpm', pyversion, filename),
)
if not self.source_only:
for rpm in binary_rpms:
rpm = os.path.join(rpm_dir['RPMS'], rpm)
if os.path.exists(rpm):
self.move_file(rpm, self.dist_dir)
filename = os.path.join(self.dist_dir,
os.path.basename(rpm))
filename = os.path.join(
self.dist_dir,
os.path.basename(rpm),
)
self.distribution.dist_files.append(
('bdist_rpm', pyversion, filename))
('bdist_rpm', pyversion, filename),
)
def _dist_path(self, path):
return os.path.join(self.dist_dir, os.path.basename(path))
@ -395,12 +508,12 @@ class bdist_rpm(Command):
# definitions and headers
spec_file = [
'%define name ' + self.distribution.get_name(),
'%define version ' + self.distribution.get_version().replace('-','_'),
'%define version ' + self.distribution.get_version().replace('-', '_'),
'%define unmangled_version ' + self.distribution.get_version(),
'%define release ' + self.release.replace('-','_'),
'%define release ' + self.release.replace('-', '_'),
'',
'Summary: ' + self.distribution.get_description(),
]
]
# Workaround for #14443 which affects some RPM based systems such as
# RHEL6 (and probably derivatives)
@ -408,10 +521,12 @@ class bdist_rpm(Command):
# Generate a potential replacement value for __os_install_post (whilst
# normalizing the whitespace to simplify the test for whether the
# invocation of brp-python-bytecompile passes in __python):
vendor_hook = '\n'.join([' %s \\' % line.strip()
for line in vendor_hook.splitlines()])
problem = "brp-python-bytecompile \\\n"
fixed = "brp-python-bytecompile %{__python} \\\n"
vendor_hook = '\n'.join([
' %s \\' % line.strip()
for line in vendor_hook.splitlines()
])
problem = 'brp-python-bytecompile \\\n'
fixed = 'brp-python-bytecompile %{__python} \\\n'
fixed_hook = vendor_hook.replace(problem, fixed)
if fixed_hook != vendor_hook:
spec_file.append('# Workaround for http://bugs.python.org/issue14443')
@ -427,7 +542,8 @@ class bdist_rpm(Command):
spec_file.extend([
'Name: %{name}',
'Version: %{version}',
'Release: %{release}',])
'Release: %{release}',
])
# XXX yuck! this filename is available from the "sdist" command,
# but only after it has run: and we create the spec file before
@ -448,21 +564,21 @@ class bdist_rpm(Command):
if not self.distribution.has_ext_modules():
spec_file.append('BuildArch: noarch')
else:
spec_file.append( 'BuildArch: %s' % self.force_arch )
spec_file.append('BuildArch: %s' % self.force_arch)
for field in ('Vendor',
'Packager',
'Provides',
'Requires',
'Conflicts',
'Obsoletes',
):
for field in (
'Vendor',
'Packager',
'Provides',
'Requires',
'Conflicts',
'Obsoletes',
):
val = getattr(self, field.lower())
if isinstance(val, list):
spec_file.append('%s: %s' % (field, ' '.join(val)))
spec_file.append('{}: {}'.format(field, ' '.join(val)))
elif val is not None:
spec_file.append('%s: %s' % (field, val))
spec_file.append('{}: {}'.format(field, val))
if self.distribution.get_url() != 'UNKNOWN':
spec_file.append('Url: ' + self.distribution.get_url())
@ -471,8 +587,10 @@ class bdist_rpm(Command):
spec_file.append('Distribution: ' + self.distribution_name)
if self.build_requires:
spec_file.append('BuildRequires: ' +
' '.join(self.build_requires))
spec_file.append(
'BuildRequires: ' +
' '.join(self.build_requires),
)
if self.icon:
spec_file.append('Icon: ' + os.path.basename(self.icon))
@ -483,8 +601,8 @@ class bdist_rpm(Command):
spec_file.extend([
'',
'%description',
self.distribution.get_long_description()
])
self.distribution.get_long_description(),
])
# put locale descriptions into spec file
# XXX again, suppressed because config file syntax doesn't
@ -498,8 +616,8 @@ class bdist_rpm(Command):
# rpm scripts
# figure out default build script
def_setup_call = "%s %s" % (self.python,os.path.basename(sys.argv[0]))
def_build = "%s build" % def_setup_call
def_setup_call = '{} {}'.format(self.python, os.path.basename(sys.argv[0]))
def_build = '%s build' % def_setup_call
if self.use_rpm_opt_flags:
def_build = 'env CFLAGS="$RPM_OPT_FLAGS" ' + def_build
@ -509,14 +627,16 @@ class bdist_rpm(Command):
# that we open and interpolate into the spec file, but the defaults
# are just text that we drop in as-is. Hmmm.
install_cmd = ('%s install -O1 --root=$RPM_BUILD_ROOT '
'--record=INSTALLED_FILES') % def_setup_call
install_cmd = (
'%s install -O1 --root=$RPM_BUILD_ROOT '
'--record=INSTALLED_FILES'
) % def_setup_call
script_options = [
('prep', 'prep_script', "%setup -n %{name}-%{unmangled_version}"),
('prep', 'prep_script', '%setup -n %{name}-%{unmangled_version}'),
('build', 'build_script', def_build),
('install', 'install_script', install_cmd),
('clean', 'clean_script', "rm -rf $RPM_BUILD_ROOT"),
('clean', 'clean_script', 'rm -rf $RPM_BUILD_ROOT'),
('verifyscript', 'verify_script', None),
('pre', 'pre_install', None),
('post', 'post_install', None),
@ -531,20 +651,20 @@ class bdist_rpm(Command):
if val or default:
spec_file.extend([
'',
'%' + rpm_opt,])
'%' + rpm_opt,
])
if val:
with open(val) as f:
spec_file.extend(f.read().split('\n'))
else:
spec_file.append(default)
# files section
spec_file.extend([
'',
'%files -f INSTALLED_FILES',
'%defattr(-,root,root)',
])
])
if self.doc_files:
spec_file.append('%doc ' + ' '.join(self.doc_files))
@ -552,7 +672,8 @@ class bdist_rpm(Command):
if self.changelog:
spec_file.extend([
'',
'%changelog',])
'%changelog',
])
spec_file.extend(self.changelog)
return spec_file

View file

@ -2,69 +2,103 @@
Implements the Distutils 'bdist_wininst' command: create a windows installer
exe-program."""
from __future__ import annotations
import os
import sys
import warnings
from distutils import log
from distutils.core import Command
from distutils.util import get_platform
from distutils.dir_util import remove_tree
from distutils.errors import *
from distutils.sysconfig import get_python_version
from distutils import log
from distutils.util import get_platform
class bdist_wininst(Command):
description = "create an executable installer for MS Windows"
description = 'create an executable installer for MS Windows'
user_options = [('bdist-dir=', None,
"temporary directory for creating the distribution"),
('plat-name=', 'p',
"platform name to embed in generated filenames "
"(default: %s)" % get_platform()),
('keep-temp', 'k',
"keep the pseudo-installation tree around after " +
"creating the distribution archive"),
('target-version=', None,
"require a specific python version" +
" on the target system"),
('no-target-compile', 'c',
"do not compile .py to .pyc on the target system"),
('no-target-optimize', 'o',
"do not compile .py to .pyo (optimized) "
"on the target system"),
('dist-dir=', 'd',
"directory to put final built distributions in"),
('bitmap=', 'b',
"bitmap to use for the installer instead of python-powered logo"),
('title=', 't',
"title to display on the installer background instead of default"),
('skip-build', None,
"skip rebuilding everything (for testing/debugging)"),
('install-script=', None,
"basename of installation script to be run after "
"installation or before deinstallation"),
('pre-install-script=', None,
"Fully qualified filename of a script to be run before "
"any files are installed. This script need not be in the "
"distribution"),
('user-access-control=', None,
"specify Vista's UAC handling - 'none'/default=no "
"handling, 'auto'=use UAC if target Python installed for "
"all users, 'force'=always use UAC"),
]
user_options = [
(
'bdist-dir=', None,
'temporary directory for creating the distribution',
),
(
'plat-name=', 'p',
'platform name to embed in generated filenames '
'(default: %s)' % get_platform(),
),
(
'keep-temp', 'k',
'keep the pseudo-installation tree around after ' +
'creating the distribution archive',
),
(
'target-version=', None,
'require a specific python version' +
' on the target system',
),
(
'no-target-compile', 'c',
'do not compile .py to .pyc on the target system',
),
(
'no-target-optimize', 'o',
'do not compile .py to .pyo (optimized) '
'on the target system',
),
(
'dist-dir=', 'd',
'directory to put final built distributions in',
),
(
'bitmap=', 'b',
'bitmap to use for the installer instead of python-powered logo',
),
(
'title=', 't',
'title to display on the installer background instead of default',
),
(
'skip-build', None,
'skip rebuilding everything (for testing/debugging)',
),
(
'install-script=', None,
'basename of installation script to be run after '
'installation or before deinstallation',
),
(
'pre-install-script=', None,
'Fully qualified filename of a script to be run before '
'any files are installed. This script need not be in the '
'distribution',
),
(
'user-access-control=', None,
"specify Vista's UAC handling - 'none'/default=no "
"handling, 'auto'=use UAC if target Python installed for "
"all users, 'force'=always use UAC",
),
]
boolean_options = ['keep-temp', 'no-target-compile', 'no-target-optimize',
'skip-build']
boolean_options = [
'keep-temp', 'no-target-compile', 'no-target-optimize',
'skip-build',
]
# bpo-10945: bdist_wininst requires mbcs encoding only available on Windows
_unsupported = (sys.platform != "win32")
_unsupported = (sys.platform != 'win32')
def __init__(self, *args, **kw):
super().__init__(*args, **kw)
warnings.warn("bdist_wininst command is deprecated since Python 3.8, "
"use bdist_wheel (wheel packages) instead",
DeprecationWarning, 2)
warnings.warn(
'bdist_wininst command is deprecated since Python 3.8, '
'use bdist_wheel (wheel packages) instead',
DeprecationWarning, 2,
)
def initialize_options(self):
self.bdist_dir = None
@ -81,7 +115,6 @@ class bdist_wininst(Command):
self.pre_install_script = None
self.user_access_control = None
def finalize_options(self):
self.set_undefined_options('bdist', ('skip_build', 'skip_build'))
@ -96,20 +129,22 @@ class bdist_wininst(Command):
self.bdist_dir = os.path.join(bdist_base, 'wininst')
if not self.target_version:
self.target_version = ""
self.target_version = ''
if not self.skip_build and self.distribution.has_ext_modules():
short_version = get_python_version()
if self.target_version and self.target_version != short_version:
raise DistutilsOptionError(
"target version can only be %s, or the '--skip-build'" \
" option must be specified" % (short_version,))
"target version can only be %s, or the '--skip-build'"
' option must be specified' % (short_version,),
)
self.target_version = short_version
self.set_undefined_options('bdist',
('dist_dir', 'dist_dir'),
('plat_name', 'plat_name'),
)
self.set_undefined_options(
'bdist',
('dist_dir', 'dist_dir'),
('plat_name', 'plat_name'),
)
if self.install_script:
for script in self.distribution.scripts:
@ -117,16 +152,22 @@ class bdist_wininst(Command):
break
else:
raise DistutilsOptionError(
"install_script '%s' not found in scripts"
% self.install_script)
"install_script '%s' not found in scripts"
% self.install_script,
)
def run(self):
if (sys.platform != "win32" and
(self.distribution.has_ext_modules() or
self.distribution.has_c_libraries())):
raise DistutilsPlatformError \
("distribution contains extensions and/or C libraries; "
"must be compiled on a Windows 32 platform")
if (
sys.platform != 'win32' and
(
self.distribution.has_ext_modules() or
self.distribution.has_c_libraries()
)
):
raise DistutilsPlatformError(
'distribution contains extensions and/or C libraries; '
'must be compiled on a Windows 32 platform',
)
if not self.skip_build:
self.run_command('build')
@ -151,12 +192,14 @@ class bdist_wininst(Command):
# version.
target_version = self.target_version
if not target_version:
assert self.skip_build, "Should have already checked this"
assert self.skip_build, 'Should have already checked this'
target_version = '%d.%d' % sys.version_info[:2]
plat_specifier = ".%s-%s" % (self.plat_name, target_version)
plat_specifier = '.{}-{}'.format(self.plat_name, target_version)
build = self.get_finalized_command('build')
build.build_lib = os.path.join(build.build_base,
'lib' + plat_specifier)
build.build_lib = os.path.join(
build.build_base,
'lib' + plat_specifier,
)
# Use a custom scheme for the zip-file, because we have to decide
# at installation time which scheme to use.
@ -164,11 +207,13 @@ class bdist_wininst(Command):
value = key.upper()
if key == 'headers':
value = value + '/Include/$dist_name'
setattr(install,
'install_' + key,
value)
setattr(
install,
'install_' + key,
value,
)
log.info("installing to %s", self.bdist_dir)
log.info('installing to %s', self.bdist_dir)
install.ensure_finalized()
# avoid warning of 'install_lib' about installing
@ -184,16 +229,20 @@ class bdist_wininst(Command):
from tempfile import mktemp
archive_basename = mktemp()
fullname = self.distribution.get_fullname()
arcname = self.make_archive(archive_basename, "zip",
root_dir=self.bdist_dir)
arcname = self.make_archive(
archive_basename, 'zip',
root_dir=self.bdist_dir,
)
# create an exe containing the zip-file
self.create_exe(arcname, fullname, self.bitmap)
if self.distribution.has_ext_modules():
pyversion = get_python_version()
else:
pyversion = 'any'
self.distribution.dist_files.append(('bdist_wininst', pyversion,
self.get_installer_filename(fullname)))
self.distribution.dist_files.append((
'bdist_wininst', pyversion,
self.get_installer_filename(fullname),
))
# remove the zip-file again
log.debug("removing temporary file '%s'", arcname)
os.remove(arcname)
@ -207,7 +256,7 @@ class bdist_wininst(Command):
metadata = self.distribution.metadata
# Write the [metadata] section.
lines.append("[metadata]")
lines.append('[metadata]')
# 'info' will be displayed in the installer's dialog box,
# describing the items to be installed.
@ -215,37 +264,41 @@ class bdist_wininst(Command):
# Escape newline characters
def escape(s):
return s.replace("\n", "\\n")
return s.replace('\n', '\\n')
for name in ["author", "author_email", "description", "maintainer",
"maintainer_email", "name", "url", "version"]:
data = getattr(metadata, name, "")
for name in [
'author', 'author_email', 'description', 'maintainer',
'maintainer_email', 'name', 'url', 'version',
]:
data = getattr(metadata, name, '')
if data:
info = info + ("\n %s: %s" % \
(name.capitalize(), escape(data)))
lines.append("%s=%s" % (name, escape(data)))
info = info + (
'\n %s: %s' %
(name.capitalize(), escape(data))
)
lines.append('{}={}'.format(name, escape(data)))
# The [setup] section contains entries controlling
# the installer runtime.
lines.append("\n[Setup]")
lines.append('\n[Setup]')
if self.install_script:
lines.append("install_script=%s" % self.install_script)
lines.append("info=%s" % escape(info))
lines.append("target_compile=%d" % (not self.no_target_compile))
lines.append("target_optimize=%d" % (not self.no_target_optimize))
lines.append('install_script=%s' % self.install_script)
lines.append('info=%s' % escape(info))
lines.append('target_compile=%d' % (not self.no_target_compile))
lines.append('target_optimize=%d' % (not self.no_target_optimize))
if self.target_version:
lines.append("target_version=%s" % self.target_version)
lines.append('target_version=%s' % self.target_version)
if self.user_access_control:
lines.append("user_access_control=%s" % self.user_access_control)
lines.append('user_access_control=%s' % self.user_access_control)
title = self.title or self.distribution.get_fullname()
lines.append("title=%s" % escape(title))
lines.append('title=%s' % escape(title))
import time
import distutils
build_info = "Built %s with distutils-%s" % \
build_info = 'Built %s with distutils-%s' % \
(time.ctime(time.time()), distutils.__version__)
lines.append("build_info=%s" % build_info)
return "\n".join(lines)
lines.append('build_info=%s' % build_info)
return '\n'.join(lines)
def create_exe(self, arcname, fullname, bitmap=None):
import struct
@ -255,37 +308,39 @@ class bdist_wininst(Command):
cfgdata = self.get_inidata()
installer_name = self.get_installer_filename(fullname)
self.announce("creating %s" % installer_name)
self.announce('creating %s' % installer_name)
if bitmap:
with open(bitmap, "rb") as f:
with open(bitmap, 'rb') as f:
bitmapdata = f.read()
bitmaplen = len(bitmapdata)
else:
bitmaplen = 0
with open(installer_name, "wb") as file:
with open(installer_name, 'wb') as file:
file.write(self.get_exe_bytes())
if bitmap:
file.write(bitmapdata)
# Convert cfgdata from unicode to ascii, mbcs encoded
if isinstance(cfgdata, str):
cfgdata = cfgdata.encode("mbcs")
cfgdata = cfgdata.encode('mbcs')
# Append the pre-install script
cfgdata = cfgdata + b"\0"
cfgdata = cfgdata + b'\0'
if self.pre_install_script:
# We need to normalize newlines, so we open in text mode and
# convert back to bytes. "latin-1" simply avoids any possible
# failures.
with open(self.pre_install_script, "r",
encoding="latin-1") as script:
script_data = script.read().encode("latin-1")
cfgdata = cfgdata + script_data + b"\n\0"
with open(
self.pre_install_script,
encoding='latin-1',
) as script:
script_data = script.read().encode('latin-1')
cfgdata = cfgdata + script_data + b'\n\0'
else:
# empty pre-install script
cfgdata = cfgdata + b"\0"
cfgdata = cfgdata + b'\0'
file.write(cfgdata)
# The 'magic number' 0x1234567B is used to make sure that the
@ -293,13 +348,14 @@ class bdist_wininst(Command):
# expects. If the layout changes, increment that number, make
# the corresponding changes to the wininst.exe sources, and
# recompile them.
header = struct.pack("<iii",
0x1234567B, # tag
len(cfgdata), # length
bitmaplen, # number of bytes in bitmap
)
header = struct.pack(
'<iii',
0x1234567B, # tag
len(cfgdata), # length
bitmaplen, # number of bytes in bitmap
)
file.write(header)
with open(arcname, "rb") as f:
with open(arcname, 'rb') as f:
file.write(f.read())
def get_installer_filename(self, fullname):
@ -307,12 +363,16 @@ class bdist_wininst(Command):
if self.target_version:
# if we create an installer for a specific python version,
# it's better to include this in the name
installer_name = os.path.join(self.dist_dir,
"%s.%s-py%s.exe" %
(fullname, self.plat_name, self.target_version))
installer_name = os.path.join(
self.dist_dir,
'%s.%s-py%s.exe' %
(fullname, self.plat_name, self.target_version),
)
else:
installer_name = os.path.join(self.dist_dir,
"%s.%s.exe" % (fullname, self.plat_name))
installer_name = os.path.join(
self.dist_dir,
'{}.{}.exe'.format(fullname, self.plat_name),
)
return installer_name
def get_exe_bytes(self):
@ -330,15 +390,15 @@ class bdist_wininst(Command):
# use what we use
# string compares seem wrong, but are what sysconfig.py itself uses
if self.target_version and self.target_version < cur_version:
if self.target_version < "2.4":
if self.target_version < '2.4':
bv = '6.0'
elif self.target_version == "2.4":
elif self.target_version == '2.4':
bv = '7.1'
elif self.target_version == "2.5":
elif self.target_version == '2.5':
bv = '8.0'
elif self.target_version <= "3.2":
elif self.target_version <= '3.2':
bv = '9.0'
elif self.target_version <= "3.4":
elif self.target_version <= '3.4':
bv = '10.0'
else:
bv = '14.0'
@ -355,7 +415,6 @@ class bdist_wininst(Command):
major = CRT_ASSEMBLY_VERSION.partition('.')[0]
bv = major + '.0'
# wininst-x.y.exe is in the same directory as this file
directory = os.path.dirname(__file__)
# we must use a wininst-x.y.exe built with the same C compiler
@ -369,8 +428,8 @@ class bdist_wininst(Command):
else:
sfix = ''
filename = os.path.join(directory, "wininst-%s%s.exe" % (bv, sfix))
f = open(filename, "rb")
filename = os.path.join(directory, 'wininst-{}{}.exe'.format(bv, sfix))
f = open(filename, 'rb')
try:
return f.read()
finally:

View file

@ -1,8 +1,11 @@
"""distutils.command.build
Implements the Distutils 'build' command."""
from __future__ import annotations
import os
import sys
import sys, os
from distutils.core import Command
from distutils.errors import DistutilsOptionError
from distutils.util import get_platform
@ -15,43 +18,69 @@ def show_compilers():
class build(Command):
description = "build everything needed to install"
description = 'build everything needed to install'
user_options = [
('build-base=', 'b',
"base directory for build library"),
('build-purelib=', None,
"build directory for platform-neutral distributions"),
('build-platlib=', None,
"build directory for platform-specific distributions"),
('build-lib=', None,
"build directory for all distribution (defaults to either " +
"build-purelib or build-platlib"),
('build-scripts=', None,
"build directory for scripts"),
('build-temp=', 't',
"temporary build directory"),
('plat-name=', 'p',
"platform name to build for, if supported "
"(default: %s)" % get_platform()),
('compiler=', 'c',
"specify the compiler type"),
('parallel=', 'j',
"number of parallel build jobs"),
('debug', 'g',
"compile extensions and libraries with debugging information"),
('force', 'f',
"forcibly build everything (ignore file timestamps)"),
('executable=', 'e',
"specify final destination interpreter path (build.py)"),
]
(
'build-base=', 'b',
'base directory for build library',
),
(
'build-purelib=', None,
'build directory for platform-neutral distributions',
),
(
'build-platlib=', None,
'build directory for platform-specific distributions',
),
(
'build-lib=', None,
'build directory for all distribution (defaults to either ' +
'build-purelib or build-platlib',
),
(
'build-scripts=', None,
'build directory for scripts',
),
(
'build-temp=', 't',
'temporary build directory',
),
(
'plat-name=', 'p',
'platform name to build for, if supported '
'(default: %s)' % get_platform(),
),
(
'compiler=', 'c',
'specify the compiler type',
),
(
'parallel=', 'j',
'number of parallel build jobs',
),
(
'debug', 'g',
'compile extensions and libraries with debugging information',
),
(
'force', 'f',
'forcibly build everything (ignore file timestamps)',
),
(
'executable=', 'e',
'specify final destination interpreter path (build.py)',
),
]
boolean_options = ['debug', 'force']
help_options = [
('help-compiler', None,
"list available compilers", show_compilers),
]
(
'help-compiler', None,
'list available compilers', show_compilers,
),
]
def initialize_options(self):
self.build_base = 'build'
@ -78,10 +107,11 @@ class build(Command):
# other platforms.
if os.name != 'nt':
raise DistutilsOptionError(
"--plat-name only supported on Windows (try "
"using './configure --help' on your platform)")
'--plat-name only supported on Windows (try '
"using './configure --help' on your platform)",
)
plat_specifier = ".%s-%d.%d" % (self.plat_name, *sys.version_info[:2])
plat_specifier = '.%s-%d.%d' % (self.plat_name, *sys.version_info[:2])
# Make it so Python 2.x and Python 2.x with --with-pydebug don't
# share the same build directories. Doing so confuses the build
@ -95,8 +125,10 @@ class build(Command):
if self.build_purelib is None:
self.build_purelib = os.path.join(self.build_base, 'lib')
if self.build_platlib is None:
self.build_platlib = os.path.join(self.build_base,
'lib' + plat_specifier)
self.build_platlib = os.path.join(
self.build_base,
'lib' + plat_specifier,
)
# 'build_lib' is the actual directory that we will use for this
# particular module distribution -- if user didn't supply it, pick
@ -110,11 +142,15 @@ class build(Command):
# 'build_temp' -- temporary directory for compiler turds,
# "build/temp.<plat>"
if self.build_temp is None:
self.build_temp = os.path.join(self.build_base,
'temp' + plat_specifier)
self.build_temp = os.path.join(
self.build_base,
'temp' + plat_specifier,
)
if self.build_scripts is None:
self.build_scripts = os.path.join(self.build_base,
'scripts-%d.%d' % sys.version_info[:2])
self.build_scripts = os.path.join(
self.build_base,
'scripts-%d.%d' % sys.version_info[:2],
)
if self.executable is None and sys.executable:
self.executable = os.path.normpath(sys.executable)
@ -123,7 +159,7 @@ class build(Command):
try:
self.parallel = int(self.parallel)
except ValueError:
raise DistutilsOptionError("parallel should be an integer")
raise DistutilsOptionError('parallel should be an integer')
def run(self):
# Run all relevant sub-commands. This will be some subset of:
@ -134,7 +170,6 @@ class build(Command):
for cmd_name in self.get_sub_commands():
self.run_command(cmd_name)
# -- Predicates for the sub-command list ---------------------------
def has_pure_modules(self):
@ -149,9 +184,9 @@ class build(Command):
def has_scripts(self):
return self.distribution.has_scripts()
sub_commands = [('build_py', has_pure_modules),
('build_clib', has_c_libraries),
('build_ext', has_ext_modules),
('build_scripts', has_scripts),
]
sub_commands = [
('build_py', has_pure_modules),
('build_clib', has_c_libraries),
('build_ext', has_ext_modules),
('build_scripts', has_scripts),
]

View file

@ -3,8 +3,6 @@
Implements the Distutils 'build_clib' command, to build a C/C++ library
that is included in the module distribution and needed by an extension
module."""
# XXX this module has *lots* of code ripped-off quite transparently from
# build_ext.py -- not surprisingly really, as the work required to build
# a static library from a collection of C source files is not really all
@ -13,12 +11,15 @@ module."""
# necessary refactoring to account for the overlap in code between the
# two modules, mainly because a number of subtle details changed in the
# cut 'n paste. Sigh.
from __future__ import annotations
import os
from distutils import log
from distutils.core import Command
from distutils.errors import *
from distutils.sysconfig import customize_compiler
from distutils import log
def show_compilers():
from distutils.ccompiler import show_compilers
@ -27,27 +28,39 @@ def show_compilers():
class build_clib(Command):
description = "build C/C++ libraries used by Python extensions"
description = 'build C/C++ libraries used by Python extensions'
user_options = [
('build-clib=', 'b',
"directory to build C/C++ libraries to"),
('build-temp=', 't',
"directory to put temporary build by-products"),
('debug', 'g',
"compile with debugging information"),
('force', 'f',
"forcibly build everything (ignore file timestamps)"),
('compiler=', 'c',
"specify the compiler type"),
]
(
'build-clib=', 'b',
'directory to build C/C++ libraries to',
),
(
'build-temp=', 't',
'directory to put temporary build by-products',
),
(
'debug', 'g',
'compile with debugging information',
),
(
'force', 'f',
'forcibly build everything (ignore file timestamps)',
),
(
'compiler=', 'c',
'specify the compiler type',
),
]
boolean_options = ['debug', 'force']
help_options = [
('help-compiler', None,
"list available compilers", show_compilers),
]
(
'help-compiler', None,
'list available compilers', show_compilers,
),
]
def initialize_options(self):
self.build_clib = None
@ -64,19 +77,20 @@ class build_clib(Command):
self.force = 0
self.compiler = None
def finalize_options(self):
# This might be confusing: both build-clib and build-temp default
# to build-temp as defined by the "build" command. This is because
# I think that C libraries are really just temporary build
# by-products, at least from the point of view of building Python
# extensions -- but I want to keep my options open.
self.set_undefined_options('build',
('build_temp', 'build_clib'),
('build_temp', 'build_temp'),
('compiler', 'compiler'),
('debug', 'debug'),
('force', 'force'))
self.set_undefined_options(
'build',
('build_temp', 'build_clib'),
('build_temp', 'build_temp'),
('compiler', 'compiler'),
('debug', 'debug'),
('force', 'force'),
)
self.libraries = self.distribution.libraries
if self.libraries:
@ -90,23 +104,24 @@ class build_clib(Command):
# XXX same as for build_ext -- what about 'self.define' and
# 'self.undef' ?
def run(self):
if not self.libraries:
return
# Yech -- this is cut 'n pasted from build_ext.py!
from distutils.ccompiler import new_compiler
self.compiler = new_compiler(compiler=self.compiler,
dry_run=self.dry_run,
force=self.force)
self.compiler = new_compiler(
compiler=self.compiler,
dry_run=self.dry_run,
force=self.force,
)
customize_compiler(self.compiler)
if self.include_dirs is not None:
self.compiler.set_include_dirs(self.include_dirs)
if self.define is not None:
# 'define' option is a list of (name,value) tuples
for (name,value) in self.define:
for (name, value) in self.define:
self.compiler.define_macro(name, value)
if self.undef is not None:
for macro in self.undef:
@ -114,7 +129,6 @@ class build_clib(Command):
self.build_libraries(self.libraries)
def check_library_list(self, libraries):
"""Ensure that the list of libraries is valid.
@ -127,29 +141,34 @@ class build_clib(Command):
"""
if not isinstance(libraries, list):
raise DistutilsSetupError(
"'libraries' option must be a list of tuples")
"'libraries' option must be a list of tuples",
)
for lib in libraries:
if not isinstance(lib, tuple) and len(lib) != 2:
raise DistutilsSetupError(
"each element of 'libraries' must a 2-tuple")
"each element of 'libraries' must a 2-tuple",
)
name, build_info = lib
if not isinstance(name, str):
raise DistutilsSetupError(
"first element of each tuple in 'libraries' "
"must be a string (the library name)")
"first element of each tuple in 'libraries' "
'must be a string (the library name)',
)
if '/' in name or (os.sep != '/' and os.sep in name):
raise DistutilsSetupError("bad library name '%s': "
"may not contain directory separators" % lib[0])
raise DistutilsSetupError(
"bad library name '%s': "
'may not contain directory separators' % lib[0],
)
if not isinstance(build_info, dict):
raise DistutilsSetupError(
"second element of each tuple in 'libraries' "
"must be a dictionary (build info)")
"second element of each tuple in 'libraries' "
'must be a dictionary (build info)',
)
def get_library_names(self):
# Assume the library list is valid -- 'check_library_list()' is
@ -162,7 +181,6 @@ class build_clib(Command):
lib_names.append(lib_name)
return lib_names
def get_source_files(self):
self.check_library_list(self.libraries)
filenames = []
@ -170,22 +188,23 @@ class build_clib(Command):
sources = build_info.get('sources')
if sources is None or not isinstance(sources, (list, tuple)):
raise DistutilsSetupError(
"in 'libraries' option (library '%s'), "
"'sources' must be present and must be "
"a list of source filenames" % lib_name)
"in 'libraries' option (library '%s'), "
"'sources' must be present and must be "
'a list of source filenames' % lib_name,
)
filenames.extend(sources)
return filenames
def build_libraries(self, libraries):
for (lib_name, build_info) in libraries:
sources = build_info.get('sources')
if sources is None or not isinstance(sources, (list, tuple)):
raise DistutilsSetupError(
"in 'libraries' option (library '%s'), "
"'sources' must be present and must be "
"a list of source filenames" % lib_name)
"in 'libraries' option (library '%s'), "
"'sources' must be present and must be "
'a list of source filenames' % lib_name,
)
sources = list(sources)
log.info("building '%s' library", lib_name)
@ -195,15 +214,19 @@ class build_clib(Command):
# files in a temporary build directory.)
macros = build_info.get('macros')
include_dirs = build_info.get('include_dirs')
objects = self.compiler.compile(sources,
output_dir=self.build_temp,
macros=macros,
include_dirs=include_dirs,
debug=self.debug)
objects = self.compiler.compile(
sources,
output_dir=self.build_temp,
macros=macros,
include_dirs=include_dirs,
debug=self.debug,
)
# Now "link" the object files together into a static library.
# (On Unix at least, this isn't really linking -- it just
# builds an archive. Whatever.)
self.compiler.create_static_lib(objects, lib_name,
output_dir=self.build_clib,
debug=self.debug)
self.compiler.create_static_lib(
objects, lib_name,
output_dir=self.build_clib,
debug=self.debug,
)

View file

@ -3,37 +3,39 @@
Implements the Distutils 'build_ext' command, for building extension
modules (currently limited to C extensions, should accommodate C++
extensions ASAP)."""
from __future__ import annotations
import contextlib
import os
import re
import sys
from distutils.core import Command
from distutils.errors import *
from distutils.sysconfig import customize_compiler, get_python_version
from distutils.sysconfig import get_config_h_filename
from distutils.dep_util import newer_group
from distutils.extension import Extension
from distutils.util import get_platform
from distutils import log
from . import py37compat
from site import USER_BASE
from distutils import log
from distutils.core import Command
from distutils.dep_util import newer_group
from distutils.errors import *
from distutils.extension import Extension
from distutils.sysconfig import customize_compiler
from distutils.sysconfig import get_config_h_filename
from distutils.sysconfig import get_python_version
from distutils.util import get_platform
from . import py37compat
# An extension name is just a dot-separated list of Python NAMEs (ie.
# the same as a fully-qualified module name).
extension_name_re = re.compile \
(r'^[a-zA-Z_][a-zA-Z_0-9]*(\.[a-zA-Z_][a-zA-Z_0-9]*)*$')
extension_name_re = re.compile(r'^[a-zA-Z_][a-zA-Z_0-9]*(\.[a-zA-Z_][a-zA-Z_0-9]*)*$')
def show_compilers ():
def show_compilers():
from distutils.ccompiler import show_compilers
show_compilers()
class build_ext(Command):
description = "build C/C++ extensions (compile/link to build directory)"
description = 'build C/C++ extensions (compile/link to build directory)'
# XXX thoughts on how to deal with complex command-line options like
# these, i.e. how to make it so fancy_getopt can suck them off the
@ -55,54 +57,94 @@ class build_ext(Command):
sep_by = " (separated by '%s')" % os.pathsep
user_options = [
('build-lib=', 'b',
"directory for compiled extension modules"),
('build-temp=', 't',
"directory for temporary files (build by-products)"),
('plat-name=', 'p',
"platform name to cross-compile for, if supported "
"(default: %s)" % get_platform()),
('inplace', 'i',
"ignore build-lib and put compiled extensions into the source " +
"directory alongside your pure Python modules"),
('include-dirs=', 'I',
"list of directories to search for header files" + sep_by),
('define=', 'D',
"C preprocessor macros to define"),
('undef=', 'U',
"C preprocessor macros to undefine"),
('libraries=', 'l',
"external C libraries to link with"),
('library-dirs=', 'L',
"directories to search for external C libraries" + sep_by),
('rpath=', 'R',
"directories to search for shared C libraries at runtime"),
('link-objects=', 'O',
"extra explicit link objects to include in the link"),
('debug', 'g',
"compile/link with debugging information"),
('force', 'f',
"forcibly build everything (ignore file timestamps)"),
('compiler=', 'c',
"specify the compiler type"),
('parallel=', 'j',
"number of parallel build jobs"),
('swig-cpp', None,
"make SWIG create C++ files (default is C)"),
('swig-opts=', None,
"list of SWIG command line options"),
('swig=', None,
"path to the SWIG executable"),
('user', None,
"add user include, library and rpath")
]
(
'build-lib=', 'b',
'directory for compiled extension modules',
),
(
'build-temp=', 't',
'directory for temporary files (build by-products)',
),
(
'plat-name=', 'p',
'platform name to cross-compile for, if supported '
'(default: %s)' % get_platform(),
),
(
'inplace', 'i',
'ignore build-lib and put compiled extensions into the source ' +
'directory alongside your pure Python modules',
),
(
'include-dirs=', 'I',
'list of directories to search for header files' + sep_by,
),
(
'define=', 'D',
'C preprocessor macros to define',
),
(
'undef=', 'U',
'C preprocessor macros to undefine',
),
(
'libraries=', 'l',
'external C libraries to link with',
),
(
'library-dirs=', 'L',
'directories to search for external C libraries' + sep_by,
),
(
'rpath=', 'R',
'directories to search for shared C libraries at runtime',
),
(
'link-objects=', 'O',
'extra explicit link objects to include in the link',
),
(
'debug', 'g',
'compile/link with debugging information',
),
(
'force', 'f',
'forcibly build everything (ignore file timestamps)',
),
(
'compiler=', 'c',
'specify the compiler type',
),
(
'parallel=', 'j',
'number of parallel build jobs',
),
(
'swig-cpp', None,
'make SWIG create C++ files (default is C)',
),
(
'swig-opts=', None,
'list of SWIG command line options',
),
(
'swig=', None,
'path to the SWIG executable',
),
(
'user', None,
'add user include, library and rpath',
),
]
boolean_options = ['inplace', 'debug', 'force', 'swig-cpp', 'user']
help_options = [
('help-compiler', None,
"list available compilers", show_compilers),
]
(
'help-compiler', None,
'list available compilers', show_compilers,
),
]
def initialize_options(self):
self.extensions = None
@ -131,15 +173,16 @@ class build_ext(Command):
def finalize_options(self):
from distutils import sysconfig
self.set_undefined_options('build',
('build_lib', 'build_lib'),
('build_temp', 'build_temp'),
('compiler', 'compiler'),
('debug', 'debug'),
('force', 'force'),
('parallel', 'parallel'),
('plat_name', 'plat_name'),
)
self.set_undefined_options(
'build',
('build_lib', 'build_lib'),
('build_temp', 'build_temp'),
('compiler', 'compiler'),
('debug', 'debug'),
('force', 'force'),
('parallel', 'parallel'),
('plat_name', 'plat_name'),
)
if self.package is None:
self.package = self.distribution.ext_package
@ -165,7 +208,8 @@ class build_ext(Command):
self.include_dirs.extend(py_include.split(os.path.pathsep))
if plat_py_include != py_include:
self.include_dirs.extend(
plat_py_include.split(os.path.pathsep))
plat_py_include.split(os.path.pathsep),
)
self.ensure_string_list('libraries')
self.ensure_string_list('link_objects')
@ -195,9 +239,9 @@ class build_ext(Command):
if sys.base_exec_prefix != sys.prefix: # Issue 16116
self.library_dirs.append(os.path.join(sys.base_exec_prefix, 'libs'))
if self.debug:
self.build_temp = os.path.join(self.build_temp, "Debug")
self.build_temp = os.path.join(self.build_temp, 'Debug')
else:
self.build_temp = os.path.join(self.build_temp, "Release")
self.build_temp = os.path.join(self.build_temp, 'Release')
# Append the source distribution include and library directories,
# this allows distutils on windows to work in the source tree
@ -222,9 +266,13 @@ class build_ext(Command):
if sys.platform[:6] == 'cygwin':
if not sysconfig.python_build:
# building third party extensions
self.library_dirs.append(os.path.join(sys.prefix, "lib",
"python" + get_python_version(),
"config"))
self.library_dirs.append(
os.path.join(
sys.prefix, 'lib',
'python' + get_python_version(),
'config',
),
)
else:
# building python standard extensions
self.library_dirs.append('.')
@ -262,8 +310,8 @@ class build_ext(Command):
# Finally add the user include and library directories if requested
if self.user:
user_include = os.path.join(USER_BASE, "include")
user_lib = os.path.join(USER_BASE, "lib")
user_include = os.path.join(USER_BASE, 'include')
user_lib = os.path.join(USER_BASE, 'lib')
if os.path.isdir(user_include):
self.include_dirs.append(user_include)
if os.path.isdir(user_lib):
@ -274,7 +322,7 @@ class build_ext(Command):
try:
self.parallel = int(self.parallel)
except ValueError:
raise DistutilsOptionError("parallel should be an integer")
raise DistutilsOptionError('parallel should be an integer')
def run(self):
from distutils.ccompiler import new_compiler
@ -304,10 +352,12 @@ class build_ext(Command):
# Setup the CCompiler object that we'll use to do all the
# compiling and linking
self.compiler = new_compiler(compiler=self.compiler,
verbose=self.verbose,
dry_run=self.dry_run,
force=self.force)
self.compiler = new_compiler(
compiler=self.compiler,
verbose=self.verbose,
dry_run=self.dry_run,
force=self.force,
)
customize_compiler(self.compiler)
# If we are cross-compiling, init the compiler now (if we are not
# cross-compiling, init would not hurt, but people may rely on
@ -352,34 +402,42 @@ class build_ext(Command):
"""
if not isinstance(extensions, list):
raise DistutilsSetupError(
"'ext_modules' option must be a list of Extension instances")
"'ext_modules' option must be a list of Extension instances",
)
for i, ext in enumerate(extensions):
if isinstance(ext, Extension):
continue # OK! (assume type-checking done
# by Extension constructor)
# by Extension constructor)
if not isinstance(ext, tuple) or len(ext) != 2:
raise DistutilsSetupError(
"each element of 'ext_modules' option must be an "
"Extension instance or 2-tuple")
"each element of 'ext_modules' option must be an "
'Extension instance or 2-tuple',
)
ext_name, build_info = ext
log.warn("old-style (ext_name, build_info) tuple found in "
"ext_modules for extension '%s' "
"-- please convert to Extension instance", ext_name)
log.warn(
'old-style (ext_name, build_info) tuple found in '
"ext_modules for extension '%s' "
'-- please convert to Extension instance', ext_name,
)
if not (isinstance(ext_name, str) and
extension_name_re.match(ext_name)):
if not (
isinstance(ext_name, str) and
extension_name_re.match(ext_name)
):
raise DistutilsSetupError(
"first element of each tuple in 'ext_modules' "
"must be the extension name (a string)")
"first element of each tuple in 'ext_modules' "
'must be the extension name (a string)',
)
if not isinstance(build_info, dict):
raise DistutilsSetupError(
"second element of each tuple in 'ext_modules' "
"must be a dictionary (build info)")
"second element of each tuple in 'ext_modules' "
'must be a dictionary (build info)',
)
# OK, the (ext_name, build_info) dict is type-safe: convert it
# to an Extension instance.
@ -387,9 +445,11 @@ class build_ext(Command):
# Easy stuff: one-to-one mapping from dict elements to
# instance attributes.
for key in ('include_dirs', 'library_dirs', 'libraries',
'extra_objects', 'extra_compile_args',
'extra_link_args'):
for key in (
'include_dirs', 'library_dirs', 'libraries',
'extra_objects', 'extra_compile_args',
'extra_link_args',
):
val = build_info.get(key)
if val is not None:
setattr(ext, key, val)
@ -397,8 +457,10 @@ class build_ext(Command):
# Medium-easy stuff: same syntax/semantics, different names.
ext.runtime_library_dirs = build_info.get('rpath')
if 'def_file' in build_info:
log.warn("'def_file' element of build info dict "
"no longer supported")
log.warn(
"'def_file' element of build info dict "
'no longer supported',
)
# Non-trivial stuff: 'macros' split into 'define_macros'
# and 'undef_macros'.
@ -409,8 +471,9 @@ class build_ext(Command):
for macro in macros:
if not (isinstance(macro, tuple) and len(macro) in (1, 2)):
raise DistutilsSetupError(
"'macros' element of build info dict "
"must be 1- or 2-tuple")
"'macros' element of build info dict "
'must be 1- or 2-tuple',
)
if len(macro) == 1:
ext.undef_macros.append(macro[0])
elif len(macro) == 2:
@ -463,8 +526,10 @@ class build_ext(Command):
return
with ThreadPoolExecutor(max_workers=workers) as executor:
futures = [executor.submit(self.build_extension, ext)
for ext in self.extensions]
futures = [
executor.submit(self.build_extension, ext)
for ext in self.extensions
]
for ext, fut in zip(self.extensions, futures):
with self._filter_build_errors(ext):
fut.result()
@ -481,16 +546,19 @@ class build_ext(Command):
except (CCompilerError, DistutilsError, CompileError) as e:
if not ext.optional:
raise
self.warn('building extension "%s" failed: %s' %
(ext.name, e))
self.warn(
'building extension "%s" failed: %s' %
(ext.name, e),
)
def build_extension(self, ext):
sources = ext.sources
if sources is None or not isinstance(sources, (list, tuple)):
raise DistutilsSetupError(
"in 'ext_modules' option (extension '%s'), "
"'sources' must be present and must be "
"a list of source filenames" % ext.name)
"in 'ext_modules' option (extension '%s'), "
"'sources' must be present and must be "
'a list of source filenames' % ext.name,
)
# sort to make the resulting .so file build reproducible
sources = sorted(sources)
@ -527,13 +595,15 @@ class build_ext(Command):
for undef in ext.undef_macros:
macros.append((undef,))
objects = self.compiler.compile(sources,
output_dir=self.build_temp,
macros=macros,
include_dirs=ext.include_dirs,
debug=self.debug,
extra_postargs=extra_args,
depends=ext.depends)
objects = self.compiler.compile(
sources,
output_dir=self.build_temp,
macros=macros,
include_dirs=ext.include_dirs,
debug=self.debug,
extra_postargs=extra_args,
depends=ext.depends,
)
# XXX outdated variable, kept here in case third-part code
# needs it.
@ -558,7 +628,8 @@ class build_ext(Command):
export_symbols=self.get_export_symbols(ext),
debug=self.debug,
build_temp=self.build_temp,
target_lang=language)
target_lang=language,
)
def swig_sources(self, sources, extension):
"""Walk the list of source files in 'sources', looking for SWIG
@ -576,7 +647,7 @@ class build_ext(Command):
# the temp dir.
if self.swig_cpp:
log.warn("--swig-cpp is deprecated - use --swig-opts=-c++")
log.warn('--swig-cpp is deprecated - use --swig-opts=-c++')
if self.swig_cpp or ('-c++' in self.swig_opts) or \
('-c++' in extension.swig_opts):
@ -586,7 +657,7 @@ class build_ext(Command):
for source in sources:
(base, ext) = os.path.splitext(source)
if ext == ".i": # SWIG interface file
if ext == '.i': # SWIG interface file
new_sources.append(base + '_wrap' + target_ext)
swig_sources.append(source)
swig_targets[source] = new_sources[-1]
@ -597,10 +668,10 @@ class build_ext(Command):
return new_sources
swig = self.swig or self.find_swig()
swig_cmd = [swig, "-python"]
swig_cmd = [swig, '-python']
swig_cmd.extend(self.swig_opts)
if self.swig_cpp:
swig_cmd.append("-c++")
swig_cmd.append('-c++')
# Do not override commandline arguments
if not self.swig_opts:
@ -609,8 +680,8 @@ class build_ext(Command):
for source in swig_sources:
target = swig_targets[source]
log.info("swigging %s to %s", source, target)
self.spawn(swig_cmd + ["-o", target, source])
log.info('swigging %s to %s', source, target)
self.spawn(swig_cmd + ['-o', target, source])
return new_sources
@ -619,22 +690,23 @@ class build_ext(Command):
just "swig" -- it should be in the PATH. Tries a bit harder on
Windows.
"""
if os.name == "posix":
return "swig"
elif os.name == "nt":
if os.name == 'posix':
return 'swig'
elif os.name == 'nt':
# Look for SWIG in its standard installation directory on
# Windows (or so I presume!). If we find it there, great;
# if not, act like Unix and assume it's in the PATH.
for vers in ("1.3", "1.2", "1.1"):
fn = os.path.join("c:\\swig%s" % vers, "swig.exe")
for vers in ('1.3', '1.2', '1.1'):
fn = os.path.join('c:\\swig%s' % vers, 'swig.exe')
if os.path.isfile(fn):
return fn
else:
return "swig.exe"
return 'swig.exe'
else:
raise DistutilsPlatformError(
"I don't know how to find (much less run) SWIG "
"on platform '%s'" % os.name)
"I don't know how to find (much less run) SWIG "
"on platform '%s'" % os.name,
)
# -- Name generators -----------------------------------------------
# (extension names, filenames, whatever)
@ -652,7 +724,7 @@ class build_ext(Command):
# no further work needed
# returning :
# build_dir/package/path/filename
filename = os.path.join(*modpath[:-1]+[filename])
filename = os.path.join(*modpath[:-1] + [filename])
return os.path.join(self.build_lib, filename)
# the inplace option requires to find the package directory
@ -698,9 +770,9 @@ class build_ext(Command):
except UnicodeEncodeError:
suffix = 'U_' + name.encode('punycode').replace(b'-', b'_').decode('ascii')
else:
suffix = "_" + name
suffix = '_' + name
initfunc_name = "PyInit" + suffix
initfunc_name = 'PyInit' + suffix
if initfunc_name not in ext.export_symbols:
ext.export_symbols.append(initfunc_name)
return ext.export_symbols
@ -715,14 +787,16 @@ class build_ext(Command):
# pyconfig.h that MSVC groks. The other Windows compilers all seem
# to need it mentioned explicitly, though, so that's what we do.
# Append '_d' to the python import library on debug builds.
if sys.platform == "win32":
if sys.platform == 'win32':
from distutils._msvccompiler import MSVCCompiler
if not isinstance(self.compiler, MSVCCompiler):
template = "python%d%d"
template = 'python%d%d'
if self.debug:
template = template + '_d'
pythonlib = (template %
(sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff))
pythonlib = (
template %
(sys.hexversion >> 24, (sys.hexversion >> 16) & 0xff)
)
# don't extend ext.libraries, it may be shared with other
# extensions, it is a reference to the original list
return ext.libraries + [pythonlib]

View file

@ -1,16 +1,18 @@
"""distutils.command.build_py
Implements the Distutils 'build_py' command."""
from __future__ import annotations
import os
import importlib.util
import sys
import glob
import importlib.util
import os
import sys
from distutils import log
from distutils.core import Command
from distutils.errors import *
from distutils.util import convert_path
from distutils import log
class build_py (Command):
@ -18,16 +20,18 @@ class build_py (Command):
user_options = [
('build-lib=', 'd', "directory to \"build\" (copy) to"),
('compile', 'c', "compile .py to .pyc"),
('compile', 'c', 'compile .py to .pyc'),
('no-compile', None, "don't compile .py files [default]"),
('optimize=', 'O',
"also compile with optimization: -O1 for \"python -O\", "
"-O2 for \"python -OO\", and -O0 to disable [default: -O0]"),
('force', 'f', "forcibly build everything (ignore file timestamps)"),
]
(
'optimize=', 'O',
"also compile with optimization: -O1 for \"python -O\", "
"-O2 for \"python -OO\", and -O0 to disable [default: -O0]",
),
('force', 'f', 'forcibly build everything (ignore file timestamps)'),
]
boolean_options = ['compile', 'force']
negative_opt = {'no-compile' : 'compile'}
negative_opt = {'no-compile': 'compile'}
def initialize_options(self):
self.build_lib = None
@ -40,9 +44,11 @@ class build_py (Command):
self.force = None
def finalize_options(self):
self.set_undefined_options('build',
('build_lib', 'build_lib'),
('force', 'force'))
self.set_undefined_options(
'build',
('build_lib', 'build_lib'),
('force', 'force'),
)
# Get the distribution options that are aliases for build_py
# options -- list of packages and list of modules.
@ -62,7 +68,7 @@ class build_py (Command):
self.optimize = int(self.optimize)
assert 0 <= self.optimize <= 2
except (ValueError, AssertionError):
raise DistutilsOptionError("optimize must be 0, 1, or 2")
raise DistutilsOptionError('optimize must be 0, 1, or 2')
def run(self):
# XXX copy_file by default preserves atime and mtime. IMHO this is
@ -109,26 +115,30 @@ class build_py (Command):
# Length of path to strip from found files
plen = 0
if src_dir:
plen = len(src_dir)+1
plen = len(src_dir) + 1
# Strip directory from globbed filenames
filenames = [
file[plen:] for file in self.find_data_files(package, src_dir)
]
]
data.append((package, src_dir, build_dir, filenames))
return data
def find_data_files(self, package, src_dir):
"""Return filenames for package's data files in 'src_dir'"""
globs = (self.package_data.get('', [])
+ self.package_data.get(package, []))
globs = (
self.package_data.get('', []) +
self.package_data.get(package, [])
)
files = []
for pattern in globs:
# Each pattern has to be converted to a platform-specific path
filelist = glob.glob(os.path.join(glob.escape(src_dir), convert_path(pattern)))
# Files that match more than one pattern are only added once
files.extend([fn for fn in filelist if fn not in files
and os.path.isfile(fn)])
files.extend([
fn for fn in filelist if fn not in files and
os.path.isfile(fn)
])
return files
def build_package_data(self):
@ -138,8 +148,10 @@ class build_py (Command):
for filename in filenames:
target = os.path.join(build_dir, filename)
self.mkpath(os.path.dirname(target))
self.copy_file(os.path.join(src_dir, filename), target,
preserve_mode=False)
self.copy_file(
os.path.join(src_dir, filename), target,
preserve_mode=False,
)
def get_package_dir(self, package):
"""Return the directory, relative to the top of the source
@ -185,23 +197,29 @@ class build_py (Command):
# assume exists. Also, os.path.exists and isdir don't know about
# my "empty string means current dir" convention, so we have to
# circumvent them.
if package_dir != "":
if package_dir != '':
if not os.path.exists(package_dir):
raise DistutilsFileError(
"package directory '%s' does not exist" % package_dir)
"package directory '%s' does not exist" % package_dir,
)
if not os.path.isdir(package_dir):
raise DistutilsFileError(
"supposed package directory '%s' exists, "
"but is not a directory" % package_dir)
"supposed package directory '%s' exists, "
'but is not a directory' % package_dir,
)
# Require __init__.py for all but the "root package"
if package:
init_py = os.path.join(package_dir, "__init__.py")
init_py = os.path.join(package_dir, '__init__.py')
if os.path.isfile(init_py):
return init_py
else:
log.warn(("package init file '%s' not found " +
"(or not a regular file)"), init_py)
log.warn(
(
"package init file '%s' not found " +
'(or not a regular file)'
), init_py,
)
# Either not in a package at all (__init__.py not expected), or
# __init__.py doesn't exist -- so don't return the filename.
@ -209,14 +227,14 @@ class build_py (Command):
def check_module(self, module, module_file):
if not os.path.isfile(module_file):
log.warn("file %s (for module %s) not found", module_file, module)
log.warn('file %s (for module %s) not found', module_file, module)
return False
else:
return True
def find_package_modules(self, package, package_dir):
self.check_package(package, package_dir)
module_files = glob.glob(os.path.join(glob.escape(package_dir), "*.py"))
module_files = glob.glob(os.path.join(glob.escape(package_dir), '*.py'))
modules = []
setup_script = os.path.abspath(self.distribution.script_name)
@ -226,7 +244,7 @@ class build_py (Command):
module = os.path.splitext(os.path.basename(f))[0]
modules.append((package, module, f))
else:
self.debug_print("excluding %s" % setup_script)
self.debug_print('excluding %s' % setup_script)
return modules
def find_modules(self):
@ -268,12 +286,12 @@ class build_py (Command):
init_py = self.check_package(package, package_dir)
packages[package] = (package_dir, 1)
if init_py:
modules.append((package, "__init__", init_py))
modules.append((package, '__init__', init_py))
# XXX perhaps we should also check for just .pyc files
# (so greedy closed-source bastards can distribute Python
# modules too)
module_file = os.path.join(package_dir, module_base + ".py")
module_file = os.path.join(package_dir, module_base + '.py')
if not self.check_module(module, module_file):
continue
@ -301,7 +319,7 @@ class build_py (Command):
return [module[-1] for module in self.find_all_modules()]
def get_module_outfile(self, build_dir, package, module):
outfile_path = [build_dir] + list(package) + [module + ".py"]
outfile_path = [build_dir] + list(package) + [module + '.py']
return os.path.join(*outfile_path)
def get_outputs(self, include_bytecode=1):
@ -313,17 +331,23 @@ class build_py (Command):
outputs.append(filename)
if include_bytecode:
if self.compile:
outputs.append(importlib.util.cache_from_source(
filename, optimization=''))
outputs.append(
importlib.util.cache_from_source(
filename, optimization='',
),
)
if self.optimize > 0:
outputs.append(importlib.util.cache_from_source(
filename, optimization=self.optimize))
outputs.append(
importlib.util.cache_from_source(
filename, optimization=self.optimize,
),
)
outputs += [
os.path.join(build_dir, filename)
for package, src_dir, build_dir, filenames in self.data_files
for filename in filenames
]
]
return outputs
@ -332,7 +356,8 @@ class build_py (Command):
package = package.split('.')
elif not isinstance(package, (list, tuple)):
raise TypeError(
"'package' must be a string (dot-separated), list, or tuple")
"'package' must be a string (dot-separated), list, or tuple",
)
# Now put the module source file into the "build" area -- this is
# easy, we just copy it somewhere under self.build_lib (the build
@ -385,8 +410,12 @@ class build_py (Command):
# method of the "install_lib" command, except for the determination
# of the 'prefix' string. Hmmm.
if self.compile:
byte_compile(files, optimize=0,
force=self.force, prefix=prefix, dry_run=self.dry_run)
byte_compile(
files, optimize=0,
force=self.force, prefix=prefix, dry_run=self.dry_run,
)
if self.optimize > 0:
byte_compile(files, optimize=self.optimize,
force=self.force, prefix=prefix, dry_run=self.dry_run)
byte_compile(
files, optimize=self.optimize,
force=self.force, prefix=prefix, dry_run=self.dry_run,
)

View file

@ -1,32 +1,35 @@
"""distutils.command.build_scripts
Implements the Distutils 'build_scripts' command."""
from __future__ import annotations
import os, re
import os
import re
import tokenize
from stat import ST_MODE
from distutils import log
from distutils import sysconfig
from distutils.core import Command
from distutils.dep_util import newer
from distutils.util import convert_path
from distutils import log
import tokenize
# check if Python is called on the first line with this expression
first_line_re = re.compile(b'^#!.*python[0-9.]*([ \t].*)?$')
class build_scripts(Command):
description = "\"build\" scripts (copy and fixup #! line)"
user_options = [
('build-dir=', 'd', "directory to \"build\" (copy) to"),
('force', 'f', "forcibly build everything (ignore file timestamps"),
('executable=', 'e', "specify final destination interpreter path"),
]
('force', 'f', 'forcibly build everything (ignore file timestamps'),
('executable=', 'e', 'specify final destination interpreter path'),
]
boolean_options = ['force']
def initialize_options(self):
self.build_dir = None
self.scripts = None
@ -35,10 +38,12 @@ class build_scripts(Command):
self.outfiles = None
def finalize_options(self):
self.set_undefined_options('build',
('build_scripts', 'build_dir'),
('force', 'force'),
('executable', 'executable'))
self.set_undefined_options(
'build',
('build_scripts', 'build_dir'),
('force', 'force'),
('executable', 'executable'),
)
self.scripts = self.distribution.scripts
def get_source_files(self):
@ -49,7 +54,6 @@ class build_scripts(Command):
return
self.copy_scripts()
def copy_scripts(self):
r"""Copy each script listed in 'self.scripts'; if it's marked as a
Python script in the Unix way (first line matches 'first_line_re',
@ -66,14 +70,14 @@ class build_scripts(Command):
outfiles.append(outfile)
if not self.force and not newer(script, outfile):
log.debug("not copying %s (up-to-date)", script)
log.debug('not copying %s (up-to-date)', script)
continue
# Always open the file, but ignore failures in dry-run mode --
# that way, we'll get accurate feedback if we can read the
# script.
try:
f = open(script, "rb")
f = open(script, 'rb')
except OSError:
if not self.dry_run:
raise
@ -83,7 +87,7 @@ class build_scripts(Command):
f.seek(0)
first_line = f.readline()
if not first_line:
self.warn("%s is an empty file (skipping)" % script)
self.warn('%s is an empty file (skipping)' % script)
continue
match = first_line_re.match(first_line)
@ -92,19 +96,24 @@ class build_scripts(Command):
post_interp = match.group(1) or b''
if adjust:
log.info("copying and adjusting %s -> %s", script,
self.build_dir)
log.info(
'copying and adjusting %s -> %s', script,
self.build_dir,
)
updated_files.append(outfile)
if not self.dry_run:
if not sysconfig.python_build:
executable = self.executable
else:
executable = os.path.join(
sysconfig.get_config_var("BINDIR"),
"python%s%s" % (sysconfig.get_config_var("VERSION"),
sysconfig.get_config_var("EXE")))
sysconfig.get_config_var('BINDIR'),
'python{}{}'.format(
sysconfig.get_config_var('VERSION'),
sysconfig.get_config_var('EXE'),
),
)
executable = os.fsencode(executable)
shebang = b"#!" + executable + post_interp + b"\n"
shebang = b'#!' + executable + post_interp + b'\n'
# Python parser starts to read a script using UTF-8 until
# it gets a #coding:xxx cookie. The shebang has to be the
# first line of a file, the #coding:xxx cookie cannot be
@ -114,8 +123,9 @@ class build_scripts(Command):
shebang.decode('utf-8')
except UnicodeDecodeError:
raise ValueError(
"The shebang ({!r}) is not decodable "
"from utf-8".format(shebang))
'The shebang ({!r}) is not decodable '
'from utf-8'.format(shebang),
)
# If the script is encoded to a custom encoding (use a
# #coding:xxx cookie), the shebang has to be decodable from
# the script encoding too.
@ -123,10 +133,11 @@ class build_scripts(Command):
shebang.decode(encoding)
except UnicodeDecodeError:
raise ValueError(
"The shebang ({!r}) is not decodable "
"from the script encoding ({})"
.format(shebang, encoding))
with open(outfile, "wb") as outf:
'The shebang ({!r}) is not decodable '
'from the script encoding ({})'
.format(shebang, encoding),
)
with open(outfile, 'wb') as outf:
outf.write(shebang)
outf.writelines(f.readlines())
if f:
@ -140,13 +151,15 @@ class build_scripts(Command):
if os.name == 'posix':
for file in outfiles:
if self.dry_run:
log.info("changing mode of %s", file)
log.info('changing mode of %s', file)
else:
oldmode = os.stat(file)[ST_MODE] & 0o7777
newmode = (oldmode | 0o555) & 0o7777
if newmode != oldmode:
log.info("changing mode of %s from %o to %o",
file, oldmode, newmode)
log.info(
'changing mode of %s from %o to %o',
file, oldmode, newmode,
)
os.chmod(file, newmode)
# XXX should we modify self.outfiles?
return outfiles, updated_files

View file

@ -2,6 +2,8 @@
Implements the Distutils 'check' command.
"""
from __future__ import annotations
from distutils.core import Command
from distutils.errors import DistutilsSetupError
@ -14,17 +16,23 @@ try:
class SilentReporter(Reporter):
def __init__(self, source, report_level, halt_level, stream=None,
debug=0, encoding='ascii', error_handler='replace'):
def __init__(
self, source, report_level, halt_level, stream=None,
debug=0, encoding='ascii', error_handler='replace',
):
self.messages = []
Reporter.__init__(self, source, report_level, halt_level, stream,
debug, encoding, error_handler)
Reporter.__init__(
self, source, report_level, halt_level, stream,
debug, encoding, error_handler,
)
def system_message(self, level, message, *children, **kwargs):
self.messages.append((level, message, children, kwargs))
return nodes.system_message(message, level=level,
type=self.levels[level],
*children, **kwargs)
return nodes.system_message(
message, level=level,
type=self.levels[level],
*children, **kwargs,
)
HAS_DOCUTILS = True
except Exception:
@ -32,16 +40,25 @@ except Exception:
# indicate that docutils is not ported to Py3k.
HAS_DOCUTILS = False
class check(Command):
"""This command checks the meta-data of the package.
"""
description = ("perform some checks on the package")
user_options = [('metadata', 'm', 'Verify meta-data'),
('restructuredtext', 'r',
('Checks if long string meta-data syntax '
'are reStructuredText-compliant')),
('strict', 's',
'Will exit with an error if a check fails')]
description = ('perform some checks on the package')
user_options = [
('metadata', 'm', 'Verify meta-data'),
(
'restructuredtext', 'r',
(
'Checks if long string meta-data syntax '
'are reStructuredText-compliant'
),
),
(
'strict', 's',
'Will exit with an error if a check fails',
),
]
boolean_options = ['metadata', 'restructuredtext', 'strict']
@ -95,19 +112,25 @@ class check(Command):
missing.append(attr)
if missing:
self.warn("missing required meta-data: %s" % ', '.join(missing))
self.warn('missing required meta-data: %s' % ', '.join(missing))
if metadata.author:
if not metadata.author_email:
self.warn("missing meta-data: if 'author' supplied, " +
"'author_email' should be supplied too")
self.warn(
"missing meta-data: if 'author' supplied, " +
"'author_email' should be supplied too",
)
elif metadata.maintainer:
if not metadata.maintainer_email:
self.warn("missing meta-data: if 'maintainer' supplied, " +
"'maintainer_email' should be supplied too")
self.warn(
"missing meta-data: if 'maintainer' supplied, " +
"'maintainer_email' should be supplied too",
)
else:
self.warn("missing meta-data: either (author and author_email) " +
"or (maintainer and maintainer_email) " +
"should be supplied")
self.warn(
'missing meta-data: either (author and author_email) ' +
'or (maintainer and maintainer_email) ' +
'should be supplied',
)
def check_restructuredtext(self):
"""Checks if the long string fields are reST-compliant."""
@ -117,7 +140,7 @@ class check(Command):
if line is None:
warning = warning[1]
else:
warning = '%s (line %s)' % (warning[1], line)
warning = '{} (line {})'.format(warning[1], line)
self.warn(warning)
def _check_rst_data(self, data):
@ -129,13 +152,15 @@ class check(Command):
settings.tab_width = 4
settings.pep_references = None
settings.rfc_references = None
reporter = SilentReporter(source_path,
settings.report_level,
settings.halt_level,
stream=settings.warning_stream,
debug=settings.debug,
encoding=settings.error_encoding,
error_handler=settings.error_encoding_error_handler)
reporter = SilentReporter(
source_path,
settings.report_level,
settings.halt_level,
stream=settings.warning_stream,
debug=settings.debug,
encoding=settings.error_encoding,
error_handler=settings.error_encoding_error_handler,
)
document = nodes.document(settings, reporter, source=source_path)
document.note_source(source_path, -1)
@ -143,6 +168,7 @@ class check(Command):
parser.parse(data, document)
except AttributeError as e:
reporter.messages.append(
(-1, 'Could not finish the parsing: %s.' % e, '', {}))
(-1, 'Could not finish the parsing: %s.' % e, '', {}),
)
return reporter.messages

View file

@ -1,30 +1,44 @@
"""distutils.command.clean
Implements the Distutils 'clean' command."""
# contributed by Bastian Kleineidam <calvin@cs.uni-sb.de>, added 2000-03-18
from __future__ import annotations
import os
from distutils import log
from distutils.core import Command
from distutils.dir_util import remove_tree
from distutils import log
class clean(Command):
description = "clean up temporary files from 'build' command"
user_options = [
('build-base=', 'b',
"base build directory (default: 'build.build-base')"),
('build-lib=', None,
"build directory for all modules (default: 'build.build-lib')"),
('build-temp=', 't',
"temporary build directory (default: 'build.build-temp')"),
('build-scripts=', None,
"build directory for scripts (default: 'build.build-scripts')"),
('bdist-base=', None,
"temporary directory for built distributions"),
('all', 'a',
"remove all build output, not just temporary by-products")
(
'build-base=', 'b',
"base build directory (default: 'build.build-base')",
),
(
'build-lib=', None,
"build directory for all modules (default: 'build.build-lib')",
),
(
'build-temp=', 't',
"temporary build directory (default: 'build.build-temp')",
),
(
'build-scripts=', None,
"build directory for scripts (default: 'build.build-scripts')",
),
(
'bdist-base=', None,
'temporary directory for built distributions',
),
(
'all', 'a',
'remove all build output, not just temporary by-products',
),
]
boolean_options = ['all']
@ -38,13 +52,17 @@ class clean(Command):
self.all = None
def finalize_options(self):
self.set_undefined_options('build',
('build_base', 'build_base'),
('build_lib', 'build_lib'),
('build_scripts', 'build_scripts'),
('build_temp', 'build_temp'))
self.set_undefined_options('bdist',
('bdist_base', 'bdist_base'))
self.set_undefined_options(
'build',
('build_base', 'build_base'),
('build_lib', 'build_lib'),
('build_scripts', 'build_scripts'),
('build_temp', 'build_temp'),
)
self.set_undefined_options(
'bdist',
('bdist_base', 'bdist_base'),
)
def run(self):
# remove the build/temp.<plat> directory (unless it's already
@ -52,19 +70,25 @@ class clean(Command):
if os.path.exists(self.build_temp):
remove_tree(self.build_temp, dry_run=self.dry_run)
else:
log.debug("'%s' does not exist -- can't clean it",
self.build_temp)
log.debug(
"'%s' does not exist -- can't clean it",
self.build_temp,
)
if self.all:
# remove build directories
for directory in (self.build_lib,
self.bdist_base,
self.build_scripts):
for directory in (
self.build_lib,
self.bdist_base,
self.build_scripts,
):
if os.path.exists(directory):
remove_tree(directory, dry_run=self.dry_run)
else:
log.warn("'%s' does not exist -- can't clean it",
directory)
log.warn(
"'%s' does not exist -- can't clean it",
directory,
)
# just for the heck of it, try to remove the base build directory:
# we might have emptied it right now, but if not we don't care

View file

@ -8,42 +8,62 @@ list of standard commands. Also, this is a good place to put common
configure-like tasks: "try to compile this C code", or "figure out where
this header file lives".
"""
from __future__ import annotations
import os, re
import os
import re
from distutils import log
from distutils.core import Command
from distutils.errors import DistutilsExecError
from distutils.sysconfig import customize_compiler
from distutils import log
LANG_EXT = {"c": ".c", "c++": ".cxx"}
LANG_EXT = {'c': '.c', 'c++': '.cxx'}
class config(Command):
description = "prepare to build"
description = 'prepare to build'
user_options = [
('compiler=', None,
"specify the compiler type"),
('cc=', None,
"specify the compiler executable"),
('include-dirs=', 'I',
"list of directories to search for header files"),
('define=', 'D',
"C preprocessor macros to define"),
('undef=', 'U',
"C preprocessor macros to undefine"),
('libraries=', 'l',
"external C libraries to link with"),
('library-dirs=', 'L',
"directories to search for external C libraries"),
('noisy', None,
"show every action (compile, link, run, ...) taken"),
('dump-source', None,
"dump generated source files before attempting to compile them"),
]
(
'compiler=', None,
'specify the compiler type',
),
(
'cc=', None,
'specify the compiler executable',
),
(
'include-dirs=', 'I',
'list of directories to search for header files',
),
(
'define=', 'D',
'C preprocessor macros to define',
),
(
'undef=', 'U',
'C preprocessor macros to undefine',
),
(
'libraries=', 'l',
'external C libraries to link with',
),
(
'library-dirs=', 'L',
'directories to search for external C libraries',
),
(
'noisy', None,
'show every action (compile, link, run, ...) taken',
),
(
'dump-source', None,
'dump generated source files before attempting to compile them',
),
]
# The three standard command methods: since the "config" command
# does nothing by default, these are empty.
@ -94,8 +114,10 @@ class config(Command):
# import.
from distutils.ccompiler import CCompiler, new_compiler
if not isinstance(self.compiler, CCompiler):
self.compiler = new_compiler(compiler=self.compiler,
dry_run=self.dry_run, force=1)
self.compiler = new_compiler(
compiler=self.compiler,
dry_run=self.dry_run, force=1,
)
customize_compiler(self.compiler)
if self.include_dirs:
self.compiler.set_include_dirs(self.include_dirs)
@ -105,20 +127,20 @@ class config(Command):
self.compiler.set_library_dirs(self.library_dirs)
def _gen_temp_sourcefile(self, body, headers, lang):
filename = "_configtest" + LANG_EXT[lang]
with open(filename, "w") as file:
filename = '_configtest' + LANG_EXT[lang]
with open(filename, 'w') as file:
if headers:
for header in headers:
file.write("#include <%s>\n" % header)
file.write("\n")
file.write('#include <%s>\n' % header)
file.write('\n')
file.write(body)
if body[-1] != "\n":
file.write("\n")
if body[-1] != '\n':
file.write('\n')
return filename
def _preprocess(self, body, headers, include_dirs, lang):
src = self._gen_temp_sourcefile(body, headers, lang)
out = "_configtest.i"
out = '_configtest.i'
self.temp_files.extend([src, out])
self.compiler.preprocess(src, out, include_dirs=include_dirs)
return (src, out)
@ -132,14 +154,18 @@ class config(Command):
self.compiler.compile([src], include_dirs=include_dirs)
return (src, obj)
def _link(self, body, headers, include_dirs, libraries, library_dirs,
lang):
def _link(
self, body, headers, include_dirs, libraries, library_dirs,
lang,
):
(src, obj) = self._compile(body, headers, include_dirs, lang)
prog = os.path.splitext(os.path.basename(src))[0]
self.compiler.link_executable([obj], prog,
libraries=libraries,
library_dirs=library_dirs,
target_lang=lang)
self.compiler.link_executable(
[obj], prog,
libraries=libraries,
library_dirs=library_dirs,
target_lang=lang,
)
if self.compiler.exe_extension is not None:
prog = prog + self.compiler.exe_extension
@ -151,14 +177,13 @@ class config(Command):
if not filenames:
filenames = self.temp_files
self.temp_files = []
log.info("removing: %s", ' '.join(filenames))
log.info('removing: %s', ' '.join(filenames))
for filename in filenames:
try:
os.remove(filename)
except OSError:
pass
# XXX these ignore the dry-run flag: what to do, what to do? even if
# you want a dry-run build, you still need some sort of configuration
# info. My inclination is to make it up to the real config command to
@ -169,7 +194,7 @@ class config(Command):
# XXX need access to the header search path and maybe default macros.
def try_cpp(self, body=None, headers=None, include_dirs=None, lang="c"):
def try_cpp(self, body=None, headers=None, include_dirs=None, lang='c'):
"""Construct a source file from 'body' (a string containing lines
of C/C++ code) and 'headers' (a list of header files to include)
and run it through the preprocessor. Return true if the
@ -187,8 +212,10 @@ class config(Command):
self._clean()
return ok
def search_cpp(self, pattern, body=None, headers=None, include_dirs=None,
lang="c"):
def search_cpp(
self, pattern, body=None, headers=None, include_dirs=None,
lang='c',
):
"""Construct a source file (just like 'try_cpp()'), run it through
the preprocessor, and return true if any line of the output matches
'pattern'. 'pattern' should either be a compiled regex object or a
@ -215,7 +242,7 @@ class config(Command):
self._clean()
return match
def try_compile(self, body, headers=None, include_dirs=None, lang="c"):
def try_compile(self, body, headers=None, include_dirs=None, lang='c'):
"""Try to compile a source file built from 'body' and 'headers'.
Return true on success, false otherwise.
"""
@ -227,12 +254,14 @@ class config(Command):
except CompileError:
ok = False
log.info(ok and "success!" or "failure.")
log.info(ok and 'success!' or 'failure.')
self._clean()
return ok
def try_link(self, body, headers=None, include_dirs=None, libraries=None,
library_dirs=None, lang="c"):
def try_link(
self, body, headers=None, include_dirs=None, libraries=None,
library_dirs=None, lang='c',
):
"""Try to compile and link a source file, built from 'body' and
'headers', to executable form. Return true on success, false
otherwise.
@ -240,18 +269,22 @@ class config(Command):
from distutils.ccompiler import CompileError, LinkError
self._check_compiler()
try:
self._link(body, headers, include_dirs,
libraries, library_dirs, lang)
self._link(
body, headers, include_dirs,
libraries, library_dirs, lang,
)
ok = True
except (CompileError, LinkError):
ok = False
log.info(ok and "success!" or "failure.")
log.info(ok and 'success!' or 'failure.')
self._clean()
return ok
def try_run(self, body, headers=None, include_dirs=None, libraries=None,
library_dirs=None, lang="c"):
def try_run(
self, body, headers=None, include_dirs=None, libraries=None,
library_dirs=None, lang='c',
):
"""Try to compile, link to an executable, and run a program
built from 'body' and 'headers'. Return true on success, false
otherwise.
@ -259,24 +292,27 @@ class config(Command):
from distutils.ccompiler import CompileError, LinkError
self._check_compiler()
try:
src, obj, exe = self._link(body, headers, include_dirs,
libraries, library_dirs, lang)
src, obj, exe = self._link(
body, headers, include_dirs,
libraries, library_dirs, lang,
)
self.spawn([exe])
ok = True
except (CompileError, LinkError, DistutilsExecError):
ok = False
log.info(ok and "success!" or "failure.")
log.info(ok and 'success!' or 'failure.')
self._clean()
return ok
# -- High-level methods --------------------------------------------
# (these are the ones that are actually likely to be useful
# when implementing a real-world config command!)
def check_func(self, func, headers=None, include_dirs=None,
libraries=None, library_dirs=None, decl=0, call=0):
def check_func(
self, func, headers=None, include_dirs=None,
libraries=None, library_dirs=None, decl=0, call=0,
):
"""Determine if function 'func' is available by constructing a
source file that refers to 'func', and compiles and links it.
If everything succeeds, returns true; otherwise returns false.
@ -293,20 +329,24 @@ class config(Command):
self._check_compiler()
body = []
if decl:
body.append("int %s ();" % func)
body.append("int main () {")
body.append('int %s ();' % func)
body.append('int main () {')
if call:
body.append(" %s();" % func)
body.append(' %s();' % func)
else:
body.append(" %s;" % func)
body.append("}")
body = "\n".join(body) + "\n"
body.append(' %s;' % func)
body.append('}')
body = '\n'.join(body) + '\n'
return self.try_link(body, headers, include_dirs,
libraries, library_dirs)
return self.try_link(
body, headers, include_dirs,
libraries, library_dirs,
)
def check_lib(self, library, library_dirs=None, headers=None,
include_dirs=None, other_libraries=[]):
def check_lib(
self, library, library_dirs=None, headers=None,
include_dirs=None, other_libraries=[],
):
"""Determine if 'library' is available to be linked against,
without actually checking that any particular symbols are provided
by it. 'headers' will be used in constructing the source file to
@ -316,17 +356,24 @@ class config(Command):
has symbols that depend on other libraries.
"""
self._check_compiler()
return self.try_link("int main (void) { }", headers, include_dirs,
[library] + other_libraries, library_dirs)
return self.try_link(
'int main (void) { }', headers, include_dirs,
[library] + other_libraries, library_dirs,
)
def check_header(self, header, include_dirs=None, library_dirs=None,
lang="c"):
def check_header(
self, header, include_dirs=None, library_dirs=None,
lang='c',
):
"""Determine if the system header file named by 'header_file'
exists and can be found by the preprocessor; return true if so,
false otherwise.
"""
return self.try_cpp(body="/* No body */", headers=[header],
include_dirs=include_dirs)
return self.try_cpp(
body='/* No body */', headers=[header],
include_dirs=include_dirs,
)
def dump_file(filename, head=None):
"""Dumps a file content into log.info.

View file

@ -1,22 +1,24 @@
"""distutils.command.install
Implements the Distutils 'install' command."""
from __future__ import annotations
import sys
import os
import sys
from site import USER_BASE
from site import USER_SITE
from distutils import log
from distutils.core import Command
from distutils.debug import DEBUG
from distutils.sysconfig import get_config_vars
from distutils.errors import DistutilsOptionError
from distutils.errors import DistutilsPlatformError
from distutils.file_util import write_file
from distutils.util import convert_path, subst_vars, change_root
from distutils.sysconfig import get_config_vars
from distutils.util import change_root
from distutils.util import convert_path
from distutils.util import get_platform
from distutils.errors import DistutilsOptionError
from site import USER_BASE
from site import USER_SITE
from distutils.util import subst_vars
HAS_USER_SITE = True
WINDOWS_SCHEME = {
@ -24,7 +26,7 @@ WINDOWS_SCHEME = {
'platlib': '$base/Lib/site-packages',
'headers': '$base/Include/$dist_name',
'scripts': '$base/Scripts',
'data' : '$base',
'data': '$base',
}
INSTALL_SCHEMES = {
@ -33,31 +35,31 @@ INSTALL_SCHEMES = {
'platlib': '$platbase/$platlibdir/python$py_version_short/site-packages',
'headers': '$base/include/python$py_version_short$abiflags/$dist_name',
'scripts': '$base/bin',
'data' : '$base',
},
'data': '$base',
},
'unix_home': {
'purelib': '$base/lib/python',
'platlib': '$base/$platlibdir/python',
'headers': '$base/include/python/$dist_name',
'scripts': '$base/bin',
'data' : '$base',
},
'data': '$base',
},
'nt': WINDOWS_SCHEME,
'pypy': {
'purelib': '$base/site-packages',
'platlib': '$base/site-packages',
'headers': '$base/include/$dist_name',
'scripts': '$base/bin',
'data' : '$base',
},
'data': '$base',
},
'pypy_nt': {
'purelib': '$base/site-packages',
'platlib': '$base/site-packages',
'headers': '$base/include/$dist_name',
'scripts': '$base/Scripts',
'data' : '$base',
},
}
'data': '$base',
},
}
# user site schemes
if HAS_USER_SITE:
@ -66,8 +68,8 @@ if HAS_USER_SITE:
'platlib': '$usersite',
'headers': '$userbase/Python$py_version_nodot/Include/$dist_name',
'scripts': '$userbase/Python$py_version_nodot/Scripts',
'data' : '$userbase',
}
'data': '$userbase',
}
INSTALL_SCHEMES['unix_user'] = {
'purelib': '$usersite',
@ -75,8 +77,8 @@ if HAS_USER_SITE:
'headers':
'$userbase/include/python$py_version_short$abiflags/$dist_name',
'scripts': '$userbase/bin',
'data' : '$userbase',
}
'data': '$userbase',
}
# The keys to an installation scheme; if any new types of files are to be
# installed, be sure to add an entry to every installation scheme above,
@ -86,56 +88,86 @@ SCHEME_KEYS = ('purelib', 'platlib', 'headers', 'scripts', 'data')
class install(Command):
description = "install everything from build directory"
description = 'install everything from build directory'
user_options = [
# Select installation scheme and set base director(y|ies)
('prefix=', None,
"installation prefix"),
('exec-prefix=', None,
"(Unix only) prefix for platform-specific files"),
('home=', None,
"(Unix only) home directory to install under"),
(
'prefix=', None,
'installation prefix',
),
(
'exec-prefix=', None,
'(Unix only) prefix for platform-specific files',
),
(
'home=', None,
'(Unix only) home directory to install under',
),
# Or, just set the base director(y|ies)
('install-base=', None,
"base installation directory (instead of --prefix or --home)"),
('install-platbase=', None,
"base installation directory for platform-specific files " +
"(instead of --exec-prefix or --home)"),
('root=', None,
"install everything relative to this alternate root directory"),
(
'install-base=', None,
'base installation directory (instead of --prefix or --home)',
),
(
'install-platbase=', None,
'base installation directory for platform-specific files ' +
'(instead of --exec-prefix or --home)',
),
(
'root=', None,
'install everything relative to this alternate root directory',
),
# Or, explicitly set the installation scheme
('install-purelib=', None,
"installation directory for pure Python module distributions"),
('install-platlib=', None,
"installation directory for non-pure module distributions"),
('install-lib=', None,
"installation directory for all module distributions " +
"(overrides --install-purelib and --install-platlib)"),
(
'install-purelib=', None,
'installation directory for pure Python module distributions',
),
(
'install-platlib=', None,
'installation directory for non-pure module distributions',
),
(
'install-lib=', None,
'installation directory for all module distributions ' +
'(overrides --install-purelib and --install-platlib)',
),
('install-headers=', None,
"installation directory for C/C++ headers"),
('install-scripts=', None,
"installation directory for Python scripts"),
('install-data=', None,
"installation directory for data files"),
(
'install-headers=', None,
'installation directory for C/C++ headers',
),
(
'install-scripts=', None,
'installation directory for Python scripts',
),
(
'install-data=', None,
'installation directory for data files',
),
# Byte-compilation options -- see install_lib.py for details, as
# these are duplicated from there (but only install_lib does
# anything with them).
('compile', 'c', "compile .py to .pyc [default]"),
('compile', 'c', 'compile .py to .pyc [default]'),
('no-compile', None, "don't compile .py files"),
('optimize=', 'O',
"also compile with optimization: -O1 for \"python -O\", "
"-O2 for \"python -OO\", and -O0 to disable [default: -O0]"),
(
'optimize=', 'O',
"also compile with optimization: -O1 for \"python -O\", "
"-O2 for \"python -OO\", and -O0 to disable [default: -O0]",
),
# Miscellaneous control options
('force', 'f',
"force installation (overwrite any existing files)"),
('skip-build', None,
"skip rebuilding everything (for testing/debugging)"),
(
'force', 'f',
'force installation (overwrite any existing files)',
),
(
'skip-build', None,
'skip rebuilding everything (for testing/debugging)',
),
# Where to install documentation (eventually!)
#('doc-format=', None, "format of documentation to generate"),
@ -143,19 +175,22 @@ class install(Command):
#('install-html=', None, "directory for HTML documentation"),
#('install-info=', None, "directory for GNU info files"),
('record=', None,
"filename in which to record list of installed files"),
]
(
'record=', None,
'filename in which to record list of installed files',
),
]
boolean_options = ['compile', 'force', 'skip-build']
if HAS_USER_SITE:
user_options.append(('user', None,
"install in user site-package '%s'" % USER_SITE))
user_options.append((
'user', None,
"install in user site-package '%s'" % USER_SITE,
))
boolean_options.append('user')
negative_opt = {'no-compile' : 'compile'}
negative_opt = {'no-compile': 'compile'}
def initialize_options(self):
"""Initializes options."""
@ -228,7 +263,6 @@ class install(Command):
self.record = None
# -- Option finalizing methods -------------------------------------
# (This is rather more involved than for most commands,
# because this is where the policy for installing third-
@ -252,24 +286,30 @@ class install(Command):
# that's wrong on any platform.
if ((self.prefix or self.exec_prefix or self.home) and
(self.install_base or self.install_platbase)):
(self.install_base or self.install_platbase)):
raise DistutilsOptionError(
"must supply either prefix/exec-prefix/home or " +
"install-base/install-platbase -- not both")
'must supply either prefix/exec-prefix/home or ' +
'install-base/install-platbase -- not both',
)
if self.home and (self.prefix or self.exec_prefix):
raise DistutilsOptionError(
"must supply either home or prefix/exec-prefix -- not both")
'must supply either home or prefix/exec-prefix -- not both',
)
if self.user and (self.prefix or self.exec_prefix or self.home or
self.install_base or self.install_platbase):
raise DistutilsOptionError("can't combine user with prefix, "
"exec_prefix/home, or install_(plat)base")
if self.user and (
self.prefix or self.exec_prefix or self.home or
self.install_base or self.install_platbase
):
raise DistutilsOptionError(
"can't combine user with prefix, "
'exec_prefix/home, or install_(plat)base',
)
# Next, stuff that's wrong (or dubious) only on certain platforms.
if os.name != "posix":
if os.name != 'posix':
if self.exec_prefix:
self.warn("exec-prefix option ignored on this platform")
self.warn('exec-prefix option ignored on this platform')
self.exec_prefix = None
# Now the interesting logic -- so interesting that we farm it out
@ -280,14 +320,14 @@ class install(Command):
# install_{purelib,platlib,lib,scripts,data,...}, and the
# INSTALL_SCHEME dictionary above. Phew!
self.dump_dirs("pre-finalize_{unix,other}")
self.dump_dirs('pre-finalize_{unix,other}')
if os.name == 'posix':
self.finalize_unix()
else:
self.finalize_other()
self.dump_dirs("post-finalize_{unix,other}()")
self.dump_dirs('post-finalize_{unix,other}()')
# Expand configuration variables, tilde, etc. in self.install_base
# and self.install_platbase -- that way, we can use $base or
@ -301,19 +341,20 @@ class install(Command):
except AttributeError:
# sys.abiflags may not be defined on all platforms.
abiflags = ''
self.config_vars = {'dist_name': self.distribution.get_name(),
'dist_version': self.distribution.get_version(),
'dist_fullname': self.distribution.get_fullname(),
'py_version': py_version,
'py_version_short': '%d.%d' % sys.version_info[:2],
'py_version_nodot': '%d%d' % sys.version_info[:2],
'sys_prefix': prefix,
'prefix': prefix,
'sys_exec_prefix': exec_prefix,
'exec_prefix': exec_prefix,
'abiflags': abiflags,
'platlibdir': getattr(sys, 'platlibdir', 'lib'),
}
self.config_vars = {
'dist_name': self.distribution.get_name(),
'dist_version': self.distribution.get_version(),
'dist_fullname': self.distribution.get_fullname(),
'py_version': py_version,
'py_version_short': '%d.%d' % sys.version_info[:2],
'py_version_nodot': '%d%d' % sys.version_info[:2],
'sys_prefix': prefix,
'prefix': prefix,
'sys_exec_prefix': exec_prefix,
'exec_prefix': exec_prefix,
'abiflags': abiflags,
'platlibdir': getattr(sys, 'platlibdir', 'lib'),
}
if HAS_USER_SITE:
self.config_vars['userbase'] = self.install_userbase
@ -321,7 +362,7 @@ class install(Command):
self.expand_basedirs()
self.dump_dirs("post-expand_basedirs()")
self.dump_dirs('post-expand_basedirs()')
# Now define config vars for the base directories so we can expand
# everything else.
@ -330,14 +371,14 @@ class install(Command):
if DEBUG:
from pprint import pprint
print("config vars:")
print('config vars:')
pprint(self.config_vars)
# Expand "~" and configuration variables in the installation
# directories.
self.expand_dirs()
self.dump_dirs("post-expand_dirs()")
self.dump_dirs('post-expand_dirs()')
# Create directories in the home dir:
if self.user:
@ -348,17 +389,18 @@ class install(Command):
# module distribution is pure or not. Of course, if the user
# already specified install_lib, use their selection.
if self.install_lib is None:
if self.distribution.has_ext_modules(): # has extensions: non-pure
if self.distribution.has_ext_modules(): # has extensions: non-pure
self.install_lib = self.install_platlib
else:
self.install_lib = self.install_purelib
# Convert directories from Unix /-separated syntax to the local
# convention.
self.convert_paths('lib', 'purelib', 'platlib',
'scripts', 'data', 'headers',
'userbase', 'usersite')
self.convert_paths(
'lib', 'purelib', 'platlib',
'scripts', 'data', 'headers',
'userbase', 'usersite',
)
# Deprecated
# Well, we're not actually fully completely finalized yet: we still
@ -366,21 +408,25 @@ class install(Command):
# non-packagized module distributions (hello, Numerical Python!) to
# get their own directories.
self.handle_extra_path()
self.install_libbase = self.install_lib # needed for .pth file
self.install_libbase = self.install_lib # needed for .pth file
self.install_lib = os.path.join(self.install_lib, self.extra_dirs)
# If a new root directory was supplied, make all the installation
# dirs relative to it.
if self.root is not None:
self.change_roots('libbase', 'lib', 'purelib', 'platlib',
'scripts', 'data', 'headers')
self.change_roots(
'libbase', 'lib', 'purelib', 'platlib',
'scripts', 'data', 'headers',
)
self.dump_dirs("after prepending root")
self.dump_dirs('after prepending root')
# Find out the build directories, ie. where to install from.
self.set_undefined_options('build',
('build_base', 'build_base'),
('build_lib', 'build_lib'))
self.set_undefined_options(
'build',
('build_base', 'build_base'),
('build_lib', 'build_lib'),
)
# Punt on doc directories for now -- after all, we're punting on
# documentation completely!
@ -390,10 +436,10 @@ class install(Command):
if not DEBUG:
return
from distutils.fancy_getopt import longopt_xlate
log.debug(msg + ":")
log.debug(msg + ':')
for opt in self.user_options:
opt_name = opt[0]
if opt_name[-1] == "=":
if opt_name[-1] == '=':
opt_name = opt_name[0:-1]
if opt_name in self.negative_opt:
opt_name = self.negative_opt[opt_name]
@ -402,36 +448,43 @@ class install(Command):
else:
opt_name = opt_name.translate(longopt_xlate)
val = getattr(self, opt_name)
log.debug(" %s: %s", opt_name, val)
log.debug(' %s: %s', opt_name, val)
def finalize_unix(self):
"""Finalizes options for posix platforms."""
if self.install_base is not None or self.install_platbase is not None:
if ((self.install_lib is None and
self.install_purelib is None and
self.install_platlib is None) or
if (
(
self.install_lib is None and
self.install_purelib is None and
self.install_platlib is None
) or
self.install_headers is None or
self.install_scripts is None or
self.install_data is None):
self.install_data is None
):
raise DistutilsOptionError(
"install-base or install-platbase supplied, but "
"installation scheme is incomplete")
'install-base or install-platbase supplied, but '
'installation scheme is incomplete',
)
return
if self.user:
if self.install_userbase is None:
raise DistutilsPlatformError(
"User base directory is not specified")
'User base directory is not specified',
)
self.install_base = self.install_platbase = self.install_userbase
self.select_scheme("unix_user")
self.select_scheme('unix_user')
elif self.home is not None:
self.install_base = self.install_platbase = self.home
self.select_scheme("unix_home")
self.select_scheme('unix_home')
else:
if self.prefix is None:
if self.exec_prefix is not None:
raise DistutilsOptionError(
"must not supply exec-prefix without prefix")
'must not supply exec-prefix without prefix',
)
self.prefix = os.path.normpath(sys.prefix)
self.exec_prefix = os.path.normpath(sys.exec_prefix)
@ -442,19 +495,20 @@ class install(Command):
self.install_base = self.prefix
self.install_platbase = self.exec_prefix
self.select_scheme("unix_prefix")
self.select_scheme('unix_prefix')
def finalize_other(self):
"""Finalizes options for non-posix platforms"""
if self.user:
if self.install_userbase is None:
raise DistutilsPlatformError(
"User base directory is not specified")
'User base directory is not specified',
)
self.install_base = self.install_platbase = self.install_userbase
self.select_scheme(os.name + "_user")
self.select_scheme(os.name + '_user')
elif self.home is not None:
self.install_base = self.install_platbase = self.home
self.select_scheme("unix_home")
self.select_scheme('unix_home')
else:
if self.prefix is None:
self.prefix = os.path.normpath(sys.prefix)
@ -464,14 +518,17 @@ class install(Command):
self.select_scheme(os.name)
except KeyError:
raise DistutilsPlatformError(
"I don't know how to install stuff on '%s'" % os.name)
"I don't know how to install stuff on '%s'" % os.name,
)
def select_scheme(self, name):
"""Sets the install directories by applying the install schemes."""
# it's the caller's problem if they supply a bad name!
if (hasattr(sys, 'pypy_version_info') and
sys.version_info < (3, 8) and
not name.endswith(('_user', '_home'))):
if (
hasattr(sys, 'pypy_version_info') and
sys.version_info < (3, 8) and
not name.endswith(('_user', '_home'))
):
if os.name == 'nt':
name = 'pypy_nt'
else:
@ -498,14 +555,16 @@ class install(Command):
def expand_dirs(self):
"""Calls `os.path.expanduser` on install dirs."""
self._expand_attrs(['install_purelib', 'install_platlib',
'install_lib', 'install_headers',
'install_scripts', 'install_data',])
self._expand_attrs([
'install_purelib', 'install_platlib',
'install_lib', 'install_headers',
'install_scripts', 'install_data',
])
def convert_paths(self, *names):
"""Call `convert_path` over `names`."""
for name in names:
attr = "install_" + name
attr = 'install_' + name
setattr(self, attr, convert_path(getattr(self, attr)))
def handle_extra_path(self):
@ -515,8 +574,8 @@ class install(Command):
if self.extra_path is not None:
log.warn(
"Distribution option extra_path is deprecated. "
"See issue27919 for details."
'Distribution option extra_path is deprecated. '
'See issue27919 for details.',
)
if isinstance(self.extra_path, str):
self.extra_path = self.extra_path.split(',')
@ -527,8 +586,9 @@ class install(Command):
path_file, extra_dirs = self.extra_path
else:
raise DistutilsOptionError(
"'extra_path' option must be a list, tuple, or "
"comma-separated string with 1 or 2 elements")
"'extra_path' option must be a list, tuple, or "
'comma-separated string with 1 or 2 elements',
)
# convert to local form in case Unix notation used (as it
# should be in setup scripts)
@ -545,14 +605,14 @@ class install(Command):
def change_roots(self, *names):
"""Change the install directories pointed by name using root."""
for name in names:
attr = "install_" + name
attr = 'install_' + name
setattr(self, attr, change_root(self.root, getattr(self, attr)))
def create_home_path(self):
"""Create directories under ~."""
if not self.user:
return
home = convert_path(os.path.expanduser("~"))
home = convert_path(os.path.expanduser('~'))
for name, path in self.config_vars.items():
if path.startswith(home) and not os.path.isdir(path):
self.debug_print("os.makedirs('%s', 0o700)" % path)
@ -571,8 +631,10 @@ class install(Command):
# internally, and not to sys.path, so we don't check the platform
# matches what we are running.
if self.warn_dir and build_plat != get_platform():
raise DistutilsPlatformError("Can't install when "
"cross-compiling")
raise DistutilsPlatformError(
"Can't install when "
'cross-compiling',
)
# Run all sub-commands (at least those that need to be run)
for cmd_name in self.get_sub_commands():
@ -588,34 +650,45 @@ class install(Command):
root_len = len(self.root)
for counter in range(len(outputs)):
outputs[counter] = outputs[counter][root_len:]
self.execute(write_file,
(self.record, outputs),
"writing list of installed files to '%s'" %
self.record)
self.execute(
write_file,
(self.record, outputs),
"writing list of installed files to '%s'" %
self.record,
)
sys_path = map(os.path.normpath, sys.path)
sys_path = map(os.path.normcase, sys_path)
install_lib = os.path.normcase(os.path.normpath(self.install_lib))
if (self.warn_dir and
if (
self.warn_dir and
not (self.path_file and self.install_path_file) and
install_lib not in sys_path):
log.debug(("modules installed to '%s', which is not in "
"Python's module search path (sys.path) -- "
"you'll have to change the search path yourself"),
self.install_lib)
install_lib not in sys_path
):
log.debug(
(
"modules installed to '%s', which is not in "
"Python's module search path (sys.path) -- "
"you'll have to change the search path yourself"
),
self.install_lib,
)
def create_path_file(self):
"""Creates the .pth file"""
filename = os.path.join(self.install_libbase,
self.path_file + ".pth")
filename = os.path.join(
self.install_libbase,
self.path_file + '.pth',
)
if self.install_path_file:
self.execute(write_file,
(filename, [self.extra_dirs]),
"creating %s" % filename)
self.execute(
write_file,
(filename, [self.extra_dirs]),
'creating %s' % filename,
)
else:
self.warn("path file '%s' not created" % filename)
# -- Reporting methods ---------------------------------------------
def get_outputs(self):
@ -630,8 +703,12 @@ class install(Command):
outputs.append(filename)
if self.path_file and self.install_path_file:
outputs.append(os.path.join(self.install_libbase,
self.path_file + ".pth"))
outputs.append(
os.path.join(
self.install_libbase,
self.path_file + '.pth',
),
)
return outputs
@ -650,8 +727,10 @@ class install(Command):
def has_lib(self):
"""Returns true if the current distribution has any Python
modules to install."""
return (self.distribution.has_pure_modules() or
self.distribution.has_ext_modules())
return (
self.distribution.has_pure_modules() or
self.distribution.has_ext_modules()
)
def has_headers(self):
"""Returns true if the current distribution has any headers to
@ -670,9 +749,10 @@ class install(Command):
# 'sub_commands': a list of commands this command might have to run to
# get its work done. See cmd.py for more info.
sub_commands = [('install_lib', has_lib),
('install_headers', has_headers),
('install_scripts', has_scripts),
('install_data', has_data),
('install_egg_info', lambda self:True),
]
sub_commands = [
('install_lib', has_lib),
('install_headers', has_headers),
('install_scripts', has_scripts),
('install_data', has_data),
('install_egg_info', lambda self: True),
]

View file

@ -2,25 +2,32 @@
Implements the Distutils 'install_data' command, for installing
platform-independent data files."""
# contributed by Bastian Kleineidam
from __future__ import annotations
import os
from distutils.core import Command
from distutils.util import change_root, convert_path
from distutils.util import change_root
from distutils.util import convert_path
class install_data(Command):
description = "install data files"
description = 'install data files'
user_options = [
('install-dir=', 'd',
"base directory for installing data files "
"(default: installation base dir)"),
('root=', None,
"install everything relative to this alternate root directory"),
('force', 'f', "force installation (overwrite existing files)"),
]
(
'install-dir=', 'd',
'base directory for installing data files '
'(default: installation base dir)',
),
(
'root=', None,
'install everything relative to this alternate root directory',
),
('force', 'f', 'force installation (overwrite existing files)'),
]
boolean_options = ['force']
@ -33,11 +40,12 @@ class install_data(Command):
self.warn_dir = 1
def finalize_options(self):
self.set_undefined_options('install',
('install_data', 'install_dir'),
('root', 'root'),
('force', 'force'),
)
self.set_undefined_options(
'install',
('install_data', 'install_dir'),
('root', 'root'),
('force', 'force'),
)
def run(self):
self.mkpath(self.install_dir)
@ -46,9 +54,11 @@ class install_data(Command):
# it's a simple file, so copy it
f = convert_path(f)
if self.warn_dir:
self.warn("setup script did not provide a directory for "
"'%s' -- installing right in '%s'" %
(f, self.install_dir))
self.warn(
'setup script did not provide a directory for '
"'%s' -- installing right in '%s'" %
(f, self.install_dir),
)
(out, _) = self.copy_file(f, self.install_dir)
self.outfiles.append(out)
else:

View file

@ -2,29 +2,34 @@
Implements the Distutils 'install_egg_info' command, for installing
a package's PKG-INFO metadata."""
from __future__ import annotations
import os
import re
import sys
from distutils import dir_util
from distutils import log
from distutils.cmd import Command
from distutils import log, dir_util
import os, sys, re
class install_egg_info(Command):
"""Install an .egg-info file for the package"""
description = "Install package's PKG-INFO metadata as an .egg-info file"
user_options = [
('install-dir=', 'd', "directory to install to"),
('install-dir=', 'd', 'directory to install to'),
]
def initialize_options(self):
self.install_dir = None
def finalize_options(self):
self.set_undefined_options('install_lib',('install_dir','install_dir'))
basename = "%s-%s-py%d.%d.egg-info" % (
self.set_undefined_options('install_lib', ('install_dir', 'install_dir'))
basename = '%s-%s-py%d.%d.egg-info' % (
to_filename(safe_name(self.distribution.get_name())),
to_filename(safe_version(self.distribution.get_version())),
*sys.version_info[:2]
*sys.version_info[:2],
)
self.target = os.path.join(self.install_dir, basename)
self.outputs = [self.target]
@ -34,11 +39,13 @@ class install_egg_info(Command):
if os.path.isdir(target) and not os.path.islink(target):
dir_util.remove_tree(target, dry_run=self.dry_run)
elif os.path.exists(target):
self.execute(os.unlink,(self.target,),"Removing "+target)
self.execute(os.unlink, (self.target,), 'Removing ' + target)
elif not os.path.isdir(self.install_dir):
self.execute(os.makedirs, (self.install_dir,),
"Creating "+self.install_dir)
log.info("Writing %s", target)
self.execute(
os.makedirs, (self.install_dir,),
'Creating ' + self.install_dir,
)
log.info('Writing %s', target)
if not self.dry_run:
with open(target, 'w', encoding='UTF-8') as f:
self.distribution.metadata.write_pkg_file(f)
@ -65,7 +72,7 @@ def safe_version(version):
Spaces become dots, and all other non-alphanumeric characters become
dashes, with runs of multiple dashes condensed to a single dash.
"""
version = version.replace(' ','.')
version = version.replace(' ', '.')
return re.sub('[^A-Za-z0-9.]+', '-', version)
@ -74,4 +81,4 @@ def to_filename(name):
Any '-' characters are currently replaced with '_'.
"""
return name.replace('-','_')
return name.replace('-', '_')

View file

@ -2,6 +2,7 @@
Implements the Distutils 'install_headers' command, to install C/C++ header
files to the Python include directory."""
from __future__ import annotations
from distutils.core import Command
@ -9,13 +10,18 @@ from distutils.core import Command
# XXX force is never used
class install_headers(Command):
description = "install C/C++ header files"
description = 'install C/C++ header files'
user_options = [('install-dir=', 'd',
"directory to install header files to"),
('force', 'f',
"force installation (overwrite existing files)"),
]
user_options = [
(
'install-dir=', 'd',
'directory to install header files to',
),
(
'force', 'f',
'force installation (overwrite existing files)',
),
]
boolean_options = ['force']
@ -25,10 +31,11 @@ class install_headers(Command):
self.outfiles = []
def finalize_options(self):
self.set_undefined_options('install',
('install_headers', 'install_dir'),
('force', 'force'))
self.set_undefined_options(
'install',
('install_headers', 'install_dir'),
('force', 'force'),
)
def run(self):
headers = self.distribution.headers

View file

@ -2,9 +2,10 @@
Implements the Distutils 'install_lib' command
(install all Python modules)."""
from __future__ import annotations
import os
import importlib.util
import os
import sys
from distutils.core import Command
@ -12,11 +13,12 @@ from distutils.errors import DistutilsOptionError
# Extension for Python source files.
PYTHON_SOURCE_EXTENSION = ".py"
PYTHON_SOURCE_EXTENSION = '.py'
class install_lib(Command):
description = "install all Python modules (extensions and pure Python)"
description = 'install all Python modules (extensions and pure Python)'
# The byte-compilation options are a tad confusing. Here are the
# possible scenarios:
@ -34,19 +36,21 @@ class install_lib(Command):
# optimization to use.
user_options = [
('install-dir=', 'd', "directory to install to"),
('build-dir=','b', "build directory (where to install from)"),
('force', 'f', "force installation (overwrite existing files)"),
('compile', 'c', "compile .py to .pyc [default]"),
('install-dir=', 'd', 'directory to install to'),
('build-dir=', 'b', 'build directory (where to install from)'),
('force', 'f', 'force installation (overwrite existing files)'),
('compile', 'c', 'compile .py to .pyc [default]'),
('no-compile', None, "don't compile .py files"),
('optimize=', 'O',
"also compile with optimization: -O1 for \"python -O\", "
"-O2 for \"python -OO\", and -O0 to disable [default: -O0]"),
('skip-build', None, "skip the build steps"),
]
(
'optimize=', 'O',
"also compile with optimization: -O1 for \"python -O\", "
"-O2 for \"python -OO\", and -O0 to disable [default: -O0]",
),
('skip-build', None, 'skip the build steps'),
]
boolean_options = ['force', 'compile', 'skip-build']
negative_opt = {'no-compile' : 'compile'}
negative_opt = {'no-compile': 'compile'}
def initialize_options(self):
# let the 'install' command dictate our installation directory
@ -61,14 +65,15 @@ class install_lib(Command):
# Get all the information we need to install pure Python modules
# from the umbrella 'install' command -- build (source) directory,
# install (target) directory, and whether to compile .py files.
self.set_undefined_options('install',
('build_lib', 'build_dir'),
('install_lib', 'install_dir'),
('force', 'force'),
('compile', 'compile'),
('optimize', 'optimize'),
('skip_build', 'skip_build'),
)
self.set_undefined_options(
'install',
('build_lib', 'build_dir'),
('install_lib', 'install_dir'),
('force', 'force'),
('compile', 'compile'),
('optimize', 'optimize'),
('skip_build', 'skip_build'),
)
if self.compile is None:
self.compile = True
@ -81,7 +86,7 @@ class install_lib(Command):
if self.optimize not in (0, 1, 2):
raise AssertionError
except (ValueError, AssertionError):
raise DistutilsOptionError("optimize must be 0, 1, or 2")
raise DistutilsOptionError('optimize must be 0, 1, or 2')
def run(self):
# Make sure we have built everything we need first
@ -110,8 +115,10 @@ class install_lib(Command):
if os.path.isdir(self.build_dir):
outfiles = self.copy_tree(self.build_dir, self.install_dir)
else:
self.warn("'%s' does not exist -- no Python modules to install" %
self.build_dir)
self.warn(
"'%s' does not exist -- no Python modules to install" %
self.build_dir,
)
return
return outfiles
@ -129,14 +136,17 @@ class install_lib(Command):
install_root = self.get_finalized_command('install').root
if self.compile:
byte_compile(files, optimize=0,
force=self.force, prefix=install_root,
dry_run=self.dry_run)
byte_compile(
files, optimize=0,
force=self.force, prefix=install_root,
dry_run=self.dry_run,
)
if self.optimize > 0:
byte_compile(files, optimize=self.optimize,
force=self.force, prefix=install_root,
verbose=self.verbose, dry_run=self.dry_run)
byte_compile(
files, optimize=self.optimize,
force=self.force, prefix=install_root,
verbose=self.verbose, dry_run=self.dry_run,
)
# -- Utility methods -----------------------------------------------
@ -165,15 +175,20 @@ class install_lib(Command):
if ext != PYTHON_SOURCE_EXTENSION:
continue
if self.compile:
bytecode_files.append(importlib.util.cache_from_source(
py_file, optimization=''))
bytecode_files.append(
importlib.util.cache_from_source(
py_file, optimization='',
),
)
if self.optimize > 0:
bytecode_files.append(importlib.util.cache_from_source(
py_file, optimization=self.optimize))
bytecode_files.append(
importlib.util.cache_from_source(
py_file, optimization=self.optimize,
),
)
return bytecode_files
# -- External interface --------------------------------------------
# (called by outsiders)
@ -183,18 +198,22 @@ class install_lib(Command):
modules have actually been built yet.
"""
pure_outputs = \
self._mutate_outputs(self.distribution.has_pure_modules(),
'build_py', 'build_lib',
self.install_dir)
self._mutate_outputs(
self.distribution.has_pure_modules(),
'build_py', 'build_lib',
self.install_dir,
)
if self.compile:
bytecode_outputs = self._bytecode_filenames(pure_outputs)
else:
bytecode_outputs = []
ext_outputs = \
self._mutate_outputs(self.distribution.has_ext_modules(),
'build_ext', 'build_lib',
self.install_dir)
self._mutate_outputs(
self.distribution.has_ext_modules(),
'build_ext', 'build_lib',
self.install_dir,
)
return pure_outputs + bytecode_outputs + ext_outputs

View file

@ -2,24 +2,25 @@
Implements the Distutils 'install_scripts' command, for installing
Python scripts."""
# contributed by Bastian Kleineidam
from __future__ import annotations
import os
from distutils.core import Command
from distutils import log
from stat import ST_MODE
from distutils import log
from distutils.core import Command
class install_scripts(Command):
description = "install scripts (Python or otherwise)"
description = 'install scripts (Python or otherwise)'
user_options = [
('install-dir=', 'd', "directory to install scripts to"),
('build-dir=','b', "build directory (where to install from)"),
('force', 'f', "force installation (overwrite existing files)"),
('skip-build', None, "skip the build steps"),
('install-dir=', 'd', 'directory to install scripts to'),
('build-dir=', 'b', 'build directory (where to install from)'),
('force', 'f', 'force installation (overwrite existing files)'),
('skip-build', None, 'skip the build steps'),
]
boolean_options = ['force', 'skip-build']
@ -32,11 +33,12 @@ class install_scripts(Command):
def finalize_options(self):
self.set_undefined_options('build', ('build_scripts', 'build_dir'))
self.set_undefined_options('install',
('install_scripts', 'install_dir'),
('force', 'force'),
('skip_build', 'skip_build'),
)
self.set_undefined_options(
'install',
('install_scripts', 'install_dir'),
('force', 'force'),
('skip_build', 'skip_build'),
)
def run(self):
if not self.skip_build:
@ -47,10 +49,10 @@ class install_scripts(Command):
# all the scripts we just installed.
for file in self.get_outputs():
if self.dry_run:
log.info("changing mode of %s", file)
log.info('changing mode of %s', file)
else:
mode = ((os.stat(file)[ST_MODE]) | 0o555) & 0o7777
log.info("changing mode of %s to %o", file, mode)
log.info('changing mode of %s to %o', file, mode)
os.chmod(file, mode)
def get_inputs(self):

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import sys
@ -23,8 +25,8 @@ def compose(f1, f2):
pythonlib = (
compose(list, _pythonlib_compat)
if sys.version_info < (3, 8)
and sys.platform != 'darwin'
and sys.platform[:3] != 'aix'
if sys.version_info < (3, 8) and
sys.platform != 'darwin' and
sys.platform[:3] != 'aix'
else list
)

View file

@ -2,29 +2,36 @@
Implements the Distutils 'register' command (register with the repository).
"""
# created 2002/10/21, Richard Jones
from __future__ import annotations
import getpass
import io
import urllib.parse, urllib.request
import urllib.parse
import urllib.request
from warnings import warn
from distutils import log
from distutils.core import PyPIRCCommand
from distutils.errors import *
from distutils import log
class register(PyPIRCCommand):
description = ("register the distribution with the Python package index")
description = ('register the distribution with the Python package index')
user_options = PyPIRCCommand.user_options + [
('list-classifiers', None,
'list the valid Trove classifiers'),
('strict', None ,
'Will stop the registering if the meta-data are not fully compliant')
]
(
'list-classifiers', None,
'list the valid Trove classifiers',
),
(
'strict', None,
'Will stop the registering if the meta-data are not fully compliant',
),
]
boolean_options = PyPIRCCommand.boolean_options + [
'verify', 'list-classifiers', 'strict']
'verify', 'list-classifiers', 'strict',
]
sub_commands = [('check', lambda self: True)]
@ -36,8 +43,10 @@ class register(PyPIRCCommand):
def finalize_options(self):
PyPIRCCommand.finalize_options(self)
# setting options for the `check` subcommand
check_options = {'strict': ('register', self.strict),
'restructuredtext': ('register', 1)}
check_options = {
'strict': ('register', self.strict),
'restructuredtext': ('register', 1),
}
self.distribution.command_options['check'] = check_options
def run(self):
@ -57,8 +66,10 @@ class register(PyPIRCCommand):
def check_metadata(self):
"""Deprecated API."""
warn("distutils.command.register.check_metadata is deprecated, \
use the check command instead", PendingDeprecationWarning)
warn(
'distutils.command.register.check_metadata is deprecated, \
use the check command instead', PendingDeprecationWarning,
)
check = self.distribution.get_command_obj('check')
check.ensure_finalized()
check.strict = self.strict
@ -85,7 +96,7 @@ class register(PyPIRCCommand):
def classifiers(self):
''' Fetch the list of classifiers from the server.
'''
url = self.repository+'?:action=list_classifiers'
url = self.repository + '?:action=list_classifiers'
response = urllib.request.urlopen(url)
log.info(self._read_pypi_response(response))
@ -137,13 +148,15 @@ class register(PyPIRCCommand):
# get the user's login info
choices = '1 2 3 4'.split()
while choice not in choices:
self.announce('''\
self.announce(
'''\
We need to know who you are, so please choose either:
1. use your existing login,
2. register as a new user,
3. have the server generate a new password for you (and email it to you), or
4. quit
Your selection [default 1]: ''', log.INFO)
Your selection [default 1]: ''', log.INFO,
)
choice = input()
if not choice:
choice = '1'
@ -162,10 +175,14 @@ Your selection [default 1]: ''', log.INFO)
host = urllib.parse.urlparse(self.repository)[1]
auth.add_password(self.realm, host, username, password)
# send the info to the server and report the result
code, result = self.post_to_server(self.build_post_data('submit'),
auth)
self.announce('Server response (%s): %s' % (code, result),
log.INFO)
code, result = self.post_to_server(
self.build_post_data('submit'),
auth,
)
self.announce(
'Server response ({}): {}'.format(code, result),
log.INFO,
)
# possibly save the login
if code == 200:
@ -174,10 +191,16 @@ Your selection [default 1]: ''', log.INFO)
# so the upload command can reuse it
self.distribution.password = password
else:
self.announce(('I can store your PyPI login so future '
'submissions will be faster.'), log.INFO)
self.announce('(the login will be stored in %s)' % \
self._get_rc_file(), log.INFO)
self.announce(
(
'I can store your PyPI login so future '
'submissions will be faster.'
), log.INFO,
)
self.announce(
'(the login will be stored in %s)' %
self._get_rc_file(), log.INFO,
)
choice = 'X'
while choice.lower() not in 'yn':
choice = input('Save your login (y/N)?')
@ -208,8 +231,10 @@ Your selection [default 1]: ''', log.INFO)
log.info('Server response (%s): %s', code, result)
else:
log.info('You will receive an email shortly.')
log.info(('Follow the instructions in it to '
'complete registration.'))
log.info(
'Follow the instructions in it to '
'complete registration.'
)
elif choice == '3':
data = {':action': 'password_reset'}
data['email'] = ''
@ -224,7 +249,7 @@ Your selection [default 1]: ''', log.INFO)
meta = self.distribution.metadata
data = {
':action': action,
'metadata_version' : '1.0',
'metadata_version': '1.0',
'name': meta.get_name(),
'version': meta.get_version(),
'summary': meta.get_description(),
@ -250,9 +275,13 @@ Your selection [default 1]: ''', log.INFO)
''' Post a query to the server, and return a string response.
'''
if 'name' in data:
self.announce('Registering %s to %s' % (data['name'],
self.repository),
log.INFO)
self.announce(
'Registering {} to {}'.format(
data['name'],
self.repository,
),
log.INFO,
)
# Build up the MIME payload for the urllib2 POST data
boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254'
sep_boundary = '\n--' + boundary
@ -260,30 +289,30 @@ Your selection [default 1]: ''', log.INFO)
body = io.StringIO()
for key, value in data.items():
# handle multiple entries for the same name
if type(value) not in (type([]), type( () )):
if type(value) not in (type([]), type(())):
value = [value]
for value in value:
value = str(value)
body.write(sep_boundary)
body.write('\nContent-Disposition: form-data; name="%s"'%key)
body.write("\n\n")
body.write('\nContent-Disposition: form-data; name="%s"' % key)
body.write('\n\n')
body.write(value)
if value and value[-1] == '\r':
body.write('\n') # write an extra newline (lurve Macs)
body.write(end_boundary)
body.write("\n")
body = body.getvalue().encode("utf-8")
body.write('\n')
body = body.getvalue().encode('utf-8')
# build the Request
headers = {
'Content-type': 'multipart/form-data; boundary=%s; charset=utf-8'%boundary,
'Content-length': str(len(body))
'Content-type': 'multipart/form-data; boundary=%s; charset=utf-8' % boundary,
'Content-length': str(len(body)),
}
req = urllib.request.Request(self.repository, body, headers)
# handle HTTP and include the Basic Auth handler
opener = urllib.request.build_opener(
urllib.request.HTTPBasicAuthHandler(password_mgr=auth)
urllib.request.HTTPBasicAuthHandler(password_mgr=auth),
)
data = ''
try:

View file

@ -1,21 +1,23 @@
"""distutils.command.sdist
Implements the Distutils 'sdist' command (create a source distribution)."""
from __future__ import annotations
import os
import sys
from glob import glob
from warnings import warn
from distutils.core import Command
from distutils import archive_util
from distutils import dir_util
from distutils import file_util
from distutils import archive_util
from distutils.text_file import TextFile
from distutils.filelist import FileList
from distutils import log
from distutils.core import Command
from distutils.errors import DistutilsOptionError
from distutils.errors import DistutilsTemplateError
from distutils.filelist import FileList
from distutils.text_file import TextFile
from distutils.util import convert_path
from distutils.errors import DistutilsTemplateError, DistutilsOptionError
def show_formats():
@ -26,16 +28,19 @@ def show_formats():
from distutils.archive_util import ARCHIVE_FORMATS
formats = []
for format in ARCHIVE_FORMATS.keys():
formats.append(("formats=" + format, None,
ARCHIVE_FORMATS[format][2]))
formats.append((
'formats=' + format, None,
ARCHIVE_FORMATS[format][2],
))
formats.sort()
FancyGetopt(formats).print_help(
"List of available source distribution formats:")
'List of available source distribution formats:',
)
class sdist(Command):
description = "create a source distribution (tarball, zip file, etc.)"
description = 'create a source distribution (tarball, zip file, etc.)'
def checking_metadata(self):
"""Callable used for the check sub-command.
@ -44,55 +49,88 @@ class sdist(Command):
return self.metadata_check
user_options = [
('template=', 't',
"name of manifest template file [default: MANIFEST.in]"),
('manifest=', 'm',
"name of manifest file [default: MANIFEST]"),
('use-defaults', None,
"include the default file set in the manifest "
"[default; disable with --no-defaults]"),
('no-defaults', None,
"don't include the default file set"),
('prune', None,
"specifically exclude files/directories that should not be "
"distributed (build tree, RCS/CVS dirs, etc.) "
"[default; disable with --no-prune]"),
('no-prune', None,
"don't automatically exclude anything"),
('manifest-only', 'o',
"just regenerate the manifest and then stop "
"(implies --force-manifest)"),
('force-manifest', 'f',
"forcibly regenerate the manifest and carry on as usual. "
"Deprecated: now the manifest is always regenerated."),
('formats=', None,
"formats for source distribution (comma-separated list)"),
('keep-temp', 'k',
"keep the distribution tree around after creating " +
"archive file(s)"),
('dist-dir=', 'd',
"directory to put the source distribution archive(s) in "
"[default: dist]"),
('metadata-check', None,
"Ensure that all required elements of meta-data "
"are supplied. Warn if any missing. [default]"),
('owner=', 'u',
"Owner name used when creating a tar file [default: current user]"),
('group=', 'g',
"Group name used when creating a tar file [default: current group]"),
]
(
'template=', 't',
'name of manifest template file [default: MANIFEST.in]',
),
(
'manifest=', 'm',
'name of manifest file [default: MANIFEST]',
),
(
'use-defaults', None,
'include the default file set in the manifest '
'[default; disable with --no-defaults]',
),
(
'no-defaults', None,
"don't include the default file set",
),
(
'prune', None,
'specifically exclude files/directories that should not be '
'distributed (build tree, RCS/CVS dirs, etc.) '
'[default; disable with --no-prune]',
),
(
'no-prune', None,
"don't automatically exclude anything",
),
(
'manifest-only', 'o',
'just regenerate the manifest and then stop '
'(implies --force-manifest)',
),
(
'force-manifest', 'f',
'forcibly regenerate the manifest and carry on as usual. '
'Deprecated: now the manifest is always regenerated.',
),
(
'formats=', None,
'formats for source distribution (comma-separated list)',
),
(
'keep-temp', 'k',
'keep the distribution tree around after creating ' +
'archive file(s)',
),
(
'dist-dir=', 'd',
'directory to put the source distribution archive(s) in '
'[default: dist]',
),
(
'metadata-check', None,
'Ensure that all required elements of meta-data '
'are supplied. Warn if any missing. [default]',
),
(
'owner=', 'u',
'Owner name used when creating a tar file [default: current user]',
),
(
'group=', 'g',
'Group name used when creating a tar file [default: current group]',
),
]
boolean_options = ['use-defaults', 'prune',
'manifest-only', 'force-manifest',
'keep-temp', 'metadata-check']
boolean_options = [
'use-defaults', 'prune',
'manifest-only', 'force-manifest',
'keep-temp', 'metadata-check',
]
help_options = [
('help-formats', None,
"list available distribution formats", show_formats),
]
(
'help-formats', None,
'list available distribution formats', show_formats,
),
]
negative_opt = {'no-defaults': 'use-defaults',
'no-prune': 'prune' }
negative_opt = {
'no-defaults': 'use-defaults',
'no-prune': 'prune', }
sub_commands = [('check', checking_metadata)]
@ -123,19 +161,20 @@ class sdist(Command):
def finalize_options(self):
if self.manifest is None:
self.manifest = "MANIFEST"
self.manifest = 'MANIFEST'
if self.template is None:
self.template = "MANIFEST.in"
self.template = 'MANIFEST.in'
self.ensure_string_list('formats')
bad_format = archive_util.check_archive_formats(self.formats)
if bad_format:
raise DistutilsOptionError(
"unknown archive format '%s'" % bad_format)
"unknown archive format '%s'" % bad_format,
)
if self.dist_dir is None:
self.dist_dir = "dist"
self.dist_dir = 'dist'
def run(self):
# 'filelist' contains the list of files that will make up the
@ -161,8 +200,10 @@ class sdist(Command):
def check_metadata(self):
"""Deprecated API."""
warn("distutils.command.sdist.check_metadata is deprecated, \
use the check command instead", PendingDeprecationWarning)
warn(
'distutils.command.sdist.check_metadata is deprecated, \
use the check command instead', PendingDeprecationWarning,
)
check = self.distribution.get_command_obj('check')
check.ensure_finalized()
check.run()
@ -189,9 +230,13 @@ class sdist(Command):
return
if not template_exists:
self.warn(("manifest template '%s' does not exist " +
"(using default file list)") %
self.template)
self.warn(
(
"manifest template '%s' does not exist " +
'(using default file list)'
) %
self.template,
)
self.filelist.findall()
if self.use_defaults:
@ -259,8 +304,10 @@ class sdist(Command):
break
if not got_it:
self.warn("standard file not found: should have one of " +
', '.join(alts))
self.warn(
'standard file not found: should have one of ' +
', '.join(alts),
)
else:
if self._cs_path_exists(fn):
self.filelist.append(fn)
@ -328,9 +375,11 @@ class sdist(Command):
'self.filelist', which updates itself accordingly.
"""
log.info("reading manifest template '%s'", self.template)
template = TextFile(self.template, strip_comments=1, skip_blanks=1,
join_lines=1, lstrip_ws=1, rstrip_ws=1,
collapse_join=1)
template = TextFile(
self.template, strip_comments=1, skip_blanks=1,
join_lines=1, lstrip_ws=1, rstrip_ws=1,
collapse_join=1,
)
try:
while True:
@ -344,9 +393,13 @@ class sdist(Command):
# malformed lines, or a ValueError from the lower-level
# convert_path function
except (DistutilsTemplateError, ValueError) as msg:
self.warn("%s, line %d: %s" % (template.filename,
template.current_line,
msg))
self.warn(
'%s, line %d: %s' % (
template.filename,
template.current_line,
msg,
),
)
finally:
template.close()
@ -369,9 +422,11 @@ class sdist(Command):
else:
seps = '/'
vcs_dirs = ['RCS', 'CVS', r'\.svn', r'\.hg', r'\.git', r'\.bzr',
'_darcs']
vcs_ptrn = r'(^|%s)(%s)(%s).*' % (seps, '|'.join(vcs_dirs), seps)
vcs_dirs = [
'RCS', 'CVS', r'\.svn', r'\.hg', r'\.git', r'\.bzr',
'_darcs',
]
vcs_ptrn = r'(^|{})({})({}).*'.format(seps, '|'.join(vcs_dirs), seps)
self.filelist.exclude_pattern(vcs_ptrn, is_regex=1)
def write_manifest(self):
@ -380,14 +435,18 @@ class sdist(Command):
named by 'self.manifest'.
"""
if self._manifest_is_not_generated():
log.info("not writing to manually maintained "
"manifest file '%s'" % self.manifest)
log.info(
'not writing to manually maintained '
"manifest file '%s'" % self.manifest,
)
return
content = self.filelist.files[:]
content.insert(0, '# file GENERATED by distutils, do NOT edit')
self.execute(file_util.write_file, (self.manifest, content),
"writing manifest file '%s'" % self.manifest)
self.execute(
file_util.write_file, (self.manifest, content),
"writing manifest file '%s'" % self.manifest,
)
def _manifest_is_not_generated(self):
# check for special comment used in 3.1.3 and higher
@ -439,13 +498,13 @@ class sdist(Command):
if hasattr(os, 'link'): # can make hard links on this system
link = 'hard'
msg = "making hard links in %s..." % base_dir
msg = 'making hard links in %s...' % base_dir
else: # nope, have to copy
link = None
msg = "copying files to %s..." % base_dir
msg = 'copying files to %s...' % base_dir
if not files:
log.warn("no files to distribute -- empty manifest?")
log.warn('no files to distribute -- empty manifest?')
else:
log.info(msg)
for file in files:
@ -477,8 +536,10 @@ class sdist(Command):
self.formats.append(self.formats.pop(self.formats.index('tar')))
for fmt in self.formats:
file = self.make_archive(base_name, fmt, base_dir=base_dir,
owner=self.owner, group=self.group)
file = self.make_archive(
base_name, fmt, base_dir=base_dir,
owner=self.owner, group=self.group,
)
archive_files.append(file)
self.distribution.dist_files.append(('sdist', '', file))

View file

@ -4,37 +4,44 @@ distutils.command.upload
Implements the Distutils 'upload' subcommand (upload package to a package
index).
"""
from __future__ import annotations
import os
import io
import hashlib
import io
import os
from base64 import standard_b64encode
from urllib.request import urlopen, Request, HTTPError
from urllib.parse import urlparse
from distutils.errors import DistutilsError, DistutilsOptionError
from distutils.core import PyPIRCCommand
from distutils.spawn import spawn
from urllib.request import HTTPError
from urllib.request import Request
from urllib.request import urlopen
from distutils import log
from distutils.core import PyPIRCCommand
from distutils.errors import DistutilsError
from distutils.errors import DistutilsOptionError
from distutils.spawn import spawn
# PyPI Warehouse supports MD5, SHA256, and Blake2 (blake2-256)
# https://bugs.python.org/issue40698
_FILE_CONTENT_DIGESTS = {
"md5_digest": getattr(hashlib, "md5", None),
"sha256_digest": getattr(hashlib, "sha256", None),
"blake2_256_digest": getattr(hashlib, "blake2b", None),
'md5_digest': getattr(hashlib, 'md5', None),
'sha256_digest': getattr(hashlib, 'sha256', None),
'blake2_256_digest': getattr(hashlib, 'blake2b', None),
}
class upload(PyPIRCCommand):
description = "upload binary package to PyPI"
description = 'upload binary package to PyPI'
user_options = PyPIRCCommand.user_options + [
('sign', 's',
'sign files to upload using gpg'),
(
'sign', 's',
'sign files to upload using gpg',
),
('identity=', 'i', 'GPG identity used to sign files'),
]
]
boolean_options = PyPIRCCommand.boolean_options + ['sign']
@ -50,7 +57,7 @@ class upload(PyPIRCCommand):
PyPIRCCommand.finalize_options(self)
if self.identity and not self.sign:
raise DistutilsOptionError(
"Must use --sign for --identity to have meaning"
'Must use --sign for --identity to have meaning',
)
config = self._read_pypirc()
if config != {}:
@ -66,8 +73,10 @@ class upload(PyPIRCCommand):
def run(self):
if not self.distribution.dist_files:
msg = ("Must create and upload files in one command "
"(e.g. setup.py sdist upload)")
msg = (
'Must create and upload files in one command '
'(e.g. setup.py sdist upload)'
)
raise DistutilsOptionError(msg)
for command, pyversion, filename in self.distribution.dist_files:
self.upload_file(command, pyversion, filename)
@ -77,22 +86,24 @@ class upload(PyPIRCCommand):
schema, netloc, url, params, query, fragments = \
urlparse(self.repository)
if params or query or fragments:
raise AssertionError("Incompatible url %s" % self.repository)
raise AssertionError('Incompatible url %s' % self.repository)
if schema not in ('http', 'https'):
raise AssertionError("unsupported schema " + schema)
raise AssertionError('unsupported schema ' + schema)
# Sign if requested
if self.sign:
gpg_args = ["gpg", "--detach-sign", "-a", filename]
gpg_args = ['gpg', '--detach-sign', '-a', filename]
if self.identity:
gpg_args[2:2] = ["--local-user", self.identity]
spawn(gpg_args,
dry_run=self.dry_run)
gpg_args[2:2] = ['--local-user', self.identity]
spawn(
gpg_args,
dry_run=self.dry_run,
)
# Fill in the data - send all the meta-data in case we need to
# register a new release
f = open(filename,'rb')
f = open(filename, 'rb')
try:
content = f.read()
finally:
@ -109,7 +120,7 @@ class upload(PyPIRCCommand):
'version': meta.get_version(),
# file content
'content': (os.path.basename(filename),content),
'content': (os.path.basename(filename), content),
'filetype': command,
'pyversion': pyversion,
@ -129,7 +140,7 @@ class upload(PyPIRCCommand):
'provides': meta.get_provides(),
'requires': meta.get_requires(),
'obsoletes': meta.get_obsoletes(),
}
}
data['comment'] = ''
@ -144,15 +155,17 @@ class upload(PyPIRCCommand):
pass
if self.sign:
with open(filename + ".asc", "rb") as f:
data['gpg_signature'] = (os.path.basename(filename) + ".asc",
f.read())
with open(filename + '.asc', 'rb') as f:
data['gpg_signature'] = (
os.path.basename(filename) + '.asc',
f.read(),
)
# set up the authentication
user_pass = (self.username + ":" + self.password).encode('ascii')
user_pass = (self.username + ':' + self.password).encode('ascii')
# The exact encoding of the authentication string is debated.
# Anyway PyPI only accepts ascii for both username or password.
auth = "Basic " + standard_b64encode(user_pass).decode('ascii')
auth = 'Basic ' + standard_b64encode(user_pass).decode('ascii')
# Build up the MIME payload for the POST data
boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254'
@ -172,12 +185,12 @@ class upload(PyPIRCCommand):
value = str(value).encode('utf-8')
body.write(sep_boundary)
body.write(title.encode('utf-8'))
body.write(b"\r\n\r\n")
body.write(b'\r\n\r\n')
body.write(value)
body.write(end_boundary)
body = body.getvalue()
msg = "Submitting %s to %s" % (filename, self.repository)
msg = 'Submitting {} to {}'.format(filename, self.repository)
self.announce(msg, log.INFO)
# build the Request
@ -187,8 +200,10 @@ class upload(PyPIRCCommand):
'Authorization': auth,
}
request = Request(self.repository, data=body,
headers=headers)
request = Request(
self.repository, data=body,
headers=headers,
)
# send the data
try:
result = urlopen(request)
@ -202,13 +217,15 @@ class upload(PyPIRCCommand):
raise
if status == 200:
self.announce('Server response (%s): %s' % (status, reason),
log.INFO)
self.announce(
'Server response ({}): {}'.format(status, reason),
log.INFO,
)
if self.show_response:
text = self._read_pypi_response(result)
msg = '\n'.join(('-' * 75, text, '-' * 75))
self.announce(msg, log.INFO)
else:
msg = 'Upload failed (%s): %s' % (status, reason)
msg = 'Upload failed ({}): {}'.format(status, reason)
self.announce(msg, log.ERROR)
raise DistutilsError(msg)

View file

@ -3,6 +3,8 @@
Provides the PyPIRCCommand class, the base class for the command classes
that uses .pypirc in the distutils.command package.
"""
from __future__ import annotations
import os
from configparser import RawConfigParser
@ -18,6 +20,7 @@ username:%s
password:%s
"""
class PyPIRCCommand(Command):
"""Base command that knows how to handle the .pypirc file
"""
@ -27,11 +30,16 @@ class PyPIRCCommand(Command):
realm = None
user_options = [
('repository=', 'r',
"url of repository [default: %s]" % \
DEFAULT_REPOSITORY),
('show-response', None,
'display full response text from server')]
(
'repository=', 'r',
'url of repository [default: %s]' %
DEFAULT_REPOSITORY,
),
(
'show-response', None,
'display full response text from server',
),
]
boolean_options = ['show-response']
@ -58,9 +66,11 @@ class PyPIRCCommand(Command):
if 'distutils' in sections:
# let's get the list of servers
index_servers = config.get('distutils', 'index-servers')
_servers = [server.strip() for server in
index_servers.split('\n')
if server.strip() != '']
_servers = [
server.strip() for server in
index_servers.split('\n')
if server.strip() != ''
]
if _servers == []:
# nothing set, let's try to get the default pypi
if 'pypi' in sections:
@ -74,10 +84,14 @@ class PyPIRCCommand(Command):
current['username'] = config.get(server, 'username')
# optional params
for key, default in (('repository',
self.DEFAULT_REPOSITORY),
('realm', self.DEFAULT_REALM),
('password', None)):
for key, default in (
(
'repository',
self.DEFAULT_REPOSITORY,
),
('realm', self.DEFAULT_REALM),
('password', None),
):
if config.has_option(server, key):
current[key] = config.get(server, key)
else:
@ -86,13 +100,17 @@ class PyPIRCCommand(Command):
# work around people having "repository" for the "pypi"
# section of their config set to the HTTP (rather than
# HTTPS) URL
if (server == 'pypi' and
repository in (self.DEFAULT_REPOSITORY, 'pypi')):
if (
server == 'pypi' and
repository in (self.DEFAULT_REPOSITORY, 'pypi')
):
current['repository'] = self.DEFAULT_REPOSITORY
return current
if (current['server'] == repository or
current['repository'] == repository):
if (
current['server'] == repository or
current['repository'] == repository
):
return current
elif 'server-login' in sections:
# old format
@ -101,11 +119,13 @@ class PyPIRCCommand(Command):
repository = config.get(server, 'repository')
else:
repository = self.DEFAULT_REPOSITORY
return {'username': config.get(server, 'username'),
'password': config.get(server, 'password'),
'repository': repository,
'server': server,
'realm': self.DEFAULT_REALM}
return {
'username': config.get(server, 'username'),
'password': config.get(server, 'password'),
'repository': repository,
'server': server,
'realm': self.DEFAULT_REALM,
}
return {}

View file

@ -5,18 +5,18 @@ the 'setup' function (which is to be called from the setup script). Also
indirectly provides the Distribution and Command classes, although they are
really defined in distutils.dist and distutils.cmd.
"""
from __future__ import annotations
import os
import sys
from distutils.debug import DEBUG
from distutils.errors import *
# Mainly import these so setup scripts can "from distutils.core import" them.
from distutils.dist import Distribution
from distutils.cmd import Command
from distutils.config import PyPIRCCommand
from distutils.debug import DEBUG
from distutils.dist import Distribution
from distutils.errors import *
from distutils.extension import Extension
# Mainly import these so setup scripts can "from distutils.core import" them.
# This is a barebones help message generated displayed when the user
# runs the setup script with no arguments at all. More useful help
@ -29,7 +29,8 @@ usage: %(script)s [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...]
or: %(script)s cmd --help
"""
def gen_usage (script_name):
def gen_usage(script_name):
script = os.path.basename(script_name)
return USAGE % vars()
@ -39,22 +40,26 @@ _setup_stop_after = None
_setup_distribution = None
# Legal keyword arguments for the setup() function
setup_keywords = ('distclass', 'script_name', 'script_args', 'options',
'name', 'version', 'author', 'author_email',
'maintainer', 'maintainer_email', 'url', 'license',
'description', 'long_description', 'keywords',
'platforms', 'classifiers', 'download_url',
'requires', 'provides', 'obsoletes',
)
setup_keywords = (
'distclass', 'script_name', 'script_args', 'options',
'name', 'version', 'author', 'author_email',
'maintainer', 'maintainer_email', 'url', 'license',
'description', 'long_description', 'keywords',
'platforms', 'classifiers', 'download_url',
'requires', 'provides', 'obsoletes',
)
# Legal keyword arguments for the Extension constructor
extension_keywords = ('name', 'sources', 'include_dirs',
'define_macros', 'undef_macros',
'library_dirs', 'libraries', 'runtime_library_dirs',
'extra_objects', 'extra_compile_args', 'extra_link_args',
'swig_opts', 'export_symbols', 'depends', 'language')
extension_keywords = (
'name', 'sources', 'include_dirs',
'define_macros', 'undef_macros',
'library_dirs', 'libraries', 'runtime_library_dirs',
'extra_objects', 'extra_compile_args', 'extra_link_args',
'swig_opts', 'export_symbols', 'depends', 'language',
)
def setup (**attrs):
def setup(**attrs):
"""The gateway to the Distutils: do everything your setup script needs
to do, in a highly flexible and user-driven way. Briefly: create a
Distribution instance; find and parse config files; parse the command
@ -99,7 +104,7 @@ def setup (**attrs):
if 'script_name' not in attrs:
attrs['script_name'] = os.path.basename(sys.argv[0])
if 'script_args' not in attrs:
if 'script_args' not in attrs:
attrs['script_args'] = sys.argv[1:]
# Create the Distribution instance, using the remaining arguments
@ -108,12 +113,14 @@ def setup (**attrs):
_setup_distribution = dist = klass(attrs)
except DistutilsSetupError as msg:
if 'name' not in attrs:
raise SystemExit("error in setup command: %s" % msg)
raise SystemExit('error in setup command: %s' % msg)
else:
raise SystemExit("error in %s setup command: %s" % \
(attrs['name'], msg))
raise SystemExit(
'error in %s setup command: %s' %
(attrs['name'], msg),
)
if _setup_stop_after == "init":
if _setup_stop_after == 'init':
return dist
# Find and parse the config file(s): they will override options from
@ -121,10 +128,10 @@ def setup (**attrs):
dist.parse_config_files()
if DEBUG:
print("options (after parsing config files):")
print('options (after parsing config files):')
dist.dump_option_dicts()
if _setup_stop_after == "config":
if _setup_stop_after == 'config':
return dist
# Parse the command line and override config files; any
@ -133,13 +140,13 @@ def setup (**attrs):
try:
ok = dist.parse_command_line()
except DistutilsArgError as msg:
raise SystemExit(gen_usage(dist.script_name) + "\nerror: %s" % msg)
raise SystemExit(gen_usage(dist.script_name) + '\nerror: %s' % msg)
if DEBUG:
print("options (after parsing command line):")
print('options (after parsing command line):')
dist.dump_option_dicts()
if _setup_stop_after == "commandline":
if _setup_stop_after == 'commandline':
return dist
# And finally, run all the commands found on the command line.
@ -147,27 +154,29 @@ def setup (**attrs):
try:
dist.run_commands()
except KeyboardInterrupt:
raise SystemExit("interrupted")
raise SystemExit('interrupted')
except OSError as exc:
if DEBUG:
sys.stderr.write("error: %s\n" % (exc,))
sys.stderr.write('error: {}\n'.format(exc))
raise
else:
raise SystemExit("error: %s" % (exc,))
raise SystemExit('error: {}'.format(exc))
except (DistutilsError,
CCompilerError) as msg:
except (
DistutilsError,
CCompilerError,
) as msg:
if DEBUG:
raise
else:
raise SystemExit("error: " + str(msg))
raise SystemExit('error: ' + str(msg))
return dist
# setup ()
def run_setup (script_name, script_args=None, stop_after="run"):
def run_setup(script_name, script_args=None, stop_after='run'):
"""Run a setup script in a somewhat controlled environment, and
return the Distribution instance that drives things. This is useful
if you need to find out the distribution meta-data (passed as
@ -199,7 +208,7 @@ def run_setup (script_name, script_args=None, stop_after="run"):
used to drive the Distutils.
"""
if stop_after not in ('init', 'config', 'commandline', 'run'):
raise ValueError("invalid value for 'stop_after': %r" % (stop_after,))
raise ValueError("invalid value for 'stop_after': {!r}".format(stop_after))
global _setup_stop_after, _setup_distribution
_setup_stop_after = stop_after
@ -222,9 +231,13 @@ def run_setup (script_name, script_args=None, stop_after="run"):
pass
if _setup_distribution is None:
raise RuntimeError(("'distutils.core.setup()' was never called -- "
"perhaps '%s' is not a Distutils setup script?") % \
script_name)
raise RuntimeError(
(
"'distutils.core.setup()' was never called -- "
"perhaps '%s' is not a Distutils setup script?"
) %
script_name,
)
# I wonder if the setup script's namespace -- g and l -- would be of
# any interest to callers?

View file

@ -5,7 +5,6 @@ handles the Cygwin port of the GNU C compiler to Windows. It also contains
the Mingw32CCompiler class which handles the mingw32 port of GCC (same as
cygwin in no-cygwin mode).
"""
# problems:
#
# * if you use a msvc compiled python version (1.5.2)
@ -46,19 +45,25 @@ cygwin in no-cygwin mode).
# (ld supports -shared)
# * llvm-mingw with Clang 11 works
# (lld supports -shared)
from __future__ import annotations
import os
import sys
import copy
from subprocess import Popen, PIPE, check_output
import os
import re
import sys
from subprocess import check_output
from subprocess import PIPE
from subprocess import Popen
from distutils.unixccompiler import UnixCCompiler
from distutils.errors import CCompilerError
from distutils.errors import CompileError
from distutils.errors import DistutilsExecError
from distutils.errors import UnknownFileError
from distutils.file_util import write_file
from distutils.errors import (DistutilsExecError, CCompilerError,
CompileError, UnknownFileError)
from distutils.version import LooseVersion
from distutils.spawn import find_executable
from distutils.unixccompiler import UnixCCompiler
from distutils.version import LooseVersion
def get_msvcr():
"""Include the appropriate MSVC runtime library if Python was built
@ -66,7 +71,7 @@ def get_msvcr():
"""
msc_pos = sys.version.find('MSC v.')
if msc_pos != -1:
msc_ver = sys.version[msc_pos+6:msc_pos+10]
msc_ver = sys.version[msc_pos + 6:msc_pos + 10]
if msc_ver == '1300':
# MSVC 7.0
return ['msvcr70']
@ -83,79 +88,90 @@ def get_msvcr():
# VS2010 / MSVC 10.0
return ['msvcr100']
else:
raise ValueError("Unknown MS Compiler version %s " % msc_ver)
raise ValueError('Unknown MS Compiler version %s ' % msc_ver)
class CygwinCCompiler(UnixCCompiler):
""" Handles the Cygwin port of the GNU C compiler to Windows.
"""
compiler_type = 'cygwin'
obj_extension = ".o"
static_lib_extension = ".a"
shared_lib_extension = ".dll"
static_lib_format = "lib%s%s"
shared_lib_format = "%s%s"
exe_extension = ".exe"
obj_extension = '.o'
static_lib_extension = '.a'
shared_lib_extension = '.dll'
static_lib_format = 'lib%s%s'
shared_lib_format = '%s%s'
exe_extension = '.exe'
def __init__(self, verbose=0, dry_run=0, force=0):
UnixCCompiler.__init__(self, verbose, dry_run, force)
status, details = check_config_h()
self.debug_print("Python's GCC status: %s (details: %s)" %
(status, details))
self.debug_print(
"Python's GCC status: %s (details: %s)" %
(status, details),
)
if status is not CONFIG_H_OK:
self.warn(
"Python's pyconfig.h doesn't seem to support your compiler. "
"Reason: %s. "
"Compiling may fail because of undefined preprocessor macros."
% details)
'Reason: %s. '
'Compiling may fail because of undefined preprocessor macros.'
% details,
)
self.cc = os.environ.get('CC', 'gcc')
self.cxx = os.environ.get('CXX', 'g++')
if ('gcc' in self.cc): # Start gcc workaround
if ('gcc' in self.cc): # Start gcc workaround
self.gcc_version, self.ld_version, self.dllwrap_version = \
get_versions()
self.debug_print(self.compiler_type + ": gcc %s, ld %s, dllwrap %s\n" %
(self.gcc_version,
self.ld_version,
self.dllwrap_version) )
self.debug_print(
self.compiler_type + ': gcc %s, ld %s, dllwrap %s\n' %
(
self.gcc_version,
self.ld_version,
self.dllwrap_version,
), )
# ld_version >= "2.10.90" and < "2.13" should also be able to use
# gcc -mdll instead of dllwrap
# Older dllwraps had own version numbers, newer ones use the
# same as the rest of binutils ( also ld )
# dllwrap 2.10.90 is buggy
if self.ld_version >= "2.10.90":
if self.ld_version >= '2.10.90':
self.linker_dll = self.cc
else:
self.linker_dll = "dllwrap"
self.linker_dll = 'dllwrap'
# ld_version >= "2.13" support -shared so use it instead of
# -mdll -static
if self.ld_version >= "2.13":
shared_option = "-shared"
if self.ld_version >= '2.13':
shared_option = '-shared'
else:
shared_option = "-mdll -static"
else: # Assume linker is up to date
shared_option = '-mdll -static'
else: # Assume linker is up to date
self.linker_dll = self.cc
shared_option = "-shared"
shared_option = '-shared'
self.set_executables(compiler='%s -mcygwin -O -Wall' % self.cc,
compiler_so='%s -mcygwin -mdll -O -Wall' % self.cc,
compiler_cxx='%s -mcygwin -O -Wall' % self.cxx,
linker_exe='%s -mcygwin' % self.cc,
linker_so=('%s -mcygwin %s' %
(self.linker_dll, shared_option)))
self.set_executables(
compiler='%s -mcygwin -O -Wall' % self.cc,
compiler_so='%s -mcygwin -mdll -O -Wall' % self.cc,
compiler_cxx='%s -mcygwin -O -Wall' % self.cxx,
linker_exe='%s -mcygwin' % self.cc,
linker_so=(
'%s -mcygwin %s' %
(self.linker_dll, shared_option)
),
)
# cygwin and mingw32 need different sets of libraries
if ('gcc' in self.cc and self.gcc_version == "2.91.57"):
if ('gcc' in self.cc and self.gcc_version == '2.91.57'):
# cygwin shouldn't need msvcrt, but without the dlls will crash
# (gcc version 2.91.57) -- perhaps something about initialization
self.dll_libraries=["msvcrt"]
self.dll_libraries = ['msvcrt']
self.warn(
"Consider upgrading to a newer version of gcc")
'Consider upgrading to a newer version of gcc',
)
else:
# Include the appropriate MSVC runtime library if Python was built
# with MSVC 7.0 or later.
@ -166,20 +182,24 @@ class CygwinCCompiler(UnixCCompiler):
if ext == '.rc' or ext == '.res':
# gcc needs '.res' and '.rc' compiled to object files !!!
try:
self.spawn(["windres", "-i", src, "-o", obj])
self.spawn(['windres', '-i', src, '-o', obj])
except DistutilsExecError as msg:
raise CompileError(msg)
else: # for other files use the C-compiler
else: # for other files use the C-compiler
try:
self.spawn(self.compiler_so + cc_args + [src, '-o', obj] +
extra_postargs)
self.spawn(
self.compiler_so + cc_args + [src, '-o', obj] +
extra_postargs,
)
except DistutilsExecError as msg:
raise CompileError(msg)
def link(self, target_desc, objects, output_filename, output_dir=None,
libraries=None, library_dirs=None, runtime_library_dirs=None,
export_symbols=None, debug=0, extra_preargs=None,
extra_postargs=None, build_temp=None, target_lang=None):
def link(
self, target_desc, objects, output_filename, output_dir=None,
libraries=None, library_dirs=None, runtime_library_dirs=None,
export_symbols=None, debug=0, extra_preargs=None,
extra_postargs=None, build_temp=None, target_lang=None,
):
"""Link the objects."""
# use separate copies, so we can modify the lists
extra_preargs = copy.copy(extra_preargs or [])
@ -192,7 +212,7 @@ class CygwinCCompiler(UnixCCompiler):
# handle export symbols by creating a def-file
# with executables this only works with gcc/ld as linker
if ((export_symbols is not None) and
(target_desc != self.EXECUTABLE or self.linker_dll == "gcc")):
(target_desc != self.EXECUTABLE or self.linker_dll == 'gcc')):
# (The linker doesn't do anything if output is up-to-date.
# So it would probably better to check if we really need this,
# but for this we had to insert some unchanged parts of
@ -204,28 +224,32 @@ class CygwinCCompiler(UnixCCompiler):
temp_dir = os.path.dirname(objects[0])
# name of dll to give the helper files the same base name
(dll_name, dll_extension) = os.path.splitext(
os.path.basename(output_filename))
os.path.basename(output_filename),
)
# generate the filenames for these files
def_file = os.path.join(temp_dir, dll_name + ".def")
lib_file = os.path.join(temp_dir, 'lib' + dll_name + ".a")
def_file = os.path.join(temp_dir, dll_name + '.def')
lib_file = os.path.join(temp_dir, 'lib' + dll_name + '.a')
# Generate .def file
contents = [
"LIBRARY %s" % os.path.basename(output_filename),
"EXPORTS"]
'LIBRARY %s' % os.path.basename(output_filename),
'EXPORTS',
]
for sym in export_symbols:
contents.append(sym)
self.execute(write_file, (def_file, contents),
"writing %s" % def_file)
self.execute(
write_file, (def_file, contents),
'writing %s' % def_file,
)
# next add options for def-file and to creating import libraries
# dllwrap uses different options than gcc/ld
if self.linker_dll == "dllwrap":
extra_preargs.extend(["--output-lib", lib_file])
if self.linker_dll == 'dllwrap':
extra_preargs.extend(['--output-lib', lib_file])
# for dllwrap we have to use a special option
extra_preargs.extend(["--def", def_file])
extra_preargs.extend(['--def', def_file])
# we use gcc/ld here and can be sure ld is >= 2.9.10
else:
# doesn't work: bfd_close build\...\libfoo.a: Invalid operation
@ -243,14 +267,16 @@ class CygwinCCompiler(UnixCCompiler):
# unstripped_file = stripped_file + XXX KiB
# ( XXX=254 for a typical python extension))
if not debug:
extra_preargs.append("-s")
extra_preargs.append('-s')
UnixCCompiler.link(self, target_desc, objects, output_filename,
output_dir, libraries, library_dirs,
runtime_library_dirs,
None, # export_symbols, we do this in our def-file
debug, extra_preargs, extra_postargs, build_temp,
target_lang)
UnixCCompiler.link(
self, target_desc, objects, output_filename,
output_dir, libraries, library_dirs,
runtime_library_dirs,
None, # export_symbols, we do this in our def-file
debug, extra_preargs, extra_postargs, build_temp,
target_lang,
)
# -- Miscellaneous methods -----------------------------------------
@ -262,21 +288,33 @@ class CygwinCCompiler(UnixCCompiler):
for src_name in source_filenames:
# use normcase to make sure '.rc' is really '.rc' and not '.RC'
base, ext = os.path.splitext(os.path.normcase(src_name))
if ext not in (self.src_extensions + ['.rc','.res']):
raise UnknownFileError("unknown file type '%s' (from '%s')" % \
(ext, src_name))
if ext not in (self.src_extensions + ['.rc', '.res']):
raise UnknownFileError(
"unknown file type '%s' (from '%s')" %
(ext, src_name),
)
if strip_dir:
base = os.path.basename (base)
base = os.path.basename(base)
if ext in ('.res', '.rc'):
# these need to be compiled to object files
obj_names.append (os.path.join(output_dir,
base + ext + self.obj_extension))
obj_names.append(
os.path.join(
output_dir,
base + ext + self.obj_extension,
),
)
else:
obj_names.append (os.path.join(output_dir,
base + self.obj_extension))
obj_names.append(
os.path.join(
output_dir,
base + self.obj_extension,
),
)
return obj_names
# the same as cygwin plus some additional parameters
class Mingw32CCompiler(CygwinCCompiler):
""" Handles the Mingw32 port of the GNU C compiler to Windows.
"""
@ -284,39 +322,44 @@ class Mingw32CCompiler(CygwinCCompiler):
def __init__(self, verbose=0, dry_run=0, force=0):
CygwinCCompiler.__init__ (self, verbose, dry_run, force)
CygwinCCompiler.__init__(self, verbose, dry_run, force)
# ld_version >= "2.13" support -shared so use it instead of
# -mdll -static
if ('gcc' in self.cc and self.ld_version < "2.13"):
shared_option = "-mdll -static"
if ('gcc' in self.cc and self.ld_version < '2.13'):
shared_option = '-mdll -static'
else:
shared_option = "-shared"
shared_option = '-shared'
# A real mingw32 doesn't need to specify a different entry point,
# but cygwin 2.91.57 in no-cygwin-mode needs it.
if ('gcc' in self.cc and self.gcc_version <= "2.91.57"):
if ('gcc' in self.cc and self.gcc_version <= '2.91.57'):
entry_point = '--entry _DllMain@12'
else:
entry_point = ''
if is_cygwincc(self.cc):
raise CCompilerError(
'Cygwin gcc cannot be used with --compiler=mingw32')
'Cygwin gcc cannot be used with --compiler=mingw32',
)
self.set_executables(compiler='%s -O -Wall' % self.cc,
compiler_so='%s -mdll -O -Wall' % self.cc,
compiler_cxx='%s -O -Wall' % self.cxx,
linker_exe='%s' % self.cc,
linker_so='%s %s %s'
% (self.linker_dll, shared_option,
entry_point))
self.set_executables(
compiler='%s -O -Wall' % self.cc,
compiler_so='%s -mdll -O -Wall' % self.cc,
compiler_cxx='%s -O -Wall' % self.cxx,
linker_exe='%s' % self.cc,
linker_so='%s %s %s'
% (
self.linker_dll, shared_option,
entry_point,
),
)
# Maybe we should also append -mthreads, but then the finished
# dlls need another dll (mingwm10.dll see Mingw32 docs)
# (-mthreads: Support thread-safe exception handling on `Mingw32')
# no additional libraries needed
self.dll_libraries=[]
self.dll_libraries = []
# Include the appropriate MSVC runtime library if Python was built
# with MSVC 7.0 or later.
@ -326,9 +369,11 @@ class Mingw32CCompiler(CygwinCCompiler):
# default, we should at least warn the user if he is using an unmodified
# version.
CONFIG_H_OK = "ok"
CONFIG_H_NOTOK = "not ok"
CONFIG_H_UNCERTAIN = "uncertain"
CONFIG_H_OK = 'ok'
CONFIG_H_NOTOK = 'not ok'
CONFIG_H_UNCERTAIN = 'uncertain'
def check_config_h():
"""Check if the current Python installation appears amenable to building
@ -355,11 +400,11 @@ def check_config_h():
# if sys.version contains GCC then python was compiled with GCC, and the
# pyconfig.h file should be OK
if "GCC" in sys.version:
if 'GCC' in sys.version:
return CONFIG_H_OK, "sys.version mentions 'GCC'"
# Clang would also work
if "Clang" in sys.version:
if 'Clang' in sys.version:
return CONFIG_H_OK, "sys.version mentions 'Clang'"
# let's see if __GNUC__ is mentioned in python.h
@ -367,18 +412,22 @@ def check_config_h():
try:
config_h = open(fn)
try:
if "__GNUC__" in config_h.read():
if '__GNUC__' in config_h.read():
return CONFIG_H_OK, "'%s' mentions '__GNUC__'" % fn
else:
return CONFIG_H_NOTOK, "'%s' does not mention '__GNUC__'" % fn
finally:
config_h.close()
except OSError as exc:
return (CONFIG_H_UNCERTAIN,
"couldn't read '%s': %s" % (fn, exc.strerror))
return (
CONFIG_H_UNCERTAIN,
"couldn't read '{}': {}".format(fn, exc.strerror),
)
RE_VERSION = re.compile(br'(\d+\.\d+(\.\d+)*)')
def _find_exe_version(cmd):
"""Find the version of an executable by running `cmd` in the shell.
@ -400,6 +449,7 @@ def _find_exe_version(cmd):
# so we need to decode our bytes
return LooseVersion(result.group(1).decode())
def get_versions():
""" Try to find out the versions of gcc, ld and dllwrap.
@ -408,6 +458,7 @@ def get_versions():
commands = ['gcc -dumpversion', 'ld -v', 'dllwrap --version']
return tuple([_find_exe_version(cmd) for cmd in commands])
def is_cygwincc(cc):
'''Try to determine if the compiler that would be used is from cygwin.'''
out_string = check_output([cc, '-dumpmachine'])

View file

@ -1,3 +1,5 @@
from __future__ import annotations
import os
# If DISTUTILS_DEBUG is anything other than the empty string, we run in

View file

@ -3,20 +3,24 @@
Utility functions for simple, timestamp-based dependency of files
and groups of files; also, function based entirely on such
timestamp dependency analysis."""
from __future__ import annotations
import os
from distutils.errors import DistutilsFileError
def newer (source, target):
def newer(source, target):
"""Return true if 'source' exists and is more recently modified than
'target', or if 'source' exists and 'target' doesn't. Return false if
both exist and 'target' is the same age or younger than 'source'.
Raise DistutilsFileError if 'source' does not exist.
"""
if not os.path.exists(source):
raise DistutilsFileError("file '%s' does not exist" %
os.path.abspath(source))
raise DistutilsFileError(
"file '%s' does not exist" %
os.path.abspath(source),
)
if not os.path.exists(target):
return 1
@ -29,7 +33,7 @@ def newer (source, target):
# newer ()
def newer_pairwise (sources, targets):
def newer_pairwise(sources, targets):
"""Walk two filename lists in parallel, testing if each source is newer
than its corresponding target. Return a pair of lists (sources,
targets) where source is newer than target, according to the semantics
@ -51,7 +55,7 @@ def newer_pairwise (sources, targets):
# newer_pairwise ()
def newer_group (sources, target, missing='error'):
def newer_group(sources, target, missing='error'):
"""Return true if 'target' is out-of-date with respect to any file
listed in 'sources'. In other words, if 'target' exists and is newer
than every file in 'sources', return false; otherwise return true.
@ -79,9 +83,9 @@ def newer_group (sources, target, missing='error'):
if missing == 'error': # blow up when we stat() the file
pass
elif missing == 'ignore': # missing source dropped from
continue # target's dependency list
continue # target's dependency list
elif missing == 'newer': # missing source means target is
return 1 # out-of-date
return 1 # out-of-date
source_mtime = os.stat(source)[ST_MTIME]
if source_mtime > target_mtime:

View file

@ -1,11 +1,14 @@
"""distutils.dir_util
Utility functions for manipulating directories and directory trees."""
from __future__ import annotations
import os
import errno
from distutils.errors import DistutilsFileError, DistutilsInternalError
import os
from distutils import log
from distutils.errors import DistutilsFileError
from distutils.errors import DistutilsInternalError
# cache for by mkpath() -- in addition to cheapening redundant calls,
# eliminates redundant "creating /foo/bar/baz" messages in dry-run mode
@ -14,6 +17,8 @@ _path_created = {}
# I don't use os.makedirs because a) it's new to Python 1.5.2, and
# b) it blows up if the directory already exists (I want to silently
# succeed in that case).
def mkpath(name, mode=0o777, verbose=1, dry_run=0):
"""Create a directory and any missing ancestor directories.
@ -30,7 +35,8 @@ def mkpath(name, mode=0o777, verbose=1, dry_run=0):
# Detect a common bug -- name is None
if not isinstance(name, str):
raise DistutilsInternalError(
"mkpath: 'name' must be a string (got %r)" % (name,))
"mkpath: 'name' must be a string (got {!r})".format(name),
)
# XXX what's the better way to handle verbosity? print as we create
# each directory in the path (the current behaviour), or only announce
@ -63,7 +69,7 @@ def mkpath(name, mode=0o777, verbose=1, dry_run=0):
continue
if verbose >= 1:
log.info("creating %s", head)
log.info('creating %s', head)
if not dry_run:
try:
@ -71,12 +77,14 @@ def mkpath(name, mode=0o777, verbose=1, dry_run=0):
except OSError as exc:
if not (exc.errno == errno.EEXIST and os.path.isdir(head)):
raise DistutilsFileError(
"could not create '%s': %s" % (head, exc.args[-1]))
"could not create '{}': {}".format(head, exc.args[-1]),
)
created_dirs.append(head)
_path_created[abs_head] = 1
return created_dirs
def create_tree(base_dir, files, mode=0o777, verbose=1, dry_run=0):
"""Create all the empty directories under 'base_dir' needed to put 'files'
there.
@ -96,8 +104,11 @@ def create_tree(base_dir, files, mode=0o777, verbose=1, dry_run=0):
for dir in sorted(need_dir):
mkpath(dir, mode, verbose=verbose, dry_run=dry_run)
def copy_tree(src, dst, preserve_mode=1, preserve_times=1,
preserve_symlinks=0, update=0, verbose=1, dry_run=0):
def copy_tree(
src, dst, preserve_mode=1, preserve_times=1,
preserve_symlinks=0, update=0, verbose=1, dry_run=0,
):
"""Copy an entire directory tree 'src' to a new location 'dst'.
Both 'src' and 'dst' must be directory names. If 'src' is not a
@ -121,7 +132,8 @@ def copy_tree(src, dst, preserve_mode=1, preserve_times=1,
if not dry_run and not os.path.isdir(src):
raise DistutilsFileError(
"cannot copy tree '%s': not a directory" % src)
"cannot copy tree '%s': not a directory" % src,
)
try:
names = os.listdir(src)
except OSError as e:
@ -129,7 +141,8 @@ def copy_tree(src, dst, preserve_mode=1, preserve_times=1,
names = []
else:
raise DistutilsFileError(
"error listing files in '%s': %s" % (src, e.strerror))
"error listing files in '{}': {}".format(src, e.strerror),
)
if not dry_run:
mkpath(dst, verbose=verbose)
@ -147,34 +160,41 @@ def copy_tree(src, dst, preserve_mode=1, preserve_times=1,
if preserve_symlinks and os.path.islink(src_name):
link_dest = os.readlink(src_name)
if verbose >= 1:
log.info("linking %s -> %s", dst_name, link_dest)
log.info('linking %s -> %s', dst_name, link_dest)
if not dry_run:
os.symlink(link_dest, dst_name)
outputs.append(dst_name)
elif os.path.isdir(src_name):
outputs.extend(
copy_tree(src_name, dst_name, preserve_mode,
preserve_times, preserve_symlinks, update,
verbose=verbose, dry_run=dry_run))
copy_tree(
src_name, dst_name, preserve_mode,
preserve_times, preserve_symlinks, update,
verbose=verbose, dry_run=dry_run,
),
)
else:
copy_file(src_name, dst_name, preserve_mode,
preserve_times, update, verbose=verbose,
dry_run=dry_run)
copy_file(
src_name, dst_name, preserve_mode,
preserve_times, update, verbose=verbose,
dry_run=dry_run,
)
outputs.append(dst_name)
return outputs
def _build_cmdtuple(path, cmdtuples):
"""Helper for remove_tree()."""
for f in os.listdir(path):
real_f = os.path.join(path,f)
real_f = os.path.join(path, f)
if os.path.isdir(real_f) and not os.path.islink(real_f):
_build_cmdtuple(real_f, cmdtuples)
else:
cmdtuples.append((os.remove, real_f))
cmdtuples.append((os.rmdir, path))
def remove_tree(directory, verbose=1, dry_run=0):
"""Recursively remove an entire directory tree.
@ -197,7 +217,8 @@ def remove_tree(directory, verbose=1, dry_run=0):
if abspath in _path_created:
del _path_created[abspath]
except OSError as exc:
log.warn("error removing %s: %s", directory, exc)
log.warn('error removing %s: %s', directory, exc)
def ensure_relative(path):
"""Take the full path 'path', and make it a relative path.

View file

@ -3,10 +3,11 @@
Provides the Distribution class, which represents the module distribution
being built/installed/distributed.
"""
from __future__ import annotations
import sys
import os
import re
import sys
from email import message_from_file
try:
@ -65,12 +66,14 @@ class Distribution:
# have minimal control over.
# The fourth entry for verbose means that it can be repeated.
global_options = [
('verbose', 'v', "run verbosely (default)", 1),
('quiet', 'q', "run quietly (turns verbosity off)"),
('verbose', 'v', 'run verbosely (default)', 1),
('quiet', 'q', 'run quietly (turns verbosity off)'),
('dry-run', 'n', "don't actually do anything"),
('help', 'h', "show detailed help message"),
('no-user-cfg', None,
'ignore pydistutils.cfg in your home directory'),
('help', 'h', 'show detailed help message'),
(
'no-user-cfg', None,
'ignore pydistutils.cfg in your home directory',
),
]
# 'common_usage' is a short (2-3 line) string describing the common
@ -84,49 +87,91 @@ Common commands: (see '--help-commands' for more)
# options that are not propagated to the commands
display_options = [
('help-commands', None,
"list all available commands"),
('name', None,
"print package name"),
('version', 'V',
"print package version"),
('fullname', None,
"print <package name>-<version>"),
('author', None,
"print the author's name"),
('author-email', None,
"print the author's email address"),
('maintainer', None,
"print the maintainer's name"),
('maintainer-email', None,
"print the maintainer's email address"),
('contact', None,
"print the maintainer's name if known, else the author's"),
('contact-email', None,
"print the maintainer's email address if known, else the author's"),
('url', None,
"print the URL for this package"),
('license', None,
"print the license of the package"),
('licence', None,
"alias for --license"),
('description', None,
"print the package description"),
('long-description', None,
"print the long package description"),
('platforms', None,
"print the list of platforms"),
('classifiers', None,
"print the list of classifiers"),
('keywords', None,
"print the list of keywords"),
('provides', None,
"print the list of packages/modules provided"),
('requires', None,
"print the list of packages/modules required"),
('obsoletes', None,
"print the list of packages/modules made obsolete")
]
(
'help-commands', None,
'list all available commands',
),
(
'name', None,
'print package name',
),
(
'version', 'V',
'print package version',
),
(
'fullname', None,
'print <package name>-<version>',
),
(
'author', None,
"print the author's name",
),
(
'author-email', None,
"print the author's email address",
),
(
'maintainer', None,
"print the maintainer's name",
),
(
'maintainer-email', None,
"print the maintainer's email address",
),
(
'contact', None,
"print the maintainer's name if known, else the author's",
),
(
'contact-email', None,
"print the maintainer's email address if known, else the author's",
),
(
'url', None,
'print the URL for this package',
),
(
'license', None,
'print the license of the package',
),
(
'licence', None,
'alias for --license',
),
(
'description', None,
'print the package description',
),
(
'long-description', None,
'print the long package description',
),
(
'platforms', None,
'print the list of platforms',
),
(
'classifiers', None,
'print the list of classifiers',
),
(
'keywords', None,
'print the list of keywords',
),
(
'provides', None,
'print the list of packages/modules provided',
),
(
'requires', None,
'print the list of packages/modules required',
),
(
'obsoletes', None,
'print the list of packages/modules made obsolete',
),
]
display_option_names = [translate_longopt(x[0]) for x in display_options]
# negative options are options that exclude other options
@ -159,7 +204,7 @@ Common commands: (see '--help-commands' for more)
# object in a sneaky and underhanded (but efficient!) way.
self.metadata = DistributionMetadata()
for basename in self.metadata._METHOD_BASENAMES:
method_name = "get_" + basename
method_name = 'get_' + basename
setattr(self, method_name, getattr(self.metadata, method_name))
# 'cmdclass' maps command names to class objects, so we
@ -250,7 +295,7 @@ Common commands: (see '--help-commands' for more)
for (command, cmd_options) in options.items():
opt_dict = self.get_option_dict(command)
for (opt, val) in cmd_options.items():
opt_dict[opt] = ("setup script", val)
opt_dict[opt] = ('setup script', val)
if 'licence' in attrs:
attrs['license'] = attrs['licence']
@ -259,19 +304,19 @@ Common commands: (see '--help-commands' for more)
if warnings is not None:
warnings.warn(msg)
else:
sys.stderr.write(msg + "\n")
sys.stderr.write(msg + '\n')
# Now work on the rest of the attributes. Any attribute that's
# not already defined is invalid!
for (key, val) in attrs.items():
if hasattr(self.metadata, "set_" + key):
getattr(self.metadata, "set_" + key)(val)
if hasattr(self.metadata, 'set_' + key):
getattr(self.metadata, 'set_' + key)(val)
elif hasattr(self.metadata, key):
setattr(self.metadata, key, val)
elif hasattr(self, key):
setattr(self, key, val)
else:
msg = "Unknown distribution option: %s" % repr(key)
msg = 'Unknown distribution option: %s' % repr(key)
warnings.warn(msg)
# no-user-cfg is handled before other command line args
@ -303,7 +348,7 @@ Common commands: (see '--help-commands' for more)
dict = self.command_options[command] = {}
return dict
def dump_option_dicts(self, header=None, commands=None, indent=""):
def dump_option_dicts(self, header=None, commands=None, indent=''):
from pprint import pformat
if commands is None: # dump all command option dicts
@ -311,23 +356,27 @@ Common commands: (see '--help-commands' for more)
if header is not None:
self.announce(indent + header)
indent = indent + " "
indent = indent + ' '
if not commands:
self.announce(indent + "no commands known yet")
self.announce(indent + 'no commands known yet')
return
for cmd_name in commands:
opt_dict = self.command_options.get(cmd_name)
if opt_dict is None:
self.announce(indent +
"no option dict for '%s' command" % cmd_name)
self.announce(
indent +
"no option dict for '%s' command" % cmd_name,
)
else:
self.announce(indent +
"option dict for '%s' command:" % cmd_name)
self.announce(
indent +
"option dict for '%s' command:" % cmd_name,
)
out = pformat(opt_dict)
for line in out.split('\n'):
self.announce(indent + " " + line)
self.announce(indent + ' ' + line)
# -- Config file finding/parsing methods ---------------------------
@ -353,15 +402,15 @@ Common commands: (see '--help-commands' for more)
sys_dir = os.path.dirname(sys.modules['distutils'].__file__)
# Look for the system config file
sys_file = os.path.join(sys_dir, "distutils.cfg")
sys_file = os.path.join(sys_dir, 'distutils.cfg')
if os.path.isfile(sys_file):
files.append(sys_file)
# What to call the per-user config file
if os.name == 'posix':
user_filename = ".pydistutils.cfg"
user_filename = '.pydistutils.cfg'
else:
user_filename = "pydistutils.cfg"
user_filename = 'pydistutils.cfg'
# And look for the user config file
if self.want_user_cfg:
@ -370,12 +419,12 @@ Common commands: (see '--help-commands' for more)
files.append(user_file)
# All platforms support local setup.cfg
local_file = "setup.cfg"
local_file = 'setup.cfg'
if os.path.isfile(local_file):
files.append(local_file)
if DEBUG:
self.announce("using config files: %s" % ', '.join(files))
self.announce('using config files: %s' % ', '.join(files))
return files
@ -388,7 +437,8 @@ Common commands: (see '--help-commands' for more)
'install-base', 'install-platbase', 'install-lib',
'install-platlib', 'install-purelib', 'install-headers',
'install-scripts', 'install-data', 'prefix', 'exec-prefix',
'home', 'user', 'root']
'home', 'user', 'root',
]
else:
ignore_options = []
@ -398,12 +448,12 @@ Common commands: (see '--help-commands' for more)
filenames = self.find_config_files()
if DEBUG:
self.announce("Distribution.parse_config_files():")
self.announce('Distribution.parse_config_files():')
parser = ConfigParser()
for filename in filenames:
if DEBUG:
self.announce(" reading %s" % filename)
self.announce(' reading %s' % filename)
parser.read(filename)
for section in parser.sections():
options = parser.options(section)
@ -411,7 +461,7 @@ Common commands: (see '--help-commands' for more)
for opt in options:
if opt != '__name__' and opt not in ignore_options:
val = parser.get(section,opt)
val = parser.get(section, opt)
opt = opt.replace('-', '_')
opt_dict[opt] = (filename, val)
@ -428,7 +478,7 @@ Common commands: (see '--help-commands' for more)
try:
if alias:
setattr(self, alias, not strtobool(val))
elif opt in ('verbose', 'dry_run'): # ugh!
elif opt in ('verbose', 'dry_run'): # ugh!
setattr(self, opt, strtobool(val))
else:
setattr(self, opt, val)
@ -492,14 +542,16 @@ Common commands: (see '--help-commands' for more)
# latter, we omit the display-only options and show help for
# each command listed on the command line.
if self.help:
self._show_help(parser,
display_options=len(self.commands) == 0,
commands=self.commands)
self._show_help(
parser,
display_options=len(self.commands) == 0,
commands=self.commands,
)
return
# Oops, no commands found -- an end-user error
if not self.commands:
raise DistutilsArgError("no commands supplied")
raise DistutilsArgError('no commands supplied')
# All is well: return true
return True
@ -511,9 +563,11 @@ Common commands: (see '--help-commands' for more)
level as well as options recognized for commands.
"""
return self.global_options + [
("command-packages=", None,
"list of packages that provide distutils commands"),
]
(
'command-packages=', None,
'list of packages that provide distutils commands',
),
]
def _parse_command_opts(self, parser, args):
"""Parse the command-line options for a single command.
@ -545,14 +599,19 @@ Common commands: (see '--help-commands' for more)
# to be sure that the basic "command" interface is implemented.
if not issubclass(cmd_class, Command):
raise DistutilsClassError(
"command class %s must subclass Command" % cmd_class)
'command class %s must subclass Command' % cmd_class,
)
# Also make sure that the command object provides a list of its
# known options.
if not (hasattr(cmd_class, 'user_options') and
isinstance(cmd_class.user_options, list)):
msg = ("command class %s must provide "
"'user_options' attribute (a list of tuples)")
if not (
hasattr(cmd_class, 'user_options') and
isinstance(cmd_class.user_options, list)
):
msg = (
'command class %s must provide '
"'user_options' attribute (a list of tuples)"
)
raise DistutilsClassError(msg % cmd_class)
# If the command class has a list of negative alias options,
@ -564,36 +623,43 @@ Common commands: (see '--help-commands' for more)
# Check for help_options in command class. They have a different
# format (tuple of four) so we need to preprocess them here.
if (hasattr(cmd_class, 'help_options') and
isinstance(cmd_class.help_options, list)):
if (
hasattr(cmd_class, 'help_options') and
isinstance(cmd_class.help_options, list)
):
help_options = fix_help_options(cmd_class.help_options)
else:
help_options = []
# All commands support the global options too, just by adding
# in 'global_options'.
parser.set_option_table(self.global_options +
cmd_class.user_options +
help_options)
parser.set_option_table(
self.global_options +
cmd_class.user_options +
help_options,
)
parser.set_negative_aliases(negative_opt)
(args, opts) = parser.getopt(args[1:])
if hasattr(opts, 'help') and opts.help:
self._show_help(parser, display_options=0, commands=[cmd_class])
return
if (hasattr(cmd_class, 'help_options') and
isinstance(cmd_class.help_options, list)):
help_option_found=0
if (
hasattr(cmd_class, 'help_options') and
isinstance(cmd_class.help_options, list)
):
help_option_found = 0
for (help_option, short, desc, func) in cmd_class.help_options:
if hasattr(opts, parser.get_attr_name(help_option)):
help_option_found=1
help_option_found = 1
if callable(func):
func()
else:
raise DistutilsClassError(
"invalid help function %r for help option '%s': "
"must be a callable object (function, etc.)"
% (func, help_option))
'must be a callable object (function, etc.)'
% (func, help_option),
)
if help_option_found:
return
@ -602,7 +668,7 @@ Common commands: (see '--help-commands' for more)
# holding pen, the 'command_options' dictionary.
opt_dict = self.get_option_dict(command)
for (name, value) in vars(opts).items():
opt_dict[name] = ("command line", value)
opt_dict[name] = ('command line', value)
return args
@ -619,8 +685,10 @@ Common commands: (see '--help-commands' for more)
value = [elm.strip() for elm in value.split(',')]
setattr(self.metadata, attr, value)
def _show_help(self, parser, global_options=1, display_options=1,
commands=[]):
def _show_help(
self, parser, global_options=1, display_options=1,
commands=[],
):
"""Show help for the setup script command-line in the form of
several lists of command-line options. 'parser' should be a
FancyGetopt instance; do not expect it to be returned in the
@ -643,14 +711,15 @@ Common commands: (see '--help-commands' for more)
else:
options = self.global_options
parser.set_option_table(options)
parser.print_help(self.common_usage + "\nGlobal options:")
parser.print_help(self.common_usage + '\nGlobal options:')
print('')
if display_options:
parser.set_option_table(self.display_options)
parser.print_help(
"Information display options (just display " +
"information, ignore any commands)")
'Information display options (just display ' +
'information, ignore any commands)',
)
print('')
for command in self.commands:
@ -658,10 +727,14 @@ Common commands: (see '--help-commands' for more)
klass = command
else:
klass = self.get_command_class(command)
if (hasattr(klass, 'help_options') and
isinstance(klass.help_options, list)):
parser.set_option_table(klass.user_options +
fix_help_options(klass.help_options))
if (
hasattr(klass, 'help_options') and
isinstance(klass.help_options, list)
):
parser.set_option_table(
klass.user_options +
fix_help_options(klass.help_options),
)
else:
parser.set_option_table(klass.user_options)
parser.print_help("Options for '%s' command:" % klass.__name__)
@ -697,11 +770,13 @@ Common commands: (see '--help-commands' for more)
for (opt, val) in option_order:
if val and is_display_option.get(opt):
opt = translate_longopt(opt)
value = getattr(self.metadata, "get_"+opt)()
value = getattr(self.metadata, 'get_' + opt)()
if opt in ['keywords', 'platforms']:
print(','.join(value))
elif opt in ('classifiers', 'provides', 'requires',
'obsoletes'):
elif opt in (
'classifiers', 'provides', 'requires',
'obsoletes',
):
print('\n'.join(value))
else:
print(value)
@ -713,7 +788,7 @@ Common commands: (see '--help-commands' for more)
"""Print a subset of the list of all commands -- used by
'print_commands()'.
"""
print(header + ":")
print(header + ':')
for cmd in commands:
klass = self.cmdclass.get(cmd)
@ -722,9 +797,9 @@ Common commands: (see '--help-commands' for more)
try:
description = klass.description
except AttributeError:
description = "(no description available)"
description = '(no description available)'
print(" %-*s %s" % (max_length, cmd, description))
print(' %-*s %s' % (max_length, cmd, description))
def print_commands(self):
"""Print out a help message listing all available commands with a
@ -750,14 +825,18 @@ Common commands: (see '--help-commands' for more)
if len(cmd) > max_length:
max_length = len(cmd)
self.print_command_list(std_commands,
"Standard commands",
max_length)
self.print_command_list(
std_commands,
'Standard commands',
max_length,
)
if extra_commands:
print()
self.print_command_list(extra_commands,
"Extra commands",
max_length)
self.print_command_list(
extra_commands,
'Extra commands',
max_length,
)
def get_command_list(self):
"""Get a list of (command, description) tuples.
@ -787,7 +866,7 @@ Common commands: (see '--help-commands' for more)
try:
description = klass.description
except AttributeError:
description = "(no description available)"
description = '(no description available)'
rv.append((cmd, description))
return rv
@ -800,8 +879,8 @@ Common commands: (see '--help-commands' for more)
if pkgs is None:
pkgs = ''
pkgs = [pkg.strip() for pkg in pkgs.split(',') if pkg != '']
if "distutils.command" not in pkgs:
pkgs.insert(0, "distutils.command")
if 'distutils.command' not in pkgs:
pkgs.insert(0, 'distutils.command')
self.command_packages = pkgs
return pkgs
@ -822,7 +901,7 @@ Common commands: (see '--help-commands' for more)
return klass
for pkgname in self.get_command_packages():
module_name = "%s.%s" % (pkgname, command)
module_name = '{}.{}'.format(pkgname, command)
klass_name = command
try:
@ -836,7 +915,8 @@ Common commands: (see '--help-commands' for more)
except AttributeError:
raise DistutilsModuleError(
"invalid command '%s' (no class '%s' in module '%s')"
% (command, klass_name, module_name))
% (command, klass_name, module_name),
)
self.cmdclass[command] = klass
return klass
@ -852,8 +932,10 @@ Common commands: (see '--help-commands' for more)
cmd_obj = self.command_obj.get(command)
if not cmd_obj and create:
if DEBUG:
self.announce("Distribution.get_command_obj(): "
"creating '%s' command object" % command)
self.announce(
'Distribution.get_command_obj(): '
"creating '%s' command object" % command,
)
klass = self.get_command_class(command)
cmd_obj = self.command_obj[command] = klass(self)
@ -887,11 +969,17 @@ Common commands: (see '--help-commands' for more)
self.announce(" setting options for '%s' command:" % command_name)
for (option, (source, value)) in option_dict.items():
if DEBUG:
self.announce(" %s = %s (from %s)" % (option, value,
source))
self.announce(
' {} = {} (from {})'.format(
option, value,
source,
),
)
try:
bool_opts = [translate_longopt(o)
for o in command_obj.boolean_options]
bool_opts = [
translate_longopt(o)
for o in command_obj.boolean_options
]
except AttributeError:
bool_opts = []
try:
@ -910,7 +998,8 @@ Common commands: (see '--help-commands' for more)
else:
raise DistutilsOptionError(
"error in %s: command '%s' has no such option '%s'"
% (source, command_name, option))
% (source, command_name, option),
)
except ValueError as msg:
raise DistutilsOptionError(msg)
@ -980,7 +1069,7 @@ Common commands: (see '--help-commands' for more)
if self.have_run.get(command):
return
log.info("running %s", command)
log.info('running %s', command)
cmd_obj = self.get_command_obj(command)
cmd_obj.ensure_finalized()
cmd_obj.run()
@ -1010,9 +1099,11 @@ Common commands: (see '--help-commands' for more)
return self.data_files and len(self.data_files) > 0
def is_pure(self):
return (self.has_pure_modules() and
not self.has_ext_modules() and
not self.has_c_libraries())
return (
self.has_pure_modules() and
not self.has_ext_modules() and
not self.has_c_libraries()
)
# -- Metadata query methods ----------------------------------------
@ -1021,19 +1112,21 @@ Common commands: (see '--help-commands' for more)
# to self.metadata.get_XXX. The actual code is in the
# DistributionMetadata class, below.
class DistributionMetadata:
"""Dummy class to hold the distribution meta-data: name, version,
author, and so forth.
"""
_METHOD_BASENAMES = ("name", "version", "author", "author_email",
"maintainer", "maintainer_email", "url",
"license", "description", "long_description",
"keywords", "platforms", "fullname", "contact",
"contact_email", "classifiers", "download_url",
# PEP 314
"provides", "requires", "obsoletes",
)
_METHOD_BASENAMES = (
'name', 'version', 'author', 'author_email',
'maintainer', 'maintainer_email', 'url',
'license', 'description', 'long_description',
'keywords', 'platforms', 'fullname', 'contact',
'contact_email', 'classifiers', 'download_url',
# PEP 314
'provides', 'requires', 'obsoletes',
)
def __init__(self, path=None):
if path is not None:
@ -1113,16 +1206,20 @@ class DistributionMetadata:
def write_pkg_info(self, base_dir):
"""Write the PKG-INFO file into the release tree.
"""
with open(os.path.join(base_dir, 'PKG-INFO'), 'w',
encoding='UTF-8') as pkg_info:
with open(
os.path.join(base_dir, 'PKG-INFO'), 'w',
encoding='UTF-8',
) as pkg_info:
self.write_pkg_file(pkg_info)
def write_pkg_file(self, file):
"""Write the PKG-INFO format data to a file object.
"""
version = '1.0'
if (self.provides or self.requires or self.obsoletes or
self.classifiers or self.download_url):
if (
self.provides or self.requires or self.obsoletes or
self.classifiers or self.download_url
):
version = '1.1'
file.write('Metadata-Version: %s\n' % version)
@ -1153,49 +1250,49 @@ class DistributionMetadata:
def _write_list(self, file, name, values):
for value in values:
file.write('%s: %s\n' % (name, value))
file.write('{}: {}\n'.format(name, value))
# -- Metadata query methods ----------------------------------------
def get_name(self):
return self.name or "UNKNOWN"
return self.name or 'UNKNOWN'
def get_version(self):
return self.version or "0.0.0"
return self.version or '0.0.0'
def get_fullname(self):
return "%s-%s" % (self.get_name(), self.get_version())
return '{}-{}'.format(self.get_name(), self.get_version())
def get_author(self):
return self.author or "UNKNOWN"
return self.author or 'UNKNOWN'
def get_author_email(self):
return self.author_email or "UNKNOWN"
return self.author_email or 'UNKNOWN'
def get_maintainer(self):
return self.maintainer or "UNKNOWN"
return self.maintainer or 'UNKNOWN'
def get_maintainer_email(self):
return self.maintainer_email or "UNKNOWN"
return self.maintainer_email or 'UNKNOWN'
def get_contact(self):
return self.maintainer or self.author or "UNKNOWN"
return self.maintainer or self.author or 'UNKNOWN'
def get_contact_email(self):
return self.maintainer_email or self.author_email or "UNKNOWN"
return self.maintainer_email or self.author_email or 'UNKNOWN'
def get_url(self):
return self.url or "UNKNOWN"
return self.url or 'UNKNOWN'
def get_license(self):
return self.license or "UNKNOWN"
return self.license or 'UNKNOWN'
get_licence = get_license
def get_description(self):
return self.description or "UNKNOWN"
return self.description or 'UNKNOWN'
def get_long_description(self):
return self.long_description or "UNKNOWN"
return self.long_description or 'UNKNOWN'
def get_keywords(self):
return self.keywords or []
@ -1204,7 +1301,7 @@ class DistributionMetadata:
self.keywords = _ensure_list(value, 'keywords')
def get_platforms(self):
return self.platforms or ["UNKNOWN"]
return self.platforms or ['UNKNOWN']
def set_platforms(self, value):
self.platforms = _ensure_list(value, 'platforms')
@ -1216,7 +1313,7 @@ class DistributionMetadata:
self.classifiers = _ensure_list(value, 'classifiers')
def get_download_url(self):
return self.download_url or "UNKNOWN"
return self.download_url or 'UNKNOWN'
# PEP 314
def get_requires(self):
@ -1247,6 +1344,7 @@ class DistributionMetadata:
distutils.versionpredicate.VersionPredicate(v)
self.obsoletes = list(value)
def fix_help_options(options):
"""Convert a 4-tuple 'help_options' list as found in various command
classes to the 3-tuple form required by FancyGetopt.

View file

@ -7,16 +7,20 @@ usually raised for errors that are obviously the end-user's fault
This module is safe to use in "from ... import *" mode; it only exports
symbols whose names start with "Distutils" and end with "Error"."""
from __future__ import annotations
class DistutilsError (Exception):
"""The root of all Distutils evil."""
pass
class DistutilsModuleError (DistutilsError):
"""Unable to load an expected module, or to find an expected class
within some module (in particular, command modules and classes)."""
pass
class DistutilsClassError (DistutilsError):
"""Some command class (or possibly distribution class, if anyone
feels a need to subclass Distribution) is found not to be holding
@ -24,21 +28,25 @@ class DistutilsClassError (DistutilsError):
"command "interface."""
pass
class DistutilsGetoptError (DistutilsError):
"""The option table provided to 'fancy_getopt()' is bogus."""
pass
class DistutilsArgError (DistutilsError):
"""Raised by fancy_getopt in response to getopt.error -- ie. an
error in the command line usage."""
pass
class DistutilsFileError (DistutilsError):
"""Any problems in the filesystem: expected file not found, etc.
Typically this is for problems that we detect before OSError
could be raised."""
pass
class DistutilsOptionError (DistutilsError):
"""Syntactic/semantic errors in command options, such as use of
mutually conflicting options, or inconsistent options,
@ -48,50 +56,63 @@ class DistutilsOptionError (DistutilsError):
the setup script, we'll raise DistutilsSetupError instead."""
pass
class DistutilsSetupError (DistutilsError):
"""For errors that can be definitely blamed on the setup script,
such as invalid keyword arguments to 'setup()'."""
pass
class DistutilsPlatformError (DistutilsError):
"""We don't know how to do something on the current platform (but
we do know how to do it on some platform) -- eg. trying to compile
C files on a platform not supported by a CCompiler subclass."""
pass
class DistutilsExecError (DistutilsError):
"""Any problems executing an external program (such as the C
compiler, when compiling C files)."""
pass
class DistutilsInternalError (DistutilsError):
"""Internal inconsistencies or impossibilities (obviously, this
should never be seen if the code is working!)."""
pass
class DistutilsTemplateError (DistutilsError):
"""Syntax error in a file list template."""
class DistutilsByteCompileError(DistutilsError):
"""Byte compile error."""
# Exception classes used by the CCompiler implementation classes
class CCompilerError (Exception):
"""Some compile/link operation failed."""
class PreprocessError (CCompilerError):
"""Failure to preprocess one or more C/C++ files."""
class CompileError (CCompilerError):
"""Failure to compile one or more C/C++ source files."""
class LibError (CCompilerError):
"""Failure to create a static library from one or more C/C++ object
files."""
class LinkError (CCompilerError):
"""Failure to link one or more C/C++ object files into an executable
or shared library file."""
class UnknownFileError (CCompilerError):
"""Attempt to process an unknown file type."""

View file

@ -2,6 +2,7 @@
Provides the Extension class, used to describe C/C++ extension
modules in setup scripts."""
from __future__ import annotations
import os
import warnings
@ -16,6 +17,7 @@ import warnings
# import that large-ish module (indirectly, through distutils.core) in
# order to do anything.
class Extension:
"""Just a collection of attributes that describes an extension
module and everything needed to build it (hopefully in a portable
@ -83,27 +85,30 @@ class Extension:
# When adding arguments to this constructor, be sure to update
# setup_keywords in core.py.
def __init__(self, name, sources,
include_dirs=None,
define_macros=None,
undef_macros=None,
library_dirs=None,
libraries=None,
runtime_library_dirs=None,
extra_objects=None,
extra_compile_args=None,
extra_link_args=None,
export_symbols=None,
swig_opts = None,
depends=None,
language=None,
optional=None,
**kw # To catch unknown keywords
):
def __init__(
self, name, sources,
include_dirs=None,
define_macros=None,
undef_macros=None,
library_dirs=None,
libraries=None,
runtime_library_dirs=None,
extra_objects=None,
extra_compile_args=None,
extra_link_args=None,
export_symbols=None,
swig_opts=None,
depends=None,
language=None,
optional=None,
**kw, # To catch unknown keywords
):
if not isinstance(name, str):
raise AssertionError("'name' must be a string")
if not (isinstance(sources, list) and
all(isinstance(v, str) for v in sources)):
if not (
isinstance(sources, list) and
all(isinstance(v, str) for v in sources)
):
raise AssertionError("'sources' must be a list of strings")
self.name = name
@ -127,21 +132,24 @@ class Extension:
if len(kw) > 0:
options = [repr(option) for option in kw]
options = ', '.join(sorted(options))
msg = "Unknown Extension options: %s" % options
msg = 'Unknown Extension options: %s' % options
warnings.warn(msg)
def __repr__(self):
return '<%s.%s(%r) at %#x>' % (
return '<{}.{}({!r}) at {:#x}>'.format(
self.__class__.__module__,
self.__class__.__qualname__,
self.name,
id(self))
id(self),
)
def read_setup_file(filename):
"""Reads a Setup file and returns Extension instances."""
from distutils.sysconfig import (parse_makefile, expand_makefile_vars,
_variable_rx)
from distutils.sysconfig import (
parse_makefile, expand_makefile_vars,
_variable_rx,
)
from distutils.text_file import TextFile
from distutils.util import split_quoted
@ -151,9 +159,11 @@ def read_setup_file(filename):
# Second pass to gobble up the real content: lines of the form
# <module> ... [<sourcefile> ...] [<cpparg> ...] [<library> ...]
file = TextFile(filename,
strip_comments=1, skip_blanks=1, join_lines=1,
lstrip_ws=1, rstrip_ws=1)
file = TextFile(
filename,
strip_comments=1, skip_blanks=1, join_lines=1,
lstrip_ws=1, rstrip_ws=1,
)
try:
extensions = []
@ -164,7 +174,7 @@ def read_setup_file(filename):
if _variable_rx.match(line): # VAR=VALUE, handled in first pass
continue
if line[0] == line[-1] == "*":
if line[0] == line[-1] == '*':
file.warn("'%s' lines not handled yet" % line)
continue
@ -188,43 +198,46 @@ def read_setup_file(filename):
continue
suffix = os.path.splitext(word)[1]
switch = word[0:2] ; value = word[2:]
switch = word[0:2]
value = word[2:]
if suffix in (".c", ".cc", ".cpp", ".cxx", ".c++", ".m", ".mm"):
if suffix in ('.c', '.cc', '.cpp', '.cxx', '.c++', '.m', '.mm'):
# hmm, should we do something about C vs. C++ sources?
# or leave it up to the CCompiler implementation to
# worry about?
ext.sources.append(word)
elif switch == "-I":
elif switch == '-I':
ext.include_dirs.append(value)
elif switch == "-D":
equals = value.find("=")
elif switch == '-D':
equals = value.find('=')
if equals == -1: # bare "-DFOO" -- no value
ext.define_macros.append((value, None))
else: # "-DFOO=blah"
ext.define_macros.append((value[0:equals],
value[equals+2:]))
elif switch == "-U":
ext.define_macros.append((
value[0:equals],
value[equals + 2:],
))
elif switch == '-U':
ext.undef_macros.append(value)
elif switch == "-C": # only here 'cause makesetup has it!
elif switch == '-C': # only here 'cause makesetup has it!
ext.extra_compile_args.append(word)
elif switch == "-l":
elif switch == '-l':
ext.libraries.append(value)
elif switch == "-L":
elif switch == '-L':
ext.library_dirs.append(value)
elif switch == "-R":
elif switch == '-R':
ext.runtime_library_dirs.append(value)
elif word == "-rpath":
elif word == '-rpath':
append_next_word = ext.runtime_library_dirs
elif word == "-Xlinker":
elif word == '-Xlinker':
append_next_word = ext.extra_link_args
elif word == "-Xcompiler":
elif word == '-Xcompiler':
append_next_word = ext.extra_compile_args
elif switch == "-u":
elif switch == '-u':
ext.extra_link_args.append(word)
if not value:
append_next_word = ext.extra_link_args
elif suffix in (".a", ".so", ".sl", ".o", ".dylib"):
elif suffix in ('.a', '.so', '.sl', '.o', '.dylib'):
# NB. a really faithful emulation of makesetup would
# append a .o file to extra_objects only if it
# had a slash in it; otherwise, it would s/.o/.c/

View file

@ -7,9 +7,13 @@ additional features:
create a complete usage summary
* options set attributes of a passed-in object
"""
from __future__ import annotations
import sys, string, re
import getopt
import re
import string
import sys
from distutils.errors import *
# Much like command_re in distutils.core, this is close to but not quite
@ -20,12 +24,13 @@ longopt_pat = r'[a-zA-Z](?:[a-zA-Z0-9-]*)'
longopt_re = re.compile(r'^%s$' % longopt_pat)
# For recognizing "negative alias" options, eg. "quiet=!verbose"
neg_alias_re = re.compile("^(%s)=!(%s)$" % (longopt_pat, longopt_pat))
neg_alias_re = re.compile('^({})=!({})$'.format(longopt_pat, longopt_pat))
# This is used to translate long options to legitimate Python identifiers
# (for use as attributes of some object).
longopt_xlate = str.maketrans('-', '_')
class FancyGetopt:
"""Wrapper around the standard 'getopt()' module that provides some
handy extra functionality:
@ -90,7 +95,8 @@ class FancyGetopt:
def add_option(self, long_option, short_option=None, help_string=None):
if long_option in self.option_index:
raise DistutilsGetoptError(
"option conflict: already an option '%s'" % long_option)
"option conflict: already an option '%s'" % long_option,
)
else:
option = (long_option, short_option, help_string)
self.option_table.append(option)
@ -111,15 +117,19 @@ class FancyGetopt:
assert isinstance(aliases, dict)
for (alias, opt) in aliases.items():
if alias not in self.option_index:
raise DistutilsGetoptError(("invalid %s '%s': "
"option '%s' not defined") % (what, alias, alias))
raise DistutilsGetoptError((
"invalid %s '%s': "
"option '%s' not defined"
) % (what, alias, alias))
if opt not in self.option_index:
raise DistutilsGetoptError(("invalid %s '%s': "
"aliased option '%s' not defined") % (what, alias, opt))
raise DistutilsGetoptError((
"invalid %s '%s': "
"aliased option '%s' not defined"
) % (what, alias, opt))
def set_aliases(self, alias):
"""Set the aliases for this option parser."""
self._check_alias_dict(alias, "alias")
self._check_alias_dict(alias, 'alias')
self.alias = alias
def set_negative_aliases(self, negative_alias):
@ -127,7 +137,7 @@ class FancyGetopt:
'negative_alias' should be a dictionary mapping option names to
option names, both the key and value must already be defined
in the option table."""
self._check_alias_dict(negative_alias, "negative alias")
self._check_alias_dict(negative_alias, 'negative alias')
self.negative_alias = negative_alias
def _grok_option_table(self):
@ -149,23 +159,32 @@ class FancyGetopt:
else:
# the option table is part of the code, so simply
# assert that it is correct
raise ValueError("invalid option tuple: %r" % (option,))
raise ValueError('invalid option tuple: {!r}'.format(option))
# Type- and value-check the option names
if not isinstance(long, str) or len(long) < 2:
raise DistutilsGetoptError(("invalid long option '%s': "
"must be a string of length >= 2") % long)
raise DistutilsGetoptError(
(
"invalid long option '%s': "
'must be a string of length >= 2'
) % long,
)
if (not ((short is None) or
(isinstance(short, str) and len(short) == 1))):
raise DistutilsGetoptError("invalid short option '%s': "
"must a single character or None" % short)
if (
not ((short is None) or
(isinstance(short, str) and len(short) == 1))
):
raise DistutilsGetoptError(
"invalid short option '%s': "
'must a single character or None' % short,
)
self.repeat[long] = repeat
self.long_opts.append(long)
if long[-1] == '=': # option takes an argument?
if short: short = short + ':'
if short:
short = short + ':'
long = long[0:-1]
self.takes_arg[long] = 1
else:
@ -175,11 +194,12 @@ class FancyGetopt:
if alias_to is not None:
if self.takes_arg[alias_to]:
raise DistutilsGetoptError(
"invalid negative alias '%s': "
"aliased option '%s' takes a value"
% (long, alias_to))
"invalid negative alias '%s': "
"aliased option '%s' takes a value"
% (long, alias_to),
)
self.long_opts[-1] = long # XXX redundant?!
self.long_opts[-1] = long # XXX redundant?!
self.takes_arg[long] = 0
# If this is an alias option, make sure its "takes arg" flag is
@ -188,10 +208,11 @@ class FancyGetopt:
if alias_to is not None:
if self.takes_arg[long] != self.takes_arg[alias_to]:
raise DistutilsGetoptError(
"invalid alias '%s': inconsistent with "
"aliased option '%s' (one of them takes a value, "
"the other doesn't"
% (long, alias_to))
"invalid alias '%s': inconsistent with "
"aliased option '%s' (one of them takes a value, "
"the other doesn't"
% (long, alias_to),
)
# Now enforce some bondage on the long option name, so we can
# later translate it to an attribute name on some object. Have
@ -199,8 +220,9 @@ class FancyGetopt:
# '='.
if not longopt_re.match(long):
raise DistutilsGetoptError(
"invalid long option name '%s' "
"(must be letters, numbers, hyphens only" % long)
"invalid long option name '%s' "
'(must be letters, numbers, hyphens only' % long,
)
self.attr_name[long] = self.get_attr_name(long)
if short:
@ -235,7 +257,7 @@ class FancyGetopt:
raise DistutilsArgError(msg)
for opt, val in opts:
if len(opt) == 2 and opt[0] == '-': # it's a short option
if len(opt) == 2 and opt[0] == '-': # it's a short option
opt = self.short2long[opt[1]]
else:
assert len(opt) > 2 and opt[:2] == '--'
@ -339,19 +361,21 @@ class FancyGetopt:
# Case 1: no short option at all (makes life easy)
if short is None:
if text:
lines.append(" --%-*s %s" % (max_opt, long, text[0]))
lines.append(' --%-*s %s' % (max_opt, long, text[0]))
else:
lines.append(" --%-*s " % (max_opt, long))
lines.append(' --%-*s ' % (max_opt, long))
# Case 2: we have a short option, so we have to include it
# just after the long option
else:
opt_names = "%s (-%s)" % (long, short)
opt_names = '{} (-{})'.format(long, short)
if text:
lines.append(" --%-*s %s" %
(max_opt, opt_names, text[0]))
lines.append(
' --%-*s %s' %
(max_opt, opt_names, text[0]),
)
else:
lines.append(" --%-*s" % opt_names)
lines.append(' --%-*s' % opt_names)
for l in text[1:]:
lines.append(big_indent + l)
@ -361,7 +385,7 @@ class FancyGetopt:
if file is None:
file = sys.stdout
for line in self.generate_help(header):
file.write(line + "\n")
file.write(line + '\n')
def fancy_getopt(options, negative_opt, object, args):
@ -370,7 +394,8 @@ def fancy_getopt(options, negative_opt, object, args):
return parser.getopt(args, object)
WS_TRANS = {ord(_wschar) : ' ' for _wschar in string.whitespace}
WS_TRANS = {ord(_wschar): ' ' for _wschar in string.whitespace}
def wrap_text(text, width):
"""wrap_text(text : string, width : int) -> [string]
@ -386,7 +411,7 @@ def wrap_text(text, width):
text = text.expandtabs()
text = text.translate(WS_TRANS)
chunks = re.split(r'( +|-+)', text)
chunks = [ch for ch in chunks if ch] # ' - ' results in empty strings
chunks = [ch for ch in chunks if ch] # ' - ' results in empty strings
lines = []
while chunks:
@ -444,7 +469,7 @@ class OptionDummy:
setattr(self, opt, None)
if __name__ == "__main__":
if __name__ == '__main__':
text = """\
Tra-la-la, supercalifragilisticexpialidocious.
How *do* you spell that odd word, anyways?
@ -452,6 +477,6 @@ How *do* you spell that odd word, anyways?
say, "How should I know?"].)"""
for w in (10, 20, 30, 40):
print("width: %d" % w)
print("\n".join(wrap_text(text, w)))
print('width: %d' % w)
print('\n'.join(wrap_text(text, w)))
print()

View file

@ -2,18 +2,20 @@
Utility functions for operating on single files.
"""
from __future__ import annotations
import os
from distutils.errors import DistutilsFileError
from distutils import log
from distutils.errors import DistutilsFileError
# for generating verbose output in 'copy_file()'
_copy_action = { None: 'copying',
'hard': 'hard linking',
'sym': 'symbolically linking' }
_copy_action = {None: 'copying',
'hard': 'hard linking',
'sym': 'symbolically linking', }
def _copy_file_contents(src, dst, buffer_size=16*1024):
def _copy_file_contents(src, dst, buffer_size=16 * 1024):
"""Copy the file 'src' to 'dst'; both must be filenames. Any error
opening either file, reading from 'src', or writing to 'dst', raises
DistutilsFileError. Data is read/written in chunks of 'buffer_size'
@ -28,27 +30,30 @@ def _copy_file_contents(src, dst, buffer_size=16*1024):
try:
fsrc = open(src, 'rb')
except OSError as e:
raise DistutilsFileError("could not open '%s': %s" % (src, e.strerror))
raise DistutilsFileError("could not open '{}': {}".format(src, e.strerror))
if os.path.exists(dst):
try:
os.unlink(dst)
except OSError as e:
raise DistutilsFileError(
"could not delete '%s': %s" % (dst, e.strerror))
"could not delete '{}': {}".format(dst, e.strerror),
)
try:
fdst = open(dst, 'wb')
except OSError as e:
raise DistutilsFileError(
"could not create '%s': %s" % (dst, e.strerror))
"could not create '{}': {}".format(dst, e.strerror),
)
while True:
try:
buf = fsrc.read(buffer_size)
except OSError as e:
raise DistutilsFileError(
"could not read from '%s': %s" % (src, e.strerror))
"could not read from '{}': {}".format(src, e.strerror),
)
if not buf:
break
@ -57,15 +62,19 @@ def _copy_file_contents(src, dst, buffer_size=16*1024):
fdst.write(buf)
except OSError as e:
raise DistutilsFileError(
"could not write to '%s': %s" % (dst, e.strerror))
"could not write to '{}': {}".format(dst, e.strerror),
)
finally:
if fdst:
fdst.close()
if fsrc:
fsrc.close()
def copy_file(src, dst, preserve_mode=1, preserve_times=1, update=0,
link=None, verbose=1, dry_run=0):
def copy_file(
src, dst, preserve_mode=1, preserve_times=1, update=0,
link=None, verbose=1, dry_run=0,
):
"""Copy a file 'src' to 'dst'. If 'dst' is a directory, then 'src' is
copied there with the same name; otherwise, it must be a filename. (If
the file exists, it will be ruthlessly clobbered.) If 'preserve_mode'
@ -102,7 +111,8 @@ def copy_file(src, dst, preserve_mode=1, preserve_times=1, update=0,
if not os.path.isfile(src):
raise DistutilsFileError(
"can't copy '%s': doesn't exist or not a regular file" % src)
"can't copy '%s': doesn't exist or not a regular file" % src,
)
if os.path.isdir(dst):
dir = dst
@ -112,7 +122,7 @@ def copy_file(src, dst, preserve_mode=1, preserve_times=1, update=0,
if update and not newer(src, dst):
if verbose >= 1:
log.debug("not copying %s (output up-to-date)", src)
log.debug('not copying %s (output up-to-date)', src)
return (dst, 0)
try:
@ -122,9 +132,9 @@ def copy_file(src, dst, preserve_mode=1, preserve_times=1, update=0,
if verbose >= 1:
if os.path.basename(dst) == os.path.basename(src):
log.info("%s %s -> %s", action, src, dir)
log.info('%s %s -> %s', action, src, dir)
else:
log.info("%s %s -> %s", action, src, dst)
log.info('%s %s -> %s', action, src, dst)
if dry_run:
return (dst, 1)
@ -163,10 +173,11 @@ def copy_file(src, dst, preserve_mode=1, preserve_times=1, update=0,
# XXX I suspect this is Unix-specific -- need porting help!
def move_file (src, dst,
verbose=1,
dry_run=0):
def move_file(
src, dst,
verbose=1,
dry_run=0,
):
"""Move a file 'src' to 'dst'. If 'dst' is a directory, the file will
be moved into it with the same name; otherwise, 'src' is just renamed
to 'dst'. Return the new full name of the file.
@ -178,7 +189,7 @@ def move_file (src, dst,
import errno
if verbose >= 1:
log.info("moving %s -> %s", src, dst)
log.info('moving %s -> %s', src, dst)
if dry_run:
return dst
@ -190,13 +201,15 @@ def move_file (src, dst,
dst = os.path.join(dst, basename(src))
elif exists(dst):
raise DistutilsFileError(
"can't move '%s': destination '%s' already exists" %
(src, dst))
"can't move '%s': destination '%s' already exists" %
(src, dst),
)
if not isdir(dirname(dst)):
raise DistutilsFileError(
"can't move '%s': destination '%s' not a valid path" %
(src, dst))
"can't move '%s': destination '%s' not a valid path" %
(src, dst),
)
copy_it = False
try:
@ -207,7 +220,8 @@ def move_file (src, dst,
copy_it = True
else:
raise DistutilsFileError(
"couldn't move '%s' to '%s': %s" % (src, dst, msg))
"couldn't move '{}' to '{}': {}".format(src, dst, msg),
)
if copy_it:
copy_file(src, dst, verbose=verbose)
@ -220,19 +234,20 @@ def move_file (src, dst,
except OSError:
pass
raise DistutilsFileError(
"couldn't move '%s' to '%s' by copy/delete: "
"delete '%s' failed: %s"
% (src, dst, src, msg))
"couldn't move '%s' to '%s' by copy/delete: "
"delete '%s' failed: %s"
% (src, dst, src, msg),
)
return dst
def write_file (filename, contents):
def write_file(filename, contents):
"""Create a file with the specified name and write 'contents' (a
sequence of strings without line terminators) to it.
"""
f = open(filename, "w")
f = open(filename, 'w')
try:
for line in contents:
f.write(line + "\n")
f.write(line + '\n')
finally:
f.close()

View file

@ -3,15 +3,17 @@
Provides the FileList class, used for poking about the filesystem
and building lists of files.
"""
from __future__ import annotations
import os
import re
import fnmatch
import functools
import os
import re
from distutils.util import convert_path
from distutils.errors import DistutilsTemplateError, DistutilsInternalError
from distutils import log
from distutils.errors import DistutilsInternalError
from distutils.errors import DistutilsTemplateError
from distutils.util import convert_path
class FileList:
@ -80,22 +82,27 @@ class FileList:
patterns = dir = dir_pattern = None
if action in ('include', 'exclude',
'global-include', 'global-exclude'):
if action in (
'include', 'exclude',
'global-include', 'global-exclude',
):
if len(words) < 2:
raise DistutilsTemplateError(
"'%s' expects <pattern1> <pattern2> ..." % action)
"'%s' expects <pattern1> <pattern2> ..." % action,
)
patterns = [convert_path(w) for w in words[1:]]
elif action in ('recursive-include', 'recursive-exclude'):
if len(words) < 3:
raise DistutilsTemplateError(
"'%s' expects <dir> <pattern1> <pattern2> ..." % action)
"'%s' expects <dir> <pattern1> <pattern2> ..." % action,
)
dir = convert_path(words[1])
patterns = [convert_path(w) for w in words[2:]]
elif action in ('graft', 'prune'):
if len(words) != 2:
raise DistutilsTemplateError(
"'%s' expects a single <dir_pattern>" % action)
"'%s' expects a single <dir_pattern>" % action,
)
dir_pattern = convert_path(words[1])
else:
raise DistutilsTemplateError("unknown action '%s'" % action)
@ -114,37 +121,53 @@ class FileList:
# right number of words on the line for that action -- so we
# can proceed with minimal error-checking.
if action == 'include':
self.debug_print("include " + ' '.join(patterns))
self.debug_print('include ' + ' '.join(patterns))
for pattern in patterns:
if not self.include_pattern(pattern, anchor=1):
log.warn("warning: no files found matching '%s'",
pattern)
log.warn(
"warning: no files found matching '%s'",
pattern,
)
elif action == 'exclude':
self.debug_print("exclude " + ' '.join(patterns))
self.debug_print('exclude ' + ' '.join(patterns))
for pattern in patterns:
if not self.exclude_pattern(pattern, anchor=1):
log.warn(("warning: no previously-included files "
"found matching '%s'"), pattern)
log.warn(
(
'warning: no previously-included files '
"found matching '%s'"
), pattern,
)
elif action == 'global-include':
self.debug_print("global-include " + ' '.join(patterns))
self.debug_print('global-include ' + ' '.join(patterns))
for pattern in patterns:
if not self.include_pattern(pattern, anchor=0):
log.warn(("warning: no files found matching '%s' "
"anywhere in distribution"), pattern)
log.warn(
(
"warning: no files found matching '%s' "
'anywhere in distribution'
), pattern,
)
elif action == 'global-exclude':
self.debug_print("global-exclude " + ' '.join(patterns))
self.debug_print('global-exclude ' + ' '.join(patterns))
for pattern in patterns:
if not self.exclude_pattern(pattern, anchor=0):
log.warn(("warning: no previously-included files matching "
"'%s' found anywhere in distribution"),
pattern)
log.warn(
(
'warning: no previously-included files matching '
"'%s' found anywhere in distribution"
),
pattern,
)
elif action == 'recursive-include':
self.debug_print("recursive-include %s %s" %
(dir, ' '.join(patterns)))
self.debug_print(
'recursive-include %s %s' %
(dir, ' '.join(patterns)),
)
for pattern in patterns:
if not self.include_pattern(pattern, prefix=dir):
msg = (
@ -154,28 +177,41 @@ class FileList:
log.warn(msg, pattern, dir)
elif action == 'recursive-exclude':
self.debug_print("recursive-exclude %s %s" %
(dir, ' '.join(patterns)))
self.debug_print(
'recursive-exclude %s %s' %
(dir, ' '.join(patterns)),
)
for pattern in patterns:
if not self.exclude_pattern(pattern, prefix=dir):
log.warn(("warning: no previously-included files matching "
"'%s' found under directory '%s'"),
pattern, dir)
log.warn(
(
'warning: no previously-included files matching '
"'%s' found under directory '%s'"
),
pattern, dir,
)
elif action == 'graft':
self.debug_print("graft " + dir_pattern)
self.debug_print('graft ' + dir_pattern)
if not self.include_pattern(None, prefix=dir_pattern):
log.warn("warning: no directories found matching '%s'",
dir_pattern)
log.warn(
"warning: no directories found matching '%s'",
dir_pattern,
)
elif action == 'prune':
self.debug_print("prune " + dir_pattern)
self.debug_print('prune ' + dir_pattern)
if not self.exclude_pattern(None, prefix=dir_pattern):
log.warn(("no previously-included directories found "
"matching '%s'"), dir_pattern)
log.warn(
(
'no previously-included directories found '
"matching '%s'"
), dir_pattern,
)
else:
raise DistutilsInternalError(
"this cannot happen: invalid action '%s'" % action)
"this cannot happen: invalid action '%s'" % action,
)
# Filtering/selection methods
@ -207,8 +243,10 @@ class FileList:
# XXX docstring lying about what the special chars are?
files_found = False
pattern_re = translate_pattern(pattern, anchor, prefix, is_regex)
self.debug_print("include_pattern: applying regex r'%s'" %
pattern_re.pattern)
self.debug_print(
"include_pattern: applying regex r'%s'" %
pattern_re.pattern,
)
# delayed loading of allfiles list
if self.allfiles is None:
@ -216,13 +254,14 @@ class FileList:
for name in self.allfiles:
if pattern_re.search(name):
self.debug_print(" adding " + name)
self.debug_print(' adding ' + name)
self.files.append(name)
files_found = True
return files_found
def exclude_pattern(
self, pattern, anchor=1, prefix=None, is_regex=0):
self, pattern, anchor=1, prefix=None, is_regex=0,
):
"""Remove strings (presumably filenames) from 'files' that match
'pattern'. Other parameters are the same as for
'include_pattern()', above.
@ -231,11 +270,13 @@ class FileList:
"""
files_found = False
pattern_re = translate_pattern(pattern, anchor, prefix, is_regex)
self.debug_print("exclude_pattern: applying regex r'%s'" %
pattern_re.pattern)
for i in range(len(self.files)-1, -1, -1):
self.debug_print(
"exclude_pattern: applying regex r'%s'" %
pattern_re.pattern,
)
for i in range(len(self.files) - 1, -1, -1):
if pattern_re.search(self.files[i]):
self.debug_print(" removing " + self.files[i])
self.debug_print(' removing ' + self.files[i])
del self.files[i]
files_found = True
return files_found
@ -262,6 +303,7 @@ class _UniqueDirs(set):
avoiding infinite recursion.
Ref https://bugs.python.org/issue44497.
"""
def __call__(self, walk_item):
"""
Given an item from an os.walk result, determine
@ -346,10 +388,11 @@ def translate_pattern(pattern, anchor=1, prefix=None, is_regex=0):
if os.sep == '\\':
sep = r'\\'
pattern_re = pattern_re[len(start): len(pattern_re) - len(end)]
pattern_re = r'%s\A%s%s.*%s%s' % (
start, prefix_re, sep, pattern_re, end)
pattern_re = r'{}\A{}{}.*{}{}'.format(
start, prefix_re, sep, pattern_re, end,
)
else: # no prefix -- respect anchor flag
if anchor:
pattern_re = r'%s\A%s' % (start, pattern_re[len(start):])
pattern_re = r'{}\A{}'.format(start, pattern_re[len(start):])
return re.compile(pattern_re)

View file

@ -1,7 +1,8 @@
"""A simple log mechanism styled after PEP 282."""
# The class here is styled after PEP 282 so that it could later be
# replaced with a standard Python logging implementation.
from __future__ import annotations
import sys
DEBUG = 1
INFO = 2
@ -9,7 +10,6 @@ WARN = 3
ERROR = 4
FATAL = 5
import sys
class Log:
@ -32,7 +32,7 @@ class Log:
except UnicodeEncodeError:
# emulate backslashreplace error handler
encoding = stream.encoding
msg = msg.encode(encoding, "backslashreplace").decode(encoding)
msg = msg.encode(encoding, 'backslashreplace').decode(encoding)
stream.write('%s\n' % msg)
stream.flush()
@ -54,6 +54,7 @@ class Log:
def fatal(self, msg, *args):
self._log(FATAL, msg, args)
_global_log = Log()
log = _global_log.log
debug = _global_log.debug
@ -62,12 +63,14 @@ warn = _global_log.warn
error = _global_log.error
fatal = _global_log.fatal
def set_threshold(level):
# return the old threshold for use from tests
old = _global_log.threshold
_global_log.threshold = level
return old
def set_verbosity(v):
if v <= 0:
set_threshold(WARN)

View file

@ -6,56 +6,62 @@ for the Microsoft Visual Studio 2008.
The module is compatible with VS 2005 and VS 2008. You can find legacy support
for older versions of VS in distutils.msvccompiler.
"""
# Written by Perry Stoll
# hacked by Robin Becker and Thomas Heller to do a better job of
# finding DevStudio (through the registry)
# ported to VS2005 and VS 2008 by Christian Heimes
from __future__ import annotations
import os
import re
import subprocess
import sys
import re
from distutils.errors import DistutilsExecError, DistutilsPlatformError, \
CompileError, LibError, LinkError
from distutils.ccompiler import CCompiler, gen_lib_options
from distutils import log
from distutils.util import get_platform
import winreg
from distutils import log
from distutils.ccompiler import CCompiler
from distutils.ccompiler import gen_lib_options
from distutils.errors import CompileError
from distutils.errors import DistutilsExecError
from distutils.errors import DistutilsPlatformError
from distutils.errors import LibError
from distutils.errors import LinkError
from distutils.util import get_platform
RegOpenKeyEx = winreg.OpenKeyEx
RegEnumKey = winreg.EnumKey
RegEnumValue = winreg.EnumValue
RegError = winreg.error
HKEYS = (winreg.HKEY_USERS,
winreg.HKEY_CURRENT_USER,
winreg.HKEY_LOCAL_MACHINE,
winreg.HKEY_CLASSES_ROOT)
HKEYS = (
winreg.HKEY_USERS,
winreg.HKEY_CURRENT_USER,
winreg.HKEY_LOCAL_MACHINE,
winreg.HKEY_CLASSES_ROOT,
)
NATIVE_WIN64 = (sys.platform == 'win32' and sys.maxsize > 2**32)
if NATIVE_WIN64:
# Visual C++ is a 32-bit application, so we need to look in
# the corresponding registry branch, if we're running a
# 64-bit Python on Win64
VS_BASE = r"Software\Wow6432Node\Microsoft\VisualStudio\%0.1f"
WINSDK_BASE = r"Software\Wow6432Node\Microsoft\Microsoft SDKs\Windows"
NET_BASE = r"Software\Wow6432Node\Microsoft\.NETFramework"
VS_BASE = r'Software\Wow6432Node\Microsoft\VisualStudio\%0.1f'
WINSDK_BASE = r'Software\Wow6432Node\Microsoft\Microsoft SDKs\Windows'
NET_BASE = r'Software\Wow6432Node\Microsoft\.NETFramework'
else:
VS_BASE = r"Software\Microsoft\VisualStudio\%0.1f"
WINSDK_BASE = r"Software\Microsoft\Microsoft SDKs\Windows"
NET_BASE = r"Software\Microsoft\.NETFramework"
VS_BASE = r'Software\Microsoft\VisualStudio\%0.1f'
WINSDK_BASE = r'Software\Microsoft\Microsoft SDKs\Windows'
NET_BASE = r'Software\Microsoft\.NETFramework'
# A map keyed by get_platform() return values to values accepted by
# 'vcvarsall.bat'. Note a cross-compile may combine these (eg, 'x86_amd64' is
# the param to cross-compile on x86 targeting amd64.)
PLAT_TO_VCVARS = {
'win32' : 'x86',
'win-amd64' : 'amd64',
'win32': 'x86',
'win-amd64': 'amd64',
}
class Reg:
"""Helper class to read values from the registry
"""
@ -109,15 +115,16 @@ class Reg:
read_values = classmethod(read_values)
def convert_mbcs(s):
dec = getattr(s, "decode", None)
dec = getattr(s, 'decode', None)
if dec is not None:
try:
s = dec("mbcs")
s = dec('mbcs')
except UnicodeError:
pass
return s
convert_mbcs = staticmethod(convert_mbcs)
class MacroExpander:
def __init__(self, version):
@ -126,56 +133,60 @@ class MacroExpander:
self.load_macros(version)
def set_macro(self, macro, path, key):
self.macros["$(%s)" % macro] = Reg.get_value(path, key)
self.macros['$(%s)' % macro] = Reg.get_value(path, key)
def load_macros(self, version):
self.set_macro("VCInstallDir", self.vsbase + r"\Setup\VC", "productdir")
self.set_macro("VSInstallDir", self.vsbase + r"\Setup\VS", "productdir")
self.set_macro("FrameworkDir", NET_BASE, "installroot")
self.set_macro('VCInstallDir', self.vsbase + r'\Setup\VC', 'productdir')
self.set_macro('VSInstallDir', self.vsbase + r'\Setup\VS', 'productdir')
self.set_macro('FrameworkDir', NET_BASE, 'installroot')
try:
if version >= 8.0:
self.set_macro("FrameworkSDKDir", NET_BASE,
"sdkinstallrootv2.0")
self.set_macro(
'FrameworkSDKDir', NET_BASE,
'sdkinstallrootv2.0',
)
else:
raise KeyError("sdkinstallrootv2.0")
raise KeyError('sdkinstallrootv2.0')
except KeyError:
raise DistutilsPlatformError(
"""Python was built with Visual Studio 2008;
"""Python was built with Visual Studio 2008;
extensions must be built with a compiler than can generate compatible binaries.
Visual Studio 2008 was not found on this system. If you have Cygwin installed,
you can try compiling with MingW32, by passing "-c mingw32" to setup.py.""")
you can try compiling with MingW32, by passing "-c mingw32" to setup.py.""",
)
if version >= 9.0:
self.set_macro("FrameworkVersion", self.vsbase, "clr version")
self.set_macro("WindowsSdkDir", WINSDK_BASE, "currentinstallfolder")
self.set_macro('FrameworkVersion', self.vsbase, 'clr version')
self.set_macro('WindowsSdkDir', WINSDK_BASE, 'currentinstallfolder')
else:
p = r"Software\Microsoft\NET Framework Setup\Product"
p = r'Software\Microsoft\NET Framework Setup\Product'
for base in HKEYS:
try:
h = RegOpenKeyEx(base, p)
except RegError:
continue
key = RegEnumKey(h, 0)
d = Reg.get_value(base, r"%s\%s" % (p, key))
self.macros["$(FrameworkVersion)"] = d["version"]
d = Reg.get_value(base, r'{}\{}'.format(p, key))
self.macros['$(FrameworkVersion)'] = d['version']
def sub(self, s):
for k, v in self.macros.items():
s = s.replace(k, v)
return s
def get_build_version():
"""Return the version of MSVC that was used to build Python.
For Python 2.3 and up, the version number is included in
sys.version. For earlier versions, assume the compiler is MSVC 6.
"""
prefix = "MSC v."
prefix = 'MSC v.'
i = sys.version.find(prefix)
if i == -1:
return 6
i = i + len(prefix)
s, rest = sys.version[i:].split(" ", 1)
s, rest = sys.version[i:].split(' ', 1)
majorVersion = int(s[:-2]) - 6
if majorVersion >= 13:
# v13 was skipped and should be v14
@ -189,6 +200,7 @@ def get_build_version():
# else we don't know what version of the compiler this is
return None
def normalize_and_reduce_paths(paths):
"""Return a list of normalized paths with duplicates removed.
@ -203,6 +215,7 @@ def normalize_and_reduce_paths(paths):
reduced_paths.append(np)
return reduced_paths
def removeDuplicates(variable):
"""Remove duplicate values of an environment variable.
"""
@ -214,6 +227,7 @@ def removeDuplicates(variable):
newVariable = os.pathsep.join(newList)
return newVariable
def find_vcvarsall(version):
"""Find the vcvarsall.bat file
@ -222,53 +236,58 @@ def find_vcvarsall(version):
"""
vsbase = VS_BASE % version
try:
productdir = Reg.get_value(r"%s\Setup\VC" % vsbase,
"productdir")
productdir = Reg.get_value(
r'%s\Setup\VC' % vsbase,
'productdir',
)
except KeyError:
log.debug("Unable to find productdir in registry")
log.debug('Unable to find productdir in registry')
productdir = None
if not productdir or not os.path.isdir(productdir):
toolskey = "VS%0.f0COMNTOOLS" % version
toolskey = 'VS%0.f0COMNTOOLS' % version
toolsdir = os.environ.get(toolskey, None)
if toolsdir and os.path.isdir(toolsdir):
productdir = os.path.join(toolsdir, os.pardir, os.pardir, "VC")
productdir = os.path.join(toolsdir, os.pardir, os.pardir, 'VC')
productdir = os.path.abspath(productdir)
if not os.path.isdir(productdir):
log.debug("%s is not a valid directory" % productdir)
log.debug('%s is not a valid directory' % productdir)
return None
else:
log.debug("Env var %s is not set or invalid" % toolskey)
log.debug('Env var %s is not set or invalid' % toolskey)
if not productdir:
log.debug("No productdir found")
log.debug('No productdir found')
return None
vcvarsall = os.path.join(productdir, "vcvarsall.bat")
vcvarsall = os.path.join(productdir, 'vcvarsall.bat')
if os.path.isfile(vcvarsall):
return vcvarsall
log.debug("Unable to find vcvarsall.bat")
log.debug('Unable to find vcvarsall.bat')
return None
def query_vcvarsall(version, arch="x86"):
def query_vcvarsall(version, arch='x86'):
"""Launch vcvarsall.bat and read the settings from its environment
"""
vcvarsall = find_vcvarsall(version)
interesting = {"include", "lib", "libpath", "path"}
interesting = {'include', 'lib', 'libpath', 'path'}
result = {}
if vcvarsall is None:
raise DistutilsPlatformError("Unable to find vcvarsall.bat")
raise DistutilsPlatformError('Unable to find vcvarsall.bat')
log.debug("Calling 'vcvarsall.bat %s' (version=%s)", arch, version)
popen = subprocess.Popen('"%s" %s & set' % (vcvarsall, arch),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
popen = subprocess.Popen(
'"{}" {} & set'.format(vcvarsall, arch),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
try:
stdout, stderr = popen.communicate()
if popen.wait() != 0:
raise DistutilsPlatformError(stderr.decode("mbcs"))
raise DistutilsPlatformError(stderr.decode('mbcs'))
stdout = stdout.decode("mbcs")
for line in stdout.split("\n"):
stdout = stdout.decode('mbcs')
for line in stdout.split('\n'):
line = Reg.convert_mbcs(line)
if '=' not in line:
continue
@ -289,13 +308,15 @@ def query_vcvarsall(version, arch="x86"):
return result
# More globals
VERSION = get_build_version()
if VERSION < 8.0:
raise DistutilsPlatformError("VC %0.1f is not supported by this module" % VERSION)
raise DistutilsPlatformError('VC %0.1f is not supported by this module' % VERSION)
# MACROS = MacroExpander(VERSION)
class MSVCCompiler(CCompiler) :
class MSVCCompiler(CCompiler):
"""Concrete class that implements an interface to Microsoft Visual C++,
as defined by the CCompiler abstract class."""
@ -316,8 +337,10 @@ class MSVCCompiler(CCompiler) :
# Needed for the filename generation methods provided by the
# base class, CCompiler.
src_extensions = (_c_extensions + _cpp_extensions +
_rc_extensions + _mc_extensions)
src_extensions = (
_c_extensions + _cpp_extensions +
_rc_extensions + _mc_extensions
)
res_extension = '.res'
obj_extension = '.obj'
static_lib_extension = '.lib'
@ -326,14 +349,14 @@ class MSVCCompiler(CCompiler) :
exe_extension = '.exe'
def __init__(self, verbose=0, dry_run=0, force=0):
CCompiler.__init__ (self, verbose, dry_run, force)
CCompiler.__init__(self, verbose, dry_run, force)
self.__version = VERSION
self.__root = r"Software\Microsoft\VisualStudio"
self.__root = r'Software\Microsoft\VisualStudio'
# self.__macros = MACROS
self.__paths = []
# target platform (.plat_name is consistent with 'bdist')
self.plat_name = None
self.__arch = None # deprecated name
self.__arch = None # deprecated name
self.initialized = False
def initialize(self, plat_name=None):
@ -344,17 +367,19 @@ class MSVCCompiler(CCompiler) :
# sanity check for platforms to prevent obscure errors later.
ok_plats = 'win32', 'win-amd64'
if plat_name not in ok_plats:
raise DistutilsPlatformError("--plat-name must be one of %s" %
(ok_plats,))
raise DistutilsPlatformError(
'--plat-name must be one of %s' %
(ok_plats,),
)
if "DISTUTILS_USE_SDK" in os.environ and "MSSdk" in os.environ and self.find_exe("cl.exe"):
if 'DISTUTILS_USE_SDK' in os.environ and 'MSSdk' in os.environ and self.find_exe('cl.exe'):
# Assume that the SDK set up everything alright; don't try to be
# smarter
self.cc = "cl.exe"
self.linker = "link.exe"
self.lib = "lib.exe"
self.rc = "rc.exe"
self.mc = "mc.exe"
self.cc = 'cl.exe'
self.linker = 'link.exe'
self.lib = 'lib.exe'
self.rc = 'rc.exe'
self.mc = 'mc.exe'
else:
# On x86, 'vcvars32.bat amd64' creates an env that doesn't work;
# to cross compile, you use 'x86_amd64'.
@ -366,7 +391,7 @@ class MSVCCompiler(CCompiler) :
else:
# cross compile from win32 -> some 64bit
plat_spec = PLAT_TO_VCVARS[get_platform()] + '_' + \
PLAT_TO_VCVARS[plat_name]
PLAT_TO_VCVARS[plat_name]
vc_env = query_vcvarsall(VERSION, plat_spec)
@ -375,16 +400,18 @@ class MSVCCompiler(CCompiler) :
os.environ['include'] = vc_env['include']
if len(self.__paths) == 0:
raise DistutilsPlatformError("Python was built with %s, "
"and extensions need to be built with the same "
"version of the compiler, but it isn't installed."
% self.__product)
raise DistutilsPlatformError(
'Python was built with %s, '
'and extensions need to be built with the same '
"version of the compiler, but it isn't installed."
% self.__product,
)
self.cc = self.find_exe("cl.exe")
self.linker = self.find_exe("link.exe")
self.lib = self.find_exe("lib.exe")
self.rc = self.find_exe("rc.exe") # resource compiler
self.mc = self.find_exe("mc.exe") # message compiler
self.cc = self.find_exe('cl.exe')
self.linker = self.find_exe('link.exe')
self.lib = self.find_exe('lib.exe')
self.rc = self.find_exe('rc.exe') # resource compiler
self.mc = self.find_exe('mc.exe') # message compiler
#self.set_path_env_var('lib')
#self.set_path_env_var('include')
@ -395,75 +422,99 @@ class MSVCCompiler(CCompiler) :
except KeyError:
pass
self.__paths = normalize_and_reduce_paths(self.__paths)
os.environ['path'] = ";".join(self.__paths)
os.environ['path'] = ';'.join(self.__paths)
self.preprocess_options = None
if self.__arch == "x86":
self.compile_options = [ '/nologo', '/O2', '/MD', '/W3',
'/DNDEBUG']
self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3',
'/Z7', '/D_DEBUG']
if self.__arch == 'x86':
self.compile_options = ['/nologo', '/O2', '/MD', '/W3',
'/DNDEBUG',
]
self.compile_options_debug = [
'/nologo', '/Od', '/MDd', '/W3',
'/Z7', '/D_DEBUG',
]
else:
# Win64
self.compile_options = [ '/nologo', '/O2', '/MD', '/W3', '/GS-' ,
'/DNDEBUG']
self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GS-',
'/Z7', '/D_DEBUG']
self.compile_options = ['/nologo', '/O2', '/MD', '/W3', '/GS-',
'/DNDEBUG',
]
self.compile_options_debug = [
'/nologo', '/Od', '/MDd', '/W3', '/GS-',
'/Z7', '/D_DEBUG',
]
self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO']
if self.__version >= 7:
self.ldflags_shared_debug = [
'/DLL', '/nologo', '/INCREMENTAL:no', '/DEBUG'
]
self.ldflags_static = [ '/nologo']
'/DLL', '/nologo', '/INCREMENTAL:no', '/DEBUG',
]
self.ldflags_static = ['/nologo']
self.initialized = True
# -- Worker methods ------------------------------------------------
def object_filenames(self,
source_filenames,
strip_dir=0,
output_dir=''):
def object_filenames(
self,
source_filenames,
strip_dir=0,
output_dir='',
):
# Copied from ccompiler.py, extended to return .res as 'object'-file
# for .rc input file
if output_dir is None: output_dir = ''
if output_dir is None:
output_dir = ''
obj_names = []
for src_name in source_filenames:
(base, ext) = os.path.splitext (src_name)
base = os.path.splitdrive(base)[1] # Chop off the drive
(base, ext) = os.path.splitext(src_name)
base = os.path.splitdrive(base)[1] # Chop off the drive
base = base[os.path.isabs(base):] # If abs, chop off leading /
if ext not in self.src_extensions:
# Better to raise an exception instead of silently continuing
# and later complain about sources and targets having
# different lengths
raise CompileError ("Don't know how to compile %s" % src_name)
raise CompileError("Don't know how to compile %s" % src_name)
if strip_dir:
base = os.path.basename (base)
base = os.path.basename(base)
if ext in self._rc_extensions:
obj_names.append (os.path.join (output_dir,
base + self.res_extension))
obj_names.append(
os.path.join(
output_dir,
base + self.res_extension,
),
)
elif ext in self._mc_extensions:
obj_names.append (os.path.join (output_dir,
base + self.res_extension))
obj_names.append(
os.path.join(
output_dir,
base + self.res_extension,
),
)
else:
obj_names.append (os.path.join (output_dir,
base + self.obj_extension))
obj_names.append(
os.path.join(
output_dir,
base + self.obj_extension,
),
)
return obj_names
def compile(self, sources,
output_dir=None, macros=None, include_dirs=None, debug=0,
extra_preargs=None, extra_postargs=None, depends=None):
def compile(
self, sources,
output_dir=None, macros=None, include_dirs=None, debug=0,
extra_preargs=None, extra_postargs=None, depends=None,
):
if not self.initialized:
self.initialize()
compile_info = self._setup_compile(output_dir, macros, include_dirs,
sources, depends, extra_postargs)
compile_info = self._setup_compile(
output_dir, macros, include_dirs,
sources, depends, extra_postargs,
)
macros, objects, extra_postargs, pp_opts, build = compile_info
compile_opts = extra_preargs or []
compile_opts.append ('/c')
compile_opts.append('/c')
if debug:
compile_opts.extend(self.compile_options_debug)
else:
@ -481,13 +532,13 @@ class MSVCCompiler(CCompiler) :
src = os.path.abspath(src)
if ext in self._c_extensions:
input_opt = "/Tc" + src
input_opt = '/Tc' + src
elif ext in self._cpp_extensions:
input_opt = "/Tp" + src
input_opt = '/Tp' + src
elif ext in self._rc_extensions:
# compile .RC to .RES file
input_opt = src
output_opt = "/fo" + obj
output_opt = '/fo' + obj
try:
self.spawn([self.rc] + pp_opts +
[output_opt] + [input_opt])
@ -512,85 +563,99 @@ class MSVCCompiler(CCompiler) :
# first compile .MC to .RC and .H file
self.spawn([self.mc] +
['-h', h_dir, '-r', rc_dir] + [src])
base, _ = os.path.splitext (os.path.basename (src))
rc_file = os.path.join (rc_dir, base + '.rc')
base, _ = os.path.splitext(os.path.basename(src))
rc_file = os.path.join(rc_dir, base + '.rc')
# then compile .RC to .RES file
self.spawn([self.rc] +
["/fo" + obj] + [rc_file])
['/fo' + obj] + [rc_file])
except DistutilsExecError as msg:
raise CompileError(msg)
continue
else:
# how to handle this file?
raise CompileError("Don't know how to compile %s to %s"
% (src, obj))
raise CompileError(
"Don't know how to compile %s to %s"
% (src, obj),
)
output_opt = "/Fo" + obj
output_opt = '/Fo' + obj
try:
self.spawn([self.cc] + compile_opts + pp_opts +
[input_opt, output_opt] +
extra_postargs)
self.spawn(
[self.cc] + compile_opts + pp_opts +
[input_opt, output_opt] +
extra_postargs,
)
except DistutilsExecError as msg:
raise CompileError(msg)
return objects
def create_static_lib(self,
objects,
output_libname,
output_dir=None,
debug=0,
target_lang=None):
def create_static_lib(
self,
objects,
output_libname,
output_dir=None,
debug=0,
target_lang=None,
):
if not self.initialized:
self.initialize()
(objects, output_dir) = self._fix_object_args(objects, output_dir)
output_filename = self.library_filename(output_libname,
output_dir=output_dir)
output_filename = self.library_filename(
output_libname,
output_dir=output_dir,
)
if self._need_link(objects, output_filename):
lib_args = objects + ['/OUT:' + output_filename]
if debug:
pass # XXX what goes here?
pass # XXX what goes here?
try:
self.spawn([self.lib] + lib_args)
except DistutilsExecError as msg:
raise LibError(msg)
else:
log.debug("skipping %s (up-to-date)", output_filename)
log.debug('skipping %s (up-to-date)', output_filename)
def link(self,
target_desc,
objects,
output_filename,
output_dir=None,
libraries=None,
library_dirs=None,
runtime_library_dirs=None,
export_symbols=None,
debug=0,
extra_preargs=None,
extra_postargs=None,
build_temp=None,
target_lang=None):
def link(
self,
target_desc,
objects,
output_filename,
output_dir=None,
libraries=None,
library_dirs=None,
runtime_library_dirs=None,
export_symbols=None,
debug=0,
extra_preargs=None,
extra_postargs=None,
build_temp=None,
target_lang=None,
):
if not self.initialized:
self.initialize()
(objects, output_dir) = self._fix_object_args(objects, output_dir)
fixed_args = self._fix_lib_args(libraries, library_dirs,
runtime_library_dirs)
fixed_args = self._fix_lib_args(
libraries, library_dirs,
runtime_library_dirs,
)
(libraries, library_dirs, runtime_library_dirs) = fixed_args
if runtime_library_dirs:
self.warn ("I don't know what to do with 'runtime_library_dirs': "
+ str (runtime_library_dirs))
self.warn(
"I don't know what to do with 'runtime_library_dirs': " +
str(runtime_library_dirs),
)
lib_opts = gen_lib_options(self,
library_dirs, runtime_library_dirs,
libraries)
lib_opts = gen_lib_options(
self,
library_dirs, runtime_library_dirs,
libraries,
)
if output_dir is not None:
output_filename = os.path.join(output_dir, output_filename)
@ -608,10 +673,12 @@ class MSVCCompiler(CCompiler) :
export_opts = []
for sym in (export_symbols or []):
export_opts.append("/EXPORT:" + sym)
export_opts.append('/EXPORT:' + sym)
ld_args = (ldflags + lib_opts + export_opts +
objects + ['/OUT:' + output_filename])
ld_args = (
ldflags + lib_opts + export_opts +
objects + ['/OUT:' + output_filename]
)
# The MSVC linker generates .lib and .exp files, which cannot be
# suppressed by any linker switches. The .lib files may even be
@ -621,11 +688,13 @@ class MSVCCompiler(CCompiler) :
build_temp = os.path.dirname(objects[0])
if export_symbols is not None:
(dll_name, dll_ext) = os.path.splitext(
os.path.basename(output_filename))
os.path.basename(output_filename),
)
implib_file = os.path.join(
build_temp,
self.library_filename(dll_name))
ld_args.append ('/IMPLIB:' + implib_file)
self.library_filename(dll_name),
)
ld_args.append('/IMPLIB:' + implib_file)
self.manifest_setup_ldargs(output_filename, build_temp, ld_args)
@ -648,14 +717,16 @@ class MSVCCompiler(CCompiler) :
mfinfo = self.manifest_get_embed_info(target_desc, ld_args)
if mfinfo is not None:
mffilename, mfid = mfinfo
out_arg = '-outputresource:%s;%s' % (output_filename, mfid)
out_arg = '-outputresource:{};{}'.format(output_filename, mfid)
try:
self.spawn(['mt.exe', '-nologo', '-manifest',
mffilename, out_arg])
self.spawn([
'mt.exe', '-nologo', '-manifest',
mffilename, out_arg,
])
except DistutilsExecError as msg:
raise LinkError(msg)
else:
log.debug("skipping %s (up-to-date)", output_filename)
log.debug('skipping %s (up-to-date)', output_filename)
def manifest_setup_ldargs(self, output_filename, build_temp, ld_args):
# If we need a manifest at all, an embedded manifest is recommended.
@ -665,8 +736,9 @@ class MSVCCompiler(CCompiler) :
# Ask the linker to generate the manifest in the temp dir, so
# we can check it, and possibly embed it, later.
temp_manifest = os.path.join(
build_temp,
os.path.basename(output_filename) + ".manifest")
build_temp,
os.path.basename(output_filename) + '.manifest',
)
ld_args.append('/MANIFESTFILE:' + temp_manifest)
def manifest_get_embed_info(self, target_desc, ld_args):
@ -675,8 +747,8 @@ class MSVCCompiler(CCompiler) :
# should be embedded. See http://bugs.python.org/issue7833 for why
# we want to avoid any manifest for extension modules if we can)
for arg in ld_args:
if arg.startswith("/MANIFESTFILE:"):
temp_manifest = arg.split(":", 1)[1]
if arg.startswith('/MANIFESTFILE:'):
temp_manifest = arg.split(':', 1)[1]
break
else:
# no /MANIFESTFILE so nothing to do.
@ -709,17 +781,19 @@ class MSVCCompiler(CCompiler) :
finally:
manifest_f.close()
pattern = re.compile(
r"""<assemblyIdentity.*?name=("|')Microsoft\."""\
r"""<assemblyIdentity.*?name=("|')Microsoft\."""
r"""VC\d{2}\.CRT("|').*?(/>|</assemblyIdentity>)""",
re.DOTALL)
manifest_buf = re.sub(pattern, "", manifest_buf)
pattern = r"<dependentAssembly>\s*</dependentAssembly>"
manifest_buf = re.sub(pattern, "", manifest_buf)
re.DOTALL,
)
manifest_buf = re.sub(pattern, '', manifest_buf)
pattern = r'<dependentAssembly>\s*</dependentAssembly>'
manifest_buf = re.sub(pattern, '', manifest_buf)
# Now see if any other assemblies are referenced - if not, we
# don't want a manifest embedded.
pattern = re.compile(
r"""<assemblyIdentity.*?name=(?:"|')(.+?)(?:"|')"""
r""".*?(?:/>|</assemblyIdentity>)""", re.DOTALL)
r""".*?(?:/>|</assemblyIdentity>)""", re.DOTALL,
)
if re.search(pattern, manifest_buf) is None:
return None
@ -737,26 +811,26 @@ class MSVCCompiler(CCompiler) :
# ccompiler.py.
def library_dir_option(self, dir):
return "/LIBPATH:" + dir
return '/LIBPATH:' + dir
def runtime_library_dir_option(self, dir):
raise DistutilsPlatformError(
"don't know how to set runtime library search path for MSVC++")
"don't know how to set runtime library search path for MSVC++",
)
def library_option(self, lib):
return self.library_filename(lib)
def find_library_file(self, dirs, lib, debug=0):
# Prefer a debugging library if found (and requested), but deal
# with it if we don't have one.
if debug:
try_names = [lib + "_d", lib]
try_names = [lib + '_d', lib]
else:
try_names = [lib]
for dir in dirs:
for name in try_names:
libfile = os.path.join(dir, self.library_filename (name))
libfile = os.path.join(dir, self.library_filename(name))
if os.path.exists(libfile):
return libfile
else:
@ -781,7 +855,7 @@ class MSVCCompiler(CCompiler) :
# didn't find it; try existing path
for p in os.environ['Path'].split(';'):
fn = os.path.join(os.path.abspath(p),exe)
fn = os.path.join(os.path.abspath(p), exe)
if os.path.isfile(fn):
return fn

View file

@ -3,18 +3,22 @@
Contains MSVCCompiler, an implementation of the abstract CCompiler class
for the Microsoft Visual Studio.
"""
# Written by Perry Stoll
# hacked by Robin Becker and Thomas Heller to do a better job of
# finding DevStudio (through the registry)
from __future__ import annotations
import os
import sys
import sys, os
from distutils.errors import \
DistutilsExecError, DistutilsPlatformError, \
CompileError, LibError, LinkError
from distutils.ccompiler import \
CCompiler, gen_lib_options
from distutils import log
from distutils.ccompiler import CCompiler
from distutils.ccompiler import gen_lib_options
from distutils.errors import CompileError
from distutils.errors import DistutilsExecError
from distutils.errors import DistutilsPlatformError
from distutils.errors import LibError
from distutils.errors import LinkError
_can_read_reg = False
try:
@ -40,17 +44,22 @@ except ImportError:
RegEnumValue = win32api.RegEnumValue
RegError = win32api.error
except ImportError:
log.info("Warning: Can't read registry to find the "
"necessary compiler setting\n"
"Make sure that Python modules winreg, "
"win32api or win32con are installed.")
log.info(
"Warning: Can't read registry to find the "
'necessary compiler setting\n'
'Make sure that Python modules winreg, '
'win32api or win32con are installed.',
)
pass
if _can_read_reg:
HKEYS = (hkey_mod.HKEY_USERS,
hkey_mod.HKEY_CURRENT_USER,
hkey_mod.HKEY_LOCAL_MACHINE,
hkey_mod.HKEY_CLASSES_ROOT)
HKEYS = (
hkey_mod.HKEY_USERS,
hkey_mod.HKEY_CURRENT_USER,
hkey_mod.HKEY_LOCAL_MACHINE,
hkey_mod.HKEY_CLASSES_ROOT,
)
def read_keys(base, key):
"""Return list of registry keys."""
@ -69,6 +78,7 @@ def read_keys(base, key):
i += 1
return L
def read_values(base, key):
"""Return dict of registry keys and values.
@ -90,15 +100,17 @@ def read_values(base, key):
i += 1
return d
def convert_mbcs(s):
dec = getattr(s, "decode", None)
dec = getattr(s, 'decode', None)
if dec is not None:
try:
s = dec("mbcs")
s = dec('mbcs')
except UnicodeError:
pass
return s
class MacroExpander:
def __init__(self, version):
self.macros = {}
@ -108,54 +120,56 @@ class MacroExpander:
for base in HKEYS:
d = read_values(base, path)
if d:
self.macros["$(%s)" % macro] = d[key]
self.macros['$(%s)' % macro] = d[key]
break
def load_macros(self, version):
vsbase = r"Software\Microsoft\VisualStudio\%0.1f" % version
self.set_macro("VCInstallDir", vsbase + r"\Setup\VC", "productdir")
self.set_macro("VSInstallDir", vsbase + r"\Setup\VS", "productdir")
net = r"Software\Microsoft\.NETFramework"
self.set_macro("FrameworkDir", net, "installroot")
vsbase = r'Software\Microsoft\VisualStudio\%0.1f' % version
self.set_macro('VCInstallDir', vsbase + r'\Setup\VC', 'productdir')
self.set_macro('VSInstallDir', vsbase + r'\Setup\VS', 'productdir')
net = r'Software\Microsoft\.NETFramework'
self.set_macro('FrameworkDir', net, 'installroot')
try:
if version > 7.0:
self.set_macro("FrameworkSDKDir", net, "sdkinstallrootv1.1")
self.set_macro('FrameworkSDKDir', net, 'sdkinstallrootv1.1')
else:
self.set_macro("FrameworkSDKDir", net, "sdkinstallroot")
except KeyError as exc: #
self.set_macro('FrameworkSDKDir', net, 'sdkinstallroot')
except KeyError as exc:
raise DistutilsPlatformError(
"""Python was built with Visual Studio 2003;
"""Python was built with Visual Studio 2003;
extensions must be built with a compiler than can generate compatible binaries.
Visual Studio 2003 was not found on this system. If you have Cygwin installed,
you can try compiling with MingW32, by passing "-c mingw32" to setup.py.""")
you can try compiling with MingW32, by passing "-c mingw32" to setup.py.""",
)
p = r"Software\Microsoft\NET Framework Setup\Product"
p = r'Software\Microsoft\NET Framework Setup\Product'
for base in HKEYS:
try:
h = RegOpenKeyEx(base, p)
except RegError:
continue
key = RegEnumKey(h, 0)
d = read_values(base, r"%s\%s" % (p, key))
self.macros["$(FrameworkVersion)"] = d["version"]
d = read_values(base, r'{}\{}'.format(p, key))
self.macros['$(FrameworkVersion)'] = d['version']
def sub(self, s):
for k, v in self.macros.items():
s = s.replace(k, v)
return s
def get_build_version():
"""Return the version of MSVC that was used to build Python.
For Python 2.3 and up, the version number is included in
sys.version. For earlier versions, assume the compiler is MSVC 6.
"""
prefix = "MSC v."
prefix = 'MSC v.'
i = sys.version.find(prefix)
if i == -1:
return 6
i = i + len(prefix)
s, rest = sys.version[i:].split(" ", 1)
s, rest = sys.version[i:].split(' ', 1)
majorVersion = int(s[:-2]) - 6
if majorVersion >= 13:
# v13 was skipped and should be v14
@ -169,18 +183,20 @@ def get_build_version():
# else we don't know what version of the compiler this is
return None
def get_build_architecture():
"""Return the processor architecture.
Possible results are "Intel" or "AMD64".
"""
prefix = " bit ("
prefix = ' bit ('
i = sys.version.find(prefix)
if i == -1:
return "Intel"
j = sys.version.find(")", i)
return sys.version[i+len(prefix):j]
return 'Intel'
j = sys.version.find(')', i)
return sys.version[i + len(prefix):j]
def normalize_and_reduce_paths(paths):
"""Return a list of normalized paths with duplicates removed.
@ -197,7 +213,7 @@ def normalize_and_reduce_paths(paths):
return reduced_paths
class MSVCCompiler(CCompiler) :
class MSVCCompiler(CCompiler):
"""Concrete class that implements an interface to Microsoft Visual C++,
as defined by the CCompiler abstract class."""
@ -218,8 +234,10 @@ class MSVCCompiler(CCompiler) :
# Needed for the filename generation methods provided by the
# base class, CCompiler.
src_extensions = (_c_extensions + _cpp_extensions +
_rc_extensions + _mc_extensions)
src_extensions = (
_c_extensions + _cpp_extensions +
_rc_extensions + _mc_extensions
)
res_extension = '.res'
obj_extension = '.obj'
static_lib_extension = '.lib'
@ -228,47 +246,49 @@ class MSVCCompiler(CCompiler) :
exe_extension = '.exe'
def __init__(self, verbose=0, dry_run=0, force=0):
CCompiler.__init__ (self, verbose, dry_run, force)
CCompiler.__init__(self, verbose, dry_run, force)
self.__version = get_build_version()
self.__arch = get_build_architecture()
if self.__arch == "Intel":
if self.__arch == 'Intel':
# x86
if self.__version >= 7:
self.__root = r"Software\Microsoft\VisualStudio"
self.__root = r'Software\Microsoft\VisualStudio'
self.__macros = MacroExpander(self.__version)
else:
self.__root = r"Software\Microsoft\Devstudio"
self.__product = "Visual Studio version %s" % self.__version
self.__root = r'Software\Microsoft\Devstudio'
self.__product = 'Visual Studio version %s' % self.__version
else:
# Win64. Assume this was built with the platform SDK
self.__product = "Microsoft SDK compiler %s" % (self.__version + 6)
self.__product = 'Microsoft SDK compiler %s' % (self.__version + 6)
self.initialized = False
def initialize(self):
self.__paths = []
if "DISTUTILS_USE_SDK" in os.environ and "MSSdk" in os.environ and self.find_exe("cl.exe"):
if 'DISTUTILS_USE_SDK' in os.environ and 'MSSdk' in os.environ and self.find_exe('cl.exe'):
# Assume that the SDK set up everything alright; don't try to be
# smarter
self.cc = "cl.exe"
self.linker = "link.exe"
self.lib = "lib.exe"
self.rc = "rc.exe"
self.mc = "mc.exe"
self.cc = 'cl.exe'
self.linker = 'link.exe'
self.lib = 'lib.exe'
self.rc = 'rc.exe'
self.mc = 'mc.exe'
else:
self.__paths = self.get_msvc_paths("path")
self.__paths = self.get_msvc_paths('path')
if len(self.__paths) == 0:
raise DistutilsPlatformError("Python was built with %s, "
"and extensions need to be built with the same "
"version of the compiler, but it isn't installed."
% self.__product)
raise DistutilsPlatformError(
'Python was built with %s, '
'and extensions need to be built with the same '
"version of the compiler, but it isn't installed."
% self.__product,
)
self.cc = self.find_exe("cl.exe")
self.linker = self.find_exe("link.exe")
self.lib = self.find_exe("lib.exe")
self.rc = self.find_exe("rc.exe") # resource compiler
self.mc = self.find_exe("mc.exe") # message compiler
self.cc = self.find_exe('cl.exe')
self.linker = self.find_exe('link.exe')
self.lib = self.find_exe('lib.exe')
self.rc = self.find_exe('rc.exe') # resource compiler
self.mc = self.find_exe('mc.exe') # message compiler
self.set_path_env_var('lib')
self.set_path_env_var('include')
@ -279,79 +299,103 @@ class MSVCCompiler(CCompiler) :
except KeyError:
pass
self.__paths = normalize_and_reduce_paths(self.__paths)
os.environ['path'] = ";".join(self.__paths)
os.environ['path'] = ';'.join(self.__paths)
self.preprocess_options = None
if self.__arch == "Intel":
self.compile_options = [ '/nologo', '/O2', '/MD', '/W3', '/GX' ,
'/DNDEBUG']
self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GX',
'/Z7', '/D_DEBUG']
if self.__arch == 'Intel':
self.compile_options = ['/nologo', '/O2', '/MD', '/W3', '/GX',
'/DNDEBUG',
]
self.compile_options_debug = [
'/nologo', '/Od', '/MDd', '/W3', '/GX',
'/Z7', '/D_DEBUG',
]
else:
# Win64
self.compile_options = [ '/nologo', '/O2', '/MD', '/W3', '/GS-' ,
'/DNDEBUG']
self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3', '/GS-',
'/Z7', '/D_DEBUG']
self.compile_options = ['/nologo', '/O2', '/MD', '/W3', '/GS-',
'/DNDEBUG',
]
self.compile_options_debug = [
'/nologo', '/Od', '/MDd', '/W3', '/GS-',
'/Z7', '/D_DEBUG',
]
self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO']
if self.__version >= 7:
self.ldflags_shared_debug = [
'/DLL', '/nologo', '/INCREMENTAL:no', '/DEBUG'
]
'/DLL', '/nologo', '/INCREMENTAL:no', '/DEBUG',
]
else:
self.ldflags_shared_debug = [
'/DLL', '/nologo', '/INCREMENTAL:no', '/pdb:None', '/DEBUG'
]
self.ldflags_static = [ '/nologo']
'/DLL', '/nologo', '/INCREMENTAL:no', '/pdb:None', '/DEBUG',
]
self.ldflags_static = ['/nologo']
self.initialized = True
# -- Worker methods ------------------------------------------------
def object_filenames(self,
source_filenames,
strip_dir=0,
output_dir=''):
def object_filenames(
self,
source_filenames,
strip_dir=0,
output_dir='',
):
# Copied from ccompiler.py, extended to return .res as 'object'-file
# for .rc input file
if output_dir is None: output_dir = ''
if output_dir is None:
output_dir = ''
obj_names = []
for src_name in source_filenames:
(base, ext) = os.path.splitext (src_name)
base = os.path.splitdrive(base)[1] # Chop off the drive
(base, ext) = os.path.splitext(src_name)
base = os.path.splitdrive(base)[1] # Chop off the drive
base = base[os.path.isabs(base):] # If abs, chop off leading /
if ext not in self.src_extensions:
# Better to raise an exception instead of silently continuing
# and later complain about sources and targets having
# different lengths
raise CompileError ("Don't know how to compile %s" % src_name)
raise CompileError("Don't know how to compile %s" % src_name)
if strip_dir:
base = os.path.basename (base)
base = os.path.basename(base)
if ext in self._rc_extensions:
obj_names.append (os.path.join (output_dir,
base + self.res_extension))
obj_names.append(
os.path.join(
output_dir,
base + self.res_extension,
),
)
elif ext in self._mc_extensions:
obj_names.append (os.path.join (output_dir,
base + self.res_extension))
obj_names.append(
os.path.join(
output_dir,
base + self.res_extension,
),
)
else:
obj_names.append (os.path.join (output_dir,
base + self.obj_extension))
obj_names.append(
os.path.join(
output_dir,
base + self.obj_extension,
),
)
return obj_names
def compile(self, sources,
output_dir=None, macros=None, include_dirs=None, debug=0,
extra_preargs=None, extra_postargs=None, depends=None):
def compile(
self, sources,
output_dir=None, macros=None, include_dirs=None, debug=0,
extra_preargs=None, extra_postargs=None, depends=None,
):
if not self.initialized:
self.initialize()
compile_info = self._setup_compile(output_dir, macros, include_dirs,
sources, depends, extra_postargs)
compile_info = self._setup_compile(
output_dir, macros, include_dirs,
sources, depends, extra_postargs,
)
macros, objects, extra_postargs, pp_opts, build = compile_info
compile_opts = extra_preargs or []
compile_opts.append ('/c')
compile_opts.append('/c')
if debug:
compile_opts.extend(self.compile_options_debug)
else:
@ -369,13 +413,13 @@ class MSVCCompiler(CCompiler) :
src = os.path.abspath(src)
if ext in self._c_extensions:
input_opt = "/Tc" + src
input_opt = '/Tc' + src
elif ext in self._cpp_extensions:
input_opt = "/Tp" + src
input_opt = '/Tp' + src
elif ext in self._rc_extensions:
# compile .RC to .RES file
input_opt = src
output_opt = "/fo" + obj
output_opt = '/fo' + obj
try:
self.spawn([self.rc] + pp_opts +
[output_opt] + [input_opt])
@ -400,85 +444,99 @@ class MSVCCompiler(CCompiler) :
# first compile .MC to .RC and .H file
self.spawn([self.mc] +
['-h', h_dir, '-r', rc_dir] + [src])
base, _ = os.path.splitext (os.path.basename (src))
rc_file = os.path.join (rc_dir, base + '.rc')
base, _ = os.path.splitext(os.path.basename(src))
rc_file = os.path.join(rc_dir, base + '.rc')
# then compile .RC to .RES file
self.spawn([self.rc] +
["/fo" + obj] + [rc_file])
['/fo' + obj] + [rc_file])
except DistutilsExecError as msg:
raise CompileError(msg)
continue
else:
# how to handle this file?
raise CompileError("Don't know how to compile %s to %s"
% (src, obj))
raise CompileError(
"Don't know how to compile %s to %s"
% (src, obj),
)
output_opt = "/Fo" + obj
output_opt = '/Fo' + obj
try:
self.spawn([self.cc] + compile_opts + pp_opts +
[input_opt, output_opt] +
extra_postargs)
self.spawn(
[self.cc] + compile_opts + pp_opts +
[input_opt, output_opt] +
extra_postargs,
)
except DistutilsExecError as msg:
raise CompileError(msg)
return objects
def create_static_lib(self,
objects,
output_libname,
output_dir=None,
debug=0,
target_lang=None):
def create_static_lib(
self,
objects,
output_libname,
output_dir=None,
debug=0,
target_lang=None,
):
if not self.initialized:
self.initialize()
(objects, output_dir) = self._fix_object_args(objects, output_dir)
output_filename = self.library_filename(output_libname,
output_dir=output_dir)
output_filename = self.library_filename(
output_libname,
output_dir=output_dir,
)
if self._need_link(objects, output_filename):
lib_args = objects + ['/OUT:' + output_filename]
if debug:
pass # XXX what goes here?
pass # XXX what goes here?
try:
self.spawn([self.lib] + lib_args)
except DistutilsExecError as msg:
raise LibError(msg)
else:
log.debug("skipping %s (up-to-date)", output_filename)
log.debug('skipping %s (up-to-date)', output_filename)
def link(self,
target_desc,
objects,
output_filename,
output_dir=None,
libraries=None,
library_dirs=None,
runtime_library_dirs=None,
export_symbols=None,
debug=0,
extra_preargs=None,
extra_postargs=None,
build_temp=None,
target_lang=None):
def link(
self,
target_desc,
objects,
output_filename,
output_dir=None,
libraries=None,
library_dirs=None,
runtime_library_dirs=None,
export_symbols=None,
debug=0,
extra_preargs=None,
extra_postargs=None,
build_temp=None,
target_lang=None,
):
if not self.initialized:
self.initialize()
(objects, output_dir) = self._fix_object_args(objects, output_dir)
fixed_args = self._fix_lib_args(libraries, library_dirs,
runtime_library_dirs)
fixed_args = self._fix_lib_args(
libraries, library_dirs,
runtime_library_dirs,
)
(libraries, library_dirs, runtime_library_dirs) = fixed_args
if runtime_library_dirs:
self.warn ("I don't know what to do with 'runtime_library_dirs': "
+ str (runtime_library_dirs))
self.warn(
"I don't know what to do with 'runtime_library_dirs': " +
str(runtime_library_dirs),
)
lib_opts = gen_lib_options(self,
library_dirs, runtime_library_dirs,
libraries)
lib_opts = gen_lib_options(
self,
library_dirs, runtime_library_dirs,
libraries,
)
if output_dir is not None:
output_filename = os.path.join(output_dir, output_filename)
@ -496,10 +554,12 @@ class MSVCCompiler(CCompiler) :
export_opts = []
for sym in (export_symbols or []):
export_opts.append("/EXPORT:" + sym)
export_opts.append('/EXPORT:' + sym)
ld_args = (ldflags + lib_opts + export_opts +
objects + ['/OUT:' + output_filename])
ld_args = (
ldflags + lib_opts + export_opts +
objects + ['/OUT:' + output_filename]
)
# The MSVC linker generates .lib and .exp files, which cannot be
# suppressed by any linker switches. The .lib files may even be
@ -508,11 +568,13 @@ class MSVCCompiler(CCompiler) :
# builds, they can go into the same directory.
if export_symbols is not None:
(dll_name, dll_ext) = os.path.splitext(
os.path.basename(output_filename))
os.path.basename(output_filename),
)
implib_file = os.path.join(
os.path.dirname(objects[0]),
self.library_filename(dll_name))
ld_args.append ('/IMPLIB:' + implib_file)
self.library_filename(dll_name),
)
ld_args.append('/IMPLIB:' + implib_file)
if extra_preargs:
ld_args[:0] = extra_preargs
@ -526,34 +588,33 @@ class MSVCCompiler(CCompiler) :
raise LinkError(msg)
else:
log.debug("skipping %s (up-to-date)", output_filename)
log.debug('skipping %s (up-to-date)', output_filename)
# -- Miscellaneous methods -----------------------------------------
# These are all used by the 'gen_lib_options() function, in
# ccompiler.py.
def library_dir_option(self, dir):
return "/LIBPATH:" + dir
return '/LIBPATH:' + dir
def runtime_library_dir_option(self, dir):
raise DistutilsPlatformError(
"don't know how to set runtime library search path for MSVC++")
"don't know how to set runtime library search path for MSVC++",
)
def library_option(self, lib):
return self.library_filename(lib)
def find_library_file(self, dirs, lib, debug=0):
# Prefer a debugging library if found (and requested), but deal
# with it if we don't have one.
if debug:
try_names = [lib + "_d", lib]
try_names = [lib + '_d', lib]
else:
try_names = [lib]
for dir in dirs:
for name in try_names:
libfile = os.path.join(dir, self.library_filename (name))
libfile = os.path.join(dir, self.library_filename(name))
if os.path.exists(libfile):
return libfile
else:
@ -578,7 +639,7 @@ class MSVCCompiler(CCompiler) :
# didn't find it; try existing path
for p in os.environ['Path'].split(';'):
fn = os.path.join(os.path.abspath(p),exe)
fn = os.path.join(os.path.abspath(p), exe)
if os.path.isfile(fn):
return fn
@ -593,30 +654,36 @@ class MSVCCompiler(CCompiler) :
if not _can_read_reg:
return []
path = path + " dirs"
path = path + ' dirs'
if self.__version >= 7:
key = (r"%s\%0.1f\VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories"
% (self.__root, self.__version))
key = (
r'%s\%0.1f\VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories'
% (self.__root, self.__version)
)
else:
key = (r"%s\6.0\Build System\Components\Platforms"
r"\Win32 (%s)\Directories" % (self.__root, platform))
key = (
r'%s\6.0\Build System\Components\Platforms'
r'\Win32 (%s)\Directories' % (self.__root, platform)
)
for base in HKEYS:
d = read_values(base, key)
if d:
if self.__version >= 7:
return self.__macros.sub(d[path]).split(";")
return self.__macros.sub(d[path]).split(';')
else:
return d[path].split(";")
return d[path].split(';')
# MSVC 6 seems to create the registry entries we need only when
# the GUI is run.
if self.__version == 6:
for base in HKEYS:
if read_values(base, r"%s\6.0" % self.__root) is not None:
self.warn("It seems you have Visual Studio 6 installed, "
"but the expected registry settings are not present.\n"
"You must at least run the Visual Studio GUI once "
"so that these entries are created.")
if read_values(base, r'%s\6.0' % self.__root) is not None:
self.warn(
'It seems you have Visual Studio 6 installed, '
'but the expected registry settings are not present.\n'
'You must at least run the Visual Studio GUI once '
'so that these entries are created.',
)
break
return []
@ -627,8 +694,8 @@ class MSVCCompiler(CCompiler) :
commands.
"""
if name == "lib":
p = self.get_msvc_paths("library")
if name == 'lib':
p = self.get_msvc_paths('library')
else:
p = self.get_msvc_paths(name)
if p:
@ -636,7 +703,7 @@ class MSVCCompiler(CCompiler) :
if get_build_version() >= 8.0:
log.debug("Importing new compiler from distutils.msvc9compiler")
log.debug('Importing new compiler from distutils.msvc9compiler')
OldMSVCCompiler = MSVCCompiler
from distutils.msvc9compiler import MSVCCompiler
# get_build_architecture not really relevant now we support cross-compile

View file

@ -1,5 +1,7 @@
import sys
from __future__ import annotations
import subprocess
import sys
def __optim_args_from_interpreter_flags():
@ -8,12 +10,12 @@ def __optim_args_from_interpreter_flags():
args = []
value = sys.flags.optimize
if value > 0:
args.append("-" + "O" * value)
args.append('-' + 'O' * value)
return args
_optim_args_from_interpreter_flags = getattr(
subprocess,
"_optim_args_from_interpreter_flags",
'_optim_args_from_interpreter_flags',
__optim_args_from_interpreter_flags,
)

View file

@ -1,7 +1,10 @@
from __future__ import annotations
def aix_platform(osname, version, release):
try:
import _aix_support
return _aix_support.aix_platform()
except ImportError:
pass
return "%s-%s.%s" % (osname, version, release)
return '{}-{}.{}'.format(osname, version, release)

View file

@ -5,14 +5,16 @@ specific functions for launching another program in a sub-process.
Also provides the 'find_executable()' to search the path for a given
executable name.
"""
from __future__ import annotations
import sys
import os
import subprocess
import sys
from distutils.errors import DistutilsPlatformError, DistutilsExecError
from distutils.debug import DEBUG
from distutils import log
from distutils.debug import DEBUG
from distutils.errors import DistutilsExecError
from distutils.errors import DistutilsPlatformError
def spawn(cmd, search_path=1, verbose=0, dry_run=0, env=None):
@ -60,13 +62,15 @@ def spawn(cmd, search_path=1, verbose=0, dry_run=0, env=None):
if not DEBUG:
cmd = cmd[0]
raise DistutilsExecError(
"command %r failed: %s" % (cmd, exc.args[-1])) from exc
'command {!r} failed: {}'.format(cmd, exc.args[-1]),
) from exc
if exitcode:
if not DEBUG:
cmd = cmd[0]
raise DistutilsExecError(
"command %r failed with exit code %s" % (cmd, exitcode))
'command {!r} failed with exit code {}'.format(cmd, exitcode),
)
def find_executable(executable, path=None):
@ -86,7 +90,7 @@ def find_executable(executable, path=None):
path = os.environ.get('PATH', None)
if path is None:
try:
path = os.confstr("CS_PATH")
path = os.confstr('CS_PATH')
except (AttributeError, ValueError):
# os.confstr() or CS_PATH is not available
path = os.defpath

View file

@ -8,6 +8,7 @@ available.
Written by: Fred L. Drake, Jr.
Email: <fdrake@acm.org>
"""
from __future__ import annotations
import _imp
import os
@ -27,8 +28,8 @@ BASE_EXEC_PREFIX = os.path.normpath(sys.base_exec_prefix)
# Path to the base directory of the project. On Windows the binary may
# live in project/PCbuild/win32 or project/PCbuild/amd64.
# set for cross builds
if "_PYTHON_PROJECT_BASE" in os.environ:
project_base = os.path.abspath(os.environ["_PYTHON_PROJECT_BASE"])
if '_PYTHON_PROJECT_BASE' in os.environ:
project_base = os.path.abspath(os.environ['_PYTHON_PROJECT_BASE'])
else:
if sys.executable:
project_base = os.path.dirname(os.path.abspath(sys.executable))
@ -42,27 +43,31 @@ else:
# building an extension with an un-installed Python, so we use
# different (hard-wired) directories.
def _is_python_source_dir(d):
for fn in ("Setup", "Setup.local"):
if os.path.isfile(os.path.join(d, "Modules", fn)):
for fn in ('Setup', 'Setup.local'):
if os.path.isfile(os.path.join(d, 'Modules', fn)):
return True
return False
_sys_home = getattr(sys, '_home', None)
if os.name == 'nt':
def _fix_pcbuild(d):
if d and os.path.normcase(d).startswith(
os.path.normcase(os.path.join(PREFIX, "PCbuild"))):
os.path.normcase(os.path.join(PREFIX, 'PCbuild')),
):
return PREFIX
return d
project_base = _fix_pcbuild(project_base)
_sys_home = _fix_pcbuild(_sys_home)
def _python_build():
if _sys_home:
return _is_python_source_dir(_sys_home)
return _is_python_source_dir(project_base)
python_build = _python_build()
@ -78,6 +83,7 @@ except AttributeError:
# this attribute, which is fine.
pass
def get_python_version():
"""Return a string containing the major and minor Python version,
leaving off the patchlevel. Sample return values could be '1.5'
@ -99,7 +105,7 @@ def get_python_inc(plat_specific=0, prefix=None):
"""
if prefix is None:
prefix = plat_specific and BASE_EXEC_PREFIX or BASE_PREFIX
if os.name == "posix":
if os.name == 'posix':
if IS_PYPY and sys.version_info < (3, 8):
return os.path.join(prefix, 'include')
if python_build:
@ -115,18 +121,21 @@ def get_python_inc(plat_specific=0, prefix=None):
return os.path.normpath(incdir)
implementation = 'pypy' if IS_PYPY else 'python'
python_dir = implementation + get_python_version() + build_flags
return os.path.join(prefix, "include", python_dir)
elif os.name == "nt":
return os.path.join(prefix, 'include', python_dir)
elif os.name == 'nt':
if python_build:
# Include both the include and PC dir to ensure we can find
# pyconfig.h
return (os.path.join(prefix, "include") + os.path.pathsep +
os.path.join(prefix, "PC"))
return os.path.join(prefix, "include")
return (
os.path.join(prefix, 'include') + os.path.pathsep +
os.path.join(prefix, 'PC')
)
return os.path.join(prefix, 'include')
else:
raise DistutilsPlatformError(
"I don't know where Python installs its C header files "
"on platform '%s'" % os.name)
"on platform '%s'" % os.name,
)
def get_python_lib(plat_specific=0, standard_lib=0, prefix=None):
@ -149,7 +158,7 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None):
if prefix is None:
prefix = PREFIX
if standard_lib:
return os.path.join(prefix, "lib-python", sys.version[0])
return os.path.join(prefix, 'lib-python', sys.version[0])
return os.path.join(prefix, 'site-packages')
if prefix is None:
@ -158,31 +167,33 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None):
else:
prefix = plat_specific and EXEC_PREFIX or PREFIX
if os.name == "posix":
if os.name == 'posix':
if plat_specific or standard_lib:
# Platform-specific modules (any module from a non-pure-Python
# module distribution) or standard Python library modules.
libdir = getattr(sys, "platlibdir", "lib")
libdir = getattr(sys, 'platlibdir', 'lib')
else:
# Pure Python
libdir = "lib"
libdir = 'lib'
implementation = 'pypy' if IS_PYPY else 'python'
libpython = os.path.join(prefix, libdir,
implementation + get_python_version())
libpython = os.path.join(
prefix, libdir,
implementation + get_python_version(),
)
if standard_lib:
return libpython
else:
return os.path.join(libpython, "site-packages")
elif os.name == "nt":
return os.path.join(libpython, 'site-packages')
elif os.name == 'nt':
if standard_lib:
return os.path.join(prefix, "Lib")
return os.path.join(prefix, 'Lib')
else:
return os.path.join(prefix, "Lib", "site-packages")
return os.path.join(prefix, 'Lib', 'site-packages')
else:
raise DistutilsPlatformError(
"I don't know where Python installs its library "
"on platform '%s'" % os.name)
"on platform '%s'" % os.name,
)
def customize_compiler(compiler):
@ -191,8 +202,8 @@ def customize_compiler(compiler):
Mainly needed on Unix, so we can plug in the information that
varies across Unices and is stored in Python's Makefile.
"""
if compiler.compiler_type == "unix":
if sys.platform == "darwin":
if compiler.compiler_type == 'unix':
if sys.platform == 'darwin':
# Perform first-time customization of compiler-related
# config vars on OS X now that we know we need a compiler.
# This is primarily to support Pythons from binary
@ -209,13 +220,17 @@ def customize_compiler(compiler):
_config_vars['CUSTOMIZED_OSX_COMPILER'] = 'True'
(cc, cxx, cflags, ccshared, ldshared, shlib_suffix, ar, ar_flags) = \
get_config_vars('CC', 'CXX', 'CFLAGS',
'CCSHARED', 'LDSHARED', 'SHLIB_SUFFIX', 'AR', 'ARFLAGS')
get_config_vars(
'CC', 'CXX', 'CFLAGS',
'CCSHARED', 'LDSHARED', 'SHLIB_SUFFIX', 'AR', 'ARFLAGS',
)
if 'CC' in os.environ:
newcc = os.environ['CC']
if('LDSHARED' not in os.environ
and ldshared.startswith(cc)):
if (
'LDSHARED' not in os.environ and
ldshared.startswith(cc)
):
# If CC is overridden, use that as the default
# command for LDSHARED as well
ldshared = newcc + ldshared[len(cc):]
@ -227,7 +242,7 @@ def customize_compiler(compiler):
if 'CPP' in os.environ:
cpp = os.environ['CPP']
else:
cpp = cc + " -E" # not always
cpp = cc + ' -E' # not always
if 'LDFLAGS' in os.environ:
ldshared = ldshared + ' ' + os.environ['LDFLAGS']
if 'CFLAGS' in os.environ:
@ -252,7 +267,8 @@ def customize_compiler(compiler):
compiler_cxx=cxx,
linker_so=ldshared,
linker_exe=cc,
archiver=archiver)
archiver=archiver,
)
if 'RANLIB' in os.environ and compiler.executables.get('ranlib', None):
compiler.set_executables(ranlib=os.environ['RANLIB'])
@ -263,8 +279,8 @@ def customize_compiler(compiler):
def get_config_h_filename():
"""Return full pathname of installed pyconfig.h file."""
if python_build:
if os.name == "nt":
inc_dir = os.path.join(_sys_home or project_base, "PC")
if os.name == 'nt':
inc_dir = os.path.join(_sys_home or project_base, 'PC')
else:
inc_dir = _sys_home or project_base
else:
@ -276,9 +292,9 @@ def get_config_h_filename():
def get_makefile_filename():
"""Return full pathname of installed Makefile from the Python build."""
if python_build:
return os.path.join(_sys_home or project_base, "Makefile")
return os.path.join(_sys_home or project_base, 'Makefile')
lib_dir = get_python_lib(plat_specific=0, standard_lib=1)
config_file = 'config-{}{}'.format(get_python_version(), build_flags)
config_file = f'config-{get_python_version()}{build_flags}'
if hasattr(sys.implementation, '_multiarch'):
config_file += '-%s' % sys.implementation._multiarch
return os.path.join(lib_dir, config_file, 'Makefile')
@ -293,8 +309,8 @@ def parse_config_h(fp, g=None):
"""
if g is None:
g = {}
define_rx = re.compile("#define ([A-Z][A-Za-z0-9_]+) (.*)\n")
undef_rx = re.compile("/[*] #undef ([A-Z][A-Za-z0-9_]+) [*]/\n")
define_rx = re.compile('#define ([A-Z][A-Za-z0-9_]+) (.*)\n')
undef_rx = re.compile('/[*] #undef ([A-Z][A-Za-z0-9_]+) [*]/\n')
#
while True:
line = fp.readline()
@ -303,8 +319,10 @@ def parse_config_h(fp, g=None):
m = define_rx.match(line)
if m:
n, v = m.group(1, 2)
try: v = int(v)
except ValueError: pass
try:
v = int(v)
except ValueError:
pass
g[n] = v
else:
m = undef_rx.match(line)
@ -315,9 +333,10 @@ def parse_config_h(fp, g=None):
# Regexes needed for parsing Makefile (and similar syntaxes,
# like old-style Setup files).
_variable_rx = re.compile(r"([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)")
_findvar1_rx = re.compile(r"\$\(([A-Za-z][A-Za-z0-9_]*)\)")
_findvar2_rx = re.compile(r"\${([A-Za-z][A-Za-z0-9_]*)}")
_variable_rx = re.compile(r'([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)')
_findvar1_rx = re.compile(r'\$\(([A-Za-z][A-Za-z0-9_]*)\)')
_findvar2_rx = re.compile(r'\${([A-Za-z][A-Za-z0-9_]*)}')
def parse_makefile(fn, g=None):
"""Parse a Makefile-style file.
@ -327,7 +346,7 @@ def parse_makefile(fn, g=None):
used instead of a new dictionary.
"""
from distutils.text_file import TextFile
fp = TextFile(fn, strip_comments=1, skip_blanks=1, join_lines=1, errors="surrogateescape")
fp = TextFile(fn, strip_comments=1, skip_blanks=1, join_lines=1, errors='surrogateescape')
if g is None:
g = {}
@ -336,7 +355,7 @@ def parse_makefile(fn, g=None):
while True:
line = fp.readline()
if line is None: # eof
if line is None: # eof
break
m = _variable_rx.match(line)
if m:
@ -345,7 +364,7 @@ def parse_makefile(fn, g=None):
# `$$' is a literal `$' in make
tmpv = v.replace('$$', '')
if "$" in tmpv:
if '$' in tmpv:
notdone[n] = v
else:
try:
@ -381,7 +400,7 @@ def parse_makefile(fn, g=None):
elif n in renamed_variables:
if name.startswith('PY_') and name[3:] in renamed_variables:
item = ""
item = ''
elif 'PY_' + n in notdone:
found = False
@ -389,14 +408,15 @@ def parse_makefile(fn, g=None):
else:
item = str(done['PY_' + n])
else:
done[n] = item = ""
done[n] = item = ''
if found:
after = value[m.end():]
value = value[:m.start()] + item + after
if "$" in after:
if '$' in after:
notdone[name] = value
else:
try: value = int(value)
try:
value = int(value)
except ValueError:
done[name] = value.strip()
else:
@ -404,7 +424,7 @@ def parse_makefile(fn, g=None):
del notdone[name]
if name.startswith('PY_') \
and name[3:] in renamed_variables:
and name[3:] in renamed_variables:
name = name[3:]
if name not in done:
@ -452,21 +472,25 @@ def expand_makefile_vars(s, vars):
_config_vars = None
def _init_posix():
"""Initialize the module as appropriate for POSIX systems."""
# _sysconfigdata is generated at build time, see the sysconfig module
name = os.environ.get('_PYTHON_SYSCONFIGDATA_NAME',
name = os.environ.get(
'_PYTHON_SYSCONFIGDATA_NAME',
'_sysconfigdata_{abi}_{platform}_{multiarch}'.format(
abi=sys.abiflags,
platform=sys.platform,
multiarch=getattr(sys.implementation, '_multiarch', ''),
))
abi=sys.abiflags,
platform=sys.platform,
multiarch=getattr(sys.implementation, '_multiarch', ''),
),
)
try:
_temp = __import__(name, globals(), locals(), ['build_time_vars'], 0)
except ImportError:
# Python 3.5 and pypy 7.3.1
_temp = __import__(
'_sysconfigdata', globals(), locals(), ['build_time_vars'], 0)
'_sysconfigdata', globals(), locals(), ['build_time_vars'], 0,
)
build_time_vars = _temp.build_time_vars
global _config_vars
_config_vars = {}
@ -484,8 +508,8 @@ def _init_nt():
g['INCLUDEPY'] = get_python_inc(plat_specific=0)
g['EXT_SUFFIX'] = _imp.extension_suffixes()[0]
g['EXE'] = ".exe"
g['VERSION'] = get_python_version().replace(".", "")
g['EXE'] = '.exe'
g['VERSION'] = get_python_version().replace('.', '')
g['BINDIR'] = os.path.dirname(os.path.abspath(sys.executable))
global _config_vars
@ -504,7 +528,7 @@ def get_config_vars(*args):
"""
global _config_vars
if _config_vars is None:
func = globals().get("_init_" + os.name)
func = globals().get('_init_' + os.name)
if func:
func()
else:
@ -543,10 +567,12 @@ def get_config_vars(*args):
# Normally it is relative to the build directory. However, during
# testing, for example, we might be running a non-installed python
# from a different directory.
if python_build and os.name == "posix":
if python_build and os.name == 'posix':
base = project_base
if (not os.path.isabs(_config_vars['srcdir']) and
base != os.getcwd()):
if (
not os.path.isabs(_config_vars['srcdir']) and
base != os.getcwd()
):
# srcdir is relative and we are not in the same directory
# as the executable. Assume executable is in the build
# directory and make srcdir absolute.
@ -567,6 +593,7 @@ def get_config_vars(*args):
else:
return _config_vars
def get_config_var(name):
"""Return the value of a single variable using the dictionary
returned by 'get_config_vars()'. Equivalent to

View file

@ -3,8 +3,10 @@
provides the TextFile class, which gives an interface to text files
that (optionally) takes care of stripping comments, ignoring blank
lines, and joining lines with backslashes."""
from __future__ import annotations
import sys, io
import io
import sys
class TextFile:
@ -66,14 +68,14 @@ class TextFile:
an all-whitespace line), if 'rstrip_ws' is true but 'skip_blanks' is
not."""
default_options = { 'strip_comments': 1,
'skip_blanks': 1,
'lstrip_ws': 0,
'rstrip_ws': 1,
'join_lines': 0,
'collapse_join': 0,
'errors': 'strict',
}
default_options = {'strip_comments': 1,
'skip_blanks': 1,
'lstrip_ws': 0,
'rstrip_ws': 1,
'join_lines': 0,
'collapse_join': 0,
'errors': 'strict',
}
def __init__(self, filename=None, file=None, **options):
"""Construct a new TextFile object. At least one of 'filename'
@ -112,7 +114,7 @@ class TextFile:
"""Open a new file named 'filename'. This overrides both the
'filename' and 'file' arguments to the constructor."""
self.filename = filename
self.file = io.open(self.filename, 'r', errors=self.errors)
self.file = open(self.filename, errors=self.errors)
self.current_line = 0
def close(self):
@ -128,16 +130,16 @@ class TextFile:
outmsg = []
if line is None:
line = self.current_line
outmsg.append(self.filename + ", ")
outmsg.append(self.filename + ', ')
if isinstance(line, (list, tuple)):
outmsg.append("lines %d-%d: " % tuple(line))
outmsg.append('lines %d-%d: ' % tuple(line))
else:
outmsg.append("line %d: " % line)
outmsg.append('line %d: ' % line)
outmsg.append(str(msg))
return "".join(outmsg)
return ''.join(outmsg)
def error(self, msg, line=None):
raise ValueError("error: " + self.gen_error(msg, line))
raise ValueError('error: ' + self.gen_error(msg, line))
def warn(self, msg, line=None):
"""Print (to stderr) a warning message tied to the current logical
@ -147,7 +149,7 @@ class TextFile:
the current line number; it may be a list or tuple to indicate a
range of physical lines, or an integer for a single physical
line."""
sys.stderr.write("warning: " + self.gen_error(msg, line) + "\n")
sys.stderr.write('warning: ' + self.gen_error(msg, line) + '\n')
def readline(self):
"""Read and return a single logical line from the current file (or
@ -186,13 +188,13 @@ class TextFile:
# unescape it (and any other escaped "#"'s that might be
# lurking in there) and otherwise leave the line alone.
pos = line.find("#")
if pos == -1: # no "#" -- no comments
pos = line.find('#')
if pos == -1: # no "#" -- no comments
pass
# It's definitely a comment -- either "#" is the first
# character, or it's elsewhere and unescaped.
elif pos == 0 or line[pos-1] != "\\":
elif pos == 0 or line[pos - 1] != '\\':
# Have to preserve the trailing newline, because it's
# the job of a later step (rstrip_ws) to remove it --
# and if rstrip_ws is false, we'd better preserve it!
@ -209,17 +211,19 @@ class TextFile:
# # comment that should be ignored
# there
# result in "hello there".
if line.strip() == "":
if line.strip() == '':
continue
else: # it's an escaped "#"
line = line.replace("\\#", "#")
else: # it's an escaped "#"
line = line.replace('\\#', '#')
# did previous line end with a backslash? then accumulate
if self.join_lines and buildup_line:
# oops: end of file
if line is None:
self.warn("continuation line immediately precedes "
"end-of-file")
self.warn(
'continuation line immediately precedes '
'end-of-file',
)
return buildup_line
if self.collapse_join:
@ -230,11 +234,13 @@ class TextFile:
if isinstance(self.current_line, list):
self.current_line[1] = self.current_line[1] + 1
else:
self.current_line = [self.current_line,
self.current_line + 1]
self.current_line = [
self.current_line,
self.current_line + 1,
]
# just an ordinary line, read it as usual
else:
if line is None: # eof
if line is None: # eof
return None
# still have to be careful about incrementing the line number!

View file

@ -12,16 +12,23 @@ the "typical" Unix-style command-line C compiler:
* link static library handled by 'ar' command (possibly with 'ranlib')
* link shared library handled by 'cc -shared'
"""
from __future__ import annotations
import os, sys, re, shlex
import os
import re
import shlex
import sys
from distutils import sysconfig
from distutils.dep_util import newer
from distutils.ccompiler import \
CCompiler, gen_preprocess_options, gen_lib_options
from distutils.errors import \
DistutilsExecError, CompileError, LibError, LinkError
from distutils import log
from distutils import sysconfig
from distutils.ccompiler import CCompiler
from distutils.ccompiler import gen_lib_options
from distutils.ccompiler import gen_preprocess_options
from distutils.dep_util import newer
from distutils.errors import CompileError
from distutils.errors import DistutilsExecError
from distutils.errors import LibError
from distutils.errors import LinkError
if sys.platform == 'darwin':
import _osx_support
@ -52,18 +59,19 @@ class UnixCCompiler(CCompiler):
# are pretty generic; they will probably have to be set by an outsider
# (eg. using information discovered by the sysconfig about building
# Python extensions).
executables = {'preprocessor' : None,
'compiler' : ["cc"],
'compiler_so' : ["cc"],
'compiler_cxx' : ["cc"],
'linker_so' : ["cc", "-shared"],
'linker_exe' : ["cc"],
'archiver' : ["ar", "-cr"],
'ranlib' : None,
}
executables = {
'preprocessor': None,
'compiler': ['cc'],
'compiler_so': ['cc'],
'compiler_cxx': ['cc'],
'linker_so': ['cc', '-shared'],
'linker_exe': ['cc'],
'archiver': ['ar', '-cr'],
'ranlib': None,
}
if sys.platform[:6] == "darwin":
executables['ranlib'] = ["ranlib"]
if sys.platform[:6] == 'darwin':
executables['ranlib'] = ['ranlib']
# Needed for the filename generation methods provided by the base
# class, CCompiler. NB. whoever instantiates/uses a particular
@ -71,19 +79,21 @@ class UnixCCompiler(CCompiler):
# reasonable common default here, but it's not necessarily used on all
# Unices!
src_extensions = [".c",".C",".cc",".cxx",".cpp",".m"]
obj_extension = ".o"
static_lib_extension = ".a"
shared_lib_extension = ".so"
dylib_lib_extension = ".dylib"
xcode_stub_lib_extension = ".tbd"
static_lib_format = shared_lib_format = dylib_lib_format = "lib%s%s"
src_extensions = ['.c', '.C', '.cc', '.cxx', '.cpp', '.m']
obj_extension = '.o'
static_lib_extension = '.a'
shared_lib_extension = '.so'
dylib_lib_extension = '.dylib'
xcode_stub_lib_extension = '.tbd'
static_lib_format = shared_lib_format = dylib_lib_format = 'lib%s%s'
xcode_stub_lib_format = dylib_lib_format
if sys.platform == "cygwin":
exe_extension = ".exe"
if sys.platform == 'cygwin':
exe_extension = '.exe'
def preprocess(self, source, output_file=None, macros=None,
include_dirs=None, extra_preargs=None, extra_postargs=None):
def preprocess(
self, source, output_file=None, macros=None,
include_dirs=None, extra_preargs=None, extra_postargs=None,
):
fixed_args = self._fix_compile_args(None, macros, include_dirs)
ignore, macros, include_dirs = fixed_args
pp_opts = gen_preprocess_options(macros, include_dirs)
@ -111,16 +121,22 @@ class UnixCCompiler(CCompiler):
def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts):
compiler_so = self.compiler_so
if sys.platform == 'darwin':
compiler_so = _osx_support.compiler_fixup(compiler_so,
cc_args + extra_postargs)
compiler_so = _osx_support.compiler_fixup(
compiler_so,
cc_args + extra_postargs,
)
try:
self.spawn(compiler_so + cc_args + [src, '-o', obj] +
extra_postargs)
self.spawn(
compiler_so + cc_args + [src, '-o', obj] +
extra_postargs,
)
except DistutilsExecError as msg:
raise CompileError(msg)
def create_static_lib(self, objects, output_libname,
output_dir=None, debug=0, target_lang=None):
def create_static_lib(
self, objects, output_libname,
output_dir=None, debug=0, target_lang=None,
):
objects, output_dir = self._fix_object_args(objects, output_dir)
output_filename = \
@ -128,9 +144,11 @@ class UnixCCompiler(CCompiler):
if self._need_link(objects, output_filename):
self.mkpath(os.path.dirname(output_filename))
self.spawn(self.archiver +
[output_filename] +
objects + self.objects)
self.spawn(
self.archiver +
[output_filename] +
objects + self.objects,
)
# Not many Unices required ranlib anymore -- SunOS 4.x is, I
# think the only major Unix that does. Maybe we need some
@ -143,28 +161,36 @@ class UnixCCompiler(CCompiler):
except DistutilsExecError as msg:
raise LibError(msg)
else:
log.debug("skipping %s (up-to-date)", output_filename)
log.debug('skipping %s (up-to-date)', output_filename)
def link(self, target_desc, objects,
output_filename, output_dir=None, libraries=None,
library_dirs=None, runtime_library_dirs=None,
export_symbols=None, debug=0, extra_preargs=None,
extra_postargs=None, build_temp=None, target_lang=None):
def link(
self, target_desc, objects,
output_filename, output_dir=None, libraries=None,
library_dirs=None, runtime_library_dirs=None,
export_symbols=None, debug=0, extra_preargs=None,
extra_postargs=None, build_temp=None, target_lang=None,
):
objects, output_dir = self._fix_object_args(objects, output_dir)
fixed_args = self._fix_lib_args(libraries, library_dirs,
runtime_library_dirs)
fixed_args = self._fix_lib_args(
libraries, library_dirs,
runtime_library_dirs,
)
libraries, library_dirs, runtime_library_dirs = fixed_args
lib_opts = gen_lib_options(self, library_dirs, runtime_library_dirs,
libraries)
lib_opts = gen_lib_options(
self, library_dirs, runtime_library_dirs,
libraries,
)
if not isinstance(output_dir, (str, type(None))):
raise TypeError("'output_dir' must be a string or None")
if output_dir is not None:
output_filename = os.path.join(output_dir, output_filename)
if self._need_link(objects, output_filename):
ld_args = (objects + self.objects +
lib_opts + ['-o', output_filename])
ld_args = (
objects + self.objects +
lib_opts + ['-o', output_filename]
)
if debug:
ld_args[:0] = ['-g']
if extra_preargs:
@ -177,14 +203,14 @@ class UnixCCompiler(CCompiler):
linker = self.linker_exe[:]
else:
linker = self.linker_so[:]
if target_lang == "c++" and self.compiler_cxx:
if target_lang == 'c++' and self.compiler_cxx:
# skip over environment variable settings if /usr/bin/env
# is used to set up the linker's environment.
# This is needed on OSX. Note: this assumes that the
# normal and C++ compiler have the same environment
# settings.
i = 0
if os.path.basename(linker[0]) == "env":
if os.path.basename(linker[0]) == 'env':
i = 1
while '=' in linker[i]:
i += 1
@ -196,7 +222,7 @@ class UnixCCompiler(CCompiler):
else:
offset = 0
linker[i+offset] = self.compiler_cxx[i]
linker[i + offset] = self.compiler_cxx[i]
if sys.platform == 'darwin':
linker = _osx_support.compiler_fixup(linker, ld_args)
@ -205,17 +231,17 @@ class UnixCCompiler(CCompiler):
except DistutilsExecError as msg:
raise LinkError(msg)
else:
log.debug("skipping %s (up-to-date)", output_filename)
log.debug('skipping %s (up-to-date)', output_filename)
# -- Miscellaneous methods -----------------------------------------
# These are all used by the 'gen_lib_options() function, in
# ccompiler.py.
def library_dir_option(self, dir):
return "-L" + dir
return '-L' + dir
def _is_gcc(self, compiler_name):
return "gcc" in compiler_name or "g++" in compiler_name
return 'gcc' in compiler_name or 'g++' in compiler_name
def runtime_library_dir_option(self, dir):
# XXX Hackish, at the very least. See Python bug #445902:
@ -231,40 +257,40 @@ class UnixCCompiler(CCompiler):
# this time, there's no way to determine this information from
# the configuration data stored in the Python installation, so
# we use this hack.
compiler = os.path.basename(shlex.split(sysconfig.get_config_var("CC"))[0])
if sys.platform[:6] == "darwin":
compiler = os.path.basename(shlex.split(sysconfig.get_config_var('CC'))[0])
if sys.platform[:6] == 'darwin':
from distutils.util import get_macosx_target_ver, split_version
macosx_target_ver = get_macosx_target_ver()
if macosx_target_ver and split_version(macosx_target_ver) >= [10, 5]:
return "-Wl,-rpath," + dir
else: # no support for -rpath on earlier macOS versions
return "-L" + dir
elif sys.platform[:7] == "freebsd":
return "-Wl,-rpath=" + dir
elif sys.platform[:5] == "hp-ux":
return '-Wl,-rpath,' + dir
else: # no support for -rpath on earlier macOS versions
return '-L' + dir
elif sys.platform[:7] == 'freebsd':
return '-Wl,-rpath=' + dir
elif sys.platform[:5] == 'hp-ux':
if self._is_gcc(compiler):
return ["-Wl,+s", "-L" + dir]
return ["+s", "-L" + dir]
return ['-Wl,+s', '-L' + dir]
return ['+s', '-L' + dir]
else:
if self._is_gcc(compiler):
# gcc on non-GNU systems does not need -Wl, but can
# use it anyway. Since distutils has always passed in
# -Wl whenever gcc was used in the past it is probably
# safest to keep doing so.
if sysconfig.get_config_var("GNULD") == "yes":
if sysconfig.get_config_var('GNULD') == 'yes':
# GNU ld needs an extra option to get a RUNPATH
# instead of just an RPATH.
return "-Wl,--enable-new-dtags,-R" + dir
return '-Wl,--enable-new-dtags,-R' + dir
else:
return "-Wl,-R" + dir
return '-Wl,-R' + dir
else:
# No idea how --enable-new-dtags would be passed on to
# ld if this system was using GNU ld. Don't know if a
# system like this even exists.
return "-R" + dir
return '-R' + dir
def library_option(self, lib):
return "-l" + lib
return '-l' + lib
def find_library_file(self, dirs, lib, debug=0):
shared_f = self.library_filename(lib, lib_type='shared')
@ -298,8 +324,6 @@ class UnixCCompiler(CCompiler):
else:
sysroot = m.group(1)
for dir in dirs:
shared = os.path.join(dir, shared_f)
dylib = os.path.join(dir, dylib_f)
@ -308,7 +332,9 @@ class UnixCCompiler(CCompiler):
if sys.platform == 'darwin' and (
dir.startswith('/System/') or (
dir.startswith('/usr/') and not dir.startswith('/usr/local/'))):
dir.startswith('/usr/') and not dir.startswith('/usr/local/')
)
):
shared = os.path.join(sysroot, dir[1:], shared_f)
dylib = os.path.join(sysroot, dir[1:], dylib_f)

View file

@ -3,17 +3,20 @@
Miscellaneous utility functions -- anything that doesn't fit into
one of the other *util.py modules.
"""
from __future__ import annotations
import importlib.util
import os
import re
import importlib.util
import string
import sys
from distutils.errors import DistutilsPlatformError
from distutils.dep_util import newer
from distutils.spawn import spawn
from distutils import log
from distutils.dep_util import newer
from distutils.errors import DistutilsByteCompileError
from distutils.errors import DistutilsPlatformError
from distutils.spawn import spawn
from .py35compat import _optim_args_from_interpreter_flags
@ -47,10 +50,10 @@ def get_host_platform():
return sys.platform
# Set for cross builds explicitly
if "_PYTHON_HOST_PLATFORM" in os.environ:
return os.environ["_PYTHON_HOST_PLATFORM"]
if '_PYTHON_HOST_PLATFORM' in os.environ:
return os.environ['_PYTHON_HOST_PLATFORM']
if os.name != "posix" or not hasattr(os, 'uname'):
if os.name != 'posix' or not hasattr(os, 'uname'):
# XXX what about the architecture? NT is Intel or Alpha,
# Mac OS is M68k or PPC, etc.
return sys.platform
@ -65,44 +68,47 @@ def get_host_platform():
machine = machine.replace(' ', '_')
machine = machine.replace('/', '-')
if osname[:5] == "linux":
if osname[:5] == 'linux':
# At least on Linux/Intel, 'machine' is the processor --
# i386, etc.
# XXX what about Alpha, SPARC, etc?
return "%s-%s" % (osname, machine)
elif osname[:5] == "sunos":
if release[0] >= "5": # SunOS 5 == Solaris 2
osname = "solaris"
release = "%d.%s" % (int(release[0]) - 3, release[2:])
return '{}-{}'.format(osname, machine)
elif osname[:5] == 'sunos':
if release[0] >= '5': # SunOS 5 == Solaris 2
osname = 'solaris'
release = '%d.%s' % (int(release[0]) - 3, release[2:])
# We can't use "platform.architecture()[0]" because a
# bootstrap problem. We use a dict to get an error
# if some suspicious happens.
bitness = {2147483647:"32bit", 9223372036854775807:"64bit"}
machine += ".%s" % bitness[sys.maxsize]
bitness = {2147483647: '32bit', 9223372036854775807: '64bit'}
machine += '.%s' % bitness[sys.maxsize]
# fall through to standard osname-release-machine representation
elif osname[:3] == "aix":
elif osname[:3] == 'aix':
from .py38compat import aix_platform
return aix_platform(osname, version, release)
elif osname[:6] == "cygwin":
osname = "cygwin"
rel_re = re.compile (r'[\d.]+', re.ASCII)
elif osname[:6] == 'cygwin':
osname = 'cygwin'
rel_re = re.compile(r'[\d.]+', re.ASCII)
m = rel_re.match(release)
if m:
release = m.group()
elif osname[:6] == "darwin":
import _osx_support, distutils.sysconfig
elif osname[:6] == 'darwin':
import _osx_support
import distutils.sysconfig
osname, release, machine = _osx_support.get_platform_osx(
distutils.sysconfig.get_config_vars(),
osname, release, machine)
distutils.sysconfig.get_config_vars(),
osname, release, machine,
)
return '{}-{}-{}'.format(osname, release, machine)
return "%s-%s-%s" % (osname, release, machine)
def get_platform():
if os.name == 'nt':
TARGET_TO_PLAT = {
'x86' : 'win32',
'x64' : 'win-amd64',
'arm' : 'win-arm32',
'x86': 'win32',
'x64': 'win-amd64',
'arm': 'win-arm32',
'arm64': 'win-arm64',
}
return TARGET_TO_PLAT.get(os.environ.get('VSCMD_ARG_TGT_ARCH')) or get_host_platform()
@ -111,14 +117,16 @@ def get_platform():
if sys.platform == 'darwin':
_syscfg_macosx_ver = None # cache the version pulled from sysconfig
_syscfg_macosx_ver = None # cache the version pulled from sysconfig
MACOSX_VERSION_VAR = 'MACOSX_DEPLOYMENT_TARGET'
def _clear_cached_macosx_ver():
"""For testing only. Do not call."""
global _syscfg_macosx_ver
_syscfg_macosx_ver = None
def get_macosx_target_ver_from_syscfg():
"""Get the version of macOS latched in the Python interpreter configuration.
Returns the version as a string or None if can't obtain one. Cached."""
@ -130,6 +138,7 @@ def get_macosx_target_ver_from_syscfg():
_syscfg_macosx_ver = ver
return _syscfg_macosx_ver
def get_macosx_target_ver():
"""Return the version of macOS for which we are building.
@ -148,11 +157,13 @@ def get_macosx_target_ver():
# values, specifically LDSHARED which can use
# '-undefined dynamic_lookup' which only works on >= 10.3.
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))
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
@ -163,7 +174,7 @@ def split_version(s):
return [int(n) for n in s.split('.')]
def convert_path (pathname):
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
@ -191,7 +202,7 @@ def convert_path (pathname):
# convert_path ()
def change_root (new_root, pathname):
def change_root(new_root, pathname):
"""Return 'pathname' with 'new_root' prepended. If 'pathname' is
relative, this is equivalent to "os.path.join(new_root,pathname)".
Otherwise, it requires making 'pathname' relative and then joining the
@ -214,7 +225,9 @@ def change_root (new_root, pathname):
_environ_checked = 0
def check_environ ():
def check_environ():
"""Ensure that 'os.environ' has all the environment variables we
guarantee that users can use in config files, command-line options,
etc. Currently this includes:
@ -241,7 +254,7 @@ def check_environ ():
_environ_checked = 1
def subst_vars (s, local_vars):
def subst_vars(s, local_vars):
"""Perform shell/Perl-style variable substitution on 'string'. Every
occurrence of '$' followed by a name is considered a variable, and
variable is substituted by the value found in the 'local_vars'
@ -251,7 +264,8 @@ def subst_vars (s, local_vars):
variables not found in either 'local_vars' or 'os.environ'.
"""
check_environ()
def _subst (match, local_vars=local_vars):
def _subst(match, local_vars=local_vars):
var_name = match.group(1)
if var_name in local_vars:
return str(local_vars[var_name])
@ -266,7 +280,7 @@ def subst_vars (s, local_vars):
# subst_vars ()
def grok_environment_error (exc, prefix="error: "):
def grok_environment_error(exc, prefix='error: '):
# Function kept for backward compatibility.
# Used to try clever things with EnvironmentErrors,
# but nowadays str(exception) produces good messages.
@ -275,13 +289,16 @@ def grok_environment_error (exc, prefix="error: "):
# Needed by 'split_quoted()'
_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):
def split_quoted(s):
"""Split a string up according to Unix shell-like rules for quotes and
backslashes. In short: words are delimited by spaces, as long as those
spaces are not escaped by a backslash, or inside a quoted string.
@ -295,7 +312,8 @@ def split_quoted (s):
# This is a nice algorithm for splitting up a single string, since it
# doesn't require character-by-character examination. It was a little
# bit of a brain-bender to get it working right, though...
if _wordchars_re is None: _init_regex()
if _wordchars_re is None:
_init_regex()
s = s.strip()
words = []
@ -308,15 +326,15 @@ def split_quoted (s):
words.append(s[:end])
break
if s[end] in string.whitespace: # unescaped, unquoted whitespace: now
if s[end] in string.whitespace: # unescaped, unquoted whitespace: now
words.append(s[:end]) # we definitely have a word delimiter
s = s[end:].lstrip()
pos = 0
elif s[end] == '\\': # preserve whatever is being escaped;
# will become part of the current word
s = s[:end] + s[end+1:]
pos = end+1
# will become part of the current word
s = s[:end] + s[end + 1:]
pos = end + 1
else:
if s[end] == "'": # slurp singly-quoted string
@ -327,10 +345,10 @@ 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('bad string (mismatched %s quotes?)' % s[end])
(beg, end) = m.span()
s = s[:beg] + s[beg+1:end-1] + s[end:]
s = s[:beg] + s[beg + 1:end - 1] + s[end:]
pos = m.end() - 2
if pos >= len(s):
@ -342,7 +360,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=0, dry_run=0):
"""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
@ -352,7 +370,7 @@ def execute (func, args, msg=None, verbose=0, dry_run=0):
print.
"""
if msg is None:
msg = "%s%r" % (func.__name__, args)
msg = '{}{!r}'.format(func.__name__, args)
if msg[-2:] == ',)': # correct for singleton tuple
msg = msg[0:-2] + ')'
@ -361,7 +379,7 @@ def execute (func, args, msg=None, verbose=0, dry_run=0):
func(*args)
def strtobool (val):
def strtobool(val):
"""Convert a string representation of truth to true (1) or false (0).
True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values
@ -374,14 +392,16 @@ def strtobool (val):
elif val in ('n', 'no', 'f', 'false', 'off', '0'):
return 0
else:
raise ValueError("invalid truth value %r" % (val,))
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):
def byte_compile(
py_files,
optimize=0, force=0,
prefix=None, base_dir=None,
verbose=1, dry_run=0,
direct=None,
):
"""Byte-compile a collection of Python source files to .pyc
files in a __pycache__ subdirectory. 'py_files' is a list
of files to compile; any files that don't end in ".py" are silently
@ -437,16 +457,16 @@ def byte_compile (py_files,
if not direct:
try:
from tempfile import mkstemp
(script_fd, script_name) = mkstemp(".py")
(script_fd, script_name) = mkstemp('.py')
except ImportError:
from tempfile import mktemp
(script_fd, script_name) = None, mktemp(".py")
(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")
script = os.fdopen(script_fd, 'w')
else:
script = open(script_name, "w")
script = open(script_name, 'w')
with script:
script.write("""\
@ -468,20 +488,24 @@ files = [
#if prefix:
# prefix = os.path.abspath(prefix)
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,
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))
""".format(optimize, force, prefix, base_dir, verbose),
)
cmd = [sys.executable]
cmd.extend(_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,), 'removing %s' % 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
@ -491,7 +515,7 @@ byte_compile(files, optimize=%r, force=%r,
from py_compile import compile
for file in py_files:
if file[-3:] != ".py":
if file[-3:] != '.py':
# This lets us be lazy and not filter filenames in
# the "install_lib" command.
continue
@ -502,14 +526,17 @@ byte_compile(files, optimize=%r, force=%r,
if optimize >= 0:
opt = '' if optimize == 0 else optimize
cfile = importlib.util.cache_from_source(
file, optimization=opt)
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))
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)
@ -517,16 +544,19 @@ byte_compile(files, optimize=%r, force=%r,
cfile_base = os.path.basename(cfile)
if direct:
if force or newer(file, cfile):
log.info("byte-compiling %s to %s", file, cfile_base)
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)
log.debug(
'skipping byte-compilation of %s to %s',
file, cfile_base,
)
# byte_compile ()
def rfc822_escape (header):
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.
"""

View file

@ -6,7 +6,6 @@
#
# $Id$
#
"""Provides classes to represent module version numbers (one class for
each style of version numbering). There are currently two such classes
implemented: StrictVersion and LooseVersion.
@ -25,9 +24,11 @@ Every version number class implements the following interface:
of the same class or a string (which will be parsed to an instance
of the same class, thus must follow the same rules)
"""
from __future__ import annotations
import re
class Version:
"""Abstract base class for version numbering classes. Just provides
constructor (__init__) and reproducer (__repr__), because those
@ -35,12 +36,12 @@ class Version:
rich comparisons to _cmp.
"""
def __init__ (self, vstring=None):
def __init__(self, vstring=None):
if vstring:
self.parse(vstring)
def __repr__ (self):
return "%s ('%s')" % (self.__class__.__name__, str(self))
def __repr__(self):
return "{} ('{}')".format(self.__class__.__name__, str(self))
def __eq__(self, other):
c = self._cmp(other)
@ -127,11 +128,12 @@ class StrictVersion (Version):
in the distutils documentation.
"""
version_re = re.compile(r'^(\d+) \. (\d+) (\. (\d+))? ([ab](\d+))?$',
re.VERBOSE | re.ASCII)
version_re = re.compile(
r'^(\d+) \. (\d+) (\. (\d+))? ([ab](\d+))?$',
re.VERBOSE | re.ASCII,
)
def parse (self, vstring):
def parse(self, vstring):
match = self.version_re.match(vstring)
if not match:
raise ValueError("invalid version number '%s'" % vstring)
@ -149,8 +151,7 @@ class StrictVersion (Version):
else:
self.prerelease = None
def __str__ (self):
def __str__(self):
if self.version[2] == 0:
vstring = '.'.join(map(str, self.version[0:2]))
@ -162,8 +163,7 @@ class StrictVersion (Version):
return vstring
def _cmp (self, other):
def _cmp(self, other):
if isinstance(other, str):
other = StrictVersion(other)
elif not isinstance(other, StrictVersion):
@ -197,7 +197,7 @@ class StrictVersion (Version):
else:
return 1
else:
assert False, "never get here"
assert False, 'never get here'
# end class StrictVersion
@ -301,18 +301,19 @@ class LooseVersion (Version):
component_re = re.compile(r'(\d+ | [a-z]+ | \.)', re.VERBOSE)
def __init__ (self, vstring=None):
def __init__(self, vstring=None):
if vstring:
self.parse(vstring)
def parse (self, vstring):
def parse(self, vstring):
# I've given up on thinking I can reconstruct the version string
# from the parsed tuple -- so I just store the string here for
# use by __str__
self.vstring = vstring
components = [x for x in self.component_re.split(vstring)
if x and x != '.']
components = [
x for x in self.component_re.split(vstring)
if x and x != '.'
]
for i, obj in enumerate(components):
try:
components[i] = int(obj)
@ -321,16 +322,13 @@ class LooseVersion (Version):
self.version = components
def __str__ (self):
def __str__(self):
return self.vstring
def __repr__ (self):
def __repr__(self):
return "LooseVersion ('%s')" % str(self)
def _cmp (self, other):
def _cmp(self, other):
if isinstance(other, str):
other = LooseVersion(other)
elif not isinstance(other, LooseVersion):

View file

@ -1,16 +1,21 @@
"""Module for parsing and testing package version predicate strings.
"""
import re
import distutils.version
from __future__ import annotations
import operator
import re
import distutils.version
re_validPackage = re.compile(r"(?i)^\s*([a-z_]\w*(?:\.[a-z_]\w*)*)(.*)",
re.ASCII)
re_validPackage = re.compile(
r'(?i)^\s*([a-z_]\w*(?:\.[a-z_]\w*)*)(.*)',
re.ASCII,
)
# (package) (rest)
re_paren = re.compile(r"^\s*\((.*)\)\s*$") # (list) inside of parentheses
re_splitComparison = re.compile(r"^\s*(<=|>=|<|>|!=|==)\s*([^\s,]+)\s*$")
re_paren = re.compile(r'^\s*\((.*)\)\s*$') # (list) inside of parentheses
re_splitComparison = re.compile(r'^\s*(<=|>=|<|>|!=|==)\s*([^\s,]+)\s*$')
# (comp) (version)
@ -21,12 +26,16 @@ def splitUp(pred):
"""
res = re_splitComparison.match(pred)
if not res:
raise ValueError("bad package restriction syntax: %r" % pred)
raise ValueError('bad package restriction syntax: %r' % pred)
comp, verStr = res.groups()
return (comp, distutils.version.StrictVersion(verStr))
compmap = {"<": operator.lt, "<=": operator.le, "==": operator.eq,
">": operator.gt, ">=": operator.ge, "!=": operator.ne}
compmap = {
'<': operator.lt, '<=': operator.le, '==': operator.eq,
'>': operator.gt, '>=': operator.ge, '!=': operator.ne,
}
class VersionPredicate:
"""Parse and test package version predicates.
@ -102,28 +111,30 @@ class VersionPredicate:
versionPredicateStr = versionPredicateStr.strip()
if not versionPredicateStr:
raise ValueError("empty package restriction")
raise ValueError('empty package restriction')
match = re_validPackage.match(versionPredicateStr)
if not match:
raise ValueError("bad package name in %r" % versionPredicateStr)
raise ValueError('bad package name in %r' % versionPredicateStr)
self.name, paren = match.groups()
paren = paren.strip()
if paren:
match = re_paren.match(paren)
if not match:
raise ValueError("expected parenthesized list: %r" % paren)
raise ValueError('expected parenthesized list: %r' % paren)
str = match.groups()[0]
self.pred = [splitUp(aPred) for aPred in str.split(",")]
self.pred = [splitUp(aPred) for aPred in str.split(',')]
if not self.pred:
raise ValueError("empty parenthesized list in %r"
% versionPredicateStr)
raise ValueError(
'empty parenthesized list in %r'
% versionPredicateStr,
)
else:
self.pred = []
def __str__(self):
if self.pred:
seq = [cond + " " + str(ver) for cond, ver in self.pred]
return self.name + " (" + ", ".join(seq) + ")"
seq = [cond + ' ' + str(ver) for cond, ver in self.pred]
return self.name + ' (' + ', '.join(seq) + ')'
else:
return self.name
@ -140,6 +151,7 @@ class VersionPredicate:
_provision_rx = None
def split_provision(value):
"""Return the name and optional version number of a provision.
@ -154,12 +166,13 @@ def split_provision(value):
global _provision_rx
if _provision_rx is None:
_provision_rx = re.compile(
r"([a-zA-Z_]\w*(?:\.[a-zA-Z_]\w*)*)(?:\s*\(\s*([^)\s]+)\s*\))?$",
re.ASCII)
r'([a-zA-Z_]\w*(?:\.[a-zA-Z_]\w*)*)(?:\s*\(\s*([^)\s]+)\s*\))?$',
re.ASCII,
)
value = value.strip()
m = _provision_rx.match(value)
if not m:
raise ValueError("illegal provides specification: %r" % value)
raise ValueError('illegal provides specification: %r' % value)
ver = m.group(2) or None
if ver:
ver = distutils.version.StrictVersion(ver)

View file

@ -2,10 +2,11 @@
Re-implementation of find_module and get_frozen_object
from the deprecated imp module.
"""
from __future__ import annotations
import os
import importlib.util
import importlib.machinery
import importlib.util
import os
from .py34compat import module_from_spec
@ -38,12 +39,14 @@ def find_module(module, paths=None):
file = None
static = isinstance(spec.loader, type)
if spec.origin == 'frozen' or static and issubclass(
spec.loader, importlib.machinery.FrozenImporter):
spec.loader, importlib.machinery.FrozenImporter,
):
kind = PY_FROZEN
path = None # imp compabilty
suffix = mode = '' # imp compatibility
elif spec.origin == 'built-in' or static and issubclass(
spec.loader, importlib.machinery.BuiltinImporter):
spec.loader, importlib.machinery.BuiltinImporter,
):
kind = C_BUILTIN
path = None # imp compabilty
suffix = mode = '' # imp compatibility

View file

@ -1,3 +1,5 @@
from __future__ import annotations
from .more import * # noqa
from .recipes import * # noqa

View file

@ -1,39 +1,55 @@
import warnings
from __future__ import annotations
from collections import Counter, defaultdict, deque, abc
import warnings
from collections import abc
from collections import Counter
from collections import defaultdict
from collections import deque
from collections.abc import Sequence
from concurrent.futures import ThreadPoolExecutor
from functools import partial, reduce, wraps
from heapq import merge, heapify, heapreplace, heappop
from itertools import (
chain,
compress,
count,
cycle,
dropwhile,
groupby,
islice,
repeat,
starmap,
takewhile,
tee,
zip_longest,
)
from math import exp, factorial, floor, log
from queue import Empty, Queue
from random import random, randrange, uniform
from operator import itemgetter, mul, sub, gt, lt
from sys import hexversion, maxsize
from functools import partial
from functools import reduce
from functools import wraps
from heapq import heapify
from heapq import heappop
from heapq import heapreplace
from heapq import merge
from itertools import chain
from itertools import compress
from itertools import count
from itertools import cycle
from itertools import dropwhile
from itertools import groupby
from itertools import islice
from itertools import repeat
from itertools import starmap
from itertools import takewhile
from itertools import tee
from itertools import zip_longest
from math import exp
from math import factorial
from math import floor
from math import log
from operator import gt
from operator import itemgetter
from operator import lt
from operator import mul
from operator import sub
from queue import Empty
from queue import Queue
from random import random
from random import randrange
from random import uniform
from sys import hexversion
from sys import maxsize
from time import monotonic
from .recipes import (
consume,
flatten,
pairwise,
powerset,
take,
unique_everseen,
)
from .recipes import consume
from .recipes import flatten
from .recipes import pairwise
from .recipes import powerset
from .recipes import take
from .recipes import unique_everseen
__all__ = [
'AbortThread',
@ -180,7 +196,7 @@ def first(iterable, default=_marker):
if default is _marker:
raise ValueError(
'first() was called on an empty iterable, and no '
'default value was provided.'
'default value was provided.',
) from e
return default
@ -209,7 +225,7 @@ def last(iterable, default=_marker):
if default is _marker:
raise ValueError(
'last() was called on an empty iterable, and no default was '
'provided.'
'provided.',
)
return default
@ -428,7 +444,7 @@ def collate(*iterables, **kwargs):
"""
warnings.warn(
"collate is no longer part of more_itertools, use heapq.merge",
'collate is no longer part of more_itertools, use heapq.merge',
DeprecationWarning,
)
return merge(*iterables, **kwargs)
@ -624,7 +640,7 @@ def distinct_permutations(iterable, r=None):
# Swap the value of A[i] with that of A[j], then reverse the
# sequence from A[i + 1] to form the new permutation
A[i], A[j] = A[j], A[i]
A[i + 1 :] = A[: i - size : -1] # A[i + 1:][::-1]
A[i + 1:] = A[: i - size: -1] # A[i + 1:][::-1]
# Algorithm: modified from the above
def _partial(A, r):
@ -662,9 +678,9 @@ def distinct_permutations(iterable, r=None):
break
# Reverse head[i + 1:] and swap it with tail[:r - (i + 1)]
tail += head[: i - r : -1] # head[i + 1:][::-1]
tail += head[: i - r: -1] # head[i + 1:][::-1]
i += 1
head[i:], tail[:] = tail[: r - i], tail[r - i :]
head[i:], tail[:] = tail[: r - i], tail[r - i:]
items = sorted(iterable)
@ -811,7 +827,7 @@ def substrings(iterable):
# And the rest
for n in range(2, item_count + 1):
for i in range(item_count - n + 1):
yield seq[i : i + n]
yield seq[i: i + n]
def substrings_indexes(seq, reverse=False):
@ -844,7 +860,7 @@ def substrings_indexes(seq, reverse=False):
if reverse:
r = reversed(r)
return (
(seq[i : i + L], i, i + L) for L in r for i in range(len(seq) - L + 1)
(seq[i: i + L], i, i + L) for L in r for i in range(len(seq) - L + 1)
)
@ -1046,9 +1062,9 @@ def collapse(iterable, base_type=None, levels=None):
def walk(node, level):
if (
((levels is not None) and (level > levels))
or isinstance(node, (str, bytes))
or ((base_type is not None) and isinstance(node, base_type))
((levels is not None) and (level > levels)) or
isinstance(node, (str, bytes)) or
((base_type is not None) and isinstance(node, base_type))
):
yield node
return
@ -1146,13 +1162,13 @@ def sliced(seq, n, strict=False):
For non-sliceable iterables, see :func:`chunked`.
"""
iterator = takewhile(len, (seq[i : i + n] for i in count(0, n)))
iterator = takewhile(len, (seq[i: i + n] for i in count(0, n)))
if strict:
def ret():
for _slice in iterator:
if len(_slice) != n:
raise ValueError("seq is not divisible by n.")
raise ValueError('seq is not divisible by n.')
yield _slice
return iter(ret())
@ -1475,7 +1491,7 @@ def stagger(iterable, offsets=(-1, 0, 1), longest=False, fillvalue=None):
children = tee(iterable, len(offsets))
return zip_offset(
*children, offsets=offsets, longest=longest, fillvalue=fillvalue
*children, offsets=offsets, longest=longest, fillvalue=fillvalue,
)
@ -1484,7 +1500,7 @@ class UnequalIterablesError(ValueError):
msg = 'Iterables have different lengths'
if details is not None:
msg += (': index 0 has length {}; index {} has length {}').format(
*details
*details,
)
super().__init__(msg)
@ -1635,17 +1651,18 @@ def sort_together(iterables, key_list=(0,), key=None, reverse=False):
# if key_list contains a single item, pass the item at that offset
# as the only argument to the key function
key_offset = key_list[0]
key_argument = lambda zipped_items: key(zipped_items[key_offset])
def key_argument(zipped_items): return key(zipped_items[key_offset])
else:
# if key_list contains multiple items, use itemgetter to return a
# tuple of items, which we pass as *args to the key function
get_key_items = itemgetter(*key_list)
key_argument = lambda zipped_items: key(
*get_key_items(zipped_items)
def key_argument(zipped_items): return key(
*get_key_items(zipped_items),
)
return list(
zip(*sorted(zip(*iterables), key=key_argument, reverse=reverse))
zip(*sorted(zip(*iterables), key=key_argument, reverse=reverse)),
)
@ -1954,12 +1971,12 @@ class numeric_range(abc.Sequence, abc.Hashable):
elif argc == 0:
raise TypeError(
'numeric_range expected at least '
'1 argument, got {}'.format(argc)
'1 argument, got {}'.format(argc),
)
else:
raise TypeError(
'numeric_range expected at most '
'3 arguments, got {}'.format(argc)
'3 arguments, got {}'.format(argc),
)
self._zero = type(self._step)(0)
@ -1992,9 +2009,9 @@ class numeric_range(abc.Sequence, abc.Hashable):
return empty_self and empty_other # True if both empty
else:
return (
self._start == other._start
and self._step == other._step
and self._get_by_index(-1) == other._get_by_index(-1)
self._start == other._start and
self._step == other._step and
self._get_by_index(-1) == other._get_by_index(-1)
)
else:
return False
@ -2023,7 +2040,7 @@ class numeric_range(abc.Sequence, abc.Hashable):
else:
raise TypeError(
'numeric range indices must be '
'integers or slices, not {}'.format(type(key).__name__)
'integers or slices, not {}'.format(type(key).__name__),
)
def __hash__(self):
@ -2063,19 +2080,19 @@ class numeric_range(abc.Sequence, abc.Hashable):
def __repr__(self):
if self._step == 1:
return "numeric_range({}, {})".format(
repr(self._start), repr(self._stop)
return 'numeric_range({}, {})'.format(
repr(self._start), repr(self._stop),
)
else:
return "numeric_range({}, {}, {})".format(
repr(self._start), repr(self._stop), repr(self._step)
return 'numeric_range({}, {}, {})'.format(
repr(self._start), repr(self._stop), repr(self._step),
)
def __reversed__(self):
return iter(
numeric_range(
self._get_by_index(-1), self._start - self._step, -self._step
)
self._get_by_index(-1), self._start - self._step, -self._step,
),
)
def count(self, value):
@ -2093,13 +2110,13 @@ class numeric_range(abc.Sequence, abc.Hashable):
if r == self._zero:
return int(q)
raise ValueError("{} is not in numeric range".format(value))
raise ValueError(f'{value} is not in numeric range')
def _get_by_index(self, i):
if i < 0:
i += self._len
if i < 0 or i >= self._len:
raise IndexError("numeric range object index out of range")
raise IndexError('numeric range object index out of range')
return self._start + i * self._step
@ -2468,7 +2485,7 @@ def consecutive_groups(iterable, ordering=lambda x: x):
"""
for k, g in groupby(
enumerate(iterable), key=lambda x: x[0] - ordering(x[1])
enumerate(iterable), key=lambda x: x[0] - ordering(x[1]),
):
yield map(itemgetter(1), g)
@ -2557,7 +2574,7 @@ class SequenceView(Sequence):
return len(self._target)
def __repr__(self):
return '{}({})'.format(self.__class__.__name__, repr(self._target))
return f'{self.__class__.__name__}({repr(self._target)})'
class seekable:
@ -3043,7 +3060,7 @@ def set_partitions(iterable, k=None):
if k is not None:
if k < 1:
raise ValueError(
"Can't partition in a negative or zero number of groups"
"Can't partition in a negative or zero number of groups",
)
elif k > n:
return
@ -3060,7 +3077,7 @@ def set_partitions(iterable, k=None):
yield [[e], *p]
for p in set_partitions_helper(M, k):
for i in range(len(p)):
yield p[:i] + [[e] + p[i]] + p[i + 1 :]
yield p[:i] + [[e] + p[i]] + p[i + 1:]
if k is None:
for k in range(1, n + 1):
@ -3225,9 +3242,9 @@ def distinct_combinations(iterable, r):
else:
generators.append(
unique_everseen(
enumerate(pool[cur_idx + 1 :], cur_idx + 1),
enumerate(pool[cur_idx + 1:], cur_idx + 1),
key=itemgetter(1),
)
),
)
level += 1
@ -3493,7 +3510,7 @@ class callback_iter:
q.put((args, kwargs))
self._future = self._executor.submit(
self._func, **{self._callback_kwd: callback}
self._func, **{self._callback_kwd: callback},
)
while True:
@ -3556,8 +3573,8 @@ def windowed_complete(iterable, n):
for i in range(size - n + 1):
beginning = seq[:i]
middle = seq[i : i + n]
end = seq[i + n :]
middle = seq[i: i + n]
end = seq[i + n:]
yield beginning, middle, end

View file

@ -7,22 +7,24 @@ Some backward-compatible usability improvements have been made.
.. [1] http://docs.python.org/library/itertools.html#recipes
"""
from __future__ import annotations
import operator
import warnings
from collections import deque
from itertools import (
chain,
combinations,
count,
cycle,
groupby,
islice,
repeat,
starmap,
tee,
zip_longest,
)
import operator
from random import randrange, sample, choice
from itertools import chain
from itertools import combinations
from itertools import count
from itertools import cycle
from itertools import groupby
from itertools import islice
from itertools import repeat
from itertools import starmap
from itertools import tee
from itertools import zip_longest
from random import choice
from random import randrange
from random import sample
__all__ = [
'all_equal',
@ -290,7 +292,7 @@ def grouper(iterable, n, fillvalue=None):
"""
if isinstance(iterable, int):
warnings.warn(
"grouper expects iterable as first parameter", DeprecationWarning
'grouper expects iterable as first parameter', DeprecationWarning,
)
n, iterable = iterable, n
args = [iter(iterable)] * n

View file

@ -5,6 +5,8 @@ entry has an index that can be looked up.
Based on a recipe originally posted to ActiveState Recipes by Raymond Hettiger,
and released under the MIT license.
"""
from __future__ import annotations
import itertools as it
from collections import deque
@ -13,10 +15,10 @@ try:
from collections.abc import MutableSet, Sequence
except ImportError:
# Python 2.7
from collections import MutableSet, Sequence
from collections.abc import MutableSet, Sequence
SLICE_ALL = slice(None)
__version__ = "3.1"
__version__ = '3.1'
def is_iterable(obj):
@ -33,9 +35,9 @@ def is_iterable(obj):
have an `__iter__` attribute anyway.
"""
return (
hasattr(obj, "__iter__")
and not isinstance(obj, str)
and not isinstance(obj, tuple)
hasattr(obj, '__iter__') and
not isinstance(obj, str) and
not isinstance(obj, tuple)
)
@ -89,7 +91,7 @@ class OrderedSet(MutableSet, Sequence):
return self.copy()
elif is_iterable(index):
return [self.items[i] for i in index]
elif hasattr(index, "__index__") or isinstance(index, slice):
elif hasattr(index, '__index__') or isinstance(index, slice):
result = self.items[index]
if isinstance(result, list):
return self.__class__(result)
@ -181,7 +183,7 @@ class OrderedSet(MutableSet, Sequence):
item_index = self.add(item)
except TypeError:
raise ValueError(
"Argument needs to be an iterable, got %s" % type(sequence)
'Argument needs to be an iterable, got %s' % type(sequence),
)
return item_index
@ -218,7 +220,7 @@ class OrderedSet(MutableSet, Sequence):
3
"""
if not self.items:
raise KeyError("Set is empty")
raise KeyError('Set is empty')
elem = self.items[-1]
del self.items[-1]
@ -274,8 +276,8 @@ class OrderedSet(MutableSet, Sequence):
def __repr__(self):
if not self:
return "%s()" % (self.__class__.__name__,)
return "%s(%r)" % (self.__class__.__name__, list(self))
return '{}()'.format(self.__class__.__name__)
return '{}({!r})'.format(self.__class__.__name__, list(self))
def __eq__(self, other):
"""
@ -484,5 +486,5 @@ class OrderedSet(MutableSet, Sequence):
items_to_add = [item for item in other if item not in self]
items_to_remove = set(other)
self._update_items(
[item for item in self.items if item not in items_to_remove] + items_to_add
[item for item in self.items if item not in items_to_remove] + items_to_add,
)

View file

@ -1,27 +1,27 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
from __future__ import absolute_import, division, print_function
from __future__ import annotations
__all__ = [
"__title__",
"__summary__",
"__uri__",
"__version__",
"__author__",
"__email__",
"__license__",
"__copyright__",
'__title__',
'__summary__',
'__uri__',
'__version__',
'__author__',
'__email__',
'__license__',
'__copyright__',
]
__title__ = "packaging"
__summary__ = "Core utilities for Python packages"
__uri__ = "https://github.com/pypa/packaging"
__title__ = 'packaging'
__summary__ = 'Core utilities for Python packages'
__uri__ = 'https://github.com/pypa/packaging'
__version__ = "20.4"
__version__ = '20.4'
__author__ = "Donald Stufft and individual contributors"
__email__ = "donald@stufft.io"
__author__ = 'Donald Stufft and individual contributors'
__email__ = 'donald@stufft.io'
__license__ = "BSD-2-Clause or Apache-2.0"
__copyright__ = "Copyright 2014-2019 %s" % __author__
__license__ = 'BSD-2-Clause or Apache-2.0'
__copyright__ = 'Copyright 2014-2019 %s' % __author__

View file

@ -1,26 +1,24 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
from __future__ import absolute_import, division, print_function
from __future__ import annotations
from .__about__ import (
__author__,
__copyright__,
__email__,
__license__,
__summary__,
__title__,
__uri__,
__version__,
)
from .__about__ import __author__
from .__about__ import __copyright__
from .__about__ import __email__
from .__about__ import __license__
from .__about__ import __summary__
from .__about__ import __title__
from .__about__ import __uri__
from .__about__ import __version__
__all__ = [
"__title__",
"__summary__",
"__uri__",
"__version__",
"__author__",
"__email__",
"__license__",
"__copyright__",
'__title__',
'__summary__',
'__uri__',
'__version__',
'__author__',
'__email__',
'__license__',
'__copyright__',
]

View file

@ -1,7 +1,7 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
from __future__ import absolute_import, division, print_function
from __future__ import annotations
import sys
@ -35,4 +35,4 @@ def with_metaclass(meta, *bases):
# type: (Type[Any], str, Tuple[Any], Dict[Any, Any]) -> Any
return meta(name, bases, d)
return type.__new__(metaclass, "temporary_class", (), {})
return type.__new__(metaclass, 'temporary_class', (), {})

View file

@ -1,13 +1,13 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
from __future__ import absolute_import, division, print_function
from __future__ import annotations
class InfinityType(object):
class InfinityType:
def __repr__(self):
# type: () -> str
return "Infinity"
return 'Infinity'
def __hash__(self):
# type: () -> int
@ -45,10 +45,10 @@ class InfinityType(object):
Infinity = InfinityType()
class NegativeInfinityType(object):
class NegativeInfinityType:
def __repr__(self):
# type: () -> str
return "-Infinity"
return '-Infinity'
def __hash__(self):
# type: () -> int

View file

@ -25,8 +25,9 @@ In packaging, all static-typing related imports should be guarded as follows:
Ref: https://github.com/python/mypy/issues/3216
"""
from __future__ import annotations
__all__ = ["TYPE_CHECKING", "cast"]
__all__ = ['TYPE_CHECKING', 'cast']
# The TYPE_CHECKING constant defined by the typing module is False at runtime
# but True while type checking.

View file

@ -1,20 +1,27 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
from __future__ import absolute_import, division, print_function
from __future__ import annotations
import operator
import os
import platform
import sys
from setuptools.extern.pyparsing import ParseException, ParseResults, stringStart, stringEnd
from setuptools.extern.pyparsing import ZeroOrMore, Group, Forward, QuotedString
from setuptools.extern.pyparsing import Forward
from setuptools.extern.pyparsing import Group
from setuptools.extern.pyparsing import Literal as L # noqa
from setuptools.extern.pyparsing import ParseException
from setuptools.extern.pyparsing import ParseResults
from setuptools.extern.pyparsing import QuotedString
from setuptools.extern.pyparsing import stringEnd
from setuptools.extern.pyparsing import stringStart
from setuptools.extern.pyparsing import ZeroOrMore
from ._compat import string_types
from ._typing import TYPE_CHECKING
from .specifiers import Specifier, InvalidSpecifier
from .specifiers import InvalidSpecifier
from .specifiers import Specifier
if TYPE_CHECKING: # pragma: no cover
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
@ -23,11 +30,11 @@ if TYPE_CHECKING: # pragma: no cover
__all__ = [
"InvalidMarker",
"UndefinedComparison",
"UndefinedEnvironmentName",
"Marker",
"default_environment",
'InvalidMarker',
'UndefinedComparison',
'UndefinedEnvironmentName',
'Marker',
'default_environment',
]
@ -50,7 +57,7 @@ class UndefinedEnvironmentName(ValueError):
"""
class Node(object):
class Node:
def __init__(self, value):
# type: (Any) -> None
self.value = value
@ -61,7 +68,7 @@ class Node(object):
def __repr__(self):
# type: () -> str
return "<{0}({1!r})>".format(self.__class__.__name__, str(self))
return f'<{self.__class__.__name__}({str(self)!r})>'
def serialize(self):
# type: () -> str
@ -77,7 +84,7 @@ class Variable(Node):
class Value(Node):
def serialize(self):
# type: () -> str
return '"{0}"'.format(self)
return f'"{self}"'
class Op(Node):
@ -87,54 +94,54 @@ class Op(Node):
VARIABLE = (
L("implementation_version")
| L("platform_python_implementation")
| L("implementation_name")
| L("python_full_version")
| L("platform_release")
| L("platform_version")
| L("platform_machine")
| L("platform_system")
| L("python_version")
| L("sys_platform")
| L("os_name")
| L("os.name") # PEP-345
| L("sys.platform") # PEP-345
| L("platform.version") # PEP-345
| L("platform.machine") # PEP-345
| L("platform.python_implementation") # PEP-345
| L("python_implementation") # undocumented setuptools legacy
| L("extra") # PEP-508
L('implementation_version') |
L('platform_python_implementation') |
L('implementation_name') |
L('python_full_version') |
L('platform_release') |
L('platform_version') |
L('platform_machine') |
L('platform_system') |
L('python_version') |
L('sys_platform') |
L('os_name') |
L('os.name') | # PEP-345
L('sys.platform') | # PEP-345
L('platform.version') | # PEP-345
L('platform.machine') | # PEP-345
L('platform.python_implementation') | # PEP-345
L('python_implementation') | # undocumented setuptools legacy
L('extra') # PEP-508
)
ALIASES = {
"os.name": "os_name",
"sys.platform": "sys_platform",
"platform.version": "platform_version",
"platform.machine": "platform_machine",
"platform.python_implementation": "platform_python_implementation",
"python_implementation": "platform_python_implementation",
'os.name': 'os_name',
'sys.platform': 'sys_platform',
'platform.version': 'platform_version',
'platform.machine': 'platform_machine',
'platform.python_implementation': 'platform_python_implementation',
'python_implementation': 'platform_python_implementation',
}
VARIABLE.setParseAction(lambda s, l, t: Variable(ALIASES.get(t[0], t[0])))
VERSION_CMP = (
L("===") | L("==") | L(">=") | L("<=") | L("!=") | L("~=") | L(">") | L("<")
L('===') | L('==') | L('>=') | L('<=') | L('!=') | L('~=') | L('>') | L('<')
)
MARKER_OP = VERSION_CMP | L("not in") | L("in")
MARKER_OP = VERSION_CMP | L('not in') | L('in')
MARKER_OP.setParseAction(lambda s, l, t: Op(t[0]))
MARKER_VALUE = QuotedString("'") | QuotedString('"')
MARKER_VALUE.setParseAction(lambda s, l, t: Value(t[0]))
BOOLOP = L("and") | L("or")
BOOLOP = L('and') | L('or')
MARKER_VAR = VARIABLE | MARKER_VALUE
MARKER_ITEM = Group(MARKER_VAR + MARKER_OP + MARKER_VAR)
MARKER_ITEM.setParseAction(lambda s, l, t: tuple(t[0]))
LPAREN = L("(").suppress()
RPAREN = L(")").suppress()
LPAREN = L('(').suppress()
RPAREN = L(')').suppress()
MARKER_EXPR = Forward()
MARKER_ATOM = MARKER_ITEM | Group(LPAREN + MARKER_EXPR + RPAREN)
@ -161,40 +168,40 @@ def _format_marker(marker, first=True):
# the rest of this function so that we don't get extraneous () on the
# outside.
if (
isinstance(marker, list)
and len(marker) == 1
and isinstance(marker[0], (list, tuple))
isinstance(marker, list) and
len(marker) == 1 and
isinstance(marker[0], (list, tuple))
):
return _format_marker(marker[0])
if isinstance(marker, list):
inner = (_format_marker(m, first=False) for m in marker)
if first:
return " ".join(inner)
return ' '.join(inner)
else:
return "(" + " ".join(inner) + ")"
return '(' + ' '.join(inner) + ')'
elif isinstance(marker, tuple):
return " ".join([m.serialize() for m in marker])
return ' '.join([m.serialize() for m in marker])
else:
return marker
_operators = {
"in": lambda lhs, rhs: lhs in rhs,
"not in": lambda lhs, rhs: lhs not in rhs,
"<": operator.lt,
"<=": operator.le,
"==": operator.eq,
"!=": operator.ne,
">=": operator.ge,
">": operator.gt,
'in': lambda lhs, rhs: lhs in rhs,
'not in': lambda lhs, rhs: lhs not in rhs,
'<': operator.lt,
'<=': operator.le,
'==': operator.eq,
'!=': operator.ne,
'>=': operator.ge,
'>': operator.gt,
} # type: Dict[str, Operator]
def _eval_op(lhs, op, rhs):
# type: (str, Op, str) -> bool
try:
spec = Specifier("".join([op.serialize(), rhs]))
spec = Specifier(''.join([op.serialize(), rhs]))
except InvalidSpecifier:
pass
else:
@ -203,13 +210,13 @@ def _eval_op(lhs, op, rhs):
oper = _operators.get(op.serialize()) # type: Optional[Operator]
if oper is None:
raise UndefinedComparison(
"Undefined {0!r} on {1!r} and {2!r}.".format(op, lhs, rhs)
f'Undefined {op!r} on {lhs!r} and {rhs!r}.',
)
return oper(lhs, rhs)
class Undefined(object):
class Undefined:
pass
@ -222,7 +229,7 @@ def _get_env(environment, name):
if isinstance(value, Undefined):
raise UndefinedEnvironmentName(
"{0!r} does not exist in evaluation environment.".format(name)
f'{name!r} does not exist in evaluation environment.',
)
return value
@ -249,8 +256,8 @@ def _evaluate_markers(markers, environment):
groups[-1].append(_eval_op(lhs_value, op, rhs_value))
else:
assert marker in ["and", "or"]
if marker == "or":
assert marker in ['and', 'or']
if marker == 'or':
groups.append([])
return any(all(item) for item in groups)
@ -258,48 +265,48 @@ def _evaluate_markers(markers, environment):
def format_full_version(info):
# type: (sys._version_info) -> str
version = "{0.major}.{0.minor}.{0.micro}".format(info)
version = '{0.major}.{0.minor}.{0.micro}'.format(info)
kind = info.releaselevel
if kind != "final":
if kind != 'final':
version += kind[0] + str(info.serial)
return version
def default_environment():
# type: () -> Dict[str, str]
if hasattr(sys, "implementation"):
if hasattr(sys, 'implementation'):
# Ignoring the `sys.implementation` reference for type checking due to
# mypy not liking that the attribute doesn't exist in Python 2.7 when
# run with the `--py27` flag.
iver = format_full_version(sys.implementation.version) # type: ignore
implementation_name = sys.implementation.name # type: ignore
else:
iver = "0"
implementation_name = ""
iver = '0'
implementation_name = ''
return {
"implementation_name": implementation_name,
"implementation_version": iver,
"os_name": os.name,
"platform_machine": platform.machine(),
"platform_release": platform.release(),
"platform_system": platform.system(),
"platform_version": platform.version(),
"python_full_version": platform.python_version(),
"platform_python_implementation": platform.python_implementation(),
"python_version": ".".join(platform.python_version_tuple()[:2]),
"sys_platform": sys.platform,
'implementation_name': implementation_name,
'implementation_version': iver,
'os_name': os.name,
'platform_machine': platform.machine(),
'platform_release': platform.release(),
'platform_system': platform.system(),
'platform_version': platform.version(),
'python_full_version': platform.python_version(),
'platform_python_implementation': platform.python_implementation(),
'python_version': '.'.join(platform.python_version_tuple()[:2]),
'sys_platform': sys.platform,
}
class Marker(object):
class Marker:
def __init__(self, marker):
# type: (str) -> None
try:
self._markers = _coerce_parse_result(MARKER.parseString(marker))
except ParseException as e:
err_str = "Invalid marker: {0!r}, parse error at {1!r}".format(
marker, marker[e.loc : e.loc + 8]
err_str = 'Invalid marker: {!r}, parse error at {!r}'.format(
marker, marker[e.loc: e.loc + 8],
)
raise InvalidMarker(err_str)
@ -309,7 +316,7 @@ class Marker(object):
def __repr__(self):
# type: () -> str
return "<Marker({0!r})>".format(str(self))
return f'<Marker({str(self)!r})>'
def evaluate(self, environment=None):
# type: (Optional[Dict[str, str]]) -> bool

View file

@ -1,19 +1,29 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
from __future__ import absolute_import, division, print_function
from __future__ import annotations
import string
import re
from setuptools.extern.pyparsing import stringStart, stringEnd, originalTextFor, ParseException
from setuptools.extern.pyparsing import ZeroOrMore, Word, Optional, Regex, Combine
from setuptools.extern.pyparsing import Literal as L # noqa
import string
from urllib import parse as urlparse
from setuptools.extern.pyparsing import Combine
from setuptools.extern.pyparsing import Literal as L # noqa
from setuptools.extern.pyparsing import Optional
from setuptools.extern.pyparsing import originalTextFor
from setuptools.extern.pyparsing import ParseException
from setuptools.extern.pyparsing import Regex
from setuptools.extern.pyparsing import stringEnd
from setuptools.extern.pyparsing import stringStart
from setuptools.extern.pyparsing import Word
from setuptools.extern.pyparsing import ZeroOrMore
from ._typing import TYPE_CHECKING
from .markers import MARKER_EXPR, Marker
from .specifiers import LegacySpecifier, Specifier, SpecifierSet
from .markers import Marker
from .markers import MARKER_EXPR
from .specifiers import LegacySpecifier
from .specifiers import Specifier
from .specifiers import SpecifierSet
if TYPE_CHECKING: # pragma: no cover
from typing import List
@ -27,43 +37,43 @@ class InvalidRequirement(ValueError):
ALPHANUM = Word(string.ascii_letters + string.digits)
LBRACKET = L("[").suppress()
RBRACKET = L("]").suppress()
LPAREN = L("(").suppress()
RPAREN = L(")").suppress()
COMMA = L(",").suppress()
SEMICOLON = L(";").suppress()
AT = L("@").suppress()
LBRACKET = L('[').suppress()
RBRACKET = L(']').suppress()
LPAREN = L('(').suppress()
RPAREN = L(')').suppress()
COMMA = L(',').suppress()
SEMICOLON = L(';').suppress()
AT = L('@').suppress()
PUNCTUATION = Word("-_.")
PUNCTUATION = Word('-_.')
IDENTIFIER_END = ALPHANUM | (ZeroOrMore(PUNCTUATION) + ALPHANUM)
IDENTIFIER = Combine(ALPHANUM + ZeroOrMore(IDENTIFIER_END))
NAME = IDENTIFIER("name")
NAME = IDENTIFIER('name')
EXTRA = IDENTIFIER
URI = Regex(r"[^ ]+")("url")
URI = Regex(r'[^ ]+')('url')
URL = AT + URI
EXTRAS_LIST = EXTRA + ZeroOrMore(COMMA + EXTRA)
EXTRAS = (LBRACKET + Optional(EXTRAS_LIST) + RBRACKET)("extras")
EXTRAS = (LBRACKET + Optional(EXTRAS_LIST) + RBRACKET)('extras')
VERSION_PEP440 = Regex(Specifier._regex_str, re.VERBOSE | re.IGNORECASE)
VERSION_LEGACY = Regex(LegacySpecifier._regex_str, re.VERBOSE | re.IGNORECASE)
VERSION_ONE = VERSION_PEP440 ^ VERSION_LEGACY
VERSION_MANY = Combine(
VERSION_ONE + ZeroOrMore(COMMA + VERSION_ONE), joinString=",", adjacent=False
)("_raw_spec")
_VERSION_SPEC = Optional(((LPAREN + VERSION_MANY + RPAREN) | VERSION_MANY))
_VERSION_SPEC.setParseAction(lambda s, l, t: t._raw_spec or "")
VERSION_ONE + ZeroOrMore(COMMA + VERSION_ONE), joinString=',', adjacent=False,
)('_raw_spec')
_VERSION_SPEC = Optional((LPAREN + VERSION_MANY + RPAREN) | VERSION_MANY)
_VERSION_SPEC.setParseAction(lambda s, l, t: t._raw_spec or '')
VERSION_SPEC = originalTextFor(_VERSION_SPEC)("specifier")
VERSION_SPEC = originalTextFor(_VERSION_SPEC)('specifier')
VERSION_SPEC.setParseAction(lambda s, l, t: t[1])
MARKER_EXPR = originalTextFor(MARKER_EXPR())("marker")
MARKER_EXPR = originalTextFor(MARKER_EXPR())('marker')
MARKER_EXPR.setParseAction(
lambda s, l, t: Marker(s[t._original_start : t._original_end])
lambda s, l, t: Marker(s[t._original_start: t._original_end]),
)
MARKER_SEPARATOR = SEMICOLON
MARKER = MARKER_SEPARATOR + MARKER_EXPR
@ -76,10 +86,10 @@ NAMED_REQUIREMENT = NAME + Optional(EXTRAS) + (URL_AND_MARKER | VERSION_AND_MARK
REQUIREMENT = stringStart + NAMED_REQUIREMENT + stringEnd
# setuptools.extern.pyparsing isn't thread safe during initialization, so we do it eagerly, see
# issue #104
REQUIREMENT.parseString("x[]")
REQUIREMENT.parseString('x[]')
class Requirement(object):
class Requirement:
"""Parse a requirement.
Parse a given requirement string into its parts, such as name, specifier,
@ -98,21 +108,21 @@ class Requirement(object):
req = REQUIREMENT.parseString(requirement_string)
except ParseException as e:
raise InvalidRequirement(
'Parse error at "{0!r}": {1}'.format(
requirement_string[e.loc : e.loc + 8], e.msg
)
'Parse error at "{!r}": {}'.format(
requirement_string[e.loc: e.loc + 8], e.msg,
),
)
self.name = req.name
if req.url:
parsed_url = urlparse.urlparse(req.url)
if parsed_url.scheme == "file":
if parsed_url.scheme == 'file':
if urlparse.urlunparse(parsed_url) != req.url:
raise InvalidRequirement("Invalid URL given")
raise InvalidRequirement('Invalid URL given')
elif not (parsed_url.scheme and parsed_url.netloc) or (
not parsed_url.scheme and not parsed_url.netloc
):
raise InvalidRequirement("Invalid URL: {0}".format(req.url))
raise InvalidRequirement(f'Invalid URL: {req.url}')
self.url = req.url
else:
self.url = None
@ -125,21 +135,21 @@ class Requirement(object):
parts = [self.name] # type: List[str]
if self.extras:
parts.append("[{0}]".format(",".join(sorted(self.extras))))
parts.append('[{}]'.format(','.join(sorted(self.extras))))
if self.specifier:
parts.append(str(self.specifier))
if self.url:
parts.append("@ {0}".format(self.url))
parts.append(f'@ {self.url}')
if self.marker:
parts.append(" ")
parts.append(' ')
if self.marker:
parts.append("; {0}".format(self.marker))
parts.append(f'; {self.marker}')
return "".join(parts)
return ''.join(parts)
def __repr__(self):
# type: () -> str
return "<Requirement({0!r})>".format(str(self))
return f'<Requirement({str(self)!r})>'

View file

@ -1,17 +1,20 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
from __future__ import absolute_import, division, print_function
from __future__ import annotations
import abc
import functools
import itertools
import re
from ._compat import string_types, with_metaclass
from ._compat import string_types
from ._compat import with_metaclass
from ._typing import TYPE_CHECKING
from .utils import canonicalize_version
from .version import Version, LegacyVersion, parse
from .version import LegacyVersion
from .version import parse
from .version import Version
if TYPE_CHECKING: # pragma: no cover
from typing import (
@ -105,15 +108,15 @@ class _IndividualSpecifier(BaseSpecifier):
_operators = {} # type: Dict[str, str]
def __init__(self, spec="", prereleases=None):
def __init__(self, spec='', prereleases=None):
# type: (str, Optional[bool]) -> None
match = self._regex.search(spec)
if not match:
raise InvalidSpecifier("Invalid specifier: '{0}'".format(spec))
raise InvalidSpecifier(f"Invalid specifier: '{spec}'")
self._spec = (
match.group("operator").strip(),
match.group("version").strip(),
match.group('operator').strip(),
match.group('version').strip(),
) # type: Tuple[str, str]
# Store whether or not this Specifier should accept prereleases
@ -122,16 +125,16 @@ class _IndividualSpecifier(BaseSpecifier):
def __repr__(self):
# type: () -> str
pre = (
", prereleases={0!r}".format(self.prereleases)
f', prereleases={self.prereleases!r}'
if self._prereleases is not None
else ""
else ''
)
return "<{0}({1!r}{2})>".format(self.__class__.__name__, str(self), pre)
return f'<{self.__class__.__name__}({str(self)!r}{pre})>'
def __str__(self):
# type: () -> str
return "{0}{1}".format(*self._spec)
return '{}{}'.format(*self._spec)
@property
def _canonical_spec(self):
@ -169,7 +172,7 @@ class _IndividualSpecifier(BaseSpecifier):
def _get_operator(self, op):
# type: (str) -> CallableOperator
operator_callable = getattr(
self, "_compare_{0}".format(self._operators[op])
self, f'_compare_{self._operators[op]}',
) # type: CallableOperator
return operator_callable
@ -231,7 +234,7 @@ class _IndividualSpecifier(BaseSpecifier):
yielded = False
found_prereleases = []
kw = {"prereleases": prereleases if prereleases is not None else True}
kw = {'prereleases': prereleases if prereleases is not None else True}
# Attempt to iterate over all the values in the iterable and if any of
# them match, yield them.
@ -274,15 +277,15 @@ class LegacySpecifier(_IndividualSpecifier):
)
"""
_regex = re.compile(r"^\s*" + _regex_str + r"\s*$", re.VERBOSE | re.IGNORECASE)
_regex = re.compile(r'^\s*' + _regex_str + r'\s*$', re.VERBOSE | re.IGNORECASE)
_operators = {
"==": "equal",
"!=": "not_equal",
"<=": "less_than_equal",
">=": "greater_than_equal",
"<": "less_than",
">": "greater_than",
'==': 'equal',
'!=': 'not_equal',
'<=': 'less_than_equal',
'>=': 'greater_than_equal',
'<': 'less_than',
'>': 'greater_than',
}
def _coerce_version(self, version):
@ -317,7 +320,7 @@ class LegacySpecifier(_IndividualSpecifier):
def _require_version_compare(
fn # type: (Callable[[Specifier, ParsedVersion, str], bool])
fn, # type: (Callable[[Specifier, ParsedVersion, str], bool])
):
# type: (...) -> Callable[[Specifier, ParsedVersion, str], bool]
@functools.wraps(fn)
@ -425,17 +428,17 @@ class Specifier(_IndividualSpecifier):
)
"""
_regex = re.compile(r"^\s*" + _regex_str + r"\s*$", re.VERBOSE | re.IGNORECASE)
_regex = re.compile(r'^\s*' + _regex_str + r'\s*$', re.VERBOSE | re.IGNORECASE)
_operators = {
"~=": "compatible",
"==": "equal",
"!=": "not_equal",
"<=": "less_than_equal",
">=": "greater_than_equal",
"<": "less_than",
">": "greater_than",
"===": "arbitrary",
'~=': 'compatible',
'==': 'equal',
'!=': 'not_equal',
'<=': 'less_than_equal',
'>=': 'greater_than_equal',
'<': 'less_than',
'>': 'greater_than',
'===': 'arbitrary',
}
@_require_version_compare
@ -451,20 +454,20 @@ class Specifier(_IndividualSpecifier):
# We want everything but the last item in the version, but we want to
# ignore post and dev releases and we want to treat the pre-release as
# it's own separate segment.
prefix = ".".join(
prefix = '.'.join(
list(
itertools.takewhile(
lambda x: (not x.startswith("post") and not x.startswith("dev")),
lambda x: (not x.startswith('post') and not x.startswith('dev')),
_version_split(spec),
)
)[:-1]
),
)[:-1],
)
# Add the prefix notation to the end of our string
prefix += ".*"
prefix += '.*'
return self._get_operator(">=")(prospective, spec) and self._get_operator("==")(
prospective, prefix
return self._get_operator('>=')(prospective, spec) and self._get_operator('==')(
prospective, prefix,
)
@_require_version_compare
@ -472,7 +475,7 @@ class Specifier(_IndividualSpecifier):
# type: (ParsedVersion, str) -> bool
# We need special logic to handle prefix matching
if spec.endswith(".*"):
if spec.endswith('.*'):
# In the case of prefix matching we want to ignore local segment.
prospective = Version(prospective.public)
# Split the spec out by dots, and pretend that there is an implicit
@ -492,7 +495,7 @@ class Specifier(_IndividualSpecifier):
# Pad out our two sides with zeros so that they both equal the same
# length.
padded_spec, padded_prospective = _pad_version(
split_spec, shortened_prospective
split_spec, shortened_prospective,
)
return padded_prospective == padded_spec
@ -608,10 +611,10 @@ class Specifier(_IndividualSpecifier):
# operators, and if they are if they are including an explicit
# prerelease.
operator, version = self._spec
if operator in ["==", ">=", "<=", "~=", "==="]:
if operator in ['==', '>=', '<=', '~=', '===']:
# The == specifier can include a trailing .*, if it does we
# want to remove before parsing.
if operator == "==" and version.endswith(".*"):
if operator == '==' and version.endswith('.*'):
version = version[:-2]
# Parse the version, and if it is a pre-release than this
@ -627,13 +630,13 @@ class Specifier(_IndividualSpecifier):
self._prereleases = value
_prefix_regex = re.compile(r"^([0-9]+)((?:a|b|c|rc)[0-9]+)$")
_prefix_regex = re.compile(r'^([0-9]+)((?:a|b|c|rc)[0-9]+)$')
def _version_split(version):
# type: (str) -> List[str]
result = [] # type: List[str]
for item in version.split("."):
for item in version.split('.'):
match = _prefix_regex.search(item)
if match:
result.extend(match.groups())
@ -651,23 +654,23 @@ def _pad_version(left, right):
right_split.append(list(itertools.takewhile(lambda x: x.isdigit(), right)))
# Get the rest of our versions
left_split.append(left[len(left_split[0]) :])
right_split.append(right[len(right_split[0]) :])
left_split.append(left[len(left_split[0]):])
right_split.append(right[len(right_split[0]):])
# Insert our padding
left_split.insert(1, ["0"] * max(0, len(right_split[0]) - len(left_split[0])))
right_split.insert(1, ["0"] * max(0, len(left_split[0]) - len(right_split[0])))
left_split.insert(1, ['0'] * max(0, len(right_split[0]) - len(left_split[0])))
right_split.insert(1, ['0'] * max(0, len(left_split[0]) - len(right_split[0])))
return (list(itertools.chain(*left_split)), list(itertools.chain(*right_split)))
class SpecifierSet(BaseSpecifier):
def __init__(self, specifiers="", prereleases=None):
def __init__(self, specifiers='', prereleases=None):
# type: (str, Optional[bool]) -> None
# Split on , to break each individual specifier into it's own item, and
# strip each item to remove leading/trailing whitespace.
split_specifiers = [s.strip() for s in specifiers.split(",") if s.strip()]
split_specifiers = [s.strip() for s in specifiers.split(',') if s.strip()]
# Parsed each individual specifier, attempting first to make it a
# Specifier and falling back to a LegacySpecifier.
@ -688,16 +691,16 @@ class SpecifierSet(BaseSpecifier):
def __repr__(self):
# type: () -> str
pre = (
", prereleases={0!r}".format(self.prereleases)
f', prereleases={self.prereleases!r}'
if self._prereleases is not None
else ""
else ''
)
return "<SpecifierSet({0!r}{1})>".format(str(self), pre)
return f'<SpecifierSet({str(self)!r}{pre})>'
def __str__(self):
# type: () -> str
return ",".join(sorted(str(s) for s in self._specs))
return ','.join(sorted(str(s) for s in self._specs))
def __hash__(self):
# type: () -> int
@ -721,8 +724,8 @@ class SpecifierSet(BaseSpecifier):
specifier._prereleases = self._prereleases
else:
raise ValueError(
"Cannot combine SpecifierSets with True and False prerelease "
"overrides."
'Cannot combine SpecifierSets with True and False prerelease '
'overrides.',
)
return specifier

View file

@ -1,8 +1,7 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
from __future__ import absolute_import
from __future__ import annotations
import distutils.util
@ -46,18 +45,18 @@ if TYPE_CHECKING: # pragma: no cover
logger = logging.getLogger(__name__)
INTERPRETER_SHORT_NAMES = {
"python": "py", # Generic.
"cpython": "cp",
"pypy": "pp",
"ironpython": "ip",
"jython": "jy",
'python': 'py', # Generic.
'cpython': 'cp',
'pypy': 'pp',
'ironpython': 'ip',
'jython': 'jy',
} # type: Dict[str, str]
_32_BIT_INTERPRETER = sys.maxsize <= 2 ** 32
class Tag(object):
class Tag:
"""
A representation of the tag triple for a wheel.
@ -65,7 +64,7 @@ class Tag(object):
is also supported.
"""
__slots__ = ["_interpreter", "_abi", "_platform"]
__slots__ = ['_interpreter', '_abi', '_platform']
def __init__(self, interpreter, abi, platform):
# type: (str, str, str) -> None
@ -94,9 +93,9 @@ class Tag(object):
return NotImplemented
return (
(self.platform == other.platform)
and (self.abi == other.abi)
and (self.interpreter == other.interpreter)
(self.platform == other.platform) and
(self.abi == other.abi) and
(self.interpreter == other.interpreter)
)
def __hash__(self):
@ -105,11 +104,11 @@ class Tag(object):
def __str__(self):
# type: () -> str
return "{}-{}-{}".format(self._interpreter, self._abi, self._platform)
return f'{self._interpreter}-{self._abi}-{self._platform}'
def __repr__(self):
# type: () -> str
return "<{self} @ {self_id}>".format(self=self, self_id=id(self))
return f'<{self} @ {id(self)}>'
def parse_tag(tag):
@ -121,10 +120,10 @@ def parse_tag(tag):
compressed tag set.
"""
tags = set()
interpreters, abis, platforms = tag.split("-")
for interpreter in interpreters.split("."):
for abi in abis.split("."):
for platform_ in platforms.split("."):
interpreters, abis, platforms = tag.split('-')
for interpreter in interpreters.split('.'):
for abi in abis.split('.'):
for platform_ in platforms.split('.'):
tags.add(Tag(interpreter, abi, platform_))
return frozenset(tags)
@ -136,13 +135,13 @@ def _warn_keyword_parameter(func_name, kwargs):
"""
if not kwargs:
return False
elif len(kwargs) > 1 or "warn" not in kwargs:
kwargs.pop("warn", None)
elif len(kwargs) > 1 or 'warn' not in kwargs:
kwargs.pop('warn', None)
arg = next(iter(kwargs.keys()))
raise TypeError(
"{}() got an unexpected keyword argument {!r}".format(func_name, arg)
f'{func_name}() got an unexpected keyword argument {arg!r}',
)
return kwargs["warn"]
return kwargs['warn']
def _get_config_var(name, warn=False):
@ -150,14 +149,14 @@ def _get_config_var(name, warn=False):
value = sysconfig.get_config_var(name)
if value is None and warn:
logger.debug(
"Config variable '%s' is unset, Python ABI tag may be incorrect", name
"Config variable '%s' is unset, Python ABI tag may be incorrect", name,
)
return value
def _normalize_string(string):
# type: (str) -> str
return string.replace(".", "_").replace("-", "_")
return string.replace('.', '_').replace('-', '_')
def _abi3_applies(python_version):
@ -175,33 +174,33 @@ def _cpython_abis(py_version, warn=False):
py_version = tuple(py_version) # To allow for version comparison.
abis = []
version = _version_nodot(py_version[:2])
debug = pymalloc = ucs4 = ""
with_debug = _get_config_var("Py_DEBUG", warn)
has_refcount = hasattr(sys, "gettotalrefcount")
debug = pymalloc = ucs4 = ''
with_debug = _get_config_var('Py_DEBUG', warn)
has_refcount = hasattr(sys, 'gettotalrefcount')
# Windows doesn't set Py_DEBUG, so checking for support of debug-compiled
# extension modules is the best option.
# https://github.com/pypa/pip/issues/3383#issuecomment-173267692
has_ext = "_d.pyd" in EXTENSION_SUFFIXES
has_ext = '_d.pyd' in EXTENSION_SUFFIXES
if with_debug or (with_debug is None and (has_refcount or has_ext)):
debug = "d"
debug = 'd'
if py_version < (3, 8):
with_pymalloc = _get_config_var("WITH_PYMALLOC", warn)
with_pymalloc = _get_config_var('WITH_PYMALLOC', warn)
if with_pymalloc or with_pymalloc is None:
pymalloc = "m"
pymalloc = 'm'
if py_version < (3, 3):
unicode_size = _get_config_var("Py_UNICODE_SIZE", warn)
unicode_size = _get_config_var('Py_UNICODE_SIZE', warn)
if unicode_size == 4 or (
unicode_size is None and sys.maxunicode == 0x10FFFF
):
ucs4 = "u"
ucs4 = 'u'
elif debug:
# Debug builds can also load "normal" extension modules.
# We can also assume no UCS-4 or pymalloc requirement.
abis.append("cp{version}".format(version=version))
abis.append(f'cp{version}')
abis.insert(
0,
"cp{version}{debug}{pymalloc}{ucs4}".format(
version=version, debug=debug, pymalloc=pymalloc, ucs4=ucs4
'cp{version}{debug}{pymalloc}{ucs4}'.format(
version=version, debug=debug, pymalloc=pymalloc, ucs4=ucs4,
),
)
return abis
@ -211,7 +210,7 @@ def cpython_tags(
python_version=None, # type: Optional[PythonVersion]
abis=None, # type: Optional[Iterable[str]]
platforms=None, # type: Optional[Iterable[str]]
**kwargs # type: bool
**kwargs, # type: bool
):
# type: (...) -> Iterator[Tag]
"""
@ -229,11 +228,11 @@ def cpython_tags(
If 'abi3' or 'none' are specified in 'abis' then they will be yielded at
their normal position and not at the beginning.
"""
warn = _warn_keyword_parameter("cpython_tags", kwargs)
warn = _warn_keyword_parameter('cpython_tags', kwargs)
if not python_version:
python_version = sys.version_info[:2]
interpreter = "cp{}".format(_version_nodot(python_version[:2]))
interpreter = f'cp{_version_nodot(python_version[:2])}'
if abis is None:
if len(python_version) > 1:
@ -242,7 +241,7 @@ def cpython_tags(
abis = []
abis = list(abis)
# 'abi3' and 'none' are explicitly handled later.
for explicit_abi in ("abi3", "none"):
for explicit_abi in ('abi3', 'none'):
try:
abis.remove(explicit_abi)
except ValueError:
@ -253,23 +252,21 @@ def cpython_tags(
for platform_ in platforms:
yield Tag(interpreter, abi, platform_)
if _abi3_applies(python_version):
for tag in (Tag(interpreter, "abi3", platform_) for platform_ in platforms):
yield tag
for tag in (Tag(interpreter, "none", platform_) for platform_ in platforms):
yield tag
yield from (Tag(interpreter, 'abi3', platform_) for platform_ in platforms)
yield from (Tag(interpreter, 'none', platform_) for platform_ in platforms)
if _abi3_applies(python_version):
for minor_version in range(python_version[1] - 1, 1, -1):
for platform_ in platforms:
interpreter = "cp{version}".format(
version=_version_nodot((python_version[0], minor_version))
interpreter = 'cp{version}'.format(
version=_version_nodot((python_version[0], minor_version)),
)
yield Tag(interpreter, "abi3", platform_)
yield Tag(interpreter, 'abi3', platform_)
def _generic_abi():
# type: () -> Iterator[str]
abi = sysconfig.get_config_var("SOABI")
abi = sysconfig.get_config_var('SOABI')
if abi:
yield _normalize_string(abi)
@ -278,7 +275,7 @@ def generic_tags(
interpreter=None, # type: Optional[str]
abis=None, # type: Optional[Iterable[str]]
platforms=None, # type: Optional[Iterable[str]]
**kwargs # type: bool
**kwargs, # type: bool
):
# type: (...) -> Iterator[Tag]
"""
@ -289,17 +286,17 @@ def generic_tags(
The "none" ABI will be added if it was not explicitly provided.
"""
warn = _warn_keyword_parameter("generic_tags", kwargs)
warn = _warn_keyword_parameter('generic_tags', kwargs)
if not interpreter:
interp_name = interpreter_name()
interp_version = interpreter_version(warn=warn)
interpreter = "".join([interp_name, interp_version])
interpreter = ''.join([interp_name, interp_version])
if abis is None:
abis = _generic_abi()
platforms = list(platforms or _platform_tags())
abis = list(abis)
if "none" not in abis:
abis.append("none")
if 'none' not in abis:
abis.append('none')
for abi in abis:
for platform_ in platforms:
yield Tag(interpreter, abi, platform_)
@ -314,11 +311,11 @@ def _py_interpreter_range(py_version):
all previous versions of that major version.
"""
if len(py_version) > 1:
yield "py{version}".format(version=_version_nodot(py_version[:2]))
yield "py{major}".format(major=py_version[0])
yield f'py{_version_nodot(py_version[:2])}'
yield f'py{py_version[0]}'
if len(py_version) > 1:
for minor in range(py_version[1] - 1, -1, -1):
yield "py{version}".format(version=_version_nodot((py_version[0], minor)))
yield f'py{_version_nodot((py_version[0], minor))}'
def compatible_tags(
@ -340,11 +337,11 @@ def compatible_tags(
platforms = list(platforms or _platform_tags())
for version in _py_interpreter_range(python_version):
for platform_ in platforms:
yield Tag(version, "none", platform_)
yield Tag(version, 'none', platform_)
if interpreter:
yield Tag(interpreter, "none", "any")
yield Tag(interpreter, 'none', 'any')
for version in _py_interpreter_range(python_version):
yield Tag(version, "none", "any")
yield Tag(version, 'none', 'any')
def _mac_arch(arch, is_32bit=_32_BIT_INTERPRETER):
@ -352,37 +349,37 @@ def _mac_arch(arch, is_32bit=_32_BIT_INTERPRETER):
if not is_32bit:
return arch
if arch.startswith("ppc"):
return "ppc"
if arch.startswith('ppc'):
return 'ppc'
return "i386"
return 'i386'
def _mac_binary_formats(version, cpu_arch):
# type: (MacVersion, str) -> List[str]
formats = [cpu_arch]
if cpu_arch == "x86_64":
if cpu_arch == 'x86_64':
if version < (10, 4):
return []
formats.extend(["intel", "fat64", "fat32"])
formats.extend(['intel', 'fat64', 'fat32'])
elif cpu_arch == "i386":
elif cpu_arch == 'i386':
if version < (10, 4):
return []
formats.extend(["intel", "fat32", "fat"])
formats.extend(['intel', 'fat32', 'fat'])
elif cpu_arch == "ppc64":
elif cpu_arch == 'ppc64':
# TODO: Need to care about 32-bit PPC for ppc64 through 10.2?
if version > (10, 5) or version < (10, 4):
return []
formats.append("fat64")
formats.append('fat64')
elif cpu_arch == "ppc":
elif cpu_arch == 'ppc':
if version > (10, 6):
return []
formats.extend(["fat32", "fat"])
formats.extend(['fat32', 'fat'])
formats.append("universal")
formats.append('universal')
return formats
@ -398,7 +395,7 @@ def mac_platforms(version=None, arch=None):
"""
version_str, _, cpu_arch = platform.mac_ver() # type: ignore
if version is None:
version = cast("MacVersion", tuple(map(int, version_str.split(".")[:2])))
version = cast('MacVersion', tuple(map(int, version_str.split('.')[:2])))
else:
version = version
if arch is None:
@ -409,7 +406,7 @@ def mac_platforms(version=None, arch=None):
compat_version = version[0], minor_version
binary_formats = _mac_binary_formats(compat_version, arch)
for binary_format in binary_formats:
yield "macosx_{major}_{minor}_{binary_format}".format(
yield 'macosx_{major}_{minor}_{binary_format}'.format(
major=compat_version[0],
minor=compat_version[1],
binary_format=binary_format,
@ -423,7 +420,7 @@ def _is_manylinux_compatible(name, glibc_version):
try:
import _manylinux # noqa
return bool(getattr(_manylinux, name + "_compatible"))
return bool(getattr(_manylinux, name + '_compatible'))
except (ImportError, AttributeError):
# Fall through to heuristic check below.
pass
@ -449,7 +446,7 @@ def _glibc_version_string_confstr():
try:
# os.confstr("CS_GNU_LIBC_VERSION") returns a string like "glibc 2.17".
version_string = os.confstr( # type: ignore[attr-defined] # noqa: F821
"CS_GNU_LIBC_VERSION"
'CS_GNU_LIBC_VERSION',
)
assert version_string is not None
_, version = version_string.split() # type: Tuple[str, str]
@ -488,7 +485,7 @@ def _glibc_version_string_ctypes():
version_str = gnu_get_libc_version() # type: str
# py2 / py3 compatibility:
if not isinstance(version_str, str):
version_str = version_str.decode("ascii")
version_str = version_str.decode('ascii')
return version_str
@ -502,17 +499,17 @@ def _check_glibc_version(version_str, required_major, minimum_minor):
# random junk that might come after the minor version -- this might happen
# in patched/forked versions of glibc (e.g. Linaro's version of glibc
# uses version strings like "2.20-2014.11"). See gh-3588.
m = re.match(r"(?P<major>[0-9]+)\.(?P<minor>[0-9]+)", version_str)
m = re.match(r'(?P<major>[0-9]+)\.(?P<minor>[0-9]+)', version_str)
if not m:
warnings.warn(
"Expected glibc version with 2 components major.minor,"
" got: %s" % version_str,
'Expected glibc version with 2 components major.minor,'
' got: %s' % version_str,
RuntimeWarning,
)
return False
return (
int(m.group("major")) == required_major
and int(m.group("minor")) >= minimum_minor
int(m.group('major')) == required_major and
int(m.group('minor')) >= minimum_minor
)
@ -528,7 +525,7 @@ def _have_compatible_glibc(required_major, minimum_minor):
# identify the architecture of the running executable in some cases, so we
# determine it dynamically by reading the information from the running
# process. This only applies on Linux, which uses the ELF format.
class _ELFFileHeader(object):
class _ELFFileHeader:
# https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#File_header
class _InvalidELFFileHeader(ValueError):
"""
@ -554,28 +551,28 @@ class _ELFFileHeader(object):
# type: (str) -> int
try:
(result,) = struct.unpack(
fmt, file.read(struct.calcsize(fmt))
fmt, file.read(struct.calcsize(fmt)),
) # type: (int, )
except struct.error:
raise _ELFFileHeader._InvalidELFFileHeader()
return result
self.e_ident_magic = unpack(">I")
self.e_ident_magic = unpack('>I')
if self.e_ident_magic != self.ELF_MAGIC_NUMBER:
raise _ELFFileHeader._InvalidELFFileHeader()
self.e_ident_class = unpack("B")
self.e_ident_class = unpack('B')
if self.e_ident_class not in {self.ELFCLASS32, self.ELFCLASS64}:
raise _ELFFileHeader._InvalidELFFileHeader()
self.e_ident_data = unpack("B")
self.e_ident_data = unpack('B')
if self.e_ident_data not in {self.ELFDATA2LSB, self.ELFDATA2MSB}:
raise _ELFFileHeader._InvalidELFFileHeader()
self.e_ident_version = unpack("B")
self.e_ident_osabi = unpack("B")
self.e_ident_abiversion = unpack("B")
self.e_ident_version = unpack('B')
self.e_ident_osabi = unpack('B')
self.e_ident_abiversion = unpack('B')
self.e_ident_pad = file.read(7)
format_h = "<H" if self.e_ident_data == self.ELFDATA2LSB else ">H"
format_i = "<I" if self.e_ident_data == self.ELFDATA2LSB else ">I"
format_q = "<Q" if self.e_ident_data == self.ELFDATA2LSB else ">Q"
format_h = '<H' if self.e_ident_data == self.ELFDATA2LSB else '>H'
format_i = '<I' if self.e_ident_data == self.ELFDATA2LSB else '>I'
format_q = '<Q' if self.e_ident_data == self.ELFDATA2LSB else '>Q'
format_p = format_i if self.e_ident_class == self.ELFCLASS32 else format_q
self.e_type = unpack(format_h)
self.e_machine = unpack(format_h)
@ -595,9 +592,9 @@ class _ELFFileHeader(object):
def _get_elf_header():
# type: () -> Optional[_ELFFileHeader]
try:
with open(sys.executable, "rb") as f:
with open(sys.executable, 'rb') as f:
elf_header = _ELFFileHeader(f)
except (IOError, OSError, TypeError, _ELFFileHeader._InvalidELFFileHeader):
except (OSError, TypeError, _ELFFileHeader._InvalidELFFileHeader):
return None
return elf_header
@ -635,9 +632,9 @@ def _is_linux_i686():
def _have_compatible_manylinux_abi(arch):
# type: (str) -> bool
if arch == "armv7l":
if arch == 'armv7l':
return _is_linux_armhf()
if arch == "i686":
if arch == 'i686':
return _is_linux_i686()
return True
@ -646,32 +643,32 @@ def _linux_platforms(is_32bit=_32_BIT_INTERPRETER):
# type: (bool) -> Iterator[str]
linux = _normalize_string(distutils.util.get_platform())
if is_32bit:
if linux == "linux_x86_64":
linux = "linux_i686"
elif linux == "linux_aarch64":
linux = "linux_armv7l"
if linux == 'linux_x86_64':
linux = 'linux_i686'
elif linux == 'linux_aarch64':
linux = 'linux_armv7l'
manylinux_support = []
_, arch = linux.split("_", 1)
_, arch = linux.split('_', 1)
if _have_compatible_manylinux_abi(arch):
if arch in {"x86_64", "i686", "aarch64", "armv7l", "ppc64", "ppc64le", "s390x"}:
if arch in {'x86_64', 'i686', 'aarch64', 'armv7l', 'ppc64', 'ppc64le', 's390x'}:
manylinux_support.append(
("manylinux2014", (2, 17))
('manylinux2014', (2, 17)),
) # CentOS 7 w/ glibc 2.17 (PEP 599)
if arch in {"x86_64", "i686"}:
if arch in {'x86_64', 'i686'}:
manylinux_support.append(
("manylinux2010", (2, 12))
('manylinux2010', (2, 12)),
) # CentOS 6 w/ glibc 2.12 (PEP 571)
manylinux_support.append(
("manylinux1", (2, 5))
('manylinux1', (2, 5)),
) # CentOS 5 w/ glibc 2.5 (PEP 513)
manylinux_support_iter = iter(manylinux_support)
for name, glibc_version in manylinux_support_iter:
if _is_manylinux_compatible(name, glibc_version):
yield linux.replace("linux", name)
yield linux.replace('linux', name)
break
# Support for a later manylinux implies support for an earlier version.
for name, _ in manylinux_support_iter:
yield linux.replace("linux", name)
yield linux.replace('linux', name)
yield linux
@ -685,9 +682,9 @@ def _platform_tags():
"""
Provides the platform tags for this installation.
"""
if platform.system() == "Darwin":
if platform.system() == 'Darwin':
return mac_platforms()
elif platform.system() == "Linux":
elif platform.system() == 'Linux':
return _linux_platforms()
else:
return _generic_platforms()
@ -711,8 +708,8 @@ def interpreter_version(**kwargs):
"""
Returns the version of the running interpreter.
"""
warn = _warn_keyword_parameter("interpreter_version", kwargs)
version = _get_config_var("py_version_nodot", warn=warn)
warn = _warn_keyword_parameter('interpreter_version', kwargs)
version = _get_config_var('py_version_nodot', warn=warn)
if version:
version = str(version)
else:
@ -723,9 +720,9 @@ def interpreter_version(**kwargs):
def _version_nodot(version):
# type: (PythonVersion) -> str
if any(v >= 10 for v in version):
sep = "_"
sep = '_'
else:
sep = ""
sep = ''
return sep.join(map(str, version))
@ -737,15 +734,12 @@ def sys_tags(**kwargs):
The order of the sequence corresponds to priority order for the
interpreter, from most to least important.
"""
warn = _warn_keyword_parameter("sys_tags", kwargs)
warn = _warn_keyword_parameter('sys_tags', kwargs)
interp_name = interpreter_name()
if interp_name == "cp":
for tag in cpython_tags(warn=warn):
yield tag
if interp_name == 'cp':
yield from cpython_tags(warn=warn)
else:
for tag in generic_tags():
yield tag
yield from generic_tags()
for tag in compatible_tags():
yield tag
yield from compatible_tags()

View file

@ -1,26 +1,28 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
from __future__ import absolute_import, division, print_function
from __future__ import annotations
import re
from ._typing import TYPE_CHECKING, cast
from .version import InvalidVersion, Version
from ._typing import cast
from ._typing import TYPE_CHECKING
from .version import InvalidVersion
from .version import Version
if TYPE_CHECKING: # pragma: no cover
from typing import NewType, Union
NormalizedName = NewType("NormalizedName", str)
NormalizedName = NewType('NormalizedName', str)
_canonicalize_regex = re.compile(r"[-_.]+")
_canonicalize_regex = re.compile(r'[-_.]+')
def canonicalize_name(name):
# type: (str) -> NormalizedName
# This is taken from PEP 503.
value = _canonicalize_regex.sub("-", name).lower()
return cast("NormalizedName", value)
value = _canonicalize_regex.sub('-', name).lower()
return cast('NormalizedName', value)
def canonicalize_version(_version):
@ -40,26 +42,26 @@ def canonicalize_version(_version):
# Epoch
if version.epoch != 0:
parts.append("{0}!".format(version.epoch))
parts.append(f'{version.epoch}!')
# Release segment
# NB: This strips trailing '.0's to normalize
parts.append(re.sub(r"(\.0)+$", "", ".".join(str(x) for x in version.release)))
parts.append(re.sub(r'(\.0)+$', '', '.'.join(str(x) for x in version.release)))
# Pre-release
if version.pre is not None:
parts.append("".join(str(x) for x in version.pre))
parts.append(''.join(str(x) for x in version.pre))
# Post-release
if version.post is not None:
parts.append(".post{0}".format(version.post))
parts.append(f'.post{version.post}')
# Development release
if version.dev is not None:
parts.append(".dev{0}".format(version.dev))
parts.append(f'.dev{version.dev}')
# Local version segment
if version.local is not None:
parts.append("+{0}".format(version.local))
parts.append(f'+{version.local}')
return "".join(parts)
return ''.join(parts)

View file

@ -1,13 +1,14 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
from __future__ import absolute_import, division, print_function
from __future__ import annotations
import collections
import itertools
import re
from ._structures import Infinity, NegativeInfinity
from ._structures import Infinity
from ._structures import NegativeInfinity
from ._typing import TYPE_CHECKING
if TYPE_CHECKING: # pragma: no cover
@ -30,18 +31,18 @@ if TYPE_CHECKING: # pragma: no cover
],
]
CmpKey = Tuple[
int, Tuple[int, ...], PrePostDevType, PrePostDevType, PrePostDevType, LocalType
int, Tuple[int, ...], PrePostDevType, PrePostDevType, PrePostDevType, LocalType,
]
LegacyCmpKey = Tuple[int, Tuple[str, ...]]
VersionComparisonMethod = Callable[
[Union[CmpKey, LegacyCmpKey], Union[CmpKey, LegacyCmpKey]], bool
[Union[CmpKey, LegacyCmpKey], Union[CmpKey, LegacyCmpKey]], bool,
]
__all__ = ["parse", "Version", "LegacyVersion", "InvalidVersion", "VERSION_PATTERN"]
__all__ = ['parse', 'Version', 'LegacyVersion', 'InvalidVersion', 'VERSION_PATTERN']
_Version = collections.namedtuple(
"_Version", ["epoch", "release", "dev", "pre", "post", "local"]
'_Version', ['epoch', 'release', 'dev', 'pre', 'post', 'local'],
)
@ -64,7 +65,7 @@ class InvalidVersion(ValueError):
"""
class _BaseVersion(object):
class _BaseVersion:
_key = None # type: Union[CmpKey, LegacyCmpKey]
def __hash__(self):
@ -115,7 +116,7 @@ class LegacyVersion(_BaseVersion):
def __repr__(self):
# type: () -> str
return "<LegacyVersion({0})>".format(repr(str(self)))
return f'<LegacyVersion({repr(str(self))})>'
@property
def public(self):
@ -173,14 +174,14 @@ class LegacyVersion(_BaseVersion):
return False
_legacy_version_component_re = re.compile(r"(\d+ | [a-z]+ | \.| -)", re.VERBOSE)
_legacy_version_component_re = re.compile(r'(\d+ | [a-z]+ | \.| -)', re.VERBOSE)
_legacy_version_replacement_map = {
"pre": "c",
"preview": "c",
"-": "final-",
"rc": "c",
"dev": "@",
'pre': 'c',
'preview': 'c',
'-': 'final-',
'rc': 'c',
'dev': '@',
}
@ -189,17 +190,17 @@ def _parse_version_parts(s):
for part in _legacy_version_component_re.split(s):
part = _legacy_version_replacement_map.get(part, part)
if not part or part == ".":
if not part or part == '.':
continue
if part[:1] in "0123456789":
if part[:1] in '0123456789':
# pad for numeric comparison
yield part.zfill(8)
else:
yield "*" + part
yield '*' + part
# ensure that alpha/beta/candidate are before final
yield "*final"
yield '*final'
def _legacy_cmpkey(version):
@ -215,14 +216,14 @@ def _legacy_cmpkey(version):
# it's adoption of the packaging library.
parts = [] # type: List[str]
for part in _parse_version_parts(version.lower()):
if part.startswith("*"):
if part.startswith('*'):
# remove "-" before a prerelease tag
if part < "*final":
while parts and parts[-1] == "*final-":
if part < '*final':
while parts and parts[-1] == '*final-':
parts.pop()
# remove trailing zeros from each series of numeric parts
while parts and parts[-1] == "00000000":
while parts and parts[-1] == '00000000':
parts.pop()
parts.append(part)
@ -266,7 +267,7 @@ VERSION_PATTERN = r"""
class Version(_BaseVersion):
_regex = re.compile(r"^\s*" + VERSION_PATTERN + r"\s*$", re.VERBOSE | re.IGNORECASE)
_regex = re.compile(r'^\s*' + VERSION_PATTERN + r'\s*$', re.VERBOSE | re.IGNORECASE)
def __init__(self, version):
# type: (str) -> None
@ -274,18 +275,18 @@ class Version(_BaseVersion):
# Validate the version and parse it into pieces
match = self._regex.search(version)
if not match:
raise InvalidVersion("Invalid version: '{0}'".format(version))
raise InvalidVersion(f"Invalid version: '{version}'")
# Store the parsed out pieces of the version
self._version = _Version(
epoch=int(match.group("epoch")) if match.group("epoch") else 0,
release=tuple(int(i) for i in match.group("release").split(".")),
pre=_parse_letter_version(match.group("pre_l"), match.group("pre_n")),
epoch=int(match.group('epoch')) if match.group('epoch') else 0,
release=tuple(int(i) for i in match.group('release').split('.')),
pre=_parse_letter_version(match.group('pre_l'), match.group('pre_n')),
post=_parse_letter_version(
match.group("post_l"), match.group("post_n1") or match.group("post_n2")
match.group('post_l'), match.group('post_n1') or match.group('post_n2'),
),
dev=_parse_letter_version(match.group("dev_l"), match.group("dev_n")),
local=_parse_local_version(match.group("local")),
dev=_parse_letter_version(match.group('dev_l'), match.group('dev_n')),
local=_parse_local_version(match.group('local')),
)
# Generate a key which will be used for sorting
@ -300,7 +301,7 @@ class Version(_BaseVersion):
def __repr__(self):
# type: () -> str
return "<Version({0})>".format(repr(str(self)))
return f'<Version({repr(str(self))})>'
def __str__(self):
# type: () -> str
@ -308,28 +309,28 @@ class Version(_BaseVersion):
# Epoch
if self.epoch != 0:
parts.append("{0}!".format(self.epoch))
parts.append(f'{self.epoch}!')
# Release segment
parts.append(".".join(str(x) for x in self.release))
parts.append('.'.join(str(x) for x in self.release))
# Pre-release
if self.pre is not None:
parts.append("".join(str(x) for x in self.pre))
parts.append(''.join(str(x) for x in self.pre))
# Post-release
if self.post is not None:
parts.append(".post{0}".format(self.post))
parts.append(f'.post{self.post}')
# Development release
if self.dev is not None:
parts.append(".dev{0}".format(self.dev))
parts.append(f'.dev{self.dev}')
# Local version segment
if self.local is not None:
parts.append("+{0}".format(self.local))
parts.append(f'+{self.local}')
return "".join(parts)
return ''.join(parts)
@property
def epoch(self):
@ -363,14 +364,14 @@ class Version(_BaseVersion):
def local(self):
# type: () -> Optional[str]
if self._version.local:
return ".".join(str(x) for x in self._version.local)
return '.'.join(str(x) for x in self._version.local)
else:
return None
@property
def public(self):
# type: () -> str
return str(self).split("+", 1)[0]
return str(self).split('+', 1)[0]
@property
def base_version(self):
@ -379,12 +380,12 @@ class Version(_BaseVersion):
# Epoch
if self.epoch != 0:
parts.append("{0}!".format(self.epoch))
parts.append(f'{self.epoch}!')
# Release segment
parts.append(".".join(str(x) for x in self.release))
parts.append('.'.join(str(x) for x in self.release))
return "".join(parts)
return ''.join(parts)
@property
def is_prerelease(self):
@ -435,27 +436,27 @@ def _parse_letter_version(
# We consider some words to be alternate spellings of other words and
# in those cases we want to normalize the spellings to our preferred
# spelling.
if letter == "alpha":
letter = "a"
elif letter == "beta":
letter = "b"
elif letter in ["c", "pre", "preview"]:
letter = "rc"
elif letter in ["rev", "r"]:
letter = "post"
if letter == 'alpha':
letter = 'a'
elif letter == 'beta':
letter = 'b'
elif letter in ['c', 'pre', 'preview']:
letter = 'rc'
elif letter in ['rev', 'r']:
letter = 'post'
return letter, int(number)
if not letter and number:
# We assume if we are given a number, but we are not given a letter
# then this is using the implicit post release syntax (e.g. 1.0-1)
letter = "post"
letter = 'post'
return letter, int(number)
return None
_local_version_separators = re.compile(r"[\._-]")
_local_version_separators = re.compile(r'[\._-]')
def _parse_local_version(local):
@ -487,7 +488,7 @@ def _cmpkey(
# re-reverse it back into the correct order and make it a tuple and use
# that for our sorting key.
_release = tuple(
reversed(list(itertools.dropwhile(lambda x: x == 0, reversed(release))))
reversed(list(itertools.dropwhile(lambda x: x == 0, reversed(release)))),
)
# We need to "trick" the sorting algorithm to put 1.0.dev0 before 1.0a0.
@ -529,7 +530,7 @@ def _cmpkey(
# - Shorter versions sort before longer versions when the prefixes
# match exactly
_local = tuple(
(i, "") if isinstance(i, int) else (NegativeInfinity, i) for i in local
(i, '') if isinstance(i, int) else (NegativeInfinity, i) for i in local
)
return epoch, _release, _pre, _post, _dev, _local

View file

@ -1,18 +1,19 @@
"""Utilities for extracting common archive formats"""
from __future__ import annotations
import zipfile
import tarfile
import os
import shutil
import posixpath
import contextlib
from distutils.errors import DistutilsError
import os
import posixpath
import shutil
import tarfile
import zipfile
from distutils.errors import DistutilsError
from pkg_resources import ensure_directory
__all__ = [
"unpack_archive", "unpack_zipfile", "unpack_tarfile", "default_filter",
"UnrecognizedFormat", "extraction_drivers", "unpack_directory",
'unpack_archive', 'unpack_zipfile', 'unpack_tarfile', 'default_filter',
'UnrecognizedFormat', 'extraction_drivers', 'unpack_directory',
]
@ -27,7 +28,8 @@ def default_filter(src, dst):
def unpack_archive(
filename, extract_dir, progress_filter=default_filter,
drivers=None):
drivers=None,
):
"""Unpack `filename` to `extract_dir`, or raise ``UnrecognizedFormat``
`progress_filter` is a function taking two arguments: a source path
@ -57,7 +59,7 @@ def unpack_archive(
return
else:
raise UnrecognizedFormat(
"Not a recognized archive type: %s" % filename
'Not a recognized archive type: %s' % filename,
)
@ -67,7 +69,7 @@ def unpack_directory(filename, extract_dir, progress_filter=default_filter):
Raises ``UnrecognizedFormat`` if `filename` is not a directory
"""
if not os.path.isdir(filename):
raise UnrecognizedFormat("%s is not a directory" % filename)
raise UnrecognizedFormat('%s is not a directory' % filename)
paths = {
filename: ('', extract_dir),
@ -97,7 +99,7 @@ def unpack_zipfile(filename, extract_dir, progress_filter=default_filter):
"""
if not zipfile.is_zipfile(filename):
raise UnrecognizedFormat("%s is not a zip file" % (filename,))
raise UnrecognizedFormat('{} is not a zip file'.format(filename))
with zipfile.ZipFile(filename) as z:
for info in z.infolist():
@ -128,7 +130,8 @@ def unpack_zipfile(filename, extract_dir, progress_filter=default_filter):
def _resolve_tar_file_or_dir(tar_obj, tar_member_obj):
"""Resolve any links and extract link targets as normal files."""
while tar_member_obj is not None and (
tar_member_obj.islnk() or tar_member_obj.issym()):
tar_member_obj.islnk() or tar_member_obj.issym()
):
linkpath = tar_member_obj.linkname
if tar_member_obj.issym():
base = posixpath.dirname(tar_member_obj.name)
@ -186,7 +189,7 @@ def unpack_tarfile(filename, extract_dir, progress_filter=default_filter):
tarobj = tarfile.open(filename)
except tarfile.TarError as e:
raise UnrecognizedFormat(
"%s is not a compressed or uncompressed tar file" % (filename,)
'{} is not a compressed or uncompressed tar file'.format(filename),
) from e
for member, final_dst in _iter_open_tar(

View file

@ -25,27 +25,29 @@ bug reports or API stability):
Again, this is not a formal definition! Just a "taste" of the module.
"""
from __future__ import annotations
import contextlib
import io
import os
import sys
import tokenize
import shutil
import contextlib
import sys
import tempfile
import tokenize
import setuptools
import distutils
import setuptools
from pkg_resources import parse_requirements
__all__ = ['get_requires_for_build_sdist',
'get_requires_for_build_wheel',
'prepare_metadata_for_build_wheel',
'build_wheel',
'build_sdist',
'__legacy__',
'SetupRequirementsError']
__all__ = [
'get_requires_for_build_sdist',
'get_requires_for_build_wheel',
'prepare_metadata_for_build_wheel',
'build_wheel',
'build_sdist',
'__legacy__',
'SetupRequirementsError',
]
class SetupRequirementsError(BaseException):
@ -92,8 +94,10 @@ def no_install_setup_requires():
def _get_immediate_subdirectories(a_dir):
return [name for name in os.listdir(a_dir)
if os.path.isdir(os.path.join(a_dir, name))]
return [
name for name in os.listdir(a_dir)
if os.path.isdir(os.path.join(a_dir, name))
]
def _file_with_extension(directory, extension):
@ -106,19 +110,20 @@ def _file_with_extension(directory, extension):
except ValueError:
raise ValueError(
'No distribution was found. Ensure that `setup.py` '
'is not empty and that it calls `setup()`.')
'is not empty and that it calls `setup()`.',
)
return file
def _open_setup_script(setup_script):
if not os.path.exists(setup_script):
# Supply a default setup.py
return io.StringIO(u"from setuptools import setup; setup()")
return io.StringIO('from setuptools import setup; setup()')
return getattr(tokenize, 'open', open)(setup_script)
class _BuildMetaBackend(object):
class _BuildMetaBackend:
def _fix_config(self, config_settings):
config_settings = config_settings or {}
@ -129,7 +134,7 @@ class _BuildMetaBackend(object):
config_settings = self._fix_config(config_settings)
sys.argv = sys.argv[:1] + ['egg_info'] + \
config_settings["--global-option"]
config_settings['--global-option']
try:
with Distribution.patch():
self.run_setup()
@ -152,23 +157,29 @@ class _BuildMetaBackend(object):
def get_requires_for_build_wheel(self, config_settings=None):
config_settings = self._fix_config(config_settings)
return self._get_build_requires(
config_settings, requirements=['wheel'])
config_settings, requirements=['wheel'],
)
def get_requires_for_build_sdist(self, config_settings=None):
config_settings = self._fix_config(config_settings)
return self._get_build_requires(config_settings, requirements=[])
def prepare_metadata_for_build_wheel(self, metadata_directory,
config_settings=None):
def prepare_metadata_for_build_wheel(
self, metadata_directory,
config_settings=None,
):
sys.argv = sys.argv[:1] + [
'dist_info', '--egg-base', metadata_directory]
'dist_info', '--egg-base', metadata_directory,
]
with no_install_setup_requires():
self.run_setup()
dist_info_directory = metadata_directory
while True:
dist_infos = [f for f in os.listdir(dist_info_directory)
if f.endswith('.dist-info')]
dist_infos = [
f for f in os.listdir(dist_info_directory)
if f.endswith('.dist-info')
]
if (
len(dist_infos) == 0 and
@ -176,7 +187,8 @@ class _BuildMetaBackend(object):
):
dist_info_directory = os.path.join(
dist_info_directory, os.listdir(dist_info_directory)[0])
dist_info_directory, os.listdir(dist_info_directory)[0],
)
continue
assert len(dist_infos) == 1
@ -187,27 +199,33 @@ class _BuildMetaBackend(object):
if dist_info_directory != metadata_directory:
shutil.move(
os.path.join(dist_info_directory, dist_infos[0]),
metadata_directory)
metadata_directory,
)
shutil.rmtree(dist_info_directory, ignore_errors=True)
return dist_infos[0]
def _build_with_temp_dir(self, setup_command, result_extension,
result_directory, config_settings):
def _build_with_temp_dir(
self, setup_command, result_extension,
result_directory, config_settings,
):
config_settings = self._fix_config(config_settings)
result_directory = os.path.abspath(result_directory)
# Build in a temporary directory, then copy to the target.
os.makedirs(result_directory, exist_ok=True)
with tempfile.TemporaryDirectory(dir=result_directory) as tmp_dist_dir:
sys.argv = (sys.argv[:1] + setup_command +
['--dist-dir', tmp_dist_dir] +
config_settings["--global-option"])
sys.argv = (
sys.argv[:1] + setup_command +
['--dist-dir', tmp_dist_dir] +
config_settings['--global-option']
)
with no_install_setup_requires():
self.run_setup()
result_basename = _file_with_extension(
tmp_dist_dir, result_extension)
tmp_dist_dir, result_extension,
)
result_path = os.path.join(result_directory, result_basename)
if os.path.exists(result_path):
# os.rename will fail overwriting on non-Unix.
@ -216,15 +234,21 @@ class _BuildMetaBackend(object):
return result_basename
def build_wheel(self, wheel_directory, config_settings=None,
metadata_directory=None):
return self._build_with_temp_dir(['bdist_wheel'], '.whl',
wheel_directory, config_settings)
def build_wheel(
self, wheel_directory, config_settings=None,
metadata_directory=None,
):
return self._build_with_temp_dir(
['bdist_wheel'], '.whl',
wheel_directory, config_settings,
)
def build_sdist(self, sdist_directory, config_settings=None):
return self._build_with_temp_dir(['sdist', '--formats', 'gztar'],
'.tar.gz', sdist_directory,
config_settings)
return self._build_with_temp_dir(
['sdist', '--formats', 'gztar'],
'.tar.gz', sdist_directory,
config_settings,
)
class _BuildMetaLegacyBackend(_BuildMetaBackend):
@ -238,6 +262,7 @@ class _BuildMetaLegacyBackend(_BuildMetaBackend):
packaging mechanism,
and will eventually be removed.
"""
def run_setup(self, setup_script='setup.py'):
# In order to maintain compatibility with scripts assuming that
# the setup.py script is in a directory on the PYTHONPATH, inject
@ -255,8 +280,7 @@ class _BuildMetaLegacyBackend(_BuildMetaBackend):
sys.argv[0] = setup_script
try:
super(_BuildMetaLegacyBackend,
self).run_setup(setup_script=setup_script)
super().run_setup(setup_script=setup_script)
finally:
# While PEP 517 frontends should be calling each hook in a fresh
# subprocess according to the standard (and thus it should not be

View file

@ -1,8 +1,11 @@
from distutils.command.bdist import bdist
from __future__ import annotations
import sys
from distutils.command.bdist import bdist
if 'egg' not in bdist.format_commands:
bdist.format_command['egg'] = ('bdist_egg', "Python .egg file")
bdist.format_command['egg'] = ('bdist_egg', 'Python .egg file')
bdist.format_commands.append('egg')
del bdist, sys

View file

@ -1,11 +1,14 @@
from distutils.errors import DistutilsOptionError
from __future__ import annotations
from setuptools.command.setopt import edit_config, option_base, config_file
from distutils.errors import DistutilsOptionError
from setuptools.command.setopt import config_file
from setuptools.command.setopt import edit_config
from setuptools.command.setopt import option_base
def shquote(arg):
"""Quote an argument for later parsing by shlex.split()"""
for c in '"', "'", "\\", "#":
for c in '"', "'", '\\', '#':
if c in arg:
return repr(arg)
if arg.split() != [arg]:
@ -16,7 +19,7 @@ def shquote(arg):
class alias(option_base):
"""Define a shortcut that invokes one or more commands"""
description = "define a shortcut to invoke one or more commands"
description = 'define a shortcut to invoke one or more commands'
command_consumes_arguments = True
user_options = [
@ -34,18 +37,18 @@ class alias(option_base):
option_base.finalize_options(self)
if self.remove and len(self.args) != 1:
raise DistutilsOptionError(
"Must specify exactly one argument (the alias name) when "
"using --remove"
'Must specify exactly one argument (the alias name) when '
'using --remove',
)
def run(self):
aliases = self.distribution.get_option_dict('aliases')
if not self.args:
print("Command Aliases")
print("---------------")
print('Command Aliases')
print('---------------')
for alias in aliases:
print("setup.py alias", format_alias(alias, aliases))
print('setup.py alias', format_alias(alias, aliases))
return
elif len(self.args) == 1:
@ -53,10 +56,10 @@ class alias(option_base):
if self.remove:
command = None
elif alias in aliases:
print("setup.py alias", format_alias(alias, aliases))
print('setup.py alias', format_alias(alias, aliases))
return
else:
print("No alias definition found for %r" % alias)
print('No alias definition found for %r' % alias)
return
else:
alias = self.args[0]

View file

@ -1,25 +1,29 @@
"""setuptools.command.bdist_egg
Build .egg distributions"""
from __future__ import annotations
from distutils.dir_util import remove_tree, mkpath
from distutils import log
from types import CodeType
import sys
import marshal
import os
import re
import sys
import textwrap
import marshal
from sysconfig import get_path
from sysconfig import get_python_version
from types import CodeType
from pkg_resources import get_build_platform, Distribution, ensure_directory
from setuptools.extension import Library
from distutils import log
from distutils.dir_util import mkpath
from distutils.dir_util import remove_tree
from pkg_resources import Distribution
from pkg_resources import ensure_directory
from pkg_resources import get_build_platform
from setuptools import Command
from sysconfig import get_path, get_python_version
from setuptools.extension import Library
def _get_purelib():
return get_path("purelib")
return get_path('purelib')
def strip_module(filename):
@ -60,23 +64,35 @@ class bdist_egg(Command):
description = "create an \"egg\" distribution"
user_options = [
('bdist-dir=', 'b',
"temporary directory for creating the distribution"),
('plat-name=', 'p', "platform name to embed in generated filenames "
"(default: %s)" % get_build_platform()),
('exclude-source-files', None,
"remove all .py files from the generated egg"),
('keep-temp', 'k',
"keep the pseudo-installation tree around after " +
"creating the distribution archive"),
('dist-dir=', 'd',
"directory to put final built distributions in"),
('skip-build', None,
"skip rebuilding everything (for testing/debugging)"),
(
'bdist-dir=', 'b',
'temporary directory for creating the distribution',
),
(
'plat-name=', 'p', 'platform name to embed in generated filenames '
'(default: %s)' % get_build_platform(),
),
(
'exclude-source-files', None,
'remove all .py files from the generated egg',
),
(
'keep-temp', 'k',
'keep the pseudo-installation tree around after ' +
'creating the distribution archive',
),
(
'dist-dir=', 'd',
'directory to put final built distributions in',
),
(
'skip-build', None,
'skip rebuilding everything (for testing/debugging)',
),
]
boolean_options = [
'keep-temp', 'skip-build', 'exclude-source-files'
'keep-temp', 'skip-build', 'exclude-source-files',
]
def initialize_options(self):
@ -89,7 +105,7 @@ class bdist_egg(Command):
self.exclude_source_files = None
def finalize_options(self):
ei_cmd = self.ei_cmd = self.get_finalized_command("egg_info")
ei_cmd = self.ei_cmd = self.get_finalized_command('egg_info')
self.egg_info = ei_cmd.egg_info
if self.bdist_dir is None:
@ -107,7 +123,7 @@ class bdist_egg(Command):
basename = Distribution(
None, None, ei_cmd.egg_name, ei_cmd.egg_version,
get_python_version(),
self.distribution.has_ext_modules() and self.plat_name
self.distribution.has_ext_modules() and self.plat_name,
).egg_name()
self.egg_output = os.path.join(self.dist_dir, basename + '.egg')
@ -125,14 +141,14 @@ class bdist_egg(Command):
realpath = os.path.realpath(item[0])
normalized = os.path.normcase(realpath)
if normalized == site_packages or normalized.startswith(
site_packages + os.sep
site_packages + os.sep,
):
item = realpath[len(site_packages) + 1:], item[1]
# XXX else: raise ???
self.distribution.data_files.append(item)
try:
log.info("installing package data to %s", self.bdist_dir)
log.info('installing package data to %s', self.bdist_dir)
self.call_command('install_data', force=0, root=None)
finally:
self.distribution.data_files = old
@ -152,10 +168,10 @@ class bdist_egg(Command):
def run(self): # noqa: C901 # is too complex (14) # FIXME
# Generate metadata first
self.run_command("egg_info")
self.run_command('egg_info')
# We run install_lib before install_data, because some data hacks
# pull their data path from the install_lib command.
log.info("installing library code to %s", self.bdist_dir)
log.info('installing library code to %s', self.bdist_dir)
instcmd = self.get_finalized_command('install')
old_root = instcmd.root
instcmd.root = None
@ -169,10 +185,12 @@ class bdist_egg(Command):
to_compile = []
for (p, ext_name) in enumerate(ext_outputs):
filename, ext = os.path.splitext(ext_name)
pyfile = os.path.join(self.bdist_dir, strip_module(filename) +
'.py')
pyfile = os.path.join(
self.bdist_dir, strip_module(filename) +
'.py',
)
self.stubs.append(pyfile)
log.info("creating stub loader for %s", ext_name)
log.info('creating stub loader for %s', ext_name)
if not self.dry_run:
write_stub(os.path.basename(ext_name), pyfile)
to_compile.append(pyfile)
@ -189,56 +207,61 @@ class bdist_egg(Command):
self.mkpath(egg_info)
if self.distribution.scripts:
script_dir = os.path.join(egg_info, 'scripts')
log.info("installing scripts to %s", script_dir)
self.call_command('install_scripts', install_dir=script_dir,
no_ep=1)
log.info('installing scripts to %s', script_dir)
self.call_command(
'install_scripts', install_dir=script_dir,
no_ep=1,
)
self.copy_metadata_to(egg_info)
native_libs = os.path.join(egg_info, "native_libs.txt")
native_libs = os.path.join(egg_info, 'native_libs.txt')
if all_outputs:
log.info("writing %s", native_libs)
log.info('writing %s', native_libs)
if not self.dry_run:
ensure_directory(native_libs)
libs_file = open(native_libs, 'wt')
libs_file = open(native_libs, 'w')
libs_file.write('\n'.join(all_outputs))
libs_file.write('\n')
libs_file.close()
elif os.path.isfile(native_libs):
log.info("removing %s", native_libs)
log.info('removing %s', native_libs)
if not self.dry_run:
os.unlink(native_libs)
write_safety_flag(
os.path.join(archive_root, 'EGG-INFO'), self.zip_safe()
os.path.join(archive_root, 'EGG-INFO'), self.zip_safe(),
)
if os.path.exists(os.path.join(self.egg_info, 'depends.txt')):
log.warn(
"WARNING: 'depends.txt' will not be used by setuptools 0.6!\n"
"Use the install_requires/extras_require setup() args instead."
'Use the install_requires/extras_require setup() args instead.',
)
if self.exclude_source_files:
self.zap_pyfiles()
# Make the archive
make_zipfile(self.egg_output, archive_root, verbose=self.verbose,
dry_run=self.dry_run, mode=self.gen_header())
make_zipfile(
self.egg_output, archive_root, verbose=self.verbose,
dry_run=self.dry_run, mode=self.gen_header(),
)
if not self.keep_temp:
remove_tree(self.bdist_dir, dry_run=self.dry_run)
# Add to 'Distribution.dist_files' so that the "upload" command works
getattr(self.distribution, 'dist_files', []).append(
('bdist_egg', get_python_version(), self.egg_output))
('bdist_egg', get_python_version(), self.egg_output),
)
def zap_pyfiles(self):
log.info("Removing .py files from temporary directory")
log.info('Removing .py files from temporary directory')
for base, dirs, files in walk_egg(self.bdist_dir):
for name in files:
path = os.path.join(base, name)
if name.endswith('.py'):
log.debug("Deleting %s", path)
log.debug('Deleting %s', path)
os.unlink(path)
if base.endswith('__pycache__'):
@ -247,10 +270,12 @@ class bdist_egg(Command):
pattern = r'(?P<name>.+)\.(?P<magic>[^.]+)\.pyc'
m = re.match(pattern, name)
path_new = os.path.join(
base, os.pardir, m.group('name') + '.pyc')
base, os.pardir, m.group('name') + '.pyc',
)
log.info(
"Renaming file from [%s] to [%s]"
% (path_old, path_new))
'Renaming file from [%s] to [%s]'
% (path_old, path_new),
)
try:
os.remove(path_new)
except OSError:
@ -261,14 +286,14 @@ class bdist_egg(Command):
safe = getattr(self.distribution, 'zip_safe', None)
if safe is not None:
return safe
log.warn("zip_safe flag not set; analyzing archive contents...")
log.warn('zip_safe flag not set; analyzing archive contents...')
return analyze_egg(self.bdist_dir, self.stubs)
def gen_header(self):
return 'w'
def copy_metadata_to(self, target_dir):
"Copy metadata (egg info) to the target_dir"
'Copy metadata (egg info) to the target_dir'
# normalize the path (so that a forward-slash in egg_info will
# match using startswith below)
norm_egg_info = os.path.normpath(self.egg_info)
@ -291,8 +316,10 @@ class bdist_egg(Command):
if os.path.splitext(filename)[1].lower() in NATIVE_EXTENSIONS:
all_outputs.append(paths[base] + filename)
for filename in dirs:
paths[os.path.join(base, filename)] = (paths[base] +
filename + '/')
paths[os.path.join(base, filename)] = (
paths[base] +
filename + '/'
)
if self.distribution.has_ext_modules():
build_cmd = self.get_finalized_command('build_ext')
@ -318,8 +345,7 @@ def walk_egg(egg_dir):
if 'EGG-INFO' in dirs:
dirs.remove('EGG-INFO')
yield base, dirs, files
for bdf in walker:
yield bdf
yield from walker
def analyze_egg(egg_dir, stubs):
@ -348,7 +374,7 @@ def write_safety_flag(egg_dir, safe):
if safe is None or bool(safe) != flag:
os.unlink(fn)
elif safe is not None and bool(safe) == flag:
f = open(fn, 'wt')
f = open(fn, 'w')
f.write('\n')
f.close()
@ -367,10 +393,7 @@ def scan_module(egg_dir, base, name, stubs):
return True # Extension module
pkg = base[len(egg_dir) + 1:].replace(os.sep, '.')
module = pkg + (pkg and '.' or '') + os.path.splitext(name)[0]
if sys.version_info < (3, 7):
skip = 12 # skip magic & date & file size
else:
skip = 16 # skip magic & reserved? & date & file size
skip = 16 # skip magic & reserved? & date & file size
f = open(filename, 'rb')
f.read(skip)
code = marshal.load(f)
@ -379,51 +402,53 @@ def scan_module(egg_dir, base, name, stubs):
symbols = dict.fromkeys(iter_symbols(code))
for bad in ['__file__', '__path__']:
if bad in symbols:
log.warn("%s: module references %s", module, bad)
log.warn('%s: module references %s', module, bad)
safe = False
if 'inspect' in symbols:
for bad in [
'getsource', 'getabsfile', 'getsourcefile', 'getfile'
'getsourcelines', 'findsource', 'getcomments', 'getframeinfo',
'getinnerframes', 'getouterframes', 'stack', 'trace'
'getinnerframes', 'getouterframes', 'stack', 'trace',
]:
if bad in symbols:
log.warn("%s: module MAY be using inspect.%s", module, bad)
log.warn('%s: module MAY be using inspect.%s', module, bad)
safe = False
return safe
def iter_symbols(code):
"""Yield names and strings used by `code` and its nested code objects"""
for name in code.co_names:
yield name
yield from code.co_names
for const in code.co_consts:
if isinstance(const, str):
yield const
elif isinstance(const, CodeType):
for name in iter_symbols(const):
yield name
yield from iter_symbols(const)
def can_scan():
if not sys.platform.startswith('java') and sys.platform != 'cli':
# CPython, PyPy, etc.
return True
log.warn("Unable to analyze compiled code on this platform.")
log.warn("Please ask the author to include a 'zip_safe'"
" setting (either True or False) in the package's setup.py")
log.warn('Unable to analyze compiled code on this platform.')
log.warn(
"Please ask the author to include a 'zip_safe'"
" setting (either True or False) in the package's setup.py",
)
# Attribute names of options for commands that might need to be convinced to
# install to the egg build directory
INSTALL_DIRECTORY_ATTRS = [
'install_lib', 'install_dir', 'install_data', 'install_base'
'install_lib', 'install_dir', 'install_data', 'install_base',
]
def make_zipfile(zip_filename, base_dir, verbose=0, dry_run=0, compress=True,
mode='w'):
def make_zipfile(
zip_filename, base_dir, verbose=0, dry_run=0, compress=True,
mode='w',
):
"""Create a zip file from all the files under 'base_dir'. The output
zip file will be named 'base_dir' + ".zip". Uses either the "zipfile"
Python module (if available) or the InfoZIP "zip" utility (if installed

View file

@ -1,6 +1,8 @@
import distutils.command.bdist_rpm as orig
from __future__ import annotations
import warnings
import distutils.command.bdist_rpm as orig
from setuptools import SetuptoolsDeprecationWarning
@ -15,8 +17,8 @@ class bdist_rpm(orig.bdist_rpm):
def run(self):
warnings.warn(
"bdist_rpm is deprecated and will be removed in a future "
"version. Use bdist_wheel (wheel packages) instead.",
'bdist_rpm is deprecated and will be removed in a future '
'version. Use bdist_wheel (wheel packages) instead.',
SetuptoolsDeprecationWarning,
)
@ -29,11 +31,11 @@ class bdist_rpm(orig.bdist_rpm):
spec = orig.bdist_rpm._make_spec_file(self)
spec = [
line.replace(
"setup.py install ",
"setup.py install --single-version-externally-managed "
'setup.py install ',
'setup.py install --single-version-externally-managed ',
).replace(
"%setup",
"%setup -n %{name}-%{unmangled_version}"
'%setup',
'%setup -n %{name}-%{unmangled_version}',
)
for line in spec
]

View file

@ -1,6 +1,8 @@
from __future__ import annotations
import distutils.command.build_clib as orig
from distutils.errors import DistutilsSetupError
from distutils import log
from distutils.errors import DistutilsSetupError
from setuptools.dep_util import newer_pairwise_group
@ -27,7 +29,8 @@ class build_clib(orig.build_clib):
raise DistutilsSetupError(
"in 'libraries' option (library '%s'), "
"'sources' must be present and must be "
"a list of source filenames" % lib_name)
'a list of source filenames' % lib_name,
)
sources = list(sources)
log.info("building '%s' library", lib_name)
@ -40,7 +43,8 @@ class build_clib(orig.build_clib):
raise DistutilsSetupError(
"in 'libraries' option (library '%s'), "
"'obj_deps' must be a dictionary of "
"type 'source: list'" % lib_name)
"type 'source: list'" % lib_name,
)
dependencies = []
# Get the global dependencies that are specified by the '' key.
@ -50,7 +54,8 @@ class build_clib(orig.build_clib):
raise DistutilsSetupError(
"in 'libraries' option (library '%s'), "
"'obj_deps' must be a dictionary of "
"type 'source: list'" % lib_name)
"type 'source: list'" % lib_name,
)
# Build the list to be used by newer_pairwise_group
# each source will be auto-added to its dependencies.
@ -62,7 +67,8 @@ class build_clib(orig.build_clib):
raise DistutilsSetupError(
"in 'libraries' option (library '%s'), "
"'obj_deps' must be a dictionary of "
"type 'source: list'" % lib_name)
"type 'source: list'" % lib_name,
)
src_deps.extend(extra_deps)
dependencies.append(src_deps)
@ -72,8 +78,8 @@ class build_clib(orig.build_clib):
)
if (
newer_pairwise_group(dependencies, expected_objects)
!= ([], [])
newer_pairwise_group(dependencies, expected_objects) !=
([], [])
):
# First, compile the source code to object files in the library
# directory. (This should probably change to putting object
@ -87,7 +93,7 @@ class build_clib(orig.build_clib):
macros=macros,
include_dirs=include_dirs,
extra_postargs=cflags,
debug=self.debug
debug=self.debug,
)
# Now "link" the object files together into a static library.
@ -97,5 +103,5 @@ class build_clib(orig.build_clib):
expected_objects,
lib_name,
output_dir=self.build_clib,
debug=self.debug
debug=self.debug,
)

View file

@ -1,14 +1,17 @@
from __future__ import annotations
import itertools
import os
import sys
import itertools
from importlib.machinery import EXTENSION_SUFFIXES
from distutils.command.build_ext import build_ext as _du_build_ext
from distutils.file_util import copy_file
from distutils.ccompiler import new_compiler
from distutils.sysconfig import customize_compiler, get_config_var
from distutils.errors import DistutilsError
from distutils import log
from distutils import log
from distutils.ccompiler import new_compiler
from distutils.command.build_ext import build_ext as _du_build_ext
from distutils.errors import DistutilsError
from distutils.file_util import copy_file
from distutils.sysconfig import customize_compiler
from distutils.sysconfig import get_config_var
from setuptools.extension import Library
try:
@ -21,12 +24,12 @@ except ImportError:
_build_ext = _du_build_ext
# make sure _config_vars is initialized
get_config_var("LDSHARED")
get_config_var('LDSHARED')
from distutils.sysconfig import _config_vars as _CONFIG_VARS # noqa
def _customize_compiler_for_shlib(compiler):
if sys.platform == "darwin":
if sys.platform == 'darwin':
# building .dylib requires additional compiler flags on OSX; here we
# temporarily substitute the pyconfig.h variables so that distutils'
# 'customize_compiler' uses them before we build the shared libraries.
@ -34,9 +37,10 @@ def _customize_compiler_for_shlib(compiler):
try:
# XXX Help! I don't have any idea whether these are right...
_CONFIG_VARS['LDSHARED'] = (
"gcc -Wl,-x -dynamiclib -undefined dynamic_lookup")
_CONFIG_VARS['CCSHARED'] = " -dynamiclib"
_CONFIG_VARS['SO'] = ".dylib"
'gcc -Wl,-x -dynamiclib -undefined dynamic_lookup'
)
_CONFIG_VARS['CCSHARED'] = ' -dynamiclib'
_CONFIG_VARS['SO'] = '.dylib'
customize_compiler(compiler)
finally:
_CONFIG_VARS.clear()
@ -49,7 +53,7 @@ have_rtld = False
use_stubs = False
libtype = 'shared'
if sys.platform == "darwin":
if sys.platform == 'darwin':
use_stubs = True
elif os.name != 'nt':
try:
@ -89,8 +93,10 @@ class build_ext(_build_ext):
modpath = fullname.split('.')
package = '.'.join(modpath[:-1])
package_dir = build_py.get_package_dir(package)
dest_filename = os.path.join(package_dir,
os.path.basename(filename))
dest_filename = os.path.join(
package_dir,
os.path.basename(filename),
)
src_filename = os.path.join(self.build_lib, filename)
# Always copy, even if source is older than destination, to ensure
@ -98,7 +104,7 @@ class build_ext(_build_ext):
# used.
copy_file(
src_filename, dest_filename, verbose=self.verbose,
dry_run=self.dry_run
dry_run=self.dry_run,
)
if ext._needs_stub:
self.write_stub(package_dir or os.curdir, ext, True)
@ -136,8 +142,10 @@ class build_ext(_build_ext):
_build_ext.finalize_options(self)
self.extensions = self.extensions or []
self.check_extensions_list(self.extensions)
self.shlibs = [ext for ext in self.extensions
if isinstance(ext, Library)]
self.shlibs = [
ext for ext in self.extensions
if isinstance(ext, Library)
]
if self.shlibs:
self.setup_shlib_compiler()
for ext in self.extensions:
@ -163,7 +171,7 @@ class build_ext(_build_ext):
def setup_shlib_compiler(self):
compiler = self.shlib_compiler = new_compiler(
compiler=self.compiler, dry_run=self.dry_run, force=self.force
compiler=self.compiler, dry_run=self.dry_run, force=self.force,
)
_customize_compiler_for_shlib(compiler)
@ -236,52 +244,60 @@ class build_ext(_build_ext):
yield '.pyo'
def write_stub(self, output_dir, ext, compile=False):
log.info("writing stub loader for %s to %s", ext._full_name,
output_dir)
stub_file = (os.path.join(output_dir, *ext._full_name.split('.')) +
'.py')
log.info(
'writing stub loader for %s to %s', ext._full_name,
output_dir,
)
stub_file = (
os.path.join(output_dir, *ext._full_name.split('.')) +
'.py'
)
if compile and os.path.exists(stub_file):
raise DistutilsError(stub_file + " already exists! Please delete.")
raise DistutilsError(stub_file + ' already exists! Please delete.')
if not self.dry_run:
f = open(stub_file, 'w')
f.write(
'\n'.join([
"def __bootstrap__():",
" global __bootstrap__, __file__, __loader__",
" import sys, os, pkg_resources, importlib.util" +
if_dl(", dl"),
" __file__ = pkg_resources.resource_filename"
"(__name__,%r)"
'def __bootstrap__():',
' global __bootstrap__, __file__, __loader__',
' import sys, os, pkg_resources, importlib.util' +
if_dl(', dl'),
' __file__ = pkg_resources.resource_filename'
'(__name__,%r)'
% os.path.basename(ext._file_name),
" del __bootstrap__",
' del __bootstrap__',
" if '__loader__' in globals():",
" del __loader__",
if_dl(" old_flags = sys.getdlopenflags()"),
" old_dir = os.getcwd()",
" try:",
" os.chdir(os.path.dirname(__file__))",
if_dl(" sys.setdlopenflags(dl.RTLD_NOW)"),
" spec = importlib.util.spec_from_file_location(",
" __name__, __file__)",
" mod = importlib.util.module_from_spec(spec)",
" spec.loader.exec_module(mod)",
" finally:",
if_dl(" sys.setdlopenflags(old_flags)"),
" os.chdir(old_dir)",
"__bootstrap__()",
"" # terminal \n
])
' del __loader__',
if_dl(' old_flags = sys.getdlopenflags()'),
' old_dir = os.getcwd()',
' try:',
' os.chdir(os.path.dirname(__file__))',
if_dl(' sys.setdlopenflags(dl.RTLD_NOW)'),
' spec = importlib.util.spec_from_file_location(',
' __name__, __file__)',
' mod = importlib.util.module_from_spec(spec)',
' spec.loader.exec_module(mod)',
' finally:',
if_dl(' sys.setdlopenflags(old_flags)'),
' os.chdir(old_dir)',
'__bootstrap__()',
'', # terminal \n
]),
)
f.close()
if compile:
from distutils.util import byte_compile
byte_compile([stub_file], optimize=0,
force=True, dry_run=self.dry_run)
byte_compile(
[stub_file], optimize=0,
force=True, dry_run=self.dry_run,
)
optimize = self.get_finalized_command('install_lib').optimize
if optimize > 0:
byte_compile([stub_file], optimize=optimize,
force=True, dry_run=self.dry_run)
byte_compile(
[stub_file], optimize=optimize,
force=True, dry_run=self.dry_run,
)
if os.path.exists(stub_file) and not self.dry_run:
os.unlink(stub_file)
@ -293,12 +309,13 @@ if use_stubs or os.name == 'nt':
self, objects, output_libname, output_dir=None, libraries=None,
library_dirs=None, runtime_library_dirs=None, export_symbols=None,
debug=0, extra_preargs=None, extra_postargs=None, build_temp=None,
target_lang=None):
target_lang=None,
):
self.link(
self.SHARED_LIBRARY, objects, output_libname,
output_dir, libraries, library_dirs, runtime_library_dirs,
export_symbols, debug, extra_preargs, extra_postargs,
build_temp, target_lang
build_temp, target_lang,
)
else:
# Build static libraries everywhere else
@ -308,7 +325,8 @@ else:
self, objects, output_libname, output_dir=None, libraries=None,
library_dirs=None, runtime_library_dirs=None, export_symbols=None,
debug=0, extra_preargs=None, extra_postargs=None, build_temp=None,
target_lang=None):
target_lang=None,
):
# XXX we need to either disallow these attrs on Library instances,
# or warn/abort here if set, or something...
# libraries=None, library_dirs=None, runtime_library_dirs=None,
@ -318,11 +336,11 @@ else:
assert output_dir is None # distutils build_ext doesn't pass this
output_dir, filename = os.path.split(output_libname)
basename, ext = os.path.splitext(filename)
if self.library_filename("x").startswith('lib'):
if self.library_filename('x').startswith('lib'):
# strip 'lib' prefix; this is kludgy if some platform uses
# a different prefix
basename = basename[3:]
self.create_static_lib(
objects, basename, output_dir, debug, target_lang
objects, basename, output_dir, debug, target_lang,
)

View file

@ -1,13 +1,16 @@
from glob import glob
from distutils.util import convert_path
import distutils.command.build_py as orig
import os
from __future__ import annotations
import fnmatch
import textwrap
import io
import distutils.errors
import itertools
import os
import stat
import textwrap
from glob import glob
import distutils.command.build_py as orig
import distutils.errors
from distutils.util import convert_path
from setuptools.extern.more_itertools import unique_everseen
@ -50,7 +53,7 @@ class build_py(orig.build_py):
self.byte_compile(orig.build_py.get_outputs(self, include_bytecode=0))
def __getattr__(self, attr):
"lazily compute data files"
'lazily compute data files'
if attr == 'data_files':
self.data_files = self._get_data_files()
return self.data_files
@ -155,14 +158,14 @@ class build_py(orig.build_py):
else:
return init_py
with io.open(init_py, 'rb') as f:
with open(init_py, 'rb') as f:
contents = f.read()
if b'declare_namespace' not in contents:
raise distutils.errors.DistutilsError(
"Namespace package problem: %s is a namespace package, but "
"its\n__init__.py does not call declare_namespace()! Please "
'Namespace package problem: %s is a namespace package, but '
'its\n__init__.py does not call declare_namespace()! Please '
'fix it.\n(See the setuptools manual under '
'"Namespace Packages" for details.)\n"' % (package,)
'"Namespace Packages" for details.)\n"' % (package,),
)
return init_py
@ -225,7 +228,7 @@ def assert_relative(path):
setup() arguments must *always* be /-separated paths relative to the
setup.py directory, *never* absolute paths.
"""
""",
).lstrip()
% path
)

View file

@ -1,14 +1,17 @@
from distutils.util import convert_path
from distutils import log
from distutils.errors import DistutilsError, DistutilsOptionError
import os
from __future__ import annotations
import glob
import io
import os
import pkg_resources
from setuptools.command.easy_install import easy_install
from setuptools import namespaces
import setuptools
from distutils import log
from distutils.errors import DistutilsError
from distutils.errors import DistutilsOptionError
from distutils.util import convert_path
from setuptools import namespaces
from setuptools.command.easy_install import easy_install
class develop(namespaces.DevelopInstaller, easy_install):
@ -17,8 +20,8 @@ class develop(namespaces.DevelopInstaller, easy_install):
description = "install package in 'development mode'"
user_options = easy_install.user_options + [
("uninstall", "u", "Uninstall this source package"),
("egg-path=", None, "Set the path to be used in the .egg-link file"),
('uninstall', 'u', 'Uninstall this source package'),
('egg-path=', None, 'Set the path to be used in the .egg-link file'),
]
boolean_options = easy_install.boolean_options + ['uninstall']
@ -42,7 +45,7 @@ class develop(namespaces.DevelopInstaller, easy_install):
self.always_copy_from = '.' # always copy eggs installed in curdir
def finalize_options(self):
ei = self.get_finalized_command("egg_info")
ei = self.get_finalized_command('egg_info')
if ei.broken_egg_info:
template = "Please rename %r to %r before using 'develop'"
args = ei.egg_info, ei.broken_egg_info
@ -63,12 +66,12 @@ class develop(namespaces.DevelopInstaller, easy_install):
target = pkg_resources.normalize_path(self.egg_base)
egg_path = pkg_resources.normalize_path(
os.path.join(self.install_dir, self.egg_path)
os.path.join(self.install_dir, self.egg_path),
)
if egg_path != target:
raise DistutilsOptionError(
"--egg-path must be a relative path from the install"
" directory to " + target
'--egg-path must be a relative path from the install'
' directory to ' + target,
)
# Make a distribution for the package's source
@ -95,12 +98,12 @@ class develop(namespaces.DevelopInstaller, easy_install):
if path_to_setup != os.curdir:
path_to_setup = '../' * (path_to_setup.count('/') + 1)
resolved = pkg_resources.normalize_path(
os.path.join(install_dir, egg_path, path_to_setup)
os.path.join(install_dir, egg_path, path_to_setup),
)
if resolved != pkg_resources.normalize_path(os.curdir):
raise DistutilsOptionError(
"Can't get a consistent path to setup script from"
" installation directory",
' installation directory',
resolved,
pkg_resources.normalize_path(os.curdir),
)
@ -120,22 +123,22 @@ class develop(namespaces.DevelopInstaller, easy_install):
self.install_namespaces()
# create an .egg-link in the installation dir, pointing to our egg
log.info("Creating %s (link to %s)", self.egg_link, self.egg_base)
log.info('Creating %s (link to %s)', self.egg_link, self.egg_base)
if not self.dry_run:
with open(self.egg_link, "w") as f:
f.write(self.egg_path + "\n" + self.setup_path)
with open(self.egg_link, 'w') as f:
f.write(self.egg_path + '\n' + self.setup_path)
# postprocess the installed distro, fixing up .pth, installing scripts,
# and handling requirements
self.process_distribution(None, self.dist, not self.no_deps)
def uninstall_link(self):
if os.path.exists(self.egg_link):
log.info("Removing %s (link to %s)", self.egg_link, self.egg_base)
log.info('Removing %s (link to %s)', self.egg_link, self.egg_base)
egg_link_file = open(self.egg_link)
contents = [line.rstrip() for line in egg_link_file]
egg_link_file.close()
if contents not in ([self.egg_path], [self.egg_path, self.setup_path]):
log.warn("Link points to %s: uninstall aborted", contents)
log.warn('Link points to %s: uninstall aborted', contents)
return
if not self.dry_run:
os.unlink(self.egg_link)
@ -143,7 +146,7 @@ class develop(namespaces.DevelopInstaller, easy_install):
self.update_pth(self.dist) # remove any .pth link to us
if self.distribution.scripts:
# XXX should also check for entry point scripts!
log.warn("Note: you must uninstall or replace scripts manually!")
log.warn('Note: you must uninstall or replace scripts manually!')
def install_egg_scripts(self, dist):
if dist is not self.dist:
@ -159,7 +162,7 @@ class develop(namespaces.DevelopInstaller, easy_install):
for script_name in self.distribution.scripts or []:
script_path = os.path.abspath(convert_path(script_name))
script_name = os.path.basename(script_path)
with io.open(script_path) as strm:
with open(script_path) as strm:
script_text = strm.read()
self.install_script(dist, script_name, script_text, script_path)

View file

@ -2,11 +2,12 @@
Create a dist_info directory
As defined in the wheel specification
"""
from __future__ import annotations
import os
from distutils.core import Command
from distutils import log
from distutils.core import Command
class dist_info(Command):
@ -14,8 +15,10 @@ class dist_info(Command):
description = 'create a .dist-info directory'
user_options = [
('egg-base=', 'e', "directory containing .egg-info directories"
" (default: top of the source tree)"),
(
'egg-base=', 'e', 'directory containing .egg-info directories'
' (default: top of the source tree)',
),
]
def initialize_options(self):
@ -30,7 +33,7 @@ class dist_info(Command):
egg_info.finalize_options()
egg_info.run()
dist_info_dir = egg_info.egg_info[:-len('.egg-info')] + '.dist-info'
log.info("creating '{}'".format(os.path.abspath(dist_info_dir)))
log.info(f"creating '{os.path.abspath(dist_info_dir)}'")
bdist_wheel = self.get_finalized_command('bdist_wheel')
bdist_wheel.egg2dist(egg_info.egg_info, dist_info_dir)

View file

@ -1,35 +1,40 @@
"""setuptools.command.egg_info
Create a distribution's .egg-info directory and contents"""
from __future__ import annotations
from distutils.filelist import FileList as _FileList
from distutils.errors import DistutilsInternalError
from distutils.util import convert_path
from distutils import log
import distutils.errors
import distutils.filelist
import collections
import functools
import io
import os
import re
import sys
import io
import warnings
import time
import collections
import warnings
import distutils.errors
import distutils.filelist
import setuptools.unicode_utils as unicode_utils
from distutils import log
from distutils.errors import DistutilsInternalError
from distutils.filelist import FileList as _FileList
from distutils.util import convert_path
from pkg_resources import EntryPoint
from pkg_resources import iter_entry_points
from pkg_resources import parse_requirements
from pkg_resources import parse_version
from pkg_resources import safe_name
from pkg_resources import safe_version
from pkg_resources import to_filename
from pkg_resources import yield_lines
from setuptools import Command
from setuptools import SetuptoolsDeprecationWarning
from setuptools.command import bdist_egg
from setuptools.command.sdist import sdist
from setuptools.command.sdist import walk_revctrl
from setuptools.command.setopt import edit_config
from setuptools.command import bdist_egg
from pkg_resources import (
parse_requirements, safe_name, parse_version,
safe_version, yield_lines, EntryPoint, iter_entry_points, to_filename)
import setuptools.unicode_utils as unicode_utils
from setuptools.glob import glob
from setuptools.extern import packaging
from setuptools import SetuptoolsDeprecationWarning
from setuptools.glob import glob
def translate_pattern(glob): # noqa: C901 # is too complex (14) # FIXME
@ -45,7 +50,7 @@ def translate_pattern(glob): # noqa: C901 # is too complex (14) # FIXME
chunks = glob.split(os.path.sep)
sep = re.escape(os.sep)
valid_char = '[^%s]' % (sep,)
valid_char = '[^{}]'.format(sep)
for c, chunk in enumerate(chunks):
last_chunk = c == len(chunks) - 1
@ -57,7 +62,7 @@ def translate_pattern(glob): # noqa: C901 # is too complex (14) # FIXME
pat += '.*'
else:
# Match '(name/)*'
pat += '(?:%s+%s)*' % (valid_char, sep)
pat += '(?:{}+{})*'.format(valid_char, sep)
continue # Break here as the whole path component has been handled
# Find any special characters in the remainder
@ -99,7 +104,7 @@ def translate_pattern(glob): # noqa: C901 # is too complex (14) # FIXME
inner = inner[1:]
char_class += re.escape(inner)
pat += '[%s]' % (char_class,)
pat += '[{}]'.format(char_class)
# Skip to the end ]
i = inner_i
@ -141,7 +146,7 @@ class InfoCommon:
if self.tag_build:
version += self.tag_build
if self.tag_date:
version += time.strftime("-%Y%m%d")
version += time.strftime('-%Y%m%d')
return version
vtags = property(tags)
@ -150,10 +155,12 @@ class egg_info(InfoCommon, Command):
description = "create a distribution's .egg-info directory"
user_options = [
('egg-base=', 'e', "directory containing .egg-info directories"
" (default: top of the source tree)"),
('tag-date', 'd', "Add date stamp (e.g. 20050528) to version number"),
('tag-build=', 'b', "Specify explicit tag to add to version number"),
(
'egg-base=', 'e', 'directory containing .egg-info directories'
' (default: top of the source tree)',
),
('tag-date', 'd', 'Add date stamp (e.g. 20050528) to version number'),
('tag-build=', 'b', 'Specify explicit tag to add to version number'),
('no-date', 'D', "Don't include date stamp [default]"),
]
@ -206,15 +213,15 @@ class egg_info(InfoCommon, Command):
try:
is_version = isinstance(parsed_version, packaging.version.Version)
spec = (
"%s==%s" if is_version else "%s===%s"
'%s==%s' if is_version else '%s===%s'
)
list(
parse_requirements(spec % (self.egg_name, self.egg_version))
parse_requirements(spec % (self.egg_name, self.egg_version)),
)
except ValueError as e:
raise distutils.errors.DistutilsOptionError(
"Invalid distribution name or version syntax: %s-%s" %
(self.egg_name, self.egg_version)
'Invalid distribution name or version syntax: %s-%s' %
(self.egg_name, self.egg_version),
) from e
if self.egg_base is None:
@ -257,7 +264,7 @@ class egg_info(InfoCommon, Command):
elif os.path.exists(filename):
if data is None and not force:
log.warn(
"%s not set in setup(), but %s exists", what, filename
'%s not set in setup(), but %s exists', what, filename,
)
return
else:
@ -269,8 +276,8 @@ class egg_info(InfoCommon, Command):
`what` is used in a log message to identify what is being written
to the file.
"""
log.info("writing %s to %s", what, filename)
data = data.encode("utf-8")
log.info('writing %s to %s', what, filename)
data = data.encode('utf-8')
if not self.dry_run:
f = open(filename, 'wb')
f.write(data)
@ -278,7 +285,7 @@ class egg_info(InfoCommon, Command):
def delete_file(self, filename):
"""Delete `filename` (if not a dry run) after announcing it"""
log.info("deleting %s", filename)
log.info('deleting %s', filename)
if not self.dry_run:
os.unlink(filename)
@ -292,7 +299,7 @@ class egg_info(InfoCommon, Command):
writer(self, ep.name, os.path.join(self.egg_info, ep.name))
# Get rid of native_libs.txt if it was put there by older bdist_egg
nl = os.path.join(self.egg_info, "native_libs.txt")
nl = os.path.join(self.egg_info, 'native_libs.txt')
if os.path.exists(nl):
self.delete_file(nl)
@ -300,7 +307,7 @@ class egg_info(InfoCommon, Command):
def find_sources(self):
"""Generate SOURCES.txt manifest file"""
manifest_filename = os.path.join(self.egg_info, "SOURCES.txt")
manifest_filename = os.path.join(self.egg_info, 'SOURCES.txt')
mm = manifest_maker(self.distribution)
mm.manifest = manifest_filename
mm.run()
@ -312,11 +319,11 @@ class egg_info(InfoCommon, Command):
bei = os.path.join(self.egg_base, bei)
if os.path.exists(bei):
log.warn(
"-" * 78 + '\n'
'-' * 78 + '\n'
"Note: Your current .egg-info directory has a '-' in its name;"
'\nthis will not work correctly with "setup.py develop".\n\n'
'Please rename %s to %s to correct this problem.\n' + '-' * 78,
bei, self.egg_info
bei, self.egg_info,
)
self.broken_egg_info = self.egg_info
self.egg_info = bei # make it work for now
@ -350,15 +357,15 @@ class FileList(_FileList):
log_map = {
'include': "warning: no files found matching '%s'",
'exclude': (
"warning: no previously-included files found "
'warning: no previously-included files found '
"matching '%s'"
),
'global-include': (
"warning: no files found matching '%s' "
"anywhere in distribution"
'anywhere in distribution'
),
'global-exclude': (
"warning: no previously-included files matching "
'warning: no previously-included files matching '
"'%s' found anywhere in distribution"
),
'recursive-include': (
@ -366,7 +373,7 @@ class FileList(_FileList):
"under directory '%s'"
),
'recursive-exclude': (
"warning: no previously-included files matching "
'warning: no previously-included files matching '
"'%s' found under directory '%s'"
),
'graft': "warning: no directories found matching '%s'",
@ -388,7 +395,7 @@ class FileList(_FileList):
action_is_recursive = action.startswith('recursive-')
if action in {'graft', 'prune'}:
patterns = [dir_pattern]
extra_log_args = (dir, ) if action_is_recursive else ()
extra_log_args = (dir,) if action_is_recursive else ()
log_tmpl = log_map[action]
self.debug_print(
@ -396,7 +403,7 @@ class FileList(_FileList):
[action] +
([dir] if action_is_recursive else []) +
patterns,
)
),
)
for pattern in patterns:
if not process_action(pattern):
@ -410,7 +417,7 @@ class FileList(_FileList):
found = False
for i in range(len(self.files) - 1, -1, -1):
if predicate(self.files[i]):
self.debug_print(" removing " + self.files[i])
self.debug_print(' removing ' + self.files[i])
del self.files[i]
found = True
return found
@ -431,8 +438,10 @@ class FileList(_FileList):
Include all files anywhere in 'dir/' that match the pattern.
"""
full_pattern = os.path.join(dir, '**', pattern)
found = [f for f in glob(full_pattern, recursive=True)
if not os.path.isdir(f)]
found = [
f for f in glob(full_pattern, recursive=True)
if not os.path.isdir(f)
]
self.extend(found)
return bool(found)
@ -508,7 +517,7 @@ class FileList(_FileList):
return False
# Must ensure utf-8 encodability
utf8_path = unicode_utils.try_encode(u_path, "utf-8")
utf8_path = unicode_utils.try_encode(u_path, 'utf-8')
if utf8_path is None:
log.warn(enc_warn, path, 'utf-8')
return False
@ -523,7 +532,7 @@ class FileList(_FileList):
class manifest_maker(sdist):
template = "MANIFEST.in"
template = 'MANIFEST.in'
def initialize_options(self):
self.use_defaults = 1
@ -572,7 +581,7 @@ class manifest_maker(sdist):
"""
suppress missing-file warnings from sdist
"""
return re.match(r"standard file .*not found", msg)
return re.match(r'standard file .*not found', msg)
def add_defaults(self):
sdist.add_defaults(self)
@ -584,10 +593,10 @@ class manifest_maker(sdist):
elif os.path.exists(self.manifest):
self.read_manifest()
if os.path.exists("setup.py"):
if os.path.exists('setup.py'):
# setup.py should be included by default, even if it's not
# the script called to create the sdist
self.filelist.append("setup.py")
self.filelist.append('setup.py')
ei_cmd = self.get_finalized_command('egg_info')
self.filelist.graft(ei_cmd.egg_info)
@ -605,25 +614,27 @@ class manifest_maker(sdist):
self.filelist.prune(build.build_base)
self.filelist.prune(base_dir)
sep = re.escape(os.sep)
self.filelist.exclude_pattern(r'(^|' + sep + r')(RCS|CVS|\.svn)' + sep,
is_regex=1)
self.filelist.exclude_pattern(
r'(^|' + sep + r')(RCS|CVS|\.svn)' + sep,
is_regex=1,
)
def write_file(filename, contents):
"""Create a file with the specified name and write 'contents' (a
sequence of strings without line terminators) to it.
"""
contents = "\n".join(contents)
contents = '\n'.join(contents)
# assuming the contents has been vetted for utf-8 encoding
contents = contents.encode("utf-8")
contents = contents.encode('utf-8')
with open(filename, "wb") as f: # always write POSIX-style manifest
with open(filename, 'wb') as f: # always write POSIX-style manifest
f.write(contents)
def write_pkg_info(cmd, basename, filename):
log.info("writing %s", filename)
log.info('writing %s', filename)
if not cmd.dry_run:
metadata = cmd.distribution.metadata
metadata.version, oldver = cmd.egg_version, metadata.version
@ -645,7 +656,7 @@ def warn_depends_obsolete(cmd, basename, filename):
if os.path.exists(filename):
log.warn(
"WARNING: 'depends.txt' is not used by setuptools 0.6!\n"
"Use the install_requires/extras_require setup() args instead."
'Use the install_requires/extras_require setup() args instead.',
)
@ -666,13 +677,13 @@ def write_requirements(cmd, basename, filename):
for extra in sorted(extras_require):
data.write('\n[{extra}]\n'.format(**vars()))
_write_requirements(data, extras_require[extra])
cmd.write_or_delete_file("requirements", filename, data.getvalue())
cmd.write_or_delete_file('requirements', filename, data.getvalue())
def write_setup_requirements(cmd, basename, filename):
data = io.StringIO()
_write_requirements(data, cmd.distribution.setup_requires)
cmd.write_or_delete_file("setup-requirements", filename, data.getvalue())
cmd.write_or_delete_file('setup-requirements', filename, data.getvalue())
def write_toplevel_names(cmd, basename, filename):
@ -680,9 +691,9 @@ def write_toplevel_names(cmd, basename, filename):
[
k.split('.', 1)[0]
for k in cmd.distribution.iter_distribution_names()
]
],
)
cmd.write_file("top-level names", filename, '\n'.join(sorted(pkgs)) + '\n')
cmd.write_file('top-level names', filename, '\n'.join(sorted(pkgs)) + '\n')
def overwrite_arg(cmd, basename, filename):
@ -708,7 +719,7 @@ def write_entries(cmd, basename, filename):
if not isinstance(contents, str):
contents = EntryPoint.parse_group(section, contents)
contents = '\n'.join(sorted(map(str, contents.values())))
data.append('[%s]\n%s\n\n' % (section, contents))
data.append('[{}]\n{}\n\n'.format(section, contents))
data = ''.join(data)
cmd.write_or_delete_file('entry points', filename, data, True)
@ -720,11 +731,12 @@ def get_pkg_info_revision():
a subversion revision.
"""
warnings.warn(
"get_pkg_info_revision is deprecated.", EggInfoDeprecationWarning)
'get_pkg_info_revision is deprecated.', EggInfoDeprecationWarning,
)
if os.path.exists('PKG-INFO'):
with io.open('PKG-INFO') as f:
with open('PKG-INFO') as f:
for line in f:
match = re.match(r"Version:.*-r(\d+)\s*$", line)
match = re.match(r'Version:.*-r(\d+)\s*$', line)
if match:
return int(match.group(1))
return 0

View file

@ -1,11 +1,13 @@
from distutils.errors import DistutilsArgError
import inspect
import glob
import warnings
import platform
import distutils.command.install as orig
from __future__ import annotations
import glob
import inspect
import platform
import warnings
import distutils.command.install as orig
import setuptools
from distutils.errors import DistutilsArgError
# Prior to numpy 1.9, NumPy relies on the '_install' name, so provide it for
# now. See https://github.com/pypa/setuptools/issues/199/
@ -16,9 +18,11 @@ class install(orig.install):
"""Use easy_install to install the package, w/dependencies"""
user_options = orig.install.user_options + [
('old-and-unmanageable', None, "Try not to use this!"),
('single-version-externally-managed', None,
"used by system package builders to create 'flat' eggs"),
('old-and-unmanageable', None, 'Try not to use this!'),
(
'single-version-externally-managed', None,
"used by system package builders to create 'flat' eggs",
),
]
boolean_options = orig.install.boolean_options + [
'old-and-unmanageable', 'single-version-externally-managed',
@ -41,8 +45,8 @@ class install(orig.install):
elif self.single_version_externally_managed:
if not self.root and not self.record:
raise DistutilsArgError(
"You must specify --record or --root when building system"
" packages"
'You must specify --record or --root when building system'
' packages',
)
def handle_extra_path(self):
@ -78,10 +82,10 @@ class install(orig.install):
is unavailable. Return False otherwise.
"""
if run_frame is None:
msg = "Call stack not available. bdist_* commands may fail."
msg = 'Call stack not available. bdist_* commands may fail.'
warnings.warn(msg)
if platform.python_implementation() == 'IronPython':
msg = "For best results, pass -X:Frames to enable call stack."
msg = 'For best results, pass -X:Frames to enable call stack.'
warnings.warn(msg)
return True
res = inspect.getouterframes(run_frame)[2]
@ -89,8 +93,8 @@ class install(orig.install):
info = inspect.getframeinfo(caller)
caller_module = caller.f_globals.get('__name__', '')
return (
caller_module == 'distutils.dist'
and info.function == 'run_commands'
caller_module == 'distutils.dist' and
info.function == 'run_commands'
)
def do_egg_install(self):
@ -98,7 +102,7 @@ class install(orig.install):
easy_install = self.distribution.get_command_class('easy_install')
cmd = easy_install(
self.distribution, args="x", root=self.root, record=self.record,
self.distribution, args='x', root=self.root, record=self.record,
)
cmd.ensure_finalized() # finalize before bdist_egg munges install cmd
cmd.always_copy_from = '.' # make sure local-dir eggs get installed

View file

@ -1,30 +1,35 @@
from distutils import log, dir_util
from __future__ import annotations
import os
import pkg_resources
from distutils import dir_util
from distutils import log
from setuptools import Command
from setuptools import namespaces
from setuptools.archive_util import unpack_archive
import pkg_resources
class install_egg_info(namespaces.Installer, Command):
"""Install an .egg-info directory for the package"""
description = "Install an .egg-info directory for the package"
description = 'Install an .egg-info directory for the package'
user_options = [
('install-dir=', 'd', "directory to install to"),
('install-dir=', 'd', 'directory to install to'),
]
def initialize_options(self):
self.install_dir = None
def finalize_options(self):
self.set_undefined_options('install_lib',
('install_dir', 'install_dir'))
ei_cmd = self.get_finalized_command("egg_info")
self.set_undefined_options(
'install_lib',
('install_dir', 'install_dir'),
)
ei_cmd = self.get_finalized_command('egg_info')
basename = pkg_resources.Distribution(
None, None, ei_cmd.egg_name, ei_cmd.egg_version
None, None, ei_cmd.egg_name, ei_cmd.egg_version,
).egg_name() + '.egg-info'
self.source = ei_cmd.egg_info
self.target = os.path.join(self.install_dir, basename)
@ -35,11 +40,11 @@ class install_egg_info(namespaces.Installer, Command):
if os.path.isdir(self.target) and not os.path.islink(self.target):
dir_util.remove_tree(self.target, dry_run=self.dry_run)
elif os.path.exists(self.target):
self.execute(os.unlink, (self.target,), "Removing " + self.target)
self.execute(os.unlink, (self.target,), 'Removing ' + self.target)
if not self.dry_run:
pkg_resources.ensure_directory(self.target)
self.execute(
self.copytree, (), "Copying %s to %s" % (self.source, self.target)
self.copytree, (), 'Copying {} to {}'.format(self.source, self.target),
)
self.install_namespaces()
@ -56,7 +61,7 @@ class install_egg_info(namespaces.Installer, Command):
if src.startswith(skip) or '/' + skip in src:
return None
self.outputs.append(dst)
log.debug("Copying %s to %s", src, dst)
log.debug('Copying %s to %s', src, dst)
return dst
unpack_archive(self.source, self.target, skimmer)

View file

@ -1,6 +1,10 @@
from __future__ import annotations
import os
import sys
from itertools import product, starmap
from itertools import product
from itertools import starmap
import distutils.command.install_lib as orig
@ -78,7 +82,8 @@ class install_lib(orig.install_lib):
return
base = os.path.join(
'__pycache__', '__init__.' + sys.implementation.cache_tag)
'__pycache__', '__init__.' + sys.implementation.cache_tag,
)
yield base + '.pyc'
yield base + '.pyo'
yield base + '.opt-1.pyc'
@ -86,7 +91,7 @@ class install_lib(orig.install_lib):
def copy_tree(
self, infile, outfile,
preserve_mode=1, preserve_times=1, preserve_symlinks=0, level=1
preserve_mode=1, preserve_times=1, preserve_symlinks=0, level=1,
):
assert preserve_mode and preserve_times and not preserve_symlinks
exclude = self.get_exclusions()
@ -103,11 +108,13 @@ class install_lib(orig.install_lib):
def pf(src, dst):
if dst in exclude:
log.warn("Skipping installation of %s (namespace package)",
dst)
log.warn(
'Skipping installation of %s (namespace package)',
dst,
)
return False
log.info("copying %s -> %s", src, os.path.dirname(dst))
log.info('copying %s -> %s', src, os.path.dirname(dst))
outfiles.append(dst)
return dst

View file

@ -1,10 +1,14 @@
from distutils import log
import distutils.command.install_scripts as orig
from distutils.errors import DistutilsModuleError
from __future__ import annotations
import os
import sys
from pkg_resources import Distribution, PathMetadata, ensure_directory
import distutils.command.install_scripts as orig
from distutils import log
from distutils.errors import DistutilsModuleError
from pkg_resources import Distribution
from pkg_resources import ensure_directory
from pkg_resources import PathMetadata
class install_scripts(orig.install_scripts):
@ -17,7 +21,7 @@ class install_scripts(orig.install_scripts):
def run(self):
import setuptools.command.easy_install as ei
self.run_command("egg_info")
self.run_command('egg_info')
if self.distribution.scripts:
orig.install_scripts.run(self) # run first to set up self.outfiles
else:
@ -26,7 +30,7 @@ class install_scripts(orig.install_scripts):
# don't install entry point scripts into .egg file!
return
ei_cmd = self.get_finalized_command("egg_info")
ei_cmd = self.get_finalized_command('egg_info')
dist = Distribution(
ei_cmd.egg_base, PathMetadata(ei_cmd.egg_base, ei_cmd.egg_info),
ei_cmd.egg_name, ei_cmd.egg_version,
@ -34,13 +38,13 @@ class install_scripts(orig.install_scripts):
bs_cmd = self.get_finalized_command('build_scripts')
exec_param = getattr(bs_cmd, 'executable', None)
try:
bw_cmd = self.get_finalized_command("bdist_wininst")
bw_cmd = self.get_finalized_command('bdist_wininst')
is_wininst = getattr(bw_cmd, '_is_running', False)
except (ImportError, DistutilsModuleError):
is_wininst = False
writer = ei.ScriptWriter
if is_wininst:
exec_param = "python.exe"
exec_param = 'python.exe'
writer = ei.WindowsScriptWriter
if exec_param == sys.executable:
# In case the path to the Python executable contains a space, wrap
@ -52,18 +56,18 @@ class install_scripts(orig.install_scripts):
for args in writer.get_args(dist, cmd.as_header()):
self.write_script(*args)
def write_script(self, script_name, contents, mode="t", *ignored):
def write_script(self, script_name, contents, mode='t', *ignored):
"""Write an executable file to the scripts directory"""
from setuptools.command.easy_install import chmod, current_umask
log.info("Installing %s script to %s", script_name, self.install_dir)
log.info('Installing %s script to %s', script_name, self.install_dir)
target = os.path.join(self.install_dir, script_name)
self.outfiles.append(target)
mask = current_umask()
if not self.dry_run:
ensure_directory(target)
f = open(target, "w" + mode)
f = open(target, 'w' + mode)
f.write(contents)
f.close()
chmod(target, 0o777 - mask)

View file

@ -1,7 +1,10 @@
from __future__ import annotations
import os
from glob import glob
from distutils.util import convert_path
from distutils.command import sdist
from distutils.util import convert_path
class sdist_add_defaults:
@ -65,8 +68,10 @@ class sdist_add_defaults:
break
if not got_it:
self.warn("standard file not found: should have one of " +
', '.join(alts))
self.warn(
'standard file not found: should have one of ' +
', '.join(alts),
)
else:
if self._cs_path_exists(fn):
self.filelist.append(fn)

View file

@ -1,6 +1,7 @@
from distutils import log
import distutils.command.register as orig
from __future__ import annotations
import distutils.command.register as orig
from distutils import log
from setuptools.errors import RemovedCommandError
@ -9,10 +10,10 @@ class register(orig.register):
def run(self):
msg = (
"The register command has been removed, use twine to upload "
+ "instead (https://pypi.org/p/twine)"
'The register command has been removed, use twine to upload ' +
'instead (https://pypi.org/p/twine)'
)
self.announce("ERROR: " + msg, log.ERROR)
self.announce('ERROR: ' + msg, log.ERROR)
raise RemovedCommandError(msg)

View file

@ -1,20 +1,22 @@
from distutils.util import convert_path
from distutils import log
from distutils.errors import DistutilsOptionError
from __future__ import annotations
import os
import shutil
from distutils import log
from distutils.errors import DistutilsOptionError
from distutils.util import convert_path
from setuptools import Command
class rotate(Command):
"""Delete older distributions"""
description = "delete older distributions, keeping N newest files"
description = 'delete older distributions, keeping N newest files'
user_options = [
('match=', 'm', "patterns to match (required)"),
('dist-dir=', 'd', "directory where the distributions are"),
('keep=', 'k', "number of matching distributions to keep"),
('match=', 'm', 'patterns to match (required)'),
('dist-dir=', 'd', 'directory where the distributions are'),
('keep=', 'k', 'number of matching distributions to keep'),
]
boolean_options = []
@ -27,15 +29,15 @@ class rotate(Command):
def finalize_options(self):
if self.match is None:
raise DistutilsOptionError(
"Must specify one or more (comma-separated) match patterns "
"(e.g. '.zip' or '.egg')"
'Must specify one or more (comma-separated) match patterns '
"(e.g. '.zip' or '.egg')",
)
if self.keep is None:
raise DistutilsOptionError("Must specify number of files to keep")
raise DistutilsOptionError('Must specify number of files to keep')
try:
self.keep = int(self.keep)
except ValueError as e:
raise DistutilsOptionError("--keep must be an integer") from e
raise DistutilsOptionError('--keep must be an integer') from e
if isinstance(self.match, str):
self.match = [
convert_path(p.strip()) for p in self.match.split(',')
@ -43,7 +45,7 @@ class rotate(Command):
self.set_undefined_options('bdist', ('dist_dir', 'dist_dir'))
def run(self):
self.run_command("egg_info")
self.run_command('egg_info')
from glob import glob
for pattern in self.match:
@ -53,10 +55,10 @@ class rotate(Command):
files.sort()
files.reverse()
log.info("%d file(s) matching %s", len(files), pattern)
log.info('%d file(s) matching %s', len(files), pattern)
files = files[self.keep:]
for (t, f) in files:
log.info("Deleting %s", f)
log.info('Deleting %s', f)
if not self.dry_run:
if os.path.isdir(f):
shutil.rmtree(f)

View file

@ -1,10 +1,13 @@
from setuptools.command.setopt import edit_config, option_base
from __future__ import annotations
from setuptools.command.setopt import edit_config
from setuptools.command.setopt import option_base
class saveopts(option_base):
"""Save command-line options to a file"""
description = "save supplied options to setup.cfg or other config file"
description = 'save supplied options to setup.cfg or other config file'
def run(self):
dist = self.distribution
@ -16,7 +19,7 @@ class saveopts(option_base):
continue # don't save our own options!
for opt, (src, val) in dist.get_option_dict(cmd).items():
if src == "command line":
if src == 'command line':
settings.setdefault(cmd, {})[opt] = val
edit_config(self.filename, settings, self.dry_run)

View file

@ -1,42 +1,49 @@
from distutils import log
import distutils.command.sdist as orig
from __future__ import annotations
import contextlib
import io
import os
import sys
import io
import contextlib
import distutils.command.sdist as orig
import pkg_resources
from distutils import log
from .py36compat import sdist_add_defaults
import pkg_resources
_default_revctrl = list
def walk_revctrl(dirname=''):
"""Find all files under revision control"""
for ep in pkg_resources.iter_entry_points('setuptools.file_finders'):
for item in ep.load()(dirname):
yield item
yield from ep.load()(dirname)
class sdist(sdist_add_defaults, orig.sdist):
"""Smart sdist that finds anything supported by revision control"""
user_options = [
('formats=', None,
"formats for source distribution (comma-separated list)"),
('keep-temp', 'k',
"keep the distribution tree around after creating " +
"archive file(s)"),
('dist-dir=', 'd',
"directory to put the source distribution archive(s) in "
"[default: dist]"),
(
'formats=', None,
'formats for source distribution (comma-separated list)',
),
(
'keep-temp', 'k',
'keep the distribution tree around after creating ' +
'archive file(s)',
),
(
'dist-dir=', 'd',
'directory to put the source distribution archive(s) in '
'[default: dist]',
),
]
negative_opt = {}
README_EXTENSIONS = ['', '.rst', '.txt', '.md']
READMES = tuple('README{0}'.format(ext) for ext in README_EXTENSIONS)
READMES = tuple(f'README{ext}' for ext in README_EXTENSIONS)
def run(self):
self.run_command('egg_info')
@ -132,7 +139,7 @@ class sdist(sdist_add_defaults, orig.sdist):
try:
super()._add_defaults_data_files()
except TypeError:
log.warn("data_files contains unexpected objects")
log.warn('data_files contains unexpected objects')
def check_readme(self):
for f in self.READMES:
@ -140,8 +147,8 @@ class sdist(sdist_add_defaults, orig.sdist):
return
else:
self.warn(
"standard file not found: should have one of " +
', '.join(self.READMES)
'standard file not found: should have one of ' +
', '.join(self.READMES),
)
def make_release_tree(self, base_dir, files):
@ -162,10 +169,12 @@ class sdist(sdist_add_defaults, orig.sdist):
if not os.path.isfile(self.manifest):
return False
with io.open(self.manifest, 'rb') as fp:
with open(self.manifest, 'rb') as fp:
first_line = fp.readline()
return (first_line !=
'# file GENERATED by distutils, do NOT edit\n'.encode())
return (
first_line !=
b'# file GENERATED by distutils, do NOT edit\n'
)
def read_manifest(self):
"""Read the manifest file (named by 'self.manifest') and use it to
@ -179,7 +188,7 @@ class sdist(sdist_add_defaults, orig.sdist):
try:
line = line.decode('UTF-8')
except UnicodeDecodeError:
log.warn("%r not UTF-8 decodable -- skipping" % line)
log.warn('%r not UTF-8 decodable -- skipping' % line)
continue
# ignore comments and blank lines
line = line.strip()

View file

@ -1,16 +1,18 @@
from distutils.util import convert_path
from __future__ import annotations
import configparser
import os
import distutils
from distutils import log
from distutils.errors import DistutilsOptionError
import distutils
import os
import configparser
from distutils.util import convert_path
from setuptools import Command
__all__ = ['config_file', 'edit_config', 'option_base', 'setopt']
def config_file(kind="local"):
def config_file(kind='local'):
"""Get the filename of the distutils, local, global, or per-user config
`kind` must be one of "local", "global", or "user"
@ -19,13 +21,13 @@ def config_file(kind="local"):
return 'setup.cfg'
if kind == 'global':
return os.path.join(
os.path.dirname(distutils.__file__), 'distutils.cfg'
os.path.dirname(distutils.__file__), 'distutils.cfg',
)
if kind == 'user':
dot = os.name == 'posix' and '.' or ''
return os.path.expanduser(convert_path("~/%spydistutils.cfg" % dot))
return os.path.expanduser(convert_path('~/%spydistutils.cfg' % dot))
raise ValueError(
"config_file() type must be 'local', 'global', or 'user'", kind
"config_file() type must be 'local', 'global', or 'user'", kind,
)
@ -37,37 +39,39 @@ def edit_config(filename, settings, dry_run=False):
while a dictionary lists settings to be changed or deleted in that section.
A setting of ``None`` means to delete that setting.
"""
log.debug("Reading configuration from %s", filename)
log.debug('Reading configuration from %s', filename)
opts = configparser.RawConfigParser()
opts.optionxform = lambda x: x
opts.read([filename])
for section, options in settings.items():
if options is None:
log.info("Deleting section [%s] from %s", section, filename)
log.info('Deleting section [%s] from %s', section, filename)
opts.remove_section(section)
else:
if not opts.has_section(section):
log.debug("Adding new section [%s] to %s", section, filename)
log.debug('Adding new section [%s] to %s', section, filename)
opts.add_section(section)
for option, value in options.items():
if value is None:
log.debug(
"Deleting %s.%s from %s",
section, option, filename
'Deleting %s.%s from %s',
section, option, filename,
)
opts.remove_option(section, option)
if not opts.options(section):
log.info("Deleting empty [%s] section from %s",
section, filename)
log.info(
'Deleting empty [%s] section from %s',
section, filename,
)
opts.remove_section(section)
else:
log.debug(
"Setting %s.%s to %r in %s",
section, option, value, filename
'Setting %s.%s to %r in %s',
section, option, value, filename,
)
opts.set(section, option, value)
log.info("Writing %s", filename)
log.info('Writing %s', filename)
if not dry_run:
with open(filename, 'w') as f:
opts.write(f)
@ -77,12 +81,18 @@ class option_base(Command):
"""Abstract base class for commands that mess with config files"""
user_options = [
('global-config', 'g',
"save options to the site-wide distutils.cfg file"),
('user-config', 'u',
"save options to the current user's pydistutils.cfg file"),
('filename=', 'f',
"configuration file to use (default=setup.cfg)"),
(
'global-config', 'g',
'save options to the site-wide distutils.cfg file',
),
(
'user-config', 'u',
"save options to the current user's pydistutils.cfg file",
),
(
'filename=', 'f',
'configuration file to use (default=setup.cfg)',
),
]
boolean_options = [
@ -106,8 +116,8 @@ class option_base(Command):
filenames.append(config_file('local'))
if len(filenames) > 1:
raise DistutilsOptionError(
"Must specify only one configuration file option",
filenames
'Must specify only one configuration file option',
filenames,
)
self.filename, = filenames
@ -115,7 +125,7 @@ class option_base(Command):
class setopt(option_base):
"""Save command-line options to a file"""
description = "set an option in setup.cfg or another config file"
description = 'set an option in setup.cfg or another config file'
user_options = [
('command=', 'c', 'command to set an option for'),
@ -136,14 +146,14 @@ class setopt(option_base):
def finalize_options(self):
option_base.finalize_options(self)
if self.command is None or self.option is None:
raise DistutilsOptionError("Must specify --command *and* --option")
raise DistutilsOptionError('Must specify --command *and* --option')
if self.set_value is None and not self.remove:
raise DistutilsOptionError("Must specify --set-value or --remove")
raise DistutilsOptionError('Must specify --set-value or --remove')
def run(self):
edit_config(
self.filename, {
self.command: {self.option.replace('-', '_'): self.set_value}
self.command: {self.option.replace('-', '_'): self.set_value},
},
self.dry_run
self.dry_run,
)

View file

@ -1,23 +1,24 @@
import os
import operator
import sys
from __future__ import annotations
import contextlib
import itertools
import operator
import os
import sys
import unittest
from distutils.errors import DistutilsError, DistutilsOptionError
from distutils import log
from unittest import TestLoader
from pkg_resources import (
resource_listdir,
resource_exists,
normalize_path,
working_set,
evaluate_marker,
add_activation_listener,
require,
EntryPoint,
)
from distutils import log
from distutils.errors import DistutilsError
from distutils.errors import DistutilsOptionError
from pkg_resources import add_activation_listener
from pkg_resources import EntryPoint
from pkg_resources import evaluate_marker
from pkg_resources import normalize_path
from pkg_resources import require
from pkg_resources import resource_exists
from pkg_resources import resource_listdir
from pkg_resources import working_set
from setuptools import Command
from setuptools.extern.more_itertools import unique_everseen
@ -41,7 +42,7 @@ class ScanningLoader(TestLoader):
tests = []
tests.append(TestLoader.loadTestsFromModule(self, module))
if hasattr(module, "additional_tests"):
if hasattr(module, 'additional_tests'):
tests.append(module.additional_tests())
if hasattr(module, '__path__'):
@ -75,7 +76,7 @@ class NonDataProperty:
class test(Command):
"""Command to run unit tests after in-place build"""
description = "run unit tests after in-place build (deprecated)"
description = 'run unit tests after in-place build (deprecated)'
user_options = [
('test-module=', 'm', "Run 'test_suite' in specified module"),
@ -84,7 +85,7 @@ class test(Command):
's',
"Run single test, case or suite (e.g. 'module.test_suite')",
),
('test-runner=', 'r', "Test runner to use"),
('test-runner=', 'r', 'Test runner to use'),
]
def initialize_options(self):
@ -96,19 +97,19 @@ class test(Command):
def finalize_options(self):
if self.test_suite and self.test_module:
msg = "You may specify a module or a suite, but not both"
msg = 'You may specify a module or a suite, but not both'
raise DistutilsOptionError(msg)
if self.test_suite is None:
if self.test_module is None:
self.test_suite = self.distribution.test_suite
else:
self.test_suite = self.test_module + ".test_suite"
self.test_suite = self.test_module + '.test_suite'
if self.test_loader is None:
self.test_loader = getattr(self.distribution, 'test_loader', None)
if self.test_loader is None:
self.test_loader = "setuptools.command.test:ScanningLoader"
self.test_loader = 'setuptools.command.test:ScanningLoader'
if self.test_runner is None:
self.test_runner = getattr(self.distribution, 'test_runner', None)
@ -139,7 +140,7 @@ class test(Command):
self.reinitialize_command('build_ext', inplace=1)
self.run_command('build_ext')
ei_cmd = self.get_finalized_command("egg_info")
ei_cmd = self.get_finalized_command('egg_info')
old_path = sys.path[:]
old_modules = sys.modules.copy()
@ -149,7 +150,7 @@ class test(Command):
sys.path.insert(0, project_path)
working_set.__init__()
add_activation_listener(lambda dist: dist.activate())
require('%s==%s' % (ei_cmd.egg_name, ei_cmd.egg_version))
require('{}=={}'.format(ei_cmd.egg_name, ei_cmd.egg_version))
with self.paths_on_pythonpath([project_path]):
yield
finally:
@ -201,10 +202,10 @@ class test(Command):
def run(self):
self.announce(
"WARNING: Testing via this command is deprecated and will be "
"removed in a future version. Users looking for a generic test "
"entry point independent of test runner are encouraged to use "
"tox.",
'WARNING: Testing via this command is deprecated and will be '
'removed in a future version. Users looking for a generic test '
'entry point independent of test runner are encouraged to use '
'tox.',
log.WARN,
)
@ -248,5 +249,5 @@ class test(Command):
"""
if val is None:
return
parsed = EntryPoint.parse("x=" + val)
parsed = EntryPoint.parse('x=' + val)
return parsed.resolve()()

View file

@ -1,6 +1,7 @@
from __future__ import annotations
from distutils import log
from distutils.command import upload as orig
from setuptools.errors import RemovedCommandError
@ -9,9 +10,9 @@ class upload(orig.upload):
def run(self):
msg = (
"The upload command has been removed, use twine to upload "
+ "instead (https://pypi.org/p/twine)"
'The upload command has been removed, use twine to upload ' +
'instead (https://pypi.org/p/twine)'
)
self.announce("ERROR: " + msg, log.ERROR)
self.announce('ERROR: ' + msg, log.ERROR)
raise RemovedCommandError(msg)

View file

@ -1,24 +1,25 @@
# -*- coding: utf-8 -*-
"""upload_docs
Implements a Distutils 'upload_docs' subcommand (upload documentation to
sites other than PyPi such as devpi).
"""
from __future__ import annotations
from base64 import standard_b64encode
from distutils import log
from distutils.errors import DistutilsOptionError
import os
import socket
import zipfile
import tempfile
import shutil
import itertools
import functools
import http.client
import itertools
import os
import shutil
import socket
import tempfile
import urllib.parse
import zipfile
from base64 import standard_b64encode
from distutils import log
from distutils.errors import DistutilsOptionError
from pkg_resources import iter_entry_points
from .upload import upload
@ -34,10 +35,14 @@ class upload_docs(upload):
description = 'Upload documentation to sites other than PyPi such as devpi'
user_options = [
('repository=', 'r',
"url of repository [default: %s]" % upload.DEFAULT_REPOSITORY),
('show-response', None,
'display full response text from server'),
(
'repository=', 'r',
'url of repository [default: %s]' % upload.DEFAULT_REPOSITORY,
),
(
'show-response', None,
'display full response text from server',
),
('upload-dir=', None, 'directory to upload'),
]
boolean_options = upload.boolean_options
@ -67,11 +72,11 @@ class upload_docs(upload):
self.ensure_dirname('upload_dir')
self.target_dir = self.upload_dir
if 'pypi.python.org' in self.repository:
log.warn("Upload_docs command is deprecated for PyPi. Use RTD instead.")
log.warn('Upload_docs command is deprecated for PyPi. Use RTD instead.')
self.announce('Using upload directory %s' % self.target_dir)
def create_zipfile(self, filename):
zip_file = zipfile.ZipFile(filename, "w")
zip_file = zipfile.ZipFile(filename, 'w')
try:
self.mkpath(self.target_dir) # just in case
for root, dirs, files in os.walk(self.target_dir):
@ -93,7 +98,7 @@ class upload_docs(upload):
tmp_dir = tempfile.mkdtemp()
name = self.distribution.metadata.get_name()
zip_file = os.path.join(tmp_dir, "%s.zip" % name)
zip_file = os.path.join(tmp_dir, '%s.zip' % name)
try:
self.create_zipfile(zip_file)
self.upload_file(zip_file)
@ -115,7 +120,7 @@ class upload_docs(upload):
value = _encode(value)
yield sep_boundary
yield _encode(title)
yield b"\n\n"
yield b'\n\n'
yield value
if value and value[-1:] == b'\r':
yield b'\n' # write an extra newline (lurve Macs)
@ -128,7 +133,7 @@ class upload_docs(upload):
boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254'
sep_boundary = b'\n--' + boundary.encode('ascii')
end_boundary = sep_boundary + b'--'
end_items = end_boundary, b"\n",
end_items = end_boundary, b'\n',
builder = functools.partial(
cls._build_part,
sep_boundary=sep_boundary,
@ -151,11 +156,11 @@ class upload_docs(upload):
# set up the authentication
credentials = _encode(self.username + ':' + self.password)
credentials = standard_b64encode(credentials).decode('ascii')
auth = "Basic " + credentials
auth = 'Basic ' + credentials
body, ct = self._build_multipart(data)
msg = "Submitting documentation to %s" % (self.repository)
msg = 'Submitting documentation to %s' % (self.repository)
self.announce(msg, log.INFO)
# build the Request
@ -169,25 +174,25 @@ class upload_docs(upload):
elif schema == 'https':
conn = http.client.HTTPSConnection(netloc)
else:
raise AssertionError("unsupported schema " + schema)
raise AssertionError('unsupported schema ' + schema)
data = ''
try:
conn.connect()
conn.putrequest("POST", url)
conn.putrequest('POST', url)
content_type = ct
conn.putheader('Content-type', content_type)
conn.putheader('Content-length', str(len(body)))
conn.putheader('Authorization', auth)
conn.endheaders()
conn.send(body)
except socket.error as e:
except OSError as e:
self.announce(str(e), log.ERROR)
return
r = conn.getresponse()
if r.status == 200:
msg = 'Server response (%s): %s' % (r.status, r.reason)
msg = 'Server response ({}): {}'.format(r.status, r.reason)
self.announce(msg, log.INFO)
elif r.status == 301:
location = r.getheader('Location')
@ -196,7 +201,7 @@ class upload_docs(upload):
msg = 'Upload successful. Visit %s' % location
self.announce(msg, log.INFO)
else:
msg = 'Upload failed (%s): %s' % (r.status, r.reason)
msg = 'Upload failed ({}): {}'.format(r.status, r.reason)
self.announce(msg, log.ERROR)
if self.show_response:
print('-' * 75, r.read(), '-' * 75)

View file

@ -1,20 +1,23 @@
from __future__ import annotations
import ast
import contextlib
import functools
import importlib
import io
import os
import sys
import warnings
import functools
import importlib
from collections import defaultdict
from functools import partial
from functools import wraps
from glob import iglob
import contextlib
from distutils.errors import DistutilsOptionError, DistutilsFileError
from setuptools.extern.packaging.version import LegacyVersion, parse
from distutils.errors import DistutilsFileError
from distutils.errors import DistutilsOptionError
from setuptools.extern.packaging.specifiers import SpecifierSet
from setuptools.extern.packaging.version import LegacyVersion
from setuptools.extern.packaging.version import parse
class StaticModule:
@ -41,7 +44,7 @@ class StaticModule:
)
except Exception as e:
raise AttributeError(
"{self.name} has no attribute {attr}".format(**locals())
f'{self.name} has no attribute {attr}',
) from e
@ -93,7 +96,7 @@ def read_configuration(filepath, find_others=False, ignore_option_errors=False):
_Distribution.parse_config_files(dist, filenames=filenames)
handlers = parse_configuration(
dist, dist.command_options, ignore_option_errors=ignore_option_errors
dist, dist.command_options, ignore_option_errors=ignore_option_errors,
)
finally:
@ -108,7 +111,7 @@ def _get_option(target_obj, key):
the target object, either through a get_{key} method or
from an attribute directly.
"""
getter_name = 'get_{key}'.format(**locals())
getter_name = f'get_{key}'
by_attribute = functools.partial(getattr, target_obj, key)
getter = getattr(target_obj, getter_name, by_attribute)
return getter()
@ -196,7 +199,7 @@ class ConfigHandler:
def parsers(self):
"""Metadata item name to parser function mapping."""
raise NotImplementedError(
'%s must provide .parsers property' % self.__class__.__name__
'%s must provide .parsers property' % self.__class__.__name__,
)
def __setitem__(self, option_name, value):
@ -275,9 +278,12 @@ class ConfigHandler:
# Has globby characters?
if any(char in value for char in glob_characters):
# then expand the glob pattern while keeping paths *relative*:
expanded_values.extend(sorted(
os.path.relpath(path, os.getcwd())
for path in iglob(os.path.abspath(value))))
expanded_values.extend(
sorted(
os.path.relpath(path, os.getcwd())
for path in iglob(os.path.abspath(value))
),
)
else:
# take the value as-is:
@ -298,7 +304,7 @@ class ConfigHandler:
key, sep, val = line.partition(separator)
if sep != separator:
raise DistutilsOptionError(
'Unable to parse option value to dict: %s' % value
'Unable to parse option value to dict: %s' % value,
)
result[key.strip()] = val.strip()
@ -330,8 +336,8 @@ class ConfigHandler:
exclude_directive = 'file:'
if value.startswith(exclude_directive):
raise ValueError(
'Only strings are accepted for the {0} field, '
'files are not accepted'.format(key)
'Only strings are accepted for the {} field, '
'files are not accepted'.format(key),
)
return value
@ -359,7 +365,7 @@ class ConfigHandler:
if not value.startswith(include_directive):
return value
spec = value[len(include_directive) :]
spec = value[len(include_directive):]
filepaths = (os.path.abspath(path.strip()) for path in spec.split(','))
return '\n'.join(
cls._read_file(path)
@ -374,7 +380,7 @@ class ConfigHandler:
@staticmethod
def _read_file(filepath):
with io.open(filepath, encoding='utf-8') as f:
with open(filepath, encoding='utf-8') as f:
return f.read()
@classmethod
@ -492,7 +498,7 @@ class ConfigHandler:
if section_parser_method is None:
raise DistutilsOptionError(
'Unsupported distribution option section: [%s.%s]'
% (self.section_prefix, section_name)
% (self.section_prefix, section_name),
)
section_parser_method(section_options)
@ -531,10 +537,10 @@ class ConfigMetadataHandler(ConfigHandler):
"""
def __init__(
self, target_obj, options, ignore_option_errors=False, package_dir=None
self, target_obj, options, ignore_option_errors=False, package_dir=None,
):
super(ConfigMetadataHandler, self).__init__(
target_obj, options, ignore_option_errors
super().__init__(
target_obj, options, ignore_option_errors,
)
self.package_dir = package_dir
@ -552,8 +558,8 @@ class ConfigMetadataHandler(ConfigHandler):
'provides': parse_list,
'requires': self._deprecated_config_handler(
parse_list,
"The requires parameter is deprecated, please use "
"install_requires for runtime dependencies.",
'The requires parameter is deprecated, please use '
'install_requires for runtime dependencies.',
DeprecationWarning,
),
'obsoletes': parse_list,
@ -561,8 +567,8 @@ class ConfigMetadataHandler(ConfigHandler):
'license': exclude_files_parser('license'),
'license_file': self._deprecated_config_handler(
exclude_files_parser('license_file'),
"The license_file parameter is deprecated, "
"use license_files instead.",
'The license_file parameter is deprecated, '
'use license_files instead.',
DeprecationWarning,
),
'license_files': parse_list,
@ -642,7 +648,7 @@ class ConfigOptionsHandler(ConfigHandler):
def _parse_cmdclass(self, value):
def resolve_class(qualified_class_name):
idx = qualified_class_name.rfind('.')
class_name = qualified_class_name[idx + 1 :]
class_name = qualified_class_name[idx + 1:]
pkg_name = qualified_class_name[:idx]
module = __import__(pkg_name)
@ -667,7 +673,7 @@ class ConfigOptionsHandler(ConfigHandler):
# Read function arguments from a dedicated section.
find_kwargs = self.parse_section_packages__find(
self.sections.get('packages.find', {})
self.sections.get('packages.find', {}),
)
if findns:
@ -688,9 +694,9 @@ class ConfigOptionsHandler(ConfigHandler):
valid_keys = ['where', 'include', 'exclude']
find_kwargs = dict(
[(k, v) for k, v in section_data.items() if k in valid_keys and v]
)
find_kwargs = {
k: v for k, v in section_data.items() if k in valid_keys and v
}
where = find_kwargs.get('where')
if where is not None:
@ -737,7 +743,7 @@ class ConfigOptionsHandler(ConfigHandler):
"""
parse_list = partial(self._parse_list, separator=';')
self['extras_require'] = self._parse_section_to_dict(
section_options, parse_list
section_options, parse_list,
)
def parse_section_data_files(self, section_options):

Some files were not shown because too many files have changed in this diff Show more