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

View File

@@ -3,7 +3,7 @@
Package containing implementation of all the standard Distutils
commands."""
__all__ = [ # noqa: F822
__all__ = [
'build',
'build_py',
'build_ext',
@@ -16,10 +16,8 @@ __all__ = [ # noqa: F822
'install_scripts',
'install_data',
'sdist',
'register',
'bdist',
'bdist_dumb',
'bdist_rpm',
'check',
'upload',
]

View File

@@ -2,15 +2,14 @@
Backward compatibility for homebrew builds on macOS.
"""
import sys
import os
import functools
import os
import subprocess
import sys
import sysconfig
@functools.lru_cache()
@functools.lru_cache
def enabled():
"""
Only enabled for Python 3.9 framework homebrew builds
@@ -38,7 +37,7 @@ schemes = dict(
)
@functools.lru_cache()
@functools.lru_cache
def vars():
if not enabled():
return {}

View File

@@ -7,7 +7,7 @@ import os
import warnings
from ..core import Command
from ..errors import DistutilsPlatformError, DistutilsOptionError
from ..errors import DistutilsOptionError, DistutilsPlatformError
from ..util import get_platform
@@ -15,9 +15,10 @@ def show_formats():
"""Print list of available formats (arguments to "--format" option)."""
from ..fancy_getopt import FancyGetopt
formats = []
for format in bdist.format_commands:
formats.append(("formats=" + format, None, bdist.format_commands[format][1]))
formats = [
("formats=" + format, None, bdist.format_commands[format][1])
for format in bdist.format_commands
]
pretty_printer = FancyGetopt(formats)
pretty_printer.print_help("List of available distribution formats:")
@@ -41,24 +42,24 @@ class bdist(Command):
'plat-name=',
'p',
"platform name to embed in generated filenames "
"(default: %s)" % get_platform(),
f"[default: {get_platform()}]",
),
('formats=', None, "formats for distribution (comma-separated list)"),
(
'dist-dir=',
'd',
"directory to put final built distributions in " "[default: dist]",
"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]",
"Owner name used when creating a tar file [default: current user]",
),
(
'group=',
'g',
"Group name used when creating a tar file" " [default: current group]",
"Group name used when creating a tar file [default: current group]",
),
]
@@ -76,17 +77,15 @@ class bdist(Command):
default_format = {'posix': 'gztar', 'nt': 'zip'}
# Define commands in preferred order for the --help-formats option
format_commands = ListCompat(
{
'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"),
'zip': ('bdist_dumb', "ZIP file"),
}
)
format_commands = ListCompat({
'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"),
'zip': ('bdist_dumb', "ZIP file"),
})
# for compatibility until consumers only reference format_commands
format_command = format_commands
@@ -96,7 +95,7 @@ class bdist(Command):
self.plat_name = None
self.formats = None
self.dist_dir = None
self.skip_build = 0
self.skip_build = False
self.group = None
self.owner = None
@@ -122,7 +121,7 @@ class bdist(Command):
except KeyError:
raise DistutilsPlatformError(
"don't know how to create built distributions "
"on platform %s" % os.name
f"on platform {os.name}"
)
if self.dist_dir is None:
@@ -135,7 +134,7 @@ class bdist(Command):
try:
commands.append(self.format_commands[format][0])
except KeyError:
raise DistutilsOptionError("invalid format '%s'" % format)
raise DistutilsOptionError(f"invalid format '{format}'")
# Reinitialize and run each command.
for i in range(len(self.formats)):
@@ -152,5 +151,5 @@ 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 :]:
sub_cmd.keep_temp = 1
sub_cmd.keep_temp = True
self.run_command(cmd_name)

View File

@@ -5,12 +5,13 @@ distribution -- i.e., just an archive to be unpacked under $prefix or
$exec_prefix)."""
import os
from distutils._log import log
from ..core import Command
from ..util import get_platform
from ..dir_util import remove_tree, ensure_relative
from ..dir_util import ensure_relative, remove_tree
from ..errors import DistutilsPlatformError
from ..sysconfig import get_python_version
from distutils._log import log
from ..util import get_platform
class bdist_dumb(Command):
@@ -22,35 +23,34 @@ class bdist_dumb(Command):
'plat-name=',
'p',
"platform name to embed in generated filenames "
"(default: %s)" % get_platform(),
f"[default: {get_platform()}]",
),
(
'format=',
'f',
"archive format to create (tar, gztar, bztar, xztar, " "ztar, zip)",
"archive format to create (tar, gztar, bztar, xztar, ztar, zip)",
),
(
'keep-temp',
'k',
"keep the pseudo-installation tree around after "
+ "creating the distribution archive",
"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)",
"build the archive using relative paths [default: false]",
),
(
'owner=',
'u',
"Owner name used when creating a tar file" " [default: current user]",
"Owner name used when creating a tar file [default: current user]",
),
(
'group=',
'g',
"Group name used when creating a tar file" " [default: current group]",
"Group name used when creating a tar file [default: current group]",
),
]
@@ -62,10 +62,10 @@ class bdist_dumb(Command):
self.bdist_dir = None
self.plat_name = None
self.format = None
self.keep_temp = 0
self.keep_temp = False
self.dist_dir = None
self.skip_build = None
self.relative = 0
self.relative = False
self.owner = None
self.group = None
@@ -80,7 +80,7 @@ class bdist_dumb(Command):
except KeyError:
raise DistutilsPlatformError(
"don't know how to create dumb built distributions "
"on platform %s" % os.name
f"on platform {os.name}"
)
self.set_undefined_options(
@@ -94,19 +94,17 @@ class bdist_dumb(Command):
if not self.skip_build:
self.run_command('build')
install = self.reinitialize_command('install', reinit_subcommands=1)
install = self.reinitialize_command('install', reinit_subcommands=True)
install.root = self.bdist_dir
install.skip_build = self.skip_build
install.warn_dir = 0
install.warn_dir = False
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 = "{}.{}".format(
self.distribution.get_fullname(), self.plat_name
)
archive_basename = f"{self.distribution.get_fullname()}.{self.plat_name}"
pseudoinstall_root = os.path.join(self.dist_dir, archive_basename)
if not self.relative:
@@ -117,8 +115,7 @@ class bdist_dumb(Command):
):
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))
f"base and platbase are different ({install.install_base!r}, {install.install_platbase!r})"
)
else:
archive_root = os.path.join(

View File

@@ -3,21 +3,21 @@
Implements the Distutils 'bdist_rpm' command (create RPM source and binary
distributions)."""
import os
import subprocess
import sys
import os
from distutils._log import log
from ..core import Command
from ..debug import DEBUG
from ..file_util import write_file
from ..errors import (
DistutilsExecError,
DistutilsFileError,
DistutilsOptionError,
DistutilsPlatformError,
DistutilsFileError,
DistutilsExecError,
)
from ..file_util import write_file
from ..sysconfig import get_python_version
from distutils._log import log
class bdist_rpm(Command):
@@ -34,13 +34,13 @@ class bdist_rpm(Command):
(
'dist-dir=',
'd',
"directory to put final RPM files in " "(and .spec files if --spec-only)",
"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\")",
"[default: \"python\"]",
),
(
'fix-python',
@@ -75,7 +75,7 @@ class bdist_rpm(Command):
(
'packager=',
None,
"RPM packager (eg. \"Jane Doe <jane@example.net>\") " "[default: vendor]",
"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"),
@@ -187,13 +187,13 @@ class bdist_rpm(Command):
self.build_requires = None
self.obsoletes = None
self.keep_temp = 0
self.use_rpm_opt_flags = 1
self.rpm3_mode = 1
self.no_autoreq = 0
self.keep_temp = False
self.use_rpm_opt_flags = True
self.rpm3_mode = True
self.no_autoreq = False
self.force_arch = None
self.quiet = 0
self.quiet = False
def finalize_options(self):
self.set_undefined_options('bdist', ('bdist_base', 'bdist_base'))
@@ -214,7 +214,7 @@ class bdist_rpm(Command):
if os.name != 'posix':
raise DistutilsPlatformError(
"don't know how to create RPM " "distributions on platform %s" % os.name
f"don't know how to create RPM distributions on platform {os.name}"
)
if self.binary_only and self.source_only:
raise DistutilsOptionError(
@@ -223,7 +223,7 @@ class bdist_rpm(Command):
# don't pass CFLAGS to pure python distributions
if not self.distribution.has_ext_modules():
self.use_rpm_opt_flags = 0
self.use_rpm_opt_flags = False
self.set_undefined_options('bdist', ('dist_dir', 'dist_dir'))
self.finalize_package_data()
@@ -232,8 +232,7 @@ class bdist_rpm(Command):
self.ensure_string('group', "Development/Libraries")
self.ensure_string(
'vendor',
"%s <%s>"
% (self.distribution.get_contact(), self.distribution.get_contact_email()),
f"{self.distribution.get_contact()} <{self.distribution.get_contact_email()}>",
)
self.ensure_string('packager')
self.ensure_string_list('doc_files')
@@ -296,9 +295,9 @@ 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())
spec_path = os.path.join(spec_dir, f"{self.distribution.get_name()}.spec")
self.execute(
write_file, (spec_path, self._make_spec_file()), "writing '%s'" % spec_path
write_file, (spec_path, self._make_spec_file()), f"writing '{spec_path}'"
)
if self.spec_only: # stop if requested
@@ -323,7 +322,7 @@ class bdist_rpm(Command):
if os.path.exists(self.icon):
self.copy_file(self.icon, source_dir)
else:
raise DistutilsFileError("icon file '%s' does not exist" % self.icon)
raise DistutilsFileError(f"icon file '{self.icon}' does not exist")
# build package
log.info("building RPMs")
@@ -335,9 +334,9 @@ class bdist_rpm(Command):
rpm_cmd.append('-bb')
else:
rpm_cmd.append('-ba')
rpm_cmd.extend(['--define', '__python %s' % self.python])
rpm_cmd.extend(['--define', f'__python {self.python}'])
if self.rpm3_mode:
rpm_cmd.extend(['--define', '_topdir %s' % os.path.abspath(self.rpm_base)])
rpm_cmd.extend(['--define', f'_topdir {os.path.abspath(self.rpm_base)}'])
if not self.keep_temp:
rpm_cmd.append('--clean')
@@ -352,11 +351,7 @@ class bdist_rpm(Command):
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,
)
q_cmd = rf"rpm -q --qf '{src_rpm} {non_src_rpm}\n' --specfile '{spec_path}'"
out = os.popen(q_cmd)
try:
@@ -375,7 +370,7 @@ class bdist_rpm(Command):
status = out.close()
if status:
raise DistutilsExecError("Failed to execute: %s" % repr(q_cmd))
raise DistutilsExecError(f"Failed to execute: {q_cmd!r}")
finally:
out.close()
@@ -401,9 +396,11 @@ class bdist_rpm(Command):
if os.path.exists(rpm):
self.move_file(rpm, self.dist_dir)
filename = os.path.join(self.dist_dir, os.path.basename(rpm))
self.distribution.dist_files.append(
('bdist_rpm', pyversion, filename)
)
self.distribution.dist_files.append((
'bdist_rpm',
pyversion,
filename,
))
def _dist_path(self, path):
return os.path.join(self.dist_dir, os.path.basename(path))
@@ -428,14 +425,14 @@ 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()]
)
vendor_hook = '\n'.join([
f' {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')
spec_file.append('# Workaround for https://bugs.python.org/issue14443')
spec_file.append('%define __os_install_post ' + fixed_hook + '\n')
# put locale summaries into spec file
@@ -445,13 +442,11 @@ class bdist_rpm(Command):
# spec_file.append('Summary(%s): %s' % (locale,
# self.summaries[locale]))
spec_file.extend(
[
'Name: %{name}',
'Version: %{version}',
'Release: %{release}',
]
)
spec_file.extend([
'Name: %{name}',
'Version: %{version}',
'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
@@ -461,21 +456,19 @@ class bdist_rpm(Command):
else:
spec_file.append('Source0: %{name}-%{unmangled_version}.tar.gz')
spec_file.extend(
[
'License: ' + (self.distribution.get_license() or "UNKNOWN"),
'Group: ' + self.group,
'BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot',
'Prefix: %{_prefix}',
]
)
spec_file.extend([
'License: ' + (self.distribution.get_license() or "UNKNOWN"),
'Group: ' + self.group,
'BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot',
'Prefix: %{_prefix}',
])
if not self.force_arch:
# noarch if no extension modules
if not self.distribution.has_ext_modules():
spec_file.append('BuildArch: noarch')
else:
spec_file.append('BuildArch: %s' % self.force_arch)
spec_file.append(f'BuildArch: {self.force_arch}')
for field in (
'Vendor',
@@ -489,7 +482,7 @@ class bdist_rpm(Command):
if isinstance(val, list):
spec_file.append('{}: {}'.format(field, ' '.join(val)))
elif val is not None:
spec_file.append('{}: {}'.format(field, val))
spec_file.append(f'{field}: {val}')
if self.distribution.get_url():
spec_file.append('Url: ' + self.distribution.get_url())
@@ -506,13 +499,11 @@ class bdist_rpm(Command):
if self.no_autoreq:
spec_file.append('AutoReq: 0')
spec_file.extend(
[
'',
'%description',
self.distribution.get_long_description() or "",
]
)
spec_file.extend([
'',
'%description',
self.distribution.get_long_description() or "",
])
# put locale descriptions into spec file
# XXX again, suppressed because config file syntax doesn't
@@ -526,8 +517,8 @@ class bdist_rpm(Command):
# rpm scripts
# figure out default build script
def_setup_call = "{} {}".format(self.python, os.path.basename(sys.argv[0]))
def_build = "%s build" % def_setup_call
def_setup_call = f"{self.python} {os.path.basename(sys.argv[0])}"
def_build = f"{def_setup_call} build"
if self.use_rpm_opt_flags:
def_build = 'env CFLAGS="$RPM_OPT_FLAGS" ' + def_build
@@ -537,9 +528,7 @@ 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 = f'{def_setup_call} install -O1 --root=$RPM_BUILD_ROOT --record=INSTALLED_FILES'
script_options = [
('prep', 'prep_script', "%setup -n %{name}-%{unmangled_version}"),
@@ -558,12 +547,10 @@ class bdist_rpm(Command):
# use 'default' as contents of script
val = getattr(self, attr)
if val or default:
spec_file.extend(
[
'',
'%' + rpm_opt,
]
)
spec_file.extend([
'',
'%' + rpm_opt,
])
if val:
with open(val) as f:
spec_file.extend(f.read().split('\n'))
@@ -571,24 +558,20 @@ class bdist_rpm(Command):
spec_file.append(default)
# files section
spec_file.extend(
[
'',
'%files -f INSTALLED_FILES',
'%defattr(-,root,root)',
]
)
spec_file.extend([
'',
'%files -f INSTALLED_FILES',
'%defattr(-,root,root)',
])
if self.doc_files:
spec_file.append('%doc ' + ' '.join(self.doc_files))
if self.changelog:
spec_file.extend(
[
'',
'%changelog',
]
)
spec_file.extend([
'',
'%changelog',
])
spec_file.extend(self.changelog)
return spec_file

View File

@@ -2,8 +2,10 @@
Implements the Distutils 'build' command."""
import sys
import os
import sys
import sysconfig
from ..core import Command
from ..errors import DistutilsOptionError
from ..util import get_platform
@@ -25,16 +27,14 @@ class build(Command):
(
'build-lib=',
None,
"build directory for all distribution (defaults to either "
+ "build-purelib or build-platlib",
"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(),
f"platform name to build for, if supported [default: {get_platform()}]",
),
('compiler=', 'c', "specify the compiler type"),
('parallel=', 'j', "number of parallel build jobs"),
@@ -61,7 +61,7 @@ class build(Command):
self.compiler = None
self.plat_name = None
self.debug = None
self.force = 0
self.force = False
self.executable = None
self.parallel = None
@@ -78,7 +78,11 @@ class build(Command):
"using './configure --help' on your platform)"
)
plat_specifier = ".{}-{}".format(self.plat_name, sys.implementation.cache_tag)
plat_specifier = f".{self.plat_name}-{sys.implementation.cache_tag}"
# Python 3.13+ with --disable-gil shouldn't share build directories
if sysconfig.get_config_var('Py_GIL_DISABLED'):
plat_specifier += 't'
# 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

View File

@@ -15,10 +15,11 @@ module."""
# cut 'n paste. Sigh.
import os
from distutils._log import log
from ..core import Command
from ..errors import DistutilsSetupError
from ..sysconfig import customize_compiler
from distutils._log import log
def show_compilers():
@@ -56,7 +57,7 @@ class build_clib(Command):
self.define = None
self.undef = None
self.debug = None
self.force = 0
self.force = False
self.compiler = None
def finalize_options(self):
@@ -137,8 +138,8 @@ class build_clib(Command):
if '/' in name or (os.sep != '/' and os.sep in name):
raise DistutilsSetupError(
"bad library name '%s': "
"may not contain directory separators" % lib[0]
f"bad library name '{lib[0]}': "
"may not contain directory separators"
)
if not isinstance(build_info, dict):
@@ -154,7 +155,7 @@ class build_clib(Command):
return None
lib_names = []
for lib_name, build_info in self.libraries:
for lib_name, _build_info in self.libraries:
lib_names.append(lib_name)
return lib_names
@@ -165,9 +166,9 @@ 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'), "
f"in 'libraries' option (library '{lib_name}'), "
"'sources' must be present and must be "
"a list of source filenames" % lib_name
"a list of source filenames"
)
filenames.extend(sources)
@@ -178,9 +179,9 @@ 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'), "
f"in 'libraries' option (library '{lib_name}'), "
"'sources' must be present and must be "
"a list of source filenames" % lib_name
"a list of source filenames"
)
sources = list(sources)

View File

@@ -8,24 +8,22 @@ import contextlib
import os
import re
import sys
from distutils._log import log
from site import USER_BASE
from .._modified import newer_group
from ..core import Command
from ..errors import (
DistutilsOptionError,
DistutilsSetupError,
CCompilerError,
DistutilsError,
CompileError,
DistutilsError,
DistutilsOptionError,
DistutilsPlatformError,
DistutilsSetupError,
)
from ..sysconfig import customize_compiler, get_python_version
from ..sysconfig import get_config_h_filename
from ..dep_util import newer_group
from ..extension import Extension
from ..util import get_platform
from distutils._log import log
from . import py37compat
from site import USER_BASE
from ..sysconfig import customize_compiler, get_config_h_filename, get_python_version
from ..util import get_platform, is_mingw
# An extension name is just a dot-separated list of Python NAMEs (ie.
# the same as a fully-qualified module name).
@@ -59,7 +57,7 @@ class build_ext(Command):
# takes care of both command-line and client options
# in between initialize_options() and finalize_options())
sep_by = " (separated by '%s')" % os.pathsep
sep_by = f" (separated by '{os.pathsep}')"
user_options = [
('build-lib=', 'b', "directory for compiled extension modules"),
('build-temp=', 't', "directory for temporary files (build by-products)"),
@@ -67,13 +65,13 @@ class build_ext(Command):
'plat-name=',
'p',
"platform name to cross-compile for, if supported "
"(default: %s)" % get_platform(),
f"[default: {get_platform()}]",
),
(
'inplace',
'i',
"ignore build-lib and put compiled extensions into the source "
+ "directory alongside your pure Python modules",
"directory alongside your pure Python modules",
),
(
'include-dirs=',
@@ -111,7 +109,7 @@ class build_ext(Command):
self.build_lib = None
self.plat_name = None
self.build_temp = None
self.inplace = 0
self.inplace = False
self.package = None
self.include_dirs = None
@@ -130,6 +128,31 @@ class build_ext(Command):
self.user = None
self.parallel = None
@staticmethod
def _python_lib_dir(sysconfig):
"""
Resolve Python's library directory for building extensions
that rely on a shared Python library.
See python/cpython#44264 and python/cpython#48686
"""
if not sysconfig.get_config_var('Py_ENABLE_SHARED'):
return
if sysconfig.python_build:
yield '.'
return
if sys.platform == 'zos':
# On z/OS, a user is not required to install Python to
# a predetermined path, but can use Python portably
installed_dir = sysconfig.get_config_var('base')
lib_dir = sysconfig.get_config_var('platlibdir')
yield os.path.join(installed_dir, lib_dir)
else:
# building third party extensions
yield sysconfig.get_config_var('LIBDIR')
def finalize_options(self): # noqa: C901
from distutils import sysconfig
@@ -152,7 +175,7 @@ class build_ext(Command):
# Make sure Python's include directories (for Python.h, pyconfig.h,
# etc.) are in the include search path.
py_include = sysconfig.get_python_inc()
plat_py_include = sysconfig.get_python_inc(plat_specific=1)
plat_py_include = sysconfig.get_python_inc(plat_specific=True)
if self.include_dirs is None:
self.include_dirs = self.distribution.include_dirs or []
if isinstance(self.include_dirs, str):
@@ -189,7 +212,7 @@ class build_ext(Command):
# for extensions under windows use different directories
# for Release and Debug builds.
# also Python's library directory must be appended to library_dirs
if os.name == 'nt':
if os.name == 'nt' and not is_mingw():
# the 'libs' directory is for binary installs - we assume that
# must be the *native* platform. But we don't really support
# cross-compiling via a binary install anyway, so we let it go.
@@ -231,16 +254,7 @@ class build_ext(Command):
# building python standard extensions
self.library_dirs.append('.')
# For building extensions with a shared Python library,
# Python's library directory must be appended to library_dirs
# See Issues: #1600860, #4366
if sysconfig.get_config_var('Py_ENABLE_SHARED'):
if not sysconfig.python_build:
# building third party extensions
self.library_dirs.append(sysconfig.get_config_var('LIBDIR'))
else:
# building python standard extensions
self.library_dirs.append('.')
self.library_dirs.extend(self._python_lib_dir(sysconfig))
# The argument parsing will result in self.define being a string, but
# it has to be a list of 2-tuples. All the preprocessor symbols
@@ -412,9 +426,7 @@ 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.warning(
"'def_file' element of build info dict " "no longer supported"
)
log.warning("'def_file' element of build info dict no longer supported")
# Non-trivial stuff: 'macros' split into 'define_macros'
# and 'undef_macros'.
@@ -453,10 +465,7 @@ class build_ext(Command):
# And build the list of output (built) filenames. Note that this
# ignores the 'inplace' flag, and assumes everything goes in the
# "build" tree.
outputs = []
for ext in self.extensions:
outputs.append(self.get_ext_fullpath(ext.name))
return outputs
return [self.get_ext_fullpath(ext.name) for ext in self.extensions]
def build_extensions(self):
# First, sanity-check the 'extensions' list
@@ -499,15 +508,15 @@ class build_ext(Command):
except (CCompilerError, DistutilsError, CompileError) as e:
if not ext.optional:
raise
self.warn('building extension "{}" failed: {}'.format(ext.name, e))
self.warn(f'building extension "{ext.name}" failed: {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'), "
f"in 'ext_modules' option (extension '{ext.name}'), "
"'sources' must be present and must be "
"a list of source filenames" % ext.name
"a list of source filenames"
)
# sort to make the resulting .so file build reproducible
sources = sorted(sources)
@@ -629,8 +638,7 @@ class build_ext(Command):
# Do not override commandline arguments
if not self.swig_opts:
for o in extension.swig_opts:
swig_cmd.append(o)
swig_cmd.extend(extension.swig_opts)
for source in swig_sources:
target = swig_targets[source]
@@ -651,7 +659,7 @@ class build_ext(Command):
# 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")
fn = os.path.join(f"c:\\swig{vers}", "swig.exe")
if os.path.isfile(fn):
return fn
else:
@@ -659,7 +667,7 @@ class build_ext(Command):
else:
raise DistutilsPlatformError(
"I don't know how to find (much less run) SWIG "
"on platform '%s'" % os.name
f"on platform '{os.name}'"
)
# -- Name generators -----------------------------------------------
@@ -742,7 +750,7 @@ 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" and not is_mingw():
from .._msvccompiler import MSVCCompiler
if not isinstance(self.compiler, MSVCCompiler):
@@ -772,7 +780,7 @@ class build_ext(Command):
# A native build on an Android device or on Cygwin
if hasattr(sys, 'getandroidapilevel'):
link_libpython = True
elif sys.platform == 'cygwin':
elif sys.platform == 'cygwin' or is_mingw():
link_libpython = True
elif '_PYTHON_HOST_PLATFORM' in os.environ:
# We are cross-compiling for one of the relevant platforms
@@ -785,4 +793,4 @@ class build_ext(Command):
ldversion = get_config_var('LDVERSION')
return ext.libraries + ['python' + ldversion]
return ext.libraries + py37compat.pythonlib()
return ext.libraries

View File

@@ -2,15 +2,15 @@
Implements the Distutils 'build_py' command."""
import os
import importlib.util
import sys
import glob
import importlib.util
import os
import sys
from distutils._log import log
from ..core import Command
from ..errors import DistutilsOptionError, DistutilsFileError
from ..errors import DistutilsFileError, DistutilsOptionError
from ..util import convert_path
from distutils._log import log
class build_py(Command):
@@ -38,7 +38,7 @@ class build_py(Command):
self.package = None
self.package_data = None
self.package_dir = None
self.compile = 0
self.compile = False
self.optimize = 0
self.force = None
@@ -95,7 +95,7 @@ class build_py(Command):
self.build_packages()
self.build_package_data()
self.byte_compile(self.get_outputs(include_bytecode=0))
self.byte_compile(self.get_outputs(include_bytecode=False))
def get_data_files(self):
"""Generate list of '(package,src_dir,build_dir,filenames)' tuples"""
@@ -129,14 +129,14 @@ class build_py(Command):
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):
"""Copy data files into build directory"""
for package, src_dir, build_dir, filenames in self.data_files:
for _package, src_dir, build_dir, filenames in self.data_files:
for filename in filenames:
target = os.path.join(build_dir, filename)
self.mkpath(os.path.dirname(target))
@@ -191,12 +191,12 @@ class build_py(Command):
if package_dir != "":
if not os.path.exists(package_dir):
raise DistutilsFileError(
"package directory '%s' does not exist" % package_dir
f"package directory '{package_dir}' does not exist"
)
if not os.path.isdir(package_dir):
raise DistutilsFileError(
"supposed package directory '%s' exists, "
"but is not a directory" % package_dir
f"supposed package directory '{package_dir}' exists, "
"but is not a directory"
)
# Directories without __init__.py are namespace packages (PEP 420).
@@ -228,7 +228,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(f"excluding {setup_script}")
return modules
def find_modules(self):
@@ -264,7 +264,7 @@ class build_py(Command):
(package_dir, checked) = packages[package]
except KeyError:
package_dir = self.get_package_dir(package)
checked = 0
checked = False
if not checked:
init_py = self.check_package(package, package_dir)
@@ -306,10 +306,10 @@ class build_py(Command):
outfile_path = [build_dir] + list(package) + [module + ".py"]
return os.path.join(*outfile_path)
def get_outputs(self, include_bytecode=1):
def get_outputs(self, include_bytecode=True):
modules = self.find_all_modules()
outputs = []
for package, module, module_file in modules:
for package, module, _module_file in modules:
package = package.split('.')
filename = self.get_module_outfile(self.build_lib, package, module)
outputs.append(filename)
@@ -347,7 +347,7 @@ class build_py(Command):
outfile = self.get_module_outfile(self.build_lib, package, module)
dir = os.path.dirname(outfile)
self.mkpath(dir)
return self.copy_file(module_file, outfile, preserve_mode=0)
return self.copy_file(module_file, outfile, preserve_mode=False)
def build_modules(self):
modules = self.find_modules()

View File

@@ -4,13 +4,14 @@ Implements the Distutils 'build_scripts' command."""
import os
import re
from stat import ST_MODE
from distutils import sysconfig
from ..core import Command
from ..dep_util import newer
from ..util import convert_path
from distutils._log import log
import tokenize
from distutils import sysconfig
from distutils._log import log
from stat import ST_MODE
from .._modified import newer
from ..core import Command
from ..util import convert_path
shebang_pattern = re.compile('^#!.*python[0-9.]*([ \t].*)?$')
"""
@@ -95,7 +96,7 @@ class build_scripts(Command):
else:
first_line = f.readline()
if not first_line:
self.warn("%s is an empty file (skipping)" % script)
self.warn(f"{script} is an empty file (skipping)")
return
shebang_match = shebang_pattern.match(first_line)
@@ -109,8 +110,7 @@ class build_scripts(Command):
else:
executable = os.path.join(
sysconfig.get_config_var("BINDIR"),
"python%s%s"
% (
"python{}{}".format(
sysconfig.get_config_var("VERSION"),
sysconfig.get_config_var("EXE"),
),
@@ -156,9 +156,7 @@ class build_scripts(Command):
try:
shebang.encode('utf-8')
except UnicodeEncodeError:
raise ValueError(
"The shebang ({!r}) is not encodable " "to utf-8".format(shebang)
)
raise ValueError(f"The shebang ({shebang!r}) is not encodable to utf-8")
# If the script is encoded to a custom encoding (use a
# #coding:xxx cookie), the shebang has to be encodable to
@@ -167,6 +165,6 @@ class build_scripts(Command):
shebang.encode(encoding)
except UnicodeEncodeError:
raise ValueError(
"The shebang ({!r}) is not encodable "
"to the script encoding ({})".format(shebang, encoding)
f"The shebang ({shebang!r}) is not encodable "
f"to the script encoding ({encoding})"
)

View File

@@ -2,16 +2,17 @@
Implements the Distutils 'check' command.
"""
import contextlib
from ..core import Command
from ..errors import DistutilsSetupError
with contextlib.suppress(ImportError):
import docutils.utils
import docutils.parsers.rst
import docutils.frontend
import docutils.nodes
import docutils.parsers.rst
import docutils.utils
class SilentReporter(docutils.utils.Reporter):
def __init__(
@@ -20,7 +21,7 @@ with contextlib.suppress(ImportError):
report_level,
halt_level,
stream=None,
debug=0,
debug=False,
encoding='ascii',
error_handler='replace',
):
@@ -32,7 +33,7 @@ with contextlib.suppress(ImportError):
def system_message(self, level, message, *children, **kwargs):
self.messages.append((level, message, children, kwargs))
return docutils.nodes.system_message(
message, level=level, type=self.levels[level], *children, **kwargs
message, *children, level=level, type=self.levels[level], **kwargs
)
@@ -57,9 +58,9 @@ class check(Command):
def initialize_options(self):
"""Sets default values for options."""
self.restructuredtext = 0
self.restructuredtext = False
self.metadata = 1
self.strict = 0
self.strict = False
self._warnings = 0
def finalize_options(self):
@@ -99,13 +100,12 @@ class check(Command):
"""
metadata = self.distribution.metadata
missing = []
for attr in 'name', 'version':
if not getattr(metadata, attr, None):
missing.append(attr)
missing = [
attr for attr in ('name', 'version') if not getattr(metadata, attr, None)
]
if missing:
self.warn("missing required meta-data: %s" % ', '.join(missing))
self.warn("missing required meta-data: {}".format(', '.join(missing)))
def check_restructuredtext(self):
"""Checks if the long string fields are reST-compliant."""
@@ -115,7 +115,7 @@ class check(Command):
if line is None:
warning = warning[1]
else:
warning = '{} (line {})'.format(warning[1], line)
warning = f'{warning[1]} (line {line})'
self.warn(warning)
def _check_rst_data(self, data):
@@ -144,8 +144,11 @@ class check(Command):
try:
parser.parse(data, document)
except AttributeError as e:
reporter.messages.append(
(-1, 'Could not finish the parsing: %s.' % e, '', {})
)
reporter.messages.append((
-1,
f'Could not finish the parsing: {e}.',
'',
{},
))
return reporter.messages

View File

@@ -5,25 +5,26 @@ Implements the Distutils 'clean' command."""
# contributed by Bastian Kleineidam <calvin@cs.uni-sb.de>, added 2000-03-18
import os
from distutils._log import log
from ..core import Command
from ..dir_util import remove_tree
from distutils._log 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-base=', 'b', "base build directory [default: 'build.build-base']"),
(
'build-lib=',
None,
"build directory for all modules (default: 'build.build-lib')",
"build directory for all modules [default: 'build.build-lib']",
),
('build-temp=', 't', "temporary build directory (default: 'build.build-temp')"),
('build-temp=', 't', "temporary build directory [default: 'build.build-temp']"),
(
'build-scripts=',
None,
"build directory for scripts (default: 'build.build-scripts')",
"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"),

View File

@@ -9,13 +9,17 @@ configure-like tasks: "try to compile this C code", or "figure out where
this header file lives".
"""
from __future__ import annotations
import os
import pathlib
import re
from collections.abc import Sequence
from distutils._log import log
from ..core import Command
from ..errors import DistutilsExecError
from ..sysconfig import customize_compiler
from distutils._log import log
LANG_EXT = {"c": ".c", "c++": ".cxx"}
@@ -90,7 +94,7 @@ class config(Command):
if not isinstance(self.compiler, CCompiler):
self.compiler = new_compiler(
compiler=self.compiler, dry_run=self.dry_run, force=1
compiler=self.compiler, dry_run=self.dry_run, force=True
)
customize_compiler(self.compiler)
if self.include_dirs:
@@ -102,10 +106,10 @@ class config(Command):
def _gen_temp_sourcefile(self, body, headers, lang):
filename = "_configtest" + LANG_EXT[lang]
with open(filename, "w") as file:
with open(filename, "w", encoding='utf-8') as file:
if headers:
for header in headers:
file.write("#include <%s>\n" % header)
file.write(f"#include <{header}>\n")
file.write("\n")
file.write(body)
if body[-1] != "\n":
@@ -122,7 +126,7 @@ class config(Command):
def _compile(self, body, headers, include_dirs, lang):
src = self._gen_temp_sourcefile(body, headers, lang)
if self.dump_source:
dump_file(src, "compiling '%s':" % src)
dump_file(src, f"compiling '{src}':")
(obj,) = self.compiler.object_filenames([src])
self.temp_files.extend([src, obj])
self.compiler.compile([src], include_dirs=include_dirs)
@@ -199,15 +203,8 @@ class config(Command):
if isinstance(pattern, str):
pattern = re.compile(pattern)
with open(out) as file:
match = False
while True:
line = file.readline()
if line == '':
break
if pattern.search(line):
match = True
break
with open(out, encoding='utf-8') as file:
match = any(pattern.search(line) for line in file)
self._clean()
return match
@@ -295,8 +292,8 @@ class config(Command):
include_dirs=None,
libraries=None,
library_dirs=None,
decl=0,
call=0,
decl=False,
call=False,
):
"""Determine if function 'func' is available by constructing a
source file that refers to 'func', and compiles and links it.
@@ -314,12 +311,12 @@ class config(Command):
self._check_compiler()
body = []
if decl:
body.append("int %s ();" % func)
body.append(f"int {func} ();")
body.append("int main () {")
if call:
body.append(" %s();" % func)
body.append(f" {func}();")
else:
body.append(" %s;" % func)
body.append(f" {func};")
body.append("}")
body = "\n".join(body) + "\n"
@@ -331,7 +328,7 @@ class config(Command):
library_dirs=None,
headers=None,
include_dirs=None,
other_libraries=[],
other_libraries: Sequence[str] = [],
):
"""Determine if 'library' is available to be linked against,
without actually checking that any particular symbols are provided
@@ -346,7 +343,7 @@ class config(Command):
"int main (void) { }",
headers,
include_dirs,
[library] + other_libraries,
[library] + list(other_libraries),
library_dirs,
)
@@ -369,8 +366,4 @@ def dump_file(filename, head=None):
log.info('%s', filename)
else:
log.info(head)
file = open(filename)
try:
log.info(file.read())
finally:
file.close()
log.info(pathlib.Path(filename).read_text(encoding='utf-8'))

