Commit 278e0273 authored by Jason R. Coombs's avatar Jason R. Coombs

Merge branch 'master' into patch-1

parents c5958f26 25edd195
[bumpversion] [bumpversion]
current_version = 41.4.0 current_version = 45.3.0
commit = True commit = True
tag = True tag = True
......
[flake8]
exclude=
.tox
setuptools/_vendor,
pkg_resources/_vendor
ignore =
# W503 violates spec https://github.com/PyCQA/pycodestyle/issues/513
W503
# W504 has issues https://github.com/OCA/maintainer-quality-tools/issues/545
W504
setuptools/site-patch.py F821
setuptools/py*compat.py F811
name: >-
👷
Test suite
on:
push:
pull_request:
schedule:
- cron: 1 0 * * * # Run daily at 0:01 UTC
jobs:
tests:
name: >-
${{ matrix.python-version }}
/
${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
# max-parallel: 5
matrix:
python-version:
- 3.8
- pypy3
- 3.7
- 3.6
- 3.5
os:
- ubuntu-latest
- ubuntu-16.04
- macOS-latest
# - windows-2019
# - windows-2016
env:
NETWORK_REQUIRED: 1
TOX_PARALLEL_NO_SPINNER: 1
TOXENV: python
steps:
- uses: actions/checkout@master
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v1.1.1
with:
python-version: ${{ matrix.python-version }}
- name: Log Python version
run: >-
python --version
- name: Log Python location
run: >-
which python
- name: Log Python env
run: >-
python -m sysconfig
- name: Pip cache
uses: actions/cache@v1
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('setup.cfg') }}
restore-keys: |
${{ runner.os }}-pip-
${{ runner.os }}-
- name: Upgrade pip/setuptools/wheel
run: >-
python
-m pip install
--disable-pip-version-check
--upgrade
pip setuptools wheel
- name: Install tox
run: >-
python -m pip install --upgrade tox tox-venv
- name: Log installed dists
run: >-
python -m pip freeze --all
- name: Adjust TOXENV for PyPy
if: startsWith(matrix.python-version, 'pypy')
run: >-
echo "::set-env name=TOXENV::${{ matrix.python-version }}"
- name: Log env vars
run: >-
env
- name: Verify that there's no cached Python modules in sources
if: >-
! startsWith(matrix.os, 'windows-')
run: >-
! grep pyc setuptools.egg-info/SOURCES.txt
- name: 'Initialize tox envs: ${{ matrix.env.TOXENV }}'
run: >-
python -m
tox
--parallel auto
--notest
--skip-missing-interpreters false
- name: Test with tox
run: >-
python -m
tox
--parallel auto
--
--cov
python: python:
version: 3 version: 3
requirements_file: docs/requirements.txt extra_requirements:
- docs
pip_install: false pip_install: false
requirements: docs/requirements.txt
...@@ -6,43 +6,29 @@ jobs: ...@@ -6,43 +6,29 @@ jobs:
include: include:
- &latest_py2 - &latest_py2
python: 2.7 python: 2.7
env: TOXENV=py27
- <<: *latest_py2 - <<: *latest_py2
env: LANG=C env: LANG=C TOXENV=py27
- python: pypy
env: DISABLE_COVERAGE=1 # Don't run coverage on pypy (too slow).
- python: pypy3 - python: pypy3
env: DISABLE_COVERAGE=1 env: DISABLE_COVERAGE=1 # Don't run coverage on pypy (too slow).
- python: 3.4
- python: 3.5 - python: 3.5
- &default_py - python: 3.6
python: 3.6 - python: 3.7
- &latest_py3 - &latest_py3
python: 3.7 python: 3.8
- <<: *latest_py3 - <<: *latest_py3
env: LANG=C env: LANG=C
- python: 3.8-dev - python: 3.8-dev
- <<: *latest_py3 - <<: *latest_py3
env: TOXENV=docs DISABLE_COVERAGE=1 env: TOXENV=docs DISABLE_COVERAGE=1
- <<: *default_py - <<: *latest_py3
stage: deploy (to PyPI for tagged commits) stage: deploy
if: tag IS present if: tag IS present
install: skip script: tox -e release
script: skip after_success: skip
after_success: true allow_failures:
before_deploy: # suppress failures due to pypa/setuptools#2000
- python bootstrap.py - python: pypy3
- "! grep pyc setuptools.egg-info/SOURCES.txt"
deploy:
provider: pypi
on:
tags: true
all_branches: true
user: __token__
password:
secure: FSp9KU+pdvWPxBOaxe6BNmcJ9y8259G3/NdTJ00r0qx/xMLpSneGjpuLqoD6BL2JoM6gRwurwakWoH/9Ah+Di7afETjMnL6WJKtDZ+Uu3YLx3ss7/FlhVz6zmVTaDJUzuo9dGr//qLBQTIxVjGYfQelRJyfMAXtrYWdeT/4489E45lMw+86Z/vnSBOxs4lWekeQW5Gem0cDViWu67RRiGkAEvrYVwuImMr2Dyhpv+l/mQGQIS/ezXuAEFToE6+q8VUVe/aK498Qovdc+O4M7OYk1JouFpffZ3tVZ6iWHQFcR11480UdI6VCIcFpPvGC/J8MWUWLjq7YOm0X9jPXgdYMUQLAP4clFgUr2qNoRSKWfuQlNdVVuS2htYcjJ3eEl90FhcIZKp+WVMrypRPOQJ8CBielZEs0dhytRrZSaJC1BNq25O/BPzws8dL8hYtoXsM6I3Zv5cZgdyqyq/eOEMCX7Cetv6do0U41VGEV5UohvyyuwH5l9GCuPREpY3sXayPg8fw7XcPjvvzSVyjcUT/ePW8sfnAyWZnngjweAn6dK8IFGPuSPQdlos78uxeUOvCVUW0xv/0m4lX73yoHdVVdLbu1MJTyibFGec86Bew9JqIcDlhHaIJ9ihZ9Z9tOtvp1cuNyKYE4kvmOtumDDicEw4DseYn2z5sZDTYTBsKY=
distributions: release
skip_cleanup: true
skip_upload_docs: true
cache: pip cache: pip
...@@ -57,8 +43,6 @@ install: ...@@ -57,8 +43,6 @@ install:
- pip freeze --all - pip freeze --all
- env - env
# update egg_info based on setup.py in checkout
- python bootstrap.py
- "! grep pyc setuptools.egg-info/SOURCES.txt" - "! grep pyc setuptools.egg-info/SOURCES.txt"
script: script:
......
v45.3.0
-------
* #1557: Deprecated eggsecutable scripts and updated docs.
* #1904: Update msvc.py to use CPython 3.8.0 mechanism to find msvc 14+
v45.2.0
-------
* #1905: Fixed defect in _imp, introduced in 41.6.0 when the 'tests' directory is not present.
* #1941: Improve editable installs with PEP 518 build isolation:
* The ``--user`` option is now always available. A warning is issued if the user site directory is not available.
* The error shown when the install directory is not in ``PYTHONPATH`` has been turned into a warning.
* #1981: Setuptools now declares its ``tests`` and ``docs`` dependencies in metadata (extras).
* #1985: Add support for installing scripts in environments where bdist_wininst is missing (i.e. Python 3.9).
* #1968: Add flake8-2020 to check for misuse of sys.version or sys.version_info.
v45.1.0
-------
* #1458: Add minimum sunset date and preamble to Python 2 warning.
* #1704: Set sys.argv[0] in setup script run by build_meta.__legacy__
* #1974: Add Python 3 Only Trove Classifier and remove universal wheel declaration for more complete transition from Python 2.
v45.0.0
-------
* #1458: Drop support for Python 2. Setuptools now requires Python 3.5 or later. Install setuptools using pip >=9 or pin to Setuptools <45 to maintain 2.7 support.
* #1959: Fix for Python 4: replace unsafe six.PY3 with six.PY2
v44.0.0
-------
* #1908: Drop support for Python 3.4.
v43.0.0
-------
* #1634: Include ``pyproject.toml`` in source distribution by default. Projects relying on the previous behavior where ``pyproject.toml`` was excluded by default should stop relying on that behavior or add ``exclude pyproject.toml`` to their MANIFEST.in file.
* #1927: Setuptools once again declares 'setuptools' in the ``build-system.requires`` and adds PEP 517 build support by declaring itself as the ``build-backend``. It additionally specifies ``build-system.backend-path`` to rely on itself for those builders that support it.
v42.0.2
-------
* #1921: Fix support for easy_install's ``find-links`` option in ``setup.cfg``.
* #1922: Build dependencies (setup_requires and tests_require) now install transitive dependencies indicated by extras.
v42.0.1
-------
* #1918: Fix regression in handling wheels compatibility tags.
v42.0.0
-------
* #1830, #1909: Mark the easy_install script and setuptools command as deprecated, and use `pip <https://pip.pypa.io/en/stable/>`_ when available to fetch/build wheels for missing ``setup_requires``/``tests_require`` requirements, with the following differences in behavior:
* support for ``python_requires``
* better support for wheels (proper handling of priority with respect to PEP 425 tags)
* PEP 517/518 support
* eggs are not supported
* no support for the ``allow_hosts`` easy_install option (``index_url``/``find_links`` are still honored)
* pip environment variables are honored (and take precedence over easy_install options)
* #1898: Removed the "upload" and "register" commands in favor of `twine <https://pypi.org/p/twine>`_.
* #1767: Add support for the ``license_files`` option in ``setup.cfg`` to automatically
include multiple license files in a source distribution.
* #1829: Update handling of wheels compatibility tags:
* add support for manylinux2010
* fix use of removed 'm' ABI flag in Python 3.8 on Windows
* #1861: Fix empty namespace package installation from wheel.
* #1877: Setuptools now exposes a new entry point hook "setuptools.finalize_distribution_options", enabling plugins like `setuptools_scm <https://pypi.org/project/setuptools_scm>`_ to configure options on the distribution at finalization time.
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 v41.4.0
------- -------
......
...@@ -4,6 +4,7 @@ recursive-include setuptools/tests *.html ...@@ -4,6 +4,7 @@ recursive-include setuptools/tests *.html
recursive-include docs *.py *.txt *.conf *.css *.css_t Makefile indexsidebar.html recursive-include docs *.py *.txt *.conf *.css *.css_t Makefile indexsidebar.html
recursive-include setuptools/_vendor *.py *.txt recursive-include setuptools/_vendor *.py *.txt
recursive-include pkg_resources *.py *.txt recursive-include pkg_resources *.py *.txt
recursive-include pkg_resources/tests/data *
include *.py include *.py
include *.rst include *.rst
include MANIFEST.in include MANIFEST.in
......
...@@ -34,8 +34,17 @@ To report a security vulnerability, please use the ...@@ -34,8 +34,17 @@ To report a security vulnerability, please use the
Tidelift will coordinate the fix and disclosure. Tidelift will coordinate the fix and disclosure.
For Enterprise
==============
Available as part of the Tidelift Subscription.
Setuptools and the maintainers of thousands of other packages are working with Tidelift to deliver one enterprise subscription that covers all of the open source you use.
`Learn more <https://tidelift.com/subscription/pkg/pypi-setuptools?utm_source=pypi-setuptools&utm_medium=referral&utm_campaign=github>`_.
Code of Conduct Code of Conduct
--------------- ===============
Everyone interacting in the setuptools project's codebases, issue trackers, Everyone interacting in the setuptools project's codebases, issue trackers,
chat rooms, and mailing lists is expected to follow the chat rooms, and mailing lists is expected to follow the
......
...@@ -7,10 +7,19 @@ environment: ...@@ -7,10 +7,19 @@ environment:
CODECOV_ENV: APPVEYOR_JOB_NAME CODECOV_ENV: APPVEYOR_JOB_NAME
matrix: matrix:
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
APPVEYOR_JOB_NAME: "python35-x64-vs2015"
PYTHON: "C:\\Python35-x64"
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
APPVEYOR_JOB_NAME: "python35-x64-vs2017"
PYTHON: "C:\\Python35-x64"
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
APPVEYOR_JOB_NAME: "python35-x64-vs2019"
PYTHON: "C:\\Python35-x64"
- APPVEYOR_JOB_NAME: "python36-x64" - APPVEYOR_JOB_NAME: "python36-x64"
PYTHON: "C:\\Python36-x64" PYTHON: "C:\\Python36-x64"
- APPVEYOR_JOB_NAME: "python27-x64" - APPVEYOR_JOB_NAME: "python37-x64"
PYTHON: "C:\\Python27-x64" PYTHON: "C:\\Python37-x64"
install: install:
# symlink python from a directory with a space # symlink python from a directory with a space
...@@ -28,7 +37,6 @@ test_script: ...@@ -28,7 +37,6 @@ test_script:
- python -m pip install --disable-pip-version-check --upgrade pip setuptools wheel - python -m pip install --disable-pip-version-check --upgrade pip setuptools wheel
- pip install --upgrade tox tox-venv virtualenv - pip install --upgrade tox tox-venv virtualenv
- pip freeze --all - pip freeze --all
- python bootstrap.py
- tox -- --cov - tox -- --cov
after_test: after_test:
......
# Create the project in Azure with:
# az devops project create --name $name --organization https://dev.azure.com/$org/ --visibility public
# then configure the pipelines (through web UI)
trigger:
branches:
include:
- '*'
tags:
include:
- '*'
pool:
vmimage: 'Ubuntu-18.04'
variables:
- group: Azure secrets
stages:
- stage: Test
jobs:
- job: 'Test'
strategy:
matrix:
Python36:
python.version: '3.6'
Python38:
python.version: '3.8'
maxParallel: 4
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: '$(python.version)'
architecture: 'x64'
- script: python -m pip install tox
displayName: 'Install tox'
- script: |
tox -- --junit-xml=test-results.xml
displayName: 'run tests'
- task: PublishTestResults@2
inputs:
testResultsFiles: '**/test-results.xml'
testRunTitle: 'Python $(python.version)'
condition: succeededOrFailed()
- stage: Publish
dependsOn: Test
jobs:
- job: 'Publish'
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: '3.8'
architecture: 'x64'
- script: python -m pip install tox
displayName: 'Install tox'
- script: |
tox -e release
env:
TWINE_PASSWORD: $(PyPI-token)
displayName: 'publish to PyPI'
condition: contains(variables['Build.SourceBranch'], 'tags')
...@@ -25,6 +25,7 @@ minimal_egg_info = textwrap.dedent(""" ...@@ -25,6 +25,7 @@ minimal_egg_info = textwrap.dedent("""
entry_points = setuptools.dist:check_entry_points entry_points = setuptools.dist:check_entry_points
[egg_info.writers] [egg_info.writers]
PKG-INFO = setuptools.command.egg_info:write_pkg_info
dependency_links.txt = setuptools.command.egg_info:overwrite_arg dependency_links.txt = setuptools.command.egg_info:overwrite_arg
entry_points.txt = setuptools.command.egg_info:write_entries entry_points.txt = setuptools.command.egg_info:write_entries
requires.txt = setuptools.command.egg_info:write_requirements requires.txt = setuptools.command.egg_info:write_requirements
...@@ -35,10 +36,11 @@ def ensure_egg_info(): ...@@ -35,10 +36,11 @@ def ensure_egg_info():
if os.path.exists('setuptools.egg-info'): if os.path.exists('setuptools.egg-info'):
return return
print("adding minimal entry_points") print("adding minimal entry_points")
build_egg_info() add_minimal_info()
run_egg_info()
def build_egg_info(): def add_minimal_info():
""" """
Build a minimal egg-info, enough to invoke egg_info Build a minimal egg-info, enough to invoke egg_info
""" """
...@@ -52,13 +54,6 @@ def run_egg_info(): ...@@ -52,13 +54,6 @@ def run_egg_info():
cmd = [sys.executable, 'setup.py', 'egg_info'] cmd = [sys.executable, 'setup.py', 'egg_info']
print("Regenerating egg_info") print("Regenerating egg_info")
subprocess.check_call(cmd) subprocess.check_call(cmd)
print("...and again.")
subprocess.check_call(cmd)
def main():
ensure_egg_info()
run_egg_info()
__name__ == '__main__' and main() __name__ == '__main__' and ensure_egg_info()
Update documentation to mention the egg format is not supported by pip and dependency links support was dropped starting with pip 19.0.
Formally deprecated the ``test`` command, with the recommendation that users migrate to ``tox``.
Added a trove classifier to document support for Python 3.8.
Include pkg_resources test data in sdist, so tests can be executed from it.
Fix broken link to distutils docs on package_data
...@@ -19,6 +19,7 @@ collect_ignore = [ ...@@ -19,6 +19,7 @@ collect_ignore = [
if sys.version_info < (3,): if sys.version_info < (3,):
collect_ignore.append('setuptools/lib2to3_ex.py') collect_ignore.append('setuptools/lib2to3_ex.py')
collect_ignore.append('setuptools/_imp.py')
if sys.version_info < (3, 6): if sys.version_info < (3, 6):
......
<h3 class="donation">For Enterprise</h3>
<p>
Professionally-supported {{ project }} is available with the
<a href="https://tidelift.com/subscription/pkg/pypi-{{ project }}?utm_source=pypi-{{ project }}&utm_medium=referral">Tidelift Subscription</a>.
</p>
<h3>Download</h3> <h3>Download</h3>
<p>Current version: <b>{{ version }}</b></p> <p>Current version: <b>{{ version }}</b></p>
...@@ -6,10 +13,3 @@ ...@@ -6,10 +13,3 @@
<h3>Questions? Suggestions? Contributions?</h3> <h3>Questions? Suggestions? Contributions?</h3>
<p>Visit the <a href="{{ package_url }}">Project page</a> </p> <p>Visit the <a href="{{ package_url }}">Project page</a> </p>
<h3 class="donation">Professional support</h3>
<p>
Professionally-supported {{ project }} is available with the
<a href="https://tidelift.com/subscription/pkg/pypi-{{ project }}?utm_source=pypi-{{ project }}&utm_medium=readme">Tidelift Subscription</a>.
</p>
# -*- coding: utf-8 -*-
#
# Setuptools documentation build configuration file, created by
# sphinx-quickstart on Fri Jul 17 14:22:37 2009.
#
# This file is execfile()d with the current directory set to its containing dir.
#
# The contents of this file are pickled, so don't put values in the namespace
# that aren't pickleable (module imports are okay, they're removed automatically).
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
import subprocess import subprocess
import sys import sys
import os import os
...@@ -26,14 +6,12 @@ import os ...@@ -26,14 +6,12 @@ import os
# hack to run the bootstrap script so that jaraco.packaging.sphinx # hack to run the bootstrap script so that jaraco.packaging.sphinx
# can invoke setup.py # can invoke setup.py
'READTHEDOCS' in os.environ and subprocess.check_call( 'READTHEDOCS' in os.environ and subprocess.check_call(
[sys.executable, 'bootstrap.py'], [sys.executable, '-m', 'bootstrap'],
cwd=os.path.join(os.path.dirname(__file__), os.path.pardir), cwd=os.path.join(os.path.dirname(__file__), os.path.pardir),
) )
# -- General configuration ----------------------------------------------------- # -- General configuration --
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['jaraco.packaging.sphinx', 'rst.linker'] extensions = ['jaraco.packaging.sphinx', 'rst.linker']
# Add any paths that contain templates here, relative to this directory. # Add any paths that contain templates here, relative to this directory.
...@@ -45,7 +23,8 @@ source_suffix = '.txt' ...@@ -45,7 +23,8 @@ source_suffix = '.txt'
# The master toctree document. # The master toctree document.
master_doc = 'index' master_doc = 'index'
# A list of glob-style patterns that should be excluded when looking for source files. # A list of glob-style patterns that should be excluded
# when looking for source files.
exclude_patterns = ['requirements.txt'] exclude_patterns = ['requirements.txt']
# List of directories, relative to source directory, that shouldn't be searched # List of directories, relative to source directory, that shouldn't be searched
...@@ -55,7 +34,7 @@ exclude_trees = [] ...@@ -55,7 +34,7 @@ exclude_trees = []
# The name of the Pygments (syntax highlighting) style to use. # The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx' pygments_style = 'sphinx'
# -- Options for HTML output --------------------------------------------------- # -- Options for HTML output --
# The theme to use for HTML and HTML Help pages. Major themes that come with # The theme to use for HTML and HTML Help pages. Major themes that come with
# Sphinx are currently 'default' and 'sphinxdoc'. # Sphinx are currently 'default' and 'sphinxdoc'.
...@@ -69,7 +48,10 @@ html_theme_path = ['_theme'] ...@@ -69,7 +48,10 @@ html_theme_path = ['_theme']
html_use_smartypants = True html_use_smartypants = True
# Custom sidebar templates, maps document names to template names. # Custom sidebar templates, maps document names to template names.
html_sidebars = {'index': ['relations.html', 'sourcelink.html', 'indexsidebar.html', 'searchbox.html']} html_sidebars = {
'index': [
'relations.html', 'sourcelink.html', 'indexsidebar.html',
'searchbox.html']}
# If false, no module index is generated. # If false, no module index is generated.
html_use_modindex = False html_use_modindex = False
...@@ -77,14 +59,15 @@ html_use_modindex = False ...@@ -77,14 +59,15 @@ html_use_modindex = False
# If false, no index is generated. # If false, no index is generated.
html_use_index = False html_use_index = False
# -- Options for LaTeX output -------------------------------------------------- # -- Options for LaTeX output --
# Grouping the document tree into LaTeX files. List of tuples # Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]). # (source start file, target name, title, author,
latex_documents = [ # documentclass [howto/manual]).
('index', 'Setuptools.tex', 'Setuptools Documentation', latex_documents = [(
'The fellowship of the packaging', 'manual'), 'index', 'Setuptools.tex', 'Setuptools Documentation',
] 'The fellowship of the packaging', 'manual',
)]
link_files = { link_files = {
'../CHANGES.rst': dict( '../CHANGES.rst': dict(
......
...@@ -104,12 +104,8 @@ from the command line after pushing a new branch. ...@@ -104,12 +104,8 @@ from the command line after pushing a new branch.
Testing Testing
------- -------
The primary tests are run using tox. To run the tests, first create the metadata The primary tests are run using tox. Make sure you have tox installed,
needed to run the tests:: and invoke it::
$ python bootstrap.py
Then make sure you have tox installed, and invoke it::
$ tox $ tox
......
...@@ -7,7 +7,7 @@ Authority (PyPA) and led by Jason R. Coombs. ...@@ -7,7 +7,7 @@ Authority (PyPA) and led by Jason R. Coombs.
This document describes the process by which Setuptools is developed. This document describes the process by which Setuptools is developed.
This document assumes the reader has some passing familiarity with 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 does not attempt to explain basic concepts like inter-project
dependencies, nor does it contain detailed lexical syntax for most dependencies, nor does it contain detailed lexical syntax for most
file formats. Neither does it explain concepts like "namespace file formats. Neither does it explain concepts like "namespace
......
...@@ -41,7 +41,7 @@ Please see the `setuptools PyPI page <https://pypi.org/project/setuptools/>`_ ...@@ -41,7 +41,7 @@ Please see the `setuptools PyPI page <https://pypi.org/project/setuptools/>`_
for download links and basic installation instructions for each of the for download links and basic installation instructions for each of the
supported platforms. supported platforms.
You will need at least Python 3.4 or 2.7. An ``easy_install`` script will be You will need at least Python 3.5 or 2.7. An ``easy_install`` script will be
installed in the normal location for Python scripts on your platform. installed in the normal location for Python scripts on your platform.
Note that the instructions on the setuptools PyPI page assume that you are Note that the instructions on the setuptools PyPI page assume that you are
...@@ -317,8 +317,8 @@ Note that instead of changing your ``PATH`` to include the Python scripts ...@@ -317,8 +317,8 @@ Note that instead of changing your ``PATH`` to include the Python scripts
directory, you can also retarget the installation location for scripts so they 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 go on a directory that's already on the ``PATH``. For more information see
`Command-Line Options`_ and `Configuration Files`_. During installation, `Command-Line Options`_ and `Configuration Files`_. During installation,
pass command line options (such as ``--script-dir``) to pass command line options (such as ``--script-dir``) to control where
``ez_setup.py`` to control where ``easy_install.exe`` will be installed. scripts will be installed.
Windows Executable Launcher Windows Executable Launcher
......
: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.
...@@ -299,11 +299,8 @@ specified by the ``setup_requires`` parameter to the Distribution. ...@@ -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 A list of dependency URLs, one per line, as specified using the
``dependency_links`` keyword to ``setup()``. These may be direct ``dependency_links`` keyword to ``setup()``. These may be direct
download URLs, or the URLs of web pages containing direct download download URLs, or the URLs of web pages containing direct download
links, and will be used by EasyInstall to find dependencies, as though links. Please see the setuptools manual for more information on
the user had manually provided them via the ``--find-links`` command specifying this option.
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.
``depends.txt`` -- Obsolete, do not create! ``depends.txt`` -- Obsolete, do not create!
......
...@@ -245,8 +245,8 @@ abbreviation for ``pkg_resources.working_set.require()``: ...@@ -245,8 +245,8 @@ abbreviation for ``pkg_resources.working_set.require()``:
interactive interpreter hacking than for production use. If you're creating interactive interpreter hacking than for production use. If you're creating
an actual library or application, it's strongly recommended that you create an actual library or application, it's strongly recommended that you create
a "setup.py" script using ``setuptools``, and declare all your requirements a "setup.py" script using ``setuptools``, and declare all your requirements
there. That way, tools like EasyInstall can automatically detect what there. That way, tools like pip can automatically detect what requirements
requirements your package has, and deal with them accordingly. your package has, and deal with them accordingly.
Note that calling ``require('SomePackage')`` will not install Note that calling ``require('SomePackage')`` will not install
``SomePackage`` if it isn't already present. If you need to do this, you ``SomePackage`` if it isn't already present. If you need to do this, you
...@@ -611,9 +611,9 @@ Requirements Parsing ...@@ -611,9 +611,9 @@ Requirements Parsing
or activation of both Report-O-Rama and any libraries it needs in order to or activation of both Report-O-Rama and any libraries it needs in order to
provide PDF support. For example, you could use:: 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 ``pkg_resources.require('Report-O-Rama[PDF]')`` to add the necessary
distributions to sys.path at runtime. distributions to sys.path at runtime.
...@@ -1843,9 +1843,9 @@ History ...@@ -1843,9 +1843,9 @@ History
because it isn't necessarily a filesystem path (and hasn't been for some because it isn't necessarily a filesystem path (and hasn't been for some
time now). The ``location`` of ``Distribution`` objects in the filesystem time now). The ``location`` of ``Distribution`` objects in the filesystem
should always be normalized using ``pkg_resources.normalize_path()``; all should always be normalized using ``pkg_resources.normalize_path()``; all
of the setuptools and EasyInstall code that generates distributions from of the setuptools' code that generates distributions from the filesystem
the filesystem (including ``Distribution.from_filename()``) ensure this (including ``Distribution.from_filename()``) ensure this invariant, but if
invariant, but if you use a more generic API like ``Distribution()`` or you use a more generic API like ``Distribution()`` or
``Distribution.from_location()`` you should take care that you don't ``Distribution.from_location()`` you should take care that you don't
create a distribution with an un-normalized filesystem path. create a distribution with an un-normalized filesystem path.
......
sphinx!=1.8.0 # keep these in sync with setup.cfg
rst.linker>=1.9 sphinx
jaraco.packaging>=6.1 jaraco.packaging>=6.1
rst.linker>=1.9
setuptools>=34
This diff is collapsed.
...@@ -83,13 +83,14 @@ __import__('pkg_resources.extern.packaging.version') ...@@ -83,13 +83,14 @@ __import__('pkg_resources.extern.packaging.version')
__import__('pkg_resources.extern.packaging.specifiers') __import__('pkg_resources.extern.packaging.specifiers')
__import__('pkg_resources.extern.packaging.requirements') __import__('pkg_resources.extern.packaging.requirements')
__import__('pkg_resources.extern.packaging.markers') __import__('pkg_resources.extern.packaging.markers')
__import__('pkg_resources.py2_warn')
__metaclass__ = type __metaclass__ = type
if (3, 0) < sys.version_info < (3, 4): if (3, 0) < sys.version_info < (3, 5):
raise RuntimeError("Python 3.4 or later is required") raise RuntimeError("Python 3.5 or later is required")
if six.PY2: if six.PY2:
# Those builtin exceptions are only defined in Python 3 # Those builtin exceptions are only defined in Python 3
...@@ -333,7 +334,7 @@ class UnknownExtra(ResolutionError): ...@@ -333,7 +334,7 @@ class UnknownExtra(ResolutionError):
_provider_factories = {} _provider_factories = {}
PY_MAJOR = sys.version[:3] PY_MAJOR = '{}.{}'.format(*sys.version_info)
EGG_DIST = 3 EGG_DIST = 3
BINARY_DIST = 2 BINARY_DIST = 2
SOURCE_DIST = 1 SOURCE_DIST = 1
...@@ -2328,7 +2329,8 @@ register_namespace_handler(object, null_ns_handler) ...@@ -2328,7 +2329,8 @@ register_namespace_handler(object, null_ns_handler)
def normalize_path(filename): def normalize_path(filename):
"""Normalize a file/dir name for comparison purposes""" """Normalize a file/dir name for comparison purposes"""
return os.path.normcase(os.path.realpath(os.path.normpath(_cygwin_patch(filename)))) return os.path.normcase(os.path.realpath(os.path.normpath(
_cygwin_patch(filename))))
def _cygwin_patch(filename): # pragma: nocover def _cygwin_patch(filename): # pragma: nocover
...@@ -3109,6 +3111,7 @@ class Requirement(packaging.requirements.Requirement): ...@@ -3109,6 +3111,7 @@ class Requirement(packaging.requirements.Requirement):
self.extras = tuple(map(safe_extra, self.extras)) self.extras = tuple(map(safe_extra, self.extras))
self.hashCmp = ( self.hashCmp = (
self.key, self.key,
self.url,
self.specifier, self.specifier,
frozenset(self.extras), frozenset(self.extras),
str(self.marker) if self.marker else None, str(self.marker) if self.marker else None,
...@@ -3286,6 +3289,7 @@ def _initialize_master_working_set(): ...@@ -3286,6 +3289,7 @@ def _initialize_master_working_set():
list(map(working_set.add_entry, sys.path)) list(map(working_set.add_entry, sys.path))
globals().update(locals()) globals().update(locals())
class PkgResourcesDeprecationWarning(Warning): class PkgResourcesDeprecationWarning(Warning):
""" """
Base class for warning about deprecations in ``pkg_resources`` Base class for warning about deprecations in ``pkg_resources``
......
...@@ -36,7 +36,7 @@ Distributions have various introspectable attributes:: ...@@ -36,7 +36,7 @@ Distributions have various introspectable attributes::
>>> dist.version >>> dist.version
'0.9' '0.9'
>>> dist.py_version == sys.version[:3] >>> dist.py_version == '{}.{}'.format(*sys.version_info)
True True
>>> print(dist.platform) >>> print(dist.platform)
......
import sys
import warnings
import textwrap
msg = textwrap.dedent("""
You are running Setuptools on Python 2, which is no longer
supported and
>>> SETUPTOOLS WILL STOP WORKING <<<
in a subsequent release (no sooner than 2020-04-20).
Please ensure you are installing
Setuptools using pip 9.x or later or pin to `setuptools<45`
in your environment.
If you have done those things and are still encountering
this message, please comment in
https://github.com/pypa/setuptools/issues/1458
about the steps that led to this unsupported combination.
""")
pre = "Setuptools will stop working on Python 2\n"
sys.version_info < (3,) and warnings.warn(pre + "*" * 60 + msg + "*" * 60)
import setuptools
setuptools.setup(
name="my-test-package",
version="1.0",
zip_safe=True,
)
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
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
import subprocess import py
import sys
import pytest import pytest
import pkg_resources import pkg_resources
SETUP_TEMPLATE = """
import setuptools TESTS_DATA_DIR = py.path.local(__file__).dirpath('data')
setuptools.setup(
name="my-test-package",
version="1.0",
zip_safe=True,
)
""".lstrip()
class TestFindDistributions: class TestFindDistributions:
...@@ -21,46 +13,22 @@ class TestFindDistributions: ...@@ -21,46 +13,22 @@ class TestFindDistributions:
target_dir = tmpdir.mkdir('target') target_dir = tmpdir.mkdir('target')
# place a .egg named directory in the target that is not an egg: # place a .egg named directory in the target that is not an egg:
target_dir.mkdir('not.an.egg') target_dir.mkdir('not.an.egg')
return str(target_dir) return 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)
def test_non_egg_dir_named_egg(self, 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) assert not list(dists)
def test_standalone_egg_directory(self, project_dir, target_dir): def test_standalone_egg_directory(self, target_dir):
# install this distro as an unpacked egg: (TESTS_DATA_DIR / 'my-test-package_unpacked-egg').copy(target_dir)
args = [ dists = pkg_resources.find_distributions(str(target_dir))
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)
assert [dist.project_name for dist in dists] == ['my-test-package'] 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) assert not list(dists)
def test_zipped_egg(self, project_dir, target_dir): def test_zipped_egg(self, target_dir):
# install this distro as an unpacked egg: (TESTS_DATA_DIR / 'my-test-package_zipped-egg').copy(target_dir)
args = [ dists = pkg_resources.find_distributions(str(target_dir))
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)
assert [dist.project_name for dist in dists] == ['my-test-package'] 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) assert not list(dists)
...@@ -17,7 +17,9 @@ try: ...@@ -17,7 +17,9 @@ try:
except ImportError: except ImportError:
import mock import mock
from pkg_resources import DistInfoDistribution, Distribution, EggInfoDistribution from pkg_resources import (
DistInfoDistribution, Distribution, EggInfoDistribution,
)
from setuptools.extern import six from setuptools.extern import six
from pkg_resources.extern.six.moves import map from pkg_resources.extern.six.moves import map
from pkg_resources.extern.six import text_type, string_types from pkg_resources.extern.six import text_type, string_types
...@@ -279,8 +281,8 @@ def make_distribution_no_version(tmpdir, basename): ...@@ -279,8 +281,8 @@ def make_distribution_no_version(tmpdir, basename):
('dist-info', 'METADATA', DistInfoDistribution), ('dist-info', 'METADATA', DistInfoDistribution),
], ],
) )
def test_distribution_version_missing(tmpdir, suffix, expected_filename, def test_distribution_version_missing(
expected_dist_type): tmpdir, suffix, expected_filename, expected_dist_type):
""" """
Test Distribution.version when the "Version" header is missing. Test Distribution.version when the "Version" header is missing.
""" """
......
...@@ -15,7 +15,7 @@ import pkg_resources ...@@ -15,7 +15,7 @@ import pkg_resources
from pkg_resources import ( from pkg_resources import (
parse_requirements, VersionConflict, parse_version, parse_requirements, VersionConflict, parse_version,
Distribution, EntryPoint, Requirement, safe_version, safe_name, Distribution, EntryPoint, Requirement, safe_version, safe_name,
WorkingSet, PkgResourcesDeprecationWarning) WorkingSet)
# from Python 3.6 docs. # from Python 3.6 docs.
...@@ -116,7 +116,7 @@ class TestDistro: ...@@ -116,7 +116,7 @@ class TestDistro:
self.checkFooPkg(d) self.checkFooPkg(d)
d = Distribution("/some/path") d = Distribution("/some/path")
assert d.py_version == sys.version[:3] assert d.py_version == '{}.{}'.format(*sys.version_info)
assert d.platform is None assert d.platform is None
def testDistroParse(self): def testDistroParse(self):
...@@ -501,7 +501,6 @@ class TestEntryPoints: ...@@ -501,7 +501,6 @@ class TestEntryPoints:
ep.load(require=False) ep.load(require=False)
class TestRequirements: class TestRequirements:
def testBasics(self): def testBasics(self):
r = Requirement.parse("Twisted>=1.2") r = Requirement.parse("Twisted>=1.2")
...@@ -520,6 +519,11 @@ class TestRequirements: ...@@ -520,6 +519,11 @@ class TestRequirements:
assert r1 == r2 assert r1 == r2
assert str(r1) == str(r2) assert str(r1) == str(r2)
assert str(r2) == "Twisted==1.2c1,>=1.2" assert str(r2) == "Twisted==1.2c1,>=1.2"
assert (
Requirement("Twisted")
!=
Requirement("Twisted @ https://localhost/twisted.zip")
)
def testBasicContains(self): def testBasicContains(self):
r = Requirement("Twisted>=1.2") r = Requirement("Twisted>=1.2")
...@@ -546,11 +550,23 @@ class TestRequirements: ...@@ -546,11 +550,23 @@ class TestRequirements:
== ==
hash(( hash((
"twisted", "twisted",
None,
packaging.specifiers.SpecifierSet(">=1.2"), packaging.specifiers.SpecifierSet(">=1.2"),
frozenset(["foo", "bar"]), frozenset(["foo", "bar"]),
None None
)) ))
) )
assert (
hash(Requirement.parse("Twisted @ https://localhost/twisted.zip"))
==
hash((
"twisted",
"https://localhost/twisted.zip",
packaging.specifiers.SpecifierSet(),
frozenset(),
None
))
)
def testVersionEquality(self): def testVersionEquality(self):
r1 = Requirement.parse("foo==0.3a2") r1 = Requirement.parse("foo==0.3a2")
......
[build-system] [build-system]
requires = ["wheel"] requires = [
# avoid self install on Python 2; ref #1996
"setuptools >= 40.8; python_version > '3'",
"wheel",
]
build-backend = "setuptools.build_meta"
backend-path = ["."]
[tool.towncrier] [tool.towncrier]
package = "setuptools" package = "setuptools"
......
[pytest] [pytest]
addopts=--doctest-modules --doctest-glob=pkg_resources/api_tests.txt -r sxX addopts=--doctest-modules --flake8 --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 .* setuptools/_vendor pkg_resources/_vendor
flake8-ignore =
setuptools/site-patch.py F821
setuptools/py*compat.py F811
doctest_optionflags=ELLIPSIS ALLOW_UNICODE doctest_optionflags=ELLIPSIS ALLOW_UNICODE
filterwarnings =
# https://github.com/pypa/setuptools/issues/1823
ignore:bdist_wininst command is deprecated
...@@ -14,12 +14,9 @@ repository = https://upload.pypi.org/legacy/ ...@@ -14,12 +14,9 @@ repository = https://upload.pypi.org/legacy/
[sdist] [sdist]
formats = zip formats = zip
[bdist_wheel]
universal = 1
[metadata] [metadata]
name = setuptools name = setuptools
version = 41.4.0 version = 45.3.0
description = Easily download, build, install, upgrade, and uninstall Python packages description = Easily download, build, install, upgrade, and uninstall Python packages
author = Python Packaging Authority author = Python Packaging Authority
author_email = distutils-sig@python.org author_email = distutils-sig@python.org
...@@ -35,10 +32,8 @@ classifiers = ...@@ -35,10 +32,8 @@ classifiers =
Intended Audience :: Developers Intended Audience :: Developers
License :: OSI Approved :: MIT License License :: OSI Approved :: MIT License
Operating System :: OS Independent Operating System :: OS Independent
Programming Language :: Python :: 2
Programming Language :: Python :: 2.7
Programming Language :: Python :: 3 Programming Language :: Python :: 3
Programming Language :: Python :: 3.4 Programming Language :: Python :: 3 :: Only
Programming Language :: Python :: 3.5 Programming Language :: Python :: 3.5
Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.6
Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.7
...@@ -50,7 +45,7 @@ classifiers = ...@@ -50,7 +45,7 @@ classifiers =
[options] [options]
zip_safe = True zip_safe = True
python_requires = >=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.* python_requires = >=3.5
py_modules = easy_install py_modules = easy_install
packages = find: packages = find:
...@@ -60,5 +55,26 @@ exclude = *.tests ...@@ -60,5 +55,26 @@ exclude = *.tests
[options.extras_require] [options.extras_require]
ssl = ssl =
wincertstore==0.2; sys_platform=='win32' wincertstore==0.2; sys_platform=='win32'
certs = certs =
certifi==2016.9.26 certifi==2016.9.26
tests =
mock
pytest-flake8
flake8-2020; python_version>="3.6"
virtualenv>=13.0.0
pytest-virtualenv>=1.2.7
pytest>=3.7
wheel
coverage>=4.5.1
pytest-cov>=2.5.1
paver; python_version>="3.6"
futures; python_version=="2.7"
pip>=19.1 # For proper file:// URLs support.
docs =
# Keep these in sync with docs/requirements.txt
sphinx
jaraco.packaging>=6.1
rst.linker>=1.9
...@@ -44,7 +44,7 @@ def _gen_console_scripts(): ...@@ -44,7 +44,7 @@ def _gen_console_scripts():
if any(os.environ.get(var) not in (None, "", "0") for var in var_names): if any(os.environ.get(var) not in (None, "", "0") for var in var_names):
return return
tmpl = "easy_install-{shortver} = setuptools.command.easy_install:main" tmpl = "easy_install-{shortver} = setuptools.command.easy_install:main"
yield tmpl.format(shortver=sys.version[:3]) yield tmpl.format(shortver='{}.{}'.format(*sys.version_info))
package_data = dict( package_data = dict(
...@@ -89,6 +89,13 @@ setup_params = dict( ...@@ -89,6 +89,13 @@ setup_params = dict(
"%(cmd)s = setuptools.command.%(cmd)s:%(cmd)s" % locals() "%(cmd)s = setuptools.command.%(cmd)s:%(cmd)s" % locals()
for cmd in read_commands() for cmd in read_commands()
], ],
"setuptools.finalize_distribution_options": [
"parent_finalize = setuptools.dist:_Distribution.finalize_options",
"features = setuptools.dist:Distribution._finalize_feature_opts",
"keywords = setuptools.dist:Distribution._finalize_setup_keywords",
"2to3_doctests = "
"setuptools.dist:Distribution._finalize_2to3_doctests",
],
"distutils.setup_keywords": [ "distutils.setup_keywords": [
"eager_resources = setuptools.dist:assert_string_list", "eager_resources = setuptools.dist:assert_string_list",
"namespace_packages = setuptools.dist:check_nsp", "namespace_packages = setuptools.dist:check_nsp",
......
"""Extensions to the 'distutils' for large or complex distributions""" """Extensions to the 'distutils' for large or complex distributions"""
import os import os
import sys
import functools import functools
import distutils.core import distutils.core
import distutils.filelist import distutils.filelist
...@@ -31,7 +30,7 @@ __all__ = [ ...@@ -31,7 +30,7 @@ __all__ = [
] ]
if PY3: if PY3:
__all__.append('find_namespace_packages') __all__.append('find_namespace_packages')
__version__ = setuptools.version.__version__ __version__ = setuptools.version.__version__
...@@ -123,7 +122,7 @@ class PEP420PackageFinder(PackageFinder): ...@@ -123,7 +122,7 @@ class PEP420PackageFinder(PackageFinder):
find_packages = PackageFinder.find find_packages = PackageFinder.find
if PY3: if PY3:
find_namespace_packages = PEP420PackageFinder.find find_namespace_packages = PEP420PackageFinder.find
def _install_setup_requires(attrs): def _install_setup_requires(attrs):
...@@ -144,6 +143,7 @@ def setup(**attrs): ...@@ -144,6 +143,7 @@ def setup(**attrs):
_install_setup_requires(attrs) _install_setup_requires(attrs)
return distutils.core.setup(**attrs) return distutils.core.setup(**attrs)
setup.__doc__ = distutils.core.setup.__doc__ setup.__doc__ = distutils.core.setup.__doc__
...@@ -191,8 +191,8 @@ class Command(_Command): ...@@ -191,8 +191,8 @@ class Command(_Command):
ok = False ok = False
if not ok: if not ok:
raise DistutilsOptionError( raise DistutilsOptionError(
"'%s' must be a list of strings (got %r)" "'%s' must be a list of strings (got %r)"
% (option, val)) % (option, val))
def reinitialize_command(self, command, reinit_subcommands=0, **kw): def reinitialize_command(self, command, reinit_subcommands=0, **kw):
cmd = _Command.reinitialize_command(self, command, reinit_subcommands) cmd = _Command.reinitialize_command(self, command, reinit_subcommands)
......
"""
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_spec(module, paths):
finder = (
importlib.machinery.PathFinder().find_spec
if isinstance(paths, list) else
importlib.util.find_spec
)
return finder(module, paths)
def find_module(module, paths=None):
"""Just like 'imp.find_module()', but with package support"""
spec = 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 = 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 = find_spec(module, paths)
if not spec:
raise ImportError("Can't find %s" % module)
return module_from_spec(spec)
...@@ -4,18 +4,24 @@ ...@@ -4,18 +4,24 @@
from __future__ import absolute_import, division, print_function from __future__ import absolute_import, division, print_function
__all__ = [ __all__ = [
"__title__", "__summary__", "__uri__", "__version__", "__author__", "__title__",
"__email__", "__license__", "__copyright__", "__summary__",
"__uri__",
"__version__",
"__author__",
"__email__",
"__license__",
"__copyright__",
] ]
__title__ = "packaging" __title__ = "packaging"
__summary__ = "Core utilities for Python packages" __summary__ = "Core utilities for Python packages"
__uri__ = "https://github.com/pypa/packaging" __uri__ = "https://github.com/pypa/packaging"
__version__ = "16.8" __version__ = "19.2"
__author__ = "Donald Stufft and individual contributors" __author__ = "Donald Stufft and individual contributors"
__email__ = "donald@stufft.io" __email__ = "donald@stufft.io"
__license__ = "BSD or Apache License, Version 2.0" __license__ = "BSD or Apache License, Version 2.0"
__copyright__ = "Copyright 2014-2016 %s" % __author__ __copyright__ = "Copyright 2014-2019 %s" % __author__
...@@ -4,11 +4,23 @@ ...@@ -4,11 +4,23 @@
from __future__ import absolute_import, division, print_function from __future__ import absolute_import, division, print_function
from .__about__ import ( from .__about__ import (
__author__, __copyright__, __email__, __license__, __summary__, __title__, __author__,
__uri__, __version__ __copyright__,
__email__,
__license__,
__summary__,
__title__,
__uri__,
__version__,
) )
__all__ = [ __all__ = [
"__title__", "__summary__", "__uri__", "__version__", "__author__", "__title__",
"__email__", "__license__", "__copyright__", "__summary__",
"__uri__",
"__version__",
"__author__",
"__email__",
"__license__",
"__copyright__",
] ]
...@@ -12,9 +12,9 @@ PY3 = sys.version_info[0] == 3 ...@@ -12,9 +12,9 @@ PY3 = sys.version_info[0] == 3
# flake8: noqa # flake8: noqa
if PY3: if PY3:
string_types = str, string_types = (str,)
else: else:
string_types = basestring, string_types = (basestring,)
def with_metaclass(meta, *bases): def with_metaclass(meta, *bases):
...@@ -27,4 +27,5 @@ def with_metaclass(meta, *bases): ...@@ -27,4 +27,5 @@ def with_metaclass(meta, *bases):
class metaclass(meta): class metaclass(meta):
def __new__(cls, name, this_bases, d): def __new__(cls, name, this_bases, d):
return meta(name, bases, d) return meta(name, bases, d)
return type.__new__(metaclass, 'temporary_class', (), {})
return type.__new__(metaclass, "temporary_class", (), {})
...@@ -5,7 +5,6 @@ from __future__ import absolute_import, division, print_function ...@@ -5,7 +5,6 @@ from __future__ import absolute_import, division, print_function
class Infinity(object): class Infinity(object):
def __repr__(self): def __repr__(self):
return "Infinity" return "Infinity"
...@@ -33,11 +32,11 @@ class Infinity(object): ...@@ -33,11 +32,11 @@ class Infinity(object):
def __neg__(self): def __neg__(self):
return NegativeInfinity return NegativeInfinity
Infinity = Infinity() Infinity = Infinity()
class NegativeInfinity(object): class NegativeInfinity(object):
def __repr__(self): def __repr__(self):
return "-Infinity" return "-Infinity"
...@@ -65,4 +64,5 @@ class NegativeInfinity(object): ...@@ -65,4 +64,5 @@ class NegativeInfinity(object):
def __neg__(self): def __neg__(self):
return Infinity return Infinity
NegativeInfinity = NegativeInfinity() NegativeInfinity = NegativeInfinity()
...@@ -17,8 +17,11 @@ from .specifiers import Specifier, InvalidSpecifier ...@@ -17,8 +17,11 @@ from .specifiers import Specifier, InvalidSpecifier
__all__ = [ __all__ = [
"InvalidMarker", "UndefinedComparison", "UndefinedEnvironmentName", "InvalidMarker",
"Marker", "default_environment", "UndefinedComparison",
"UndefinedEnvironmentName",
"Marker",
"default_environment",
] ]
...@@ -42,7 +45,6 @@ class UndefinedEnvironmentName(ValueError): ...@@ -42,7 +45,6 @@ class UndefinedEnvironmentName(ValueError):
class Node(object): class Node(object):
def __init__(self, value): def __init__(self, value):
self.value = value self.value = value
...@@ -57,62 +59,52 @@ class Node(object): ...@@ -57,62 +59,52 @@ class Node(object):
class Variable(Node): class Variable(Node):
def serialize(self): def serialize(self):
return str(self) return str(self)
class Value(Node): class Value(Node):
def serialize(self): def serialize(self):
return '"{0}"'.format(self) return '"{0}"'.format(self)
class Op(Node): class Op(Node):
def serialize(self): def serialize(self):
return str(self) return str(self)
VARIABLE = ( VARIABLE = (
L("implementation_version") | L("implementation_version")
L("platform_python_implementation") | | L("platform_python_implementation")
L("implementation_name") | | L("implementation_name")
L("python_full_version") | | L("python_full_version")
L("platform_release") | | L("platform_release")
L("platform_version") | | L("platform_version")
L("platform_machine") | | L("platform_machine")
L("platform_system") | | L("platform_system")
L("python_version") | | L("python_version")
L("sys_platform") | | L("sys_platform")
L("os_name") | | L("os_name")
L("os.name") | # PEP-345 | L("os.name")
L("sys.platform") | # PEP-345 | L("sys.platform") # PEP-345
L("platform.version") | # PEP-345 | L("platform.version") # PEP-345
L("platform.machine") | # PEP-345 | L("platform.machine") # PEP-345
L("platform.python_implementation") | # PEP-345 | L("platform.python_implementation") # PEP-345
L("python_implementation") | # undocumented setuptools legacy | L("python_implementation") # PEP-345
L("extra") | L("extra") # undocumented setuptools legacy
) )
ALIASES = { ALIASES = {
'os.name': 'os_name', "os.name": "os_name",
'sys.platform': 'sys_platform', "sys.platform": "sys_platform",
'platform.version': 'platform_version', "platform.version": "platform_version",
'platform.machine': 'platform_machine', "platform.machine": "platform_machine",
'platform.python_implementation': 'platform_python_implementation', "platform.python_implementation": "platform_python_implementation",
'python_implementation': 'platform_python_implementation' "python_implementation": "platform_python_implementation",
} }
VARIABLE.setParseAction(lambda s, l, t: Variable(ALIASES.get(t[0], t[0]))) VARIABLE.setParseAction(lambda s, l, t: Variable(ALIASES.get(t[0], t[0])))
VERSION_CMP = ( VERSION_CMP = (
L("===") | L("===") | L("==") | L(">=") | L("<=") | L("!=") | L("~=") | L(">") | L("<")
L("==") |
L(">=") |
L("<=") |
L("!=") |
L("~=") |
L(">") |
L("<")
) )
MARKER_OP = VERSION_CMP | L("not in") | L("in") MARKER_OP = VERSION_CMP | L("not in") | L("in")
...@@ -152,8 +144,11 @@ def _format_marker(marker, first=True): ...@@ -152,8 +144,11 @@ def _format_marker(marker, first=True):
# where the single item is itself it's own list. In that case we want skip # where the single item is itself it's own list. In that case we want skip
# the rest of this function so that we don't get extraneous () on the # the rest of this function so that we don't get extraneous () on the
# outside. # outside.
if (isinstance(marker, list) and len(marker) == 1 and if (
isinstance(marker[0], (list, tuple))): isinstance(marker, list)
and len(marker) == 1
and isinstance(marker[0], (list, tuple))
):
return _format_marker(marker[0]) return _format_marker(marker[0])
if isinstance(marker, list): if isinstance(marker, list):
...@@ -239,20 +234,20 @@ def _evaluate_markers(markers, environment): ...@@ -239,20 +234,20 @@ def _evaluate_markers(markers, environment):
def format_full_version(info): def format_full_version(info):
version = '{0.major}.{0.minor}.{0.micro}'.format(info) version = "{0.major}.{0.minor}.{0.micro}".format(info)
kind = info.releaselevel kind = info.releaselevel
if kind != 'final': if kind != "final":
version += kind[0] + str(info.serial) version += kind[0] + str(info.serial)
return version return version
def default_environment(): def default_environment():
if hasattr(sys, 'implementation'): if hasattr(sys, "implementation"):
iver = format_full_version(sys.implementation.version) iver = format_full_version(sys.implementation.version)
implementation_name = sys.implementation.name implementation_name = sys.implementation.name
else: else:
iver = '0' iver = "0"
implementation_name = '' implementation_name = ""
return { return {
"implementation_name": implementation_name, "implementation_name": implementation_name,
...@@ -264,19 +259,19 @@ def default_environment(): ...@@ -264,19 +259,19 @@ def default_environment():
"platform_version": platform.version(), "platform_version": platform.version(),
"python_full_version": platform.python_version(), "python_full_version": platform.python_version(),
"platform_python_implementation": platform.python_implementation(), "platform_python_implementation": platform.python_implementation(),
"python_version": platform.python_version()[:3], "python_version": ".".join(platform.python_version_tuple()[:2]),
"sys_platform": sys.platform, "sys_platform": sys.platform,
} }
class Marker(object): class Marker(object):
def __init__(self, marker): def __init__(self, marker):
try: try:
self._markers = _coerce_parse_result(MARKER.parseString(marker)) self._markers = _coerce_parse_result(MARKER.parseString(marker))
except ParseException as e: except ParseException as e:
err_str = "Invalid marker: {0!r}, parse error at {1!r}".format( err_str = "Invalid marker: {0!r}, parse error at {1!r}".format(
marker, marker[e.loc:e.loc + 8]) marker, marker[e.loc : e.loc + 8]
)
raise InvalidMarker(err_str) raise InvalidMarker(err_str)
def __str__(self): def __str__(self):
......
...@@ -38,8 +38,8 @@ IDENTIFIER = Combine(ALPHANUM + ZeroOrMore(IDENTIFIER_END)) ...@@ -38,8 +38,8 @@ IDENTIFIER = Combine(ALPHANUM + ZeroOrMore(IDENTIFIER_END))
NAME = IDENTIFIER("name") NAME = IDENTIFIER("name")
EXTRA = IDENTIFIER EXTRA = IDENTIFIER
URI = Regex(r'[^ ]+')("url") URI = Regex(r"[^ ]+")("url")
URL = (AT + URI) URL = AT + URI
EXTRAS_LIST = EXTRA + ZeroOrMore(COMMA + EXTRA) EXTRAS_LIST = EXTRA + ZeroOrMore(COMMA + EXTRA)
EXTRAS = (LBRACKET + Optional(EXTRAS_LIST) + RBRACKET)("extras") EXTRAS = (LBRACKET + Optional(EXTRAS_LIST) + RBRACKET)("extras")
...@@ -48,28 +48,31 @@ VERSION_PEP440 = Regex(Specifier._regex_str, re.VERBOSE | re.IGNORECASE) ...@@ -48,28 +48,31 @@ VERSION_PEP440 = Regex(Specifier._regex_str, re.VERBOSE | re.IGNORECASE)
VERSION_LEGACY = Regex(LegacySpecifier._regex_str, re.VERBOSE | re.IGNORECASE) VERSION_LEGACY = Regex(LegacySpecifier._regex_str, re.VERBOSE | re.IGNORECASE)
VERSION_ONE = VERSION_PEP440 ^ VERSION_LEGACY VERSION_ONE = VERSION_PEP440 ^ VERSION_LEGACY
VERSION_MANY = Combine(VERSION_ONE + ZeroOrMore(COMMA + VERSION_ONE), VERSION_MANY = Combine(
joinString=",", adjacent=False)("_raw_spec") VERSION_ONE + ZeroOrMore(COMMA + VERSION_ONE), joinString=",", adjacent=False
)("_raw_spec")
_VERSION_SPEC = Optional(((LPAREN + VERSION_MANY + RPAREN) | VERSION_MANY)) _VERSION_SPEC = Optional(((LPAREN + VERSION_MANY + RPAREN) | VERSION_MANY))
_VERSION_SPEC.setParseAction(lambda s, l, t: t._raw_spec or '') _VERSION_SPEC.setParseAction(lambda s, l, t: t._raw_spec or "")
VERSION_SPEC = originalTextFor(_VERSION_SPEC)("specifier") VERSION_SPEC = originalTextFor(_VERSION_SPEC)("specifier")
VERSION_SPEC.setParseAction(lambda s, l, t: t[1]) VERSION_SPEC.setParseAction(lambda s, l, t: t[1])
MARKER_EXPR = originalTextFor(MARKER_EXPR())("marker") MARKER_EXPR = originalTextFor(MARKER_EXPR())("marker")
MARKER_EXPR.setParseAction( MARKER_EXPR.setParseAction(
lambda s, l, t: Marker(s[t._original_start:t._original_end]) lambda s, l, t: Marker(s[t._original_start : t._original_end])
) )
MARKER_SEPERATOR = SEMICOLON MARKER_SEPARATOR = SEMICOLON
MARKER = MARKER_SEPERATOR + MARKER_EXPR MARKER = MARKER_SEPARATOR + MARKER_EXPR
VERSION_AND_MARKER = VERSION_SPEC + Optional(MARKER) VERSION_AND_MARKER = VERSION_SPEC + Optional(MARKER)
URL_AND_MARKER = URL + Optional(MARKER) URL_AND_MARKER = URL + Optional(MARKER)
NAMED_REQUIREMENT = \ NAMED_REQUIREMENT = NAME + Optional(EXTRAS) + (URL_AND_MARKER | VERSION_AND_MARKER)
NAME + Optional(EXTRAS) + (URL_AND_MARKER | VERSION_AND_MARKER)
REQUIREMENT = stringStart + NAMED_REQUIREMENT + stringEnd REQUIREMENT = stringStart + NAMED_REQUIREMENT + stringEnd
# setuptools.extern.pyparsing isn't thread safe during initialization, so we do it eagerly, see
# issue #104
REQUIREMENT.parseString("x[]")
class Requirement(object): class Requirement(object):
...@@ -90,15 +93,21 @@ class Requirement(object): ...@@ -90,15 +93,21 @@ class Requirement(object):
req = REQUIREMENT.parseString(requirement_string) req = REQUIREMENT.parseString(requirement_string)
except ParseException as e: except ParseException as e:
raise InvalidRequirement( raise InvalidRequirement(
"Invalid requirement, parse error at \"{0!r}\"".format( 'Parse error at "{0!r}": {1}'.format(
requirement_string[e.loc:e.loc + 8])) requirement_string[e.loc : e.loc + 8], e.msg
)
)
self.name = req.name self.name = req.name
if req.url: if req.url:
parsed_url = urlparse.urlparse(req.url) parsed_url = urlparse.urlparse(req.url)
if not (parsed_url.scheme and parsed_url.netloc) or ( if parsed_url.scheme == "file":
not parsed_url.scheme and not parsed_url.netloc): if urlparse.urlunparse(parsed_url) != req.url:
raise InvalidRequirement("Invalid URL given") raise InvalidRequirement("Invalid URL given")
elif not (parsed_url.scheme and parsed_url.netloc) or (
not parsed_url.scheme and not parsed_url.netloc
):
raise InvalidRequirement("Invalid URL: {0}".format(req.url))
self.url = req.url self.url = req.url
else: else:
self.url = None self.url = None
...@@ -117,6 +126,8 @@ class Requirement(object): ...@@ -117,6 +126,8 @@ class Requirement(object):
if self.url: if self.url:
parts.append("@ {0}".format(self.url)) parts.append("@ {0}".format(self.url))
if self.marker:
parts.append(" ")
if self.marker: if self.marker:
parts.append("; {0}".format(self.marker)) parts.append("; {0}".format(self.marker))
......
...@@ -19,7 +19,6 @@ class InvalidSpecifier(ValueError): ...@@ -19,7 +19,6 @@ class InvalidSpecifier(ValueError):
class BaseSpecifier(with_metaclass(abc.ABCMeta, object)): class BaseSpecifier(with_metaclass(abc.ABCMeta, object)):
@abc.abstractmethod @abc.abstractmethod
def __str__(self): def __str__(self):
""" """
...@@ -84,10 +83,7 @@ class _IndividualSpecifier(BaseSpecifier): ...@@ -84,10 +83,7 @@ class _IndividualSpecifier(BaseSpecifier):
if not match: if not match:
raise InvalidSpecifier("Invalid specifier: '{0}'".format(spec)) raise InvalidSpecifier("Invalid specifier: '{0}'".format(spec))
self._spec = ( self._spec = (match.group("operator").strip(), match.group("version").strip())
match.group("operator").strip(),
match.group("version").strip(),
)
# Store whether or not this Specifier should accept prereleases # Store whether or not this Specifier should accept prereleases
self._prereleases = prereleases self._prereleases = prereleases
...@@ -99,11 +95,7 @@ class _IndividualSpecifier(BaseSpecifier): ...@@ -99,11 +95,7 @@ class _IndividualSpecifier(BaseSpecifier):
else "" else ""
) )
return "<{0}({1!r}{2})>".format( return "<{0}({1!r}{2})>".format(self.__class__.__name__, str(self), pre)
self.__class__.__name__,
str(self),
pre,
)
def __str__(self): def __str__(self):
return "{0}{1}".format(*self._spec) return "{0}{1}".format(*self._spec)
...@@ -194,11 +186,12 @@ class _IndividualSpecifier(BaseSpecifier): ...@@ -194,11 +186,12 @@ class _IndividualSpecifier(BaseSpecifier):
# If our version is a prerelease, and we were not set to allow # If our version is a prerelease, and we were not set to allow
# prereleases, then we'll store it for later incase nothing # prereleases, then we'll store it for later incase nothing
# else matches this specifier. # else matches this specifier.
if (parsed_version.is_prerelease and not if parsed_version.is_prerelease and not (
(prereleases or self.prereleases)): prereleases or self.prereleases
):
found_prereleases.append(version) found_prereleases.append(version)
# Either this is not a prerelease, or we should have been # Either this is not a prerelease, or we should have been
# accepting prereleases from the begining. # accepting prereleases from the beginning.
else: else:
yielded = True yielded = True
yield version yield version
...@@ -213,8 +206,7 @@ class _IndividualSpecifier(BaseSpecifier): ...@@ -213,8 +206,7 @@ class _IndividualSpecifier(BaseSpecifier):
class LegacySpecifier(_IndividualSpecifier): class LegacySpecifier(_IndividualSpecifier):
_regex_str = ( _regex_str = r"""
r"""
(?P<operator>(==|!=|<=|>=|<|>)) (?P<operator>(==|!=|<=|>=|<|>))
\s* \s*
(?P<version> (?P<version>
...@@ -225,10 +217,8 @@ class LegacySpecifier(_IndividualSpecifier): ...@@ -225,10 +217,8 @@ class LegacySpecifier(_IndividualSpecifier):
# them, and a comma since it's a version separator. # them, and a comma since it's a version separator.
) )
""" """
)
_regex = re.compile( _regex = re.compile(r"^\s*" + _regex_str + r"\s*$", re.VERBOSE | re.IGNORECASE)
r"^\s*" + _regex_str + r"\s*$", re.VERBOSE | re.IGNORECASE)
_operators = { _operators = {
"==": "equal", "==": "equal",
...@@ -269,13 +259,13 @@ def _require_version_compare(fn): ...@@ -269,13 +259,13 @@ def _require_version_compare(fn):
if not isinstance(prospective, Version): if not isinstance(prospective, Version):
return False return False
return fn(self, prospective, spec) return fn(self, prospective, spec)
return wrapped return wrapped
class Specifier(_IndividualSpecifier): class Specifier(_IndividualSpecifier):
_regex_str = ( _regex_str = r"""
r"""
(?P<operator>(~=|==|!=|<=|>=|<|>|===)) (?P<operator>(~=|==|!=|<=|>=|<|>|===))
(?P<version> (?P<version>
(?: (?:
...@@ -367,10 +357,8 @@ class Specifier(_IndividualSpecifier): ...@@ -367,10 +357,8 @@ class Specifier(_IndividualSpecifier):
) )
) )
""" """
)
_regex = re.compile( _regex = re.compile(r"^\s*" + _regex_str + r"\s*$", re.VERBOSE | re.IGNORECASE)
r"^\s*" + _regex_str + r"\s*$", re.VERBOSE | re.IGNORECASE)
_operators = { _operators = {
"~=": "compatible", "~=": "compatible",
...@@ -397,8 +385,7 @@ class Specifier(_IndividualSpecifier): ...@@ -397,8 +385,7 @@ class Specifier(_IndividualSpecifier):
prefix = ".".join( prefix = ".".join(
list( list(
itertools.takewhile( itertools.takewhile(
lambda x: (not x.startswith("post") and not lambda x: (not x.startswith("post") and not x.startswith("dev")),
x.startswith("dev")),
_version_split(spec), _version_split(spec),
) )
)[:-1] )[:-1]
...@@ -407,8 +394,9 @@ class Specifier(_IndividualSpecifier): ...@@ -407,8 +394,9 @@ class Specifier(_IndividualSpecifier):
# Add the prefix notation to the end of our string # Add the prefix notation to the end of our string
prefix += ".*" prefix += ".*"
return (self._get_operator(">=")(prospective, spec) and return self._get_operator(">=")(prospective, spec) and self._get_operator("==")(
self._get_operator("==")(prospective, prefix)) prospective, prefix
)
@_require_version_compare @_require_version_compare
def _compare_equal(self, prospective, spec): def _compare_equal(self, prospective, spec):
...@@ -428,7 +416,7 @@ class Specifier(_IndividualSpecifier): ...@@ -428,7 +416,7 @@ class Specifier(_IndividualSpecifier):
# Shorten the prospective version to be the same length as the spec # Shorten the prospective version to be the same length as the spec
# so that we can determine if the specifier is a prefix of the # so that we can determine if the specifier is a prefix of the
# prospective version or not. # prospective version or not.
prospective = prospective[:len(spec)] prospective = prospective[: len(spec)]
# Pad out our two sides with zeros so that they both equal the same # Pad out our two sides with zeros so that they both equal the same
# length. # length.
...@@ -503,7 +491,7 @@ class Specifier(_IndividualSpecifier): ...@@ -503,7 +491,7 @@ class Specifier(_IndividualSpecifier):
return False return False
# Ensure that we do not allow a local version of the version mentioned # Ensure that we do not allow a local version of the version mentioned
# in the specifier, which is techincally greater than, to match. # in the specifier, which is technically greater than, to match.
if prospective.local is not None: if prospective.local is not None:
if Version(prospective.base_version) == Version(spec.base_version): if Version(prospective.base_version) == Version(spec.base_version):
return False return False
...@@ -567,27 +555,17 @@ def _pad_version(left, right): ...@@ -567,27 +555,17 @@ def _pad_version(left, right):
right_split.append(list(itertools.takewhile(lambda x: x.isdigit(), right))) right_split.append(list(itertools.takewhile(lambda x: x.isdigit(), right)))
# Get the rest of our versions # Get the rest of our versions
left_split.append(left[len(left_split[0]):]) left_split.append(left[len(left_split[0]) :])
right_split.append(right[len(right_split[0]):]) right_split.append(right[len(right_split[0]) :])
# Insert our padding # Insert our padding
left_split.insert( left_split.insert(1, ["0"] * max(0, len(right_split[0]) - len(left_split[0])))
1, right_split.insert(1, ["0"] * max(0, len(left_split[0]) - len(right_split[0])))
["0"] * max(0, len(right_split[0]) - len(left_split[0])),
)
right_split.insert(
1,
["0"] * max(0, len(left_split[0]) - len(right_split[0])),
)
return ( return (list(itertools.chain(*left_split)), list(itertools.chain(*right_split)))
list(itertools.chain(*left_split)),
list(itertools.chain(*right_split)),
)
class SpecifierSet(BaseSpecifier): class SpecifierSet(BaseSpecifier):
def __init__(self, specifiers="", prereleases=None): def __init__(self, specifiers="", prereleases=None):
# Split on , to break each indidivual specifier into it's own item, and # Split on , to break each indidivual specifier into it's own item, and
# strip each item to remove leading/trailing whitespace. # strip each item to remove leading/trailing whitespace.
...@@ -721,10 +699,7 @@ class SpecifierSet(BaseSpecifier): ...@@ -721,10 +699,7 @@ class SpecifierSet(BaseSpecifier):
# given version is contained within all of them. # given version is contained within all of them.
# Note: This use of all() here means that an empty set of specifiers # Note: This use of all() here means that an empty set of specifiers
# will always return True, this is an explicit design decision. # will always return True, this is an explicit design decision.
return all( return all(s.contains(item, prereleases=prereleases) for s in self._specs)
s.contains(item, prereleases=prereleases)
for s in self._specs
)
def filter(self, iterable, prereleases=None): def filter(self, iterable, prereleases=None):
# Determine if we're forcing a prerelease or not, if we're not forcing # Determine if we're forcing a prerelease or not, if we're not forcing
......
This diff is collapsed.
...@@ -5,6 +5,8 @@ from __future__ import absolute_import, division, print_function ...@@ -5,6 +5,8 @@ from __future__ import absolute_import, division, print_function
import re import re
from .version import InvalidVersion, Version
_canonicalize_regex = re.compile(r"[-_.]+") _canonicalize_regex = re.compile(r"[-_.]+")
...@@ -12,3 +14,44 @@ _canonicalize_regex = re.compile(r"[-_.]+") ...@@ -12,3 +14,44 @@ _canonicalize_regex = re.compile(r"[-_.]+")
def canonicalize_name(name): def canonicalize_name(name):
# This is taken from PEP 503. # This is taken from PEP 503.
return _canonicalize_regex.sub("-", name).lower() return _canonicalize_regex.sub("-", name).lower()
def canonicalize_version(version):
"""
This is very similar to Version.__str__, but has one subtle differences
with the way it handles the release segment.
"""
try:
version = Version(version)
except InvalidVersion:
# Legacy versions cannot be normalized
return version
parts = []
# Epoch
if version.epoch != 0:
parts.append("{0}!".format(version.epoch))
# Release segment
# NB: This strips trailing '.0's to normalize
parts.append(re.sub(r"(\.0)+$", "", ".".join(str(x) for x in version.release)))
# Pre-release
if version.pre is not None:
parts.append("".join(str(x) for x in version.pre))
# Post-release
if version.post is not None:
parts.append(".post{0}".format(version.post))
# Development release
if version.dev is not None:
parts.append(".dev{0}".format(version.dev))
# Local version segment
if version.local is not None:
parts.append("+{0}".format(version.local))
return "".join(parts)
...@@ -10,14 +10,11 @@ import re ...@@ -10,14 +10,11 @@ import re
from ._structures import Infinity from ._structures import Infinity
__all__ = [ __all__ = ["parse", "Version", "LegacyVersion", "InvalidVersion", "VERSION_PATTERN"]
"parse", "Version", "LegacyVersion", "InvalidVersion", "VERSION_PATTERN"
]
_Version = collections.namedtuple( _Version = collections.namedtuple(
"_Version", "_Version", ["epoch", "release", "dev", "pre", "post", "local"]
["epoch", "release", "dev", "pre", "post", "local"],
) )
...@@ -40,7 +37,6 @@ class InvalidVersion(ValueError): ...@@ -40,7 +37,6 @@ class InvalidVersion(ValueError):
class _BaseVersion(object): class _BaseVersion(object):
def __hash__(self): def __hash__(self):
return hash(self._key) return hash(self._key)
...@@ -70,7 +66,6 @@ class _BaseVersion(object): ...@@ -70,7 +66,6 @@ class _BaseVersion(object):
class LegacyVersion(_BaseVersion): class LegacyVersion(_BaseVersion):
def __init__(self, version): def __init__(self, version):
self._version = str(version) self._version = str(version)
self._key = _legacy_cmpkey(self._version) self._key = _legacy_cmpkey(self._version)
...@@ -89,6 +84,26 @@ class LegacyVersion(_BaseVersion): ...@@ -89,6 +84,26 @@ class LegacyVersion(_BaseVersion):
def base_version(self): def base_version(self):
return self._version return self._version
@property
def epoch(self):
return -1
@property
def release(self):
return None
@property
def pre(self):
return None
@property
def post(self):
return None
@property
def dev(self):
return None
@property @property
def local(self): def local(self):
return None return None
...@@ -101,13 +116,19 @@ class LegacyVersion(_BaseVersion): ...@@ -101,13 +116,19 @@ class LegacyVersion(_BaseVersion):
def is_postrelease(self): def is_postrelease(self):
return False return False
@property
def is_devrelease(self):
return False
_legacy_version_component_re = re.compile(
r"(\d+ | [a-z]+ | \.| -)", re.VERBOSE, _legacy_version_component_re = re.compile(r"(\d+ | [a-z]+ | \.| -)", re.VERBOSE)
)
_legacy_version_replacement_map = { _legacy_version_replacement_map = {
"pre": "c", "preview": "c", "-": "final-", "rc": "c", "dev": "@", "pre": "c",
"preview": "c",
"-": "final-",
"rc": "c",
"dev": "@",
} }
...@@ -154,6 +175,7 @@ def _legacy_cmpkey(version): ...@@ -154,6 +175,7 @@ def _legacy_cmpkey(version):
return epoch, parts return epoch, parts
# Deliberately not anchored to the start and end of the string, to make it # Deliberately not anchored to the start and end of the string, to make it
# easier for 3rd party code to reuse # easier for 3rd party code to reuse
VERSION_PATTERN = r""" VERSION_PATTERN = r"""
...@@ -190,10 +212,7 @@ VERSION_PATTERN = r""" ...@@ -190,10 +212,7 @@ VERSION_PATTERN = r"""
class Version(_BaseVersion): class Version(_BaseVersion):
_regex = re.compile( _regex = re.compile(r"^\s*" + VERSION_PATTERN + r"\s*$", re.VERBOSE | re.IGNORECASE)
r"^\s*" + VERSION_PATTERN + r"\s*$",
re.VERBOSE | re.IGNORECASE,
)
def __init__(self, version): def __init__(self, version):
# Validate the version and parse it into pieces # Validate the version and parse it into pieces
...@@ -205,18 +224,11 @@ class Version(_BaseVersion): ...@@ -205,18 +224,11 @@ class Version(_BaseVersion):
self._version = _Version( self._version = _Version(
epoch=int(match.group("epoch")) if match.group("epoch") else 0, epoch=int(match.group("epoch")) if match.group("epoch") else 0,
release=tuple(int(i) for i in match.group("release").split(".")), release=tuple(int(i) for i in match.group("release").split(".")),
pre=_parse_letter_version( pre=_parse_letter_version(match.group("pre_l"), match.group("pre_n")),
match.group("pre_l"),
match.group("pre_n"),
),
post=_parse_letter_version( post=_parse_letter_version(
match.group("post_l"), match.group("post_l"), match.group("post_n1") or match.group("post_n2")
match.group("post_n1") or match.group("post_n2"),
),
dev=_parse_letter_version(
match.group("dev_l"),
match.group("dev_n"),
), ),
dev=_parse_letter_version(match.group("dev_l"), match.group("dev_n")),
local=_parse_local_version(match.group("local")), local=_parse_local_version(match.group("local")),
) )
...@@ -237,32 +249,57 @@ class Version(_BaseVersion): ...@@ -237,32 +249,57 @@ class Version(_BaseVersion):
parts = [] parts = []
# Epoch # Epoch
if self._version.epoch != 0: if self.epoch != 0:
parts.append("{0}!".format(self._version.epoch)) parts.append("{0}!".format(self.epoch))
# Release segment # Release segment
parts.append(".".join(str(x) for x in self._version.release)) parts.append(".".join(str(x) for x in self.release))
# Pre-release # Pre-release
if self._version.pre is not None: if self.pre is not None:
parts.append("".join(str(x) for x in self._version.pre)) parts.append("".join(str(x) for x in self.pre))
# Post-release # Post-release
if self._version.post is not None: if self.post is not None:
parts.append(".post{0}".format(self._version.post[1])) parts.append(".post{0}".format(self.post))
# Development release # Development release
if self._version.dev is not None: if self.dev is not None:
parts.append(".dev{0}".format(self._version.dev[1])) parts.append(".dev{0}".format(self.dev))
# Local version segment # Local version segment
if self._version.local is not None: if self.local is not None:
parts.append( parts.append("+{0}".format(self.local))
"+{0}".format(".".join(str(x) for x in self._version.local))
)
return "".join(parts) return "".join(parts)
@property
def epoch(self):
return self._version.epoch
@property
def release(self):
return self._version.release
@property
def pre(self):
return self._version.pre
@property
def post(self):
return self._version.post[1] if self._version.post else None
@property
def dev(self):
return self._version.dev[1] if self._version.dev else None
@property
def local(self):
if self._version.local:
return ".".join(str(x) for x in self._version.local)
else:
return None
@property @property
def public(self): def public(self):
return str(self).split("+", 1)[0] return str(self).split("+", 1)[0]
...@@ -272,27 +309,25 @@ class Version(_BaseVersion): ...@@ -272,27 +309,25 @@ class Version(_BaseVersion):
parts = [] parts = []
# Epoch # Epoch
if self._version.epoch != 0: if self.epoch != 0:
parts.append("{0}!".format(self._version.epoch)) parts.append("{0}!".format(self.epoch))
# Release segment # Release segment
parts.append(".".join(str(x) for x in self._version.release)) parts.append(".".join(str(x) for x in self.release))
return "".join(parts) return "".join(parts)
@property
def local(self):
version_string = str(self)
if "+" in version_string:
return version_string.split("+", 1)[1]
@property @property
def is_prerelease(self): def is_prerelease(self):
return bool(self._version.dev or self._version.pre) return self.dev is not None or self.pre is not None
@property @property
def is_postrelease(self): def is_postrelease(self):
return bool(self._version.post) return self.post is not None
@property
def is_devrelease(self):
return self.dev is not None
def _parse_letter_version(letter, number): def _parse_letter_version(letter, number):
...@@ -326,7 +361,7 @@ def _parse_letter_version(letter, number): ...@@ -326,7 +361,7 @@ def _parse_letter_version(letter, number):
return letter, int(number) return letter, int(number)
_local_version_seperators = re.compile(r"[\._-]") _local_version_separators = re.compile(r"[\._-]")
def _parse_local_version(local): def _parse_local_version(local):
...@@ -336,7 +371,7 @@ def _parse_local_version(local): ...@@ -336,7 +371,7 @@ def _parse_local_version(local):
if local is not None: if local is not None:
return tuple( return tuple(
part.lower() if not part.isdigit() else int(part) part.lower() if not part.isdigit() else int(part)
for part in _local_version_seperators.split(local) for part in _local_version_separators.split(local)
) )
...@@ -347,12 +382,7 @@ def _cmpkey(epoch, release, pre, post, dev, local): ...@@ -347,12 +382,7 @@ def _cmpkey(epoch, release, pre, post, dev, local):
# re-reverse it back into the correct order and make it a tuple and use # re-reverse it back into the correct order and make it a tuple and use
# that for our sorting key. # that for our sorting key.
release = tuple( release = tuple(
reversed(list( reversed(list(itertools.dropwhile(lambda x: x == 0, reversed(release))))
itertools.dropwhile(
lambda x: x == 0,
reversed(release),
)
))
) )
# We need to "trick" the sorting algorithm to put 1.0.dev0 before 1.0a0. # We need to "trick" the sorting algorithm to put 1.0.dev0 before 1.0a0.
...@@ -385,9 +415,6 @@ def _cmpkey(epoch, release, pre, post, dev, local): ...@@ -385,9 +415,6 @@ def _cmpkey(epoch, release, pre, post, dev, local):
# - Numeric segments sort numerically # - Numeric segments sort numerically
# - Shorter versions sort before longer versions when the prefixes # - Shorter versions sort before longer versions when the prefixes
# match exactly # match exactly
local = tuple( local = tuple((i, "") if isinstance(i, int) else (-Infinity, i) for i in local)
(i, "") if isinstance(i, int) else (-Infinity, i)
for i in local
)
return epoch, release, pre, post, dev, local return epoch, release, pre, post, dev, local
packaging==16.8 packaging==19.2
pyparsing==2.2.1 pyparsing==2.2.1
six==1.10.0 six==1.10.0
ordered-set==3.1.1 ordered-set==3.1.1
...@@ -25,7 +25,8 @@ def default_filter(src, dst): ...@@ -25,7 +25,8 @@ def default_filter(src, dst):
return dst return dst
def unpack_archive(filename, extract_dir, progress_filter=default_filter, def unpack_archive(
filename, extract_dir, progress_filter=default_filter,
drivers=None): drivers=None):
"""Unpack `filename` to `extract_dir`, or raise ``UnrecognizedFormat`` """Unpack `filename` to `extract_dir`, or raise ``UnrecognizedFormat``
...@@ -148,7 +149,8 @@ def unpack_tarfile(filename, extract_dir, progress_filter=default_filter): ...@@ -148,7 +149,8 @@ def unpack_tarfile(filename, extract_dir, progress_filter=default_filter):
# resolve any links and to extract the link targets as normal # resolve any links and to extract the link targets as normal
# files # files
while member is not None and (member.islnk() or member.issym()): while member is not None and (
member.islnk() or member.issym()):
linkpath = member.linkname linkpath = member.linkname
if member.issym(): if member.issym():
base = posixpath.dirname(member.name) base = posixpath.dirname(member.name)
......
...@@ -48,6 +48,7 @@ __all__ = ['get_requires_for_build_sdist', ...@@ -48,6 +48,7 @@ __all__ = ['get_requires_for_build_sdist',
'__legacy__', '__legacy__',
'SetupRequirementsError'] 'SetupRequirementsError']
class SetupRequirementsError(BaseException): class SetupRequirementsError(BaseException):
def __init__(self, specifiers): def __init__(self, specifiers):
self.specifiers = specifiers self.specifiers = specifiers
...@@ -143,7 +144,8 @@ class _BuildMetaBackend(object): ...@@ -143,7 +144,8 @@ class _BuildMetaBackend(object):
def get_requires_for_build_wheel(self, config_settings=None): def get_requires_for_build_wheel(self, config_settings=None):
config_settings = self._fix_config(config_settings) config_settings = self._fix_config(config_settings)
return self._get_build_requires(config_settings, requirements=['wheel']) return self._get_build_requires(
config_settings, requirements=['wheel'])
def get_requires_for_build_sdist(self, config_settings=None): def get_requires_for_build_sdist(self, config_settings=None):
config_settings = self._fix_config(config_settings) config_settings = self._fix_config(config_settings)
...@@ -160,8 +162,10 @@ class _BuildMetaBackend(object): ...@@ -160,8 +162,10 @@ class _BuildMetaBackend(object):
dist_infos = [f for f in os.listdir(dist_info_directory) dist_infos = [f for f in os.listdir(dist_info_directory)
if f.endswith('.dist-info')] if f.endswith('.dist-info')]
if (len(dist_infos) == 0 and if (
len(_get_immediate_subdirectories(dist_info_directory)) == 1): len(dist_infos) == 0 and
len(_get_immediate_subdirectories(dist_info_directory)) == 1
):
dist_info_directory = os.path.join( dist_info_directory = os.path.join(
dist_info_directory, os.listdir(dist_info_directory)[0]) dist_info_directory, os.listdir(dist_info_directory)[0])
...@@ -193,7 +197,8 @@ class _BuildMetaBackend(object): ...@@ -193,7 +197,8 @@ class _BuildMetaBackend(object):
config_settings["--global-option"]) config_settings["--global-option"])
self.run_setup() self.run_setup()
result_basename = _file_with_extension(tmp_dist_dir, result_extension) result_basename = _file_with_extension(
tmp_dist_dir, result_extension)
result_path = os.path.join(result_directory, result_basename) result_path = os.path.join(result_directory, result_basename)
if os.path.exists(result_path): if os.path.exists(result_path):
# os.rename will fail overwriting on non-Unix. # os.rename will fail overwriting on non-Unix.
...@@ -202,7 +207,6 @@ class _BuildMetaBackend(object): ...@@ -202,7 +207,6 @@ class _BuildMetaBackend(object):
return result_basename return result_basename
def build_wheel(self, wheel_directory, config_settings=None, def build_wheel(self, wheel_directory, config_settings=None,
metadata_directory=None): metadata_directory=None):
return self._build_with_temp_dir(['bdist_wheel'], '.whl', return self._build_with_temp_dir(['bdist_wheel'], '.whl',
...@@ -217,9 +221,12 @@ class _BuildMetaBackend(object): ...@@ -217,9 +221,12 @@ class _BuildMetaBackend(object):
class _BuildMetaLegacyBackend(_BuildMetaBackend): class _BuildMetaLegacyBackend(_BuildMetaBackend):
"""Compatibility backend for setuptools """Compatibility backend for setuptools
This is a version of setuptools.build_meta that endeavors to maintain backwards This is a version of setuptools.build_meta that endeavors
compatibility with pre-PEP 517 modes of invocation. It exists as a temporary to maintain backwards
bridge between the old packaging mechanism and the new packaging mechanism, compatibility with pre-PEP 517 modes of invocation. It
exists as a temporary
bridge between the old packaging mechanism and the new
packaging mechanism,
and will eventually be removed. and will eventually be removed.
""" """
def run_setup(self, setup_script='setup.py'): def run_setup(self, setup_script='setup.py'):
...@@ -232,6 +239,12 @@ class _BuildMetaLegacyBackend(_BuildMetaBackend): ...@@ -232,6 +239,12 @@ class _BuildMetaLegacyBackend(_BuildMetaBackend):
if script_dir not in sys.path: if script_dir not in sys.path:
sys.path.insert(0, script_dir) sys.path.insert(0, script_dir)
# Some setup.py scripts (e.g. in pygame and numpy) use sys.argv[0] to
# get the directory of the source code. They expect it to refer to the
# setup.py script.
sys_argv_0 = sys.argv[0]
sys.argv[0] = setup_script
try: try:
super(_BuildMetaLegacyBackend, super(_BuildMetaLegacyBackend,
self).run_setup(setup_script=setup_script) self).run_setup(setup_script=setup_script)
...@@ -242,6 +255,8 @@ class _BuildMetaLegacyBackend(_BuildMetaBackend): ...@@ -242,6 +255,8 @@ class _BuildMetaLegacyBackend(_BuildMetaBackend):
# the original path so that the path manipulation does not persist # the original path so that the path manipulation does not persist
# within the hook after run_setup is called. # within the hook after run_setup is called.
sys.path[:] = sys_path sys.path[:] = sys_path
sys.argv[0] = sys_argv_0
# The primary backend # The primary backend
_BACKEND = _BuildMetaBackend() _BACKEND = _BuildMetaBackend()
......
...@@ -2,8 +2,7 @@ __all__ = [ ...@@ -2,8 +2,7 @@ __all__ = [
'alias', 'bdist_egg', 'bdist_rpm', 'build_ext', 'build_py', 'develop', 'alias', 'bdist_egg', 'bdist_rpm', 'build_ext', 'build_py', 'develop',
'easy_install', 'egg_info', 'install', 'install_lib', 'rotate', 'saveopts', 'easy_install', 'egg_info', 'install', 'install_lib', 'rotate', 'saveopts',
'sdist', 'setopt', 'test', 'install_egg_info', 'install_scripts', 'sdist', 'setopt', 'test', 'install_egg_info', 'install_scripts',
'register', 'bdist_wininst', 'upload_docs', 'upload', 'build_clib', 'bdist_wininst', 'upload_docs', 'build_clib', 'dist_info',
'dist_info',
] ]
from distutils.command.bdist import bdist from distutils.command.bdist import bdist
......
...@@ -11,13 +11,14 @@ import os ...@@ -11,13 +11,14 @@ import os
import re import re
import textwrap import textwrap
import marshal import marshal
import warnings
from setuptools.extern import six from setuptools.extern import six
from pkg_resources import get_build_platform, Distribution, ensure_directory from pkg_resources import get_build_platform, Distribution, ensure_directory
from pkg_resources import EntryPoint from pkg_resources import EntryPoint
from setuptools.extension import Library from setuptools.extension import Library
from setuptools import Command from setuptools import Command, SetuptoolsDeprecationWarning
try: try:
# Python 2.7 or >=3.2 # Python 2.7 or >=3.2
...@@ -278,13 +279,19 @@ class bdist_egg(Command): ...@@ -278,13 +279,19 @@ class bdist_egg(Command):
if ep is None: if ep is None:
return 'w' # not an eggsecutable, do it the usual way. return 'w' # not an eggsecutable, do it the usual way.
warnings.warn(
"Eggsecutables are deprecated and will be removed in a future "
"version.",
SetuptoolsDeprecationWarning
)
if not ep.attrs or ep.extras: if not ep.attrs or ep.extras:
raise DistutilsSetupError( raise DistutilsSetupError(
"eggsecutable entry point (%r) cannot have 'extras' " "eggsecutable entry point (%r) cannot have 'extras' "
"or refer to a module" % (ep,) "or refer to a module" % (ep,)
) )
pyver = sys.version[:3] pyver = '{}.{}'.format(*sys.version_info)
pkg = ep.module_name pkg = ep.module_name
full = '.'.join(ep.attrs) full = '.'.join(ep.attrs)
base = ep.attrs[0] base = ep.attrs[0]
......
...@@ -25,9 +25,9 @@ class build_clib(orig.build_clib): ...@@ -25,9 +25,9 @@ class build_clib(orig.build_clib):
sources = build_info.get('sources') sources = build_info.get('sources')
if sources is None or not isinstance(sources, (list, tuple)): if sources is None or not isinstance(sources, (list, tuple)):
raise DistutilsSetupError( raise DistutilsSetupError(
"in 'libraries' option (library '%s'), " "in 'libraries' option (library '%s'), "
"'sources' must be present and must be " "'sources' must be present and must be "
"a list of source filenames" % lib_name) "a list of source filenames" % lib_name)
sources = list(sources) sources = list(sources)
log.info("building '%s' library", lib_name) log.info("building '%s' library", lib_name)
...@@ -38,9 +38,9 @@ class build_clib(orig.build_clib): ...@@ -38,9 +38,9 @@ class build_clib(orig.build_clib):
obj_deps = build_info.get('obj_deps', dict()) obj_deps = build_info.get('obj_deps', dict())
if not isinstance(obj_deps, dict): if not isinstance(obj_deps, dict):
raise DistutilsSetupError( raise DistutilsSetupError(
"in 'libraries' option (library '%s'), " "in 'libraries' option (library '%s'), "
"'obj_deps' must be a dictionary of " "'obj_deps' must be a dictionary of "
"type 'source: list'" % lib_name) "type 'source: list'" % lib_name)
dependencies = [] dependencies = []
# Get the global dependencies that are specified by the '' key. # Get the global dependencies that are specified by the '' key.
...@@ -48,9 +48,9 @@ class build_clib(orig.build_clib): ...@@ -48,9 +48,9 @@ class build_clib(orig.build_clib):
global_deps = obj_deps.get('', list()) global_deps = obj_deps.get('', list())
if not isinstance(global_deps, (list, tuple)): if not isinstance(global_deps, (list, tuple)):
raise DistutilsSetupError( raise DistutilsSetupError(
"in 'libraries' option (library '%s'), " "in 'libraries' option (library '%s'), "
"'obj_deps' must be a dictionary of " "'obj_deps' must be a dictionary of "
"type 'source: list'" % lib_name) "type 'source: list'" % lib_name)
# Build the list to be used by newer_pairwise_group # Build the list to be used by newer_pairwise_group
# each source will be auto-added to its dependencies. # each source will be auto-added to its dependencies.
...@@ -60,39 +60,42 @@ class build_clib(orig.build_clib): ...@@ -60,39 +60,42 @@ class build_clib(orig.build_clib):
extra_deps = obj_deps.get(source, list()) extra_deps = obj_deps.get(source, list())
if not isinstance(extra_deps, (list, tuple)): if not isinstance(extra_deps, (list, tuple)):
raise DistutilsSetupError( raise DistutilsSetupError(
"in 'libraries' option (library '%s'), " "in 'libraries' option (library '%s'), "
"'obj_deps' must be a dictionary of " "'obj_deps' must be a dictionary of "
"type 'source: list'" % lib_name) "type 'source: list'" % lib_name)
src_deps.extend(extra_deps) src_deps.extend(extra_deps)
dependencies.append(src_deps) dependencies.append(src_deps)
expected_objects = self.compiler.object_filenames( expected_objects = self.compiler.object_filenames(
sources, sources,
output_dir=self.build_temp output_dir=self.build_temp,
) )
if newer_pairwise_group(dependencies, expected_objects) != ([], []): if (
newer_pairwise_group(dependencies, expected_objects)
!= ([], [])
):
# First, compile the source code to object files in the library # First, compile the source code to object files in the library
# directory. (This should probably change to putting object # directory. (This should probably change to putting object
# files in a temporary build directory.) # files in a temporary build directory.)
macros = build_info.get('macros') macros = build_info.get('macros')
include_dirs = build_info.get('include_dirs') include_dirs = build_info.get('include_dirs')
cflags = build_info.get('cflags') cflags = build_info.get('cflags')
objects = self.compiler.compile( self.compiler.compile(
sources, sources,
output_dir=self.build_temp, output_dir=self.build_temp,
macros=macros, macros=macros,
include_dirs=include_dirs, include_dirs=include_dirs,
extra_postargs=cflags, extra_postargs=cflags,
debug=self.debug debug=self.debug
) )
# Now "link" the object files together into a static library. # Now "link" the object files together into a static library.
# (On Unix at least, this isn't really linking -- it just # (On Unix at least, this isn't really linking -- it just
# builds an archive. Whatever.) # builds an archive. Whatever.)
self.compiler.create_static_lib( self.compiler.create_static_lib(
expected_objects, expected_objects,
lib_name, lib_name,
output_dir=self.build_clib, output_dir=self.build_clib,
debug=self.debug debug=self.debug
) )
...@@ -14,7 +14,8 @@ from setuptools.extern import six ...@@ -14,7 +14,8 @@ from setuptools.extern import six
if six.PY2: if six.PY2:
import imp import imp
EXTENSION_SUFFIXES = [s for s, _, tp in imp.get_suffixes() if tp == imp.C_EXTENSION] EXTENSION_SUFFIXES = [
s for s, _, tp in imp.get_suffixes() if tp == imp.C_EXTENSION]
else: else:
from importlib.machinery import EXTENSION_SUFFIXES from importlib.machinery import EXTENSION_SUFFIXES
...@@ -29,7 +30,7 @@ except ImportError: ...@@ -29,7 +30,7 @@ except ImportError:
# make sure _config_vars is initialized # make sure _config_vars is initialized
get_config_var("LDSHARED") get_config_var("LDSHARED")
from distutils.sysconfig import _config_vars as _CONFIG_VARS from distutils.sysconfig import _config_vars as _CONFIG_VARS # noqa
def _customize_compiler_for_shlib(compiler): def _customize_compiler_for_shlib(compiler):
...@@ -65,7 +66,9 @@ elif os.name != 'nt': ...@@ -65,7 +66,9 @@ elif os.name != 'nt':
except ImportError: except ImportError:
pass pass
if_dl = lambda s: s if have_rtld else ''
def if_dl(s):
return s if have_rtld else ''
def get_abi3_suffix(): def get_abi3_suffix():
...@@ -113,7 +116,7 @@ class build_ext(_build_ext): ...@@ -113,7 +116,7 @@ class build_ext(_build_ext):
if fullname in self.ext_map: if fullname in self.ext_map:
ext = self.ext_map[fullname] ext = self.ext_map[fullname]
use_abi3 = ( use_abi3 = (
six.PY3 not six.PY2
and getattr(ext, 'py_limited_api') and getattr(ext, 'py_limited_api')
and get_abi3_suffix() and get_abi3_suffix()
) )
......
...@@ -108,7 +108,7 @@ class develop(namespaces.DevelopInstaller, easy_install): ...@@ -108,7 +108,7 @@ class develop(namespaces.DevelopInstaller, easy_install):
return path_to_setup return path_to_setup
def install_for_development(self): def install_for_development(self):
if six.PY3 and getattr(self.distribution, 'use_2to3', False): if not six.PY2 and getattr(self.distribution, 'use_2to3', False):
# If we run 2to3 we can not do this inplace: # If we run 2to3 we can not do this inplace:
# Ensure metadata is up-to-date # Ensure metadata is up-to-date
......
...@@ -121,7 +121,8 @@ else: ...@@ -121,7 +121,8 @@ else:
return False return False
_one_liner = lambda text: textwrap.dedent(text).strip().replace('\n', '; ') def _one_liner(text):
return textwrap.dedent(text).strip().replace('\n', '; ')
class easy_install(Command): class easy_install(Command):
...@@ -156,19 +157,16 @@ class easy_install(Command): ...@@ -156,19 +157,16 @@ class easy_install(Command):
"allow building eggs from local checkouts"), "allow building eggs from local checkouts"),
('version', None, "print version information and exit"), ('version', None, "print version information and exit"),
('no-find-links', None, ('no-find-links', None,
"Don't load find-links defined in packages being installed") "Don't load find-links defined in packages being installed"),
('user', None, "install in user site-package '%s'" % site.USER_SITE)
] ]
boolean_options = [ boolean_options = [
'zip-ok', 'multi-version', 'exclude-scripts', 'upgrade', 'always-copy', 'zip-ok', 'multi-version', 'exclude-scripts', 'upgrade', 'always-copy',
'editable', 'editable',
'no-deps', 'local-snapshots-ok', 'version' 'no-deps', 'local-snapshots-ok', 'version',
'user'
] ]
if site.ENABLE_USER_SITE:
help_msg = "install in user site-package '%s'" % site.USER_SITE
user_options.append(('user', None, help_msg))
boolean_options.append('user')
negative_opt = {'always-unzip': 'zip-ok'} negative_opt = {'always-unzip': 'zip-ok'}
create_index = PackageIndex create_index = PackageIndex
...@@ -241,7 +239,7 @@ class easy_install(Command): ...@@ -241,7 +239,7 @@ class easy_install(Command):
""" """
Render the Setuptools version and installation details, then exit. Render the Setuptools version and installation details, then exit.
""" """
ver = sys.version[:3] ver = '{}.{}'.format(*sys.version_info)
dist = get_distribution('setuptools') dist = get_distribution('setuptools')
tmpl = 'setuptools {dist.version} from {dist.location} (Python {ver})' tmpl = 'setuptools {dist.version} from {dist.location} (Python {ver})'
print(tmpl.format(**locals())) print(tmpl.format(**locals()))
...@@ -272,6 +270,9 @@ class easy_install(Command): ...@@ -272,6 +270,9 @@ class easy_install(Command):
self.config_vars['userbase'] = self.install_userbase self.config_vars['userbase'] = self.install_userbase
self.config_vars['usersite'] = self.install_usersite self.config_vars['usersite'] = self.install_usersite
elif self.user:
log.warn("WARNING: The user site-packages directory is disabled.")
self._fix_install_dir_for_user_site() self._fix_install_dir_for_user_site()
self.expand_basedirs() self.expand_basedirs()
...@@ -410,7 +411,13 @@ class easy_install(Command): ...@@ -410,7 +411,13 @@ class easy_install(Command):
] ]
self._expand_attrs(dirs) 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: if self.verbose != self.distribution.verbose:
log.set_verbosity(self.verbose) log.set_verbosity(self.verbose)
try: try:
...@@ -472,8 +479,9 @@ class easy_install(Command): ...@@ -472,8 +479,9 @@ class easy_install(Command):
self.cant_write_to_target() self.cant_write_to_target()
if not is_site_dir and not self.multi_version: if not is_site_dir and not self.multi_version:
# Can't install non-multi to non-site dir # Can't install non-multi to non-site dir with easy_install
raise DistutilsError(self.no_default_version_msg()) pythonpath = os.environ.get('PYTHONPATH', '')
log.warn(self.__no_default_msg, self.install_dir, pythonpath)
if is_site_dir: if is_site_dir:
if self.pth_file is None: if self.pth_file is None:
...@@ -501,13 +509,13 @@ class easy_install(Command): ...@@ -501,13 +509,13 @@ class easy_install(Command):
the distutils default setting) was: the distutils default setting) was:
%s %s
""").lstrip() """).lstrip() # noqa
__not_exists_id = textwrap.dedent(""" __not_exists_id = textwrap.dedent("""
This directory does not currently exist. Please create it and try again, or This directory does not currently exist. Please create it and try again, or
choose a different installation directory (using the -d or --install-dir choose a different installation directory (using the -d or --install-dir
option). option).
""").lstrip() """).lstrip() # noqa
__access_msg = textwrap.dedent(""" __access_msg = textwrap.dedent("""
Perhaps your account does not have write access to this directory? If the Perhaps your account does not have write access to this directory? If the
...@@ -523,7 +531,7 @@ class easy_install(Command): ...@@ -523,7 +531,7 @@ class easy_install(Command):
https://setuptools.readthedocs.io/en/latest/easy_install.html https://setuptools.readthedocs.io/en/latest/easy_install.html
Please make the appropriate changes for your system and try again. Please make the appropriate changes for your system and try again.
""").lstrip() """).lstrip() # noqa
def cant_write_to_target(self): def cant_write_to_target(self):
msg = self.__cant_write_msg % (sys.exc_info()[1], self.install_dir,) msg = self.__cant_write_msg % (sys.exc_info()[1], self.install_dir,)
...@@ -1087,13 +1095,13 @@ class easy_install(Command): ...@@ -1087,13 +1095,13 @@ class easy_install(Command):
pkg_resources.require("%(name)s") # latest installed version pkg_resources.require("%(name)s") # latest installed version
pkg_resources.require("%(name)s==%(version)s") # this exact version pkg_resources.require("%(name)s==%(version)s") # this exact version
pkg_resources.require("%(name)s>=%(version)s") # this version or higher pkg_resources.require("%(name)s>=%(version)s") # this version or higher
""").lstrip() """).lstrip() # noqa
__id_warning = textwrap.dedent(""" __id_warning = textwrap.dedent("""
Note also that the installation directory must be on sys.path at runtime for Note also that the installation directory must be on sys.path at runtime for
this to work. (e.g. by being the application's script directory, by being on this to work. (e.g. by being the application's script directory, by being on
PYTHONPATH, or by being added to sys.path by your code.) PYTHONPATH, or by being added to sys.path by your code.)
""") """) # noqa
def installation_report(self, req, dist, what="Installed"): def installation_report(self, req, dist, what="Installed"):
"""Helpful installation message for display to package users""" """Helpful installation message for display to package users"""
...@@ -1118,7 +1126,7 @@ class easy_install(Command): ...@@ -1118,7 +1126,7 @@ class easy_install(Command):
%(python)s setup.py develop %(python)s setup.py develop
See the setuptools documentation for the "develop" command for more info. See the setuptools documentation for the "develop" command for more info.
""").lstrip() """).lstrip() # noqa
def report_editable(self, spec, setup_script): def report_editable(self, spec, setup_script):
dirname = os.path.dirname(setup_script) dirname = os.path.dirname(setup_script)
...@@ -1301,11 +1309,8 @@ class easy_install(Command): ...@@ -1301,11 +1309,8 @@ class easy_install(Command):
https://setuptools.readthedocs.io/en/latest/easy_install.html#custom-installation-locations https://setuptools.readthedocs.io/en/latest/easy_install.html#custom-installation-locations
Please make the appropriate changes for your system and try again.""").lstrip() Please make the appropriate changes for your system and try again.
""").strip()
def no_default_version_msg(self):
template = self.__no_default_msg
return template % (self.install_dir, os.environ.get('PYTHONPATH', ''))
def install_site_py(self): def install_site_py(self):
"""Make sure there's a site.py in the target dir, if needed""" """Make sure there's a site.py in the target dir, if needed"""
...@@ -1411,7 +1416,7 @@ def get_site_dirs(): ...@@ -1411,7 +1416,7 @@ def get_site_dirs():
os.path.join( os.path.join(
prefix, prefix,
"lib", "lib",
"python" + sys.version[:3], "python{}.{}".format(*sys.version_info),
"site-packages", "site-packages",
), ),
os.path.join(prefix, "lib", "site-python"), os.path.join(prefix, "lib", "site-python"),
...@@ -1432,7 +1437,7 @@ def get_site_dirs(): ...@@ -1432,7 +1437,7 @@ def get_site_dirs():
home, home,
'Library', 'Library',
'Python', 'Python',
sys.version[:3], '{}.{}'.format(*sys.version_info),
'site-packages', 'site-packages',
) )
sitedirs.append(home_sp) sitedirs.append(home_sp)
...@@ -1561,7 +1566,7 @@ def get_exe_prefixes(exe_filename): ...@@ -1561,7 +1566,7 @@ def get_exe_prefixes(exe_filename):
continue continue
if parts[0].upper() in ('PURELIB', 'PLATLIB'): if parts[0].upper() in ('PURELIB', 'PLATLIB'):
contents = z.read(name) contents = z.read(name)
if six.PY3: if not six.PY2:
contents = contents.decode() contents = contents.decode()
for pth in yield_lines(contents): for pth in yield_lines(contents):
pth = pth.strip().replace('\\', '/') pth = pth.strip().replace('\\', '/')
...@@ -2087,7 +2092,8 @@ class ScriptWriter: ...@@ -2087,7 +2092,8 @@ class ScriptWriter:
@classmethod @classmethod
def get_script_header(cls, script_text, executable=None, wininst=False): def get_script_header(cls, script_text, executable=None, wininst=False):
# for backward compatibility # for backward compatibility
warnings.warn("Use get_header", EasyInstallDeprecationWarning, stacklevel=2) warnings.warn(
"Use get_header", EasyInstallDeprecationWarning, stacklevel=2)
if wininst: if wininst:
executable = "python.exe" executable = "python.exe"
return cls.get_header(script_text, executable) return cls.get_header(script_text, executable)
...@@ -2336,6 +2342,8 @@ def _patch_usage(): ...@@ -2336,6 +2342,8 @@ def _patch_usage():
finally: finally:
distutils.core.gen_usage = saved distutils.core.gen_usage = saved
class EasyInstallDeprecationWarning(SetuptoolsDeprecationWarning): class EasyInstallDeprecationWarning(SetuptoolsDeprecationWarning):
"""Class for warning about deprecations in EasyInstall in SetupTools. Not ignored by default, unlike DeprecationWarning.""" """
Warning for EasyInstall deprecations, bypassing suppression.
"""
...@@ -33,6 +33,7 @@ from setuptools.glob import glob ...@@ -33,6 +33,7 @@ from setuptools.glob import glob
from setuptools.extern import packaging from setuptools.extern import packaging
from setuptools import SetuptoolsDeprecationWarning from setuptools import SetuptoolsDeprecationWarning
def translate_pattern(glob): def translate_pattern(glob):
""" """
Translate a file path glob like '*.txt' in to a regular expression. Translate a file path glob like '*.txt' in to a regular expression.
...@@ -113,7 +114,7 @@ def translate_pattern(glob): ...@@ -113,7 +114,7 @@ def translate_pattern(glob):
pat += sep pat += sep
pat += r'\Z' pat += r'\Z'
return re.compile(pat, flags=re.MULTILINE|re.DOTALL) return re.compile(pat, flags=re.MULTILINE | re.DOTALL)
class InfoCommon: class InfoCommon:
...@@ -266,7 +267,7 @@ class egg_info(InfoCommon, Command): ...@@ -266,7 +267,7 @@ class egg_info(InfoCommon, Command):
to the file. to the file.
""" """
log.info("writing %s to %s", what, filename) log.info("writing %s to %s", what, filename)
if six.PY3: if not six.PY2:
data = data.encode("utf-8") data = data.encode("utf-8")
if not self.dry_run: if not self.dry_run:
f = open(filename, 'wb') f = open(filename, 'wb')
...@@ -637,7 +638,9 @@ def warn_depends_obsolete(cmd, basename, filename): ...@@ -637,7 +638,9 @@ def warn_depends_obsolete(cmd, basename, filename):
def _write_requirements(stream, reqs): def _write_requirements(stream, reqs):
lines = yield_lines(reqs or ()) lines = yield_lines(reqs or ())
append_cr = lambda line: line + '\n'
def append_cr(line):
return line + '\n'
lines = map(append_cr, lines) lines = map(append_cr, lines)
stream.writelines(lines) stream.writelines(lines)
...@@ -703,7 +706,8 @@ def get_pkg_info_revision(): ...@@ -703,7 +706,8 @@ def get_pkg_info_revision():
Get a -r### off of PKG-INFO Version in case this is an sdist of Get a -r### off of PKG-INFO Version in case this is an sdist of
a subversion revision. a subversion revision.
""" """
warnings.warn("get_pkg_info_revision is deprecated.", EggInfoDeprecationWarning) warnings.warn(
"get_pkg_info_revision is deprecated.", EggInfoDeprecationWarning)
if os.path.exists('PKG-INFO'): if os.path.exists('PKG-INFO'):
with io.open('PKG-INFO') as f: with io.open('PKG-INFO') as f:
for line in f: for line in f:
...@@ -714,4 +718,4 @@ def get_pkg_info_revision(): ...@@ -714,4 +718,4 @@ def get_pkg_info_revision():
class EggInfoDeprecationWarning(SetuptoolsDeprecationWarning): class EggInfoDeprecationWarning(SetuptoolsDeprecationWarning):
"""Class for warning about deprecations in eggInfo in setupTools. Not ignored by default, unlike DeprecationWarning.""" """Deprecated behavior warning for EggInfo, bypassing suppression."""
...@@ -114,7 +114,7 @@ class install(orig.install): ...@@ -114,7 +114,7 @@ class install(orig.install):
args.insert(0, setuptools.bootstrap_install_from) args.insert(0, setuptools.bootstrap_install_from)
cmd.args = args cmd.args = args
cmd.run() cmd.run(show_deprecation=False)
setuptools.bootstrap_install_from = None setuptools.bootstrap_install_from = None
......
...@@ -77,7 +77,8 @@ class install_lib(orig.install_lib): ...@@ -77,7 +77,8 @@ class install_lib(orig.install_lib):
if not hasattr(sys, 'implementation'): if not hasattr(sys, 'implementation'):
return return
base = os.path.join('__pycache__', '__init__.' + sys.implementation.cache_tag) base = os.path.join(
'__pycache__', '__init__.' + sys.implementation.cache_tag)
yield base + '.pyc' yield base + '.pyc'
yield base + '.pyo' yield base + '.pyo'
yield base + '.opt-1.pyc' yield base + '.opt-1.pyc'
......
...@@ -32,8 +32,11 @@ class install_scripts(orig.install_scripts): ...@@ -32,8 +32,11 @@ class install_scripts(orig.install_scripts):
) )
bs_cmd = self.get_finalized_command('build_scripts') bs_cmd = self.get_finalized_command('build_scripts')
exec_param = getattr(bs_cmd, 'executable', None) exec_param = getattr(bs_cmd, 'executable', None)
bw_cmd = self.get_finalized_command("bdist_wininst") try:
is_wininst = getattr(bw_cmd, '_is_running', False) bw_cmd = self.get_finalized_command("bdist_wininst")
is_wininst = getattr(bw_cmd, '_is_running', False)
except ImportError:
is_wininst = False
writer = ei.ScriptWriter writer = ei.ScriptWriter
if is_wininst: if is_wininst:
exec_param = "python.exe" exec_param = "python.exe"
......
...@@ -132,5 +132,5 @@ class sdist_add_defaults: ...@@ -132,5 +132,5 @@ class sdist_add_defaults:
if hasattr(sdist.sdist, '_add_defaults_standards'): if hasattr(sdist.sdist, '_add_defaults_standards'):
# disable the functionality already available upstream # disable the functionality already available upstream
class sdist_add_defaults: class sdist_add_defaults: # noqa
pass pass
from distutils import log from distutils import log
import distutils.command.register as orig import distutils.command.register as orig
from setuptools.errors import RemovedCommandError
class register(orig.register): class register(orig.register):
__doc__ = orig.register.__doc__ """Formerly used to register packages on PyPI."""
def run(self): def run(self):
try: msg = (
# Make sure that we are using valid current name/version info "The register command has been removed, use twine to upload "
self.run_command('egg_info') + "instead (https://pypi.org/p/twine)"
orig.register.run(self) )
finally:
self.announce( self.announce("ERROR: " + msg, log.ERROR)
"WARNING: Registering is deprecated, use twine to "
"upload instead (https://pypi.org/p/twine/)", raise RemovedCommandError(msg)
log.WARN
)
...@@ -5,7 +5,7 @@ import sys ...@@ -5,7 +5,7 @@ import sys
import io import io
import contextlib import contextlib
from setuptools.extern import six from setuptools.extern import six, ordered_set
from .py36compat import sdist_add_defaults from .py36compat import sdist_add_defaults
...@@ -121,19 +121,40 @@ class sdist(sdist_add_defaults, orig.sdist): ...@@ -121,19 +121,40 @@ class sdist(sdist_add_defaults, orig.sdist):
if has_leaky_handle: if has_leaky_handle:
read_template = __read_template_hack read_template = __read_template_hack
def _add_defaults_optional(self):
if six.PY2:
sdist_add_defaults._add_defaults_optional(self)
else:
super()._add_defaults_optional()
if os.path.isfile('pyproject.toml'):
self.filelist.append('pyproject.toml')
def _add_defaults_python(self): def _add_defaults_python(self):
"""getting python files""" """getting python files"""
if self.distribution.has_pure_modules(): if self.distribution.has_pure_modules():
build_py = self.get_finalized_command('build_py') build_py = self.get_finalized_command('build_py')
self.filelist.extend(build_py.get_source_files()) self.filelist.extend(build_py.get_source_files())
# This functionality is incompatible with include_package_data, and self._add_data_files(self._safe_data_files(build_py))
# will in fact create an infinite recursion if include_package_data
# is True. Use of include_package_data will imply that def _safe_data_files(self, build_py):
# distutils-style automatic handling of package_data is disabled """
if not self.distribution.include_package_data: Extracting data_files from build_py is known to cause
for _, src_dir, _, filenames in build_py.data_files: infinite recursion errors when `include_package_data`
self.filelist.extend([os.path.join(src_dir, filename) is enabled, so suppress it in that case.
for filename in filenames]) """
if self.distribution.include_package_data:
return ()
return build_py.data_files
def _add_data_files(self, data_files):
"""
Add data files as found in build_py.data_files.
"""
self.filelist.extend(
os.path.join(src_dir, name)
for _, src_dir, _, filenames in data_files
for name in filenames
)
def _add_defaults_data_files(self): def _add_defaults_data_files(self):
try: try:
...@@ -186,7 +207,7 @@ class sdist(sdist_add_defaults, orig.sdist): ...@@ -186,7 +207,7 @@ class sdist(sdist_add_defaults, orig.sdist):
manifest = open(self.manifest, 'rb') manifest = open(self.manifest, 'rb')
for line in manifest: for line in manifest:
# The manifest must contain UTF-8. See #303. # The manifest must contain UTF-8. See #303.
if six.PY3: if not six.PY2:
try: try:
line = line.decode('UTF-8') line = line.decode('UTF-8')
except UnicodeDecodeError: except UnicodeDecodeError:
...@@ -200,10 +221,12 @@ class sdist(sdist_add_defaults, orig.sdist): ...@@ -200,10 +221,12 @@ class sdist(sdist_add_defaults, orig.sdist):
manifest.close() manifest.close()
def check_license(self): def check_license(self):
"""Checks if license_file' is configured and adds it to """Checks if license_file' or 'license_files' is configured and adds any
'self.filelist' if the value contains a valid path. valid paths to 'self.filelist'.
""" """
files = ordered_set.OrderedSet()
opts = self.distribution.get_option_dict('metadata') opts = self.distribution.get_option_dict('metadata')
# ignore the source of the value # ignore the source of the value
...@@ -211,11 +234,19 @@ class sdist(sdist_add_defaults, orig.sdist): ...@@ -211,11 +234,19 @@ class sdist(sdist_add_defaults, orig.sdist):
if license_file is None: if license_file is None:
log.debug("'license_file' option was not specified") log.debug("'license_file' option was not specified")
return else:
files.add(license_file)
if not os.path.exists(license_file): try:
log.warn("warning: Failed to find the configured license file '%s'", files.update(self.distribution.metadata.license_files)
license_file) except TypeError:
return 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)
...@@ -129,7 +129,8 @@ class test(Command): ...@@ -129,7 +129,8 @@ class test(Command):
@contextlib.contextmanager @contextlib.contextmanager
def project_on_sys_path(self, include_dists=[]): def project_on_sys_path(self, include_dists=[]):
with_2to3 = six.PY3 and getattr(self.distribution, 'use_2to3', False) with_2to3 = not six.PY2 and getattr(
self.distribution, 'use_2to3', False)
if with_2to3: if with_2to3:
# If we run 2to3 we can not do this inplace: # If we run 2to3 we can not do this inplace:
...@@ -240,7 +241,7 @@ class test(Command): ...@@ -240,7 +241,7 @@ class test(Command):
# Purge modules under test from sys.modules. The test loader will # Purge modules under test from sys.modules. The test loader will
# re-import them from the build location. Required when 2to3 is used # re-import them from the build location. Required when 2to3 is used
# with namespace packages. # with namespace packages.
if six.PY3 and getattr(self.distribution, 'use_2to3', False): if not six.PY2 and getattr(self.distribution, 'use_2to3', False):
module = self.test_suite.split('.')[0] module = self.test_suite.split('.')[0]
if module in _namespace_packages: if module in _namespace_packages:
del_modules = [] del_modules = []
......
import io
import os
import hashlib
import getpass
from base64 import standard_b64encode
from distutils import log from distutils import log
from distutils.command import upload as orig 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.errors import RemovedCommandError
from setuptools.extern.six.moves.urllib.error import HTTPError
from setuptools.extern.six.moves.urllib.parse import urlparse
class upload(orig.upload): class upload(orig.upload):
""" """Formerly used to upload packages to PyPI."""
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
)
def finalize_options(self): def run(self):
orig.upload.finalize_options(self) msg = (
self.username = ( "The upload command has been removed, use twine to upload "
self.username or + "instead (https://pypi.org/p/twine)"
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 upload_file(self, command, pyversion, filename): self.announce("ERROR: " + msg, log.ERROR)
# Makes sure the repository URL is compliant raise RemovedCommandError(msg)
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
...@@ -24,7 +24,7 @@ from .upload import upload ...@@ -24,7 +24,7 @@ from .upload import upload
def _encode(s): def _encode(s):
errors = 'surrogateescape' if six.PY3 else 'strict' errors = 'strict' if six.PY2 else 'surrogateescape'
return s.encode('utf-8', errors) return s.encode('utf-8', errors)
...@@ -127,8 +127,8 @@ class upload_docs(upload): ...@@ -127,8 +127,8 @@ class upload_docs(upload):
""" """
Build up the MIME payload for the POST data Build up the MIME payload for the POST data
""" """
boundary = b'--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254'
sep_boundary = b'\n--' + boundary sep_boundary = b'\n--' + boundary.encode('ascii')
end_boundary = sep_boundary + b'--' end_boundary = sep_boundary + b'--'
end_items = end_boundary, b"\n", end_items = end_boundary, b"\n",
builder = functools.partial( builder = functools.partial(
...@@ -138,7 +138,7 @@ class upload_docs(upload): ...@@ -138,7 +138,7 @@ class upload_docs(upload):
part_groups = map(builder, data.items()) part_groups = map(builder, data.items())
parts = itertools.chain.from_iterable(part_groups) parts = itertools.chain.from_iterable(part_groups)
body_items = itertools.chain(parts, end_items) body_items = itertools.chain(parts, end_items)
content_type = 'multipart/form-data; boundary=%s' % boundary.decode('ascii') content_type = 'multipart/form-data; boundary=%s' % boundary
return b''.join(body_items), content_type return b''.join(body_items), content_type
def upload_file(self, filename): def upload_file(self, filename):
...@@ -153,7 +153,7 @@ class upload_docs(upload): ...@@ -153,7 +153,7 @@ class upload_docs(upload):
# set up the authentication # set up the authentication
credentials = _encode(self.username + ':' + self.password) credentials = _encode(self.username + ':' + self.password)
credentials = standard_b64encode(credentials) credentials = standard_b64encode(credentials)
if six.PY3: if not six.PY2:
credentials = credentials.decode('ascii') credentials = credentials.decode('ascii')
auth = "Basic " + credentials auth = "Basic " + credentials
......
...@@ -483,6 +483,7 @@ class ConfigMetadataHandler(ConfigHandler): ...@@ -483,6 +483,7 @@ class ConfigMetadataHandler(ConfigHandler):
'obsoletes': parse_list, 'obsoletes': parse_list,
'classifiers': self._get_parser_compound(parse_file, parse_list), 'classifiers': self._get_parser_compound(parse_file, parse_list),
'license': exclude_files_parser('license'), 'license': exclude_files_parser('license'),
'license_files': parse_list,
'description': parse_file, 'description': parse_file,
'long_description': parse_file, 'long_description': parse_file,
'version': self._parse_version, 'version': self._parse_version,
......
from distutils.dep_util import newer_group from distutils.dep_util import newer_group
# yes, this is was almost entirely copy-pasted from # yes, this is was almost entirely copy-pasted from
# 'newer_pairwise()', this is just another convenience # 'newer_pairwise()', this is just another convenience
# function. # function.
...@@ -10,7 +11,8 @@ def newer_pairwise_group(sources_groups, targets): ...@@ -10,7 +11,8 @@ def newer_pairwise_group(sources_groups, targets):
of 'newer_group()'. of 'newer_group()'.
""" """
if len(sources_groups) != len(targets): if len(sources_groups) != len(targets):
raise ValueError("'sources_group' and 'targets' must be the same length") raise ValueError(
"'sources_group' and 'targets' must be the same length")
# build a pair of lists (sources_groups, targets) where source is newer # build a pair of lists (sources_groups, targets) where source is newer
n_sources = [] n_sources = []
......
import sys import sys
import imp
import marshal import marshal
import contextlib
from distutils.version import StrictVersion from distutils.version import StrictVersion
from imp import PKG_DIRECTORY, PY_COMPILED, PY_SOURCE, PY_FROZEN
from .py33compat import Bytecode from .py33compat import Bytecode
from .py27compat import find_module, PY_COMPILED, PY_FROZEN, PY_SOURCE
from . import py27compat
__all__ = [ __all__ = [
'Require', 'find_module', 'get_module_constant', 'extract_constant' 'Require', 'find_module', 'get_module_constant', 'extract_constant'
...@@ -15,7 +17,8 @@ __all__ = [ ...@@ -15,7 +17,8 @@ __all__ = [
class Require: class Require:
"""A prerequisite to building or installing a distribution""" """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): attribute=None, format=None):
if format is None and requested_version is not None: if format is None and requested_version is not None:
...@@ -79,23 +82,15 @@ class Require: ...@@ -79,23 +82,15 @@ class Require:
return self.version_ok(version) return self.version_ok(version)
def find_module(module, paths=None): def maybe_close(f):
"""Just like 'imp.find_module()', but with package support""" @contextlib.contextmanager
def empty():
parts = module.split('.') yield
return
while parts: if not f:
part = parts.pop(0) return empty()
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))
return info return contextlib.closing(f)
def get_module_constant(module, symbol, default=-1, paths=None): def get_module_constant(module, symbol, default=-1, paths=None):
...@@ -106,28 +101,23 @@ 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'.""" constant. Otherwise, return 'default'."""
try: try:
f, path, (suffix, mode, kind) = find_module(module, paths) f, path, (suffix, mode, kind) = info = find_module(module, paths)
except ImportError: except ImportError:
# Module doesn't exist # Module doesn't exist
return None return None
try: with maybe_close(f):
if kind == PY_COMPILED: if kind == PY_COMPILED:
f.read(8) # skip magic & date f.read(8) # skip magic & date
code = marshal.load(f) code = marshal.load(f)
elif kind == PY_FROZEN: elif kind == PY_FROZEN:
code = imp.get_frozen_object(module) code = py27compat.get_frozen_object(module, paths)
elif kind == PY_SOURCE: elif kind == PY_SOURCE:
code = compile(f.read(), path, 'exec') code = compile(f.read(), path, 'exec')
else: else:
# Not something we can parse; we'll have to import it. :( # Not something we can parse; we'll have to import it. :(
if module not in sys.modules: imported = py27compat.get_module(module, paths, info)
imp.load_module(module, f, path, (suffix, mode, kind)) return getattr(imported, symbol, None)
return getattr(sys.modules[module], symbol, None)
finally:
if f:
f.close()
return extract_constant(code, symbol, default) return extract_constant(code, symbol, default)
......
...@@ -162,7 +162,7 @@ def write_pkg_file(self, file): ...@@ -162,7 +162,7 @@ def write_pkg_file(self, file):
if self.download_url: if self.download_url:
write_field('Download-URL', self.download_url) write_field('Download-URL', self.download_url)
for project_url in self.project_urls.items(): for project_url in self.project_urls.items():
write_field('Project-URL', '%s, %s' % project_url) write_field('Project-URL', '%s, %s' % project_url)
long_desc = rfc822_escape(self.get_long_description()) long_desc = rfc822_escape(self.get_long_description())
write_field('Description', long_desc) write_field('Description', long_desc)
...@@ -409,6 +409,7 @@ class Distribution(_Distribution): ...@@ -409,6 +409,7 @@ class Distribution(_Distribution):
'long_description_content_type': None, 'long_description_content_type': None,
'project_urls': dict, 'project_urls': dict,
'provides_extras': ordered_set.OrderedSet, 'provides_extras': ordered_set.OrderedSet,
'license_files': ordered_set.OrderedSet,
} }
_patched_dist = None _patched_dist = None
...@@ -570,7 +571,7 @@ class Distribution(_Distribution): ...@@ -570,7 +571,7 @@ class Distribution(_Distribution):
from setuptools.extern.six.moves.configparser import ConfigParser from setuptools.extern.six.moves.configparser import ConfigParser
# Ignore install directory options if we have a venv # Ignore install directory options if we have a venv
if six.PY3 and sys.prefix != sys.base_prefix: if not six.PY2 and sys.prefix != sys.base_prefix:
ignore_options = [ ignore_options = [
'install-base', 'install-platbase', 'install-lib', 'install-base', 'install-platbase', 'install-lib',
'install-platlib', 'install-purelib', 'install-headers', 'install-platlib', 'install-purelib', 'install-headers',
...@@ -592,7 +593,7 @@ class Distribution(_Distribution): ...@@ -592,7 +593,7 @@ class Distribution(_Distribution):
with io.open(filename, encoding='utf-8') as reader: with io.open(filename, encoding='utf-8') as reader:
if DEBUG: if DEBUG:
self.announce(" reading {filename}".format(**locals())) self.announce(" reading {filename}".format(**locals()))
(parser.read_file if six.PY3 else parser.readfp)(reader) (parser.readfp if six.PY2 else parser.read_file)(reader)
for section in parser.sections(): for section in parser.sections():
options = parser.options(section) options = parser.options(section)
opt_dict = self.get_option_dict(section) opt_dict = self.get_option_dict(section)
...@@ -635,7 +636,7 @@ class Distribution(_Distribution): ...@@ -635,7 +636,7 @@ class Distribution(_Distribution):
Ref #1653 Ref #1653
""" """
if six.PY3: if not six.PY2:
return val return val
try: try:
return val.encode() return val.encode()
...@@ -724,15 +725,28 @@ class Distribution(_Distribution): ...@@ -724,15 +725,28 @@ class Distribution(_Distribution):
return resolved_dists return resolved_dists
def finalize_options(self): def finalize_options(self):
_Distribution.finalize_options(self) """
if self.features: Allow plugins to apply arbitrary operations to the
self._set_global_opts_from_features() distribution. Each hook may optionally define a 'order'
to influence the order of execution. Smaller numbers
go first and the default is 0.
"""
hook_key = 'setuptools.finalize_distribution_options'
def by_order(hook):
return getattr(hook, 'order', 0)
eps = pkg_resources.iter_entry_points(hook_key)
for ep in sorted(eps, key=by_order):
ep.load()(self)
def _finalize_setup_keywords(self):
for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'): for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'):
value = getattr(self, ep.name, None) value = getattr(self, ep.name, None)
if value is not None: if value is not None:
ep.require(installer=self.fetch_build_egg) ep.require(installer=self.fetch_build_egg)
ep.load()(self, ep.name, value) ep.load()(self, ep.name, value)
def _finalize_2to3_doctests(self):
if getattr(self, 'convert_2to3_doctests', None): if getattr(self, 'convert_2to3_doctests', None):
# XXX may convert to set here when we can rely on set being builtin # XXX may convert to set here when we can rely on set being builtin
self.convert_2to3_doctests = [ self.convert_2to3_doctests = [
...@@ -759,36 +773,15 @@ class Distribution(_Distribution): ...@@ -759,36 +773,15 @@ class Distribution(_Distribution):
def fetch_build_egg(self, req): def fetch_build_egg(self, req):
"""Fetch an egg needed for building""" """Fetch an egg needed for building"""
from setuptools.command.easy_install import easy_install from setuptools.installer import fetch_build_egg
dist = self.__class__({'script_args': ['easy_install']}) return fetch_build_egg(self, req)
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)
def _set_global_opts_from_features(self): def _finalize_feature_opts(self):
"""Add --with-X/--without-X options based on optional features""" """Add --with-X/--without-X options based on optional features"""
if not self.features:
return
go = [] go = []
no = self.negative_opt.copy() no = self.negative_opt.copy()
......
"""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``.
"""
# This file originally from pip:
# https://github.com/pypa/pip/blob/8f4f15a5a95d7d5b511ceaee9ed261176c181970/src/pip/_internal/utils/glibc.py
from __future__ import absolute_import
import ctypes
import re
import warnings
def glibc_version_string():
"Returns glibc version string, or None if not using glibc."
# ctypes.CDLL(None) internally calls dlopen(NULL), and as the dlopen
# manpage says, "If filename is NULL, then the returned handle is for the
# main program". This way we can let the linker do the work to figure out
# which libc our process is actually using.
process_namespace = ctypes.CDLL(None)
try:
gnu_get_libc_version = process_namespace.gnu_get_libc_version
except AttributeError:
# Symbol doesn't exist -> therefore, we are not linked to
# glibc.
return None
# Call gnu_get_libc_version, which returns a string like "2.5"
gnu_get_libc_version.restype = ctypes.c_char_p
version_str = gnu_get_libc_version()
# py2 / py3 compatibility:
if not isinstance(version_str, str):
version_str = version_str.decode("ascii")
return version_str
# Separated out from have_compatible_glibc for easier unit testing
def check_glibc_version(version_str, required_major, minimum_minor):
# Parse string and check against requested version.
#
# We use a regexp instead of str.split because we want to discard any
# random junk that might come after the minor version -- this might happen
# in patched/forked versions of glibc (e.g. Linaro's version of glibc
# uses version strings like "2.20-2014.11"). See gh-3588.
m = re.match(r"(?P<major>[0-9]+)\.(?P<minor>[0-9]+)", version_str)
if not m:
warnings.warn("Expected glibc version with 2 components major.minor,"
" got: %s" % version_str, RuntimeWarning)
return False
return (int(m.group("major")) == required_major and
int(m.group("minor")) >= minimum_minor)
def have_compatible_glibc(required_major, minimum_minor):
version_str = glibc_version_string()
if version_str is None:
return False
return check_glibc_version(version_str, required_major, minimum_minor)
# platform.libc_ver regularly returns completely nonsensical glibc
# versions. E.g. on my computer, platform says:
#
# ~$ python2.7 -c 'import platform; print(platform.libc_ver())'
# ('glibc', '2.7')
# ~$ python3.5 -c 'import platform; print(platform.libc_ver())'
# ('glibc', '2.9')
#
# But the truth is:
#
# ~$ ldd --version
# ldd (Debian GLIBC 2.22-11) 2.22
#
# This is unfortunate, because it means that the linehaul data on libc
# versions that was generated by pip 8.1.2 and earlier is useless and
# misleading. Solution: instead of using platform, use our code that actually
# works.
def libc_ver():
"""Try to determine the glibc version
Returns a tuple of strings (lib, version) which default to empty strings
in case the lookup fails.
"""
glibc_version = glibc_version_string()
if glibc_version is None:
return ("", "")
else:
return ("glibc", glibc_version)
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.extern import six
from setuptools.wheel import Wheel
from .py31compat import TemporaryDirectory
def _fixup_find_links(find_links):
"""Ensure find-links option end-up being a list of strings."""
if isinstance(find_links, six.string_types):
return find_links.split()
assert isinstance(find_links, (tuple, list))
return find_links
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 = _fixup_find_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)
# Ignore environment markers; if supplied, it is required.
req = strip_marker(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 = _fixup_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
def strip_marker(req):
"""
Return a new requirement without the environment marker to avoid
calling pip with something like `babel; extra == "i18n"`, which
would always be ignored.
"""
# create a copy to avoid mutating the input
req = pkg_resources.Requirement.parse(str(req))
req.marker = None
return req
This diff is collapsed.
...@@ -47,13 +47,17 @@ class Installer: ...@@ -47,13 +47,17 @@ class Installer:
"p = os.path.join(%(root)s, *%(pth)r)", "p = os.path.join(%(root)s, *%(pth)r)",
"importlib = has_mfs and __import__('importlib.util')", "importlib = has_mfs and __import__('importlib.util')",
"has_mfs and __import__('importlib.machinery')", "has_mfs and __import__('importlib.machinery')",
"m = has_mfs and " (
"m = has_mfs and "
"sys.modules.setdefault(%(pkg)r, " "sys.modules.setdefault(%(pkg)r, "
"importlib.util.module_from_spec(" "importlib.util.module_from_spec("
"importlib.machinery.PathFinder.find_spec(%(pkg)r, " "importlib.machinery.PathFinder.find_spec(%(pkg)r, "
"[os.path.dirname(p)])))", "[os.path.dirname(p)])))"
"m = m or " ),
"sys.modules.setdefault(%(pkg)r, types.ModuleType(%(pkg)r))", (
"m = m or "
"sys.modules.setdefault(%(pkg)r, types.ModuleType(%(pkg)r))"
),
"mp = (m or []) and m.__dict__.setdefault('__path__',[])", "mp = (m or []) and m.__dict__.setdefault('__path__',[])",
"(p not in mp) and mp.append(p)", "(p not in mp) and mp.append(p)",
) )
......
...@@ -46,7 +46,8 @@ __all__ = [ ...@@ -46,7 +46,8 @@ __all__ = [
_SOCKET_TIMEOUT = 15 _SOCKET_TIMEOUT = 15
_tmpl = "setuptools/{setuptools.__version__} Python-urllib/{py_major}" _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): def parse_requirement_arg(spec):
...@@ -1092,7 +1093,8 @@ def open_with_auth(url, opener=urllib.request.urlopen): ...@@ -1092,7 +1093,8 @@ def open_with_auth(url, opener=urllib.request.urlopen):
# copy of urllib.parse._splituser from Python 3.8 # copy of urllib.parse._splituser from Python 3.8
def _splituser(host): def _splituser(host):
"""splituser('user[:passwd]@host[:port]') --> 'user[:passwd]', 'host[:port]'.""" """splituser('user[:passwd]@host[:port]')
--> 'user[:passwd]', 'host[:port]'."""
user, delim, host = host.rpartition('@') user, delim, host = host.rpartition('@')
return (user if delim else None), host return (user if delim else None), host
......
This diff is collapsed.
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
Compatibility Support for Python 2.7 and earlier Compatibility Support for Python 2.7 and earlier
""" """
import sys
import platform import platform
from setuptools.extern import six from setuptools.extern import six
...@@ -15,7 +16,7 @@ def get_all_headers(message, key): ...@@ -15,7 +16,7 @@ def get_all_headers(message, key):
if six.PY2: if six.PY2:
def get_all_headers(message, key): def get_all_headers(message, key): # noqa
return message.getheaders(key) return message.getheaders(key)
...@@ -26,3 +27,34 @@ linux_py2_ascii = ( ...@@ -26,3 +27,34 @@ linux_py2_ascii = (
rmtree_safe = str if linux_py2_ascii else lambda x: x rmtree_safe = str if linux_py2_ascii else lambda x: x
"""Workaround for http://bugs.python.org/issue24672""" """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]
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)
...@@ -13,6 +13,8 @@ from setuptools.extern import six ...@@ -13,6 +13,8 @@ from setuptools.extern import six
from setuptools.extern.six.moves import builtins, map from setuptools.extern.six.moves import builtins, map
import pkg_resources.py31compat import pkg_resources.py31compat
from distutils.errors import DistutilsError
from pkg_resources import working_set
if sys.platform.startswith('java'): if sys.platform.startswith('java'):
import org.python.modules.posix.PosixModule as _os import org.python.modules.posix.PosixModule as _os
...@@ -23,8 +25,6 @@ try: ...@@ -23,8 +25,6 @@ try:
except NameError: except NameError:
_file = None _file = None
_open = open _open = open
from distutils.errors import DistutilsError
from pkg_resources import working_set
__all__ = [ __all__ = [
...@@ -374,7 +374,7 @@ class AbstractSandbox: ...@@ -374,7 +374,7 @@ class AbstractSandbox:
if hasattr(os, 'devnull'): if hasattr(os, 'devnull'):
_EXCEPTIONS = [os.devnull,] _EXCEPTIONS = [os.devnull]
else: else:
_EXCEPTIONS = [] _EXCEPTIONS = []
...@@ -466,7 +466,8 @@ class DirectorySandbox(AbstractSandbox): ...@@ -466,7 +466,8 @@ class DirectorySandbox(AbstractSandbox):
WRITE_FLAGS = functools.reduce( WRITE_FLAGS = functools.reduce(
operator.or_, [getattr(_os, a, 0) for a in operator.or_, [
getattr(_os, a, 0) for a in
"O_WRONLY O_RDWR O_APPEND O_CREAT O_TRUNC O_TEMPORARY".split()] "O_WRONLY O_RDWR O_APPEND O_CREAT O_TRUNC O_TEMPORARY".split()]
) )
......
This diff is collapsed.
This diff is collapsed.
...@@ -6,7 +6,7 @@ from setuptools.extern.six import PY2, PY3 ...@@ -6,7 +6,7 @@ from setuptools.extern.six import PY2, PY3
__all__ = [ __all__ = [
'fail_on_ascii', 'py2_only', 'py3_only' 'fail_on_ascii', 'py2_only', 'py3_only'
] ]
......
mock mock
pytest-flake8; python_version!="3.4" pytest-flake8
pytest-flake8<=1.0.0; python_version=="3.4" flake8-2020; python_version>="3.6"
virtualenv>=13.0.0 virtualenv>=13.0.0
pytest-virtualenv>=1.2.7 pytest-virtualenv>=1.2.7
pytest>=3.7 pytest>=3.7
...@@ -9,4 +9,4 @@ coverage>=4.5.1 ...@@ -9,4 +9,4 @@ coverage>=4.5.1
pytest-cov>=2.5.1 pytest-cov>=2.5.1
paver; python_version>="3.6" paver; python_version>="3.6"
futures; python_version=="2.7" futures; python_version=="2.7"
pip==18.1 # Temporary workaround for #1644. pip>=19.1 # For proper file:// URLs support.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment