aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.bumpversion.cfg2
-rw-r--r--.travis.yml3
-rw-r--r--CHANGES.rst26
-rw-r--r--changelog.d/1767.change.rst2
-rw-r--r--changelog.d/1860.doc.rst1
-rw-r--r--changelog.d/1861.change.rst1
-rw-r--r--changelog.d/1898.breaking.rst1
-rw-r--r--conftest.py1
-rw-r--r--docs/development.txt2
-rw-r--r--docs/easy_install.txt1085
-rw-r--r--docs/ez_setup.txt195
-rw-r--r--docs/formats.txt7
-rw-r--r--docs/index.txt1
-rw-r--r--docs/pkg_resources.txt14
-rw-r--r--docs/setuptools.txt253
-rw-r--r--easy_install.py5
-rw-r--r--pkg_resources/__init__.py3
-rw-r--r--pkg_resources/api_tests.txt2
-rw-r--r--pkg_resources/tests/data/my-test-package-source/setup.cfg0
-rw-r--r--pkg_resources/tests/data/my-test-package-source/setup.py6
-rw-r--r--pkg_resources/tests/data/my-test-package_unpacked-egg/my_test_package-1.0-py3.7.egg/EGG-INFO/PKG-INFO10
-rw-r--r--pkg_resources/tests/data/my-test-package_unpacked-egg/my_test_package-1.0-py3.7.egg/EGG-INFO/SOURCES.txt7
-rw-r--r--pkg_resources/tests/data/my-test-package_unpacked-egg/my_test_package-1.0-py3.7.egg/EGG-INFO/dependency_links.txt1
-rw-r--r--pkg_resources/tests/data/my-test-package_unpacked-egg/my_test_package-1.0-py3.7.egg/EGG-INFO/top_level.txt1
-rw-r--r--pkg_resources/tests/data/my-test-package_unpacked-egg/my_test_package-1.0-py3.7.egg/EGG-INFO/zip-safe1
-rw-r--r--pkg_resources/tests/data/my-test-package_zipped-egg/my_test_package-1.0-py3.7.eggbin0 -> 843 bytes
-rw-r--r--pkg_resources/tests/test_find_distributions.py58
-rw-r--r--pkg_resources/tests/test_resources.py19
-rw-r--r--pytest.ini5
-rw-r--r--setup.cfg4
-rwxr-xr-xsetup.py19
-rw-r--r--setuptools/_imp.py73
-rw-r--r--setuptools/command/__init__.py3
-rw-r--r--setuptools/command/bdist_egg.py2
-rw-r--r--setuptools/command/easy_install.py72
-rw-r--r--setuptools/command/install.py2
-rw-r--r--setuptools/command/register.py22
-rw-r--r--setuptools/command/sdist.py28
-rw-r--r--setuptools/command/test.py10
-rw-r--r--setuptools/command/upload.py195
-rw-r--r--setuptools/config.py1
-rw-r--r--setuptools/depends.py48
-rw-r--r--setuptools/dist.py29
-rw-r--r--setuptools/errors.py16
-rw-r--r--setuptools/installer.py129
-rw-r--r--setuptools/msvc.py1022
-rw-r--r--setuptools/package_index.py2
-rw-r--r--setuptools/py27compat.py32
-rw-r--r--setuptools/py34compat.py13
-rw-r--r--setuptools/tests/server.py19
-rw-r--r--setuptools/tests/test_bdist_egg.py2
-rw-r--r--setuptools/tests/test_config.py2
-rw-r--r--setuptools/tests/test_easy_install.py229
-rw-r--r--setuptools/tests/test_egg_info.py208
-rw-r--r--setuptools/tests/test_integration.py2
-rw-r--r--setuptools/tests/test_namespaces.py5
-rw-r--r--setuptools/tests/test_register.py43
-rw-r--r--setuptools/tests/test_test.py60
-rw-r--r--setuptools/tests/test_upload.py211
-rw-r--r--setuptools/tests/test_virtualenv.py18
-rw-r--r--setuptools/tests/test_wheel.py28
-rw-r--r--setuptools/wheel.py21
-rw-r--r--tests/manual_test.py100
-rw-r--r--tests/requirements.txt2
-rw-r--r--tools/tox_pip.py38
-rw-r--r--tox.ini16
66 files changed, 1819 insertions, 2619 deletions
diff --git a/.bumpversion.cfg b/.bumpversion.cfg
index d6768cb8..40db5b03 100644
--- a/.bumpversion.cfg
+++ b/.bumpversion.cfg
@@ -1,5 +1,5 @@
[bumpversion]
-current_version = 41.4.0
+current_version = 41.6.0
commit = True
tag = True
diff --git a/.travis.yml b/.travis.yml
index 8b7cece8..7088d166 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -16,8 +16,9 @@ jobs:
- python: 3.5
- &default_py
python: 3.6
+ - python: 3.7
- &latest_py3
- python: 3.7
+ python: 3.8
- <<: *latest_py3
env: LANG=C
- python: 3.8-dev
diff --git a/CHANGES.rst b/CHANGES.rst
index ecde25a5..ba7b4647 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -1,3 +1,29 @@
+v41.6.0
+-------
+
+* #479: Replace usage of deprecated ``imp`` module with local re-implementation in ``setuptools._imp``.
+
+
+v41.5.1
+-------
+
+* #1891: Fix code for detecting Visual Studio's version on Windows under Python 2.
+
+
+v41.5.0
+-------
+
+* #1811: Improve Visual C++ 14.X support, mainly for Visual Studio 2017 and 2019.
+* #1814: Fix ``pkg_resources.Requirement`` hash/equality implementation: take PEP 508 direct URL into account.
+* #1824: Fix tests when running under ``python3.10``.
+* #1878: Formally deprecated the ``test`` command, with the recommendation that users migrate to ``tox``.
+* #1860: Update documentation to mention the egg format is not supported by pip and dependency links support was dropped starting with pip 19.0.
+* #1862: Drop ez_setup documentation: deprecated for some time (last updated in 2016), and still relying on easy_install (deprecated too).
+* #1868: Drop most documentation references to (deprecated) EasyInstall.
+* #1884: Added a trove classifier to document support for Python 3.8.
+* #1886: Added Python 3.8 release to the Travis test matrix.
+
+
v41.4.0
-------
diff --git a/changelog.d/1767.change.rst b/changelog.d/1767.change.rst
new file mode 100644
index 00000000..5d42aedc
--- /dev/null
+++ b/changelog.d/1767.change.rst
@@ -0,0 +1,2 @@
+Add support for the ``license_files`` option in ``setup.cfg`` to automatically
+include multiple license files in a source distribution.
diff --git a/changelog.d/1860.doc.rst b/changelog.d/1860.doc.rst
deleted file mode 100644
index f3554643..00000000
--- a/changelog.d/1860.doc.rst
+++ /dev/null
@@ -1 +0,0 @@
-Update documentation to mention the egg format is not supported by pip and dependency links support was dropped starting with pip 19.0.
diff --git a/changelog.d/1861.change.rst b/changelog.d/1861.change.rst
new file mode 100644
index 00000000..5a4e0a56
--- /dev/null
+++ b/changelog.d/1861.change.rst
@@ -0,0 +1 @@
+Fix empty namespace package installation from wheel.
diff --git a/changelog.d/1898.breaking.rst b/changelog.d/1898.breaking.rst
new file mode 100644
index 00000000..844a8a42
--- /dev/null
+++ b/changelog.d/1898.breaking.rst
@@ -0,0 +1 @@
+Removed the "upload" and "register" commands in favor of `twine <https://pypi.org/p/twine>`_.
diff --git a/conftest.py b/conftest.py
index 0d7b274c..1746bfb5 100644
--- a/conftest.py
+++ b/conftest.py
@@ -19,6 +19,7 @@ collect_ignore = [
if sys.version_info < (3,):
collect_ignore.append('setuptools/lib2to3_ex.py')
+ collect_ignore.append('setuptools/_imp.py')
if sys.version_info < (3, 6):
diff --git a/docs/development.txt b/docs/development.txt
index 455f038a..28e653fe 100644
--- a/docs/development.txt
+++ b/docs/development.txt
@@ -7,7 +7,7 @@ Authority (PyPA) and led by Jason R. Coombs.
This document describes the process by which Setuptools is developed.
This document assumes the reader has some passing familiarity with
-*using* setuptools, the ``pkg_resources`` module, and EasyInstall. It
+*using* setuptools, the ``pkg_resources`` module, and pip. It
does not attempt to explain basic concepts like inter-project
dependencies, nor does it contain detailed lexical syntax for most
file formats. Neither does it explain concepts like "namespace
diff --git a/docs/easy_install.txt b/docs/easy_install.txt
deleted file mode 100644
index aa11f890..00000000
--- a/docs/easy_install.txt
+++ /dev/null
@@ -1,1085 +0,0 @@
-============
-Easy Install
-============
-
-.. warning::
- Easy Install is deprecated. Do not use it. Instead use pip. If
- you think you need Easy Install, please reach out to the PyPA
- team (a ticket to pip or setuptools is fine), describing your
- use-case.
-
-Easy Install is a python module (``easy_install``) bundled with ``setuptools``
-that lets you automatically download, build, install, and manage Python
-packages.
-
-Please share your experiences with us! If you encounter difficulty installing
-a package, please contact us via the `distutils mailing list
-<http://mail.python.org/pipermail/distutils-sig/>`_. (Note: please DO NOT send
-private email directly to the author of setuptools; it will be discarded. The
-mailing list is a searchable archive of previously-asked and answered
-questions; you should begin your research there before reporting something as a
-bug -- and then do so via list discussion first.)
-
-(Also, if you'd like to learn about how you can use ``setuptools`` to make your
-own packages work better with EasyInstall, or provide EasyInstall-like features
-without requiring your users to use EasyInstall directly, you'll probably want
-to check out the full documentation as well.)
-
-.. contents:: **Table of Contents**
-
-
-Using "Easy Install"
-====================
-
-
-.. _installation instructions:
-
-Installing "Easy Install"
--------------------------
-
-Please see the `setuptools PyPI page <https://pypi.org/project/setuptools/>`_
-for download links and basic installation instructions for each of the
-supported platforms.
-
-You will need at least Python 3.4 or 2.7. An ``easy_install`` script will be
-installed in the normal location for Python scripts on your platform.
-
-Note that the instructions on the setuptools PyPI page assume that you are
-are installing to Python's primary ``site-packages`` directory. If this is
-not the case, you should consult the section below on `Custom Installation
-Locations`_ before installing. (And, on Windows, you should not use the
-``.exe`` installer when installing to an alternate location.)
-
-Note that ``easy_install`` normally works by downloading files from the
-internet. If you are behind an NTLM-based firewall that prevents Python
-programs from accessing the net directly, you may wish to first install and use
-the `APS proxy server <http://ntlmaps.sf.net/>`_, which lets you get past such
-firewalls in the same way that your web browser(s) do.
-
-(Alternately, if you do not wish easy_install to actually download anything, you
-can restrict it from doing so with the ``--allow-hosts`` option; see the
-sections on `restricting downloads with --allow-hosts`_ and `command-line
-options`_ for more details.)
-
-
-Troubleshooting
-~~~~~~~~~~~~~~~
-
-If EasyInstall/setuptools appears to install correctly, and you can run the
-``easy_install`` command but it fails with an ``ImportError``, the most likely
-cause is that you installed to a location other than ``site-packages``,
-without taking any of the steps described in the `Custom Installation
-Locations`_ section below. Please see that section and follow the steps to
-make sure that your custom location will work correctly. Then re-install.
-
-Similarly, if you can run ``easy_install``, and it appears to be installing
-packages, but then you can't import them, the most likely issue is that you
-installed EasyInstall correctly but are using it to install packages to a
-non-standard location that hasn't been properly prepared. Again, see the
-section on `Custom Installation Locations`_ for more details.
-
-
-Windows Notes
-~~~~~~~~~~~~~
-
-Installing setuptools will provide an ``easy_install`` command according to
-the techniques described in `Executables and Launchers`_. If the
-``easy_install`` command is not available after installation, that section
-provides details on how to configure Windows to make the commands available.
-
-
-Downloading and Installing a Package
-------------------------------------
-
-For basic use of ``easy_install``, you need only supply the filename or URL of
-a source distribution or .egg file (`Python Egg`__).
-
-__ http://peak.telecommunity.com/DevCenter/PythonEggs
-
-**Example 1**. Install a package by name, searching PyPI for the latest
-version, and automatically downloading, building, and installing it::
-
- easy_install SQLObject
-
-**Example 2**. Install or upgrade a package by name and version by finding
-links on a given "download page"::
-
- easy_install -f http://pythonpaste.org/package_index.html SQLObject
-
-**Example 3**. Download a source distribution from a specified URL,
-automatically building and installing it::
-
- easy_install http://example.com/path/to/MyPackage-1.2.3.tgz
-
-**Example 4**. Install an already-downloaded .egg file::
-
- easy_install /my_downloads/OtherPackage-3.2.1-py2.3.egg
-
-**Example 5**. Upgrade an already-installed package to the latest version
-listed on PyPI::
-
- easy_install --upgrade PyProtocols
-
-**Example 6**. Install a source distribution that's already downloaded and
-extracted in the current directory (New in 0.5a9)::
-
- easy_install .
-
-**Example 7**. (New in 0.6a1) Find a source distribution or Subversion
-checkout URL for a package, and extract it or check it out to
-``~/projects/sqlobject`` (the name will always be in all-lowercase), where it
-can be examined or edited. (The package will not be installed, but it can
-easily be installed with ``easy_install ~/projects/sqlobject``. See `Editing
-and Viewing Source Packages`_ below for more info.)::
-
- easy_install --editable --build-directory ~/projects SQLObject
-
-**Example 7**. (New in 0.6.11) Install a distribution within your home dir::
-
- easy_install --user SQLAlchemy
-
-Easy Install accepts URLs, filenames, PyPI package names (i.e., ``distutils``
-"distribution" names), and package+version specifiers. In each case, it will
-attempt to locate the latest available version that meets your criteria.
-
-When downloading or processing downloaded files, Easy Install recognizes
-distutils source distribution files with extensions of .tgz, .tar, .tar.gz,
-.tar.bz2, or .zip. And of course it handles already-built .egg
-distributions as well as ``.win32.exe`` installers built using distutils.
-
-By default, packages are installed to the running Python installation's
-``site-packages`` directory, unless you provide the ``-d`` or ``--install-dir``
-option to specify an alternative directory, or specify an alternate location
-using distutils configuration files. (See `Configuration Files`_, below.)
-
-By default, any scripts included with the package are installed to the running
-Python installation's standard script installation location. However, if you
-specify an installation directory via the command line or a config file, then
-the default directory for installing scripts will be the same as the package
-installation directory, to ensure that the script will have access to the
-installed package. You can override this using the ``-s`` or ``--script-dir``
-option.
-
-Installed packages are added to an ``easy-install.pth`` file in the install
-directory, so that Python will always use the most-recently-installed version
-of the package. If you would like to be able to select which version to use at
-runtime, you should use the ``-m`` or ``--multi-version`` option.
-
-
-Upgrading a Package
--------------------
-
-You don't need to do anything special to upgrade a package: just install the
-new version, either by requesting a specific version, e.g.::
-
- easy_install "SomePackage==2.0"
-
-a version greater than the one you have now::
-
- easy_install "SomePackage>2.0"
-
-using the upgrade flag, to find the latest available version on PyPI::
-
- easy_install --upgrade SomePackage
-
-or by using a download page, direct download URL, or package filename::
-
- easy_install -f http://example.com/downloads ExamplePackage
-
- easy_install http://example.com/downloads/ExamplePackage-2.0-py2.4.egg
-
- easy_install my_downloads/ExamplePackage-2.0.tgz
-
-If you're using ``-m`` or ``--multi-version`` , using the ``require()``
-function at runtime automatically selects the newest installed version of a
-package that meets your version criteria. So, installing a newer version is
-the only step needed to upgrade such packages.
-
-If you're installing to a directory on PYTHONPATH, or a configured "site"
-directory (and not using ``-m``), installing a package automatically replaces
-any previous version in the ``easy-install.pth`` file, so that Python will
-import the most-recently installed version by default. So, again, installing
-the newer version is the only upgrade step needed.
-
-If you haven't suppressed script installation (using ``--exclude-scripts`` or
-``-x``), then the upgraded version's scripts will be installed, and they will
-be automatically patched to ``require()`` the corresponding version of the
-package, so that you can use them even if they are installed in multi-version
-mode.
-
-``easy_install`` never actually deletes packages (unless you're installing a
-package with the same name and version number as an existing package), so if
-you want to get rid of older versions of a package, please see `Uninstalling
-Packages`_, below.
-
-
-Changing the Active Version
----------------------------
-
-If you've upgraded a package, but need to revert to a previously-installed
-version, you can do so like this::
-
- easy_install PackageName==1.2.3
-
-Where ``1.2.3`` is replaced by the exact version number you wish to switch to.
-If a package matching the requested name and version is not already installed
-in a directory on ``sys.path``, it will be located via PyPI and installed.
-
-If you'd like to switch to the latest installed version of ``PackageName``, you
-can do so like this::
-
- easy_install PackageName
-
-This will activate the latest installed version. (Note: if you have set any
-``find_links`` via distutils configuration files, those download pages will be
-checked for the latest available version of the package, and it will be
-downloaded and installed if it is newer than your current version.)
-
-Note that changing the active version of a package will install the newly
-active version's scripts, unless the ``--exclude-scripts`` or ``-x`` option is
-specified.
-
-
-Uninstalling Packages
----------------------
-
-If you have replaced a package with another version, then you can just delete
-the package(s) you don't need by deleting the PackageName-versioninfo.egg file
-or directory (found in the installation directory).
-
-If you want to delete the currently installed version of a package (or all
-versions of a package), you should first run::
-
- easy_install -m PackageName
-
-This will ensure that Python doesn't continue to search for a package you're
-planning to remove. After you've done this, you can safely delete the .egg
-files or directories, along with any scripts you wish to remove.
-
-
-Managing Scripts
-----------------
-
-Whenever you install, upgrade, or change versions of a package, EasyInstall
-automatically installs the scripts for the selected package version, unless
-you tell it not to with ``-x`` or ``--exclude-scripts``. If any scripts in
-the script directory have the same name, they are overwritten.
-
-Thus, you do not normally need to manually delete scripts for older versions of
-a package, unless the newer version of the package does not include a script
-of the same name. However, if you are completely uninstalling a package, you
-may wish to manually delete its scripts.
-
-EasyInstall's default behavior means that you can normally only run scripts
-from one version of a package at a time. If you want to keep multiple versions
-of a script available, however, you can simply use the ``--multi-version`` or
-``-m`` option, and rename the scripts that EasyInstall creates. This works
-because EasyInstall installs scripts as short code stubs that ``require()`` the
-matching version of the package the script came from, so renaming the script
-has no effect on what it executes.
-
-For example, suppose you want to use two versions of the ``rst2html`` tool
-provided by the `docutils <http://docutils.sf.net/>`_ package. You might
-first install one version::
-
- easy_install -m docutils==0.3.9
-
-then rename the ``rst2html.py`` to ``r2h_039``, and install another version::
-
- easy_install -m docutils==0.3.10
-
-This will create another ``rst2html.py`` script, this one using docutils
-version 0.3.10 instead of 0.3.9. You now have two scripts, each using a
-different version of the package. (Notice that we used ``-m`` for both
-installations, so that Python won't lock us out of using anything but the most
-recently-installed version of the package.)
-
-
-Executables and Launchers
--------------------------
-
-On Unix systems, scripts are installed with as natural files with a "#!"
-header and no extension and they launch under the Python version indicated in
-the header.
-
-On Windows, there is no mechanism to "execute" files without extensions, so
-EasyInstall provides two techniques to mirror the Unix behavior. The behavior
-is indicated by the SETUPTOOLS_LAUNCHER environment variable, which may be
-"executable" (default) or "natural".
-
-Regardless of the technique used, the script(s) will be installed to a Scripts
-directory (by default in the Python installation directory). It is recommended
-for EasyInstall that you ensure this directory is in the PATH environment
-variable. The easiest way to ensure the Scripts directory is in the PATH is
-to run ``Tools\Scripts\win_add2path.py`` from the Python directory.
-
-Note that instead of changing your ``PATH`` to include the Python scripts
-directory, you can also retarget the installation location for scripts so they
-go on a directory that's already on the ``PATH``. For more information see
-`Command-Line Options`_ and `Configuration Files`_. During installation,
-pass command line options (such as ``--script-dir``) to
-``ez_setup.py`` to control where ``easy_install.exe`` will be installed.
-
-
-Windows Executable Launcher
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-If the "executable" launcher is used, EasyInstall will create a '.exe'
-launcher of the same name beside each installed script (including
-``easy_install`` itself). These small .exe files launch the script of the
-same name using the Python version indicated in the '#!' header.
-
-This behavior is currently default. To force
-the use of executable launchers, set ``SETUPTOOLS_LAUNCHER`` to "executable".
-
-Natural Script Launcher
-~~~~~~~~~~~~~~~~~~~~~~~
-
-EasyInstall also supports deferring to an external launcher such as
-`pylauncher <https://bitbucket.org/pypa/pylauncher>`_ for launching scripts.
-Enable this experimental functionality by setting the
-``SETUPTOOLS_LAUNCHER`` environment variable to "natural". EasyInstall will
-then install scripts as simple
-scripts with a .pya (or .pyw) extension appended. If these extensions are
-associated with the pylauncher and listed in the PATHEXT environment variable,
-these scripts can then be invoked simply and directly just like any other
-executable. This behavior may become default in a future version.
-
-EasyInstall uses the .pya extension instead of simply
-the typical '.py' extension. This distinct extension is necessary to prevent
-Python
-from treating the scripts as importable modules (where name conflicts exist).
-Current releases of pylauncher do not yet associate with .pya files by
-default, but future versions should do so.
-
-
-Tips & Techniques
------------------
-
-Multiple Python Versions
-~~~~~~~~~~~~~~~~~~~~~~~~
-
-EasyInstall installs itself under two names:
-``easy_install`` and ``easy_install-N.N``, where ``N.N`` is the Python version
-used to install it. Thus, if you install EasyInstall for both Python 3.2 and
-2.7, you can use the ``easy_install-3.2`` or ``easy_install-2.7`` scripts to
-install packages for the respective Python version.
-
-Setuptools also supplies easy_install as a runnable module which may be
-invoked using ``python -m easy_install`` for any Python with Setuptools
-installed.
-
-Restricting Downloads with ``--allow-hosts``
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-You can use the ``--allow-hosts`` (``-H``) option to restrict what domains
-EasyInstall will look for links and downloads on. ``--allow-hosts=None``
-prevents downloading altogether. You can also use wildcards, for example
-to restrict downloading to hosts in your own intranet. See the section below
-on `Command-Line Options`_ for more details on the ``--allow-hosts`` option.
-
-By default, there are no host restrictions in effect, but you can change this
-default by editing the appropriate `configuration files`_ and adding:
-
-.. code-block:: ini
-
- [easy_install]
- allow_hosts = *.myintranet.example.com,*.python.org
-
-The above example would then allow downloads only from hosts in the
-``python.org`` and ``myintranet.example.com`` domains, unless overridden on the
-command line.
-
-
-Installing on Un-networked Machines
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Just copy the eggs or source packages you need to a directory on the target
-machine, then use the ``-f`` or ``--find-links`` option to specify that
-directory's location. For example::
-
- easy_install -H None -f somedir SomePackage
-
-will attempt to install SomePackage using only eggs and source packages found
-in ``somedir`` and disallowing all remote access. You should of course make
-sure you have all of SomePackage's dependencies available in somedir.
-
-If you have another machine of the same operating system and library versions
-(or if the packages aren't platform-specific), you can create the directory of
-eggs using a command like this::
-
- easy_install -zmaxd somedir SomePackage
-
-This will tell EasyInstall to put zipped eggs or source packages for
-SomePackage and all its dependencies into ``somedir``, without creating any
-scripts or .pth files. You can then copy the contents of ``somedir`` to the
-target machine. (``-z`` means zipped eggs, ``-m`` means multi-version, which
-prevents .pth files from being used, ``-a`` means to copy all the eggs needed,
-even if they're installed elsewhere on the machine, and ``-d`` indicates the
-directory to place the eggs in.)
-
-You can also build the eggs from local development packages that were installed
-with the ``setup.py develop`` command, by including the ``-l`` option, e.g.::
-
- easy_install -zmaxld somedir SomePackage
-
-This will use locally-available source distributions to build the eggs.
-
-
-Packaging Others' Projects As Eggs
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Need to distribute a package that isn't published in egg form? You can use
-EasyInstall to build eggs for a project. You'll want to use the ``--zip-ok``,
-``--exclude-scripts``, and possibly ``--no-deps`` options (``-z``, ``-x`` and
-``-N``, respectively). Use ``-d`` or ``--install-dir`` to specify the location
-where you'd like the eggs placed. By placing them in a directory that is
-published to the web, you can then make the eggs available for download, either
-in an intranet or to the internet at large.
-
-If someone distributes a package in the form of a single ``.py`` file, you can
-wrap it in an egg by tacking an ``#egg=name-version`` suffix on the file's URL.
-So, something like this::
-
- easy_install -f "http://some.example.com/downloads/foo.py#egg=foo-1.0" foo
-
-will install the package as an egg, and this::
-
- easy_install -zmaxd. \
- -f "http://some.example.com/downloads/foo.py#egg=foo-1.0" foo
-
-will create a ``.egg`` file in the current directory.
-
-
-Creating your own Package Index
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-In addition to local directories and the Python Package Index, EasyInstall can
-find download links on most any web page whose URL is given to the ``-f``
-(``--find-links``) option. In the simplest case, you can simply have a web
-page with links to eggs or Python source packages, even an automatically
-generated directory listing (such as the Apache web server provides).
-
-If you are setting up an intranet site for package downloads, you may want to
-configure the target machines to use your download site by default, adding
-something like this to their `configuration files`_:
-
-.. code-block:: ini
-
- [easy_install]
- find_links = http://mypackages.example.com/somedir/
- http://turbogears.org/download/
- http://peak.telecommunity.com/dist/
-
-As you can see, you can list multiple URLs separated by whitespace, continuing
-on multiple lines if necessary (as long as the subsequent lines are indented.
-
-If you are more ambitious, you can also create an entirely custom package index
-or PyPI mirror. See the ``--index-url`` option under `Command-Line Options`_,
-below, and also the section on `Package Index "API"`_.
-
-
-Password-Protected Sites
-------------------------
-
-If a site you want to download from is password-protected using HTTP "Basic"
-authentication, you can specify your credentials in the URL, like so::
-
- http://some_userid:some_password@some.example.com/some_path/
-
-You can do this with both index page URLs and direct download URLs. As long
-as any HTML pages read by easy_install use *relative* links to point to the
-downloads, the same user ID and password will be used to do the downloading.
-
-Using .pypirc Credentials
--------------------------
-
-In additional to supplying credentials in the URL, ``easy_install`` will also
-honor credentials if present in the .pypirc file. Teams maintaining a private
-repository of packages may already have defined access credentials for
-uploading packages according to the distutils documentation. ``easy_install``
-will attempt to honor those if present. Refer to the distutils documentation
-for Python 2.5 or later for details on the syntax.
-
-Controlling Build Options
-~~~~~~~~~~~~~~~~~~~~~~~~~
-
-EasyInstall respects standard distutils `Configuration Files`_, so you can use
-them to configure build options for packages that it installs from source. For
-example, if you are on Windows using the MinGW compiler, you can configure the
-default compiler by putting something like this:
-
-.. code-block:: ini
-
- [build]
- compiler = mingw32
-
-into the appropriate distutils configuration file. In fact, since this is just
-normal distutils configuration, it will affect any builds using that config
-file, not just ones done by EasyInstall. For example, if you add those lines
-to ``distutils.cfg`` in the ``distutils`` package directory, it will be the
-default compiler for *all* packages you build. See `Configuration Files`_
-below for a list of the standard configuration file locations, and links to
-more documentation on using distutils configuration files.
-
-
-Editing and Viewing Source Packages
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Sometimes a package's source distribution contains additional documentation,
-examples, configuration files, etc., that are not part of its actual code. If
-you want to be able to examine these files, you can use the ``--editable``
-option to EasyInstall, and EasyInstall will look for a source distribution
-or Subversion URL for the package, then download and extract it or check it out
-as a subdirectory of the ``--build-directory`` you specify. If you then wish
-to install the package after editing or configuring it, you can do so by
-rerunning EasyInstall with that directory as the target.
-
-Note that using ``--editable`` stops EasyInstall from actually building or
-installing the package; it just finds, obtains, and possibly unpacks it for
-you. This allows you to make changes to the package if necessary, and to
-either install it in development mode using ``setup.py develop`` (if the
-package uses setuptools, that is), or by running ``easy_install projectdir``
-(where ``projectdir`` is the subdirectory EasyInstall created for the
-downloaded package.
-
-In order to use ``--editable`` (``-e`` for short), you *must* also supply a
-``--build-directory`` (``-b`` for short). The project will be placed in a
-subdirectory of the build directory. The subdirectory will have the same
-name as the project itself, but in all-lowercase. If a file or directory of
-that name already exists, EasyInstall will print an error message and exit.
-
-Also, when using ``--editable``, you cannot use URLs or filenames as arguments.
-You *must* specify project names (and optional version requirements) so that
-EasyInstall knows what directory name(s) to create. If you need to force
-EasyInstall to use a particular URL or filename, you should specify it as a
-``--find-links`` item (``-f`` for short), and then also specify
-the project name, e.g.::
-
- easy_install -eb ~/projects \
- -fhttp://prdownloads.sourceforge.net/ctypes/ctypes-0.9.6.tar.gz?download \
- ctypes==0.9.6
-
-
-Dealing with Installation Conflicts
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-(NOTE: As of 0.6a11, this section is obsolete; it is retained here only so that
-people using older versions of EasyInstall can consult it. As of version
-0.6a11, installation conflicts are handled automatically without deleting the
-old or system-installed packages, and without ignoring the issue. Instead,
-eggs are automatically shifted to the front of ``sys.path`` using special
-code added to the ``easy-install.pth`` file. So, if you are using version
-0.6a11 or better of setuptools, you do not need to worry about conflicts,
-and the following issues do not apply to you.)
-
-EasyInstall installs distributions in a "managed" way, such that each
-distribution can be independently activated or deactivated on ``sys.path``.
-However, packages that were not installed by EasyInstall are "unmanaged",
-in that they usually live all in one directory and cannot be independently
-activated or deactivated.
-
-As a result, if you are using EasyInstall to upgrade an existing package, or
-to install a package with the same name as an existing package, EasyInstall
-will warn you of the conflict. (This is an improvement over ``setup.py
-install``, because the ``distutils`` just install new packages on top of old
-ones, possibly combining two unrelated packages or leaving behind modules that
-have been deleted in the newer version of the package.)
-
-EasyInstall will stop the installation if it detects a conflict
-between an existing, "unmanaged" package, and a module or package in any of
-the distributions you're installing. It will display a list of all of the
-existing files and directories that would need to be deleted for the new
-package to be able to function correctly. To proceed, you must manually
-delete these conflicting files and directories and re-run EasyInstall.
-
-Of course, once you've replaced all of your existing "unmanaged" packages with
-versions managed by EasyInstall, you won't have any more conflicts to worry
-about!
-
-
-Compressed Installation
-~~~~~~~~~~~~~~~~~~~~~~~
-
-EasyInstall tries to install packages in zipped form, if it can. Zipping
-packages can improve Python's overall import performance if you're not using
-the ``--multi-version`` option, because Python processes zipfile entries on
-``sys.path`` much faster than it does directories.
-
-As of version 0.5a9, EasyInstall analyzes packages to determine whether they
-can be safely installed as a zipfile, and then acts on its analysis. (Previous
-versions would not install a package as a zipfile unless you used the
-``--zip-ok`` option.)
-
-The current analysis approach is fairly conservative; it currently looks for:
-
- * Any use of the ``__file__`` or ``__path__`` variables (which should be
- replaced with ``pkg_resources`` API calls)
-
- * Possible use of ``inspect`` functions that expect to manipulate source files
- (e.g. ``inspect.getsource()``)
-
- * Top-level modules that might be scripts used with ``python -m`` (Python 2.4)
-
-If any of the above are found in the package being installed, EasyInstall will
-assume that the package cannot be safely run from a zipfile, and unzip it to
-a directory instead. You can override this analysis with the ``-zip-ok`` flag,
-which will tell EasyInstall to install the package as a zipfile anyway. Or,
-you can use the ``--always-unzip`` flag, in which case EasyInstall will always
-unzip, even if its analysis says the package is safe to run as a zipfile.
-
-Normally, however, it is simplest to let EasyInstall handle the determination
-of whether to zip or unzip, and only specify overrides when needed to work
-around a problem. If you find you need to override EasyInstall's guesses, you
-may want to contact the package author and the EasyInstall maintainers, so that
-they can make appropriate changes in future versions.
-
-(Note: If a package uses ``setuptools`` in its setup script, the package author
-has the option to declare the package safe or unsafe for zipped usage via the
-``zip_safe`` argument to ``setup()``. If the package author makes such a
-declaration, EasyInstall believes the package's author and does not perform its
-own analysis. However, your command-line option, if any, will still override
-the package author's choice.)
-
-
-Reference Manual
-================
-
-Configuration Files
--------------------
-
-(New in 0.4a2)
-
-You may specify default options for EasyInstall using the standard
-distutils configuration files, under the command heading ``easy_install``.
-EasyInstall will look first for a ``setup.cfg`` file in the current directory,
-then a ``~/.pydistutils.cfg`` or ``$HOME\\pydistutils.cfg`` (on Unix-like OSes
-and Windows, respectively), and finally a ``distutils.cfg`` file in the
-``distutils`` package directory. Here's a simple example:
-
-.. code-block:: ini
-
- [easy_install]
-
- # set the default location to install packages
- install_dir = /home/me/lib/python
-
- # Notice that indentation can be used to continue an option
- # value; this is especially useful for the "--find-links"
- # option, which tells easy_install to use download links on
- # these pages before consulting PyPI:
- #
- find_links = http://sqlobject.org/
- http://peak.telecommunity.com/dist/
-
-In addition to accepting configuration for its own options under
-``[easy_install]``, EasyInstall also respects defaults specified for other
-distutils commands. For example, if you don't set an ``install_dir`` for
-``[easy_install]``, but *have* set an ``install_lib`` for the ``[install]``
-command, this will become EasyInstall's default installation directory. Thus,
-if you are already using distutils configuration files to set default install
-locations, build options, etc., EasyInstall will respect your existing settings
-until and unless you override them explicitly in an ``[easy_install]`` section.
-
-For more information, see also the current Python documentation on the `use and
-location of distutils configuration files <https://docs.python.org/install/index.html#inst-config-files>`_.
-
-Notice that ``easy_install`` will use the ``setup.cfg`` from the current
-working directory only if it was triggered from ``setup.py`` through the
-``install_requires`` option. The standalone command will not use that file.
-
-Command-Line Options
---------------------
-
-``--zip-ok, -z``
- Install all packages as zip files, even if they are marked as unsafe for
- running as a zipfile. This can be useful when EasyInstall's analysis
- of a non-setuptools package is too conservative, but keep in mind that
- the package may not work correctly. (Changed in 0.5a9; previously this
- option was required in order for zipped installation to happen at all.)
-
-``--always-unzip, -Z``
- Don't install any packages as zip files, even if the packages are marked
- as safe for running as a zipfile. This can be useful if a package does
- something unsafe, but not in a way that EasyInstall can easily detect.
- EasyInstall's default analysis is currently very conservative, however, so
- you should only use this option if you've had problems with a particular
- package, and *after* reporting the problem to the package's maintainer and
- to the EasyInstall maintainers.
-
- (Note: the ``-z/-Z`` options only affect the installation of newly-built
- or downloaded packages that are not already installed in the target
- directory; if you want to convert an existing installed version from
- zipped to unzipped or vice versa, you'll need to delete the existing
- version first, and re-run EasyInstall.)
-
-``--multi-version, -m``
- "Multi-version" mode. Specifying this option prevents ``easy_install`` from
- adding an ``easy-install.pth`` entry for the package being installed, and
- if an entry for any version the package already exists, it will be removed
- upon successful installation. In multi-version mode, no specific version of
- the package is available for importing, unless you use
- ``pkg_resources.require()`` to put it on ``sys.path``. This can be as
- simple as::
-
- from pkg_resources import require
- require("SomePackage", "OtherPackage", "MyPackage")
-
- which will put the latest installed version of the specified packages on
- ``sys.path`` for you. (For more advanced uses, like selecting specific
- versions and enabling optional dependencies, see the ``pkg_resources`` API
- doc.)
-
- Changed in 0.6a10: this option is no longer silently enabled when
- installing to a non-PYTHONPATH, non-"site" directory. You must always
- explicitly use this option if you want it to be active.
-
-``--upgrade, -U`` (New in 0.5a4)
- By default, EasyInstall only searches online if a project/version
- requirement can't be met by distributions already installed
- on sys.path or the installation directory. However, if you supply the
- ``--upgrade`` or ``-U`` flag, EasyInstall will always check the package
- index and ``--find-links`` URLs before selecting a version to install. In
- this way, you can force EasyInstall to use the latest available version of
- any package it installs (subject to any version requirements that might
- exclude such later versions).
-
-``--install-dir=DIR, -d DIR``
- Set the installation directory. It is up to you to ensure that this
- directory is on ``sys.path`` at runtime, and to use
- ``pkg_resources.require()`` to enable the installed package(s) that you
- need.
-
- (New in 0.4a2) If this option is not directly specified on the command line
- or in a distutils configuration file, the distutils default installation
- location is used. Normally, this would be the ``site-packages`` directory,
- but if you are using distutils configuration files, setting things like
- ``prefix`` or ``install_lib``, then those settings are taken into
- account when computing the default installation directory, as is the
- ``--prefix`` option.
-
-``--script-dir=DIR, -s DIR``
- Set the script installation directory. If you don't supply this option
- (via the command line or a configuration file), but you *have* supplied
- an ``--install-dir`` (via command line or config file), then this option
- defaults to the same directory, so that the scripts will be able to find
- their associated package installation. Otherwise, this setting defaults
- to the location where the distutils would normally install scripts, taking
- any distutils configuration file settings into account.
-
-``--exclude-scripts, -x``
- Don't install scripts. This is useful if you need to install multiple
- versions of a package, but do not want to reset the version that will be
- run by scripts that are already installed.
-
-``--user`` (New in 0.6.11)
- Use the user-site-packages as specified in :pep:`370`
- instead of the global site-packages.
-
-``--always-copy, -a`` (New in 0.5a4)
- Copy all needed distributions to the installation directory, even if they
- are already present in a directory on sys.path. In older versions of
- EasyInstall, this was the default behavior, but now you must explicitly
- request it. By default, EasyInstall will no longer copy such distributions
- from other sys.path directories to the installation directory, unless you
- explicitly gave the distribution's filename on the command line.
-
- Note that as of 0.6a10, using this option excludes "system" and
- "development" eggs from consideration because they can't be reliably
- copied. This may cause EasyInstall to choose an older version of a package
- than what you expected, or it may cause downloading and installation of a
- fresh copy of something that's already installed. You will see warning
- messages for any eggs that EasyInstall skips, before it falls back to an
- older version or attempts to download a fresh copy.
-
-``--find-links=URLS_OR_FILENAMES, -f URLS_OR_FILENAMES``
- Scan the specified "download pages" or directories for direct links to eggs
- or other distributions. Any existing file or directory names or direct
- download URLs are immediately added to EasyInstall's search cache, and any
- indirect URLs (ones that don't point to eggs or other recognized archive
- formats) are added to a list of additional places to search for download
- links. As soon as EasyInstall has to go online to find a package (either
- because it doesn't exist locally, or because ``--upgrade`` or ``-U`` was
- used), the specified URLs will be downloaded and scanned for additional
- direct links.
-
- Eggs and archives found by way of ``--find-links`` are only downloaded if
- they are needed to meet a requirement specified on the command line; links
- to unneeded packages are ignored.
-
- If all requested packages can be found using links on the specified
- download pages, the Python Package Index will not be consulted unless you
- also specified the ``--upgrade`` or ``-U`` option.
-
- (Note: if you want to refer to a local HTML file containing links, you must
- use a ``file:`` URL, as filenames that do not refer to a directory, egg, or
- archive are ignored.)
-
- You may specify multiple URLs or file/directory names with this option,
- separated by whitespace. Note that on the command line, you will probably
- have to surround the URL list with quotes, so that it is recognized as a
- single option value. You can also specify URLs in a configuration file;
- see `Configuration Files`_, above.
-
- Changed in 0.6a10: previously all URLs and directories passed to this
- option were scanned as early as possible, but from 0.6a10 on, only
- directories and direct archive links are scanned immediately; URLs are not
- retrieved unless a package search was already going to go online due to a
- package not being available locally, or due to the use of the ``--update``
- or ``-U`` option.
-
-``--no-find-links`` Blocks the addition of any link.
- This parameter is useful if you want to avoid adding links defined in a
- project easy_install is installing (whether it's a requested project or a
- dependency). When used, ``--find-links`` is ignored.
-
- Added in Distribute 0.6.11 and Setuptools 0.7.
-
-``--index-url=URL, -i URL`` (New in 0.4a1; default changed in 0.6c7)
- Specifies the base URL of the Python Package Index. The default is
- https://pypi.org/simple/ if not specified. When a package is requested
- that is not locally available or linked from a ``--find-links`` download
- page, the package index will be searched for download pages for the needed
- package, and those download pages will be searched for links to download
- an egg or source distribution.
-
-``--editable, -e`` (New in 0.6a1)
- Only find and download source distributions for the specified projects,
- unpacking them to subdirectories of the specified ``--build-directory``.
- EasyInstall will not actually build or install the requested projects or
- their dependencies; it will just find and extract them for you. See
- `Editing and Viewing Source Packages`_ above for more details.
-
-``--build-directory=DIR, -b DIR`` (UPDATED in 0.6a1)
- Set the directory used to build source packages. If a package is built
- from a source distribution or checkout, it will be extracted to a
- subdirectory of the specified directory. The subdirectory will have the
- same name as the extracted distribution's project, but in all-lowercase.
- If a file or directory of that name already exists in the given directory,
- a warning will be printed to the console, and the build will take place in
- a temporary directory instead.
-
- This option is most useful in combination with the ``--editable`` option,
- which forces EasyInstall to *only* find and extract (but not build and
- install) source distributions. See `Editing and Viewing Source Packages`_,
- above, for more information.
-
-``--verbose, -v, --quiet, -q`` (New in 0.4a4)
- Control the level of detail of EasyInstall's progress messages. The
- default detail level is "info", which prints information only about
- relatively time-consuming operations like running a setup script, unpacking
- an archive, or retrieving a URL. Using ``-q`` or ``--quiet`` drops the
- detail level to "warn", which will only display installation reports,
- warnings, and errors. Using ``-v`` or ``--verbose`` increases the detail
- level to include individual file-level operations, link analysis messages,
- and distutils messages from any setup scripts that get run. If you include
- the ``-v`` option more than once, the second and subsequent uses are passed
- down to any setup scripts, increasing the verbosity of their reporting as
- well.
-
-``--dry-run, -n`` (New in 0.4a4)
- Don't actually install the package or scripts. This option is passed down
- to any setup scripts run, so packages should not actually build either.
- This does *not* skip downloading, nor does it skip extracting source
- distributions to a temporary/build directory.
-
-``--optimize=LEVEL``, ``-O LEVEL`` (New in 0.4a4)
- If you are installing from a source distribution, and are *not* using the
- ``--zip-ok`` option, this option controls the optimization level for
- compiling installed ``.py`` files to ``.pyo`` files. It does not affect
- the compilation of modules contained in ``.egg`` files, only those in
- ``.egg`` directories. The optimization level can be set to 0, 1, or 2;
- the default is 0 (unless it's set under ``install`` or ``install_lib`` in
- one of your distutils configuration files).
-
-``--record=FILENAME`` (New in 0.5a4)
- Write a record of all installed files to FILENAME. This is basically the
- same as the same option for the standard distutils "install" command, and
- is included for compatibility with tools that expect to pass this option
- to "setup.py install".
-
-``--site-dirs=DIRLIST, -S DIRLIST`` (New in 0.6a1)
- Specify one or more custom "site" directories (separated by commas).
- "Site" directories are directories where ``.pth`` files are processed, such
- as the main Python ``site-packages`` directory. As of 0.6a10, EasyInstall
- automatically detects whether a given directory processes ``.pth`` files
- (or can be made to do so), so you should not normally need to use this
- option. It is is now only necessary if you want to override EasyInstall's
- judgment and force an installation directory to be treated as if it
- supported ``.pth`` files.
-
-``--no-deps, -N`` (New in 0.6a6)
- Don't install any dependencies. This is intended as a convenience for
- tools that wrap eggs in a platform-specific packaging system. (We don't
- recommend that you use it for anything else.)
-
-``--allow-hosts=PATTERNS, -H PATTERNS`` (New in 0.6a6)
- Restrict downloading and spidering to hosts matching the specified glob
- patterns. E.g. ``-H *.python.org`` restricts web access so that only
- packages listed and downloadable from machines in the ``python.org``
- domain. The glob patterns must match the *entire* user/host/port section of
- the target URL(s). For example, ``*.python.org`` will NOT accept a URL
- like ``http://python.org/foo`` or ``http://www.python.org:8080/``.
- Multiple patterns can be specified by separating them with commas. The
- default pattern is ``*``, which matches anything.
-
- In general, this option is mainly useful for blocking EasyInstall's web
- access altogether (e.g. ``-Hlocalhost``), or to restrict it to an intranet
- or other trusted site. EasyInstall will do the best it can to satisfy
- dependencies given your host restrictions, but of course can fail if it
- can't find suitable packages. EasyInstall displays all blocked URLs, so
- that you can adjust your ``--allow-hosts`` setting if it is more strict
- than you intended. Some sites may wish to define a restrictive default
- setting for this option in their `configuration files`_, and then manually
- override the setting on the command line as needed.
-
-``--prefix=DIR`` (New in 0.6a10)
- Use the specified directory as a base for computing the default
- installation and script directories. On Windows, the resulting default
- directories will be ``prefix\\Lib\\site-packages`` and ``prefix\\Scripts``,
- while on other platforms the defaults will be
- ``prefix/lib/python2.X/site-packages`` (with the appropriate version
- substituted) for libraries and ``prefix/bin`` for scripts.
-
- Note that the ``--prefix`` option only sets the *default* installation and
- script directories, and does not override the ones set on the command line
- or in a configuration file.
-
-``--local-snapshots-ok, -l`` (New in 0.6c6)
- Normally, EasyInstall prefers to only install *released* versions of
- projects, not in-development ones, because such projects may not
- have a currently-valid version number. So, it usually only installs them
- when their ``setup.py`` directory is explicitly passed on the command line.
-
- However, if this option is used, then any in-development projects that were
- installed using the ``setup.py develop`` command, will be used to build
- eggs, effectively upgrading the "in-development" project to a snapshot
- release. Normally, this option is used only in conjunction with the
- ``--always-copy`` option to create a distributable snapshot of every egg
- needed to run an application.
-
- Note that if you use this option, you must make sure that there is a valid
- version number (such as an SVN revision number tag) for any in-development
- projects that may be used, as otherwise EasyInstall may not be able to tell
- what version of the project is "newer" when future installations or
- upgrades are attempted.
-
-
-.. _non-root installation:
-
-Custom Installation Locations
------------------------------
-
-By default, EasyInstall installs python packages into Python's main ``site-packages`` directory,
-and manages them using a custom ``.pth`` file in that same directory.
-
-Very often though, a user or developer wants ``easy_install`` to install and manage python packages
-in an alternative location, usually for one of 3 reasons:
-
-1. They don't have access to write to the main Python site-packages directory.
-
-2. They want a user-specific stash of packages, that is not visible to other users.
-
-3. They want to isolate a set of packages to a specific python application, usually to minimize
- the possibility of version conflicts.
-
-Historically, there have been many approaches to achieve custom installation.
-The following section lists only the easiest and most relevant approaches [1]_.
-
-`Use the "--user" option`_
-
-`Use the "--user" option and customize "PYTHONUSERBASE"`_
-
-`Use "virtualenv"`_
-
-.. [1] There are older ways to achieve custom installation using various ``easy_install`` and ``setup.py install`` options, combined with ``PYTHONPATH`` and/or ``PYTHONUSERBASE`` alterations, but all of these are effectively deprecated by the User scheme brought in by `PEP-370`_.
-
-.. _PEP-370: http://www.python.org/dev/peps/pep-0370/
-
-
-Use the "--user" option
-~~~~~~~~~~~~~~~~~~~~~~~
-Python provides a User scheme for installation, which means that all
-python distributions support an alternative install location that is specific to a user [3]_.
-The Default location for each OS is explained in the python documentation
-for the ``site.USER_BASE`` variable. This mode of installation can be turned on by
-specifying the ``--user`` option to ``setup.py install`` or ``easy_install``.
-This approach serves the need to have a user-specific stash of packages.
-
-.. [3] Prior to the User scheme, there was the Home scheme, which is still available, but requires more effort than the User scheme to get packages recognized.
-
-Use the "--user" option and customize "PYTHONUSERBASE"
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-The User scheme install location can be customized by setting the ``PYTHONUSERBASE`` environment
-variable, which updates the value of ``site.USER_BASE``. To isolate packages to a specific
-application, simply set the OS environment of that application to a specific value of
-``PYTHONUSERBASE``, that contains just those packages.
-
-Use "virtualenv"
-~~~~~~~~~~~~~~~~
-"virtualenv" is a 3rd-party python package that effectively "clones" a python installation, thereby
-creating an isolated location to install packages. The evolution of "virtualenv" started before the existence
-of the User installation scheme. "virtualenv" provides a version of ``easy_install`` that is
-scoped to the cloned python install and is used in the normal way. "virtualenv" does offer various features
-that the User installation scheme alone does not provide, e.g. the ability to hide the main python site-packages.
-
-Please refer to the `virtualenv`_ documentation for more details.
-
-.. _virtualenv: https://pypi.org/project/virtualenv/
-
-
-
-Package Index "API"
--------------------
-
-Custom package indexes (and PyPI) must follow the following rules for
-EasyInstall to be able to look up and download packages:
-
-1. Except where stated otherwise, "pages" are HTML or XHTML, and "links"
- refer to ``href`` attributes.
-
-2. Individual project version pages' URLs must be of the form
- ``base/projectname/version``, where ``base`` is the package index's base URL.
-
-3. Omitting the ``/version`` part of a project page's URL (but keeping the
- trailing ``/``) should result in a page that is either:
-
- a) The single active version of that project, as though the version had been
- explicitly included, OR
-
- b) A page with links to all of the active version pages for that project.
-
-4. Individual project version pages should contain direct links to downloadable
- distributions where possible. It is explicitly permitted for a project's
- "long_description" to include URLs, and these should be formatted as HTML
- links by the package index, as EasyInstall does no special processing to
- identify what parts of a page are index-specific and which are part of the
- project's supplied description.
-
-5. Where available, MD5 information should be added to download URLs by
- appending a fragment identifier of the form ``#md5=...``, where ``...`` is
- the 32-character hex MD5 digest. EasyInstall will verify that the
- downloaded file's MD5 digest matches the given value.
-
-6. Individual project version pages should identify any "homepage" or
- "download" URLs using ``rel="homepage"`` and ``rel="download"`` attributes
- on the HTML elements linking to those URLs. Use of these attributes will
- cause EasyInstall to always follow the provided links, unless it can be
- determined by inspection that they are downloadable distributions. If the
- links are not to downloadable distributions, they are retrieved, and if they
- are HTML, they are scanned for download links. They are *not* scanned for
- additional "homepage" or "download" links, as these are only processed for
- pages that are part of a package index site.
-
-7. The root URL of the index, if retrieved with a trailing ``/``, must result
- in a page containing links to *all* projects' active version pages.
-
- (Note: This requirement is a workaround for the absence of case-insensitive
- ``safe_name()`` matching of project names in URL paths. If project names are
- matched in this fashion (e.g. via the PyPI server, mod_rewrite, or a similar
- mechanism), then it is not necessary to include this all-packages listing
- page.)
-
-8. If a package index is accessed via a ``file://`` URL, then EasyInstall will
- automatically use ``index.html`` files, if present, when trying to read a
- directory with a trailing ``/`` on the URL.
diff --git a/docs/ez_setup.txt b/docs/ez_setup.txt
deleted file mode 100644
index 0126fee3..00000000
--- a/docs/ez_setup.txt
+++ /dev/null
@@ -1,195 +0,0 @@
-:orphan:
-
-``ez_setup`` distribution guide
-===============================
-
-Using ``setuptools``... Without bundling it!
----------------------------------------------
-
-.. warning:: **ez_setup** is deprecated in favor of PIP with **PEP-518** support.
-
-.. _ez_setup.py: https://bootstrap.pypa.io/ez_setup.py
-
-.. _EasyInstall Installation Instructions: easy_install.html
-
-.. _Custom Installation Locations: easy_install.html
-
-Your users might not have ``setuptools`` installed on their machines, or even
-if they do, it might not be the right version. Fixing this is easy; just
-download `ez_setup.py`_, and put it in the same directory as your ``setup.py``
-script. (Be sure to add it to your revision control system, too.) Then add
-these two lines to the very top of your setup script, before the script imports
-anything from setuptools:
-
-.. code-block:: python
-
- import ez_setup
- ez_setup.use_setuptools()
-
-That's it. The ``ez_setup`` module will automatically download a matching
-version of ``setuptools`` from PyPI, if it isn't present on the target system.
-Whenever you install an updated version of setuptools, you should also update
-your projects' ``ez_setup.py`` files, so that a matching version gets installed
-on the target machine(s).
-
-(By the way, if you need to distribute a specific version of ``setuptools``,
-you can specify the exact version and base download URL as parameters to the
-``use_setuptools()`` function. See the function's docstring for details.)
-
-
-What Your Users Should Know
----------------------------
-
-In general, a setuptools-based project looks just like any distutils-based
-project -- as long as your users have an internet connection and are installing
-to ``site-packages``, that is. But for some users, these conditions don't
-apply, and they may become frustrated if this is their first encounter with
-a setuptools-based project. To keep these users happy, you should review the
-following topics in your project's installation instructions, if they are
-relevant to your project and your target audience isn't already familiar with
-setuptools and ``easy_install``.
-
-Network Access
- If your project is using ``ez_setup``, you should inform users of the
- need to either have network access, or to preinstall the correct version of
- setuptools using the `EasyInstall installation instructions`_. Those
- instructions also have tips for dealing with firewalls as well as how to
- manually download and install setuptools.
-
-Custom Installation Locations
- You should inform your users that if they are installing your project to
- somewhere other than the main ``site-packages`` directory, they should
- first install setuptools using the instructions for `Custom Installation
- Locations`_, before installing your project.
-
-Your Project's Dependencies
- If your project depends on other projects that may need to be downloaded
- from PyPI or elsewhere, you should list them in your installation
- instructions, or tell users how to find out what they are. While most
- users will not need this information, any users who don't have unrestricted
- internet access may have to find, download, and install the other projects
- manually. (Note, however, that they must still install those projects
- using ``easy_install``, or your project will not know they are installed,
- and your setup script will try to download them again.)
-
- If you want to be especially friendly to users with limited network access,
- you may wish to build eggs for your project and its dependencies, making
- them all available for download from your site, or at least create a page
- with links to all of the needed eggs. In this way, users with limited
- network access can manually download all the eggs to a single directory,
- then use the ``-f`` option of ``easy_install`` to specify the directory
- to find eggs in. Users who have full network access can just use ``-f``
- with the URL of your download page, and ``easy_install`` will find all the
- needed eggs using your links directly. This is also useful when your
- target audience isn't able to compile packages (e.g. most Windows users)
- and your package or some of its dependencies include C code.
-
-Revision Control System Users and Co-Developers
- Users and co-developers who are tracking your in-development code using
- a revision control system should probably read this manual's sections
- regarding such development. Alternately, you may wish to create a
- quick-reference guide containing the tips from this manual that apply to
- your particular situation. For example, if you recommend that people use
- ``setup.py develop`` when tracking your in-development code, you should let
- them know that this needs to be run after every update or commit.
-
- Similarly, if you remove modules or data files from your project, you
- should remind them to run ``setup.py clean --all`` and delete any obsolete
- ``.pyc`` or ``.pyo``. (This tip applies to the distutils in general, not
- just setuptools, but not everybody knows about them; be kind to your users
- by spelling out your project's best practices rather than leaving them
- guessing.)
-
-Creating System Packages
- Some users want to manage all Python packages using a single package
- manager, and sometimes that package manager isn't ``easy_install``!
- Setuptools currently supports ``bdist_rpm``, ``bdist_wininst``, and
- ``bdist_dumb`` formats for system packaging. If a user has a locally-
- installed "bdist" packaging tool that internally uses the distutils
- ``install`` command, it should be able to work with ``setuptools``. Some
- examples of "bdist" formats that this should work with include the
- ``bdist_nsi`` and ``bdist_msi`` formats for Windows.
-
- However, packaging tools that build binary distributions by running
- ``setup.py install`` on the command line or as a subprocess will require
- modification to work with setuptools. They should use the
- ``--single-version-externally-managed`` option to the ``install`` command,
- combined with the standard ``--root`` or ``--record`` options.
- See the `install command`_ documentation below for more details. The
- ``bdist_deb`` command is an example of a command that currently requires
- this kind of patching to work with setuptools.
-
- Please note that building system packages may require you to install
- some system software, for example ``bdist_rpm`` requires the ``rpmbuild``
- command to be installed.
-
- If you or your users have a problem building a usable system package for
- your project, please report the problem via the mailing list so that
- either the "bdist" tool in question or setuptools can be modified to
- resolve the issue.
-
-Your users might not have ``setuptools`` installed on their machines, or even
-if they do, it might not be the right version. Fixing this is easy; just
-download `ez_setup.py`_, and put it in the same directory as your ``setup.py``
-script. (Be sure to add it to your revision control system, too.) Then add
-these two lines to the very top of your setup script, before the script imports
-anything from setuptools:
-
-.. code-block:: python
-
- import ez_setup
- ez_setup.use_setuptools()
-
-That's it. The ``ez_setup`` module will automatically download a matching
-version of ``setuptools`` from PyPI, if it isn't present on the target system.
-Whenever you install an updated version of setuptools, you should also update
-your projects' ``ez_setup.py`` files, so that a matching version gets installed
-on the target machine(s).
-
-(By the way, if you need to distribute a specific version of ``setuptools``,
-you can specify the exact version and base download URL as parameters to the
-``use_setuptools()`` function. See the function's docstring for details.)
-
-.. _install command:
-
-``install`` - Run ``easy_install`` or old-style installation
-============================================================
-
-The setuptools ``install`` command is basically a shortcut to run the
-``easy_install`` command on the current project. However, for convenience
-in creating "system packages" of setuptools-based projects, you can also
-use this option:
-
-``--single-version-externally-managed``
- This boolean option tells the ``install`` command to perform an "old style"
- installation, with the addition of an ``.egg-info`` directory so that the
- installed project will still have its metadata available and operate
- normally. If you use this option, you *must* also specify the ``--root``
- or ``--record`` options (or both), because otherwise you will have no way
- to identify and remove the installed files.
-
-This option is automatically in effect when ``install`` is invoked by another
-distutils command, so that commands like ``bdist_wininst`` and ``bdist_rpm``
-will create system packages of eggs. It is also automatically in effect if
-you specify the ``--root`` option.
-
-
-``install_egg_info`` - Install an ``.egg-info`` directory in ``site-packages``
-==============================================================================
-
-Setuptools runs this command as part of ``install`` operations that use the
-``--single-version-externally-managed`` options. You should not invoke it
-directly; it is documented here for completeness and so that distutils
-extensions such as system package builders can make use of it. This command
-has only one option:
-
-``--install-dir=DIR, -d DIR``
- The parent directory where the ``.egg-info`` directory will be placed.
- Defaults to the same as the ``--install-dir`` option specified for the
- ``install_lib`` command, which is usually the system ``site-packages``
- directory.
-
-This command assumes that the ``egg_info`` command has been given valid options
-via the command line or ``setup.cfg``, as it will invoke the ``egg_info``
-command and use its options to locate the project's source ``.egg-info``
-directory.
diff --git a/docs/formats.txt b/docs/formats.txt
index a182eb99..6c0456de 100644
--- a/docs/formats.txt
+++ b/docs/formats.txt
@@ -299,11 +299,8 @@ specified by the ``setup_requires`` parameter to the Distribution.
A list of dependency URLs, one per line, as specified using the
``dependency_links`` keyword to ``setup()``. These may be direct
download URLs, or the URLs of web pages containing direct download
-links, and will be used by EasyInstall to find dependencies, as though
-the user had manually provided them via the ``--find-links`` command
-line option. Please see the setuptools manual and EasyInstall manual
-for more information on specifying this option, and for information on
-how EasyInstall processes ``--find-links`` URLs.
+links. Please see the setuptools manual for more information on
+specifying this option.
``depends.txt`` -- Obsolete, do not create!
diff --git a/docs/index.txt b/docs/index.txt
index 13a46e74..c251260d 100644
--- a/docs/index.txt
+++ b/docs/index.txt
@@ -21,5 +21,4 @@ Documentation content:
python3
development
roadmap
- Deprecated: Easy Install <easy_install>
history
diff --git a/docs/pkg_resources.txt b/docs/pkg_resources.txt
index 806f1b14..b887a923 100644
--- a/docs/pkg_resources.txt
+++ b/docs/pkg_resources.txt
@@ -245,8 +245,8 @@ abbreviation for ``pkg_resources.working_set.require()``:
interactive interpreter hacking than for production use. If you're creating
an actual library or application, it's strongly recommended that you create
a "setup.py" script using ``setuptools``, and declare all your requirements
- there. That way, tools like EasyInstall can automatically detect what
- requirements your package has, and deal with them accordingly.
+ there. That way, tools like pip can automatically detect what requirements
+ your package has, and deal with them accordingly.
Note that calling ``require('SomePackage')`` will not install
``SomePackage`` if it isn't already present. If you need to do this, you
@@ -611,9 +611,9 @@ Requirements Parsing
or activation of both Report-O-Rama and any libraries it needs in order to
provide PDF support. For example, you could use::
- easy_install.py Report-O-Rama[PDF]
+ pip install Report-O-Rama[PDF]
- To install the necessary packages using the EasyInstall program, or call
+ To install the necessary packages using pip, or call
``pkg_resources.require('Report-O-Rama[PDF]')`` to add the necessary
distributions to sys.path at runtime.
@@ -1843,9 +1843,9 @@ History
because it isn't necessarily a filesystem path (and hasn't been for some
time now). The ``location`` of ``Distribution`` objects in the filesystem
should always be normalized using ``pkg_resources.normalize_path()``; all
- of the setuptools and EasyInstall code that generates distributions from
- the filesystem (including ``Distribution.from_filename()``) ensure this
- invariant, but if you use a more generic API like ``Distribution()`` or
+ of the setuptools' code that generates distributions from the filesystem
+ (including ``Distribution.from_filename()``) ensure this invariant, but if
+ you use a more generic API like ``Distribution()`` or
``Distribution.from_location()`` you should take care that you don't
create a distribution with an un-normalized filesystem path.
diff --git a/docs/setuptools.txt b/docs/setuptools.txt
index 26a3044e..0dda5622 100644
--- a/docs/setuptools.txt
+++ b/docs/setuptools.txt
@@ -8,23 +8,10 @@ distribute Python packages, especially ones that have dependencies on other
packages.
Packages built and distributed using ``setuptools`` look to the user like
-ordinary Python packages based on the ``distutils``. Your users don't need to
-install or even know about setuptools in order to use them, and you don't
-have to include the entire setuptools package in your distributions. By
-including just a single `bootstrap module`_ (a 12K .py file), your package will
-automatically download and install ``setuptools`` if the user is building your
-package from source and doesn't have a suitable version already installed.
-
-.. _bootstrap module: https://bootstrap.pypa.io/ez_setup.py
+ordinary Python packages based on the ``distutils``.
Feature Highlights:
-* Automatically find/download/install/upgrade dependencies at build time using
- the `EasyInstall tool <easy_install.html>`_,
- which supports downloading via HTTP, FTP, Subversion, and SourceForge, and
- automatically scans web pages linked from PyPI to find download links. (It's
- the closest thing to CPAN currently available for Python.)
-
* Create `Python Eggs <http://peak.telecommunity.com/DevCenter/PythonEggs>`_ -
a single-file importable distribution format
@@ -62,8 +49,6 @@ Feature Highlights:
.. contents:: **Table of Contents**
-.. _ez_setup.py: `bootstrap module`_
-
-----------------
Developer's Guide
@@ -73,10 +58,6 @@ Developer's Guide
Installing ``setuptools``
=========================
-.. _EasyInstall Installation Instructions: easy_install.html
-
-.. _Custom Installation Locations: easy_install.html
-
.. _Installing Packages: https://packaging.python.org/tutorials/installing-packages/
To install the latest version of setuptools, use::
@@ -160,7 +141,7 @@ Specifying Your Project's Version
Setuptools can work well with most versioning schemes; there are, however, a
few special things to watch out for, in order to ensure that setuptools and
-EasyInstall can always tell what version of your package is newer than another
+other tools can always tell what version of your package is newer than another
version. Knowing these things will also help you correctly specify what
versions of other projects your project depends on.
@@ -301,11 +282,11 @@ unless you need the associated ``setuptools`` feature.
``setup_requires``
A string or list of strings specifying what other distributions need to
be present in order for the *setup script* to run. ``setuptools`` will
- attempt to obtain these (even going so far as to download them using
- ``EasyInstall``) before processing the rest of the setup script or commands.
- This argument is needed if you are using distutils extensions as part of
- your build process; for example, extensions that process setup() arguments
- and turn them into EGG-INFO metadata files.
+ attempt to obtain these (using pip if available) before processing the
+ rest of the setup script or commands. This argument is needed if you
+ are using distutils extensions as part of your build process; for
+ example, extensions that process setup() arguments and turn them into
+ EGG-INFO metadata files.
(Note: projects listed in ``setup_requires`` will NOT be automatically
installed on the system where the setup script is being run. They are
@@ -318,8 +299,7 @@ unless you need the associated ``setuptools`` feature.
A list of strings naming URLs to be searched when satisfying dependencies.
These links will be used if needed to install packages specified by
``setup_requires`` or ``tests_require``. They will also be written into
- the egg's metadata for use by tools like EasyInstall to use when installing
- an ``.egg`` file.
+ the egg's metadata for use during install by tools that support them.
``namespace_packages``
A list of strings naming the project's "namespace packages". A namespace
@@ -346,16 +326,19 @@ unless you need the associated ``setuptools`` feature.
specified test suite, e.g. via ``setup.py test``. See the section on the
`test`_ command below for more details.
+ New in 41.5.0: Deprecated the test command.
+
``tests_require``
If your project's tests need one or more additional packages besides those
needed to install it, you can use this option to specify them. It should
be a string or list of strings specifying what other distributions need to
be present for the package's tests to run. When you run the ``test``
- command, ``setuptools`` will attempt to obtain these (even going
- so far as to download them using ``EasyInstall``). Note that these
- required projects will *not* be installed on the system where the tests
- are run, but only downloaded to the project's setup directory if they're
- not already installed locally.
+ command, ``setuptools`` will attempt to obtain these (using pip if
+ available). Note that these required projects will *not* be installed on
+ the system where the tests are run, but only downloaded to the project's setup
+ directory if they're not already installed locally.
+
+ New in 41.5.0: Deprecated the test command.
.. _test_loader:
@@ -380,6 +363,8 @@ unless you need the associated ``setuptools`` feature.
as long as you use the ``tests_require`` option to ensure that the package
containing the loader class is available when the ``test`` command is run.
+ New in 41.5.0: Deprecated the test command.
+
``eager_resources``
A list of strings naming resources that should be extracted together, if
any of them is needed, or if any C extensions included in the project are
@@ -552,11 +537,12 @@ script called ``baz``, you might do something like this::
)
When this project is installed on non-Windows platforms (using "setup.py
-install", "setup.py develop", or by using EasyInstall), a set of ``foo``,
-``bar``, and ``baz`` scripts will be installed that import ``main_func`` and
-``some_func`` from the specified modules. The functions you specify are called
-with no arguments, and their return value is passed to ``sys.exit()``, so you
-can return an errorlevel or message to print to stderr.
+install", "setup.py develop", or with pip), a set of ``foo``, ``bar``,
+and ``baz`` scripts will be installed that import ``main_func`` and
+``some_func`` from the specified modules. The functions you specify are
+called with no arguments, and their return value is passed to
+``sys.exit()``, so you can return an errorlevel or message to print to
+stderr.
On Windows, a set of ``foo.exe``, ``bar.exe``, and ``baz.exe`` launchers are
created, alongside a set of ``foo.py``, ``bar.py``, and ``baz.pyw`` files. The
@@ -596,10 +582,6 @@ Python must be available via the ``PATH`` environment variable, under its
"long" name. That is, if the egg is built for Python 2.3, there must be a
``python2.3`` executable present in a directory on ``PATH``.
-This feature is primarily intended to support ez_setup the installation of
-setuptools itself on non-Windows platforms, but may also be useful for other
-projects as well.
-
IMPORTANT NOTE: Eggs with an "eggsecutable" header cannot be renamed, or
invoked via symlinks. They *must* be invoked using their original filename, in
order to ensure that, once running, ``pkg_resources`` will know what project
@@ -613,7 +595,7 @@ Declaring Dependencies
``setuptools`` supports automatically installing dependencies when a package is
installed, and including information about dependencies in Python Eggs (so that
-package management tools like EasyInstall can use the information).
+package management tools like pip can use the information).
``setuptools`` and ``pkg_resources`` use a common syntax for specifying a
project's required dependencies. This syntax consists of a project's PyPI
@@ -652,10 +634,9 @@ requirement in a string, each requirement must begin on a new line.
This has three effects:
-1. When your project is installed, either by using EasyInstall, ``setup.py
- install``, or ``setup.py develop``, all of the dependencies not already
- installed will be located (via PyPI), downloaded, built (if necessary),
- and installed.
+1. When your project is installed, either by using pip, ``setup.py install``,
+ or ``setup.py develop``, all of the dependencies not already installed will
+ be located (via PyPI), downloaded, built (if necessary), and installed.
2. Any scripts in your project will be installed with wrappers that verify
the availability of the specified dependencies at runtime, and ensure that
@@ -729,9 +710,8 @@ This will do a checkout (or a clone, in Git and Mercurial parlance) to a
temporary folder and run ``setup.py bdist_egg``.
The ``dependency_links`` option takes the form of a list of URL strings. For
-example, the below will cause EasyInstall to search the specified page for
-eggs or source distributions, if the package's dependencies aren't already
-installed::
+example, this will cause a search of the specified page for eggs or source
+distributions, if the package's dependencies aren't already installed::
setup(
...
@@ -771,7 +751,7 @@ names of "extra" features, to strings or lists of strings describing those
features' requirements. These requirements will *not* be automatically
installed unless another package depends on them (directly or indirectly) by
including the desired "extras" in square brackets after the associated project
-name. (Or if the extras were listed in a requirement spec on the EasyInstall
+name. (Or if the extras were listed in a requirement spec on the "pip install"
command line.)
Extras can be used by a project's `entry points`_ to specify dynamic
@@ -1186,13 +1166,12 @@ preferred way of working (as opposed to using a common independent staging area
or the site-packages directory).
To do this, use the ``setup.py develop`` command. It works very similarly to
-``setup.py install`` or the EasyInstall tool, except that it doesn't actually
-install anything. Instead, it creates a special ``.egg-link`` file in the
-deployment directory, that links to your project's source code. And, if your
-deployment directory is Python's ``site-packages`` directory, it will also
-update the ``easy-install.pth`` file to include your project's source code,
-thereby making it available on ``sys.path`` for all programs using that Python
-installation.
+``setup.py install``, except that it doesn't actually install anything.
+Instead, it creates a special ``.egg-link`` file in the deployment directory,
+that links to your project's source code. And, if your deployment directory is
+Python's ``site-packages`` directory, it will also update the
+``easy-install.pth`` file to include your project's source code, thereby making
+it available on ``sys.path`` for all programs using that Python installation.
If you have enabled the ``use_2to3`` flag, then of course the ``.egg-link``
will not link directly to your source code when run under Python 3, since
@@ -1229,7 +1208,7 @@ the quoted part.
Distributing a ``setuptools``-based project
===========================================
-Detailed instructions to distribute a setuptools project can be found at
+Detailed instructions to distribute a setuptools project can be found at
`Packaging project tutorials`_.
.. _Packaging project tutorials: https://packaging.python.org/tutorials/packaging-projects/#generating-distribution-archives
@@ -1245,7 +1224,7 @@ setup.py is located::
This will generate distribution archives in the `dist` directory.
-Before you upload the generated archives make sure you're registered on
+Before you upload the generated archives make sure you're registered on
https://test.pypi.org/account/register/. You will also need to verify your email
to be able to upload any packages.
You should install twine to be able to upload packages::
@@ -1263,20 +1242,6 @@ To install your newly uploaded package ``example_pkg``, you can use pip::
If you have issues at any point, please refer to `Packaging project tutorials`_
for clarification.
-Distributing legacy ``setuptools`` projects using ez_setup.py
--------------------------------------------------------------
-
-.. warning:: **ez_setup** is deprecated in favor of PIP with **PEP-518** support.
-
-Distributing packages using the legacy ``ez_setup.py`` and ``easy_install`` is
-deprecated in favor of PIP. Please consider migrating to using pip and twine based
-distribution.
-
-However, if you still have any ``ez_setup`` based packages, documentation for
-ez_setup based distributions can be found at `ez_setup distribution guide`_.
-
-.. _ez_setup distribution guide: ez_setup.html
-
Setting the ``zip_safe`` flag
-----------------------------
@@ -1312,20 +1277,14 @@ you've checked over all the warnings it issued, and you are either satisfied it
doesn't work, you can always change it to ``False``, which will force
``setuptools`` to install your project as a directory rather than as a zipfile.
-Of course, the end-user can still override either decision, if they are using
-EasyInstall to install your package. And, if you want to override for testing
-purposes, you can just run ``setup.py easy_install --zip-ok .`` or ``setup.py
-easy_install --always-unzip .`` in your project directory. to install the
-package as a zipfile or directory, respectively.
-
In the future, as we gain more experience with different packages and become
more satisfied with the robustness of the ``pkg_resources`` runtime, the
"zip safety" analysis may become less conservative. However, we strongly
recommend that you determine for yourself whether your project functions
correctly when installed as a zipfile, correct any problems if you can, and
then make an explicit declaration of ``True`` or ``False`` for the ``zip_safe``
-flag, so that it will not be necessary for ``bdist_egg`` or ``EasyInstall`` to
-try to guess whether your project can work as a zipfile.
+flag, so that it will not be necessary for ``bdist_egg`` to try to guess
+whether your project can work as a zipfile.
.. _Namespace Packages:
@@ -1439,9 +1398,9 @@ to generate a daily build or snapshot for. See the section below on the
(Also, before you release your project, be sure to see the section above on
`Specifying Your Project's Version`_ for more information about how pre- and
-post-release tags affect how setuptools and EasyInstall interpret version
-numbers. This is important in order to make sure that dependency processing
-tools will know which versions of your project are newer than others.)
+post-release tags affect how version numbers are interpreted. This is
+important in order to make sure that dependency processing tools will know
+which versions of your project are newer than others.)
Finally, if you are creating builds frequently, and either building them in a
downloadable location or are copying them to a distribution server, you should
@@ -1497,58 +1456,6 @@ all practical purposes, you'll probably use only the ``--formats`` option, if
you use any option at all.
-Making your package available for EasyInstall
----------------------------------------------
-
-There may be reasons why you don't want to upload distributions to
-PyPI, and just want your existing distributions (or perhaps a Subversion
-checkout) to be used instead.
-
-There are three ``setup()`` arguments that affect EasyInstall:
-
-``url`` and ``download_url``
- These become links on your project's PyPI page. EasyInstall will examine
- them to see if they link to a package ("primary links"), or whether they are
- HTML pages. If they're HTML pages, EasyInstall scans all HREF's on the
- page for primary links
-
-``long_description``
- EasyInstall will check any URLs contained in this argument to see if they
- are primary links.
-
-A URL is considered a "primary link" if it is a link to a .tar.gz, .tgz, .zip,
-.egg, .egg.zip, .tar.bz2, or .exe file, or if it has an ``#egg=project`` or
-``#egg=project-version`` fragment identifier attached to it. EasyInstall
-attempts to determine a project name and optional version number from the text
-of a primary link *without* downloading it. When it has found all the primary
-links, EasyInstall will select the best match based on requested version,
-platform compatibility, and other criteria.
-
-So, if your ``url`` or ``download_url`` point either directly to a downloadable
-source distribution, or to HTML page(s) that have direct links to such, then
-EasyInstall will be able to locate downloads automatically. If you want to
-make Subversion checkouts available, then you should create links with either
-``#egg=project`` or ``#egg=project-version`` added to the URL. You should
-replace ``project`` and ``version`` with the values they would have in an egg
-filename. (Be sure to actually generate an egg and then use the initial part
-of the filename, rather than trying to guess what the escaped form of the
-project name and version number will be.)
-
-Note that Subversion checkout links are of lower precedence than other kinds
-of distributions, so EasyInstall will not select a Subversion checkout for
-downloading unless it has a version included in the ``#egg=`` suffix, and
-it's a higher version than EasyInstall has seen in any other links for your
-project.
-
-As a result, it's a common practice to use mark checkout URLs with a version of
-"dev" (i.e., ``#egg=projectname-dev``), so that users can do something like
-this::
-
- easy_install --editable projectname==dev
-
-in order to check out the in-development version of ``projectname``.
-
-
Making "Official" (Non-Snapshot) Releases
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -1803,9 +1710,9 @@ Here are some of the options that the ``develop`` command accepts. Note that
they affect the project's dependencies as well as the project itself, so if you
have dependencies that need to be installed and you use ``--exclude-scripts``
(for example), the dependencies' scripts will not be installed either! For
-this reason, you may want to use EasyInstall to install the project's
-dependencies before using the ``develop`` command, if you need finer control
-over the installation options for dependencies.
+this reason, you may want to use pip to install the project's dependencies
+before using the ``develop`` command, if you need finer control over the
+installation options for dependencies.
``--uninstall, -u``
Un-deploy the current project. You may use the ``--install-dir`` or ``-d``
@@ -1815,10 +1722,10 @@ over the installation options for dependencies.
staging area is Python's ``site-packages`` directory.
Note that this option currently does *not* uninstall script wrappers! You
- must uninstall them yourself, or overwrite them by using EasyInstall to
- activate a different version of the package. You can also avoid installing
- script wrappers in the first place, if you use the ``--exclude-scripts``
- (aka ``-x``) option when you run ``develop`` to deploy the project.
+ must uninstall them yourself, or overwrite them by using pip to install a
+ different version of the package. You can also avoid installing script
+ wrappers in the first place, if you use the ``--exclude-scripts`` (aka
+ ``-x``) option when you run ``develop`` to deploy the project.
``--multi-version, -m``
"Multi-version" mode. Specifying this option prevents ``develop`` from
@@ -1827,8 +1734,8 @@ over the installation options for dependencies.
removed upon successful deployment. In multi-version mode, no specific
version of the package is available for importing, unless you use
``pkg_resources.require()`` to put it on ``sys.path``, or you are running
- a wrapper script generated by ``setuptools`` or EasyInstall. (In which
- case the wrapper script calls ``require()`` for you.)
+ a wrapper script generated by ``setuptools``. (In which case the wrapper
+ script calls ``require()`` for you.)
Note that if you install to a directory other than ``site-packages``,
this option is automatically in effect, because ``.pth`` files can only be
@@ -1881,25 +1788,6 @@ files), the ``develop`` command will use them as defaults, unless you override
them in a ``[develop]`` section or on the command line.
-``easy_install`` - Find and install packages
-============================================
-
-This command runs the `EasyInstall tool
-<easy_install.html>`_ for you. It is exactly
-equivalent to running the ``easy_install`` command. All command line arguments
-following this command are consumed and not processed further by the distutils,
-so this must be the last command listed on the command line. Please see
-the EasyInstall documentation for the options reference and usage examples.
-Normally, there is no reason to use this command via the command line, as you
-can just use ``easy_install`` directly. It's only listed here so that you know
-it's a distutils command, which means that you can:
-
-* create command aliases that use it,
-* create distutils extensions that invoke it as a subcommand, and
-* configure options for it in your ``setup.cfg`` or other distutils config
- files.
-
-
.. _egg_info:
``egg_info`` - Create egg metadata and set build tags
@@ -1958,9 +1846,9 @@ added in the following order:
(Note: Because these options modify the version number used for source and
binary distributions of your project, you should first make sure that you know
how the resulting version numbers will be interpreted by automated tools
-like EasyInstall. See the section above on `Specifying Your Project's
-Version`_ for an explanation of pre- and post-release tags, as well as tips on
-how to choose and verify a versioning scheme for your your project.)
+like pip. See the section above on `Specifying Your Project's Version`_ for an
+explanation of pre- and post-release tags, as well as tips on how to choose and
+verify a versioning scheme for your your project.)
For advanced uses, there is one other option that can be set, to change the
location of the project's ``.egg-info`` directory. Commands that need to find
@@ -2142,6 +2030,11 @@ distutils configuration file the option will be added to (or removed from).
``test`` - Build package and run a unittest suite
=================================================
+.. warning::
+ ``test`` is deprecated and will be removed in a future version. Users
+ looking for a generic test entry point independent of test runner are
+ encouraged to use `tox <https://tox.readthedocs.io>`_.
+
When doing test-driven development, or running automated builds that need
testing before they are deployed for downloading or use, it's often useful
to be able to run a project's unit tests without actually deploying the project
@@ -2187,22 +2080,21 @@ available:
If you did not set a ``test_suite`` in your ``setup()`` call, and do not
provide a ``--test-suite`` option, an error will occur.
+New in 41.5.0: Deprecated the test command.
+
.. _upload:
``upload`` - Upload source and/or egg distributions to PyPI
===========================================================
-.. warning::
- **upload** is deprecated in favor of using `twine
- <https://pypi.org/p/twine>`_
-
-The ``upload`` command is implemented and `documented
-<https://docs.python.org/3.1/distutils/uploading.html>`_
-in distutils.
+The ``upload`` command was deprecated in version 40.0 and removed in version
+42.0. Use `twine <https://pypi.org/p/twine>`_ instead.
-New in 20.1: Added keyring support.
-New in 40.0: Deprecated the upload command.
+For more information on the current best practices in uploading your packages
+to PyPI, see the Python Packaging User Guide's "Packaging Python Projects"
+tutorial specifically the section on `uploading the distribution archives
+<https://packaging.python.org/tutorials/packaging-projects/#uploading-the-distribution-archives>`_.
-----------------------------------------
@@ -2382,6 +2274,7 @@ maintainer_email maintainer-email str
classifiers classifier file:, list-comma
license str
license_file str
+license_files list-comma
description summary file:, str
long_description long-description file:, str
long_description_content_type str 38.6.0
@@ -2423,7 +2316,7 @@ tests_require list-semi
include_package_data bool
packages find:, find_namespace:, list-comma
package_dir dict
-package_data section
+package_data section (1)
exclude_package_data section
namespace_packages list-comma
py_modules list-comma
@@ -2440,6 +2333,10 @@ data_files dict 40.6.0
**find_namespace directive** - The ``find_namespace:`` directive is supported since Python >=3.3.
+Notes:
+1. In the `package_data` section, a key named with a single asterisk (`*`)
+refers to all packages, in lieu of the empty string used in `setup.py`.
+
Configuration API
=================
diff --git a/easy_install.py b/easy_install.py
deleted file mode 100644
index d87e9840..00000000
--- a/easy_install.py
+++ /dev/null
@@ -1,5 +0,0 @@
-"""Run the EasyInstall command"""
-
-if __name__ == '__main__':
- from setuptools.command.easy_install import main
- main()
diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py
index 1f170cfd..51fb1192 100644
--- a/pkg_resources/__init__.py
+++ b/pkg_resources/__init__.py
@@ -333,7 +333,7 @@ class UnknownExtra(ResolutionError):
_provider_factories = {}
-PY_MAJOR = sys.version[:3]
+PY_MAJOR = '{}.{}'.format(*sys.version_info)
EGG_DIST = 3
BINARY_DIST = 2
SOURCE_DIST = 1
@@ -3109,6 +3109,7 @@ class Requirement(packaging.requirements.Requirement):
self.extras = tuple(map(safe_extra, self.extras))
self.hashCmp = (
self.key,
+ self.url,
self.specifier,
frozenset(self.extras),
str(self.marker) if self.marker else None,
diff --git a/pkg_resources/api_tests.txt b/pkg_resources/api_tests.txt
index 0a75170e..7ae5a038 100644
--- a/pkg_resources/api_tests.txt
+++ b/pkg_resources/api_tests.txt
@@ -36,7 +36,7 @@ Distributions have various introspectable attributes::
>>> dist.version
'0.9'
- >>> dist.py_version == sys.version[:3]
+ >>> dist.py_version == '{}.{}'.format(*sys.version_info)
True
>>> print(dist.platform)
diff --git a/pkg_resources/tests/data/my-test-package-source/setup.cfg b/pkg_resources/tests/data/my-test-package-source/setup.cfg
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/pkg_resources/tests/data/my-test-package-source/setup.cfg
diff --git a/pkg_resources/tests/data/my-test-package-source/setup.py b/pkg_resources/tests/data/my-test-package-source/setup.py
new file mode 100644
index 00000000..fe80d28f
--- /dev/null
+++ b/pkg_resources/tests/data/my-test-package-source/setup.py
@@ -0,0 +1,6 @@
+import setuptools
+setuptools.setup(
+ name="my-test-package",
+ version="1.0",
+ zip_safe=True,
+)
diff --git a/pkg_resources/tests/data/my-test-package_unpacked-egg/my_test_package-1.0-py3.7.egg/EGG-INFO/PKG-INFO b/pkg_resources/tests/data/my-test-package_unpacked-egg/my_test_package-1.0-py3.7.egg/EGG-INFO/PKG-INFO
new file mode 100644
index 00000000..7328e3f7
--- /dev/null
+++ b/pkg_resources/tests/data/my-test-package_unpacked-egg/my_test_package-1.0-py3.7.egg/EGG-INFO/PKG-INFO
@@ -0,0 +1,10 @@
+Metadata-Version: 1.0
+Name: my-test-package
+Version: 1.0
+Summary: UNKNOWN
+Home-page: UNKNOWN
+Author: UNKNOWN
+Author-email: UNKNOWN
+License: UNKNOWN
+Description: UNKNOWN
+Platform: UNKNOWN
diff --git a/pkg_resources/tests/data/my-test-package_unpacked-egg/my_test_package-1.0-py3.7.egg/EGG-INFO/SOURCES.txt b/pkg_resources/tests/data/my-test-package_unpacked-egg/my_test_package-1.0-py3.7.egg/EGG-INFO/SOURCES.txt
new file mode 100644
index 00000000..3c4ee167
--- /dev/null
+++ b/pkg_resources/tests/data/my-test-package_unpacked-egg/my_test_package-1.0-py3.7.egg/EGG-INFO/SOURCES.txt
@@ -0,0 +1,7 @@
+setup.cfg
+setup.py
+my_test_package.egg-info/PKG-INFO
+my_test_package.egg-info/SOURCES.txt
+my_test_package.egg-info/dependency_links.txt
+my_test_package.egg-info/top_level.txt
+my_test_package.egg-info/zip-safe \ No newline at end of file
diff --git a/pkg_resources/tests/data/my-test-package_unpacked-egg/my_test_package-1.0-py3.7.egg/EGG-INFO/dependency_links.txt b/pkg_resources/tests/data/my-test-package_unpacked-egg/my_test_package-1.0-py3.7.egg/EGG-INFO/dependency_links.txt
new file mode 100644
index 00000000..8b137891
--- /dev/null
+++ b/pkg_resources/tests/data/my-test-package_unpacked-egg/my_test_package-1.0-py3.7.egg/EGG-INFO/dependency_links.txt
@@ -0,0 +1 @@
+
diff --git a/pkg_resources/tests/data/my-test-package_unpacked-egg/my_test_package-1.0-py3.7.egg/EGG-INFO/top_level.txt b/pkg_resources/tests/data/my-test-package_unpacked-egg/my_test_package-1.0-py3.7.egg/EGG-INFO/top_level.txt
new file mode 100644
index 00000000..8b137891
--- /dev/null
+++ b/pkg_resources/tests/data/my-test-package_unpacked-egg/my_test_package-1.0-py3.7.egg/EGG-INFO/top_level.txt
@@ -0,0 +1 @@
+
diff --git a/pkg_resources/tests/data/my-test-package_unpacked-egg/my_test_package-1.0-py3.7.egg/EGG-INFO/zip-safe b/pkg_resources/tests/data/my-test-package_unpacked-egg/my_test_package-1.0-py3.7.egg/EGG-INFO/zip-safe
new file mode 100644
index 00000000..8b137891
--- /dev/null
+++ b/pkg_resources/tests/data/my-test-package_unpacked-egg/my_test_package-1.0-py3.7.egg/EGG-INFO/zip-safe
@@ -0,0 +1 @@
+
diff --git a/pkg_resources/tests/data/my-test-package_zipped-egg/my_test_package-1.0-py3.7.egg b/pkg_resources/tests/data/my-test-package_zipped-egg/my_test_package-1.0-py3.7.egg
new file mode 100644
index 00000000..5115b895
--- /dev/null
+++ b/pkg_resources/tests/data/my-test-package_zipped-egg/my_test_package-1.0-py3.7.egg
Binary files differ
diff --git a/pkg_resources/tests/test_find_distributions.py b/pkg_resources/tests/test_find_distributions.py
index d735c590..f9594422 100644
--- a/pkg_resources/tests/test_find_distributions.py
+++ b/pkg_resources/tests/test_find_distributions.py
@@ -1,17 +1,9 @@
-import subprocess
-import sys
-
+import py
import pytest
import pkg_resources
-SETUP_TEMPLATE = """
-import setuptools
-setuptools.setup(
- name="my-test-package",
- version="1.0",
- zip_safe=True,
-)
-""".lstrip()
+
+TESTS_DATA_DIR = py.path.local(__file__).dirpath('data')
class TestFindDistributions:
@@ -21,46 +13,22 @@ class TestFindDistributions:
target_dir = tmpdir.mkdir('target')
# place a .egg named directory in the target that is not an egg:
target_dir.mkdir('not.an.egg')
- return str(target_dir)
-
- @pytest.fixture
- def project_dir(self, tmpdir):
- project_dir = tmpdir.mkdir('my-test-package')
- (project_dir / "setup.py").write(SETUP_TEMPLATE)
- return str(project_dir)
+ return target_dir
def test_non_egg_dir_named_egg(self, target_dir):
- dists = pkg_resources.find_distributions(target_dir)
+ dists = pkg_resources.find_distributions(str(target_dir))
assert not list(dists)
- def test_standalone_egg_directory(self, project_dir, target_dir):
- # install this distro as an unpacked egg:
- args = [
- sys.executable,
- '-c', 'from setuptools.command.easy_install import main; main()',
- '-mNx',
- '-d', target_dir,
- '--always-unzip',
- project_dir,
- ]
- subprocess.check_call(args)
- dists = pkg_resources.find_distributions(target_dir)
+ def test_standalone_egg_directory(self, target_dir):
+ (TESTS_DATA_DIR / 'my-test-package_unpacked-egg').copy(target_dir)
+ dists = pkg_resources.find_distributions(str(target_dir))
assert [dist.project_name for dist in dists] == ['my-test-package']
- dists = pkg_resources.find_distributions(target_dir, only=True)
+ dists = pkg_resources.find_distributions(str(target_dir), only=True)
assert not list(dists)
- def test_zipped_egg(self, project_dir, target_dir):
- # install this distro as an unpacked egg:
- args = [
- sys.executable,
- '-c', 'from setuptools.command.easy_install import main; main()',
- '-mNx',
- '-d', target_dir,
- '--zip-ok',
- project_dir,
- ]
- subprocess.check_call(args)
- dists = pkg_resources.find_distributions(target_dir)
+ def test_zipped_egg(self, target_dir):
+ (TESTS_DATA_DIR / 'my-test-package_zipped-egg').copy(target_dir)
+ dists = pkg_resources.find_distributions(str(target_dir))
assert [dist.project_name for dist in dists] == ['my-test-package']
- dists = pkg_resources.find_distributions(target_dir, only=True)
+ dists = pkg_resources.find_distributions(str(target_dir), only=True)
assert not list(dists)
diff --git a/pkg_resources/tests/test_resources.py b/pkg_resources/tests/test_resources.py
index 86afcf74..93fa7114 100644
--- a/pkg_resources/tests/test_resources.py
+++ b/pkg_resources/tests/test_resources.py
@@ -116,7 +116,7 @@ class TestDistro:
self.checkFooPkg(d)
d = Distribution("/some/path")
- assert d.py_version == sys.version[:3]
+ assert d.py_version == '{}.{}'.format(*sys.version_info)
assert d.platform is None
def testDistroParse(self):
@@ -520,6 +520,11 @@ class TestRequirements:
assert r1 == r2
assert str(r1) == str(r2)
assert str(r2) == "Twisted==1.2c1,>=1.2"
+ assert (
+ Requirement("Twisted")
+ !=
+ Requirement("Twisted @ https://localhost/twisted.zip")
+ )
def testBasicContains(self):
r = Requirement("Twisted>=1.2")
@@ -546,11 +551,23 @@ class TestRequirements:
==
hash((
"twisted",
+ None,
packaging.specifiers.SpecifierSet(">=1.2"),
frozenset(["foo", "bar"]),
None
))
)
+ assert (
+ hash(Requirement.parse("Twisted @ https://localhost/twisted.zip"))
+ ==
+ hash((
+ "twisted",
+ "https://localhost/twisted.zip",
+ packaging.specifiers.SpecifierSet(),
+ frozenset(),
+ None
+ ))
+ )
def testVersionEquality(self):
r1 = Requirement.parse("foo==0.3a2")
diff --git a/pytest.ini b/pytest.ini
index 612fb91f..0bc1ec01 100644
--- a/pytest.ini
+++ b/pytest.ini
@@ -1,7 +1,10 @@
[pytest]
addopts=--doctest-modules --doctest-glob=pkg_resources/api_tests.txt -r sxX
-norecursedirs=dist build *.egg setuptools/extern pkg_resources/extern .*
+norecursedirs=dist build *.egg setuptools/extern pkg_resources/extern pkg_resources/tests/data tools .*
flake8-ignore =
setuptools/site-patch.py F821
setuptools/py*compat.py F811
doctest_optionflags=ELLIPSIS ALLOW_UNICODE
+filterwarnings =
+ # https://github.com/pypa/setuptools/issues/1823
+ ignore:bdist_wininst command is deprecated
diff --git a/setup.cfg b/setup.cfg
index a55106a5..385ba14d 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -19,7 +19,7 @@ universal = 1
[metadata]
name = setuptools
-version = 41.4.0
+version = 41.6.0
description = Easily download, build, install, upgrade, and uninstall Python packages
author = Python Packaging Authority
author_email = distutils-sig@python.org
@@ -42,6 +42,7 @@ classifiers =
Programming Language :: Python :: 3.5
Programming Language :: Python :: 3.6
Programming Language :: Python :: 3.7
+ Programming Language :: Python :: 3.8
Topic :: Software Development :: Libraries :: Python Modules
Topic :: System :: Archiving :: Packaging
Topic :: System :: Systems Administration
@@ -50,7 +51,6 @@ classifiers =
[options]
zip_safe = True
python_requires = >=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*
-py_modules = easy_install
packages = find:
[options.packages.find]
diff --git a/setup.py b/setup.py
index f5030dd6..59efc237 100755
--- a/setup.py
+++ b/setup.py
@@ -31,22 +31,6 @@ def read_commands():
return command_ns['__all__']
-def _gen_console_scripts():
- yield "easy_install = setuptools.command.easy_install:main"
-
- # Gentoo distributions manage the python-version-specific scripts
- # themselves, so those platforms define an environment variable to
- # suppress the creation of the version-specific scripts.
- var_names = (
- 'SETUPTOOLS_DISABLE_VERSIONED_EASY_INSTALL_SCRIPT',
- 'DISTRIBUTE_DISABLE_VERSIONED_EASY_INSTALL_SCRIPT',
- )
- if any(os.environ.get(var) not in (None, "", "0") for var in var_names):
- return
- tmpl = "easy_install-{shortver} = setuptools.command.easy_install:main"
- yield tmpl.format(shortver=sys.version[:3])
-
-
package_data = dict(
setuptools=['script (dev).tmpl', 'script.tmpl', 'site-patch.py'],
)
@@ -125,9 +109,6 @@ setup_params = dict(
"depends.txt = setuptools.command.egg_info:warn_depends_obsolete",
"dependency_links.txt = setuptools.command.egg_info:overwrite_arg",
],
- "console_scripts": list(_gen_console_scripts()),
- "setuptools.installation":
- ['eggsecutable = setuptools.command.easy_install:bootstrap'],
},
dependency_links=[
pypi_link(
diff --git a/setuptools/_imp.py b/setuptools/_imp.py
new file mode 100644
index 00000000..a3cce9b2
--- /dev/null
+++ b/setuptools/_imp.py
@@ -0,0 +1,73 @@
+"""
+Re-implementation of find_module and get_frozen_object
+from the deprecated imp module.
+"""
+
+import os
+import importlib.util
+import importlib.machinery
+
+from .py34compat import module_from_spec
+
+
+PY_SOURCE = 1
+PY_COMPILED = 2
+C_EXTENSION = 3
+C_BUILTIN = 6
+PY_FROZEN = 7
+
+
+def find_module(module, paths=None):
+ """Just like 'imp.find_module()', but with package support"""
+ spec = importlib.util.find_spec(module, paths)
+ if spec is None:
+ raise ImportError("Can't find %s" % module)
+ if not spec.has_location and hasattr(spec, 'submodule_search_locations'):
+ spec = importlib.util.spec_from_loader('__init__.py', spec.loader)
+
+ kind = -1
+ file = None
+ static = isinstance(spec.loader, type)
+ if spec.origin == 'frozen' or static and issubclass(
+ spec.loader, importlib.machinery.FrozenImporter):
+ kind = PY_FROZEN
+ path = None # imp compabilty
+ suffix = mode = '' # imp compability
+ elif spec.origin == 'built-in' or static and issubclass(
+ spec.loader, importlib.machinery.BuiltinImporter):
+ kind = C_BUILTIN
+ path = None # imp compabilty
+ suffix = mode = '' # imp compability
+ elif spec.has_location:
+ path = spec.origin
+ suffix = os.path.splitext(path)[1]
+ mode = 'r' if suffix in importlib.machinery.SOURCE_SUFFIXES else 'rb'
+
+ if suffix in importlib.machinery.SOURCE_SUFFIXES:
+ kind = PY_SOURCE
+ elif suffix in importlib.machinery.BYTECODE_SUFFIXES:
+ kind = PY_COMPILED
+ elif suffix in importlib.machinery.EXTENSION_SUFFIXES:
+ kind = C_EXTENSION
+
+ if kind in {PY_SOURCE, PY_COMPILED}:
+ file = open(path, mode)
+ else:
+ path = None
+ suffix = mode = ''
+
+ return file, path, (suffix, mode, kind)
+
+
+def get_frozen_object(module, paths=None):
+ spec = importlib.util.find_spec(module, paths)
+ if not spec:
+ raise ImportError("Can't find %s" % module)
+ return spec.loader.get_code(module)
+
+
+def get_module(module, paths, info):
+ spec = importlib.util.find_spec(module, paths)
+ if not spec:
+ raise ImportError("Can't find %s" % module)
+ return module_from_spec(spec)
diff --git a/setuptools/command/__init__.py b/setuptools/command/__init__.py
index fe619e2e..743f5588 100644
--- a/setuptools/command/__init__.py
+++ b/setuptools/command/__init__.py
@@ -2,8 +2,7 @@ __all__ = [
'alias', 'bdist_egg', 'bdist_rpm', 'build_ext', 'build_py', 'develop',
'easy_install', 'egg_info', 'install', 'install_lib', 'rotate', 'saveopts',
'sdist', 'setopt', 'test', 'install_egg_info', 'install_scripts',
- 'register', 'bdist_wininst', 'upload_docs', 'upload', 'build_clib',
- 'dist_info',
+ 'bdist_wininst', 'upload_docs', 'build_clib', 'dist_info',
]
from distutils.command.bdist import bdist
diff --git a/setuptools/command/bdist_egg.py b/setuptools/command/bdist_egg.py
index 9f8df917..98470f17 100644
--- a/setuptools/command/bdist_egg.py
+++ b/setuptools/command/bdist_egg.py
@@ -284,7 +284,7 @@ class bdist_egg(Command):
"or refer to a module" % (ep,)
)
- pyver = sys.version[:3]
+ pyver = '{}.{}'.format(*sys.version_info)
pkg = ep.module_name
full = '.'.join(ep.attrs)
base = ep.attrs[0]
diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py
index 06c98271..d273bc10 100644
--- a/setuptools/command/easy_install.py
+++ b/setuptools/command/easy_install.py
@@ -73,7 +73,7 @@ warnings.filterwarnings("default", category=pkg_resources.PEP440Warning)
__all__ = [
'samefile', 'easy_install', 'PthDistributions', 'extract_wininst_cfg',
- 'main', 'get_exe_prefixes',
+ 'get_exe_prefixes',
]
@@ -241,7 +241,7 @@ class easy_install(Command):
"""
Render the Setuptools version and installation details, then exit.
"""
- ver = sys.version[:3]
+ ver = '{}.{}'.format(*sys.version_info)
dist = get_distribution('setuptools')
tmpl = 'setuptools {dist.version} from {dist.location} (Python {ver})'
print(tmpl.format(**locals()))
@@ -410,7 +410,13 @@ class easy_install(Command):
]
self._expand_attrs(dirs)
- def run(self):
+ def run(self, show_deprecation=True):
+ if show_deprecation:
+ self.announce(
+ "WARNING: The easy_install command is deprecated "
+ "and will be removed in a future version."
+ , log.WARN,
+ )
if self.verbose != self.distribution.verbose:
log.set_verbosity(self.verbose)
try:
@@ -1180,8 +1186,7 @@ class easy_install(Command):
# to the setup.cfg file.
ei_opts = self.distribution.get_option_dict('easy_install').copy()
fetch_directives = (
- 'find_links', 'site_dirs', 'index_url', 'optimize',
- 'site_dirs', 'allow_hosts',
+ 'find_links', 'site_dirs', 'index_url', 'optimize', 'allow_hosts',
)
fetch_options = {}
for key, val in ei_opts.items():
@@ -1412,7 +1417,7 @@ def get_site_dirs():
os.path.join(
prefix,
"lib",
- "python" + sys.version[:3],
+ "python{}.{}".format(*sys.version_info),
"site-packages",
),
os.path.join(prefix, "lib", "site-python"),
@@ -1433,7 +1438,7 @@ def get_site_dirs():
home,
'Library',
'Python',
- sys.version[:3],
+ '{}.{}'.format(*sys.version_info),
'site-packages',
)
sitedirs.append(home_sp)
@@ -2284,59 +2289,6 @@ def current_umask():
return tmp
-def bootstrap():
- # This function is called when setuptools*.egg is run using /bin/sh
- import setuptools
-
- argv0 = os.path.dirname(setuptools.__path__[0])
- sys.argv[0] = argv0
- sys.argv.append(argv0)
- main()
-
-
-def main(argv=None, **kw):
- from setuptools import setup
- from setuptools.dist import Distribution
-
- class DistributionWithoutHelpCommands(Distribution):
- common_usage = ""
-
- def _show_help(self, *args, **kw):
- with _patch_usage():
- Distribution._show_help(self, *args, **kw)
-
- if argv is None:
- argv = sys.argv[1:]
-
- with _patch_usage():
- setup(
- script_args=['-q', 'easy_install', '-v'] + argv,
- script_name=sys.argv[0] or 'easy_install',
- distclass=DistributionWithoutHelpCommands,
- **kw
- )
-
-
-@contextlib.contextmanager
-def _patch_usage():
- import distutils.core
- USAGE = textwrap.dedent("""
- usage: %(script)s [options] requirement_or_url ...
- or: %(script)s --help
- """).lstrip()
-
- def gen_usage(script_name):
- return USAGE % dict(
- script=os.path.basename(script_name),
- )
-
- saved = distutils.core.gen_usage
- distutils.core.gen_usage = gen_usage
- try:
- yield
- finally:
- distutils.core.gen_usage = saved
-
class EasyInstallDeprecationWarning(SetuptoolsDeprecationWarning):
"""Class for warning about deprecations in EasyInstall in SetupTools. Not ignored by default, unlike DeprecationWarning."""
diff --git a/setuptools/command/install.py b/setuptools/command/install.py
index 31a5ddb5..72b9a3e4 100644
--- a/setuptools/command/install.py
+++ b/setuptools/command/install.py
@@ -114,7 +114,7 @@ class install(orig.install):
args.insert(0, setuptools.bootstrap_install_from)
cmd.args = args
- cmd.run()
+ cmd.run(show_deprecation=False)
setuptools.bootstrap_install_from = None
diff --git a/setuptools/command/register.py b/setuptools/command/register.py
index 98bc0156..b8266b9a 100644
--- a/setuptools/command/register.py
+++ b/setuptools/command/register.py
@@ -1,18 +1,18 @@
from distutils import log
import distutils.command.register as orig
+from setuptools.errors import RemovedCommandError
+
class register(orig.register):
- __doc__ = orig.register.__doc__
+ """Formerly used to register packages on PyPI."""
def run(self):
- try:
- # Make sure that we are using valid current name/version info
- self.run_command('egg_info')
- orig.register.run(self)
- finally:
- self.announce(
- "WARNING: Registering is deprecated, use twine to "
- "upload instead (https://pypi.org/p/twine/)",
- log.WARN
- )
+ msg = (
+ "The register command has been removed, use twine to upload "
+ + "instead (https://pypi.org/p/twine)"
+ )
+
+ self.announce("ERROR: " + msg, log.ERROR)
+
+ raise RemovedCommandError(msg)
diff --git a/setuptools/command/sdist.py b/setuptools/command/sdist.py
index dc253981..55ecdd97 100644
--- a/setuptools/command/sdist.py
+++ b/setuptools/command/sdist.py
@@ -5,7 +5,7 @@ import sys
import io
import contextlib
-from setuptools.extern import six
+from setuptools.extern import six, ordered_set
from .py36compat import sdist_add_defaults
@@ -200,10 +200,12 @@ class sdist(sdist_add_defaults, orig.sdist):
manifest.close()
def check_license(self):
- """Checks if license_file' is configured and adds it to
- 'self.filelist' if the value contains a valid path.
+ """Checks if license_file' or 'license_files' is configured and adds any
+ valid paths to 'self.filelist'.
"""
+ files = ordered_set.OrderedSet()
+
opts = self.distribution.get_option_dict('metadata')
# ignore the source of the value
@@ -211,11 +213,19 @@ class sdist(sdist_add_defaults, orig.sdist):
if license_file is None:
log.debug("'license_file' option was not specified")
- return
+ else:
+ files.add(license_file)
- if not os.path.exists(license_file):
- log.warn("warning: Failed to find the configured license file '%s'",
- license_file)
- return
+ try:
+ files.update(self.distribution.metadata.license_files)
+ except TypeError:
+ log.warn("warning: 'license_files' option is malformed")
+
+ for f in files:
+ if not os.path.exists(f):
+ log.warn(
+ "warning: Failed to find the configured license file '%s'",
+ f)
+ files.remove(f)
- self.filelist.append(license_file)
+ self.filelist.extend(files)
diff --git a/setuptools/command/test.py b/setuptools/command/test.py
index 973e4eb2..c148b38d 100644
--- a/setuptools/command/test.py
+++ b/setuptools/command/test.py
@@ -74,7 +74,7 @@ class NonDataProperty:
class test(Command):
"""Command to run unit tests after in-place build"""
- description = "run unit tests after in-place build"
+ description = "run unit tests after in-place build (deprecated)"
user_options = [
('test-module=', 'm', "Run 'test_suite' in specified module"),
@@ -214,6 +214,14 @@ class test(Command):
return itertools.chain(ir_d, tr_d, er_d)
def run(self):
+ self.announce(
+ "WARNING: Testing via this command is deprecated and will be "
+ "removed in a future version. Users looking for a generic test "
+ "entry point independent of test runner are encouraged to use "
+ "tox.",
+ log.WARN,
+ )
+
installed_dists = self.install_dists(self.distribution)
cmd = ' '.join(self._argv)
diff --git a/setuptools/command/upload.py b/setuptools/command/upload.py
index 6db8888b..ec7f81e2 100644
--- a/setuptools/command/upload.py
+++ b/setuptools/command/upload.py
@@ -1,196 +1,17 @@
-import io
-import os
-import hashlib
-import getpass
-
-from base64 import standard_b64encode
-
from distutils import log
from distutils.command import upload as orig
-from distutils.spawn import spawn
-
-from distutils.errors import DistutilsError
-from setuptools.extern.six.moves.urllib.request import urlopen, Request
-from setuptools.extern.six.moves.urllib.error import HTTPError
-from setuptools.extern.six.moves.urllib.parse import urlparse
+from setuptools.errors import RemovedCommandError
class upload(orig.upload):
- """
- Override default upload behavior to obtain password
- in a variety of different ways.
- """
- def run(self):
- try:
- orig.upload.run(self)
- finally:
- self.announce(
- "WARNING: Uploading via this command is deprecated, use twine "
- "to upload instead (https://pypi.org/p/twine/)",
- log.WARN
- )
+ """Formerly used to upload packages to PyPI."""
- def finalize_options(self):
- orig.upload.finalize_options(self)
- self.username = (
- self.username or
- getpass.getuser()
- )
- # Attempt to obtain password. Short circuit evaluation at the first
- # sign of success.
- self.password = (
- self.password or
- self._load_password_from_keyring() or
- self._prompt_for_password()
+ def run(self):
+ msg = (
+ "The upload command has been removed, use twine to upload "
+ + "instead (https://pypi.org/p/twine)"
)
- def upload_file(self, command, pyversion, filename):
- # Makes sure the repository URL is compliant
- schema, netloc, url, params, query, fragments = \
- urlparse(self.repository)
- if params or query or fragments:
- raise AssertionError("Incompatible url %s" % self.repository)
-
- if schema not in ('http', 'https'):
- raise AssertionError("unsupported schema " + schema)
-
- # Sign if requested
- if self.sign:
- gpg_args = ["gpg", "--detach-sign", "-a", filename]
- if self.identity:
- gpg_args[2:2] = ["--local-user", self.identity]
- spawn(gpg_args,
- dry_run=self.dry_run)
-
- # Fill in the data - send all the meta-data in case we need to
- # register a new release
- with open(filename, 'rb') as f:
- content = f.read()
-
- meta = self.distribution.metadata
-
- data = {
- # action
- ':action': 'file_upload',
- 'protocol_version': '1',
-
- # identify release
- 'name': meta.get_name(),
- 'version': meta.get_version(),
-
- # file content
- 'content': (os.path.basename(filename), content),
- 'filetype': command,
- 'pyversion': pyversion,
- 'md5_digest': hashlib.md5(content).hexdigest(),
-
- # additional meta-data
- 'metadata_version': str(meta.get_metadata_version()),
- 'summary': meta.get_description(),
- 'home_page': meta.get_url(),
- 'author': meta.get_contact(),
- 'author_email': meta.get_contact_email(),
- 'license': meta.get_licence(),
- 'description': meta.get_long_description(),
- 'keywords': meta.get_keywords(),
- 'platform': meta.get_platforms(),
- 'classifiers': meta.get_classifiers(),
- 'download_url': meta.get_download_url(),
- # PEP 314
- 'provides': meta.get_provides(),
- 'requires': meta.get_requires(),
- 'obsoletes': meta.get_obsoletes(),
- }
-
- data['comment'] = ''
-
- if self.sign:
- data['gpg_signature'] = (os.path.basename(filename) + ".asc",
- open(filename+".asc", "rb").read())
-
- # set up the authentication
- user_pass = (self.username + ":" + self.password).encode('ascii')
- # The exact encoding of the authentication string is debated.
- # Anyway PyPI only accepts ascii for both username or password.
- auth = "Basic " + standard_b64encode(user_pass).decode('ascii')
-
- # Build up the MIME payload for the POST data
- boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254'
- sep_boundary = b'\r\n--' + boundary.encode('ascii')
- end_boundary = sep_boundary + b'--\r\n'
- body = io.BytesIO()
- for key, value in data.items():
- title = '\r\nContent-Disposition: form-data; name="%s"' % key
- # handle multiple entries for the same name
- if not isinstance(value, list):
- value = [value]
- for value in value:
- if type(value) is tuple:
- title += '; filename="%s"' % value[0]
- value = value[1]
- else:
- value = str(value).encode('utf-8')
- body.write(sep_boundary)
- body.write(title.encode('utf-8'))
- body.write(b"\r\n\r\n")
- body.write(value)
- body.write(end_boundary)
- body = body.getvalue()
-
- msg = "Submitting %s to %s" % (filename, self.repository)
- self.announce(msg, log.INFO)
-
- # build the Request
- headers = {
- 'Content-type': 'multipart/form-data; boundary=%s' % boundary,
- 'Content-length': str(len(body)),
- 'Authorization': auth,
- }
-
- request = Request(self.repository, data=body,
- headers=headers)
- # send the data
- try:
- result = urlopen(request)
- status = result.getcode()
- reason = result.msg
- except HTTPError as e:
- status = e.code
- reason = e.msg
- except OSError as e:
- self.announce(str(e), log.ERROR)
- raise
-
- if status == 200:
- self.announce('Server response (%s): %s' % (status, reason),
- log.INFO)
- if self.show_response:
- text = getattr(self, '_read_pypi_response',
- lambda x: None)(result)
- if text is not None:
- msg = '\n'.join(('-' * 75, text, '-' * 75))
- self.announce(msg, log.INFO)
- else:
- msg = 'Upload failed (%s): %s' % (status, reason)
- self.announce(msg, log.ERROR)
- raise DistutilsError(msg)
-
- def _load_password_from_keyring(self):
- """
- Attempt to load password from keyring. Suppress Exceptions.
- """
- try:
- keyring = __import__('keyring')
- return keyring.get_password(self.repository, self.username)
- except Exception:
- pass
-
- def _prompt_for_password(self):
- """
- Prompt for a password on the tty. Suppress Exceptions.
- """
- try:
- return getpass.getpass()
- except (Exception, KeyboardInterrupt):
- pass
+ self.announce("ERROR: " + msg, log.ERROR)
+ raise RemovedCommandError(msg)
diff --git a/setuptools/config.py b/setuptools/config.py
index 2d50e25e..9b9a0c45 100644
--- a/setuptools/config.py
+++ b/setuptools/config.py
@@ -483,6 +483,7 @@ class ConfigMetadataHandler(ConfigHandler):
'obsoletes': parse_list,
'classifiers': self._get_parser_compound(parse_file, parse_list),
'license': exclude_files_parser('license'),
+ 'license_files': parse_list,
'description': parse_file,
'long_description': parse_file,
'version': self._parse_version,
diff --git a/setuptools/depends.py b/setuptools/depends.py
index 45e7052d..a37675cb 100644
--- a/setuptools/depends.py
+++ b/setuptools/depends.py
@@ -1,11 +1,13 @@
import sys
-import imp
import marshal
+import contextlib
from distutils.version import StrictVersion
-from imp import PKG_DIRECTORY, PY_COMPILED, PY_SOURCE, PY_FROZEN
from .py33compat import Bytecode
+from .py27compat import find_module, PY_COMPILED, PY_FROZEN, PY_SOURCE
+from . import py27compat
+
__all__ = [
'Require', 'find_module', 'get_module_constant', 'extract_constant'
@@ -15,7 +17,8 @@ __all__ = [
class Require:
"""A prerequisite to building or installing a distribution"""
- def __init__(self, name, requested_version, module, homepage='',
+ def __init__(
+ self, name, requested_version, module, homepage='',
attribute=None, format=None):
if format is None and requested_version is not None:
@@ -79,23 +82,15 @@ class Require:
return self.version_ok(version)
-def find_module(module, paths=None):
- """Just like 'imp.find_module()', but with package support"""
-
- parts = module.split('.')
-
- while parts:
- part = parts.pop(0)
- f, path, (suffix, mode, kind) = info = imp.find_module(part, paths)
-
- if kind == PKG_DIRECTORY:
- parts = parts or ['__init__']
- paths = [path]
-
- elif parts:
- raise ImportError("Can't find %r in %s" % (parts, module))
+def maybe_close(f):
+ @contextlib.contextmanager
+ def empty():
+ yield
+ return
+ if not f:
+ return empty()
- return info
+ return contextlib.closing(f)
def get_module_constant(module, symbol, default=-1, paths=None):
@@ -106,28 +101,23 @@ def get_module_constant(module, symbol, default=-1, paths=None):
constant. Otherwise, return 'default'."""
try:
- f, path, (suffix, mode, kind) = find_module(module, paths)
+ f, path, (suffix, mode, kind) = info = find_module(module, paths)
except ImportError:
# Module doesn't exist
return None
- try:
+ with maybe_close(f):
if kind == PY_COMPILED:
f.read(8) # skip magic & date
code = marshal.load(f)
elif kind == PY_FROZEN:
- code = imp.get_frozen_object(module)
+ code = py27compat.get_frozen_object(module, paths)
elif kind == PY_SOURCE:
code = compile(f.read(), path, 'exec')
else:
# Not something we can parse; we'll have to import it. :(
- if module not in sys.modules:
- imp.load_module(module, f, path, (suffix, mode, kind))
- return getattr(sys.modules[module], symbol, None)
-
- finally:
- if f:
- f.close()
+ imported = py27compat.get_module(module, paths, info)
+ return getattr(imported, symbol, None)
return extract_constant(code, symbol, default)
diff --git a/setuptools/dist.py b/setuptools/dist.py
index 2e5ad4bd..f0a30837 100644
--- a/setuptools/dist.py
+++ b/setuptools/dist.py
@@ -409,6 +409,7 @@ class Distribution(_Distribution):
'long_description_content_type': None,
'project_urls': dict,
'provides_extras': ordered_set.OrderedSet,
+ 'license_files': ordered_set.OrderedSet,
}
_patched_dist = None
@@ -759,32 +760,8 @@ class Distribution(_Distribution):
def fetch_build_egg(self, req):
"""Fetch an egg needed for building"""
- from setuptools.command.easy_install import easy_install
- dist = self.__class__({'script_args': ['easy_install']})
- opts = dist.get_option_dict('easy_install')
- opts.clear()
- opts.update(
- (k, v)
- for k, v in self.get_option_dict('easy_install').items()
- if k in (
- # don't use any other settings
- 'find_links', 'site_dirs', 'index_url',
- 'optimize', 'site_dirs', 'allow_hosts',
- ))
- if self.dependency_links:
- links = self.dependency_links[:]
- if 'find_links' in opts:
- links = opts['find_links'][1] + links
- opts['find_links'] = ('setup', links)
- install_dir = self.get_egg_cache_dir()
- cmd = easy_install(
- dist, args=["x"], install_dir=install_dir,
- exclude_scripts=True,
- always_copy=False, build_directory=None, editable=False,
- upgrade=False, multi_version=True, no_report=True, user=False
- )
- cmd.ensure_finalized()
- return cmd.easy_install(req)
+ from setuptools.installer import fetch_build_egg
+ return fetch_build_egg(self, req)
def _set_global_opts_from_features(self):
"""Add --with-X/--without-X options based on optional features"""
diff --git a/setuptools/errors.py b/setuptools/errors.py
new file mode 100644
index 00000000..2701747f
--- /dev/null
+++ b/setuptools/errors.py
@@ -0,0 +1,16 @@
+"""setuptools.errors
+
+Provides exceptions used by setuptools modules.
+"""
+
+from distutils.errors import DistutilsError
+
+
+class RemovedCommandError(DistutilsError, RuntimeError):
+ """Error used for commands that have been removed in setuptools.
+
+ Since ``setuptools`` is built on ``distutils``, simply removing a command
+ from ``setuptools`` will make the behavior fall back to ``distutils``; this
+ error is raised if a command exists in ``distutils`` but has been actively
+ removed in ``setuptools``.
+ """
diff --git a/setuptools/installer.py b/setuptools/installer.py
new file mode 100644
index 00000000..35bc3cc5
--- /dev/null
+++ b/setuptools/installer.py
@@ -0,0 +1,129 @@
+import glob
+import os
+import subprocess
+import sys
+from distutils import log
+from distutils.errors import DistutilsError
+
+import pkg_resources
+from setuptools.command.easy_install import easy_install
+from setuptools.wheel import Wheel
+
+from .py31compat import TemporaryDirectory
+
+
+def _legacy_fetch_build_egg(dist, req):
+ """Fetch an egg needed for building.
+
+ Legacy path using EasyInstall.
+ """
+ tmp_dist = dist.__class__({'script_args': ['easy_install']})
+ opts = tmp_dist.get_option_dict('easy_install')
+ opts.clear()
+ opts.update(
+ (k, v)
+ for k, v in dist.get_option_dict('easy_install').items()
+ if k in (
+ # don't use any other settings
+ 'find_links', 'site_dirs', 'index_url',
+ 'optimize', 'site_dirs', 'allow_hosts',
+ ))
+ if dist.dependency_links:
+ links = dist.dependency_links[:]
+ if 'find_links' in opts:
+ links = opts['find_links'][1] + links
+ opts['find_links'] = ('setup', links)
+ install_dir = dist.get_egg_cache_dir()
+ cmd = easy_install(
+ tmp_dist, args=["x"], install_dir=install_dir,
+ exclude_scripts=True,
+ always_copy=False, build_directory=None, editable=False,
+ upgrade=False, multi_version=True, no_report=True, user=False
+ )
+ cmd.ensure_finalized()
+ return cmd.easy_install(req)
+
+
+def fetch_build_egg(dist, req):
+ """Fetch an egg needed for building.
+
+ Use pip/wheel to fetch/build a wheel."""
+ # Check pip is available.
+ try:
+ pkg_resources.get_distribution('pip')
+ except pkg_resources.DistributionNotFound:
+ dist.announce(
+ 'WARNING: The pip package is not available, falling back '
+ 'to EasyInstall for handling setup_requires/test_requires; '
+ 'this is deprecated and will be removed in a future version.'
+ , log.WARN
+ )
+ return _legacy_fetch_build_egg(dist, req)
+ # Warn if wheel is not.
+ try:
+ pkg_resources.get_distribution('wheel')
+ except pkg_resources.DistributionNotFound:
+ dist.announce('WARNING: The wheel package is not available.', log.WARN)
+ if not isinstance(req, pkg_resources.Requirement):
+ req = pkg_resources.Requirement.parse(req)
+ # Take easy_install options into account, but do not override relevant
+ # pip environment variables (like PIP_INDEX_URL or PIP_QUIET); they'll
+ # take precedence.
+ opts = dist.get_option_dict('easy_install')
+ if 'allow_hosts' in opts:
+ raise DistutilsError('the `allow-hosts` option is not supported '
+ 'when using pip to install requirements.')
+ if 'PIP_QUIET' in os.environ or 'PIP_VERBOSE' in os.environ:
+ quiet = False
+ else:
+ quiet = True
+ if 'PIP_INDEX_URL' in os.environ:
+ index_url = None
+ elif 'index_url' in opts:
+ index_url = opts['index_url'][1]
+ else:
+ index_url = None
+ if 'find_links' in opts:
+ find_links = opts['find_links'][1][:]
+ else:
+ find_links = []
+ if dist.dependency_links:
+ find_links.extend(dist.dependency_links)
+ eggs_dir = os.path.realpath(dist.get_egg_cache_dir())
+ environment = pkg_resources.Environment()
+ for egg_dist in pkg_resources.find_distributions(eggs_dir):
+ if egg_dist in req and environment.can_add(egg_dist):
+ return egg_dist
+ with TemporaryDirectory() as tmpdir:
+ cmd = [
+ sys.executable, '-m', 'pip',
+ '--disable-pip-version-check',
+ 'wheel', '--no-deps',
+ '-w', tmpdir,
+ ]
+ if quiet:
+ cmd.append('--quiet')
+ if index_url is not None:
+ cmd.extend(('--index-url', index_url))
+ if find_links is not None:
+ for link in find_links:
+ cmd.extend(('--find-links', link))
+ # If requirement is a PEP 508 direct URL, directly pass
+ # the URL to pip, as `req @ url` does not work on the
+ # command line.
+ if req.url:
+ cmd.append(req.url)
+ else:
+ cmd.append(str(req))
+ try:
+ subprocess.check_call(cmd)
+ except subprocess.CalledProcessError as e:
+ raise DistutilsError(str(e))
+ wheel = Wheel(glob.glob(os.path.join(tmpdir, '*.whl'))[0])
+ dist_location = os.path.join(eggs_dir, wheel.egg_name())
+ wheel.install_as_egg(dist_location)
+ dist_metadata = pkg_resources.PathMetadata(
+ dist_location, os.path.join(dist_location, 'EGG-INFO'))
+ dist = pkg_resources.Distribution.from_filename(
+ dist_location, metadata=dist_metadata)
+ return dist
diff --git a/setuptools/msvc.py b/setuptools/msvc.py
index b9c472f1..2ffe1c81 100644
--- a/setuptools/msvc.py
+++ b/setuptools/msvc.py
@@ -11,13 +11,18 @@ Microsoft Visual C++ 9.0:
Microsoft Visual C++ 10.0:
Microsoft Windows SDK 7.1 (x86, x64, ia64)
-Microsoft Visual C++ 14.0:
+Microsoft Visual C++ 14.X:
Microsoft Visual C++ Build Tools 2015 (x86, x64, arm)
- Microsoft Visual Studio 2017 (x86, x64, arm, arm64)
Microsoft Visual Studio Build Tools 2017 (x86, x64, arm, arm64)
+ Microsoft Visual Studio Build Tools 2019 (x86, x64, arm, arm64)
+
+This may also support compilers shipped with compatible Visual Studio versions.
"""
-import os
+import json
+from io import open
+from os import listdir, pathsep
+from os.path import join, isfile, isdir, dirname
import sys
import platform
import itertools
@@ -30,12 +35,9 @@ from .monkey import get_unpatched
if platform.system() == 'Windows':
from setuptools.extern.six.moves import winreg
- safe_env = os.environ
+ from os import environ
else:
- """
- Mock winreg and environ so the module can be imported
- on this platform.
- """
+ # Mock winreg and environ so the module can be imported on this platform.
class winreg:
HKEY_USERS = None
@@ -43,7 +45,7 @@ else:
HKEY_LOCAL_MACHINE = None
HKEY_CLASSES_ROOT = None
- safe_env = dict()
+ environ = dict()
_msvc9_suppress_errors = (
# msvc9compiler isn't available on some platforms
@@ -63,15 +65,13 @@ except _msvc9_suppress_errors:
def msvc9_find_vcvarsall(version):
"""
Patched "distutils.msvc9compiler.find_vcvarsall" to use the standalone
- compiler build for Python (VCForPython). Fall back to original behavior
- when the standalone compiler is not available.
+ compiler build for Python
+ (VCForPython / Microsoft Visual C++ Compiler for Python 2.7).
- Redirect the path of "vcvarsall.bat".
+ Fall back to original behavior when the standalone compiler is not
+ available.
- Known supported compilers
- -------------------------
- Microsoft Visual C++ 9.0:
- Microsoft Visual C++ Compiler for Python 2.7 (x86, amd64)
+ Redirect the path of "vcvarsall.bat".
Parameters
----------
@@ -80,24 +80,25 @@ def msvc9_find_vcvarsall(version):
Return
------
- vcvarsall.bat path: str
+ str
+ vcvarsall.bat path
"""
- VC_BASE = r'Software\%sMicrosoft\DevDiv\VCForPython\%0.1f'
- key = VC_BASE % ('', version)
+ vc_base = r'Software\%sMicrosoft\DevDiv\VCForPython\%0.1f'
+ key = vc_base % ('', version)
try:
# Per-user installs register the compiler path here
productdir = Reg.get_value(key, "installdir")
except KeyError:
try:
# All-user installs on a 64-bit system register here
- key = VC_BASE % ('Wow6432Node\\', version)
+ key = vc_base % ('Wow6432Node\\', version)
productdir = Reg.get_value(key, "installdir")
except KeyError:
productdir = None
if productdir:
- vcvarsall = os.path.os.path.join(productdir, "vcvarsall.bat")
- if os.path.isfile(vcvarsall):
+ vcvarsall = join(productdir, "vcvarsall.bat")
+ if isfile(vcvarsall):
return vcvarsall
return get_unpatched(msvc9_find_vcvarsall)(version)
@@ -106,20 +107,10 @@ def msvc9_find_vcvarsall(version):
def msvc9_query_vcvarsall(ver, arch='x86', *args, **kwargs):
"""
Patched "distutils.msvc9compiler.query_vcvarsall" for support extra
- compilers.
+ Microsoft Visual C++ 9.0 and 10.0 compilers.
Set environment without use of "vcvarsall.bat".
- Known supported compilers
- -------------------------
- Microsoft Visual C++ 9.0:
- Microsoft Visual C++ Compiler for Python 2.7 (x86, amd64)
- Microsoft Windows SDK 6.1 (x86, x64, ia64)
- Microsoft Windows SDK 7.0 (x86, x64, ia64)
-
- Microsoft Visual C++ 10.0:
- Microsoft Windows SDK 7.1 (x86, x64, ia64)
-
Parameters
----------
ver: float
@@ -129,9 +120,10 @@ def msvc9_query_vcvarsall(ver, arch='x86', *args, **kwargs):
Return
------
- environment: dict
+ dict
+ environment
"""
- # Try to get environement from vcvarsall.bat (Classical way)
+ # Try to get environment from vcvarsall.bat (Classical way)
try:
orig = get_unpatched(msvc9_query_vcvarsall)
return orig(ver, arch, *args, **kwargs)
@@ -153,17 +145,10 @@ def msvc9_query_vcvarsall(ver, arch='x86', *args, **kwargs):
def msvc14_get_vc_env(plat_spec):
"""
Patched "distutils._msvccompiler._get_vc_env" for support extra
- compilers.
+ Microsoft Visual C++ 14.X compilers.
Set environment without use of "vcvarsall.bat".
- Known supported compilers
- -------------------------
- Microsoft Visual C++ 14.0:
- Microsoft Visual C++ Build Tools 2015 (x86, x64, arm)
- Microsoft Visual Studio 2017 (x86, x64, arm, arm64)
- Microsoft Visual Studio Build Tools 2017 (x86, x64, arm, arm64)
-
Parameters
----------
plat_spec: str
@@ -171,7 +156,8 @@ def msvc14_get_vc_env(plat_spec):
Return
------
- environment: dict
+ dict
+ environment
"""
# Try to get environment from vcvarsall.bat (Classical way)
try:
@@ -217,9 +203,9 @@ def _augment_exception(exc, version, arch=''):
if version == 9.0:
if arch.lower().find('ia64') > -1:
# For VC++ 9.0, if IA64 support is needed, redirect user
- # to Windows SDK 7.0
- message += ' Get it with "Microsoft Windows SDK 7.0": '
- message += msdownload % 3138
+ # to Windows SDK 7.0.
+ # Note: No download link available from Microsoft.
+ message += ' Get it with "Microsoft Windows SDK 7.0"'
else:
# For VC++ 9.0 redirect user to Vc++ for Python 2.7 :
# This redirection link is maintained by Microsoft.
@@ -230,8 +216,8 @@ def _augment_exception(exc, version, arch=''):
message += ' Get it with "Microsoft Windows SDK 7.1": '
message += msdownload % 8279
elif version >= 14.0:
- # For VC++ 14.0 Redirect user to Visual C++ Build Tools
- message += (' Get it with "Microsoft Visual C++ Build Tools": '
+ # For VC++ 14.X Redirect user to latest Visual C++ Build Tools
+ message += (' Get it with "Build Tools for Visual Studio": '
r'https://visualstudio.microsoft.com/downloads/')
exc.args = (message, )
@@ -239,26 +225,50 @@ def _augment_exception(exc, version, arch=''):
class PlatformInfo:
"""
- Current and Target Architectures informations.
+ Current and Target Architectures information.
Parameters
----------
arch: str
Target architecture.
"""
- current_cpu = safe_env.get('processor_architecture', '').lower()
+ current_cpu = environ.get('processor_architecture', '').lower()
def __init__(self, arch):
self.arch = arch.lower().replace('x64', 'amd64')
@property
def target_cpu(self):
+ """
+ Return Target CPU architecture.
+
+ Return
+ ------
+ str
+ Target CPU
+ """
return self.arch[self.arch.find('_') + 1:]
def target_is_x86(self):
+ """
+ Return True if target CPU is x86 32 bits..
+
+ Return
+ ------
+ bool
+ CPU is x86 32 bits
+ """
return self.target_cpu == 'x86'
def current_is_x86(self):
+ """
+ Return True if current CPU is x86 32 bits..
+
+ Return
+ ------
+ bool
+ CPU is x86 32 bits
+ """
return self.current_cpu == 'x86'
def current_dir(self, hidex86=False, x64=False):
@@ -274,8 +284,8 @@ class PlatformInfo:
Return
------
- subfolder: str
- '\target', or '' (see hidex86 parameter)
+ str
+ subfolder: '\target', or '' (see hidex86 parameter)
"""
return (
'' if (self.current_cpu == 'x86' and hidex86) else
@@ -296,8 +306,8 @@ class PlatformInfo:
Return
------
- subfolder: str
- '\current', or '' (see hidex86 parameter)
+ str
+ subfolder: '\current', or '' (see hidex86 parameter)
"""
return (
'' if (self.target_cpu == 'x86' and hidex86) else
@@ -312,13 +322,13 @@ class PlatformInfo:
Parameters
----------
forcex86: bool
- Use 'x86' as current architecture even if current acritecture is
+ Use 'x86' as current architecture even if current architecture is
not x86.
Return
------
- subfolder: str
- '' if target architecture is current architecture,
+ str
+ subfolder: '' if target architecture is current architecture,
'\current_target' if not.
"""
current = 'x86' if forcex86 else self.current_cpu
@@ -330,7 +340,7 @@ class PlatformInfo:
class RegistryInfo:
"""
- Microsoft Visual Studio related registry informations.
+ Microsoft Visual Studio related registry information.
Parameters
----------
@@ -349,6 +359,11 @@ class RegistryInfo:
def visualstudio(self):
"""
Microsoft Visual Studio root registry key.
+
+ Return
+ ------
+ str
+ Registry key
"""
return 'VisualStudio'
@@ -356,27 +371,47 @@ class RegistryInfo:
def sxs(self):
"""
Microsoft Visual Studio SxS registry key.
+
+ Return
+ ------
+ str
+ Registry key
"""
- return os.path.join(self.visualstudio, 'SxS')
+ return join(self.visualstudio, 'SxS')
@property
def vc(self):
"""
Microsoft Visual C++ VC7 registry key.
+
+ Return
+ ------
+ str
+ Registry key
"""
- return os.path.join(self.sxs, 'VC7')
+ return join(self.sxs, 'VC7')
@property
def vs(self):
"""
Microsoft Visual Studio VS7 registry key.
+
+ Return
+ ------
+ str
+ Registry key
"""
- return os.path.join(self.sxs, 'VS7')
+ return join(self.sxs, 'VS7')
@property
def vc_for_python(self):
"""
Microsoft Visual C++ for Python registry key.
+
+ Return
+ ------
+ str
+ Registry key
"""
return r'DevDiv\VCForPython'
@@ -384,6 +419,11 @@ class RegistryInfo:
def microsoft_sdk(self):
"""
Microsoft SDK registry key.
+
+ Return
+ ------
+ str
+ Registry key
"""
return 'Microsoft SDKs'
@@ -391,20 +431,35 @@ class RegistryInfo:
def windows_sdk(self):
"""
Microsoft Windows/Platform SDK registry key.
+
+ Return
+ ------
+ str
+ Registry key
"""
- return os.path.join(self.microsoft_sdk, 'Windows')
+ return join(self.microsoft_sdk, 'Windows')
@property
def netfx_sdk(self):
"""
Microsoft .NET Framework SDK registry key.
+
+ Return
+ ------
+ str
+ Registry key
"""
- return os.path.join(self.microsoft_sdk, 'NETFXSDK')
+ return join(self.microsoft_sdk, 'NETFXSDK')
@property
def windows_kits_roots(self):
"""
Microsoft Windows Kits Roots registry key.
+
+ Return
+ ------
+ str
+ Registry key
"""
return r'Windows Kits\Installed Roots'
@@ -421,10 +476,11 @@ class RegistryInfo:
Return
------
- str: value
+ str
+ Registry key
"""
node64 = '' if self.pi.current_is_x86() or x86 else 'Wow6432Node'
- return os.path.join('Software', node64, 'Microsoft', key)
+ return join('Software', node64, 'Microsoft', key)
def lookup(self, key, name):
"""
@@ -439,18 +495,19 @@ class RegistryInfo:
Return
------
- str: value
+ str
+ value
"""
- KEY_READ = winreg.KEY_READ
+ key_read = winreg.KEY_READ
openkey = winreg.OpenKey
ms = self.microsoft
for hkey in self.HKEYS:
try:
- bkey = openkey(hkey, ms(key), 0, KEY_READ)
+ bkey = openkey(hkey, ms(key), 0, key_read)
except (OSError, IOError):
if not self.pi.current_is_x86():
try:
- bkey = openkey(hkey, ms(key, True), 0, KEY_READ)
+ bkey = openkey(hkey, ms(key, True), 0, key_read)
except (OSError, IOError):
continue
else:
@@ -463,7 +520,7 @@ class RegistryInfo:
class SystemInfo:
"""
- Microsoft Windows and Visual Studio related system inormations.
+ Microsoft Windows and Visual Studio related system information.
Parameters
----------
@@ -474,30 +531,52 @@ class SystemInfo:
"""
# Variables and properties in this class use originals CamelCase variables
- # names from Microsoft source files for more easy comparaison.
- WinDir = safe_env.get('WinDir', '')
- ProgramFiles = safe_env.get('ProgramFiles', '')
- ProgramFilesx86 = safe_env.get('ProgramFiles(x86)', ProgramFiles)
+ # names from Microsoft source files for more easy comparison.
+ WinDir = environ.get('WinDir', '')
+ ProgramFiles = environ.get('ProgramFiles', '')
+ ProgramFilesx86 = environ.get('ProgramFiles(x86)', ProgramFiles)
def __init__(self, registry_info, vc_ver=None):
self.ri = registry_info
self.pi = self.ri.pi
- self.vc_ver = vc_ver or self._find_latest_available_vc_ver()
- def _find_latest_available_vc_ver(self):
- try:
- return self.find_available_vc_vers()[-1]
- except IndexError:
- err = 'No Microsoft Visual C++ version found'
- raise distutils.errors.DistutilsPlatformError(err)
+ self.known_vs_paths = self.find_programdata_vs_vers()
+
+ # Except for VS15+, VC version is aligned with VS version
+ self.vs_ver = self.vc_ver = (
+ vc_ver or self._find_latest_available_vs_ver())
+
+ def _find_latest_available_vs_ver(self):
+ """
+ Find the latest VC version
+
+ Return
+ ------
+ float
+ version
+ """
+ reg_vc_vers = self.find_reg_vs_vers()
+
+ if not (reg_vc_vers or self.known_vs_paths):
+ raise distutils.errors.DistutilsPlatformError(
+ 'No Microsoft Visual C++ version found')
+
+ vc_vers = set(reg_vc_vers)
+ vc_vers.update(self.known_vs_paths)
+ return sorted(vc_vers)[-1]
- def find_available_vc_vers(self):
+ def find_reg_vs_vers(self):
"""
- Find all available Microsoft Visual C++ versions.
+ Find Microsoft Visual Studio versions available in registry.
+
+ Return
+ ------
+ list of float
+ Versions
"""
ms = self.ri.microsoft
vckeys = (self.ri.vc, self.ri.vc_for_python, self.ri.vs)
- vc_vers = []
+ vs_vers = []
for hkey in self.ri.HKEYS:
for key in vckeys:
try:
@@ -508,49 +587,108 @@ class SystemInfo:
for i in range(values):
try:
ver = float(winreg.EnumValue(bkey, i)[0])
- if ver not in vc_vers:
- vc_vers.append(ver)
+ if ver not in vs_vers:
+ vs_vers.append(ver)
except ValueError:
pass
for i in range(subkeys):
try:
ver = float(winreg.EnumKey(bkey, i))
- if ver not in vc_vers:
- vc_vers.append(ver)
+ if ver not in vs_vers:
+ vs_vers.append(ver)
except ValueError:
pass
- return sorted(vc_vers)
+ return sorted(vs_vers)
+
+ def find_programdata_vs_vers(self):
+ r"""
+ Find Visual studio 2017+ versions from information in
+ "C:\ProgramData\Microsoft\VisualStudio\Packages\_Instances".
+
+ Return
+ ------
+ dict
+ float version as key, path as value.
+ """
+ vs_versions = {}
+ instances_dir = \
+ r'C:\ProgramData\Microsoft\VisualStudio\Packages\_Instances'
+
+ try:
+ hashed_names = listdir(instances_dir)
+
+ except (OSError, IOError):
+ # Directory not exists with all Visual Studio versions
+ return vs_versions
+
+ for name in hashed_names:
+ try:
+ # Get VS installation path from "state.json" file
+ state_path = join(instances_dir, name, 'state.json')
+ with open(state_path, 'rt', encoding='utf-8') as state_file:
+ state = json.load(state_file)
+ vs_path = state['installationPath']
+
+ # Raises OSError if this VS installation does not contain VC
+ listdir(join(vs_path, r'VC\Tools\MSVC'))
+
+ # Store version and path
+ vs_versions[self._as_float_version(
+ state['installationVersion'])] = vs_path
+
+ except (OSError, IOError, KeyError):
+ # Skip if "state.json" file is missing or bad format
+ continue
+
+ return vs_versions
+
+ @staticmethod
+ def _as_float_version(version):
+ """
+ Return a string version as a simplified float version (major.minor)
+
+ Parameters
+ ----------
+ version: str
+ Version.
+
+ Return
+ ------
+ float
+ version
+ """
+ return float('.'.join(version.split('.')[:2]))
@property
def VSInstallDir(self):
"""
Microsoft Visual Studio directory.
+
+ Return
+ ------
+ str
+ path
"""
# Default path
- name = 'Microsoft Visual Studio %0.1f' % self.vc_ver
- default = os.path.join(self.ProgramFilesx86, name)
+ default = join(self.ProgramFilesx86,
+ 'Microsoft Visual Studio %0.1f' % self.vs_ver)
# Try to get path from registry, if fail use default path
- return self.ri.lookup(self.ri.vs, '%0.1f' % self.vc_ver) or default
+ return self.ri.lookup(self.ri.vs, '%0.1f' % self.vs_ver) or default
@property
def VCInstallDir(self):
"""
Microsoft Visual C++ directory.
- """
- self.VSInstallDir
-
- guess_vc = self._guess_vc() or self._guess_vc_legacy()
-
- # Try to get "VC++ for Python" path from registry as default path
- reg_path = os.path.join(self.ri.vc_for_python, '%0.1f' % self.vc_ver)
- python_vc = self.ri.lookup(reg_path, 'installdir')
- default_vc = os.path.join(python_vc, 'VC') if python_vc else guess_vc
- # Try to get path from registry, if fail use default path
- path = self.ri.lookup(self.ri.vc, '%0.1f' % self.vc_ver) or default_vc
+ Return
+ ------
+ str
+ path
+ """
+ path = self._guess_vc() or self._guess_vc_legacy()
- if not os.path.isdir(path):
+ if not isdir(path):
msg = 'Microsoft Visual C++ directory not found'
raise distutils.errors.DistutilsPlatformError(msg)
@@ -558,186 +696,256 @@ class SystemInfo:
def _guess_vc(self):
"""
- Locate Visual C for 2017
+ Locate Visual C++ for VS2017+.
+
+ Return
+ ------
+ str
+ path
"""
- if self.vc_ver <= 14.0:
- return
+ if self.vs_ver <= 14.0:
+ return ''
+
+ try:
+ # First search in known VS paths
+ vs_dir = self.known_vs_paths[self.vs_ver]
+ except KeyError:
+ # Else, search with path from registry
+ vs_dir = self.VSInstallDir
+
+ guess_vc = join(vs_dir, r'VC\Tools\MSVC')
- default = r'VC\Tools\MSVC'
- guess_vc = os.path.join(self.VSInstallDir, default)
# Subdir with VC exact version as name
try:
- vc_exact_ver = os.listdir(guess_vc)[-1]
- return os.path.join(guess_vc, vc_exact_ver)
+ # Update the VC version with real one instead of VS version
+ vc_ver = listdir(guess_vc)[-1]
+ self.vc_ver = self._as_float_version(vc_ver)
+ return join(guess_vc, vc_ver)
except (OSError, IOError, IndexError):
- pass
+ return ''
def _guess_vc_legacy(self):
"""
- Locate Visual C for versions prior to 2017
+ Locate Visual C++ for versions prior to 2017.
+
+ Return
+ ------
+ str
+ path
"""
- default = r'Microsoft Visual Studio %0.1f\VC' % self.vc_ver
- return os.path.join(self.ProgramFilesx86, default)
+ default = join(self.ProgramFilesx86,
+ r'Microsoft Visual Studio %0.1f\VC' % self.vs_ver)
+
+ # Try to get "VC++ for Python" path from registry as default path
+ reg_path = join(self.ri.vc_for_python, '%0.1f' % self.vs_ver)
+ python_vc = self.ri.lookup(reg_path, 'installdir')
+ default_vc = join(python_vc, 'VC') if python_vc else default
+
+ # Try to get path from registry, if fail use default path
+ return self.ri.lookup(self.ri.vc, '%0.1f' % self.vs_ver) or default_vc
@property
def WindowsSdkVersion(self):
"""
Microsoft Windows SDK versions for specified MSVC++ version.
- """
- if self.vc_ver <= 9.0:
- return ('7.0', '6.1', '6.0a')
- elif self.vc_ver == 10.0:
- return ('7.1', '7.0a')
- elif self.vc_ver == 11.0:
- return ('8.0', '8.0a')
- elif self.vc_ver == 12.0:
- return ('8.1', '8.1a')
- elif self.vc_ver >= 14.0:
- return ('10.0', '8.1')
+
+ Return
+ ------
+ tuple of str
+ versions
+ """
+ if self.vs_ver <= 9.0:
+ return '7.0', '6.1', '6.0a'
+ elif self.vs_ver == 10.0:
+ return '7.1', '7.0a'
+ elif self.vs_ver == 11.0:
+ return '8.0', '8.0a'
+ elif self.vs_ver == 12.0:
+ return '8.1', '8.1a'
+ elif self.vs_ver >= 14.0:
+ return '10.0', '8.1'
@property
def WindowsSdkLastVersion(self):
"""
- Microsoft Windows SDK last version
+ Microsoft Windows SDK last version.
+
+ Return
+ ------
+ str
+ version
"""
- return self._use_last_dir_name(os.path.join(
- self.WindowsSdkDir, 'lib'))
+ return self._use_last_dir_name(join(self.WindowsSdkDir, 'lib'))
@property
def WindowsSdkDir(self):
"""
Microsoft Windows SDK directory.
+
+ Return
+ ------
+ str
+ path
"""
sdkdir = ''
for ver in self.WindowsSdkVersion:
# Try to get it from registry
- loc = os.path.join(self.ri.windows_sdk, 'v%s' % ver)
+ loc = join(self.ri.windows_sdk, 'v%s' % ver)
sdkdir = self.ri.lookup(loc, 'installationfolder')
if sdkdir:
break
- if not sdkdir or not os.path.isdir(sdkdir):
+ if not sdkdir or not isdir(sdkdir):
# Try to get "VC++ for Python" version from registry
- path = os.path.join(self.ri.vc_for_python, '%0.1f' % self.vc_ver)
+ path = join(self.ri.vc_for_python, '%0.1f' % self.vc_ver)
install_base = self.ri.lookup(path, 'installdir')
if install_base:
- sdkdir = os.path.join(install_base, 'WinSDK')
- if not sdkdir or not os.path.isdir(sdkdir):
+ sdkdir = join(install_base, 'WinSDK')
+ if not sdkdir or not isdir(sdkdir):
# If fail, use default new path
for ver in self.WindowsSdkVersion:
intver = ver[:ver.rfind('.')]
- path = r'Microsoft SDKs\Windows Kits\%s' % (intver)
- d = os.path.join(self.ProgramFiles, path)
- if os.path.isdir(d):
+ path = r'Microsoft SDKs\Windows Kits\%s' % intver
+ d = join(self.ProgramFiles, path)
+ if isdir(d):
sdkdir = d
- if not sdkdir or not os.path.isdir(sdkdir):
+ if not sdkdir or not isdir(sdkdir):
# If fail, use default old path
for ver in self.WindowsSdkVersion:
path = r'Microsoft SDKs\Windows\v%s' % ver
- d = os.path.join(self.ProgramFiles, path)
- if os.path.isdir(d):
+ d = join(self.ProgramFiles, path)
+ if isdir(d):
sdkdir = d
if not sdkdir:
# If fail, use Platform SDK
- sdkdir = os.path.join(self.VCInstallDir, 'PlatformSDK')
+ sdkdir = join(self.VCInstallDir, 'PlatformSDK')
return sdkdir
@property
def WindowsSDKExecutablePath(self):
"""
Microsoft Windows SDK executable directory.
+
+ Return
+ ------
+ str
+ path
"""
# Find WinSDK NetFx Tools registry dir name
- if self.vc_ver <= 11.0:
+ if self.vs_ver <= 11.0:
netfxver = 35
arch = ''
else:
netfxver = 40
- hidex86 = True if self.vc_ver <= 12.0 else False
+ hidex86 = True if self.vs_ver <= 12.0 else False
arch = self.pi.current_dir(x64=True, hidex86=hidex86)
fx = 'WinSDK-NetFx%dTools%s' % (netfxver, arch.replace('\\', '-'))
- # liste all possibles registry paths
+ # list all possibles registry paths
regpaths = []
- if self.vc_ver >= 14.0:
+ if self.vs_ver >= 14.0:
for ver in self.NetFxSdkVersion:
- regpaths += [os.path.join(self.ri.netfx_sdk, ver, fx)]
+ regpaths += [join(self.ri.netfx_sdk, ver, fx)]
for ver in self.WindowsSdkVersion:
- regpaths += [os.path.join(self.ri.windows_sdk, 'v%sA' % ver, fx)]
+ regpaths += [join(self.ri.windows_sdk, 'v%sA' % ver, fx)]
# Return installation folder from the more recent path
for path in regpaths:
execpath = self.ri.lookup(path, 'installationfolder')
if execpath:
- break
- return execpath
+ return execpath
@property
def FSharpInstallDir(self):
"""
Microsoft Visual F# directory.
+
+ Return
+ ------
+ str
+ path
"""
- path = r'%0.1f\Setup\F#' % self.vc_ver
- path = os.path.join(self.ri.visualstudio, path)
+ path = join(self.ri.visualstudio, r'%0.1f\Setup\F#' % self.vs_ver)
return self.ri.lookup(path, 'productdir') or ''
@property
def UniversalCRTSdkDir(self):
"""
Microsoft Universal CRT SDK directory.
+
+ Return
+ ------
+ str
+ path
"""
# Set Kit Roots versions for specified MSVC++ version
- if self.vc_ver >= 14.0:
- vers = ('10', '81')
- else:
- vers = ()
+ vers = ('10', '81') if self.vs_ver >= 14.0 else ()
# Find path of the more recent Kit
for ver in vers:
sdkdir = self.ri.lookup(self.ri.windows_kits_roots,
'kitsroot%s' % ver)
if sdkdir:
- break
- return sdkdir or ''
+ return sdkdir or ''
@property
def UniversalCRTSdkLastVersion(self):
"""
- Microsoft Universal C Runtime SDK last version
+ Microsoft Universal C Runtime SDK last version.
+
+ Return
+ ------
+ str
+ version
"""
- return self._use_last_dir_name(os.path.join(
- self.UniversalCRTSdkDir, 'lib'))
+ return self._use_last_dir_name(join(self.UniversalCRTSdkDir, 'lib'))
@property
def NetFxSdkVersion(self):
"""
Microsoft .NET Framework SDK versions.
+
+ Return
+ ------
+ tuple of str
+ versions
"""
- # Set FxSdk versions for specified MSVC++ version
- if self.vc_ver >= 14.0:
- return ('4.6.1', '4.6')
- else:
- return ()
+ # Set FxSdk versions for specified VS version
+ return (('4.7.2', '4.7.1', '4.7',
+ '4.6.2', '4.6.1', '4.6',
+ '4.5.2', '4.5.1', '4.5')
+ if self.vs_ver >= 14.0 else ())
@property
def NetFxSdkDir(self):
"""
Microsoft .NET Framework SDK directory.
+
+ Return
+ ------
+ str
+ path
"""
+ sdkdir = ''
for ver in self.NetFxSdkVersion:
- loc = os.path.join(self.ri.netfx_sdk, ver)
+ loc = join(self.ri.netfx_sdk, ver)
sdkdir = self.ri.lookup(loc, 'kitsinstallationfolder')
if sdkdir:
break
- return sdkdir or ''
+ return sdkdir
@property
def FrameworkDir32(self):
"""
Microsoft .NET Framework 32bit directory.
+
+ Return
+ ------
+ str
+ path
"""
# Default path
- guess_fw = os.path.join(self.WinDir, r'Microsoft.NET\Framework')
+ guess_fw = join(self.WinDir, r'Microsoft.NET\Framework')
# Try to get path from registry, if fail use default path
return self.ri.lookup(self.ri.vc, 'frameworkdir32') or guess_fw
@@ -746,9 +954,14 @@ class SystemInfo:
def FrameworkDir64(self):
"""
Microsoft .NET Framework 64bit directory.
+
+ Return
+ ------
+ str
+ path
"""
# Default path
- guess_fw = os.path.join(self.WinDir, r'Microsoft.NET\Framework64')
+ guess_fw = join(self.WinDir, r'Microsoft.NET\Framework64')
# Try to get path from registry, if fail use default path
return self.ri.lookup(self.ri.vc, 'frameworkdir64') or guess_fw
@@ -757,6 +970,11 @@ class SystemInfo:
def FrameworkVersion32(self):
"""
Microsoft .NET Framework 32bit versions.
+
+ Return
+ ------
+ tuple of str
+ versions
"""
return self._find_dot_net_versions(32)
@@ -764,6 +982,11 @@ class SystemInfo:
def FrameworkVersion64(self):
"""
Microsoft .NET Framework 64bit versions.
+
+ Return
+ ------
+ tuple of str
+ versions
"""
return self._find_dot_net_versions(64)
@@ -775,6 +998,11 @@ class SystemInfo:
----------
bits: int
Platform number of bits: 32 or 64.
+
+ Return
+ ------
+ tuple of str
+ versions
"""
# Find actual .NET version in registry
reg_ver = self.ri.lookup(self.ri.vc, 'frameworkver%d' % bits)
@@ -782,18 +1010,17 @@ class SystemInfo:
ver = reg_ver or self._use_last_dir_name(dot_net_dir, 'v') or ''
# Set .NET versions for specified MSVC++ version
- if self.vc_ver >= 12.0:
- frameworkver = (ver, 'v4.0')
- elif self.vc_ver >= 10.0:
- frameworkver = ('v4.0.30319' if ver.lower()[:2] != 'v4' else ver,
- 'v3.5')
- elif self.vc_ver == 9.0:
- frameworkver = ('v3.5', 'v2.0.50727')
- if self.vc_ver == 8.0:
- frameworkver = ('v3.0', 'v2.0.50727')
- return frameworkver
-
- def _use_last_dir_name(self, path, prefix=''):
+ if self.vs_ver >= 12.0:
+ return ver, 'v4.0'
+ elif self.vs_ver >= 10.0:
+ return 'v4.0.30319' if ver.lower()[:2] != 'v4' else ver, 'v3.5'
+ elif self.vs_ver == 9.0:
+ return 'v3.5', 'v2.0.50727'
+ elif self.vs_ver == 8.0:
+ return 'v3.0', 'v2.0.50727'
+
+ @staticmethod
+ def _use_last_dir_name(path, prefix=''):
"""
Return name of the last dir in path or '' if no dir found.
@@ -802,12 +1029,17 @@ class SystemInfo:
path: str
Use dirs in this path
prefix: str
- Use only dirs startings by this prefix
+ Use only dirs starting by this prefix
+
+ Return
+ ------
+ str
+ name
"""
matching_dirs = (
dir_name
- for dir_name in reversed(os.listdir(path))
- if os.path.isdir(os.path.join(path, dir_name)) and
+ for dir_name in reversed(listdir(path))
+ if isdir(join(path, dir_name)) and
dir_name.startswith(prefix)
)
return next(matching_dirs, None) or ''
@@ -818,7 +1050,7 @@ class EnvironmentInfo:
Return environment variables for specified Microsoft Visual C++ version
and platform : Lib, Include, Path and libpath.
- This function is compatible with Microsoft Visual C++ 9.0 to 14.0.
+ This function is compatible with Microsoft Visual C++ 9.0 to 14.X.
Script created by analysing Microsoft environment configuration files like
"vcvars[...].bat", "SetEnv.Cmd", "vcbuildtools.bat", ...
@@ -835,7 +1067,7 @@ class EnvironmentInfo:
"""
# Variables and properties in this class use originals CamelCase variables
- # names from Microsoft source files for more easy comparaison.
+ # names from Microsoft source files for more easy comparison.
def __init__(self, arch, vc_ver=None, vc_min_ver=0):
self.pi = PlatformInfo(arch)
@@ -847,204 +1079,254 @@ class EnvironmentInfo:
raise distutils.errors.DistutilsPlatformError(err)
@property
+ def vs_ver(self):
+ """
+ Microsoft Visual Studio.
+
+ Return
+ ------
+ float
+ version
+ """
+ return self.si.vs_ver
+
+ @property
def vc_ver(self):
"""
Microsoft Visual C++ version.
+
+ Return
+ ------
+ float
+ version
"""
return self.si.vc_ver
@property
def VSTools(self):
"""
- Microsoft Visual Studio Tools
+ Microsoft Visual Studio Tools.
+
+ Return
+ ------
+ list of str
+ paths
"""
paths = [r'Common7\IDE', r'Common7\Tools']
- if self.vc_ver >= 14.0:
+ if self.vs_ver >= 14.0:
arch_subdir = self.pi.current_dir(hidex86=True, x64=True)
paths += [r'Common7\IDE\CommonExtensions\Microsoft\TestWindow']
paths += [r'Team Tools\Performance Tools']
paths += [r'Team Tools\Performance Tools%s' % arch_subdir]
- return [os.path.join(self.si.VSInstallDir, path) for path in paths]
+ return [join(self.si.VSInstallDir, path) for path in paths]
@property
def VCIncludes(self):
"""
- Microsoft Visual C++ & Microsoft Foundation Class Includes
+ Microsoft Visual C++ & Microsoft Foundation Class Includes.
+
+ Return
+ ------
+ list of str
+ paths
"""
- return [os.path.join(self.si.VCInstallDir, 'Include'),
- os.path.join(self.si.VCInstallDir, r'ATLMFC\Include')]
+ return [join(self.si.VCInstallDir, 'Include'),
+ join(self.si.VCInstallDir, r'ATLMFC\Include')]
@property
def VCLibraries(self):
"""
- Microsoft Visual C++ & Microsoft Foundation Class Libraries
+ Microsoft Visual C++ & Microsoft Foundation Class Libraries.
+
+ Return
+ ------
+ list of str
+ paths
"""
- if self.vc_ver >= 15.0:
+ if self.vs_ver >= 15.0:
arch_subdir = self.pi.target_dir(x64=True)
else:
arch_subdir = self.pi.target_dir(hidex86=True)
paths = ['Lib%s' % arch_subdir, r'ATLMFC\Lib%s' % arch_subdir]
- if self.vc_ver >= 14.0:
+ if self.vs_ver >= 14.0:
paths += [r'Lib\store%s' % arch_subdir]
- return [os.path.join(self.si.VCInstallDir, path) for path in paths]
+ return [join(self.si.VCInstallDir, path) for path in paths]
@property
def VCStoreRefs(self):
"""
- Microsoft Visual C++ store references Libraries
+ Microsoft Visual C++ store references Libraries.
+
+ Return
+ ------
+ list of str
+ paths
"""
- if self.vc_ver < 14.0:
+ if self.vs_ver < 14.0:
return []
- return [os.path.join(self.si.VCInstallDir, r'Lib\store\references')]
+ return [join(self.si.VCInstallDir, r'Lib\store\references')]
@property
def VCTools(self):
"""
- Microsoft Visual C++ Tools
+ Microsoft Visual C++ Tools.
+
+ Return
+ ------
+ list of str
+ paths
"""
si = self.si
- tools = [os.path.join(si.VCInstallDir, 'VCPackages')]
+ tools = [join(si.VCInstallDir, 'VCPackages')]
- forcex86 = True if self.vc_ver <= 10.0 else False
+ forcex86 = True if self.vs_ver <= 10.0 else False
arch_subdir = self.pi.cross_dir(forcex86)
if arch_subdir:
- tools += [os.path.join(si.VCInstallDir, 'Bin%s' % arch_subdir)]
+ tools += [join(si.VCInstallDir, 'Bin%s' % arch_subdir)]
- if self.vc_ver == 14.0:
+ if self.vs_ver == 14.0:
path = 'Bin%s' % self.pi.current_dir(hidex86=True)
- tools += [os.path.join(si.VCInstallDir, path)]
+ tools += [join(si.VCInstallDir, path)]
- elif self.vc_ver >= 15.0:
+ elif self.vs_ver >= 15.0:
host_dir = (r'bin\HostX86%s' if self.pi.current_is_x86() else
r'bin\HostX64%s')
- tools += [os.path.join(
+ tools += [join(
si.VCInstallDir, host_dir % self.pi.target_dir(x64=True))]
if self.pi.current_cpu != self.pi.target_cpu:
- tools += [os.path.join(
+ tools += [join(
si.VCInstallDir, host_dir % self.pi.current_dir(x64=True))]
else:
- tools += [os.path.join(si.VCInstallDir, 'Bin')]
+ tools += [join(si.VCInstallDir, 'Bin')]
return tools
@property
def OSLibraries(self):
"""
- Microsoft Windows SDK Libraries
+ Microsoft Windows SDK Libraries.
+
+ Return
+ ------
+ list of str
+ paths
"""
- if self.vc_ver <= 10.0:
+ if self.vs_ver <= 10.0:
arch_subdir = self.pi.target_dir(hidex86=True, x64=True)
- return [os.path.join(self.si.WindowsSdkDir, 'Lib%s' % arch_subdir)]
+ return [join(self.si.WindowsSdkDir, 'Lib%s' % arch_subdir)]
else:
arch_subdir = self.pi.target_dir(x64=True)
- lib = os.path.join(self.si.WindowsSdkDir, 'lib')
+ lib = join(self.si.WindowsSdkDir, 'lib')
libver = self._sdk_subdir
- return [os.path.join(lib, '%sum%s' % (libver , arch_subdir))]
+ return [join(lib, '%sum%s' % (libver , arch_subdir))]
@property
def OSIncludes(self):
"""
- Microsoft Windows SDK Include
+ Microsoft Windows SDK Include.
+
+ Return
+ ------
+ list of str
+ paths
"""
- include = os.path.join(self.si.WindowsSdkDir, 'include')
+ include = join(self.si.WindowsSdkDir, 'include')
- if self.vc_ver <= 10.0:
- return [include, os.path.join(include, 'gl')]
+ if self.vs_ver <= 10.0:
+ return [include, join(include, 'gl')]
else:
- if self.vc_ver >= 14.0:
+ if self.vs_ver >= 14.0:
sdkver = self._sdk_subdir
else:
sdkver = ''
- return [os.path.join(include, '%sshared' % sdkver),
- os.path.join(include, '%sum' % sdkver),
- os.path.join(include, '%swinrt' % sdkver)]
+ return [join(include, '%sshared' % sdkver),
+ join(include, '%sum' % sdkver),
+ join(include, '%swinrt' % sdkver)]
@property
def OSLibpath(self):
"""
- Microsoft Windows SDK Libraries Paths
+ Microsoft Windows SDK Libraries Paths.
+
+ Return
+ ------
+ list of str
+ paths
"""
- ref = os.path.join(self.si.WindowsSdkDir, 'References')
+ ref = join(self.si.WindowsSdkDir, 'References')
libpath = []
- if self.vc_ver <= 9.0:
+ if self.vs_ver <= 9.0:
libpath += self.OSLibraries
- if self.vc_ver >= 11.0:
- libpath += [os.path.join(ref, r'CommonConfiguration\Neutral')]
+ if self.vs_ver >= 11.0:
+ libpath += [join(ref, r'CommonConfiguration\Neutral')]
- if self.vc_ver >= 14.0:
+ if self.vs_ver >= 14.0:
libpath += [
ref,
- os.path.join(self.si.WindowsSdkDir, 'UnionMetadata'),
- os.path.join(
- ref,
- 'Windows.Foundation.UniversalApiContract',
- '1.0.0.0',
- ),
- os.path.join(
- ref,
- 'Windows.Foundation.FoundationContract',
- '1.0.0.0',
- ),
- os.path.join(
- ref,
- 'Windows.Networking.Connectivity.WwanContract',
- '1.0.0.0',
- ),
- os.path.join(
- self.si.WindowsSdkDir,
- 'ExtensionSDKs',
- 'Microsoft.VCLibs',
- '%0.1f' % self.vc_ver,
- 'References',
- 'CommonConfiguration',
- 'neutral',
- ),
+ join(self.si.WindowsSdkDir, 'UnionMetadata'),
+ join(ref, 'Windows.Foundation.UniversalApiContract', '1.0.0.0'),
+ join(ref, 'Windows.Foundation.FoundationContract', '1.0.0.0'),
+ join(ref,'Windows.Networking.Connectivity.WwanContract',
+ '1.0.0.0'),
+ join(self.si.WindowsSdkDir, 'ExtensionSDKs', 'Microsoft.VCLibs',
+ '%0.1f' % self.vs_ver, 'References', 'CommonConfiguration',
+ 'neutral'),
]
return libpath
@property
def SdkTools(self):
"""
- Microsoft Windows SDK Tools
+ Microsoft Windows SDK Tools.
+
+ Return
+ ------
+ list of str
+ paths
"""
return list(self._sdk_tools())
def _sdk_tools(self):
"""
- Microsoft Windows SDK Tools paths generator
+ Microsoft Windows SDK Tools paths generator.
+
+ Return
+ ------
+ generator of str
+ paths
"""
- if self.vc_ver < 15.0:
- bin_dir = 'Bin' if self.vc_ver <= 11.0 else r'Bin\x86'
- yield os.path.join(self.si.WindowsSdkDir, bin_dir)
+ if self.vs_ver < 15.0:
+ bin_dir = 'Bin' if self.vs_ver <= 11.0 else r'Bin\x86'
+ yield join(self.si.WindowsSdkDir, bin_dir)
if not self.pi.current_is_x86():
arch_subdir = self.pi.current_dir(x64=True)
path = 'Bin%s' % arch_subdir
- yield os.path.join(self.si.WindowsSdkDir, path)
+ yield join(self.si.WindowsSdkDir, path)
- if self.vc_ver == 10.0 or self.vc_ver == 11.0:
+ if self.vs_ver in (10.0, 11.0):
if self.pi.target_is_x86():
arch_subdir = ''
else:
arch_subdir = self.pi.current_dir(hidex86=True, x64=True)
path = r'Bin\NETFX 4.0 Tools%s' % arch_subdir
- yield os.path.join(self.si.WindowsSdkDir, path)
+ yield join(self.si.WindowsSdkDir, path)
- elif self.vc_ver >= 15.0:
- path = os.path.join(self.si.WindowsSdkDir, 'Bin')
+ elif self.vs_ver >= 15.0:
+ path = join(self.si.WindowsSdkDir, 'Bin')
arch_subdir = self.pi.current_dir(x64=True)
sdkver = self.si.WindowsSdkLastVersion
- yield os.path.join(path, '%s%s' % (sdkver, arch_subdir))
+ yield join(path, '%s%s' % (sdkver, arch_subdir))
if self.si.WindowsSDKExecutablePath:
yield self.si.WindowsSDKExecutablePath
@@ -1052,7 +1334,12 @@ class EnvironmentInfo:
@property
def _sdk_subdir(self):
"""
- Microsoft Windows SDK version subdir
+ Microsoft Windows SDK version subdir.
+
+ Return
+ ------
+ str
+ subdir
"""
ucrtver = self.si.WindowsSdkLastVersion
return ('%s\\' % ucrtver) if ucrtver else ''
@@ -1060,22 +1347,32 @@ class EnvironmentInfo:
@property
def SdkSetup(self):
"""
- Microsoft Windows SDK Setup
+ Microsoft Windows SDK Setup.
+
+ Return
+ ------
+ list of str
+ paths
"""
- if self.vc_ver > 9.0:
+ if self.vs_ver > 9.0:
return []
- return [os.path.join(self.si.WindowsSdkDir, 'Setup')]
+ return [join(self.si.WindowsSdkDir, 'Setup')]
@property
def FxTools(self):
"""
- Microsoft .NET Framework Tools
+ Microsoft .NET Framework Tools.
+
+ Return
+ ------
+ list of str
+ paths
"""
pi = self.pi
si = self.si
- if self.vc_ver <= 10.0:
+ if self.vs_ver <= 10.0:
include32 = True
include64 = not pi.target_is_x86() and not pi.current_is_x86()
else:
@@ -1084,102 +1381,142 @@ class EnvironmentInfo:
tools = []
if include32:
- tools += [os.path.join(si.FrameworkDir32, ver)
+ tools += [join(si.FrameworkDir32, ver)
for ver in si.FrameworkVersion32]
if include64:
- tools += [os.path.join(si.FrameworkDir64, ver)
+ tools += [join(si.FrameworkDir64, ver)
for ver in si.FrameworkVersion64]
return tools
@property
def NetFxSDKLibraries(self):
"""
- Microsoft .Net Framework SDK Libraries
+ Microsoft .Net Framework SDK Libraries.
+
+ Return
+ ------
+ list of str
+ paths
"""
- if self.vc_ver < 14.0 or not self.si.NetFxSdkDir:
+ if self.vs_ver < 14.0 or not self.si.NetFxSdkDir:
return []
arch_subdir = self.pi.target_dir(x64=True)
- return [os.path.join(self.si.NetFxSdkDir, r'lib\um%s' % arch_subdir)]
+ return [join(self.si.NetFxSdkDir, r'lib\um%s' % arch_subdir)]
@property
def NetFxSDKIncludes(self):
"""
- Microsoft .Net Framework SDK Includes
+ Microsoft .Net Framework SDK Includes.
+
+ Return
+ ------
+ list of str
+ paths
"""
- if self.vc_ver < 14.0 or not self.si.NetFxSdkDir:
+ if self.vs_ver < 14.0 or not self.si.NetFxSdkDir:
return []
- return [os.path.join(self.si.NetFxSdkDir, r'include\um')]
+ return [join(self.si.NetFxSdkDir, r'include\um')]
@property
def VsTDb(self):
"""
- Microsoft Visual Studio Team System Database
+ Microsoft Visual Studio Team System Database.
+
+ Return
+ ------
+ list of str
+ paths
"""
- return [os.path.join(self.si.VSInstallDir, r'VSTSDB\Deploy')]
+ return [join(self.si.VSInstallDir, r'VSTSDB\Deploy')]
@property
def MSBuild(self):
"""
- Microsoft Build Engine
+ Microsoft Build Engine.
+
+ Return
+ ------
+ list of str
+ paths
"""
- if self.vc_ver < 12.0:
+ if self.vs_ver < 12.0:
return []
- elif self.vc_ver < 15.0:
+ elif self.vs_ver < 15.0:
base_path = self.si.ProgramFilesx86
arch_subdir = self.pi.current_dir(hidex86=True)
else:
base_path = self.si.VSInstallDir
arch_subdir = ''
- path = r'MSBuild\%0.1f\bin%s' % (self.vc_ver, arch_subdir)
- build = [os.path.join(base_path, path)]
+ path = r'MSBuild\%0.1f\bin%s' % (self.vs_ver, arch_subdir)
+ build = [join(base_path, path)]
- if self.vc_ver >= 15.0:
+ if self.vs_ver >= 15.0:
# Add Roslyn C# & Visual Basic Compiler
- build += [os.path.join(base_path, path, 'Roslyn')]
+ build += [join(base_path, path, 'Roslyn')]
return build
@property
def HTMLHelpWorkshop(self):
"""
- Microsoft HTML Help Workshop
+ Microsoft HTML Help Workshop.
+
+ Return
+ ------
+ list of str
+ paths
"""
- if self.vc_ver < 11.0:
+ if self.vs_ver < 11.0:
return []
- return [os.path.join(self.si.ProgramFilesx86, 'HTML Help Workshop')]
+ return [join(self.si.ProgramFilesx86, 'HTML Help Workshop')]
@property
def UCRTLibraries(self):
"""
- Microsoft Universal C Runtime SDK Libraries
+ Microsoft Universal C Runtime SDK Libraries.
+
+ Return
+ ------
+ list of str
+ paths
"""
- if self.vc_ver < 14.0:
+ if self.vs_ver < 14.0:
return []
arch_subdir = self.pi.target_dir(x64=True)
- lib = os.path.join(self.si.UniversalCRTSdkDir, 'lib')
+ lib = join(self.si.UniversalCRTSdkDir, 'lib')
ucrtver = self._ucrt_subdir
- return [os.path.join(lib, '%sucrt%s' % (ucrtver, arch_subdir))]
+ return [join(lib, '%sucrt%s' % (ucrtver, arch_subdir))]
@property
def UCRTIncludes(self):
"""
- Microsoft Universal C Runtime SDK Include
+ Microsoft Universal C Runtime SDK Include.
+
+ Return
+ ------
+ list of str
+ paths
"""
- if self.vc_ver < 14.0:
+ if self.vs_ver < 14.0:
return []
- include = os.path.join(self.si.UniversalCRTSdkDir, 'include')
- return [os.path.join(include, '%sucrt' % self._ucrt_subdir)]
+ include = join(self.si.UniversalCRTSdkDir, 'include')
+ return [join(include, '%sucrt' % self._ucrt_subdir)]
@property
def _ucrt_subdir(self):
"""
- Microsoft Universal C Runtime SDK version subdir
+ Microsoft Universal C Runtime SDK version subdir.
+
+ Return
+ ------
+ str
+ subdir
"""
ucrtver = self.si.UniversalCRTSdkLastVersion
return ('%s\\' % ucrtver) if ucrtver else ''
@@ -1187,31 +1524,52 @@ class EnvironmentInfo:
@property
def FSharp(self):
"""
- Microsoft Visual F#
+ Microsoft Visual F#.
+
+ Return
+ ------
+ list of str
+ paths
"""
- if self.vc_ver < 11.0 and self.vc_ver > 12.0:
+ if 11.0 > self.vs_ver > 12.0:
return []
- return self.si.FSharpInstallDir
+ return [self.si.FSharpInstallDir]
@property
def VCRuntimeRedist(self):
"""
- Microsoft Visual C++ runtime redistribuable dll
- """
- arch_subdir = self.pi.target_dir(x64=True)
- if self.vc_ver < 15:
- redist_path = self.si.VCInstallDir
- vcruntime = 'redist%s\\Microsoft.VC%d0.CRT\\vcruntime%d0.dll'
- else:
- redist_path = self.si.VCInstallDir.replace('\\Tools', '\\Redist')
- vcruntime = 'onecore%s\\Microsoft.VC%d0.CRT\\vcruntime%d0.dll'
-
- # Visual Studio 2017 is still Visual C++ 14.0
- dll_ver = 14.0 if self.vc_ver == 15 else self.vc_ver
+ Microsoft Visual C++ runtime redistributable dll.
- vcruntime = vcruntime % (arch_subdir, self.vc_ver, dll_ver)
- return os.path.join(redist_path, vcruntime)
+ Return
+ ------
+ str
+ path
+ """
+ vcruntime = 'vcruntime%d0.dll' % self.vc_ver
+ arch_subdir = self.pi.target_dir(x64=True).strip('\\')
+
+ # Installation prefixes candidates
+ prefixes = []
+ tools_path = self.si.VCInstallDir
+ redist_path = dirname(tools_path.replace(r'\Tools', r'\Redist'))
+ if isdir(redist_path):
+ # Redist version may not be exactly the same as tools
+ redist_path = join(redist_path, listdir(redist_path)[-1])
+ prefixes += [redist_path, join(redist_path, 'onecore')]
+
+ prefixes += [join(tools_path, 'redist')] # VS14 legacy path
+
+ # CRT directory
+ crt_dirs = ('Microsoft.VC%d.CRT' % (self.vc_ver * 10),
+ # Sometime store in directory with VS version instead of VC
+ 'Microsoft.VC%d.CRT' % (int(self.vs_ver) * 10))
+
+ # vcruntime path
+ for prefix, crt_dir in itertools.product(prefixes, crt_dirs):
+ path = join(prefix, arch_subdir, crt_dir, vcruntime)
+ if isfile(path):
+ return path
def return_env(self, exists=True):
"""
@@ -1221,6 +1579,11 @@ class EnvironmentInfo:
----------
exists: bool
It True, only return existing paths.
+
+ Return
+ ------
+ dict
+ environment
"""
env = dict(
include=self._build_paths('include',
@@ -1254,7 +1617,7 @@ class EnvironmentInfo:
self.FSharp],
exists),
)
- if self.vc_ver >= 14 and os.path.isfile(self.VCRuntimeRedist):
+ if self.vs_ver >= 14 and isfile(self.VCRuntimeRedist):
env['py_vcruntime_redist'] = self.VCRuntimeRedist
return env
@@ -1265,20 +1628,35 @@ class EnvironmentInfo:
unique, extant, directories from those paths and from
the environment variable. Raise an error if no paths
are resolved.
+
+ Parameters
+ ----------
+ name: str
+ Environment variable name
+ spec_path_lists: list of str
+ Paths
+ exists: bool
+ It True, only return existing paths.
+
+ Return
+ ------
+ str
+ Pathsep-separated paths
"""
# flatten spec_path_lists
spec_paths = itertools.chain.from_iterable(spec_path_lists)
- env_paths = safe_env.get(name, '').split(os.pathsep)
+ env_paths = environ.get(name, '').split(pathsep)
paths = itertools.chain(spec_paths, env_paths)
- extant_paths = list(filter(os.path.isdir, paths)) if exists else paths
+ extant_paths = list(filter(isdir, paths)) if exists else paths
if not extant_paths:
msg = "%s environment variable is empty" % name.upper()
raise distutils.errors.DistutilsPlatformError(msg)
unique_paths = self._unique_everseen(extant_paths)
- return os.pathsep.join(unique_paths)
+ return pathsep.join(unique_paths)
# from Python docs
- def _unique_everseen(self, iterable, key=None):
+ @staticmethod
+ def _unique_everseen(iterable, key=None):
"""
List unique elements, preserving order.
Remember all elements ever seen.
diff --git a/setuptools/package_index.py b/setuptools/package_index.py
index 6b06f2ca..f419d471 100644
--- a/setuptools/package_index.py
+++ b/setuptools/package_index.py
@@ -46,7 +46,7 @@ __all__ = [
_SOCKET_TIMEOUT = 15
_tmpl = "setuptools/{setuptools.__version__} Python-urllib/{py_major}"
-user_agent = _tmpl.format(py_major=sys.version[:3], setuptools=setuptools)
+user_agent = _tmpl.format(py_major='{}.{}'.format(*sys.version_info), setuptools=setuptools)
def parse_requirement_arg(spec):
diff --git a/setuptools/py27compat.py b/setuptools/py27compat.py
index 2985011b..1d57360f 100644
--- a/setuptools/py27compat.py
+++ b/setuptools/py27compat.py
@@ -2,6 +2,7 @@
Compatibility Support for Python 2.7 and earlier
"""
+import sys
import platform
from setuptools.extern import six
@@ -26,3 +27,34 @@ linux_py2_ascii = (
rmtree_safe = str if linux_py2_ascii else lambda x: x
"""Workaround for http://bugs.python.org/issue24672"""
+
+
+try:
+ from ._imp import find_module, PY_COMPILED, PY_FROZEN, PY_SOURCE
+ from ._imp import get_frozen_object, get_module
+except ImportError:
+ import imp
+ from imp import PY_COMPILED, PY_FROZEN, PY_SOURCE # noqa
+
+ def find_module(module, paths=None):
+ """Just like 'imp.find_module()', but with package support"""
+ parts = module.split('.')
+ while parts:
+ part = parts.pop(0)
+ f, path, (suffix, mode, kind) = info = imp.find_module(part, paths)
+
+ if kind == imp.PKG_DIRECTORY:
+ parts = parts or ['__init__']
+ paths = [path]
+
+ elif parts:
+ raise ImportError("Can't find %r in %s" % (parts, module))
+
+ return info
+
+ def get_frozen_object(module, paths):
+ return imp.get_frozen_object(module)
+
+ def get_module(module, paths, info):
+ imp.load_module(module, *info)
+ return sys.modules[module]
diff --git a/setuptools/py34compat.py b/setuptools/py34compat.py
new file mode 100644
index 00000000..3ad91722
--- /dev/null
+++ b/setuptools/py34compat.py
@@ -0,0 +1,13 @@
+import importlib
+
+try:
+ import importlib.util
+except ImportError:
+ pass
+
+
+try:
+ module_from_spec = importlib.util.module_from_spec
+except AttributeError:
+ def module_from_spec(spec):
+ return spec.loader.load_module(spec.name)
diff --git a/setuptools/tests/server.py b/setuptools/tests/server.py
index fc3a5975..8b17b081 100644
--- a/setuptools/tests/server.py
+++ b/setuptools/tests/server.py
@@ -1,10 +1,13 @@
"""Basic http server for tests to simulate PyPI or custom indexes
"""
+import os
import time
import threading
from setuptools.extern.six.moves import BaseHTTPServer, SimpleHTTPServer
+from setuptools.extern.six.moves.urllib_parse import urljoin
+from setuptools.extern.six.moves.urllib.request import pathname2url
class IndexServer(BaseHTTPServer.HTTPServer):
@@ -70,5 +73,19 @@ class MockServer(BaseHTTPServer.HTTPServer, threading.Thread):
self.serve_forever()
@property
+ def netloc(self):
+ return 'localhost:%s' % self.server_port
+
+ @property
def url(self):
- return 'http://localhost:%(server_port)s/' % vars(self)
+ return 'http://%s/' % self.netloc
+
+
+def path_to_url(path, authority=None):
+ """ Convert a path to a file: URL. """
+ path = os.path.normpath(os.path.abspath(path))
+ base = 'file:'
+ if authority is not None:
+ base += '//' + authority
+ url = urljoin(base, pathname2url(path))
+ return url
diff --git a/setuptools/tests/test_bdist_egg.py b/setuptools/tests/test_bdist_egg.py
index 54742aa6..fb5b90b1 100644
--- a/setuptools/tests/test_bdist_egg.py
+++ b/setuptools/tests/test_bdist_egg.py
@@ -42,7 +42,7 @@ class Test:
# let's see if we got our egg link at the right place
[content] = os.listdir('dist')
- assert re.match(r'foo-0.0.0-py[23].\d.egg$', content)
+ assert re.match(r'foo-0.0.0-py[23].\d+.egg$', content)
@pytest.mark.xfail(
os.environ.get('PYTHONDONTWRITEBYTECODE'),
diff --git a/setuptools/tests/test_config.py b/setuptools/tests/test_config.py
index 1b94a586..69d8d00d 100644
--- a/setuptools/tests/test_config.py
+++ b/setuptools/tests/test_config.py
@@ -1,4 +1,4 @@
-# -*- coding: UTF-8 -*-
+# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import contextlib
diff --git a/setuptools/tests/test_easy_install.py b/setuptools/tests/test_easy_install.py
index c3fd1c6e..68319c2f 100644
--- a/setuptools/tests/test_easy_install.py
+++ b/setuptools/tests/test_easy_install.py
@@ -15,24 +15,24 @@ import distutils.errors
import io
import zipfile
import mock
-from setuptools.command.easy_install import (
- EasyInstallDeprecationWarning, ScriptWriter, WindowsScriptWriter,
-)
import time
+
from setuptools.extern import six
-from setuptools.extern.six.moves import urllib
import pytest
from setuptools import sandbox
from setuptools.sandbox import run_setup
import setuptools.command.easy_install as ei
-from setuptools.command.easy_install import PthDistributions
+from setuptools.command.easy_install import (
+ EasyInstallDeprecationWarning, ScriptWriter, PthDistributions,
+ WindowsScriptWriter,
+)
from setuptools.command import easy_install as easy_install_pkg
from setuptools.dist import Distribution
from pkg_resources import normalize_path, working_set
from pkg_resources import Distribution as PRDistribution
-import setuptools.tests.server
+from setuptools.tests.server import MockServer, path_to_url
from setuptools.tests import fail_on_ascii
import pkg_resources
@@ -440,48 +440,53 @@ def distutils_package():
yield
+@pytest.fixture
+def mock_index():
+ # set up a server which will simulate an alternate package index.
+ p_index = MockServer()
+ if p_index.server_port == 0:
+ # Some platforms (Jython) don't find a port to which to bind,
+ # so skip test for them.
+ pytest.skip("could not find a valid port")
+ p_index.start()
+ return p_index
+
+
class TestDistutilsPackage:
def test_bdist_egg_available_on_distutils_pkg(self, distutils_package):
run_setup('setup.py', ['bdist_egg'])
class TestSetupRequires:
- def test_setup_requires_honors_fetch_params(self):
+
+ def test_setup_requires_honors_fetch_params(self, mock_index, monkeypatch):
"""
When easy_install installs a source distribution which specifies
setup_requires, it should honor the fetch parameters (such as
- allow-hosts, index-url, and find-links).
+ index-url, and find-links).
"""
- # set up a server which will simulate an alternate package index.
- p_index = setuptools.tests.server.MockServer()
- p_index.start()
- netloc = 1
- p_index_loc = urllib.parse.urlparse(p_index.url)[netloc]
- if p_index_loc.endswith(':0'):
- # Some platforms (Jython) don't find a port to which to bind,
- # so skip this test for them.
- return
- with contexts.quiet():
- # create an sdist that has a build-time dependency.
- with TestSetupRequires.create_sdist() as dist_file:
- with contexts.tempdir() as temp_install_dir:
- with contexts.environment(PYTHONPATH=temp_install_dir):
- ei_params = [
- '--index-url', p_index.url,
- '--allow-hosts', p_index_loc,
- '--exclude-scripts',
- '--install-dir', temp_install_dir,
- dist_file,
- ]
- with sandbox.save_argv(['easy_install']):
- # attempt to install the dist. It should
- # fail because it doesn't exist.
- with pytest.raises(SystemExit):
- easy_install_pkg.main(ei_params)
- # there should have been two or three requests to the server
- # (three happens on Python 3.3a)
- assert 2 <= len(p_index.requests) <= 3
- assert p_index.requests[0].path == '/does-not-exist/'
+ monkeypatch.setenv(str('PIP_RETRIES'), str('0'))
+ monkeypatch.setenv(str('PIP_TIMEOUT'), str('0'))
+ monkeypatch.setenv(str('PIP_VERBOSE'), str('1'))
+ # create an sdist that has a build-time dependency.
+ with TestSetupRequires.create_sdist() as dist_file:
+ with contexts.tempdir() as temp_dir:
+ setup_py = os.path.join(temp_dir, 'setup.py')
+ with open(setup_py, 'w') as fp:
+ fp.write('__import__("setuptools").setup()')
+ temp_install_dir = os.path.join(temp_dir, 'target')
+ os.mkdir(temp_install_dir)
+ with contexts.environment(PYTHONPATH=temp_install_dir):
+ # attempt to install the dist. It should
+ # fail because it doesn't exist.
+ with pytest.raises(SystemExit):
+ run_setup(setup_py, ['easy_install',
+ '--exclude-scripts',
+ '--index-url', mock_index.url,
+ '--install-dir', temp_install_dir,
+ dist_file])
+ # there should have been one requests to the server
+ assert [r.path for r in mock_index.requests] == ['/does-not-exist/']
@staticmethod
@contextlib.contextmanager
@@ -500,7 +505,9 @@ class TestSetupRequires:
version="1.0",
setup_requires = ['does-not-exist'],
)
- """))])
+ """)),
+ ('setup.cfg', ''),
+ ])
yield dist_path
use_setup_cfg = (
@@ -632,6 +639,113 @@ class TestSetupRequires:
assert len(lines) > 0
assert lines[-1].strip() == '42'
+ def test_setup_requires_honors_pip_env(self, mock_index, monkeypatch):
+ monkeypatch.setenv(str('PIP_RETRIES'), str('0'))
+ monkeypatch.setenv(str('PIP_TIMEOUT'), str('0'))
+ monkeypatch.setenv(str('PIP_INDEX_URL'), mock_index.url)
+ with contexts.save_pkg_resources_state():
+ with contexts.tempdir() as temp_dir:
+ test_pkg = create_setup_requires_package(
+ temp_dir, 'python-xlib', '0.19',
+ setup_attrs=dict(dependency_links=[]))
+ test_setup_cfg = os.path.join(test_pkg, 'setup.cfg')
+ with open(test_setup_cfg, 'w') as fp:
+ fp.write(DALS(
+ '''
+ [easy_install]
+ index_url = https://pypi.org/legacy/
+ '''))
+ test_setup_py = os.path.join(test_pkg, 'setup.py')
+ with pytest.raises(distutils.errors.DistutilsError):
+ run_setup(test_setup_py, [str('--version')])
+ assert len(mock_index.requests) == 1
+ assert mock_index.requests[0].path == '/python-xlib/'
+
+ def test_setup_requires_with_pep508_url(self, mock_index, monkeypatch):
+ monkeypatch.setenv(str('PIP_RETRIES'), str('0'))
+ monkeypatch.setenv(str('PIP_TIMEOUT'), str('0'))
+ monkeypatch.setenv(str('PIP_INDEX_URL'), mock_index.url)
+ with contexts.save_pkg_resources_state():
+ with contexts.tempdir() as temp_dir:
+ dep_sdist = os.path.join(temp_dir, 'dep.tar.gz')
+ make_trivial_sdist(dep_sdist, 'dependency', '42')
+ dep_url = path_to_url(dep_sdist, authority='localhost')
+ test_pkg = create_setup_requires_package(
+ temp_dir,
+ 'python-xlib', '0.19', # Ignored (overriden by setup_attrs).
+ setup_attrs=dict(setup_requires='dependency @ %s' % dep_url))
+ test_setup_py = os.path.join(test_pkg, 'setup.py')
+ run_setup(test_setup_py, [str('--version')])
+ assert len(mock_index.requests) == 0
+
+ def test_setup_requires_with_allow_hosts(self, mock_index):
+ ''' The `allow-hosts` option in not supported anymore. '''
+ with contexts.save_pkg_resources_state():
+ with contexts.tempdir() as temp_dir:
+ test_pkg = os.path.join(temp_dir, 'test_pkg')
+ test_setup_py = os.path.join(test_pkg, 'setup.py')
+ test_setup_cfg = os.path.join(test_pkg, 'setup.cfg')
+ os.mkdir(test_pkg)
+ with open(test_setup_py, 'w') as fp:
+ fp.write(DALS(
+ '''
+ from setuptools import setup
+ setup(setup_requires='python-xlib')
+ '''))
+ with open(test_setup_cfg, 'w') as fp:
+ fp.write(DALS(
+ '''
+ [easy_install]
+ allow_hosts = *
+ '''))
+ with pytest.raises(distutils.errors.DistutilsError):
+ run_setup(test_setup_py, [str('--version')])
+ assert len(mock_index.requests) == 0
+
+ def test_setup_requires_with_python_requires(self, monkeypatch, tmpdir):
+ ''' Check `python_requires` is honored. '''
+ monkeypatch.setenv(str('PIP_RETRIES'), str('0'))
+ monkeypatch.setenv(str('PIP_TIMEOUT'), str('0'))
+ monkeypatch.setenv(str('PIP_NO_INDEX'), str('1'))
+ monkeypatch.setenv(str('PIP_VERBOSE'), str('1'))
+ dep_1_0_sdist = 'dep-1.0.tar.gz'
+ dep_1_0_url = path_to_url(str(tmpdir / dep_1_0_sdist))
+ dep_1_0_python_requires = '>=2.7'
+ make_python_requires_sdist(str(tmpdir / dep_1_0_sdist), 'dep', '1.0', dep_1_0_python_requires)
+ dep_2_0_sdist = 'dep-2.0.tar.gz'
+ dep_2_0_url = path_to_url(str(tmpdir / dep_2_0_sdist))
+ dep_2_0_python_requires = '!=' + '.'.join(map(str, sys.version_info[:2])) + '.*'
+ make_python_requires_sdist(str(tmpdir / dep_2_0_sdist), 'dep', '2.0', dep_2_0_python_requires)
+ index = tmpdir / 'index.html'
+ index.write_text(DALS(
+ '''
+ <!DOCTYPE html>
+ <html><head><title>Links for dep</title></head>
+ <body>
+ <h1>Links for dep</h1>
+ <a href="{dep_1_0_url}" data-requires-python="{dep_1_0_python_requires}">{dep_1_0_sdist}</a><br/>
+ <a href="{dep_2_0_url}" data-requires-python="{dep_2_0_python_requires}">{dep_2_0_sdist}</a><br/>
+ </body>
+ </html>
+ ''').format(
+ dep_1_0_url=dep_1_0_url,
+ dep_1_0_sdist=dep_1_0_sdist,
+ dep_1_0_python_requires=dep_1_0_python_requires,
+ dep_2_0_url=dep_2_0_url,
+ dep_2_0_sdist=dep_2_0_sdist,
+ dep_2_0_python_requires=dep_2_0_python_requires,
+ ), 'utf-8')
+ index_url = path_to_url(str(index))
+ with contexts.save_pkg_resources_state():
+ test_pkg = create_setup_requires_package(
+ str(tmpdir),
+ 'python-xlib', '0.19', # Ignored (overriden by setup_attrs).
+ setup_attrs=dict(setup_requires='dep', dependency_links=[index_url]))
+ test_setup_py = os.path.join(test_pkg, 'setup.py')
+ run_setup(test_setup_py, [str('--version')])
+ eggs = list(map(str, pkg_resources.find_distributions(os.path.join(test_pkg, '.eggs'))))
+ assert eggs == ['dep 1.0']
+
def make_trivial_sdist(dist_path, distname, version):
"""
@@ -647,7 +761,9 @@ def make_trivial_sdist(dist_path, distname, version):
name=%r,
version=%r
)
- """ % (distname, version)))])
+ """ % (distname, version))),
+ ('setup.cfg', ''),
+ ])
def make_nspkg_sdist(dist_path, distname, version):
@@ -683,12 +799,29 @@ def make_nspkg_sdist(dist_path, distname, version):
make_sdist(dist_path, files)
+def make_python_requires_sdist(dist_path, distname, version, python_requires):
+ make_sdist(dist_path, [
+ ('setup.py', DALS("""\
+ import setuptools
+ setuptools.setup(
+ name={name!r},
+ version={version!r},
+ python_requires={python_requires!r},
+ )
+ """).format(name=distname, version=version,
+ python_requires=python_requires)),
+ ('setup.cfg', ''),
+ ])
+
+
def make_sdist(dist_path, files):
"""
Create a simple sdist tarball at dist_path, containing the files
listed in ``files`` as ``(filename, content)`` tuples.
"""
+ # Distributions with only one file don't play well with pip.
+ assert len(files) > 1
with tarfile.open(dist_path, 'w:gz') as dist:
for filename, content in files:
file_bytes = io.BytesIO(content.encode('utf-8'))
@@ -721,8 +854,8 @@ def create_setup_requires_package(path, distname='foobar', version='0.1',
test_pkg = os.path.join(path, 'test_pkg')
os.mkdir(test_pkg)
+ # setup.cfg
if use_setup_cfg:
- test_setup_cfg = os.path.join(test_pkg, 'setup.cfg')
options = []
metadata = []
for name in use_setup_cfg:
@@ -734,8 +867,7 @@ def create_setup_requires_package(path, distname='foobar', version='0.1',
if isinstance(value, (tuple, list)):
value = ';'.join(value)
section.append('%s: %s' % (name, value))
- with open(test_setup_cfg, 'w') as f:
- f.write(DALS(
+ test_setup_cfg_contents = DALS(
"""
[metadata]
{metadata}
@@ -745,16 +877,19 @@ def create_setup_requires_package(path, distname='foobar', version='0.1',
).format(
options='\n'.join(options),
metadata='\n'.join(metadata),
- ))
-
- test_setup_py = os.path.join(test_pkg, 'setup.py')
+ )
+ else:
+ test_setup_cfg_contents = ''
+ with open(os.path.join(test_pkg, 'setup.cfg'), 'w') as f:
+ f.write(test_setup_cfg_contents)
+ # setup.py
if setup_py_template is None:
setup_py_template = DALS("""\
import setuptools
setuptools.setup(**%r)
""")
- with open(test_setup_py, 'w') as f:
+ with open(os.path.join(test_pkg, 'setup.py'), 'w') as f:
f.write(setup_py_template % test_setup_attrs)
foobar_path = os.path.join(path, '%s-%s.tar.gz' % (distname, version))
diff --git a/setuptools/tests/test_egg_info.py b/setuptools/tests/test_egg_info.py
index 316eb2ed..0db204ba 100644
--- a/setuptools/tests/test_egg_info.py
+++ b/setuptools/tests/test_egg_info.py
@@ -524,27 +524,27 @@ class TestEggInfo:
[metadata]
license_file = LICENSE
"""),
- 'LICENSE': DALS("Test license")
+ 'LICENSE': "Test license"
}, True), # with license
({
'setup.cfg': DALS("""
[metadata]
license_file = INVALID_LICENSE
"""),
- 'LICENSE': DALS("Test license")
+ 'LICENSE': "Test license"
}, False), # with an invalid license
({
'setup.cfg': DALS("""
"""),
- 'LICENSE': DALS("Test license")
+ 'LICENSE': "Test license"
}, False), # no license_file attribute
({
'setup.cfg': DALS("""
[metadata]
license_file = LICENSE
"""),
- 'MANIFEST.in': DALS("exclude LICENSE"),
- 'LICENSE': DALS("Test license")
+ 'MANIFEST.in': "exclude LICENSE",
+ 'LICENSE': "Test license"
}, False) # license file is manually excluded
])
def test_setup_cfg_license_file(
@@ -567,6 +567,204 @@ class TestEggInfo:
assert 'LICENSE' not in sources_text
assert 'INVALID_LICENSE' not in sources_text # for invalid license test
+ @pytest.mark.parametrize("files, incl_licenses, excl_licenses", [
+ ({
+ 'setup.cfg': DALS("""
+ [metadata]
+ license_files =
+ LICENSE-ABC
+ LICENSE-XYZ
+ """),
+ 'LICENSE-ABC': "ABC license",
+ 'LICENSE-XYZ': "XYZ license"
+ }, ['LICENSE-ABC', 'LICENSE-XYZ'], []), # with licenses
+ ({
+ 'setup.cfg': DALS("""
+ [metadata]
+ license_files = LICENSE-ABC, LICENSE-XYZ
+ """),
+ 'LICENSE-ABC': "ABC license",
+ 'LICENSE-XYZ': "XYZ license"
+ }, ['LICENSE-ABC', 'LICENSE-XYZ'], []), # with commas
+ ({
+ 'setup.cfg': DALS("""
+ [metadata]
+ license_files =
+ LICENSE-ABC
+ """),
+ 'LICENSE-ABC': "ABC license",
+ 'LICENSE-XYZ': "XYZ license"
+ }, ['LICENSE-ABC'], ['LICENSE-XYZ']), # with one license
+ ({
+ 'setup.cfg': DALS("""
+ [metadata]
+ license_files =
+ """),
+ 'LICENSE-ABC': "ABC license",
+ 'LICENSE-XYZ': "XYZ license"
+ }, [], ['LICENSE-ABC', 'LICENSE-XYZ']), # empty
+ ({
+ 'setup.cfg': DALS("""
+ [metadata]
+ license_files = LICENSE-XYZ
+ """),
+ 'LICENSE-ABC': "ABC license",
+ 'LICENSE-XYZ': "XYZ license"
+ }, ['LICENSE-XYZ'], ['LICENSE-ABC']), # on same line
+ ({
+ 'setup.cfg': DALS("""
+ [metadata]
+ license_files =
+ LICENSE-ABC
+ INVALID_LICENSE
+ """),
+ 'LICENSE-ABC': "Test license"
+ }, ['LICENSE-ABC'], ['INVALID_LICENSE']), # with an invalid license
+ ({
+ 'setup.cfg': DALS("""
+ """),
+ 'LICENSE': "Test license"
+ }, [], ['LICENSE']), # no license_files attribute
+ ({
+ 'setup.cfg': DALS("""
+ [metadata]
+ license_files = LICENSE
+ """),
+ 'MANIFEST.in': "exclude LICENSE",
+ 'LICENSE': "Test license"
+ }, [], ['LICENSE']), # license file is manually excluded
+ ({
+ 'setup.cfg': DALS("""
+ [metadata]
+ license_files =
+ LICENSE-ABC
+ LICENSE-XYZ
+ """),
+ 'MANIFEST.in': "exclude LICENSE-XYZ",
+ 'LICENSE-ABC': "ABC license",
+ 'LICENSE-XYZ': "XYZ license"
+ }, ['LICENSE-ABC'], ['LICENSE-XYZ']) # subset is manually excluded
+ ])
+ def test_setup_cfg_license_files(
+ self, tmpdir_cwd, env, files, incl_licenses, excl_licenses):
+ self._create_project()
+ build_files(files)
+
+ environment.run_setup_py(
+ cmd=['egg_info'],
+ pypath=os.pathsep.join([env.paths['lib'], str(tmpdir_cwd)])
+ )
+ egg_info_dir = os.path.join('.', 'foo.egg-info')
+
+ with open(os.path.join(egg_info_dir, 'SOURCES.txt')) as sources_file:
+ sources_lines = list(line.strip() for line in sources_file)
+
+ for lf in incl_licenses:
+ assert sources_lines.count(lf) == 1
+
+ for lf in excl_licenses:
+ assert sources_lines.count(lf) == 0
+
+ @pytest.mark.parametrize("files, incl_licenses, excl_licenses", [
+ ({
+ 'setup.cfg': DALS("""
+ [metadata]
+ license_file =
+ license_files =
+ """),
+ 'LICENSE-ABC': "ABC license",
+ 'LICENSE-XYZ': "XYZ license"
+ }, [], ['LICENSE-ABC', 'LICENSE-XYZ']), # both empty
+ ({
+ 'setup.cfg': DALS("""
+ [metadata]
+ license_file =
+ LICENSE-ABC
+ LICENSE-XYZ
+ """),
+ 'LICENSE-ABC': "ABC license",
+ 'LICENSE-XYZ': "XYZ license"
+ }, [], ['LICENSE-ABC', 'LICENSE-XYZ']), # license_file is still singular
+ ({
+ 'setup.cfg': DALS("""
+ [metadata]
+ license_file = LICENSE-ABC
+ license_files =
+ LICENSE-XYZ
+ LICENSE-PQR
+ """),
+ 'LICENSE-ABC': "ABC license",
+ 'LICENSE-PQR': "PQR license",
+ 'LICENSE-XYZ': "XYZ license"
+ }, ['LICENSE-ABC', 'LICENSE-PQR', 'LICENSE-XYZ'], []), # combined
+ ({
+ 'setup.cfg': DALS("""
+ [metadata]
+ license_file = LICENSE-ABC
+ license_files =
+ LICENSE-ABC
+ LICENSE-XYZ
+ LICENSE-PQR
+ """),
+ 'LICENSE-ABC': "ABC license",
+ 'LICENSE-PQR': "PQR license",
+ 'LICENSE-XYZ': "XYZ license"
+ }, ['LICENSE-ABC', 'LICENSE-PQR', 'LICENSE-XYZ'], []), # duplicate license
+ ({
+ 'setup.cfg': DALS("""
+ [metadata]
+ license_file = LICENSE-ABC
+ license_files =
+ LICENSE-XYZ
+ """),
+ 'LICENSE-ABC': "ABC license",
+ 'LICENSE-PQR': "PQR license",
+ 'LICENSE-XYZ': "XYZ license"
+ }, ['LICENSE-ABC', 'LICENSE-XYZ'], ['LICENSE-PQR']), # combined subset
+ ({
+ 'setup.cfg': DALS("""
+ [metadata]
+ license_file = LICENSE-ABC
+ license_files =
+ LICENSE-XYZ
+ LICENSE-PQR
+ """),
+ 'LICENSE-PQR': "Test license"
+ }, ['LICENSE-PQR'], ['LICENSE-ABC', 'LICENSE-XYZ']), # with invalid licenses
+ ({
+ 'setup.cfg': DALS("""
+ [metadata]
+ license_file = LICENSE-ABC
+ license_files =
+ LICENSE-PQR
+ LICENSE-XYZ
+ """),
+ 'MANIFEST.in': "exclude LICENSE-ABC\nexclude LICENSE-PQR",
+ 'LICENSE-ABC': "ABC license",
+ 'LICENSE-PQR': "PQR license",
+ 'LICENSE-XYZ': "XYZ license"
+ }, ['LICENSE-XYZ'], ['LICENSE-ABC', 'LICENSE-PQR']) # manually excluded
+ ])
+ def test_setup_cfg_license_file_license_files(
+ self, tmpdir_cwd, env, files, incl_licenses, excl_licenses):
+ self._create_project()
+ build_files(files)
+
+ environment.run_setup_py(
+ cmd=['egg_info'],
+ pypath=os.pathsep.join([env.paths['lib'], str(tmpdir_cwd)])
+ )
+ egg_info_dir = os.path.join('.', 'foo.egg-info')
+
+ with open(os.path.join(egg_info_dir, 'SOURCES.txt')) as sources_file:
+ sources_lines = list(line.strip() for line in sources_file)
+
+ for lf in incl_licenses:
+ assert sources_lines.count(lf) == 1
+
+ for lf in excl_licenses:
+ assert sources_lines.count(lf) == 0
+
def test_long_description_content_type(self, tmpdir_cwd, env):
# Test that specifying a `long_description_content_type` keyword arg to
# the `setup` function results in writing a `Description-Content-Type`
diff --git a/setuptools/tests/test_integration.py b/setuptools/tests/test_integration.py
index 1c0b2b18..f1a27f8b 100644
--- a/setuptools/tests/test_integration.py
+++ b/setuptools/tests/test_integration.py
@@ -64,7 +64,7 @@ def install_context(request, tmpdir, monkeypatch):
monkeypatch.setattr('site.USER_BASE', user_base.strpath)
monkeypatch.setattr('site.USER_SITE', user_site.strpath)
monkeypatch.setattr('sys.path', sys.path + [install_dir.strpath])
- monkeypatch.setenv('PYTHONPATH', os.path.pathsep.join(sys.path))
+ monkeypatch.setenv(str('PYTHONPATH'), str(os.path.pathsep.join(sys.path)))
# Set up the command for performing the installation.
dist = Distribution()
diff --git a/setuptools/tests/test_namespaces.py b/setuptools/tests/test_namespaces.py
index f937d981..3c5df68a 100644
--- a/setuptools/tests/test_namespaces.py
+++ b/setuptools/tests/test_namespaces.py
@@ -64,9 +64,8 @@ class TestNamespaces:
target.mkdir()
install_cmd = [
sys.executable,
- '-m', 'easy_install',
- '-d', str(target),
- str(pkg),
+ '-m', 'pip.__main__', 'install',
+ '-t', str(target), str(pkg),
]
with test.test.paths_on_pythonpath([str(target)]):
subprocess.check_call(install_cmd)
diff --git a/setuptools/tests/test_register.py b/setuptools/tests/test_register.py
index 96114595..98605806 100644
--- a/setuptools/tests/test_register.py
+++ b/setuptools/tests/test_register.py
@@ -1,43 +1,22 @@
-import mock
-from distutils import log
-
-import pytest
-
from setuptools.command.register import register
from setuptools.dist import Distribution
+from setuptools.errors import RemovedCommandError
+try:
+ from unittest import mock
+except ImportError:
+ import mock
-class TestRegisterTest:
- def test_warns_deprecation(self):
- dist = Distribution()
-
- cmd = register(dist)
- cmd.run_command = mock.Mock()
- cmd.send_metadata = mock.Mock()
- cmd.announce = mock.Mock()
-
- cmd.run()
+import pytest
- cmd.announce.assert_called_with(
- "WARNING: Registering is deprecated, use twine to upload instead "
- "(https://pypi.org/p/twine/)",
- log.WARN
- )
- def test_warns_deprecation_when_raising(self):
+class TestRegister:
+ def test_register_exception(self):
+ """Ensure that the register command has been properly removed."""
dist = Distribution()
+ dist.dist_files = [(mock.Mock(), mock.Mock(), mock.Mock())]
cmd = register(dist)
- cmd.run_command = mock.Mock()
- cmd.send_metadata = mock.Mock()
- cmd.send_metadata.side_effect = Exception
- cmd.announce = mock.Mock()
- with pytest.raises(Exception):
+ with pytest.raises(RemovedCommandError):
cmd.run()
-
- cmd.announce.assert_called_with(
- "WARNING: Registering is deprecated, use twine to upload instead "
- "(https://pypi.org/p/twine/)",
- log.WARN
- )
diff --git a/setuptools/tests/test_test.py b/setuptools/tests/test_test.py
index faaa6ba9..6242a018 100644
--- a/setuptools/tests/test_test.py
+++ b/setuptools/tests/test_test.py
@@ -1,7 +1,8 @@
-# -*- coding: UTF-8 -*-
+# -*- coding: utf-8 -*-
from __future__ import unicode_literals
+import mock
from distutils import log
import os
@@ -85,9 +86,7 @@ def test_test(capfd):
dist.script_name = 'setup.py'
cmd = test(dist)
cmd.ensure_finalized()
- # The test runner calls sys.exit
- with contexts.suppress_exceptions(SystemExit):
- cmd.run()
+ cmd.run()
out, err = capfd.readouterr()
assert out == 'Foo\n'
@@ -119,8 +118,55 @@ def test_tests_are_run_once(capfd):
dist.script_name = 'setup.py'
cmd = test(dist)
cmd.ensure_finalized()
- # The test runner calls sys.exit
- with contexts.suppress_exceptions(SystemExit):
- cmd.run()
+ cmd.run()
out, err = capfd.readouterr()
assert out == 'Foo\n'
+
+
+@pytest.mark.usefixtures('sample_test')
+def test_warns_deprecation(capfd):
+ params = dict(
+ name='foo',
+ packages=['name', 'name.space', 'name.space.tests'],
+ namespace_packages=['name'],
+ test_suite='name.space.tests.test_suite',
+ use_2to3=True
+ )
+ dist = Distribution(params)
+ dist.script_name = 'setup.py'
+ cmd = test(dist)
+ cmd.ensure_finalized()
+ cmd.announce = mock.Mock()
+ cmd.run()
+ capfd.readouterr()
+ msg = (
+ "WARNING: Testing via this command is deprecated and will be "
+ "removed in a future version. Users looking for a generic test "
+ "entry point independent of test runner are encouraged to use "
+ "tox."
+ )
+ cmd.announce.assert_any_call(msg, log.WARN)
+
+
+@pytest.mark.usefixtures('sample_test')
+def test_deprecation_stderr(capfd):
+ params = dict(
+ name='foo',
+ packages=['name', 'name.space', 'name.space.tests'],
+ namespace_packages=['name'],
+ test_suite='name.space.tests.test_suite',
+ use_2to3=True
+ )
+ dist = Distribution(params)
+ dist.script_name = 'setup.py'
+ cmd = test(dist)
+ cmd.ensure_finalized()
+ cmd.run()
+ out, err = capfd.readouterr()
+ msg = (
+ "WARNING: Testing via this command is deprecated and will be "
+ "removed in a future version. Users looking for a generic test "
+ "entry point independent of test runner are encouraged to use "
+ "tox.\n"
+ )
+ assert msg in err
diff --git a/setuptools/tests/test_upload.py b/setuptools/tests/test_upload.py
index 320c6959..7586cb26 100644
--- a/setuptools/tests/test_upload.py
+++ b/setuptools/tests/test_upload.py
@@ -1,213 +1,22 @@
-import mock
-import os
-import re
-
-from distutils import log
-from distutils.errors import DistutilsError
-
-import pytest
-
from setuptools.command.upload import upload
from setuptools.dist import Distribution
-from setuptools.extern import six
-
-
-def _parse_upload_body(body):
- boundary = u'\r\n----------------GHSKFJDLGDS7543FJKLFHRE75642756743254'
- entries = []
- name_re = re.compile(u'^Content-Disposition: form-data; name="([^\"]+)"')
-
- for entry in body.split(boundary):
- pair = entry.split(u'\r\n\r\n')
- if not len(pair) == 2:
- continue
-
- key, value = map(six.text_type.strip, pair)
- m = name_re.match(key)
- if m is not None:
- key = m.group(1)
-
- entries.append((key, value))
-
- return entries
-
-
-@pytest.fixture
-def patched_upload(tmpdir):
- class Fix:
- def __init__(self, cmd, urlopen):
- self.cmd = cmd
- self.urlopen = urlopen
-
- def __iter__(self):
- return iter((self.cmd, self.urlopen))
-
- def get_uploaded_metadata(self):
- request = self.urlopen.call_args_list[0][0][0]
- body = request.data.decode('utf-8')
- entries = dict(_parse_upload_body(body))
-
- return entries
+from setuptools.errors import RemovedCommandError
- class ResponseMock(mock.Mock):
- def getheader(self, name, default=None):
- """Mocked getheader method for response object"""
- return {
- 'content-type': 'text/plain; charset=utf-8',
- }.get(name.lower(), default)
+try:
+ from unittest import mock
+except ImportError:
+ import mock
- with mock.patch('setuptools.command.upload.urlopen') as urlopen:
- urlopen.return_value = ResponseMock()
- urlopen.return_value.getcode.return_value = 200
- urlopen.return_value.read.return_value = b''
-
- content = os.path.join(str(tmpdir), "content_data")
-
- with open(content, 'w') as f:
- f.write("Some content")
-
- dist = Distribution()
- dist.dist_files = [('sdist', '3.7.0', content)]
-
- cmd = upload(dist)
- cmd.announce = mock.Mock()
- cmd.username = 'user'
- cmd.password = 'hunter2'
-
- yield Fix(cmd, urlopen)
-
-
-class TestUploadTest:
- def test_upload_metadata(self, patched_upload):
- cmd, patch = patched_upload
-
- # Set the metadata version to 2.1
- cmd.distribution.metadata.metadata_version = '2.1'
-
- # Run the command
- cmd.ensure_finalized()
- cmd.run()
-
- # Make sure we did the upload
- patch.assert_called_once()
-
- # Make sure the metadata version is correct in the headers
- entries = patched_upload.get_uploaded_metadata()
- assert entries['metadata_version'] == '2.1'
-
- def test_warns_deprecation(self):
- dist = Distribution()
- dist.dist_files = [(mock.Mock(), mock.Mock(), mock.Mock())]
-
- cmd = upload(dist)
- cmd.upload_file = mock.Mock()
- cmd.announce = mock.Mock()
-
- cmd.run()
+import pytest
- cmd.announce.assert_called_once_with(
- "WARNING: Uploading via this command is deprecated, use twine to "
- "upload instead (https://pypi.org/p/twine/)",
- log.WARN
- )
- def test_warns_deprecation_when_raising(self):
+class TestUpload:
+ def test_upload_exception(self):
+ """Ensure that the register command has been properly removed."""
dist = Distribution()
dist.dist_files = [(mock.Mock(), mock.Mock(), mock.Mock())]
cmd = upload(dist)
- cmd.upload_file = mock.Mock()
- cmd.upload_file.side_effect = Exception
- cmd.announce = mock.Mock()
-
- with pytest.raises(Exception):
- cmd.run()
-
- cmd.announce.assert_called_once_with(
- "WARNING: Uploading via this command is deprecated, use twine to "
- "upload instead (https://pypi.org/p/twine/)",
- log.WARN
- )
-
- @pytest.mark.parametrize('url', [
- 'https://example.com/a;parameter', # Has parameters
- 'https://example.com/a?query', # Has query
- 'https://example.com/a#fragment', # Has fragment
- 'ftp://example.com', # Invalid scheme
-
- ])
- def test_upload_file_invalid_url(self, url, patched_upload):
- patched_upload.urlopen.side_effect = Exception("Should not be reached")
-
- cmd = patched_upload.cmd
- cmd.repository = url
-
- cmd.ensure_finalized()
- with pytest.raises(AssertionError):
- cmd.run()
-
- def test_upload_file_http_error(self, patched_upload):
- patched_upload.urlopen.side_effect = six.moves.urllib.error.HTTPError(
- 'https://example.com',
- 404,
- 'File not found',
- None,
- None
- )
-
- cmd = patched_upload.cmd
- cmd.ensure_finalized()
- with pytest.raises(DistutilsError):
+ with pytest.raises(RemovedCommandError):
cmd.run()
-
- cmd.announce.assert_any_call(
- 'Upload failed (404): File not found',
- log.ERROR)
-
- def test_upload_file_os_error(self, patched_upload):
- patched_upload.urlopen.side_effect = OSError("Invalid")
-
- cmd = patched_upload.cmd
- cmd.ensure_finalized()
-
- with pytest.raises(OSError):
- cmd.run()
-
- cmd.announce.assert_any_call('Invalid', log.ERROR)
-
- @mock.patch('setuptools.command.upload.spawn')
- def test_upload_file_gpg(self, spawn, patched_upload):
- cmd, urlopen = patched_upload
-
- cmd.sign = True
- cmd.identity = "Alice"
- cmd.dry_run = True
- content_fname = cmd.distribution.dist_files[0][2]
- signed_file = content_fname + '.asc'
-
- with open(signed_file, 'wb') as f:
- f.write("signed-data".encode('utf-8'))
-
- cmd.ensure_finalized()
- cmd.run()
-
- # Make sure that GPG was called
- spawn.assert_called_once_with([
- "gpg", "--detach-sign", "--local-user", "Alice", "-a",
- content_fname
- ], dry_run=True)
-
- # Read the 'signed' data that was transmitted
- entries = patched_upload.get_uploaded_metadata()
- assert entries['gpg_signature'] == 'signed-data'
-
- def test_show_response_no_error(self, patched_upload):
- # This test is just that show_response doesn't throw an error
- # It is not really important what the printed response looks like
- # in a deprecated command, but we don't want to introduce new
- # errors when importing this function from distutils
-
- patched_upload.cmd.show_response = True
- patched_upload.cmd.ensure_finalized()
- patched_upload.cmd.run()
diff --git a/setuptools/tests/test_virtualenv.py b/setuptools/tests/test_virtualenv.py
index 74a1284c..cd3d9313 100644
--- a/setuptools/tests/test_virtualenv.py
+++ b/setuptools/tests/test_virtualenv.py
@@ -121,14 +121,12 @@ def test_pip_upgrade_from_source(pip_version, virtualenv):
virtualenv.run('pip install --no-cache-dir --upgrade ' + sdist)
-def test_test_command_install_requirements(bare_virtualenv, tmpdir):
+def _check_test_command_install_requirements(virtualenv, tmpdir):
"""
Check the test command will install all required dependencies.
"""
- bare_virtualenv.run(' && '.join((
- 'cd {source}',
- 'python setup.py develop',
- )).format(source=SOURCE_DIR))
+ # Install setuptools.
+ virtualenv.run('python setup.py develop', cd=SOURCE_DIR)
def sdist(distname, version):
dist_path = tmpdir.join('%s-%s.tar.gz' % (distname, version))
@@ -179,12 +177,20 @@ def test_test_command_install_requirements(bare_virtualenv, tmpdir):
open('success', 'w').close()
'''))
# Run test command for test package.
- bare_virtualenv.run(' && '.join((
+ virtualenv.run(' && '.join((
'cd {tmpdir}',
'python setup.py test -s test',
)).format(tmpdir=tmpdir))
assert tmpdir.join('success').check()
+def test_test_command_install_requirements(virtualenv, tmpdir):
+ # Ensure pip/wheel packages are installed.
+ virtualenv.run("python -c \"__import__('pkg_resources').require(['pip', 'wheel'])\"")
+ _check_test_command_install_requirements(virtualenv, tmpdir)
+
+def test_test_command_install_requirements_when_using_easy_install(bare_virtualenv, tmpdir):
+ _check_test_command_install_requirements(bare_virtualenv, tmpdir)
+
def test_no_missing_dependencies(bare_virtualenv):
"""
diff --git a/setuptools/tests/test_wheel.py b/setuptools/tests/test_wheel.py
index e85a4a7e..d50816c2 100644
--- a/setuptools/tests/test_wheel.py
+++ b/setuptools/tests/test_wheel.py
@@ -451,6 +451,34 @@ WHEEL_INSTALL_TESTS = (
),
dict(
+ id='empty_namespace_package',
+ file_defs={
+ 'foobar': {
+ '__init__.py': "__import__('pkg_resources').declare_namespace(__name__)",
+ },
+ },
+ setup_kwargs=dict(
+ namespace_packages=['foobar'],
+ packages=['foobar'],
+ ),
+ install_tree=flatten_tree({
+ 'foo-1.0-py{py_version}.egg': [
+ 'foo-1.0-py{py_version}-nspkg.pth',
+ {'EGG-INFO': [
+ 'PKG-INFO',
+ 'RECORD',
+ 'WHEEL',
+ 'namespace_packages.txt',
+ 'top_level.txt',
+ ]},
+ {'foobar': [
+ '__init__.py',
+ ]},
+ ]
+ }),
+ ),
+
+ dict(
id='data_in_package',
file_defs={
'foo': {
diff --git a/setuptools/wheel.py b/setuptools/wheel.py
index 502f8410..3effd79b 100644
--- a/setuptools/wheel.py
+++ b/setuptools/wheel.py
@@ -1,6 +1,7 @@
"""Wheels support."""
from distutils.util import get_platform
+from distutils import log
import email
import itertools
import os
@@ -162,11 +163,17 @@ class Wheel:
extras_require=extras_require,
),
)
- write_requirements(
- setup_dist.get_command_obj('egg_info'),
- None,
- os.path.join(egg_info, 'requires.txt'),
- )
+ # Temporarily disable info traces.
+ log_threshold = log._global_log.threshold
+ log.set_threshold(log.WARN)
+ try:
+ write_requirements(
+ setup_dist.get_command_obj('egg_info'),
+ None,
+ os.path.join(egg_info, 'requires.txt'),
+ )
+ finally:
+ log.set_threshold(log_threshold)
@staticmethod
def _move_data_entries(destination_eggdir, dist_data):
@@ -206,6 +213,8 @@ class Wheel:
for mod in namespace_packages:
mod_dir = os.path.join(destination_eggdir, *mod.split('.'))
mod_init = os.path.join(mod_dir, '__init__.py')
- if os.path.exists(mod_dir) and not os.path.exists(mod_init):
+ if not os.path.exists(mod_dir):
+ os.mkdir(mod_dir)
+ if not os.path.exists(mod_init):
with open(mod_init, 'w') as fp:
fp.write(NAMESPACE_PACKAGE_INIT)
diff --git a/tests/manual_test.py b/tests/manual_test.py
deleted file mode 100644
index 99db4b01..00000000
--- a/tests/manual_test.py
+++ /dev/null
@@ -1,100 +0,0 @@
-#!/usr/bin/env python
-
-import sys
-import os
-import shutil
-import tempfile
-import subprocess
-from distutils.command.install import INSTALL_SCHEMES
-from string import Template
-
-from setuptools.extern.six.moves import urllib
-
-
-def _system_call(*args):
- assert subprocess.call(args) == 0
-
-
-def tempdir(func):
- def _tempdir(*args, **kwargs):
- test_dir = tempfile.mkdtemp()
- old_dir = os.getcwd()
- os.chdir(test_dir)
- try:
- return func(*args, **kwargs)
- finally:
- os.chdir(old_dir)
- shutil.rmtree(test_dir)
-
- return _tempdir
-
-
-SIMPLE_BUILDOUT = """\
-[buildout]
-
-parts = eggs
-
-[eggs]
-recipe = zc.recipe.egg
-
-eggs =
- extensions
-"""
-
-BOOTSTRAP = 'http://downloads.buildout.org/1/bootstrap.py'
-PYVER = sys.version.split()[0][:3]
-
-_VARS = {'base': '.',
- 'py_version_short': PYVER}
-
-scheme = 'nt' if sys.platform == 'win32' else 'unix_prefix'
-PURELIB = INSTALL_SCHEMES[scheme]['purelib']
-
-
-@tempdir
-def test_virtualenv():
- """virtualenv with setuptools"""
- purelib = os.path.abspath(Template(PURELIB).substitute(**_VARS))
- _system_call('virtualenv', '--no-site-packages', '.')
- _system_call('bin/easy_install', 'setuptools==dev')
- # linux specific
- site_pkg = os.listdir(purelib)
- site_pkg.sort()
- assert 'setuptools' in site_pkg[0]
- easy_install = os.path.join(purelib, 'easy-install.pth')
- with open(easy_install) as f:
- res = f.read()
- assert 'setuptools' in res
-
-
-@tempdir
-def test_full():
- """virtualenv + pip + buildout"""
- _system_call('virtualenv', '--no-site-packages', '.')
- _system_call('bin/easy_install', '-q', 'setuptools==dev')
- _system_call('bin/easy_install', '-qU', 'setuptools==dev')
- _system_call('bin/easy_install', '-q', 'pip')
- _system_call('bin/pip', 'install', '-q', 'zc.buildout')
-
- with open('buildout.cfg', 'w') as f:
- f.write(SIMPLE_BUILDOUT)
-
- with open('bootstrap.py', 'w') as f:
- f.write(urllib.request.urlopen(BOOTSTRAP).read())
-
- _system_call('bin/python', 'bootstrap.py')
- _system_call('bin/buildout', '-q')
- eggs = os.listdir('eggs')
- eggs.sort()
- assert len(eggs) == 3
- assert eggs[1].startswith('setuptools')
- del eggs[1]
- assert eggs == [
- 'extensions-0.3-py2.6.egg',
- 'zc.recipe.egg-1.2.2-py2.6.egg',
- ]
-
-
-if __name__ == '__main__':
- test_virtualenv()
- test_full()
diff --git a/tests/requirements.txt b/tests/requirements.txt
index cb3e6726..1f8bd19d 100644
--- a/tests/requirements.txt
+++ b/tests/requirements.txt
@@ -9,4 +9,4 @@ coverage>=4.5.1
pytest-cov>=2.5.1
paver; python_version>="3.6"
futures; python_version=="2.7"
-pip==18.1 # Temporary workaround for #1644.
+pip>=19.1 # For proper file:// URLs support.
diff --git a/tools/tox_pip.py b/tools/tox_pip.py
new file mode 100644
index 00000000..5aeca805
--- /dev/null
+++ b/tools/tox_pip.py
@@ -0,0 +1,38 @@
+import os
+import shutil
+import subprocess
+import sys
+from glob import glob
+
+VIRTUAL_ENV = os.environ['VIRTUAL_ENV']
+TOX_PIP_DIR = os.path.join(VIRTUAL_ENV, 'pip')
+
+
+def pip(args):
+ # First things first, get a recent (stable) version of pip.
+ if not os.path.exists(TOX_PIP_DIR):
+ subprocess.check_call([sys.executable, '-m', 'pip',
+ '--disable-pip-version-check',
+ 'install', '-t', TOX_PIP_DIR,
+ 'pip'])
+ shutil.rmtree(glob(os.path.join(TOX_PIP_DIR, 'pip-*.dist-info'))[0])
+ # And use that version.
+ pypath = os.environ.get('PYTHONPATH')
+ pypath = pypath.split(os.pathsep) if pypath is not None else []
+ pypath.insert(0, TOX_PIP_DIR)
+ os.environ['PYTHONPATH'] = os.pathsep.join(pypath)
+ # Disable PEP 517 support when using editable installs.
+ for n, a in enumerate(args):
+ if not a.startswith('-'):
+ if a in 'install' and '-e' in args[n:]:
+ args.insert(n + 1, '--no-use-pep517')
+ break
+ # Fix call for setuptools editable install.
+ for n, a in enumerate(args):
+ if a == '.':
+ args[n] = os.getcwd()
+ subprocess.check_call([sys.executable, '-m', 'pip'] + args, cwd=TOX_PIP_DIR)
+
+
+if __name__ == '__main__':
+ pip(sys.argv[1:])
diff --git a/tox.ini b/tox.ini
index e0eef95a..5d439cb3 100644
--- a/tox.ini
+++ b/tox.ini
@@ -7,14 +7,16 @@
[tox]
envlist=python
+[helpers]
+# Wrapper for calls to pip that make sure the version being used is a
+# up-to-date, and to prevent the current working directory from being
+# added to `sys.path`.
+pip = python {toxinidir}/tools/tox_pip.py
+
[testenv]
-deps=-rtests/requirements.txt
-# Changed from default (`python -m pip ...`)
-# to prevent the current working directory
-# from being added to `sys.path`.
-install_command=python -c 'import sys; sys.path.remove(""); from pkg_resources import load_entry_point; load_entry_point("pip", "console_scripts", "pip")()' install {opts} {packages}
-# Same as above.
-list_dependencies_command={envbindir}/pip freeze --all
+deps=-r{toxinidir}/tests/requirements.txt
+install_command = {[helpers]pip} install {opts} {packages}
+list_dependencies_command = {[helpers]pip} freeze --all
setenv=COVERAGE_FILE={toxworkdir}/.coverage.{envname}
# TODO: The passed environment variables came from copying other tox.ini files
# These should probably be individually annotated to explain what needs them.