View File

@@ -2,25 +2,23 @@
Implements the Distutils 'install' command."""
import sys
import os
import contextlib
import sysconfig
import itertools
import os
import sys
import sysconfig
from distutils._log import log
from site import USER_BASE, USER_SITE
import jaraco.collections
from ..core import Command
from ..debug import DEBUG
from ..sysconfig import get_config_vars
from ..file_util import write_file
from ..util import convert_path, subst_vars, change_root
from ..util import get_platform
from ..errors import DistutilsOptionError, DistutilsPlatformError
from ..file_util import write_file
from ..sysconfig import get_config_vars
from ..util import change_root, convert_path, get_platform, subst_vars
from . import _framework_compat as fw
from .. import _collections
from site import USER_BASE
from site import USER_SITE
HAS_USER_SITE = True
@@ -196,8 +194,7 @@ class install(Command):
(
'install-platbase=',
None,
"base installation directory for platform-specific files "
+ "(instead of --exec-prefix or --home)",
"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
@@ -214,8 +211,7 @@ class install(Command):
(
'install-lib=',
None,
"installation directory for all module distributions "
+ "(overrides --install-purelib and --install-platlib)",
"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"),
@@ -245,9 +241,11 @@ class install(Command):
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,
f"install in user site-package '{USER_SITE}'",
))
boolean_options.append('user')
negative_opt = {'no-compile': 'compile'}
@@ -259,7 +257,7 @@ class install(Command):
self.prefix = None
self.exec_prefix = None
self.home = None
self.user = 0
self.user = False
# These select only the installation base; it's up to the user to
# specify the installation scheme (currently, that means supplying
@@ -294,7 +292,7 @@ class install(Command):
# 'install_path_file' is always true unless some outsider meddles
# with it.
self.extra_path = None
self.install_path_file = 1
self.install_path_file = True
# 'force' forces installation, even if target files are not
# out-of-date. 'skip_build' skips running the "build" command,
@@ -302,9 +300,9 @@ class install(Command):
# a user option, it's just there so the bdist_* commands can turn
# it off) determines whether we warn about installing to a
# directory not in sys.path.
self.force = 0
self.skip_build = 0
self.warn_dir = 1
self.force = False
self.skip_build = False
self.warn_dir = True
# These are only here as a conduit from the 'build' command to the
# 'install_*' commands that do the real work. ('build_base' isn't
@@ -349,8 +347,7 @@ class install(Command):
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):
@@ -432,9 +429,12 @@ class install(Command):
local_vars['userbase'] = self.install_userbase
local_vars['usersite'] = self.install_usersite
self.config_vars = _collections.DictStack(
[fw.vars(), compat_vars, sysconfig.get_config_vars(), local_vars]
)
self.config_vars = jaraco.collections.DictStack([
fw.vars(),
compat_vars,
sysconfig.get_config_vars(),
local_vars,
])
self.expand_basedirs()
@@ -598,7 +598,7 @@ class install(Command):
self.select_scheme(os.name)
except KeyError:
raise DistutilsPlatformError(
"I don't know how to install stuff on '%s'" % os.name
f"I don't know how to install stuff on '{os.name}'"
)
def select_scheme(self, name):
@@ -620,16 +620,14 @@ 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`."""
@@ -683,9 +681,9 @@ class install(Command):
if not self.user:
return
home = convert_path(os.path.expanduser("~"))
for name, path in self.config_vars.items():
for path in self.config_vars.values():
if str(path).startswith(home) and not os.path.isdir(path):
self.debug_print("os.makedirs('%s', 0o700)" % path)
self.debug_print(f"os.makedirs('{path}', 0o700)")
os.makedirs(path, 0o700)
# -- Command execution methods -------------------------------------
@@ -701,7 +699,7 @@ 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():
@@ -720,7 +718,7 @@ class install(Command):
self.execute(
write_file,
(self.record, outputs),
"writing list of installed files to '%s'" % self.record,
f"writing list of installed files to '{self.record}'",
)
sys_path = map(os.path.normpath, sys.path)
@@ -745,10 +743,10 @@ class install(Command):
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
write_file, (filename, [self.extra_dirs]), f"creating {filename}"
)
else:
self.warn("path file '%s' not created" % filename)
self.warn(f"path file '{filename}' not created")
# -- Reporting methods ---------------------------------------------

