diff options
| author | Jason R. Coombs <jaraco@jaraco.com> | 2023-01-14 11:13:55 -0500 |
|---|---|---|
| committer | Jason R. Coombs <jaraco@jaraco.com> | 2023-01-14 11:13:55 -0500 |
| commit | 245da5441248eeb2d575034d04cbc241bf545161 (patch) | |
| tree | d76526e1461252cc1119cd9482a64ef1e75f7838 /setuptools/_distutils/command | |
| parent | d7b783a4b8b01e58135e40bd9a1db8a82c090982 (diff) | |
| parent | 82eee6a998251b33ab3984f39b25c27ca72ba8b0 (diff) | |
| download | python-setuptools-git-245da5441248eeb2d575034d04cbc241bf545161.tar.gz | |
Merge branch 'main' into debt/remove-legacy-version
Diffstat (limited to 'setuptools/_distutils/command')
25 files changed, 1682 insertions, 2473 deletions
diff --git a/setuptools/_distutils/command/__init__.py b/setuptools/_distutils/command/__init__.py index 481eea9f..028dcfa0 100644 --- a/setuptools/_distutils/command/__init__.py +++ b/setuptools/_distutils/command/__init__.py @@ -3,29 +3,23 @@ Package containing implementation of all the standard Distutils commands.""" -__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__ = [ # noqa: F822 + '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', + 'check', + 'upload', +] diff --git a/setuptools/_distutils/command/_framework_compat.py b/setuptools/_distutils/command/_framework_compat.py new file mode 100644 index 00000000..cffa27cb --- /dev/null +++ b/setuptools/_distutils/command/_framework_compat.py @@ -0,0 +1,55 @@ +""" +Backward compatibility for homebrew builds on macOS. +""" + + +import sys +import os +import functools +import subprocess +import sysconfig + + +@functools.lru_cache() +def enabled(): + """ + Only enabled for Python 3.9 framework homebrew builds + except ensurepip and venv. + """ + PY39 = (3, 9) < sys.version_info < (3, 10) + framework = sys.platform == 'darwin' and sys._framework + homebrew = "Cellar" in sysconfig.get_config_var('projectbase') + venv = sys.prefix != sys.base_prefix + ensurepip = os.environ.get("ENSUREPIP_OPTIONS") + return PY39 and framework and homebrew and not venv and not ensurepip + + +schemes = dict( + osx_framework_library=dict( + stdlib='{installed_base}/{platlibdir}/python{py_version_short}', + platstdlib='{platbase}/{platlibdir}/python{py_version_short}', + purelib='{homebrew_prefix}/lib/python{py_version_short}/site-packages', + platlib='{homebrew_prefix}/{platlibdir}/python{py_version_short}/site-packages', + include='{installed_base}/include/python{py_version_short}{abiflags}', + platinclude='{installed_platbase}/include/python{py_version_short}{abiflags}', + scripts='{homebrew_prefix}/bin', + data='{homebrew_prefix}', + ) +) + + +@functools.lru_cache() +def vars(): + if not enabled(): + return {} + homebrew_prefix = subprocess.check_output(['brew', '--prefix'], text=True).strip() + return locals() + + +def scheme(name): + """ + Override the selected scheme for posix_prefix. + """ + if not enabled() or not name.endswith('_prefix'): + return name + return 'osx_framework_library' diff --git a/setuptools/_distutils/command/bdist.py b/setuptools/_distutils/command/bdist.py index 014871d2..bf0baab0 100644 --- a/setuptools/_distutils/command/bdist.py +++ b/setuptools/_distutils/command/bdist.py @@ -4,79 +4,93 @@ Implements the Distutils 'bdist' command (create a built [binary] distribution).""" import os -from distutils.core import Command -from distutils.errors import * -from distutils.util import get_platform +import warnings + +from ..core import Command +from ..errors import DistutilsPlatformError, DistutilsOptionError +from ..util import get_platform def show_formats(): - """Print list of available formats (arguments to "--format" option). - """ - from distutils.fancy_getopt import FancyGetopt + """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_command[format][1])) + formats.append(("formats=" + format, None, bdist.format_commands[format][1])) pretty_printer = FancyGetopt(formats) pretty_printer.print_help("List of available distribution formats:") +class ListCompat(dict): + # adapter to allow for Setuptools compatibility in format_commands + def append(self, item): + warnings.warn( + """format_commands is now a dict. append is deprecated.""", + DeprecationWarning, + stacklevel=2, + ) + + class bdist(Command): 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'} - - # Establish the preferred order (for the --help-formats option). - 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") - } - + 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"), + } + ) + + # for compatibility until consumers only reference format_commands + format_command = format_commands def initialize_options(self): self.bdist_base = None @@ -100,8 +114,7 @@ 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,8 +122,9 @@ 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" @@ -120,7 +134,7 @@ class bdist(Command): commands = [] for format in self.formats: try: - commands.append(self.format_command[format][0]) + commands.append(self.format_commands[format][0]) except KeyError: raise DistutilsOptionError("invalid format '%s'" % format) @@ -138,6 +152,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) diff --git a/setuptools/_distutils/command/bdist_dumb.py b/setuptools/_distutils/command/bdist_dumb.py index f0d6b5b8..071da77e 100644 --- a/setuptools/_distutils/command/bdist_dumb.py +++ b/setuptools/_distutils/command/bdist_dumb.py @@ -5,47 +5,59 @@ distribution -- i.e., just an archive to be unpacked under $prefix or $exec_prefix).""" import os -from distutils.core import Command -from distutils.util import get_platform -from distutils.dir_util import remove_tree, ensure_relative -from distutils.errors import * -from distutils.sysconfig import get_python_version -from distutils import log +from ..core import Command +from ..util import get_platform +from ..dir_util import remove_tree, ensure_relative +from ..errors import DistutilsPlatformError +from ..sysconfig import get_python_version +from distutils._log import log + 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 +80,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: @@ -90,34 +105,40 @@ class bdist_dumb(Command): # 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) diff --git a/setuptools/_distutils/command/bdist_msi.py b/setuptools/_distutils/command/bdist_msi.py deleted file mode 100644 index 15259532..00000000 --- a/setuptools/_distutils/command/bdist_msi.py +++ /dev/null @@ -1,749 +0,0 @@ -# Copyright (C) 2005, 2006 Martin von Löwis -# Licensed to PSF under a Contributor Agreement. -# The bdist_wininst command proper -# based on bdist_wininst -""" -Implements the bdist_msi command. -""" - -import os -import sys -import warnings -from distutils.core import Command -from distutils.dir_util import remove_tree -from distutils.sysconfig import get_python_version -from distutils.version import StrictVersion -from distutils.errors import DistutilsOptionError -from distutils.util import get_platform -from distutils import log -import msilib -from msilib import schema, sequence, text -from msilib import Directory, Feature, Dialog, add_data - -class PyDialog(Dialog): - """Dialog class with a fixed layout: controls at the top, then a ruler, - then a list of buttons: back, next, cancel. Optionally a bitmap at the - left.""" - def __init__(self, *args, **kw): - """Dialog(database, name, x, y, w, h, attributes, title, first, - default, cancel, bitmap=true)""" - super().__init__(*args) - ruler = self.h - 36 - bmwidth = 152*ruler/328 - #if kw.get("bitmap", True): - # self.bitmap("Bitmap", 0, 0, bmwidth, ruler, "PythonWin") - self.line("BottomLine", 0, ruler, self.w, 0) - - def title(self, title): - "Set the title text of the dialog at the top." - # name, x, y, w, h, flags=Visible|Enabled|Transparent|NoPrefix, - # text, in VerdanaBold10 - self.text("Title", 15, 10, 320, 60, 0x30003, - r"{\VerdanaBold10}%s" % title) - - def back(self, title, next, name = "Back", active = 1): - """Add a back button with a given title, the tab-next button, - its name in the Control table, possibly initially disabled. - - Return the button, so that events can be associated""" - if active: - flags = 3 # Visible|Enabled - else: - flags = 1 # Visible - return self.pushbutton(name, 180, self.h-27 , 56, 17, flags, title, next) - - def cancel(self, title, next, name = "Cancel", active = 1): - """Add a cancel button with a given title, the tab-next button, - its name in the Control table, possibly initially disabled. - - Return the button, so that events can be associated""" - if active: - flags = 3 # Visible|Enabled - else: - flags = 1 # Visible - return self.pushbutton(name, 304, self.h-27, 56, 17, flags, title, next) - - def next(self, title, next, name = "Next", active = 1): - """Add a Next button with a given title, the tab-next button, - its name in the Control table, possibly initially disabled. - - Return the button, so that events can be associated""" - if active: - flags = 3 # Visible|Enabled - else: - flags = 1 # Visible - return self.pushbutton(name, 236, self.h-27, 56, 17, flags, title, next) - - def xbutton(self, name, title, next, xpos): - """Add a button with a given title, the tab-next button, - its name in the Control table, giving its x position; the - y-position is aligned with the other buttons. - - Return the button, so that events can be associated""" - return self.pushbutton(name, int(self.w*xpos - 28), self.h-27, 56, 17, 3, title, next) - -class bdist_msi(Command): - - description = "create a Microsoft Installer (.msi) binary distribution" - - 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"), - ('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"), - ] - - boolean_options = ['keep-temp', 'no-target-compile', 'no-target-optimize', - 'skip-build'] - - all_versions = ['2.0', '2.1', '2.2', '2.3', '2.4', - '2.5', '2.6', '2.7', '2.8', '2.9', - '3.0', '3.1', '3.2', '3.3', '3.4', - '3.5', '3.6', '3.7', '3.8', '3.9'] - other_version = 'X' - - def __init__(self, *args, **kw): - super().__init__(*args, **kw) - warnings.warn("bdist_msi command is deprecated since Python 3.9, " - "use bdist_wheel (wheel packages) instead", - DeprecationWarning, 2) - - def initialize_options(self): - self.bdist_dir = None - self.plat_name = None - self.keep_temp = 0 - self.no_target_compile = 0 - self.no_target_optimize = 0 - self.target_version = None - self.dist_dir = None - self.skip_build = None - self.install_script = None - self.pre_install_script = None - self.versions = None - - def finalize_options(self): - self.set_undefined_options('bdist', ('skip_build', 'skip_build')) - - if self.bdist_dir is None: - bdist_base = self.get_finalized_command('bdist').bdist_base - self.bdist_dir = os.path.join(bdist_base, 'msi') - - short_version = get_python_version() - if (not self.target_version) and self.distribution.has_ext_modules(): - self.target_version = short_version - - if self.target_version: - self.versions = [self.target_version] - if not self.skip_build and self.distribution.has_ext_modules()\ - and self.target_version != short_version: - raise DistutilsOptionError( - "target version can only be %s, or the '--skip-build'" - " option must be specified" % (short_version,)) - else: - self.versions = list(self.all_versions) - - self.set_undefined_options('bdist', - ('dist_dir', 'dist_dir'), - ('plat_name', 'plat_name'), - ) - - if self.pre_install_script: - raise DistutilsOptionError( - "the pre-install-script feature is not yet implemented") - - if self.install_script: - for script in self.distribution.scripts: - if self.install_script == os.path.basename(script): - break - else: - raise DistutilsOptionError( - "install_script '%s' not found in scripts" - % self.install_script) - self.install_script_key = None - - def run(self): - if not self.skip_build: - self.run_command('build') - - install = self.reinitialize_command('install', reinit_subcommands=1) - install.prefix = self.bdist_dir - install.skip_build = self.skip_build - install.warn_dir = 0 - - install_lib = self.reinitialize_command('install_lib') - # we do not want to include pyc or pyo files - install_lib.compile = 0 - install_lib.optimize = 0 - - if self.distribution.has_ext_modules(): - # If we are building an installer for a Python version other - # than the one we are currently running, then we need to ensure - # our build_lib reflects the other Python version rather than ours. - # Note that for target_version!=sys.version, we must have skipped the - # build step, so there is no issue with enforcing the build of this - # version. - target_version = self.target_version - if not target_version: - 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) - build = self.get_finalized_command('build') - build.build_lib = os.path.join(build.build_base, - 'lib' + plat_specifier) - - log.info("installing to %s", self.bdist_dir) - install.ensure_finalized() - - # avoid warning of 'install_lib' about installing - # into a directory not in sys.path - sys.path.insert(0, os.path.join(self.bdist_dir, 'PURELIB')) - - install.run() - - del sys.path[0] - - self.mkpath(self.dist_dir) - fullname = self.distribution.get_fullname() - installer_name = self.get_installer_filename(fullname) - installer_name = os.path.abspath(installer_name) - if os.path.exists(installer_name): os.unlink(installer_name) - - metadata = self.distribution.metadata - author = metadata.author - if not author: - author = metadata.maintainer - if not author: - author = "UNKNOWN" - version = metadata.get_version() - # ProductVersion must be strictly numeric - # XXX need to deal with prerelease versions - sversion = "%d.%d.%d" % StrictVersion(version).version - # Prefix ProductName with Python x.y, so that - # it sorts together with the other Python packages - # in Add-Remove-Programs (APR) - fullname = self.distribution.get_fullname() - if self.target_version: - product_name = "Python %s %s" % (self.target_version, fullname) - else: - product_name = "Python %s" % (fullname) - self.db = msilib.init_database(installer_name, schema, - product_name, msilib.gen_uuid(), - sversion, author) - msilib.add_tables(self.db, sequence) - props = [('DistVersion', version)] - email = metadata.author_email or metadata.maintainer_email - if email: - props.append(("ARPCONTACT", email)) - if metadata.url: - props.append(("ARPURLINFOABOUT", metadata.url)) - if props: - add_data(self.db, 'Property', props) - - self.add_find_python() - self.add_files() - self.add_scripts() - self.add_ui() - self.db.Commit() - - if hasattr(self.distribution, 'dist_files'): - tup = 'bdist_msi', self.target_version or 'any', fullname - self.distribution.dist_files.append(tup) - - if not self.keep_temp: - remove_tree(self.bdist_dir, dry_run=self.dry_run) - - def add_files(self): - db = self.db - cab = msilib.CAB("distfiles") - rootdir = os.path.abspath(self.bdist_dir) - - root = Directory(db, cab, None, rootdir, "TARGETDIR", "SourceDir") - f = Feature(db, "Python", "Python", "Everything", - 0, 1, directory="TARGETDIR") - - items = [(f, root, '')] - for version in self.versions + [self.other_version]: - target = "TARGETDIR" + version - name = default = "Python" + version - desc = "Everything" - if version is self.other_version: - title = "Python from another location" - level = 2 - else: - title = "Python %s from registry" % version - level = 1 - f = Feature(db, name, title, desc, 1, level, directory=target) - dir = Directory(db, cab, root, rootdir, target, default) - items.append((f, dir, version)) - db.Commit() - - seen = {} - for feature, dir, version in items: - todo = [dir] - while todo: - dir = todo.pop() - for file in os.listdir(dir.absolute): - afile = os.path.join(dir.absolute, file) - if os.path.isdir(afile): - short = "%s|%s" % (dir.make_short(file), file) - default = file + version - newdir = Directory(db, cab, dir, file, default, short) - todo.append(newdir) - else: - if not dir.component: - dir.start_component(dir.logical, feature, 0) - if afile not in seen: - key = seen[afile] = dir.add_file(file) - if file==self.install_script: - if self.install_script_key: - raise DistutilsOptionError( - "Multiple files with name %s" % file) - self.install_script_key = '[#%s]' % key - else: - key = seen[afile] - add_data(self.db, "DuplicateFile", - [(key + version, dir.component, key, None, dir.logical)]) - db.Commit() - cab.commit(db) - - def add_find_python(self): - """Adds code to the installer to compute the location of Python. - - Properties PYTHON.MACHINE.X.Y and PYTHON.USER.X.Y will be set from the - registry for each version of Python. - - Properties TARGETDIRX.Y will be set from PYTHON.USER.X.Y if defined, - else from PYTHON.MACHINE.X.Y. - - Properties PYTHONX.Y will be set to TARGETDIRX.Y\\python.exe""" - - start = 402 - for ver in self.versions: - install_path = r"SOFTWARE\Python\PythonCore\%s\InstallPath" % ver - machine_reg = "python.machine." + ver - user_reg = "python.user." + ver - machine_prop = "PYTHON.MACHINE." + ver - user_prop = "PYTHON.USER." + ver - machine_action = "PythonFromMachine" + ver - user_action = "PythonFromUser" + ver - exe_action = "PythonExe" + ver - target_dir_prop = "TARGETDIR" + ver - exe_prop = "PYTHON" + ver - if msilib.Win64: - # type: msidbLocatorTypeRawValue + msidbLocatorType64bit - Type = 2+16 - else: - Type = 2 - add_data(self.db, "RegLocator", - [(machine_reg, 2, install_path, None, Type), - (user_reg, 1, install_path, None, Type)]) - add_data(self.db, "AppSearch", - [(machine_prop, machine_reg), - (user_prop, user_reg)]) - add_data(self.db, "CustomAction", - [(machine_action, 51+256, target_dir_prop, "[" + machine_prop + "]"), - (user_action, 51+256, target_dir_prop, "[" + user_prop + "]"), - (exe_action, 51+256, exe_prop, "[" + target_dir_prop + "]\\python.exe"), - ]) - add_data(self.db, "InstallExecuteSequence", - [(machine_action, machine_prop, start), - (user_action, user_prop, start + 1), - (exe_action, None, start + 2), - ]) - add_data(self.db, "InstallUISequence", - [(machine_action, machine_prop, start), - (user_action, user_prop, start + 1), - (exe_action, None, start + 2), - ]) - add_data(self.db, "Condition", - [("Python" + ver, 0, "NOT TARGETDIR" + ver)]) - start += 4 - assert start < 500 - - def add_scripts(self): - if self.install_script: - start = 6800 - for ver in self.versions + [self.other_version]: - install_action = "install_script." + ver - exe_prop = "PYTHON" + ver - add_data(self.db, "CustomAction", - [(install_action, 50, exe_prop, self.install_script_key)]) - add_data(self.db, "InstallExecuteSequence", - [(install_action, "&Python%s=3" % ver, start)]) - start += 1 - # XXX pre-install scripts are currently refused in finalize_options() - # but if this feature is completed, it will also need to add - # entries for each version as the above code does - if self.pre_install_script: - scriptfn = os.path.join(self.bdist_dir, "preinstall.bat") - with open(scriptfn, "w") as f: - # The batch file will be executed with [PYTHON], so that %1 - # is the path to the Python interpreter; %0 will be the path - # of the batch file. - # rem =""" - # %1 %0 - # exit - # """ - # <actual script> - f.write('rem ="""\n%1 %0\nexit\n"""\n') - with open(self.pre_install_script) as fin: - f.write(fin.read()) - add_data(self.db, "Binary", - [("PreInstall", msilib.Binary(scriptfn)) - ]) - add_data(self.db, "CustomAction", - [("PreInstall", 2, "PreInstall", None) - ]) - add_data(self.db, "InstallExecuteSequence", - [("PreInstall", "NOT Installed", 450)]) - - - def add_ui(self): - db = self.db - x = y = 50 - w = 370 - h = 300 - title = "[ProductName] Setup" - - # see "Dialog Style Bits" - modal = 3 # visible | modal - modeless = 1 # visible - track_disk_space = 32 - - # UI customization properties - add_data(db, "Property", - # See "DefaultUIFont Property" - [("DefaultUIFont", "DlgFont8"), - # See "ErrorDialog Style Bit" - ("ErrorDialog", "ErrorDlg"), - ("Progress1", "Install"), # modified in maintenance type dlg - ("Progress2", "installs"), - ("MaintenanceForm_Action", "Repair"), - # possible values: ALL, JUSTME - ("WhichUsers", "ALL") - ]) - - # Fonts, see "TextStyle Table" - add_data(db, "TextStyle", - [("DlgFont8", "Tahoma", 9, None, 0), - ("DlgFontBold8", "Tahoma", 8, None, 1), #bold - ("VerdanaBold10", "Verdana", 10, None, 1), - ("VerdanaRed9", "Verdana", 9, 255, 0), - ]) - - # UI Sequences, see "InstallUISequence Table", "Using a Sequence Table" - # Numbers indicate sequence; see sequence.py for how these action integrate - add_data(db, "InstallUISequence", - [("PrepareDlg", "Not Privileged or Windows9x or Installed", 140), - ("WhichUsersDlg", "Privileged and not Windows9x and not Installed", 141), - # In the user interface, assume all-users installation if privileged. - ("SelectFeaturesDlg", "Not Installed", 1230), - # XXX no support for resume installations yet - #("ResumeDlg", "Installed AND (RESUME OR Preselected)", 1240), - ("MaintenanceTypeDlg", "Installed AND NOT RESUME AND NOT Preselected", 1250), - ("ProgressDlg", None, 1280)]) - - add_data(db, 'ActionText', text.ActionText) - add_data(db, 'UIText', text.UIText) - ##################################################################### - # Standard dialogs: FatalError, UserExit, ExitDialog - fatal=PyDialog(db, "FatalError", x, y, w, h, modal, title, - "Finish", "Finish", "Finish") - fatal.title("[ProductName] Installer ended prematurely") - fatal.back("< Back", "Finish", active = 0) - fatal.cancel("Cancel", "Back", active = 0) - fatal.text("Description1", 15, 70, 320, 80, 0x30003, - "[ProductName] setup ended prematurely because of an error. Your system has not been modified. To install this program at a later time, please run the installation again.") - fatal.text("Description2", 15, 155, 320, 20, 0x30003, - "Click the Finish button to exit the Installer.") - c=fatal.next("Finish", "Cancel", name="Finish") - c.event("EndDialog", "Exit") - - user_exit=PyDialog(db, "UserExit", x, y, w, h, modal, title, - "Finish", "Finish", "Finish") - user_exit.title("[ProductName] Installer was interrupted") - user_exit.back("< Back", "Finish", active = 0) - user_exit.cancel("Cancel", "Back", active = 0) - user_exit.text("Description1", 15, 70, 320, 80, 0x30003, - "[ProductName] setup was interrupted. Your system has not been modified. " - "To install this program at a later time, please run the installation again.") - user_exit.text("Description2", 15, 155, 320, 20, 0x30003, - "Click the Finish button to exit the Installer.") - c = user_exit.next("Finish", "Cancel", name="Finish") - c.event("EndDialog", "Exit") - - exit_dialog = PyDialog(db, "ExitDialog", x, y, w, h, modal, title, - "Finish", "Finish", "Finish") - exit_dialog.title("Completing the [ProductName] Installer") - exit_dialog.back("< Back", "Finish", active = 0) - exit_dialog.cancel("Cancel", "Back", active = 0) - exit_dialog.text("Description", 15, 235, 320, 20, 0x30003, - "Click the Finish button to exit the Installer.") - c = exit_dialog.next("Finish", "Cancel", name="Finish") - c.event("EndDialog", "Return") - - ##################################################################### - # Required dialog: FilesInUse, ErrorDlg - inuse = PyDialog(db, "FilesInUse", - x, y, w, h, - 19, # KeepModeless|Modal|Visible - title, - "Retry", "Retry", "Retry", bitmap=False) - inuse.text("Title", 15, 6, 200, 15, 0x30003, - r"{\DlgFontBold8}Files in Use") - inuse.text("Description", 20, 23, 280, 20, 0x30003, - "Some files that need to be updated are currently in use.") - inuse.text("Text", 20, 55, 330, 50, 3, - "The following applications are using files that need to be updated by this setup. Close these applications and then click Retry to continue the installation or Cancel to exit it.") - inuse.control("List", "ListBox", 20, 107, 330, 130, 7, "FileInUseProcess", - None, None, None) - c=inuse.back("Exit", "Ignore", name="Exit") - c.event("EndDialog", "Exit") - c=inuse.next("Ignore", "Retry", name="Ignore") - c.event("EndDialog", "Ignore") - c=inuse.cancel("Retry", "Exit", name="Retry") - c.event("EndDialog","Retry") - - # See "Error Dialog". See "ICE20" for the required names of the controls. - error = Dialog(db, "ErrorDlg", - 50, 10, 330, 101, - 65543, # Error|Minimize|Modal|Visible - title, - "ErrorText", None, None) - error.text("ErrorText", 50,9,280,48,3, "") - #error.control("ErrorIcon", "Icon", 15, 9, 24, 24, 5242881, None, "py.ico", None, None) - error.pushbutton("N",120,72,81,21,3,"No",None).event("EndDialog","ErrorNo") - error.pushbutton("Y",240,72,81,21,3,"Yes",None).event("EndDialog","ErrorYes") - error.pushbutton("A",0,72,81,21,3,"Abort",None).event("EndDialog","ErrorAbort") - error.pushbutton("C",42,72,81,21,3,"Cancel",None).event("EndDialog","ErrorCancel") - error.pushbutton("I",81,72,81,21,3,"Ignore",None).event("EndDialog","ErrorIgnore") - error.pushbutton("O",159,72,81,21,3,"Ok",None).event("EndDialog","ErrorOk") - error.pushbutton("R",198,72,81,21,3,"Retry",None).event("EndDialog","ErrorRetry") - - ##################################################################### - # Global "Query Cancel" dialog - cancel = Dialog(db, "CancelDlg", 50, 10, 260, 85, 3, title, - "No", "No", "No") - cancel.text("Text", 48, 15, 194, 30, 3, - "Are you sure you want to cancel [ProductName] installation?") - #cancel.control("Icon", "Icon", 15, 15, 24, 24, 5242881, None, - # "py.ico", None, None) - c=cancel.pushbutton("Yes", 72, 57, 56, 17, 3, "Yes", "No") - c.event("EndDialog", "Exit") - - c=cancel.pushbutton("No", 132, 57, 56, 17, 3, "No", "Yes") - c.event("EndDialog", "Return") - - ##################################################################### - # Global "Wait for costing" dialog - costing = Dialog(db, "WaitForCostingDlg", 50, 10, 260, 85, modal, title, - "Return", "Return", "Return") - costing.text("Text", 48, 15, 194, 30, 3, - "Please wait while the installer finishes determining your disk space requirements.") - c = costing.pushbutton("Return", 102, 57, 56, 17, 3, "Return", None) - c.event("EndDialog", "Exit") - - ##################################################################### - # Preparation dialog: no user input except cancellation - prep = PyDialog(db, "PrepareDlg", x, y, w, h, modeless, title, - "Cancel", "Cancel", "Cancel") - prep.text("Description", 15, 70, 320, 40, 0x30003, - "Please wait while the Installer prepares to guide you through the installation.") - prep.title("Welcome to the [ProductName] Installer") - c=prep.text("ActionText", 15, 110, 320, 20, 0x30003, "Pondering...") - c.mapping("ActionText", "Text") - c=prep.text("ActionData", 15, 135, 320, 30, 0x30003, None) - c.mapping("ActionData", "Text") - prep.back("Back", None, active=0) - prep.next("Next", None, active=0) - c=prep.cancel("Cancel", None) - c.event("SpawnDialog", "CancelDlg") - - ##################################################################### - # Feature (Python directory) selection - seldlg = PyDialog(db, "SelectFeaturesDlg", x, y, w, h, modal, title, - "Next", "Next", "Cancel") - seldlg.title("Select Python Installations") - - seldlg.text("Hint", 15, 30, 300, 20, 3, - "Select the Python locations where %s should be installed." - % self.distribution.get_fullname()) - - seldlg.back("< Back", None, active=0) - c = seldlg.next("Next >", "Cancel") - order = 1 - c.event("[TARGETDIR]", "[SourceDir]", ordering=order) - for version in self.versions + [self.other_version]: - order += 1 - c.event("[TARGETDIR]", "[TARGETDIR%s]" % version, - "FEATURE_SELECTED AND &Python%s=3" % version, - ordering=order) - c.event("SpawnWaitDialog", "WaitForCostingDlg", ordering=order + 1) - c.event("EndDialog", "Return", ordering=order + 2) - c = seldlg.cancel("Cancel", "Features") - c.event("SpawnDialog", "CancelDlg") - - c = seldlg.control("Features", "SelectionTree", 15, 60, 300, 120, 3, - "FEATURE", None, "PathEdit", None) - c.event("[FEATURE_SELECTED]", "1") - ver = self.other_version - install_other_cond = "FEATURE_SELECTED AND &Python%s=3" % ver - dont_install_other_cond = "FEATURE_SELECTED AND &Python%s<>3" % ver - - c = seldlg.text("Other", 15, 200, 300, 15, 3, - "Provide an alternate Python location") - c.condition("Enable", install_other_cond) - c.condition("Show", install_other_cond) - c.condition("Disable", dont_install_other_cond) - c.condition("Hide", dont_install_other_cond) - - c = seldlg.control("PathEdit", "PathEdit", 15, 215, 300, 16, 1, - "TARGETDIR" + ver, None, "Next", None) - c.condition("Enable", install_other_cond) - c.condition("Show", install_other_cond) - c.condition("Disable", dont_install_other_cond) - c.condition("Hide", dont_install_other_cond) - - ##################################################################### - # Disk cost - cost = PyDialog(db, "DiskCostDlg", x, y, w, h, modal, title, - "OK", "OK", "OK", bitmap=False) - cost.text("Title", 15, 6, 200, 15, 0x30003, - r"{\DlgFontBold8}Disk Space Requirements") - cost.text("Description", 20, 20, 280, 20, 0x30003, - "The disk space required for the installation of the selected features.") - cost.text("Text", 20, 53, 330, 60, 3, - "The highlighted volumes (if any) do not have enough disk space " - "available for the currently selected features. You can either " - "remove some files from the highlighted volumes, or choose to " - "install less features onto local drive(s), or select different " - "destination drive(s).") - cost.control("VolumeList", "VolumeCostList", 20, 100, 330, 150, 393223, - None, "{120}{70}{70}{70}{70}", None, None) - cost.xbutton("OK", "Ok", None, 0.5).event("EndDialog", "Return") - - ##################################################################### - # WhichUsers Dialog. Only available on NT, and for privileged users. - # This must be run before FindRelatedProducts, because that will - # take into account whether the previous installation was per-user - # or per-machine. We currently don't support going back to this - # dialog after "Next" was selected; to support this, we would need to - # find how to reset the ALLUSERS property, and how to re-run - # FindRelatedProducts. - # On Windows9x, the ALLUSERS property is ignored on the command line - # and in the Property table, but installer fails according to the documentation - # if a dialog attempts to set ALLUSERS. - whichusers = PyDialog(db, "WhichUsersDlg", x, y, w, h, modal, title, - "AdminInstall", "Next", "Cancel") - whichusers.title("Select whether to install [ProductName] for all users of this computer.") - # A radio group with two options: allusers, justme - g = whichusers.radiogroup("AdminInstall", 15, 60, 260, 50, 3, - "WhichUsers", "", "Next") - g.add("ALL", 0, 5, 150, 20, "Install for all users") - g.add("JUSTME", 0, 25, 150, 20, "Install just for me") - - whichusers.back("Back", None, active=0) - - c = whichusers.next("Next >", "Cancel") - c.event("[ALLUSERS]", "1", 'WhichUsers="ALL"', 1) - c.event("EndDialog", "Return", ordering = 2) - - c = whichusers.cancel("Cancel", "AdminInstall") - c.event("SpawnDialog", "CancelDlg") - - ##################################################################### - # Installation Progress dialog (modeless) - progress = PyDialog(db, "ProgressDlg", x, y, w, h, modeless, title, - "Cancel", "Cancel", "Cancel", bitmap=False) - progress.text("Title", 20, 15, 200, 15, 0x30003, - r"{\DlgFontBold8}[Progress1] [ProductName]") - progress.text("Text", 35, 65, 300, 30, 3, - "Please wait while the Installer [Progress2] [ProductName]. " - "This may take several minutes.") - progress.text("StatusLabel", 35, 100, 35, 20, 3, "Status:") - - c=progress.text("ActionText", 70, 100, w-70, 20, 3, "Pondering...") - c.mapping("ActionText", "Text") - - #c=progress.text("ActionData", 35, 140, 300, 20, 3, None) - #c.mapping("ActionData", "Text") - - c=progress.control("ProgressBar", "ProgressBar", 35, 120, 300, 10, 65537, - None, "Progress done", None, None) - c.mapping("SetProgress", "Progress") - - progress.back("< Back", "Next", active=False) - progress.next("Next >", "Cancel", active=False) - progress.cancel("Cancel", "Back").event("SpawnDialog", "CancelDlg") - - ################################################################### - # Maintenance type: repair/uninstall - maint = PyDialog(db, "MaintenanceTypeDlg", x, y, w, h, modal, title, - "Next", "Next", "Cancel") - maint.title("Welcome to the [ProductName] Setup Wizard") - maint.text("BodyText", 15, 63, 330, 42, 3, - "Select whether you want to repair or remove [ProductName].") - g=maint.radiogroup("RepairRadioGroup", 15, 108, 330, 60, 3, - "MaintenanceForm_Action", "", "Next") - #g.add("Change", 0, 0, 200, 17, "&Change [ProductName]") - g.add("Repair", 0, 18, 200, 17, "&Repair [ProductName]") - g.add("Remove", 0, 36, 200, 17, "Re&move [ProductName]") - - maint.back("< Back", None, active=False) - c=maint.next("Finish", "Cancel") - # Change installation: Change progress dialog to "Change", then ask - # for feature selection - #c.event("[Progress1]", "Change", 'MaintenanceForm_Action="Change"', 1) - #c.event("[Progress2]", "changes", 'MaintenanceForm_Action="Change"', 2) - - # Reinstall: Change progress dialog to "Repair", then invoke reinstall - # Also set list of reinstalled features to "ALL" - c.event("[REINSTALL]", "ALL", 'MaintenanceForm_Action="Repair"', 5) - c.event("[Progress1]", "Repairing", 'MaintenanceForm_Action="Repair"', 6) - c.event("[Progress2]", "repairs", 'MaintenanceForm_Action="Repair"', 7) - c.event("Reinstall", "ALL", 'MaintenanceForm_Action="Repair"', 8) - - # Uninstall: Change progress to "Remove", then invoke uninstall - # Also set list of removed features to "ALL" - c.event("[REMOVE]", "ALL", 'MaintenanceForm_Action="Remove"', 11) - c.event("[Progress1]", "Removing", 'MaintenanceForm_Action="Remove"', 12) - c.event("[Progress2]", "removes", 'MaintenanceForm_Action="Remove"', 13) - c.event("Remove", "ALL", 'MaintenanceForm_Action="Remove"', 14) - - # Close dialog when maintenance action scheduled - c.event("EndDialog", "Return", 'MaintenanceForm_Action<>"Change"', 20) - #c.event("NewDialog", "SelectFeaturesDlg", 'MaintenanceForm_Action="Change"', 21) - - maint.cancel("Cancel", "RepairRadioGroup").event("SpawnDialog", "CancelDlg") - - def get_installer_filename(self, fullname): - # Factored out to allow overriding in subclasses - if self.target_version: - base_name = "%s.%s-py%s.msi" % (fullname, self.plat_name, - self.target_version) - else: - base_name = "%s.%s.msi" % (fullname, self.plat_name) - installer_name = os.path.join(self.dist_dir, base_name) - return installer_name diff --git a/setuptools/_distutils/command/bdist_rpm.py b/setuptools/_distutils/command/bdist_rpm.py index 550cbfa1..340527b0 100644 --- a/setuptools/_distutils/command/bdist_rpm.py +++ b/setuptools/_distutils/command/bdist_rpm.py @@ -3,134 +3,153 @@ Implements the Distutils 'bdist_rpm' command (create RPM source and binary distributions).""" -import subprocess, sys, os -from distutils.core import Command -from distutils.debug import DEBUG -from distutils.file_util import write_file -from distutils.errors import * -from distutils.sysconfig import get_python_version -from distutils import log +import subprocess +import sys +import os + +from ..core import Command +from ..debug import DEBUG +from ..file_util import write_file +from ..errors import ( + DistutilsOptionError, + DistutilsPlatformError, + DistutilsFileError, + DistutilsExecError, +) +from ..sysconfig import get_python_version +from distutils._log import log + class bdist_rpm(Command): 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"), - - ('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"), - - ('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"), - + ('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-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"), + ( + '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"), - - ('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'} - + ('force-arch=', None, "Force an architecture onto the RPM build process"), + ('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', + } def initialize_options(self): self.bdist_base = None @@ -181,8 +200,7 @@ class bdist_rpm(Command): self.set_undefined_options('bdist', ('bdist_base', 'bdist_base')) if self.rpm_base is None: if not self.rpm3_mode: - raise DistutilsOptionError( - "you must specify --rpm-base in RPM 2 mode") + raise DistutilsOptionError("you must specify --rpm-base in RPM 2 mode") self.rpm_base = os.path.join(self.bdist_base, "rpm") if self.python is None: @@ -192,14 +210,17 @@ class bdist_rpm(Command): 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(): @@ -210,9 +231,11 @@ class bdist_rpm(Command): 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( + 'vendor', + "%s <%s>" + % (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): @@ -221,12 +244,12 @@ class bdist_rpm(Command): self.doc_files.append(readme) self.ensure_string('release', "1") - self.ensure_string('serial') # should it be an int? + 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') @@ -253,7 +276,7 @@ class bdist_rpm(Command): self.ensure_string('force_arch') - def run(self): + def run(self): # noqa: C901 if DEBUG: print("before _get_package_data():") print("vendor =", self.vendor) @@ -274,14 +297,12 @@ 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) - - if self.spec_only: # stop if requested + 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 return # Make a source distribution and copy to SOURCES directory with @@ -303,14 +324,13 @@ 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("icon file '%s' does not exist" % self.icon) # build package 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 +338,7 @@ 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') @@ -334,8 +353,11 @@ 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 '%s %s\n' --specfile '%s'" % ( - src_rpm, non_src_rpm, spec_path) + q_cmd = r"rpm -q --qf '{} {}\n' --specfile '{}'".format( + src_rpm, + non_src_rpm, + spec_path, + ) out = os.popen(q_cmd) try: @@ -345,12 +367,12 @@ class bdist_rpm(Command): line = out.readline() if not line: break - l = line.strip().split() - assert(len(l) == 2) - binary_rpms.append(l[1]) + ell = line.strip().split() + assert len(ell) == 2 + binary_rpms.append(ell[1]) # The source rpm is named after the first entry in the spec file if source_rpm is None: - source_rpm = l[0] + source_rpm = ell[0] status = out.close() if status: @@ -369,38 +391,37 @@ 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)) + self.distribution.dist_files.append(('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)) - def _make_spec_file(self): + def _make_spec_file(self): # noqa: C901 """Generate the text of an RPM spec file and return it as a list of strings (one per line). """ # 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(), - ] + 'Summary: ' + (self.distribution.get_description() or "UNKNOWN"), + ] # Workaround for #14443 which affects some RPM based systems such as # RHEL6 (and probably derivatives) @@ -408,8 +429,9 @@ 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( + [' %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) @@ -420,14 +442,17 @@ class bdist_rpm(Command): # put locale summaries into spec file # XXX not supported for now (hard to put a dictionary # in a config file -- arg!) - #for locale in self.summaries.keys(): + # for locale in self.summaries.keys(): # 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 @@ -437,42 +462,44 @@ class bdist_rpm(Command): else: spec_file.append('Source0: %{name}-%{unmangled_version}.tar.gz') - spec_file.extend([ - 'License: ' + self.distribution.get_license(), - '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 ) - - for field in ('Vendor', - 'Packager', - 'Provides', - 'Requires', - 'Conflicts', - 'Obsoletes', - ): + spec_file.append('BuildArch: %s' % self.force_arch) + + 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': + if self.distribution.get_url(): spec_file.append('Url: ' + self.distribution.get_url()) if self.distribution_name: 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)) @@ -480,16 +507,18 @@ class bdist_rpm(Command): if self.no_autoreq: spec_file.append('AutoReq: 0') - spec_file.extend([ - '', - '%description', - self.distribution.get_long_description() - ]) + 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 # easily support this ;-( - #for locale in self.descriptions.keys(): + # for locale in self.descriptions.keys(): # spec_file.extend([ # '', # '%description -l ' + locale, @@ -498,7 +527,7 @@ 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_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,8 +538,9 @@ 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}"), @@ -529,37 +559,43 @@ 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')) else: 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 def _format_changelog(self, changelog): - """Format the changelog correctly and convert it to a list of strings - """ + """Format the changelog correctly and convert it to a list of strings""" if not changelog: return changelog new_changelog = [] diff --git a/setuptools/_distutils/command/bdist_wininst.py b/setuptools/_distutils/command/bdist_wininst.py deleted file mode 100644 index 0e9ddaa2..00000000 --- a/setuptools/_distutils/command/bdist_wininst.py +++ /dev/null @@ -1,377 +0,0 @@ -"""distutils.command.bdist_wininst - -Implements the Distutils 'bdist_wininst' command: create a windows installer -exe-program.""" - -import os -import sys -import warnings -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 - -class bdist_wininst(Command): - - 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"), - ] - - 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") - - 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) - - def initialize_options(self): - self.bdist_dir = None - self.plat_name = None - self.keep_temp = 0 - self.no_target_compile = 0 - self.no_target_optimize = 0 - self.target_version = None - self.dist_dir = None - self.bitmap = None - self.title = None - self.skip_build = None - self.install_script = None - self.pre_install_script = None - self.user_access_control = None - - - def finalize_options(self): - self.set_undefined_options('bdist', ('skip_build', 'skip_build')) - - if self.bdist_dir is None: - if self.skip_build and self.plat_name: - # If build is skipped and plat_name is overridden, bdist will - # not see the correct 'plat_name' - so set that up manually. - bdist = self.distribution.get_command_obj('bdist') - bdist.plat_name = self.plat_name - # next the command will be initialized using that name - bdist_base = self.get_finalized_command('bdist').bdist_base - self.bdist_dir = os.path.join(bdist_base, 'wininst') - - if not 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,)) - self.target_version = short_version - - self.set_undefined_options('bdist', - ('dist_dir', 'dist_dir'), - ('plat_name', 'plat_name'), - ) - - if self.install_script: - for script in self.distribution.scripts: - if self.install_script == os.path.basename(script): - break - else: - raise DistutilsOptionError( - "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 not self.skip_build: - self.run_command('build') - - install = self.reinitialize_command('install', reinit_subcommands=1) - install.root = self.bdist_dir - install.skip_build = self.skip_build - install.warn_dir = 0 - install.plat_name = self.plat_name - - install_lib = self.reinitialize_command('install_lib') - # we do not want to include pyc or pyo files - install_lib.compile = 0 - install_lib.optimize = 0 - - if self.distribution.has_ext_modules(): - # If we are building an installer for a Python version other - # than the one we are currently running, then we need to ensure - # our build_lib reflects the other Python version rather than ours. - # Note that for target_version!=sys.version, we must have skipped the - # build step, so there is no issue with enforcing the build of this - # version. - target_version = self.target_version - if not target_version: - 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) - build = self.get_finalized_command('build') - 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. - for key in ('purelib', 'platlib', 'headers', 'scripts', 'data'): - value = key.upper() - if key == 'headers': - value = value + '/Include/$dist_name' - setattr(install, - 'install_' + key, - value) - - log.info("installing to %s", self.bdist_dir) - install.ensure_finalized() - - # avoid warning of 'install_lib' about installing - # into a directory not in sys.path - sys.path.insert(0, os.path.join(self.bdist_dir, 'PURELIB')) - - install.run() - - del sys.path[0] - - # And make an archive relative to the root of the - # pseudo-installation tree. - from tempfile import mktemp - archive_basename = mktemp() - fullname = self.distribution.get_fullname() - 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))) - # remove the zip-file again - log.debug("removing temporary file '%s'", arcname) - os.remove(arcname) - - if not self.keep_temp: - remove_tree(self.bdist_dir, dry_run=self.dry_run) - - def get_inidata(self): - # Return data describing the installation. - lines = [] - metadata = self.distribution.metadata - - # Write the [metadata] section. - lines.append("[metadata]") - - # 'info' will be displayed in the installer's dialog box, - # describing the items to be installed. - info = (metadata.long_description or '') + '\n' - - # Escape newline characters - def escape(s): - return s.replace("\n", "\\n") - - 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))) - - # The [setup] section contains entries controlling - # the installer runtime. - 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)) - if 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) - - title = self.title or self.distribution.get_fullname() - lines.append("title=%s" % escape(title)) - import time - import distutils - build_info = "Built %s with distutils-%s" % \ - (time.ctime(time.time()), distutils.__version__) - lines.append("build_info=%s" % build_info) - return "\n".join(lines) - - def create_exe(self, arcname, fullname, bitmap=None): - import struct - - self.mkpath(self.dist_dir) - - cfgdata = self.get_inidata() - - installer_name = self.get_installer_filename(fullname) - self.announce("creating %s" % installer_name) - - if bitmap: - with open(bitmap, "rb") as f: - bitmapdata = f.read() - bitmaplen = len(bitmapdata) - else: - bitmaplen = 0 - - 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") - - # Append the pre-install script - 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" - else: - # empty pre-install script - cfgdata = cfgdata + b"\0" - file.write(cfgdata) - - # The 'magic number' 0x1234567B is used to make sure that the - # binary layout of 'cfgdata' is what the wininst.exe binary - # 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 - ) - file.write(header) - with open(arcname, "rb") as f: - file.write(f.read()) - - def get_installer_filename(self, fullname): - # Factored out to allow overriding in subclasses - 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)) - else: - installer_name = os.path.join(self.dist_dir, - "%s.%s.exe" % (fullname, self.plat_name)) - return installer_name - - def get_exe_bytes(self): - # If a target-version other than the current version has been - # specified, then using the MSVC version from *this* build is no good. - # Without actually finding and executing the target version and parsing - # its sys.version, we just hard-code our knowledge of old versions. - # NOTE: Possible alternative is to allow "--target-version" to - # specify a Python executable rather than a simple version string. - # We can then execute this program to obtain any info we need, such - # as the real sys.version string for the build. - cur_version = get_python_version() - - # If the target version is *later* than us, then we assume they - # 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": - bv = '6.0' - elif self.target_version == "2.4": - bv = '7.1' - elif self.target_version == "2.5": - bv = '8.0' - elif self.target_version <= "3.2": - bv = '9.0' - elif self.target_version <= "3.4": - bv = '10.0' - else: - bv = '14.0' - else: - # for current version - use authoritative check. - try: - from msvcrt import CRT_ASSEMBLY_VERSION - except ImportError: - # cross-building, so assume the latest version - bv = '14.0' - else: - # as far as we know, CRT is binary compatible based on - # the first field, so assume 'x.0' until proven otherwise - 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 - # used for python. XXX What about mingw, borland, and so on? - - # if plat_name starts with "win" but is not "win32" - # we want to strip "win" and leave the rest (e.g. -amd64) - # for all other cases, we don't want any suffix - if self.plat_name != 'win32' and self.plat_name[:3] == 'win': - sfix = self.plat_name[3:] - else: - sfix = '' - - filename = os.path.join(directory, "wininst-%s%s.exe" % (bv, sfix)) - f = open(filename, "rb") - try: - return f.read() - finally: - f.close() diff --git a/setuptools/_distutils/command/build.py b/setuptools/_distutils/command/build.py index 4355a632..c3ab410f 100644 --- a/setuptools/_distutils/command/build.py +++ b/setuptools/_distutils/command/build.py @@ -2,14 +2,16 @@ Implements the Distutils 'build' command.""" -import sys, os -from distutils.core import Command -from distutils.errors import DistutilsOptionError -from distutils.util import get_platform +import sys +import os +from ..core import Command +from ..errors import DistutilsOptionError +from ..util import get_platform def show_compilers(): - from distutils.ccompiler import show_compilers + from ..ccompiler import show_compilers + show_compilers() @@ -18,40 +20,35 @@ class build(Command): 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' @@ -69,7 +66,7 @@ class build(Command): self.executable = None self.parallel = None - def finalize_options(self): + def finalize_options(self): # noqa: C901 if self.plat_name is None: self.plat_name = get_platform() else: @@ -78,10 +75,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 = ".{}-{}".format(self.plat_name, sys.implementation.cache_tag) # 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 +93,7 @@ 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 +107,11 @@ 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) @@ -134,7 +131,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 +145,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), + ] diff --git a/setuptools/_distutils/command/build_clib.py b/setuptools/_distutils/command/build_clib.py index 3e20ef23..f90c5664 100644 --- a/setuptools/_distutils/command/build_clib.py +++ b/setuptools/_distutils/command/build_clib.py @@ -15,13 +15,15 @@ module.""" # cut 'n paste. Sigh. import os -from distutils.core import Command -from distutils.errors import * -from distutils.sysconfig import customize_compiler -from distutils import log +from ..core import Command +from ..errors import DistutilsSetupError +from ..sysconfig import customize_compiler +from distutils._log import log + def show_compilers(): - from distutils.ccompiler import show_compilers + from ..ccompiler import show_compilers + show_compilers() @@ -30,24 +32,18 @@ class build_clib(Command): 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 +60,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 +87,23 @@ 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) + from ..ccompiler import new_compiler + + 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 +111,6 @@ class build_clib(Command): self.build_libraries(self.libraries) - def check_library_list(self, libraries): """Ensure that the list of libraries is valid. @@ -126,30 +122,31 @@ class build_clib(Command): just returns otherwise. """ if not isinstance(libraries, list): - raise DistutilsSetupError( - "'libraries' option must be a list of tuples") + raise DistutilsSetupError("'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") + raise DistutilsSetupError("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 +159,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 +166,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 +192,17 @@ 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 + ) diff --git a/setuptools/_distutils/command/build_ext.py b/setuptools/_distutils/command/build_ext.py index 181671bf..f4c0eccd 100644 --- a/setuptools/_distutils/command/build_ext.py +++ b/setuptools/_distutils/command/build_ext.py @@ -8,26 +8,33 @@ 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 ..core import Command +from ..errors import ( + DistutilsOptionError, + DistutilsSetupError, + CCompilerError, + DistutilsError, + CompileError, + DistutilsPlatformError, +) +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 # 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 (): - from distutils.ccompiler import show_compilers +def show_compilers(): + from ..ccompiler import show_compilers + show_compilers() @@ -55,54 +62,50 @@ 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 @@ -128,18 +131,19 @@ class build_ext(Command): self.user = None self.parallel = None - def finalize_options(self): + def finalize_options(self): # noqa: C901 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 @@ -164,8 +168,7 @@ class build_ext(Command): # any local include dirs take precedence. 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)) + self.include_dirs.extend(plat_py_include.split(os.path.pathsep)) self.ensure_string_list('libraries') self.ensure_string_list('link_objects') @@ -220,9 +223,11 @@ 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('.') @@ -230,7 +235,7 @@ class build_ext(Command): # 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 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')) @@ -274,8 +279,8 @@ class build_ext(Command): except ValueError: raise DistutilsOptionError("parallel should be an integer") - def run(self): - from distutils.ccompiler import new_compiler + def run(self): # noqa: C901 + from ..ccompiler import new_compiler # 'self.extensions', as supplied by setup.py, is a list of # Extension instances. See the documentation for Extension (in @@ -302,10 +307,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 @@ -338,7 +345,7 @@ class build_ext(Command): # Now actually compile and link everything. self.build_extensions() - def check_extensions_list(self, extensions): + def check_extensions_list(self, extensions): # noqa: C901 """Ensure that the list of extensions (presumably provided as a command option 'extensions') is valid, i.e. it is a list of Extension objects. We also support the old-style list of 2-tuples, @@ -350,34 +357,40 @@ 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) + continue # OK! (assume type-checking done + # 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.warning( + "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. @@ -385,9 +398,14 @@ 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) @@ -395,8 +413,9 @@ 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.warning( + "'def_file' element of build info dict " "no longer supported" + ) # Non-trivial stuff: 'macros' split into 'define_macros' # and 'undef_macros'. @@ -407,8 +426,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: @@ -461,8 +481,9 @@ 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() @@ -479,16 +500,16 @@ 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 "{}" failed: {}'.format(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) @@ -525,13 +546,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. @@ -548,7 +571,8 @@ class build_ext(Command): language = ext.language or self.compiler.detect_language(sources) self.compiler.link_shared_object( - objects, ext_path, + objects, + ext_path, libraries=self.get_libraries(ext), library_dirs=ext.library_dirs, runtime_library_dirs=ext.runtime_library_dirs, @@ -556,7 +580,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 @@ -574,17 +599,20 @@ class build_ext(Command): # the temp dir. if self.swig_cpp: - log.warn("--swig-cpp is deprecated - use --swig-opts=-c++") + log.warning("--swig-cpp is deprecated - use --swig-opts=-c++") - if self.swig_cpp or ('-c++' in self.swig_opts) or \ - ('-c++' in extension.swig_opts): + if ( + self.swig_cpp + or ('-c++' in self.swig_opts) + or ('-c++' in extension.swig_opts) + ): target_ext = '.cpp' else: target_ext = '.c' 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] @@ -631,8 +659,9 @@ class build_ext(Command): 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) @@ -650,7 +679,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 @@ -677,7 +706,8 @@ class build_ext(Command): of the file from which it will be loaded (eg. "foo/bar.so", or "foo\bar.pyd"). """ - from distutils.sysconfig import get_config_var + from ..sysconfig import get_config_var + ext_path = ext_name.split('.') ext_suffix = get_config_var('EXT_SUFFIX') return os.path.join(*ext_path) + ext_suffix @@ -703,7 +733,7 @@ class build_ext(Command): ext.export_symbols.append(initfunc_name) return ext.export_symbols - def get_libraries(self, ext): + def get_libraries(self, ext): # noqa: C901 """Return the list of libraries to link against when building a shared extension. On most platforms, this is just 'ext.libraries'; on Windows, we add the Python library (eg. python20.dll). @@ -714,13 +744,16 @@ class build_ext(Command): # 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": - from distutils._msvccompiler import MSVCCompiler + from .._msvccompiler import MSVCCompiler + if not isinstance(self.compiler, MSVCCompiler): 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] @@ -733,7 +766,8 @@ class build_ext(Command): # On Cygwin (and if required, other POSIX-like platforms based on # Windows like MinGW) it is simply necessary that all symbols in # shared libraries are resolved at link time. - from distutils.sysconfig import get_config_var + from ..sysconfig import get_config_var + link_libpython = False if get_config_var('Py_ENABLE_SHARED'): # A native build on an Android device or on Cygwin diff --git a/setuptools/_distutils/command/build_py.py b/setuptools/_distutils/command/build_py.py index 7ef9bcef..9f783244 100644 --- a/setuptools/_distutils/command/build_py.py +++ b/setuptools/_distutils/command/build_py.py @@ -7,12 +7,13 @@ import importlib.util import sys import glob -from distutils.core import Command -from distutils.errors import * -from distutils.util import convert_path -from distutils import log +from ..core import Command +from ..errors import DistutilsOptionError, DistutilsFileError +from ..util import convert_path +from distutils._log import log -class build_py (Command): + +class build_py(Command): description = "\"build\" pure Python modules (copy to build directory)" @@ -20,14 +21,17 @@ class build_py (Command): ('build-lib=', 'd', "directory to \"build\" (copy) to"), ('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]"), + ( + '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,9 @@ 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. @@ -109,42 +113,42 @@ 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) - ] + 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))) + 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): """Copy data files into build directory""" - lastdir = None 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)) - 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 - distribution, where package 'package' should be found - (at least according to the 'package_dir' option, if any).""" + distribution, where package 'package' should be found + (at least according to the 'package_dir' option, if any).""" path = package.split('.') if not self.package_dir: @@ -188,20 +192,19 @@ class build_py (Command): 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" + # Directories without __init__.py are namespace packages (PEP 420). if package: 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) # Either not in a package at all (__init__.py not expected), or # __init__.py doesn't exist -- so don't return the filename. @@ -209,7 +212,7 @@ 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.warning("file %s (for module %s) not found", module_file, module) return False else: return True @@ -313,17 +316,21 @@ 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 +339,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 @@ -376,7 +384,8 @@ class build_py (Command): self.warn('byte-compiling is disabled, skipping.') return - from distutils.util import byte_compile + from ..util import byte_compile + prefix = self.build_lib if prefix[-1] != os.sep: prefix = prefix + os.sep @@ -385,8 +394,14 @@ 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, + ) diff --git a/setuptools/_distutils/command/build_scripts.py b/setuptools/_distutils/command/build_scripts.py index e3312cf0..87174f6b 100644 --- a/setuptools/_distutils/command/build_scripts.py +++ b/setuptools/_distutils/command/build_scripts.py @@ -2,17 +2,24 @@ Implements the Distutils 'build_scripts' command.""" -import os, re +import os +import re from stat import ST_MODE 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 +from ..core import Command +from ..dep_util import newer +from ..util import convert_path +from distutils._log 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].*)?$') +shebang_pattern = re.compile('^#!.*python[0-9.]*([ \t].*)?$') +""" +Pattern matching a Python interpreter indicated in first line of a script. +""" + +# for Setuptools compatibility +first_line_re = shebang_pattern + class build_scripts(Command): @@ -22,23 +29,23 @@ class build_scripts(Command): ('build-dir=', 'd', "directory to \"build\" (copy) to"), ('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 self.force = None self.executable = None - 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,104 +56,118 @@ 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', - ie. starts with "\#!" and contains "python"), then adjust the first - line to refer to the current Python interpreter as we copy. + """ + Copy each script listed in ``self.scripts``. + + If a script is marked as a Python script (first line matches + 'shebang_pattern', i.e. starts with ``#!`` and contains + "python"), then adjust in the copy the first line to refer to + the current Python interpreter. """ self.mkpath(self.build_dir) outfiles = [] updated_files = [] for script in self.scripts: - adjust = False - script = convert_path(script) - outfile = os.path.join(self.build_dir, os.path.basename(script)) - outfiles.append(outfile) - - if not self.force and not newer(script, outfile): - 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") - except OSError: - if not self.dry_run: - raise - f = None - else: - encoding, lines = tokenize.detect_encoding(f.readline) - f.seek(0) - first_line = f.readline() - if not first_line: - self.warn("%s is an empty file (skipping)" % script) - continue - - match = first_line_re.match(first_line) - if match: - adjust = True - post_interp = match.group(1) or b'' - - if adjust: - 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"))) - executable = os.fsencode(executable) - 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 - # written before. So the shebang has to be decodable from - # UTF-8. - try: - shebang.decode('utf-8') - except UnicodeDecodeError: - raise ValueError( - "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. - try: - 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: - outf.write(shebang) - outf.writelines(f.readlines()) - if f: - f.close() - else: - if f: - f.close() - updated_files.append(outfile) - self.copy_file(script, outfile) - - if os.name == 'posix': - for file in outfiles: - if self.dry_run: - 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) - os.chmod(file, newmode) - # XXX should we modify self.outfiles? + self._copy_script(script, outfiles, updated_files) + + self._change_modes(outfiles) + return outfiles, updated_files + + def _copy_script(self, script, outfiles, updated_files): # noqa: C901 + shebang_match = None + script = convert_path(script) + outfile = os.path.join(self.build_dir, os.path.basename(script)) + outfiles.append(outfile) + + if not self.force and not newer(script, outfile): + log.debug("not copying %s (up-to-date)", script) + return + + # Always open the file, but ignore failures in dry-run mode + # in order to attempt to copy directly. + try: + f = tokenize.open(script) + except OSError: + if not self.dry_run: + raise + f = None + else: + first_line = f.readline() + if not first_line: + self.warn("%s is an empty file (skipping)" % script) + return + + shebang_match = shebang_pattern.match(first_line) + + updated_files.append(outfile) + if shebang_match: + log.info("copying and adjusting %s -> %s", script, self.build_dir) + 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"), + ), + ) + post_interp = shebang_match.group(1) or '' + shebang = "#!" + executable + post_interp + "\n" + self._validate_shebang(shebang, f.encoding) + with open(outfile, "w", encoding=f.encoding) as outf: + outf.write(shebang) + outf.writelines(f.readlines()) + if f: + f.close() + else: + if f: + f.close() + self.copy_file(script, outfile) + + def _change_modes(self, outfiles): + if os.name != 'posix': + return + + for file in outfiles: + self._change_mode(file) + + def _change_mode(self, file): + if self.dry_run: + log.info("changing mode of %s", file) + return + + 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) + os.chmod(file, newmode) + + @staticmethod + def _validate_shebang(shebang, encoding): + # 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 + # written before. So the shebang has to be encodable to + # UTF-8. + try: + shebang.encode('utf-8') + except UnicodeEncodeError: + raise ValueError( + "The shebang ({!r}) is not encodable " "to utf-8".format(shebang) + ) + + # If the script is encoded to a custom encoding (use a + # #coding:xxx cookie), the shebang has to be encodable to + # the script encoding too. + try: + shebang.encode(encoding) + except UnicodeEncodeError: + raise ValueError( + "The shebang ({!r}) is not encodable " + "to the script encoding ({})".format(shebang, encoding) + ) diff --git a/setuptools/_distutils/command/check.py b/setuptools/_distutils/command/check.py index 525540b6..575e49fb 100644 --- a/setuptools/_distutils/command/check.py +++ b/setuptools/_distutils/command/check.py @@ -2,46 +2,56 @@ Implements the Distutils 'check' command. """ -from distutils.core import Command -from distutils.errors import DistutilsSetupError - -try: - # docutils is installed - from docutils.utils import Reporter - from docutils.parsers.rst import Parser - from docutils import frontend - from docutils import nodes - - class SilentReporter(Reporter): - - def __init__(self, source, report_level, halt_level, stream=None, - debug=0, encoding='ascii', error_handler='replace'): +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 + + class SilentReporter(docutils.utils.Reporter): + def __init__( + self, + source, + report_level, + halt_level, + stream=None, + debug=0, + encoding='ascii', + error_handler='replace', + ): self.messages = [] - super().__init__(source, report_level, halt_level, stream, - debug, encoding, error_handler) + super().__init__( + 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 docutils.nodes.system_message( + message, level=level, type=self.levels[level], *children, **kwargs + ) - HAS_DOCUTILS = True -except Exception: - # Catch all exceptions because exceptions besides ImportError probably - # 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')] + """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'), + ] boolean_options = ['metadata', 'restructuredtext', 'strict'] @@ -66,8 +76,11 @@ class check(Command): if self.metadata: self.check_metadata() if self.restructuredtext: - if HAS_DOCUTILS: - self.check_restructuredtext() + if 'docutils' in globals(): + try: + self.check_restructuredtext() + except TypeError as exc: + raise DistutilsSetupError(str(exc)) elif self.strict: raise DistutilsSetupError('The docutils package is needed.') @@ -80,34 +93,19 @@ class check(Command): """Ensures that all required elements of meta-data are supplied. Required fields: - name, version, URL - - Recommended fields: - (author and author_email) or (maintainer and maintainer_email)) + name, version Warns if any are missing. """ metadata = self.distribution.metadata missing = [] - for attr in ('name', 'version', 'url'): - if not (hasattr(metadata, attr) and getattr(metadata, attr)): + for attr in 'name', 'version': + if not getattr(metadata, attr, None): missing.append(attr) if 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") - elif metadata.maintainer: - if not metadata.maintainer_email: - 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 required meta-data: %s" % ', '.join(missing)) def check_restructuredtext(self): """Checks if the long string fields are reST-compliant.""" @@ -117,32 +115,37 @@ 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): """Returns warnings when the provided data doesn't compile.""" # the include and csv_table directives need this to be a path source_path = self.distribution.script_name or 'setup.py' - parser = Parser() - settings = frontend.OptionParser(components=(Parser,)).get_default_values() + parser = docutils.parsers.rst.Parser() + settings = docutils.frontend.OptionParser( + components=(docutils.parsers.rst.Parser,) + ).get_default_values() 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) - - document = nodes.document(settings, reporter, source=source_path) + 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 = docutils.nodes.document(settings, reporter, source=source_path) document.note_source(source_path, -1) try: 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 diff --git a/setuptools/_distutils/command/clean.py b/setuptools/_distutils/command/clean.py index 0cb27016..d6eb3eba 100644 --- a/setuptools/_distutils/command/clean.py +++ b/setuptools/_distutils/command/clean.py @@ -5,26 +5,29 @@ Implements the Distutils 'clean' command.""" # contributed by Bastian Kleineidam <calvin@cs.uni-sb.de>, added 2000-03-18 import os -from distutils.core import Command -from distutils.dir_util import remove_tree -from distutils 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-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 +41,14 @@ 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 +56,15 @@ 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.warning("'%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 diff --git a/setuptools/_distutils/command/config.py b/setuptools/_distutils/command/config.py index aeda408e..8bf0e489 100644 --- a/setuptools/_distutils/command/config.py +++ b/setuptools/_distutils/command/config.py @@ -9,41 +9,36 @@ configure-like tasks: "try to compile this C code", or "figure out where this header file lives". """ -import os, re +import os +import re -from distutils.core import Command -from distutils.errors import DistutilsExecError -from distutils.sysconfig import customize_compiler -from distutils 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"} + class config(Command): 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. @@ -92,10 +87,12 @@ class config(Command): """ # We do this late, and only on-demand, because this is an expensive # import. - from distutils.ccompiler import CCompiler, new_compiler + from ..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) @@ -132,14 +129,16 @@ 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 @@ -158,7 +157,6 @@ class config(Command): 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 @@ -176,7 +174,8 @@ class config(Command): preprocessor succeeded, false if there were any errors. ('body' probably isn't of much use, but what the heck.) """ - from distutils.ccompiler import CompileError + from ..ccompiler import CompileError + self._check_compiler() ok = True try: @@ -187,8 +186,7 @@ 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 @@ -219,7 +217,8 @@ class config(Command): """Try to compile a source file built from 'body' and 'headers'. Return true on success, false otherwise. """ - from distutils.ccompiler import CompileError + from ..ccompiler import CompileError + self._check_compiler() try: self._compile(body, headers, include_dirs, lang) @@ -231,17 +230,24 @@ class config(Command): 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. """ - from distutils.ccompiler import CompileError, LinkError + from ..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 @@ -250,17 +256,26 @@ class config(Command): 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. """ - from distutils.ccompiler import CompileError, LinkError + from ..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): @@ -270,13 +285,20 @@ class config(Command): 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. @@ -302,11 +324,16 @@ class config(Command): 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 +343,23 @@ 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) - - def check_header(self, header, include_dirs=None, library_dirs=None, - lang="c"): + 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"): """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. diff --git a/setuptools/_distutils/command/install.py b/setuptools/_distutils/command/install.py index 41c17d8a..08d2f881 100644 --- a/setuptools/_distutils/command/install.py +++ b/setuptools/_distutils/command/install.py @@ -8,19 +8,20 @@ import contextlib import sysconfig import itertools -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 DistutilsPlatformError -from distutils.file_util import write_file -from distutils.util import convert_path, subst_vars, change_root -from distutils.util import get_platform -from distutils.errors import DistutilsOptionError +from distutils._log import log +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 . import _framework_compat as fw from .. import _collections from site import USER_BASE from site import USER_SITE + HAS_USER_SITE = True WINDOWS_SCHEME = { @@ -28,59 +29,66 @@ WINDOWS_SCHEME = { 'platlib': '{base}/Lib/site-packages', 'headers': '{base}/Include/{dist_name}', 'scripts': '{base}/Scripts', - 'data' : '{base}', + 'data': '{base}', } INSTALL_SCHEMES = { 'posix_prefix': { 'purelib': '{base}/lib/{implementation_lower}{py_version_short}/site-packages', - 'platlib': '{platbase}/{platlibdir}/{implementation_lower}{py_version_short}/site-packages', - 'headers': '{base}/include/{implementation_lower}{py_version_short}{abiflags}/{dist_name}', + 'platlib': '{platbase}/{platlibdir}/{implementation_lower}' + '{py_version_short}/site-packages', + 'headers': '{base}/include/{implementation_lower}' + '{py_version_short}{abiflags}/{dist_name}', 'scripts': '{base}/bin', - 'data' : '{base}', - }, + 'data': '{base}', + }, 'posix_home': { 'purelib': '{base}/lib/{implementation_lower}', 'platlib': '{base}/{platlibdir}/{implementation_lower}', 'headers': '{base}/include/{implementation_lower}/{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: INSTALL_SCHEMES['nt_user'] = { 'purelib': '{usersite}', 'platlib': '{usersite}', - 'headers': '{userbase}/{implementation}{py_version_nodot_plat}/Include/{dist_name}', + 'headers': '{userbase}/{implementation}{py_version_nodot_plat}' + '/Include/{dist_name}', 'scripts': '{userbase}/{implementation}{py_version_nodot_plat}/Scripts', - 'data' : '{userbase}', - } + 'data': '{userbase}', + } INSTALL_SCHEMES['posix_user'] = { 'purelib': '{usersite}', 'platlib': '{usersite}', - 'headers': - '{userbase}/include/{implementation_lower}{py_version_short}{abiflags}/{dist_name}', + 'headers': '{userbase}/include/{implementation_lower}' + '{py_version_short}{abiflags}/{dist_name}', 'scripts': '{userbase}/bin', - 'data' : '{userbase}', - } + 'data': '{userbase}', + } + + +INSTALL_SCHEMES.update(fw.schemes) + # 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, @@ -128,11 +136,7 @@ def _remove_set(ob, attrs): """ Include only attrs that are None in ob. """ - return { - key: value - for key, value in attrs.items() - if getattr(ob, key) is None - } + return {key: value for key, value in attrs.items() if getattr(ob, key) is None} def _resolve_scheme(name): @@ -140,7 +144,7 @@ def _resolve_scheme(name): try: resolved = sysconfig.get_preferred_scheme(key) except Exception: - resolved = _pypy_hack(name) + resolved = fw.scheme(_pypy_hack(name)) return resolved @@ -164,10 +168,7 @@ def _inject_headers(name, scheme): def _scheme_attrs(scheme): """Resolve install directories by applying the install schemes.""" - return { - f'install_{key}': scheme[key] - for key in SCHEME_KEYS - } + return {f'install_{key}': scheme[key] for key in SCHEME_KEYS} def _pypy_hack(name): @@ -184,72 +185,73 @@ class install(Command): 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-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-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"), # 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]"), ('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"), - #('install-man=', None, "directory for Unix man pages"), - #('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"), - ] + # ('doc-format=', None, "format of documentation to generate"), + # ('install-man=', None, "directory for Unix man pages"), + # ('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"), + ] 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.""" @@ -271,10 +273,10 @@ class install(Command): # supplied by the user, they are filled in using the installation # scheme implied by prefix/exec-prefix/home and the contents of # that installation scheme. - self.install_purelib = None # for pure module distributions - self.install_platlib = None # non-pure (dists w/ extensions) - self.install_headers = None # for C/C++ headers - self.install_lib = None # set to either purelib or platlib + self.install_purelib = None # for pure module distributions + self.install_platlib = None # non-pure (dists w/ extensions) + self.install_headers = None # for C/C++ headers + self.install_lib = None # set to either purelib or platlib self.install_scripts = None self.install_data = None self.install_userbase = USER_BASE @@ -316,20 +318,19 @@ class install(Command): # Not defined yet because we don't know anything about # documentation yet. - #self.install_man = None - #self.install_html = None - #self.install_info = None + # self.install_man = None + # self.install_html = None + # self.install_info = None self.record = None - # -- Option finalizing methods ------------------------------------- # (This is rather more involved than for most commands, # because this is where the policy for installing third- # party Python modules on various platforms given a wide # array of user input is decided. Yes, it's quite complex!) - def finalize_options(self): + def finalize_options(self): # noqa: C901 """Finalizes options.""" # This method (and its helpers, like 'finalize_unix()', # 'finalize_other()', and 'select_scheme()') is where the default @@ -345,20 +346,30 @@ class install(Command): # Check for errors/inconsistencies in the options; first, stuff # that's wrong on any platform. - if ((self.prefix or self.exec_prefix or self.home) and - (self.install_base or self.install_platbase)): + if (self.prefix or self.exec_prefix or self.home) and ( + 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": @@ -423,7 +434,8 @@ class install(Command): local_vars['usersite'] = self.install_usersite self.config_vars = _collections.DictStack( - [compat_vars, sysconfig.get_config_vars(), local_vars]) + [fw.vars(), compat_vars, sysconfig.get_config_vars(), local_vars] + ) self.expand_basedirs() @@ -436,6 +448,7 @@ class install(Command): if DEBUG: from pprint import pprint + print("config vars:") pprint(dict(self.config_vars)) @@ -454,17 +467,23 @@ 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 @@ -472,21 +491,22 @@ 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") # 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! @@ -495,7 +515,8 @@ class install(Command): """Dumps the list of user options.""" if not DEBUG: return - from distutils.fancy_getopt import longopt_xlate + from ..fancy_getopt import longopt_xlate + log.debug(msg + ":") for opt in self.user_options: opt_name = opt[0] @@ -515,24 +536,24 @@ class install(Command): if self.install_base is not None or self.install_platbase is not None: incomplete_scheme = ( ( - 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_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 ) if incomplete_scheme: 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") + raise DistutilsPlatformError("User base directory is not specified") self.install_base = self.install_platbase = self.install_userbase self.select_scheme("posix_user") elif self.home is not None: @@ -542,15 +563,14 @@ class install(Command): 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" + ) # Allow Fedora to add components to the prefix _prefix_addition = getattr(sysconfig, '_prefix_addition', "") - self.prefix = ( - os.path.normpath(sys.prefix) + _prefix_addition) - self.exec_prefix = ( - os.path.normpath(sys.exec_prefix) + _prefix_addition) + self.prefix = os.path.normpath(sys.prefix) + _prefix_addition + self.exec_prefix = os.path.normpath(sys.exec_prefix) + _prefix_addition else: if self.exec_prefix is None: @@ -564,8 +584,7 @@ class install(Command): """Finalizes options for non-posix platforms""" if self.user: if self.install_userbase is None: - raise DistutilsPlatformError( - "User base directory is not specified") + raise DistutilsPlatformError("User base directory is not specified") self.install_base = self.install_platbase = self.install_userbase self.select_scheme(os.name + "_user") elif self.home is not None: @@ -580,7 +599,8 @@ 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): _select_scheme(self, name) @@ -601,9 +621,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`.""" @@ -617,7 +644,7 @@ class install(Command): self.extra_path = self.distribution.extra_path if self.extra_path is not None: - log.warn( + log.warning( "Distribution option extra_path is deprecated. " "See issue27919 for details." ) @@ -630,8 +657,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) @@ -674,8 +702,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(): @@ -687,38 +714,43 @@ class install(Command): # write list of installed files, if requested. if self.record: outputs = self.get_outputs() - if self.root: # strip any package prefix + if self.root: # strip any package prefix 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 - 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) + 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, + ) 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): @@ -733,8 +765,7 @@ 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 @@ -753,8 +784,9 @@ 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 @@ -773,9 +805,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), + ] diff --git a/setuptools/_distutils/command/install_data.py b/setuptools/_distutils/command/install_data.py index 947cd76a..d92ed87a 100644 --- a/setuptools/_distutils/command/install_data.py +++ b/setuptools/_distutils/command/install_data.py @@ -6,21 +6,24 @@ platform-independent data files.""" # contributed by Bastian Kleineidam import os -from distutils.core import Command -from distutils.util import change_root, convert_path +from ..core import Command +from ..util import change_root, convert_path + class install_data(Command): 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"), + ( + '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 +36,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 +50,10 @@ 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: diff --git a/setuptools/_distutils/command/install_egg_info.py b/setuptools/_distutils/command/install_egg_info.py index adc0323f..f3e8f344 100644 --- a/setuptools/_distutils/command/install_egg_info.py +++ b/setuptools/_distutils/command/install_egg_info.py @@ -1,12 +1,18 @@ -"""distutils.command.install_egg_info +""" +distutils.command.install_egg_info Implements the Distutils 'install_egg_info' command, for installing -a package's PKG-INFO metadata.""" +a package's PKG-INFO metadata. +""" +import os +import sys +import re + +from ..cmd import Command +from .. import dir_util +from .._log 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""" @@ -28,11 +34,11 @@ class install_egg_info(Command): return "%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], ) def finalize_options(self): - self.set_undefined_options('install_lib',('install_dir','install_dir')) + self.set_undefined_options('install_lib', ('install_dir', 'install_dir')) self.target = os.path.join(self.install_dir, self.basename) self.outputs = [self.target] @@ -41,10 +47,11 @@ 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) + 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: @@ -58,6 +65,7 @@ class install_egg_info(Command): # can be replaced by importing them from pkg_resources once it is included # in the stdlib. + def safe_name(name): """Convert an arbitrary string to a standard distribution name @@ -72,7 +80,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) @@ -81,4 +89,4 @@ def to_filename(name): Any '-' characters are currently replaced with '_'. """ - return name.replace('-','_') + return name.replace('-', '_') diff --git a/setuptools/_distutils/command/install_headers.py b/setuptools/_distutils/command/install_headers.py index 9bb0b18d..1cdee823 100644 --- a/setuptools/_distutils/command/install_headers.py +++ b/setuptools/_distutils/command/install_headers.py @@ -3,7 +3,7 @@ Implements the Distutils 'install_headers' command, to install C/C++ header files to the Python include directory.""" -from distutils.core import Command +from ..core import Command # XXX force is never used @@ -11,11 +11,10 @@ class install_headers(Command): 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 +24,9 @@ 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 diff --git a/setuptools/_distutils/command/install_lib.py b/setuptools/_distutils/command/install_lib.py index 6154cf09..840d3403 100644 --- a/setuptools/_distutils/command/install_lib.py +++ b/setuptools/_distutils/command/install_lib.py @@ -7,13 +7,14 @@ import os import importlib.util import sys -from distutils.core import Command -from distutils.errors import DistutilsOptionError +from ..core import Command +from ..errors import DistutilsOptionError # Extension for Python source files. PYTHON_SOURCE_EXTENSION = ".py" + class install_lib(Command): description = "install all Python modules (extensions and pure Python)" @@ -35,18 +36,21 @@ class install_lib(Command): user_options = [ ('install-dir=', 'd', "directory to install to"), - ('build-dir=','b', "build directory (where to install from)"), + ('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]"), + ( + '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 @@ -110,8 +115,9 @@ 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 @@ -120,7 +126,7 @@ class install_lib(Command): self.warn('byte-compiling is disabled, skipping.') return - from distutils.util import byte_compile + from ..util import byte_compile # Get the "--root" directory supplied to the "install" command, # and use it as a prefix to strip off the purported filename @@ -129,14 +135,22 @@ 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 +179,18 @@ 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) @@ -182,19 +199,23 @@ class install_lib(Command): were actually run. Not affected by the "dry-run" flag or whether modules have actually been built yet. """ - pure_outputs = \ - self._mutate_outputs(self.distribution.has_pure_modules(), - 'build_py', 'build_lib', - self.install_dir) + pure_outputs = 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) + ext_outputs = self._mutate_outputs( + self.distribution.has_ext_modules(), + 'build_ext', + 'build_lib', + self.install_dir, + ) return pure_outputs + bytecode_outputs + ext_outputs diff --git a/setuptools/_distutils/command/install_scripts.py b/setuptools/_distutils/command/install_scripts.py index 31a1130e..ec6ec5ac 100644 --- a/setuptools/_distutils/command/install_scripts.py +++ b/setuptools/_distutils/command/install_scripts.py @@ -6,8 +6,8 @@ Python scripts.""" # contributed by Bastian Kleineidam import os -from distutils.core import Command -from distutils import log +from ..core import Command +from distutils._log import log from stat import ST_MODE @@ -17,7 +17,7 @@ class install_scripts(Command): user_options = [ ('install-dir=', 'd', "directory to install scripts to"), - ('build-dir=','b', "build directory (where to install from)"), + ('build-dir=', 'b', "build directory (where to install from)"), ('force', 'f', "force installation (overwrite existing files)"), ('skip-build', None, "skip the build steps"), ] @@ -32,11 +32,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: diff --git a/setuptools/_distutils/command/py37compat.py b/setuptools/_distutils/command/py37compat.py index 754715a5..aa0c0a7f 100644 --- a/setuptools/_distutils/command/py37compat.py +++ b/setuptools/_distutils/command/py37compat.py @@ -7,12 +7,13 @@ def _pythonlib_compat(): 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, + (sys.hexversion >> 16) & 0xFF, sysconfig.get_config_var('ABIFLAGS'), ) diff --git a/setuptools/_distutils/command/register.py b/setuptools/_distutils/command/register.py index 0fac94e9..55c1045e 100644 --- a/setuptools/_distutils/command/register.py +++ b/setuptools/_distutils/command/register.py @@ -7,24 +7,31 @@ Implements the Distutils 'register' command (register with the repository). import getpass import io -import urllib.parse, urllib.request +import logging +import urllib.parse +import urllib.request from warnings import warn -from distutils.core import PyPIRCCommand -from distutils.errors import * -from distutils import log +from ..core import PyPIRCCommand +from distutils._log 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,11 @@ 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", + DeprecationWarning, + ) check = self.distribution.get_command_obj('check') check.ensure_finalized() check.strict = self.strict @@ -66,8 +78,7 @@ class register(PyPIRCCommand): check.run() def _set_config(self): - ''' Reads the configuration file and set attributes. - ''' + '''Reads the configuration file and set attributes.''' config = self._read_pypirc() if config != {}: self.username = config['username'] @@ -83,45 +94,43 @@ class register(PyPIRCCommand): self.has_config = False def classifiers(self): - ''' Fetch the list of classifiers from the server. - ''' - url = self.repository+'?:action=list_classifiers' + '''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 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): - ''' Send the metadata to the package index server. + 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. + 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: + 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 + [distutils] + index-servers = + pypi - [pypi] - username: fred - password: sekrit + [pypi] + username: fred + password: sekrit - Otherwise, to figure who the user is, we offer the user three - choices: + 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. + 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 @@ -137,13 +146,16 @@ 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]: ''', + logging.INFO, + ) choice = input() if not choice: choice = '1' @@ -162,10 +174,8 @@ 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), logging.INFO) # possibly save the login if code == 200: @@ -174,10 +184,17 @@ 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.' + ), + 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)?') @@ -208,8 +225,7 @@ 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 +240,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(), @@ -246,13 +262,13 @@ Your selection [default 1]: ''', log.INFO) data['metadata_version'] = '1.1' return data - def post_to_server(self, data, auth=None): - ''' Post a query to the server, and return a string response. - ''' + 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 %s to %s' % (data['name'], - self.repository), - log.INFO) + 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 @@ -260,12 +276,12 @@ 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('\nContent-Disposition: form-data; name="%s"' % key) body.write("\n\n") body.write(value) if value and value[-1] == '\r': @@ -276,8 +292,9 @@ Your selection [default 1]: ''', log.INFO) # 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) @@ -300,5 +317,5 @@ Your selection [default 1]: ''', log.INFO) result = 200, 'OK' if self.show_response: msg = '\n'.join(('-' * 75, data, '-' * 75)) - self.announce(msg, log.INFO) + self.announce(msg, logging.INFO) return result diff --git a/setuptools/_distutils/command/sdist.py b/setuptools/_distutils/command/sdist.py index b4996fcb..5cfd4c14 100644 --- a/setuptools/_distutils/command/sdist.py +++ b/setuptools/_distutils/command/sdist.py @@ -7,30 +7,29 @@ import sys from glob import glob from warnings import warn -from distutils.core import Command +from ..core import Command 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.util import convert_path -from distutils.errors import DistutilsTemplateError, DistutilsOptionError +from ..text_file import TextFile +from ..filelist import FileList +from distutils._log import log +from ..util import convert_path +from ..errors import DistutilsOptionError, DistutilsTemplateError def show_formats(): """Print all possible values for the 'formats' option (used by the "--help-formats" command-line option). """ - from distutils.fancy_getopt import FancyGetopt - from distutils.archive_util import ARCHIVE_FORMATS + from ..fancy_getopt import FancyGetopt + from ..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:") + FancyGetopt(formats).print_help("List of available source distribution formats:") class sdist(Command): @@ -44,55 +43,77 @@ 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]"), - ] - - boolean_options = ['use-defaults', 'prune', - 'manifest-only', 'force-manifest', - 'keep-temp', 'metadata-check'] + ('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', + ] 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)] @@ -131,8 +152,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("unknown archive format '%s'" % bad_format) if self.dist_dir is None: self.dist_dir = "dist" @@ -161,8 +181,11 @@ 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 +212,10 @@ 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 +283,9 @@ 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,14 +353,20 @@ 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: line = template.readline() - if line is None: # end of file + if line is None: # end of file break try: @@ -344,9 +375,10 @@ 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 +401,8 @@ 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 +411,19 @@ 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 @@ -437,20 +473,20 @@ class sdist(Command): # out-of-date, because by default we blow away 'base_dir' when # we're done making the distribution archives.) - if hasattr(os, 'link'): # can make hard links on this system + if hasattr(os, 'link'): # can make hard links on this system link = 'hard' msg = "making hard links in %s..." % base_dir - else: # nope, have to copy + else: # nope, have to copy link = None msg = "copying files to %s..." % base_dir if not files: - log.warn("no files to distribute -- empty manifest?") + log.warning("no files to distribute -- empty manifest?") else: log.info(msg) for file in files: if not os.path.isfile(file): - log.warn("'%s' not a regular file -- skipping", file) + log.warning("'%s' not a regular file -- skipping", file) else: dest = os.path.join(base_dir, file) self.copy_file(file, dest, link=link) @@ -471,14 +507,15 @@ class sdist(Command): base_name = os.path.join(self.dist_dir, base_dir) self.make_release_tree(base_dir, self.filelist.files) - archive_files = [] # remember names of files we create + archive_files = [] # remember names of files we create # tar archive must be created last to avoid overwrite and remove if 'tar' in self.formats: 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)) diff --git a/setuptools/_distutils/command/upload.py b/setuptools/_distutils/command/upload.py index 95e9fda1..16e15d8b 100644 --- a/setuptools/_distutils/command/upload.py +++ b/setuptools/_distutils/command/upload.py @@ -8,13 +8,13 @@ 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 distutils.errors import DistutilsError, DistutilsOptionError -from distutils.core import PyPIRCCommand -from distutils.spawn import spawn -from distutils import log +from ..errors import DistutilsError, DistutilsOptionError +from ..core import PyPIRCCommand +from ..spawn import spawn # PyPI Warehouse supports MD5, SHA256, and Blake2 (blake2-256) @@ -31,10 +31,9 @@ class upload(PyPIRCCommand): 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'] @@ -49,9 +48,7 @@ class upload(PyPIRCCommand): 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" - ) + raise DistutilsOptionError("Must use --sign for --identity to have meaning") config = self._read_pypirc() if config != {}: self.username = config['username'] @@ -66,16 +63,17 @@ 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) - def upload_file(self, 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) + schema, netloc, url, params, query, fragments = urlparse(self.repository) if params or query or fragments: raise AssertionError("Incompatible url %s" % self.repository) @@ -87,12 +85,11 @@ class upload(PyPIRCCommand): 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) + 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: @@ -103,16 +100,13 @@ class upload(PyPIRCCommand): # 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), + 'content': (os.path.basename(filename), content), 'filetype': command, 'pyversion': pyversion, - # additional meta-data 'metadata_version': '1.0', 'summary': meta.get_description(), @@ -129,7 +123,7 @@ class upload(PyPIRCCommand): 'provides': meta.get_provides(), 'requires': meta.get_requires(), 'obsoletes': meta.get_obsoletes(), - } + } data['comment'] = '' @@ -145,8 +139,7 @@ class upload(PyPIRCCommand): if self.sign: with open(filename + ".asc", "rb") as f: - data['gpg_signature'] = (os.path.basename(filename) + ".asc", - f.read()) + data['gpg_signature'] = (os.path.basename(filename) + ".asc", f.read()) # set up the authentication user_pass = (self.username + ":" + self.password).encode('ascii') @@ -177,8 +170,8 @@ class upload(PyPIRCCommand): body.write(end_boundary) body = body.getvalue() - msg = "Submitting %s to %s" % (filename, self.repository) - self.announce(msg, log.INFO) + msg = "Submitting {} to {}".format(filename, self.repository) + self.announce(msg, logging.INFO) # build the Request headers = { @@ -187,8 +180,7 @@ 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) @@ -198,17 +190,18 @@ class upload(PyPIRCCommand): status = e.code reason = e.msg except OSError as e: - self.announce(str(e), log.ERROR) + self.announce(str(e), logging.ERROR) raise if status == 200: - self.announce('Server response (%s): %s' % (status, reason), - log.INFO) + 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, log.INFO) + self.announce(msg, logging.INFO) else: - msg = 'Upload failed (%s): %s' % (status, reason) - self.announce(msg, log.ERROR) + msg = 'Upload failed ({}): {}'.format(status, reason) + self.announce(msg, logging.ERROR) raise DistutilsError(msg) |