View File

@@ -5,7 +5,12 @@ platform-independent data files."""
# contributed by Bastian Kleineidam
from __future__ import annotations
import functools
import os
from typing import Iterable
from ..core import Command
from ..util import change_root, convert_path
@@ -18,7 +23,7 @@ class install_data(Command):
'install-dir=',
'd',
"base directory for installing data files "
"(default: installation base dir)",
"[default: installation base dir]",
),
('root=', None, "install everything relative to this alternate root directory"),
('force', 'f', "force installation (overwrite existing files)"),
@@ -30,9 +35,9 @@ class install_data(Command):
self.install_dir = None
self.outfiles = []
self.root = None
self.force = 0
self.force = False
self.data_files = self.distribution.data_files
self.warn_dir = 1
self.warn_dir = True
def finalize_options(self):
self.set_undefined_options(
@@ -45,36 +50,42 @@ class install_data(Command):
def run(self):
self.mkpath(self.install_dir)
for f in self.data_files:
if isinstance(f, str):
# 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)
)
(out, _) = self.copy_file(f, self.install_dir)
self.outfiles.append(out)
else:
# it's a tuple with path to install to and a list of files
dir = convert_path(f[0])
if not os.path.isabs(dir):
dir = os.path.join(self.install_dir, dir)
elif self.root:
dir = change_root(self.root, dir)
self.mkpath(dir)
self._copy(f)
if f[1] == []:
# If there are no files listed, the user must be
# trying to create an empty directory, so add the
# directory to the list of output files.
self.outfiles.append(dir)
else:
# Copy files, adding them to the list of output files.
for data in f[1]:
data = convert_path(data)
(out, _) = self.copy_file(data, dir)
self.outfiles.append(out)
@functools.singledispatchmethod
def _copy(self, f: tuple[str | os.PathLike, Iterable[str | os.PathLike]]):
# it's a tuple with path to install to and a list of files
dir = convert_path(f[0])
if not os.path.isabs(dir):
dir = os.path.join(self.install_dir, dir)
elif self.root:
dir = change_root(self.root, dir)
self.mkpath(dir)
if f[1] == []:
# If there are no files listed, the user must be
# trying to create an empty directory, so add the
# directory to the list of output files.
self.outfiles.append(dir)
else:
# Copy files, adding them to the list of output files.
for data in f[1]:
data = convert_path(data)
(out, _) = self.copy_file(data, dir)
self.outfiles.append(out)
@_copy.register(str)
@_copy.register(os.PathLike)
def _(self, f: str | os.PathLike):
# 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 "
f"'{f}' -- installing right in '{self.install_dir}'"
)
(out, _) = self.copy_file(f, self.install_dir)
self.outfiles.append(out)
def get_inputs(self):
return self.data_files or []

View File

@@ -6,12 +6,12 @@ a package's PKG-INFO metadata.
"""
import os
import sys
import re
import sys
from ..cmd import Command
from .. import dir_util
from .._log import log
from ..cmd import Command
class install_egg_info(Command):

View File

@@ -19,7 +19,7 @@ class install_headers(Command):
def initialize_options(self):
self.install_dir = None
self.force = 0
self.force = False
self.outfiles = []
def finalize_options(self):

View File

@@ -3,14 +3,13 @@
Implements the Distutils 'install_lib' command
(install all Python modules)."""
import os
import importlib.util
import os
import sys
from ..core import Command
from ..errors import DistutilsOptionError
# Extension for Python source files.
PYTHON_SOURCE_EXTENSION = ".py"
@@ -55,7 +54,7 @@ class install_lib(Command):
# let the 'install' command dictate our installation directory
self.install_dir = None
self.build_dir = None
self.force = 0
self.force = False
self.compile = None
self.optimize = None
self.skip_build = None
@@ -82,9 +81,9 @@ class install_lib(Command):
if not isinstance(self.optimize, int):
try:
self.optimize = int(self.optimize)
if self.optimize not in (0, 1, 2):
raise AssertionError
except (ValueError, AssertionError):
except ValueError:
pass
if self.optimize not in (0, 1, 2):
raise DistutilsOptionError("optimize must be 0, 1, or 2")
def run(self):
@@ -115,7 +114,7 @@ class install_lib(Command):
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
f"'{self.build_dir}' does not exist -- no Python modules to install"
)
return
return outfiles
@@ -162,9 +161,7 @@ class install_lib(Command):
build_dir = getattr(build_cmd, cmd_option)
prefix_len = len(build_dir) + len(os.sep)
outputs = []
for file in build_files:
outputs.append(os.path.join(output_dir, file[prefix_len:]))
outputs = [os.path.join(output_dir, file[prefix_len:]) for file in build_files]
return outputs

View File

@@ -6,10 +6,11 @@ Python scripts."""
# contributed by Bastian Kleineidam
import os
from ..core import Command
from distutils._log import log
from stat import ST_MODE
from ..core import Command
class install_scripts(Command):
description = "install scripts (Python or otherwise)"
@@ -25,7 +26,7 @@ class install_scripts(Command):
def initialize_options(self):
self.install_dir = None
self.force = 0
self.force = False
self.build_dir = None
self.skip_build = None

View File

@@ -1,31 +0,0 @@
import sys
def _pythonlib_compat():
"""
On Python 3.7 and earlier, distutils would include the Python
library. See pypa/distutils#9.
"""
from distutils import sysconfig
if not sysconfig.get_config_var('Py_ENABLED_SHARED'):
return
yield 'python{}.{}{}'.format(
sys.hexversion >> 24,
(sys.hexversion >> 16) & 0xFF,
sysconfig.get_config_var('ABIFLAGS'),
)
def compose(f1, f2):
return lambda *args, **kwargs: f1(f2(*args, **kwargs))
pythonlib = (
compose(list, _pythonlib_compat)
if sys.version_info < (3, 8)
and sys.platform != 'darwin'
and sys.platform[:3] != 'aix'
else list
)

View File

@@ -1,320 +0,0 @@
"""distutils.command.register
Implements the Distutils 'register' command (register with the repository).
"""
# created 2002/10/21, Richard Jones
import getpass
import io
import logging
import urllib.parse
import urllib.request
from warnings import warn
from ..core import PyPIRCCommand
from distutils._log import log
class register(PyPIRCCommand):
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',
),
]
boolean_options = PyPIRCCommand.boolean_options + [
'verify',
'list-classifiers',
'strict',
]
sub_commands = [('check', lambda self: True)]
def initialize_options(self):
PyPIRCCommand.initialize_options(self)
self.list_classifiers = 0
self.strict = 0
def finalize_options(self):
PyPIRCCommand.finalize_options(self)
# setting options for the `check` subcommand
check_options = {
'strict': ('register', self.strict),
'restructuredtext': ('register', 1),
}
self.distribution.command_options['check'] = check_options
def run(self):
self.finalize_options()
self._set_config()
# Run sub commands
for cmd_name in self.get_sub_commands():
self.run_command(cmd_name)
if self.dry_run:
self.verify_metadata()
elif self.list_classifiers:
self.classifiers()
else:
self.send_metadata()
def check_metadata(self):
"""Deprecated API."""
warn(
"distutils.command.register.check_metadata is deprecated; "
"use the check command instead",
DeprecationWarning,
)
check = self.distribution.get_command_obj('check')
check.ensure_finalized()
check.strict = self.strict
check.restructuredtext = 1
check.run()
def _set_config(self):
'''Reads the configuration file and set attributes.'''
config = self._read_pypirc()
if config != {}:
self.username = config['username']
self.password = config['password']
self.repository = config['repository']
self.realm = config['realm']
self.has_config = True
else:
if self.repository not in ('pypi', self.DEFAULT_REPOSITORY):
raise ValueError('%s not found in .pypirc' % self.repository)
if self.repository == 'pypi':
self.repository = self.DEFAULT_REPOSITORY
self.has_config = False
def classifiers(self):
'''Fetch the list of classifiers from the server.'''
url = self.repository + '?:action=list_classifiers'
response = urllib.request.urlopen(url)
log.info(self._read_pypi_response(response))
def verify_metadata(self):
'''Send the metadata to the package index server to be checked.'''
# send the info to the server and report the result
(code, result) = self.post_to_server(self.build_post_data('verify'))
log.info('Server response (%s): %s', code, result)
def send_metadata(self): # noqa: C901
'''Send the metadata to the package index server.
Well, do the following:
1. figure who the user is, and then
2. send the data as a Basic auth'ed POST.
First we try to read the username/password from $HOME/.pypirc,
which is a ConfigParser-formatted file with a section
[distutils] containing username and password entries (both
in clear text). Eg:
[distutils]
index-servers =
pypi
[pypi]
username: fred
password: sekrit
Otherwise, to figure who the user is, we offer the user three
choices:
1. use existing login,
2. register as a new user, or
3. set the password to a random string and email the user.
'''
# see if we can short-cut and get the username/password from the
# config
if self.has_config:
choice = '1'
username = self.username
password = self.password
else:
choice = 'x'
username = password = ''
# get the user's login info
choices = '1 2 3 4'.split()
while choice not in choices:
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]: ''',
logging.INFO,
)
choice = input()
if not choice:
choice = '1'
elif choice not in choices:
print('Please choose one of the four options!')
if choice == '1':
# get the username and password
while not username:
username = input('Username: ')
while not password:
password = getpass.getpass('Password: ')
# set up the authentication
auth = urllib.request.HTTPPasswordMgr()
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 ({}): {}'.format(code, result), logging.INFO)
# possibly save the login
if code == 200:
if self.has_config:
# sharing the password in the distribution instance
# 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.'
),
logging.INFO,
)
self.announce(
'(the login will be stored in %s)' % self._get_rc_file(),
logging.INFO,
)
choice = 'X'
while choice.lower() not in 'yn':
choice = input('Save your login (y/N)?')
if not choice:
choice = 'n'
if choice.lower() == 'y':
self._store_pypirc(username, password)
elif choice == '2':
data = {':action': 'user'}
data['name'] = data['password'] = data['email'] = ''
data['confirm'] = None
while not data['name']:
data['name'] = input('Username: ')
while data['password'] != data['confirm']:
while not data['password']:
data['password'] = getpass.getpass('Password: ')
while not data['confirm']:
data['confirm'] = getpass.getpass(' Confirm: ')
if data['password'] != data['confirm']:
data['password'] = ''
data['confirm'] = None
print("Password and confirm don't match!")
while not data['email']:
data['email'] = input(' EMail: ')
code, result = self.post_to_server(data)
if code != 200:
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.')
elif choice == '3':
data = {':action': 'password_reset'}
data['email'] = ''
while not data['email']:
data['email'] = input('Your email address: ')
code, result = self.post_to_server(data)
log.info('Server response (%s): %s', code, result)
def build_post_data(self, action):
# figure the data to send - the metadata plus some additional
# information used by the package server
meta = self.distribution.metadata
data = {
':action': action,
'metadata_version': '1.0',
'name': meta.get_name(),
'version': meta.get_version(),
'summary': meta.get_description(),
'home_page': meta.get_url(),
'author': meta.get_contact(),
'author_email': meta.get_contact_email(),
'license': meta.get_licence(),
'description': meta.get_long_description(),
'keywords': meta.get_keywords(),
'platform': meta.get_platforms(),
'classifiers': meta.get_classifiers(),
'download_url': meta.get_download_url(),
# PEP 314
'provides': meta.get_provides(),
'requires': meta.get_requires(),
'obsoletes': meta.get_obsoletes(),
}
if data['provides'] or data['requires'] or data['obsoletes']:
data['metadata_version'] = '1.1'
return data
def post_to_server(self, data, auth=None): # noqa: C901
'''Post a query to the server, and return a string response.'''
if 'name' in data:
self.announce(
'Registering {} to {}'.format(data['name'], self.repository),
logging.INFO,
)
# Build up the MIME payload for the urllib2 POST data
boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254'
sep_boundary = '\n--' + boundary
end_boundary = sep_boundary + '--'
body = io.StringIO()
for key, value in data.items():
# handle multiple entries for the same name
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(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")
# build the Request
headers = {
'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)
)
data = ''
try:
result = opener.open(req)
except urllib.error.HTTPError as e:
if self.show_response:
data = e.fp.read()
result = e.code, e.msg
except urllib.error.URLError as e:
result = 500, str(e)
else:
if self.show_response:
data = self._read_pypi_response(result)
result = 200, 'OK'
if self.show_response:
msg = '\n'.join(('-' * 75, data, '-' * 75))
self.announce(msg, logging.INFO)
return result

View File

@@ -4,31 +4,29 @@ Implements the Distutils 'sdist' command (create a source distribution)."""
import os
import sys
from distutils import archive_util, dir_util, file_util
from distutils._log import log
from glob import glob
from warnings import warn
from itertools import filterfalse
from ..core import Command
from distutils import dir_util
from distutils import file_util
from distutils import archive_util
from ..text_file import TextFile
from ..filelist import FileList
from distutils._log import log
from ..util import convert_path
from ..errors import DistutilsOptionError, DistutilsTemplateError
from ..filelist import FileList
from ..text_file import TextFile
from ..util import convert_path
def show_formats():
"""Print all possible values for the 'formats' option (used by
the "--help-formats" command-line option).
"""
from ..fancy_getopt import FancyGetopt
from ..archive_util import ARCHIVE_FORMATS
from ..fancy_getopt import FancyGetopt
formats = []
for format in ARCHIVE_FORMATS.keys():
formats.append(("formats=" + format, None, ARCHIVE_FORMATS[format][2]))
formats.sort()
formats = sorted(
("formats=" + format, None, ARCHIVE_FORMATS[format][2])
for format in ARCHIVE_FORMATS.keys()
)
FancyGetopt(formats).print_help("List of available source distribution formats:")
@@ -62,7 +60,7 @@ class sdist(Command):
(
'manifest-only',
'o',
"just regenerate the manifest and then stop " "(implies --force-manifest)",
"just regenerate the manifest and then stop (implies --force-manifest)",
),
(
'force-manifest',
@@ -79,7 +77,7 @@ class sdist(Command):
(
'dist-dir=',
'd',
"directory to put the source distribution archive(s) in " "[default: dist]",
"directory to put the source distribution archive(s) in [default: dist]",
),
(
'metadata-check',
@@ -126,14 +124,14 @@ class sdist(Command):
# 'use_defaults': if true, we will include the default file set
# in the manifest
self.use_defaults = 1
self.prune = 1
self.use_defaults = True
self.prune = True
self.manifest_only = 0
self.force_manifest = 0
self.manifest_only = False
self.force_manifest = False
self.formats = ['gztar']
self.keep_temp = 0
self.keep_temp = False
self.dist_dir = None
self.archive_files = None
@@ -151,7 +149,7 @@ class sdist(Command):
bad_format = archive_util.check_archive_formats(self.formats)
if bad_format:
raise DistutilsOptionError("unknown archive format '%s'" % bad_format)
raise DistutilsOptionError(f"unknown archive format '{bad_format}'")
if self.dist_dir is None:
self.dist_dir = "dist"
@@ -178,17 +176,6 @@ class sdist(Command):
# or zipfile, or whatever.
self.make_distribution()
def check_metadata(self):
"""Deprecated API."""
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()
def get_file_list(self):
"""Figure out the list of files to include in the source
distribution, and put it in 'self.filelist'. This might involve
@@ -289,7 +276,7 @@ class sdist(Command):
if self._cs_path_exists(fn):
self.filelist.append(fn)
else:
self.warn("standard file '%s' not found" % fn)
self.warn(f"standard file '{fn}' not found")
def _add_defaults_optional(self):
optional = ['tests/test*.py', 'test/test*.py', 'setup.cfg']
@@ -309,7 +296,7 @@ class sdist(Command):
# getting package_data files
# (computed in build_py.data_files by build_py.finalize_options)
for pkg, src_dir, build_dir, filenames in build_py.data_files:
for _pkg, src_dir, _build_dir, filenames in build_py.data_files:
for filename in filenames:
self.filelist.append(os.path.join(src_dir, filename))
@@ -354,12 +341,12 @@ class sdist(Command):
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,
strip_comments=True,
skip_blanks=True,
join_lines=True,
lstrip_ws=True,
rstrip_ws=True,
collapse_join=True,
)
try:
@@ -392,7 +379,7 @@ class sdist(Command):
build = self.get_finalized_command('build')
base_dir = self.distribution.get_fullname()
self.filelist.exclude_pattern(None, prefix=build.build_base)
self.filelist.exclude_pattern(None, prefix=os.fspath(build.build_base))
self.filelist.exclude_pattern(None, prefix=base_dir)
if sys.platform == 'win32':
@@ -402,7 +389,7 @@ class sdist(Command):
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)
self.filelist.exclude_pattern(vcs_ptrn, is_regex=True)
def write_manifest(self):
"""Write the file list in 'self.filelist' (presumably as filled in
@@ -411,8 +398,7 @@ class sdist(Command):
"""
if self._manifest_is_not_generated():
log.info(
"not writing to manually maintained "
"manifest file '%s'" % self.manifest
f"not writing to manually maintained manifest file '{self.manifest}'"
)
return
@@ -421,7 +407,7 @@ class sdist(Command):
self.execute(
file_util.write_file,
(self.manifest, content),
"writing manifest file '%s'" % self.manifest,
f"writing manifest file '{self.manifest}'",
)
def _manifest_is_not_generated(self):
@@ -429,11 +415,8 @@ class sdist(Command):
if not os.path.isfile(self.manifest):
return False
fp = open(self.manifest)
try:
first_line = fp.readline()
finally:
fp.close()
with open(self.manifest, encoding='utf-8') as fp:
first_line = next(fp)
return first_line != '# file GENERATED by distutils, do NOT edit\n'
def read_manifest(self):
@@ -442,13 +425,11 @@ class sdist(Command):
distribution.
"""
log.info("reading manifest file '%s'", self.manifest)
with open(self.manifest) as manifest:
for line in manifest:
with open(self.manifest, encoding='utf-8') as lines:
self.filelist.extend(
# ignore comments and blank lines
line = line.strip()
if line.startswith('#') or not line:
continue
self.filelist.append(line)
filter(None, filterfalse(is_comment, map(str.strip, lines)))
)
def make_release_tree(self, base_dir, files):
"""Create the directory tree that will become the source
@@ -474,10 +455,10 @@ 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 = f"making hard links in {base_dir}..."
else: # nope, have to copy
link = None
msg = "copying files to %s..." % base_dir
msg = f"copying files to {base_dir}..."
if not files:
log.warning("no files to distribute -- empty manifest?")
@@ -528,3 +509,7 @@ class sdist(Command):
was run, or None if the command hasn't run yet.
"""
return self.archive_files
def is_comment(line):
return line.startswith('#')

View File

@@ -1,206 +0,0 @@
"""
distutils.command.upload
Implements the Distutils 'upload' subcommand (upload package to a package
index).
"""
import os
import io
import hashlib
import logging
from base64 import standard_b64encode
from urllib.request import urlopen, Request, HTTPError
from urllib.parse import urlparse
from ..errors import DistutilsError, DistutilsOptionError
from ..core import PyPIRCCommand
from ..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),
}
class upload(PyPIRCCommand):
description = "upload binary package to PyPI"
user_options = PyPIRCCommand.user_options + [
('sign', 's', 'sign files to upload using gpg'),
('identity=', 'i', 'GPG identity used to sign files'),
]
boolean_options = PyPIRCCommand.boolean_options + ['sign']
def initialize_options(self):
PyPIRCCommand.initialize_options(self)
self.username = ''
self.password = ''
self.show_response = 0
self.sign = False
self.identity = None
def finalize_options(self):
PyPIRCCommand.finalize_options(self)
if self.identity and not self.sign:
raise DistutilsOptionError("Must use --sign for --identity to have meaning")
config = self._read_pypirc()
if config != {}:
self.username = config['username']
self.password = config['password']
self.repository = config['repository']
self.realm = config['realm']
# getting the password from the distribution
# if previously set by the register command
if not self.password and self.distribution.password:
self.password = self.distribution.password
def run(self):
if not self.distribution.dist_files:
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)
def upload_file(self, command, pyversion, filename): # noqa: C901
# Makes sure the repository URL is compliant
schema, netloc, url, params, query, fragments = urlparse(self.repository)
if params or query or fragments:
raise AssertionError("Incompatible url %s" % self.repository)
if schema not in ('http', 'https'):
raise AssertionError("unsupported schema " + schema)
# Sign if requested
if self.sign:
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)
# Fill in the data - send all the meta-data in case we need to
# register a new release
f = open(filename, 'rb')
try:
content = f.read()
finally:
f.close()
meta = self.distribution.metadata
data = {
# action
':action': 'file_upload',
'protocol_version': '1',
# identify release
'name': meta.get_name(),
'version': meta.get_version(),
# file content
'content': (os.path.basename(filename), content),
'filetype': command,
'pyversion': pyversion,
# additional meta-data
'metadata_version': '1.0',
'summary': meta.get_description(),
'home_page': meta.get_url(),
'author': meta.get_contact(),
'author_email': meta.get_contact_email(),
'license': meta.get_licence(),
'description': meta.get_long_description(),
'keywords': meta.get_keywords(),
'platform': meta.get_platforms(),
'classifiers': meta.get_classifiers(),
'download_url': meta.get_download_url(),
# PEP 314
'provides': meta.get_provides(),
'requires': meta.get_requires(),
'obsoletes': meta.get_obsoletes(),
}
data['comment'] = ''
# file content digests
for digest_name, digest_cons in _FILE_CONTENT_DIGESTS.items():
if digest_cons is None:
continue
try:
data[digest_name] = digest_cons(content).hexdigest()
except ValueError:
# hash digest not available or blocked by security policy
pass
if self.sign:
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')
# 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')
# Build up the MIME payload for the POST data
boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254'
sep_boundary = b'\r\n--' + boundary.encode('ascii')
end_boundary = sep_boundary + b'--\r\n'
body = io.BytesIO()
for key, value in data.items():
title = '\r\nContent-Disposition: form-data; name="%s"' % key
# handle multiple entries for the same name
if not isinstance(value, list):
value = [value]
for value in value:
if type(value) is tuple:
title += '; filename="%s"' % value[0]
value = value[1]
else:
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(value)
body.write(end_boundary)
body = body.getvalue()
msg = "Submitting {} to {}".format(filename, self.repository)
self.announce(msg, logging.INFO)
# build the Request
headers = {
'Content-type': 'multipart/form-data; boundary=%s' % boundary,
'Content-length': str(len(body)),
'Authorization': auth,
}
request = Request(self.repository, data=body, headers=headers)
# send the data
try:
result = urlopen(request)
status = result.getcode()
reason = result.msg
except HTTPError as e:
status = e.code
reason = e.msg
except OSError as e:
self.announce(str(e), logging.ERROR)
raise
if status == 200:
self.announce(
'Server response ({}): {}'.format(status, reason), logging.INFO
)
if self.show_response:
text = self._read_pypi_response(result)
msg = '\n'.join(('-' * 75, text, '-' * 75))
self.announce(msg, logging.INFO)
else:
msg = 'Upload failed ({}): {}'.format(status, reason)
self.announce(msg, logging.ERROR)
raise DistutilsError(msg)