Commit 3463820b authored by Jason R. Coombs's avatar Jason R. Coombs

Merge branch 'master' into patch-1

parents bbf825ee c9fb0772
[bumpversion]
current_version = 41.6.0
current_version = 45.1.0
commit = 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: 👷
runs-on: ${{ matrix.os }}
strategy:
# max-parallel: 5
matrix:
python-version:
- 3.8
- 3.7
- 3.6
- 3.5
os:
- ubuntu-18.04
- ubuntu-16.04
- macOS-latest
# - windows-2019
# - windows-2016
env:
- TOXENV: python
steps:
- uses: actions/checkout@master
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v1
with:
version: ${{ matrix.python-version }}
- 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: Log env vars
run: >-
env
env: ${{ matrix.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
env: ${{ matrix.env }}
- name: Test with tox
run: |
${{ startsWith(matrix.os, 'windows-') && 'setx NETWORK_REQUIRED ' || 'export NETWORK_REQUIRED=' }}1
python -m tox \
--parallel 0 \
-- \
--cov
env: ${{ matrix.env }}
......@@ -6,16 +6,13 @@ jobs:
include:
- &latest_py2
python: 2.7
env: TOXENV=py27
- <<: *latest_py2
env: LANG=C
- python: pypy
env: DISABLE_COVERAGE=1 # Don't run coverage on pypy (too slow).
env: LANG=C TOXENV=py27
- python: pypy3
env: DISABLE_COVERAGE=1
- python: 3.4
env: DISABLE_COVERAGE=1 # Don't run coverage on pypy (too slow).
- python: 3.5
- &default_py
python: 3.6
- python: 3.6
- python: 3.7
- &latest_py3
python: 3.8
......@@ -24,26 +21,11 @@ jobs:
- python: 3.8-dev
- <<: *latest_py3
env: TOXENV=docs DISABLE_COVERAGE=1
- <<: *default_py
stage: deploy (to PyPI for tagged commits)
- <<: *latest_py3
stage: deploy
if: tag IS present
install: skip
script: skip
after_success: true
before_deploy:
- python bootstrap.py
- "! 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
script: tox -e release
after_success: skip
cache: pip
......@@ -58,8 +40,6 @@ install:
- pip freeze --all
- env
# update egg_info based on setup.py in checkout
- python bootstrap.py
- "! grep pyc setuptools.egg-info/SOURCES.txt"
script:
......
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
-------
......
......@@ -34,8 +34,17 @@ To report a security vulnerability, please use the
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
---------------
===============
Everyone interacting in the setuptools project's codebases, issue trackers,
chat rooms, and mailing lists is expected to follow the
......
......@@ -9,8 +9,8 @@ environment:
matrix:
- APPVEYOR_JOB_NAME: "python36-x64"
PYTHON: "C:\\Python36-x64"
- APPVEYOR_JOB_NAME: "python27-x64"
PYTHON: "C:\\Python27-x64"
- APPVEYOR_JOB_NAME: "python37-x64"
PYTHON: "C:\\Python37-x64"
install:
# symlink python from a directory with a space
......@@ -28,7 +28,6 @@ test_script:
- python -m pip install --disable-pip-version-check --upgrade pip setuptools wheel
- pip install --upgrade tox tox-venv virtualenv
- pip freeze --all
- python bootstrap.py
- tox -- --cov
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("""
entry_points = setuptools.dist:check_entry_points
[egg_info.writers]
PKG-INFO = setuptools.command.egg_info:write_pkg_info
dependency_links.txt = setuptools.command.egg_info:overwrite_arg
entry_points.txt = setuptools.command.egg_info:write_entries
requires.txt = setuptools.command.egg_info:write_requirements
......@@ -35,10 +36,11 @@ def ensure_egg_info():
if os.path.exists('setuptools.egg-info'):
return
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
"""
......@@ -52,13 +54,6 @@ def run_egg_info():
cmd = [sys.executable, 'setup.py', 'egg_info']
print("Regenerating egg_info")
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()
Fix empty namespace package installation from wheel.
Removed the "upload" and "register" commands in favor of `twine <https://pypi.org/p/twine>`_.
Add flake8-2020 to check for misuse of sys.version or sys.version_info.
<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>
<p>Current version: <b>{{ version }}</b></p>
......@@ -6,10 +13,3 @@
<h3>Questions? Suggestions? Contributions?</h3>
<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 sys
import os
......@@ -26,14 +6,12 @@ import os
# hack to run the bootstrap script so that jaraco.packaging.sphinx
# can invoke setup.py
'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),
)
# -- 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']
# Add any paths that contain templates here, relative to this directory.
......@@ -45,7 +23,8 @@ source_suffix = '.txt'
# The master toctree document.
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']
# List of directories, relative to source directory, that shouldn't be searched
......@@ -55,7 +34,7 @@ exclude_trees = []
# The name of the Pygments (syntax highlighting) style to use.
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
# Sphinx are currently 'default' and 'sphinxdoc'.
......@@ -69,7 +48,10 @@ html_theme_path = ['_theme']
html_use_smartypants = True
# 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.
html_use_modindex = False
......@@ -77,14 +59,15 @@ html_use_modindex = False
# If false, no index is generated.
html_use_index = False
# -- Options for LaTeX output --------------------------------------------------
# -- Options for LaTeX output --
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'Setuptools.tex', 'Setuptools Documentation',
'The fellowship of the packaging', 'manual'),
]
# (source start file, target name, title, author,
# documentclass [howto/manual]).
latex_documents = [(
'index', 'Setuptools.tex', 'Setuptools Documentation',
'The fellowship of the packaging', 'manual',
)]
link_files = {
'../CHANGES.rst': dict(
......
......@@ -104,12 +104,8 @@ from the command line after pushing a new branch.
Testing
-------
The primary tests are run using tox. To run the tests, first create the metadata
needed to run the tests::
$ python bootstrap.py
Then make sure you have tox installed, and invoke it::
The primary tests are run using tox. Make sure you have tox installed,
and invoke it::
$ tox
......
......@@ -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
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.
Note that the instructions on the setuptools PyPI page assume that you are
......
......@@ -62,7 +62,7 @@ Installing ``setuptools``
To install the latest version of setuptools, use::
pip install -U setuptools
pip install --upgrade setuptools
Refer to `Installing Packages`_ guide for more information.
......@@ -88,7 +88,7 @@ packages in the directory where the setup.py lives. See the `Command
Reference`_ section below to see what commands you can give to this setup
script. For example, to produce a source distribution, simply invoke::
python setup.py sdist
setup.py sdist
Of course, before you release your project to PyPI, you'll want to add a bit
more information to your setup script to help people find or learn about your
......@@ -100,17 +100,17 @@ dependencies, and perhaps some data files and scripts::
name="HelloWorld",
version="0.1",
packages=find_packages(),
scripts=['say_hello.py'],
scripts=["say_hello.py"],
# Project uses reStructuredText, so ensure that the docutils get
# installed or upgraded on the target machine
install_requires=['docutils>=0.3'],
install_requires=["docutils>=0.3"],
package_data={
# If any package contains *.txt or *.rst files, include them:
'': ['*.txt', '*.rst'],
# And include any *.msg files found in the 'hello' package, too:
'hello': ['*.msg'],
"": ["*.txt", "*.rst"],
# And include any *.msg files found in the "hello" package, too:
"hello": ["*.msg"],
},
# metadata to display on PyPI
......@@ -125,7 +125,7 @@ dependencies, and perhaps some data files and scripts::
"Source Code": "https://code.example.com/HelloWorld/",
},
classifiers=[
'License :: OSI Approved :: Python Software Foundation License'
"License :: OSI Approved :: Python Software Foundation License"
]
# could also include long_description, download_url, etc.
......@@ -207,11 +207,11 @@ but here are a few tips that will keep you out of trouble in the corner cases:
to compare different version numbers::
>>> from pkg_resources import parse_version
>>> parse_version('1.9.a.dev') == parse_version('1.9a0dev')
>>> parse_version("1.9.a.dev") == parse_version("1.9a0dev")
True
>>> parse_version('2.1-rc2') < parse_version('2.1')
>>> parse_version("2.1-rc2") < parse_version("2.1")
True
>>> parse_version('0.6a9dev-r41475') < parse_version('0.6a9')
>>> parse_version("0.6a9dev-r41475") < parse_version("0.6a9")
True
Once you've decided on a version numbering scheme for your project, you can
......@@ -282,10 +282,11 @@ unless you need the associated ``setuptools`` feature.
``setup_requires``
A string or list of strings specifying what other distributions need to
be present in order for the *setup script* to run. ``setuptools`` will
attempt to obtain these before processing the rest of the setup script or
commands. This argument is needed if you are using distutils extensions as
part of your build process; for example, extensions that process setup()
arguments and turn them into EGG-INFO metadata files.
attempt to obtain these (using pip if available) before processing the
rest of the setup script or commands. This argument is needed if you
are using distutils extensions as part of your build process; for
example, extensions that process setup() arguments and turn them into
EGG-INFO metadata files.
(Note: projects listed in ``setup_requires`` will NOT be automatically
installed on the system where the setup script is being run. They are
......@@ -332,10 +333,10 @@ unless you need the associated ``setuptools`` feature.
needed to install it, you can use this option to specify them. It should
be a string or list of strings specifying what other distributions need to
be present for the package's tests to run. When you run the ``test``
command, ``setuptools`` will attempt to obtain these. Note that these
required projects will *not* be installed on the system where the tests
are run, but only downloaded to the project's setup directory if they're
not already installed locally.
command, ``setuptools`` will attempt to obtain these (using pip if
available). Note that these required projects will *not* be installed on
the system where the tests are run, but only downloaded to the project's setup
directory if they're not already installed locally.
New in 41.5.0: Deprecated the test command.
......@@ -370,7 +371,7 @@ unless you need the associated ``setuptools`` feature.
imported. This argument is only useful if the project will be installed as
a zipfile, and there is a need to have all of the listed resources be
extracted to the filesystem *as a unit*. Resources listed here
should be '/'-separated paths, relative to the source root, so to list a
should be "/"-separated paths, relative to the source root, so to list a
resource ``foo.png`` in package ``bar.baz``, you would include the string
``bar/baz/foo.png`` in this argument.
......@@ -412,7 +413,7 @@ the same
directory as the setup script. Some projects use a ``src`` or ``lib``
directory as the root of their source tree, and those projects would of course
use ``"src"`` or ``"lib"`` as the first argument to ``find_packages()``. (And
such projects also need something like ``package_dir={'':'src'}`` in their
such projects also need something like ``package_dir={"": "src"}`` in their
``setup()`` arguments, but that's just a normal distutils thing.)
Anyway, ``find_packages()`` walks the target directory, filtering by inclusion
......@@ -479,7 +480,7 @@ top-level package called ``tests``! One way to avoid this problem is to use the
setup(
name="namespace.mypackage",
version="0.1",
packages=find_namespace_packages(include=['namespace.*'])
packages=find_namespace_packages(include=["namespace.*"])
)
Another option is to use the "src" layout, where all package code is placed in
......@@ -499,8 +500,8 @@ With this layout, the package directory is specified as ``src``, as such::
setup(name="namespace.mypackage",
version="0.1",
package_dir={'': 'src'},
packages=find_namespace_packages(where='src'))
package_dir={"": "src"},
packages=find_namespace_packages(where="src"))
.. _PEP 420: https://www.python.org/dev/peps/pep-0420/
......@@ -525,12 +526,12 @@ script called ``baz``, you might do something like this::
setup(
# other arguments here...
entry_points={
'console_scripts': [
'foo = my_package.some_module:main_func',
'bar = other_module:some_func',
"console_scripts": [
"foo = my_package.some_module:main_func",
"bar = other_module:some_func",
],
'gui_scripts': [
'baz = my_package_gui:start_func',
"gui_scripts": [
"baz = my_package_gui:start_func",
]
}
)
......@@ -566,8 +567,8 @@ as the following::
setup(
# other arguments here...
entry_points={
'setuptools.installation': [
'eggsecutable = my_package.some_module:main_func',
"setuptools.installation": [
"eggsecutable = my_package.some_module:main_func",
]
}
)
......@@ -740,8 +741,8 @@ For example, let's say that Project A offers optional PDF and reST support::
name="Project-A",
...
extras_require={
'PDF': ["ReportLab>=1.2", "RXP"],
'reST': ["docutils>=0.3"],
"PDF": ["ReportLab>=1.2", "RXP"],
"reST": ["docutils>=0.3"],
}
)
......@@ -762,9 +763,9 @@ declare it like this, so that the "PDF" requirements are only resolved if the
name="Project-A",
...
entry_points={
'console_scripts': [
'rst2pdf = project_a.tools.pdfgen [PDF]',
'rst2html = project_a.tools.htmlgen',
"console_scripts": [
"rst2pdf = project_a.tools.pdfgen [PDF]",
"rst2html = project_a.tools.htmlgen",
# more script entry points ...
],
}
......@@ -800,8 +801,8 @@ setup to this::
name="Project-A",
...
extras_require={
'PDF': [],
'reST': ["docutils>=0.3"],
"PDF": [],
"reST": ["docutils>=0.3"],
}
)
......@@ -828,8 +829,8 @@ For example, here is a project that uses the ``enum`` module and ``pywin32``::
name="Project",
...
install_requires=[
'enum34;python_version<"3.4"',
'pywin32 >= 1.0;platform_system=="Windows"'
"enum34;python_version<'3.4'",
"pywin32 >= 1.0;platform_system=='Windows'"
]
)
......@@ -877,9 +878,9 @@ e.g.::
...
package_data={
# If any package contains *.txt or *.rst files, include them:
'': ['*.txt', '*.rst'],
# And include any *.msg files found in the 'hello' package, too:
'hello': ['*.msg'],
"": ["*.txt", "*.rst"],
# And include any *.msg files found in the "hello" package, too:
"hello": ["*.msg"],
}
)
......@@ -902,15 +903,15 @@ The setuptools setup file might look like this::
from setuptools import setup, find_packages
setup(
...
packages=find_packages('src'), # include all packages under src
package_dir={'':'src'}, # tell distutils packages are under src
packages=find_packages("src"), # include all packages under src
package_dir={"": "src"}, # tell distutils packages are under src
package_data={
# If any package contains *.txt files, include them:
'': ['*.txt'],
# And include any *.dat files found in the 'data' subdirectory
# of the 'mypkg' package, also:
'mypkg': ['data/*.dat'],
"": ["*.txt"],
# And include any *.dat files found in the "data" subdirectory
# of the "mypkg" package, also:
"mypkg": ["data/*.dat"],
}
)
......@@ -925,7 +926,7 @@ converts slashes to appropriate platform-specific separators at build time.
If datafiles are contained in a subdirectory of a package that isn't a package
itself (no ``__init__.py``), then the subdirectory names (or ``*``) are required
in the ``package_data`` argument (as shown above with ``'data/*.dat'``).
in the ``package_data`` argument (as shown above with ``"data/*.dat"``).
When building an ``sdist``, the datafiles are also drawn from the
``package_name.egg-info/SOURCES.txt`` file, so make sure that this is removed if
......@@ -950,18 +951,18 @@ to do things like this::
from setuptools import setup, find_packages
setup(
...
packages=find_packages('src'), # include all packages under src
package_dir={'':'src'}, # tell distutils packages are under src
packages=find_packages("src"), # include all packages under src
package_dir={"": "src"}, # tell distutils packages are under src
include_package_data=True, # include everything in source control
# ...but exclude README.txt from all packages
exclude_package_data={'': ['README.txt']},
exclude_package_data={"": ["README.txt"]},
)
The ``exclude_package_data`` option is a dictionary mapping package names to
lists of wildcard patterns, just like the ``package_data`` option. And, just
as with that option, a key of ``''`` will apply the given pattern(s) to all
as with that option, a key of ``""`` will apply the given pattern(s) to all
packages. However, any files that match these patterns will be *excluded*
from installation, even if they were listed in ``package_data`` or were
included as a result of using ``include_package_data``.
......@@ -999,11 +1000,11 @@ and Python Eggs. It is strongly recommended that, if you are using data files,
you should use the :ref:`ResourceManager API` of ``pkg_resources`` to access
them. The ``pkg_resources`` module is distributed as part of setuptools, so if
you're using setuptools to distribute your package, there is no reason not to
use its resource management API. See also `Accessing Package Resources`_ for
use its resource management API. See also `Importlib Resources`_ for
a quick example of converting code that uses ``__file__`` to use
``pkg_resources`` instead.
.. _Accessing Package Resources: http://peak.telecommunity.com/DevCenter/PythonEggs#accessing-package-resources
.. _Importlib Resources: https://docs.python.org/3/library/importlib.html#module-importlib.resources
Non-Package Data Files
......@@ -1095,12 +1096,12 @@ for our hypothetical blogging tool::
setup(
# ...
entry_points={'blogtool.parsers': '.rst = some_module:SomeClass'}
entry_points={"blogtool.parsers": ".rst = some_module:SomeClass"}
)
setup(
# ...
entry_points={'blogtool.parsers': ['.rst = some_module:a_func']}
entry_points={"blogtool.parsers": [".rst = some_module:a_func"]}
)
setup(
......@@ -1198,7 +1199,7 @@ command; see the section on the `develop`_ command below for more details.
Note that you can also apply setuptools commands to non-setuptools projects,
using commands like this::
python -c "import setuptools; execfile('setup.py')" develop
python -c "import setuptools; with open('setup.py') as f: exec(compile(f.read(), 'setup.py', 'exec'))" develop
That is, you can simply list the normal setup commands and options following
the quoted part.
......@@ -1207,28 +1208,28 @@ the quoted part.
Distributing a ``setuptools``-based project
===========================================
Detailed instructions to distribute a setuptools project can be found at
Detailed instructions to distribute a setuptools project can be found at
`Packaging project tutorials`_.
.. _Packaging project tutorials: https://packaging.python.org/tutorials/packaging-projects/#generating-distribution-archives
Before you begin, make sure you have the latest versions of setuptools and wheel::
python3 -m pip install --user --upgrade setuptools wheel
pip install --upgrade setuptools wheel
To build a setuptools project, run this command from the same directory where
setup.py is located::
python3 setup.py sdist bdist_wheel
setup.py sdist bdist_wheel
This will generate distribution archives in the `dist` directory.
Before you upload the generated archives make sure you're registered on
Before you upload the generated archives make sure you're registered on
https://test.pypi.org/account/register/. You will also need to verify your email
to be able to upload any packages.
You should install twine to be able to upload packages::
python3 -m pip install --user --upgrade setuptools wheel
pip install --upgrade twine
Now, to upload these archives, run::
......@@ -1236,7 +1237,7 @@ Now, to upload these archives, run::
To install your newly uploaded package ``example_pkg``, you can use pip::
python3 -m pip install --index-url https://test.pypi.org/simple/ example_pkg
pip install --index-url https://test.pypi.org/simple/ example_pkg
If you have issues at any point, please refer to `Packaging project tutorials`_
for clarification.
......@@ -1308,7 +1309,7 @@ participates in. For example, the ZopeInterface project might do this::
setup(
# ...
namespace_packages=['zope']
namespace_packages=["zope"]
)
because it contains a ``zope.interface`` package that lives in the ``zope``
......@@ -1326,7 +1327,7 @@ packages' ``__init__.py`` files (and the ``__init__.py`` of any parent
packages), in a normal Python package layout. These ``__init__.py`` files
*must* contain the line::
__import__('pkg_resources').declare_namespace(__name__)
__import__("pkg_resources").declare_namespace(__name__)
This code ensures that the namespace package machinery is operating and that
the current package is registered as a namespace package.
......@@ -1409,7 +1410,7 @@ pattern. So, you can use a command line like::
setup.py egg_info -rbDEV bdist_egg rotate -m.egg -k3
to build an egg whose version info includes 'DEV-rNNNN' (where NNNN is the
to build an egg whose version info includes "DEV-rNNNN" (where NNNN is the
most recent Subversion revision that affected the source tree), and then
delete any egg files from the distribution directory except for the three
that were built most recently.
......@@ -1468,7 +1469,7 @@ tagging the release, so the trunk will still produce development snapshots.
Alternately, if you are not branching for releases, you can override the
default version options on the command line, using something like::
python setup.py egg_info -Db "" sdist bdist_egg
setup.py egg_info -Db "" sdist bdist_egg
The first part of this command (``egg_info -Db ""``) will override the
configured tag information, before creating source and binary eggs. Thus, these
......@@ -1478,11 +1479,11 @@ build designation string.
Of course, if you will be doing this a lot, you may wish to create a personal
alias for this operation, e.g.::
python setup.py alias -u release egg_info -Db ""
setup.py alias -u release egg_info -Db ""
You can then use it like this::
python setup.py release sdist bdist_egg
setup.py release sdist bdist_egg
Or of course you can create more elaborate aliases that do all of the above.
See the sections below on the `egg_info`_ and `alias`_ commands for more ideas.
......@@ -1499,7 +1500,7 @@ To ensure Cython is available, include Cython in the build-requires section
of your pyproject.toml::
[build-system]
requires=[..., 'cython']
requires=[..., "cython"]
Built with pip 10 or later, that declaration is sufficient to include Cython
in the build. For broader compatibility, declare the dependency in your
......@@ -1799,7 +1800,7 @@ to support "daily builds" or "snapshot" releases. It is run automatically by
the ``sdist``, ``bdist_egg``, ``develop``, and ``test`` commands in order to
update the project's metadata, but you can also specify it explicitly in order
to temporarily change the project's version string while executing other
commands. (It also generates the``.egg-info/SOURCES.txt`` manifest file, which
commands. (It also generates the ``.egg-info/SOURCES.txt`` manifest file, which
is used when you are building source distributions.)
In addition to writing the core egg metadata defined by ``setuptools`` and
......@@ -1847,7 +1848,7 @@ binary distributions of your project, you should first make sure that you know
how the resulting version numbers will be interpreted by automated tools
like pip. See the section above on `Specifying Your Project's Version`_ for an
explanation of pre- and post-release tags, as well as tips on how to choose and
verify a versioning scheme for your your project.)
verify a versioning scheme for your project.)
For advanced uses, there is one other option that can be set, to change the
location of the project's ``.egg-info`` directory. Commands that need to find
......@@ -1872,12 +1873,12 @@ Other ``egg_info`` Options
Creating a dated "nightly build" snapshot egg::
python setup.py egg_info --tag-date --tag-build=DEV bdist_egg
setup.py egg_info --tag-date --tag-build=DEV bdist_egg
Creating a release with no version tags, even if some default tags are
specified in ``setup.cfg``::
python setup.py egg_info -RDb "" sdist bdist_egg
setup.py egg_info -RDb "" sdist bdist_egg
(Notice that ``egg_info`` must always appear on the command line *before* any
commands that you want the version changes to apply to.)
......@@ -2273,6 +2274,7 @@ maintainer_email maintainer-email str
classifiers classifier file:, list-comma
license str
license_file str
license_files list-comma
description summary file:, str
long_description long-description file:, str
long_description_content_type str 38.6.0
......@@ -2349,7 +2351,7 @@ parsing ``metadata`` and ``options`` sections into a dictionary.
from setuptools.config import read_configuration
conf_dict = read_configuration('/home/user/dev/package/setup.cfg')
conf_dict = read_configuration("/home/user/dev/package/setup.cfg")
By default, ``read_configuration()`` will read only the file provided
......@@ -2417,6 +2419,10 @@ script defines entry points for them!
Adding ``setup()`` Arguments
----------------------------
.. warning:: Adding arguments to setup is discouraged as such arguments
are only supported through imperative execution and not supported through
declarative config.
Sometimes, your commands may need additional arguments to the ``setup()``
call. You can enable this by defining entry points in the
``distutils.setup_keywords`` group. For example, if you wanted a ``setup()``
......@@ -2468,6 +2474,25 @@ script using your extension lists your project in its ``setup_requires``
argument.
Customizing Distribution Options
--------------------------------
Plugins may wish to extend or alter the options on a Distribution object to
suit the purposes of that project. For example, a tool that infers the
``Distribution.version`` from SCM-metadata may need to hook into the
option finalization. To enable this feature, Setuptools offers an entry
point "setuptools.finalize_distribution_options". That entry point must
be a callable taking one argument (the Distribution instance).
If the callable has an ``.order`` property, that value will be used to
determine the order in which the hook is called. Lower numbers are called
first and the default is zero (0).
Plugins may read, alter, and set properties on the distribution, but each
plugin is encouraged to load the configuration/settings for their behavior
independently.
Adding new EGG-INFO Files
-------------------------
......@@ -2506,7 +2531,7 @@ a file. Here's what the writer utility looks like::
argname = os.path.splitext(basename)[0]
value = getattr(cmd.distribution, argname, None)
if value is not None:
value = '\n'.join(value) + '\n'
value = "\n".join(value) + "\n"
cmd.write_or_delete_file(argname, filename, value)
As you can see, ``egg_info.writers`` entry points must be a function taking
......
......@@ -83,13 +83,14 @@ __import__('pkg_resources.extern.packaging.version')
__import__('pkg_resources.extern.packaging.specifiers')
__import__('pkg_resources.extern.packaging.requirements')
__import__('pkg_resources.extern.packaging.markers')
__import__('pkg_resources.py2_warn')
__metaclass__ = type
if (3, 0) < sys.version_info < (3, 4):
raise RuntimeError("Python 3.4 or later is required")
if (3, 0) < sys.version_info < (3, 5):
raise RuntimeError("Python 3.5 or later is required")
if six.PY2:
# Those builtin exceptions are only defined in Python 3
......@@ -2328,7 +2329,8 @@ register_namespace_handler(object, null_ns_handler)
def normalize_path(filename):
"""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
......@@ -3287,6 +3289,7 @@ def _initialize_master_working_set():
list(map(working_set.add_entry, sys.path))
globals().update(locals())
class PkgResourcesDeprecationWarning(Warning):
"""
Base class for warning about deprecations in ``pkg_resources``
......
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)
......@@ -17,7 +17,9 @@ try:
except ImportError:
import mock
from pkg_resources import DistInfoDistribution, Distribution, EggInfoDistribution
from pkg_resources import (
DistInfoDistribution, Distribution, EggInfoDistribution,
)
from setuptools.extern import six
from pkg_resources.extern.six.moves import map
from pkg_resources.extern.six import text_type, string_types
......@@ -279,8 +281,8 @@ def make_distribution_no_version(tmpdir, basename):
('dist-info', 'METADATA', DistInfoDistribution),
],
)
def test_distribution_version_missing(tmpdir, suffix, expected_filename,
expected_dist_type):
def test_distribution_version_missing(
tmpdir, suffix, expected_filename, expected_dist_type):
"""
Test Distribution.version when the "Version" header is missing.
"""
......
......@@ -15,7 +15,7 @@ import pkg_resources
from pkg_resources import (
parse_requirements, VersionConflict, parse_version,
Distribution, EntryPoint, Requirement, safe_version, safe_name,
WorkingSet, PkgResourcesDeprecationWarning)
WorkingSet)
# from Python 3.6 docs.
......@@ -501,7 +501,6 @@ class TestEntryPoints:
ep.load(require=False)
class TestRequirements:
def testBasics(self):
r = Requirement.parse("Twisted>=1.2")
......
[build-system]
requires = ["wheel"]
requires = ["setuptools >= 40.8", "wheel"]
build-backend = "setuptools.build_meta"
backend-path = ["."]
[tool.towncrier]
package = "setuptools"
......
[pytest]
addopts=--doctest-modules --doctest-glob=pkg_resources/api_tests.txt -r sxX
norecursedirs=dist build *.egg setuptools/extern pkg_resources/extern pkg_resources/tests/data tools .*
flake8-ignore =
setuptools/site-patch.py F821
setuptools/py*compat.py F811
addopts=--doctest-modules --flake8 --doctest-glob=pkg_resources/api_tests.txt -r sxX
norecursedirs=dist build *.egg setuptools/extern pkg_resources/extern pkg_resources/tests/data tools .* setuptools/_vendor pkg_resources/_vendor
doctest_optionflags=ELLIPSIS ALLOW_UNICODE
filterwarnings =
# https://github.com/pypa/setuptools/issues/1823
......
......@@ -14,12 +14,9 @@ repository = https://upload.pypi.org/legacy/
[sdist]
formats = zip
[bdist_wheel]
universal = 1
[metadata]
name = setuptools
version = 41.6.0
version = 45.1.0
description = Easily download, build, install, upgrade, and uninstall Python packages
author = Python Packaging Authority
author_email = distutils-sig@python.org
......@@ -35,10 +32,8 @@ classifiers =
Intended Audience :: Developers
License :: OSI Approved :: MIT License
Operating System :: OS Independent
Programming Language :: Python :: 2
Programming Language :: Python :: 2.7
Programming Language :: Python :: 3
Programming Language :: Python :: 3.4
Programming Language :: Python :: 3 :: Only
Programming Language :: Python :: 3.5
Programming Language :: Python :: 3.6
Programming Language :: Python :: 3.7
......@@ -50,7 +45,7 @@ classifiers =
[options]
zip_safe = True
python_requires = >=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*
python_requires = >=3.5
py_modules = easy_install
packages = find:
......
......@@ -89,6 +89,13 @@ setup_params = dict(
"%(cmd)s = setuptools.command.%(cmd)s:%(cmd)s" % locals()
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": [
"eager_resources = setuptools.dist:assert_string_list",
"namespace_packages = setuptools.dist:check_nsp",
......
"""Extensions to the 'distutils' for large or complex distributions"""
import os
import sys
import functools
import distutils.core
import distutils.filelist
......@@ -31,7 +30,7 @@ __all__ = [
]
if PY3:
__all__.append('find_namespace_packages')
__all__.append('find_namespace_packages')
__version__ = setuptools.version.__version__
......@@ -123,7 +122,7 @@ class PEP420PackageFinder(PackageFinder):
find_packages = PackageFinder.find
if PY3:
find_namespace_packages = PEP420PackageFinder.find
find_namespace_packages = PEP420PackageFinder.find
def _install_setup_requires(attrs):
......@@ -144,6 +143,7 @@ def setup(**attrs):
_install_setup_requires(attrs)
return distutils.core.setup(**attrs)
setup.__doc__ = distutils.core.setup.__doc__
......@@ -191,8 +191,8 @@ class Command(_Command):
ok = False
if not ok:
raise DistutilsOptionError(
"'%s' must be a list of strings (got %r)"
% (option, val))
"'%s' must be a list of strings (got %r)"
% (option, val))
def reinitialize_command(self, command, reinit_subcommands=0, **kw):
cmd = _Command.reinitialize_command(self, command, reinit_subcommands)
......
......@@ -4,18 +4,24 @@
from __future__ import absolute_import, division, print_function
__all__ = [
"__title__", "__summary__", "__uri__", "__version__", "__author__",
"__email__", "__license__", "__copyright__",
"__title__",
"__summary__",
"__uri__",
"__version__",
"__author__",
"__email__",
"__license__",
"__copyright__",
]
__title__ = "packaging"
__summary__ = "Core utilities for Python packages"
__uri__ = "https://github.com/pypa/packaging"
__version__ = "16.8"
__version__ = "19.2"
__author__ = "Donald Stufft and individual contributors"
__email__ = "donald@stufft.io"
__license__ = "BSD or Apache License, Version 2.0"
__copyright__ = "Copyright 2014-2016 %s" % __author__
__copyright__ = "Copyright 2014-2019 %s" % __author__
......@@ -4,11 +4,23 @@
from __future__ import absolute_import, division, print_function
from .__about__ import (
__author__, __copyright__, __email__, __license__, __summary__, __title__,
__uri__, __version__
__author__,
__copyright__,
__email__,
__license__,
__summary__,
__title__,
__uri__,
__version__,
)
__all__ = [
"__title__", "__summary__", "__uri__", "__version__", "__author__",
"__email__", "__license__", "__copyright__",
"__title__",
"__summary__",
"__uri__",
"__version__",
"__author__",
"__email__",
"__license__",
"__copyright__",
]
......@@ -12,9 +12,9 @@ PY3 = sys.version_info[0] == 3
# flake8: noqa
if PY3:
string_types = str,
string_types = (str,)
else:
string_types = basestring,
string_types = (basestring,)
def with_metaclass(meta, *bases):
......@@ -27,4 +27,5 @@ def with_metaclass(meta, *bases):
class metaclass(meta):
def __new__(cls, name, this_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
class Infinity(object):
def __repr__(self):
return "Infinity"
......@@ -33,11 +32,11 @@ class Infinity(object):
def __neg__(self):
return NegativeInfinity
Infinity = Infinity()
class NegativeInfinity(object):
def __repr__(self):
return "-Infinity"
......@@ -65,4 +64,5 @@ class NegativeInfinity(object):
def __neg__(self):
return Infinity
NegativeInfinity = NegativeInfinity()
......@@ -17,8 +17,11 @@ from .specifiers import Specifier, InvalidSpecifier
__all__ = [
"InvalidMarker", "UndefinedComparison", "UndefinedEnvironmentName",
"Marker", "default_environment",
"InvalidMarker",
"UndefinedComparison",
"UndefinedEnvironmentName",
"Marker",
"default_environment",
]
......@@ -42,7 +45,6 @@ class UndefinedEnvironmentName(ValueError):
class Node(object):
def __init__(self, value):
self.value = value
......@@ -57,62 +59,52 @@ class Node(object):
class Variable(Node):
def serialize(self):
return str(self)
class Value(Node):
def serialize(self):
return '"{0}"'.format(self)
class Op(Node):
def serialize(self):
return str(self)
VARIABLE = (
L("implementation_version") |
L("platform_python_implementation") |
L("implementation_name") |
L("python_full_version") |
L("platform_release") |
L("platform_version") |
L("platform_machine") |
L("platform_system") |
L("python_version") |
L("sys_platform") |
L("os_name") |
L("os.name") | # PEP-345
L("sys.platform") | # PEP-345
L("platform.version") | # PEP-345
L("platform.machine") | # PEP-345
L("platform.python_implementation") | # PEP-345
L("python_implementation") | # undocumented setuptools legacy
L("extra")
L("implementation_version")
| L("platform_python_implementation")
| L("implementation_name")
| L("python_full_version")
| L("platform_release")
| L("platform_version")
| L("platform_machine")
| L("platform_system")
| L("python_version")
| L("sys_platform")
| L("os_name")
| L("os.name")
| L("sys.platform") # PEP-345
| L("platform.version") # PEP-345
| L("platform.machine") # PEP-345
| L("platform.python_implementation") # PEP-345
| L("python_implementation") # PEP-345
| L("extra") # undocumented setuptools legacy
)
ALIASES = {
'os.name': 'os_name',
'sys.platform': 'sys_platform',
'platform.version': 'platform_version',
'platform.machine': 'platform_machine',
'platform.python_implementation': 'platform_python_implementation',
'python_implementation': 'platform_python_implementation'
"os.name": "os_name",
"sys.platform": "sys_platform",
"platform.version": "platform_version",
"platform.machine": "platform_machine",
"platform.python_implementation": "platform_python_implementation",
"python_implementation": "platform_python_implementation",
}
VARIABLE.setParseAction(lambda s, l, t: Variable(ALIASES.get(t[0], t[0])))
VERSION_CMP = (
L("===") |
L("==") |
L(">=") |
L("<=") |
L("!=") |
L("~=") |
L(">") |
L("<")
L("===") | L("==") | L(">=") | L("<=") | L("!=") | L("~=") | L(">") | L("<")
)
MARKER_OP = VERSION_CMP | L("not in") | L("in")
......@@ -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
# the rest of this function so that we don't get extraneous () on the
# outside.
if (isinstance(marker, list) and len(marker) == 1 and
isinstance(marker[0], (list, tuple))):
if (
isinstance(marker, list)
and len(marker) == 1
and isinstance(marker[0], (list, tuple))
):
return _format_marker(marker[0])
if isinstance(marker, list):
......@@ -239,20 +234,20 @@ def _evaluate_markers(markers, environment):
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
if kind != 'final':
if kind != "final":
version += kind[0] + str(info.serial)
return version
def default_environment():
if hasattr(sys, 'implementation'):
if hasattr(sys, "implementation"):
iver = format_full_version(sys.implementation.version)
implementation_name = sys.implementation.name
else:
iver = '0'
implementation_name = ''
iver = "0"
implementation_name = ""
return {
"implementation_name": implementation_name,
......@@ -264,19 +259,19 @@ def default_environment():
"platform_version": platform.version(),
"python_full_version": platform.python_version(),
"platform_python_implementation": platform.python_implementation(),
"python_version": platform.python_version()[:3],
"python_version": ".".join(platform.python_version_tuple()[:2]),
"sys_platform": sys.platform,
}
class Marker(object):
def __init__(self, marker):
try:
self._markers = _coerce_parse_result(MARKER.parseString(marker))
except ParseException as e:
err_str = "Invalid marker: {0!r}, parse error at {1!r}".format(
marker, marker[e.loc:e.loc + 8])
marker, marker[e.loc : e.loc + 8]
)
raise InvalidMarker(err_str)
def __str__(self):
......
......@@ -38,8 +38,8 @@ IDENTIFIER = Combine(ALPHANUM + ZeroOrMore(IDENTIFIER_END))
NAME = IDENTIFIER("name")
EXTRA = IDENTIFIER
URI = Regex(r'[^ ]+')("url")
URL = (AT + URI)
URI = Regex(r"[^ ]+")("url")
URL = AT + URI
EXTRAS_LIST = EXTRA + ZeroOrMore(COMMA + EXTRA)
EXTRAS = (LBRACKET + Optional(EXTRAS_LIST) + RBRACKET)("extras")
......@@ -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_ONE = VERSION_PEP440 ^ VERSION_LEGACY
VERSION_MANY = Combine(VERSION_ONE + ZeroOrMore(COMMA + VERSION_ONE),
joinString=",", adjacent=False)("_raw_spec")
VERSION_MANY = Combine(
VERSION_ONE + ZeroOrMore(COMMA + VERSION_ONE), joinString=",", adjacent=False
)("_raw_spec")
_VERSION_SPEC = Optional(((LPAREN + VERSION_MANY + RPAREN) | VERSION_MANY))
_VERSION_SPEC.setParseAction(lambda s, l, t: t._raw_spec or '')
_VERSION_SPEC.setParseAction(lambda s, l, t: t._raw_spec or "")
VERSION_SPEC = originalTextFor(_VERSION_SPEC)("specifier")
VERSION_SPEC.setParseAction(lambda s, l, t: t[1])
MARKER_EXPR = originalTextFor(MARKER_EXPR())("marker")
MARKER_EXPR.setParseAction(
lambda s, l, t: Marker(s[t._original_start:t._original_end])
lambda s, l, t: Marker(s[t._original_start : t._original_end])
)
MARKER_SEPERATOR = SEMICOLON
MARKER = MARKER_SEPERATOR + MARKER_EXPR
MARKER_SEPARATOR = SEMICOLON
MARKER = MARKER_SEPARATOR + MARKER_EXPR
VERSION_AND_MARKER = VERSION_SPEC + Optional(MARKER)
URL_AND_MARKER = URL + Optional(MARKER)
NAMED_REQUIREMENT = \
NAME + Optional(EXTRAS) + (URL_AND_MARKER | VERSION_AND_MARKER)
NAMED_REQUIREMENT = NAME + Optional(EXTRAS) + (URL_AND_MARKER | VERSION_AND_MARKER)
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):
......@@ -90,15 +93,21 @@ class Requirement(object):
req = REQUIREMENT.parseString(requirement_string)
except ParseException as e:
raise InvalidRequirement(
"Invalid requirement, parse error at \"{0!r}\"".format(
requirement_string[e.loc:e.loc + 8]))
'Parse error at "{0!r}": {1}'.format(
requirement_string[e.loc : e.loc + 8], e.msg
)
)
self.name = req.name
if req.url:
parsed_url = urlparse.urlparse(req.url)
if not (parsed_url.scheme and parsed_url.netloc) or (
not parsed_url.scheme and not parsed_url.netloc):
raise InvalidRequirement("Invalid URL given")
if parsed_url.scheme == "file":
if urlparse.urlunparse(parsed_url) != req.url:
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
else:
self.url = None
......@@ -117,6 +126,8 @@ class Requirement(object):
if self.url:
parts.append("@ {0}".format(self.url))
if self.marker:
parts.append(" ")
if self.marker:
parts.append("; {0}".format(self.marker))
......
......@@ -19,7 +19,6 @@ class InvalidSpecifier(ValueError):
class BaseSpecifier(with_metaclass(abc.ABCMeta, object)):
@abc.abstractmethod
def __str__(self):
"""
......@@ -84,10 +83,7 @@ class _IndividualSpecifier(BaseSpecifier):
if not match:
raise InvalidSpecifier("Invalid specifier: '{0}'".format(spec))
self._spec = (
match.group("operator").strip(),
match.group("version").strip(),
)
self._spec = (match.group("operator").strip(), match.group("version").strip())
# Store whether or not this Specifier should accept prereleases
self._prereleases = prereleases
......@@ -99,11 +95,7 @@ class _IndividualSpecifier(BaseSpecifier):
else ""
)
return "<{0}({1!r}{2})>".format(
self.__class__.__name__,
str(self),
pre,
)
return "<{0}({1!r}{2})>".format(self.__class__.__name__, str(self), pre)
def __str__(self):
return "{0}{1}".format(*self._spec)
......@@ -194,11 +186,12 @@ class _IndividualSpecifier(BaseSpecifier):
# If our version is a prerelease, and we were not set to allow
# prereleases, then we'll store it for later incase nothing
# else matches this specifier.
if (parsed_version.is_prerelease and not
(prereleases or self.prereleases)):
if parsed_version.is_prerelease and not (
prereleases or self.prereleases
):
found_prereleases.append(version)
# Either this is not a prerelease, or we should have been
# accepting prereleases from the begining.
# accepting prereleases from the beginning.
else:
yielded = True
yield version
......@@ -213,8 +206,7 @@ class _IndividualSpecifier(BaseSpecifier):
class LegacySpecifier(_IndividualSpecifier):
_regex_str = (
r"""
_regex_str = r"""
(?P<operator>(==|!=|<=|>=|<|>))
\s*
(?P<version>
......@@ -225,10 +217,8 @@ class LegacySpecifier(_IndividualSpecifier):
# them, and a comma since it's a version separator.
)
"""
)
_regex = re.compile(
r"^\s*" + _regex_str + r"\s*$", re.VERBOSE | re.IGNORECASE)
_regex = re.compile(r"^\s*" + _regex_str + r"\s*$", re.VERBOSE | re.IGNORECASE)
_operators = {
"==": "equal",
......@@ -269,13 +259,13 @@ def _require_version_compare(fn):
if not isinstance(prospective, Version):
return False
return fn(self, prospective, spec)
return wrapped
class Specifier(_IndividualSpecifier):
_regex_str = (
r"""
_regex_str = r"""
(?P<operator>(~=|==|!=|<=|>=|<|>|===))
(?P<version>
(?:
......@@ -367,10 +357,8 @@ class Specifier(_IndividualSpecifier):
)
)
"""
)
_regex = re.compile(
r"^\s*" + _regex_str + r"\s*$", re.VERBOSE | re.IGNORECASE)
_regex = re.compile(r"^\s*" + _regex_str + r"\s*$", re.VERBOSE | re.IGNORECASE)
_operators = {
"~=": "compatible",
......@@ -397,8 +385,7 @@ class Specifier(_IndividualSpecifier):
prefix = ".".join(
list(
itertools.takewhile(
lambda x: (not x.startswith("post") and not
x.startswith("dev")),
lambda x: (not x.startswith("post") and not x.startswith("dev")),
_version_split(spec),
)
)[:-1]
......@@ -407,8 +394,9 @@ class Specifier(_IndividualSpecifier):
# Add the prefix notation to the end of our string
prefix += ".*"
return (self._get_operator(">=")(prospective, spec) and
self._get_operator("==")(prospective, prefix))
return self._get_operator(">=")(prospective, spec) and self._get_operator("==")(
prospective, prefix
)
@_require_version_compare
def _compare_equal(self, prospective, spec):
......@@ -428,7 +416,7 @@ class Specifier(_IndividualSpecifier):
# 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
# 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
# length.
......@@ -503,7 +491,7 @@ class Specifier(_IndividualSpecifier):
return False
# 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 Version(prospective.base_version) == Version(spec.base_version):
return False
......@@ -567,27 +555,17 @@ def _pad_version(left, right):
right_split.append(list(itertools.takewhile(lambda x: x.isdigit(), right)))
# Get the rest of our versions
left_split.append(left[len(left_split[0]):])
right_split.append(right[len(right_split[0]):])
left_split.append(left[len(left_split[0]) :])
right_split.append(right[len(right_split[0]) :])
# Insert our padding
left_split.insert(
1,
["0"] * max(0, len(right_split[0]) - len(left_split[0])),
)
right_split.insert(
1,
["0"] * max(0, len(left_split[0]) - len(right_split[0])),
)
left_split.insert(1, ["0"] * max(0, len(right_split[0]) - len(left_split[0])))
right_split.insert(1, ["0"] * max(0, len(left_split[0]) - len(right_split[0])))
return (
list(itertools.chain(*left_split)),
list(itertools.chain(*right_split)),
)
return (list(itertools.chain(*left_split)), list(itertools.chain(*right_split)))
class SpecifierSet(BaseSpecifier):
def __init__(self, specifiers="", prereleases=None):
# Split on , to break each indidivual specifier into it's own item, and
# strip each item to remove leading/trailing whitespace.
......@@ -721,10 +699,7 @@ class SpecifierSet(BaseSpecifier):
# given version is contained within all of them.
# Note: This use of all() here means that an empty set of specifiers
# will always return True, this is an explicit design decision.
return all(
s.contains(item, prereleases=prereleases)
for s in self._specs
)
return all(s.contains(item, prereleases=prereleases) for s in self._specs)
def filter(self, iterable, prereleases=None):
# Determine if we're forcing a prerelease or not, if we're not forcing
......
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
from __future__ import absolute_import
import distutils.util
try:
from importlib.machinery import EXTENSION_SUFFIXES
except ImportError: # pragma: no cover
import imp
EXTENSION_SUFFIXES = [x[0] for x in imp.get_suffixes()]
del imp
import platform
import re
import sys
import sysconfig
import warnings
INTERPRETER_SHORT_NAMES = {
"python": "py", # Generic.
"cpython": "cp",
"pypy": "pp",
"ironpython": "ip",
"jython": "jy",
}
_32_BIT_INTERPRETER = sys.maxsize <= 2 ** 32
class Tag(object):
__slots__ = ["_interpreter", "_abi", "_platform"]
def __init__(self, interpreter, abi, platform):
self._interpreter = interpreter.lower()
self._abi = abi.lower()
self._platform = platform.lower()
@property
def interpreter(self):
return self._interpreter
@property
def abi(self):
return self._abi
@property
def platform(self):
return self._platform
def __eq__(self, other):
return (
(self.platform == other.platform)
and (self.abi == other.abi)
and (self.interpreter == other.interpreter)
)
def __hash__(self):
return hash((self._interpreter, self._abi, self._platform))
def __str__(self):
return "{}-{}-{}".format(self._interpreter, self._abi, self._platform)
def __repr__(self):
return "<{self} @ {self_id}>".format(self=self, self_id=id(self))
def parse_tag(tag):
tags = set()
interpreters, abis, platforms = tag.split("-")
for interpreter in interpreters.split("."):
for abi in abis.split("."):
for platform_ in platforms.split("."):
tags.add(Tag(interpreter, abi, platform_))
return frozenset(tags)
def _normalize_string(string):
return string.replace(".", "_").replace("-", "_")
def _cpython_interpreter(py_version):
# TODO: Is using py_version_nodot for interpreter version critical?
return "cp{major}{minor}".format(major=py_version[0], minor=py_version[1])
def _cpython_abis(py_version):
abis = []
version = "{}{}".format(*py_version[:2])
debug = pymalloc = ucs4 = ""
with_debug = sysconfig.get_config_var("Py_DEBUG")
has_refcount = hasattr(sys, "gettotalrefcount")
# Windows doesn't set Py_DEBUG, so checking for support of debug-compiled
# extension modules is the best option.
# https://github.com/pypa/pip/issues/3383#issuecomment-173267692
has_ext = "_d.pyd" in EXTENSION_SUFFIXES
if with_debug or (with_debug is None and (has_refcount or has_ext)):
debug = "d"
if py_version < (3, 8):
with_pymalloc = sysconfig.get_config_var("WITH_PYMALLOC")
if with_pymalloc or with_pymalloc is None:
pymalloc = "m"
if py_version < (3, 3):
unicode_size = sysconfig.get_config_var("Py_UNICODE_SIZE")
if unicode_size == 4 or (
unicode_size is None and sys.maxunicode == 0x10FFFF
):
ucs4 = "u"
elif debug:
# Debug builds can also load "normal" extension modules.
# We can also assume no UCS-4 or pymalloc requirement.
abis.append("cp{version}".format(version=version))
abis.insert(
0,
"cp{version}{debug}{pymalloc}{ucs4}".format(
version=version, debug=debug, pymalloc=pymalloc, ucs4=ucs4
),
)
return abis
def _cpython_tags(py_version, interpreter, abis, platforms):
for abi in abis:
for platform_ in platforms:
yield Tag(interpreter, abi, platform_)
for tag in (Tag(interpreter, "abi3", platform_) for platform_ in platforms):
yield tag
for tag in (Tag(interpreter, "none", platform_) for platform_ in platforms):
yield tag
# PEP 384 was first implemented in Python 3.2.
for minor_version in range(py_version[1] - 1, 1, -1):
for platform_ in platforms:
interpreter = "cp{major}{minor}".format(
major=py_version[0], minor=minor_version
)
yield Tag(interpreter, "abi3", platform_)
def _pypy_interpreter():
return "pp{py_major}{pypy_major}{pypy_minor}".format(
py_major=sys.version_info[0],
pypy_major=sys.pypy_version_info.major,
pypy_minor=sys.pypy_version_info.minor,
)
def _generic_abi():
abi = sysconfig.get_config_var("SOABI")
if abi:
return _normalize_string(abi)
else:
return "none"
def _pypy_tags(py_version, interpreter, abi, platforms):
for tag in (Tag(interpreter, abi, platform) for platform in platforms):
yield tag
for tag in (Tag(interpreter, "none", platform) for platform in platforms):
yield tag
def _generic_tags(interpreter, py_version, abi, platforms):
for tag in (Tag(interpreter, abi, platform) for platform in platforms):
yield tag
if abi != "none":
tags = (Tag(interpreter, "none", platform_) for platform_ in platforms)
for tag in tags:
yield tag
def _py_interpreter_range(py_version):
"""
Yield Python versions in descending order.
After the latest version, the major-only version will be yielded, and then
all following versions up to 'end'.
"""
yield "py{major}{minor}".format(major=py_version[0], minor=py_version[1])
yield "py{major}".format(major=py_version[0])
for minor in range(py_version[1] - 1, -1, -1):
yield "py{major}{minor}".format(major=py_version[0], minor=minor)
def _independent_tags(interpreter, py_version, platforms):
"""
Return the sequence of tags that are consistent across implementations.
The tags consist of:
- py*-none-<platform>
- <interpreter>-none-any
- py*-none-any
"""
for version in _py_interpreter_range(py_version):
for platform_ in platforms:
yield Tag(version, "none", platform_)
yield Tag(interpreter, "none", "any")
for version in _py_interpreter_range(py_version):
yield Tag(version, "none", "any")
def _mac_arch(arch, is_32bit=_32_BIT_INTERPRETER):
if not is_32bit:
return arch
if arch.startswith("ppc"):
return "ppc"
return "i386"
def _mac_binary_formats(version, cpu_arch):
formats = [cpu_arch]
if cpu_arch == "x86_64":
if version < (10, 4):
return []
formats.extend(["intel", "fat64", "fat32"])
elif cpu_arch == "i386":
if version < (10, 4):
return []
formats.extend(["intel", "fat32", "fat"])
elif cpu_arch == "ppc64":
# TODO: Need to care about 32-bit PPC for ppc64 through 10.2?
if version > (10, 5) or version < (10, 4):
return []
formats.append("fat64")
elif cpu_arch == "ppc":
if version > (10, 6):
return []
formats.extend(["fat32", "fat"])
formats.append("universal")
return formats
def _mac_platforms(version=None, arch=None):
version_str, _, cpu_arch = platform.mac_ver()
if version is None:
version = tuple(map(int, version_str.split(".")[:2]))
if arch is None:
arch = _mac_arch(cpu_arch)
platforms = []
for minor_version in range(version[1], -1, -1):
compat_version = version[0], minor_version
binary_formats = _mac_binary_formats(compat_version, arch)
for binary_format in binary_formats:
platforms.append(
"macosx_{major}_{minor}_{binary_format}".format(
major=compat_version[0],
minor=compat_version[1],
binary_format=binary_format,
)
)
return platforms
# From PEP 513.
def _is_manylinux_compatible(name, glibc_version):
# Check for presence of _manylinux module.
try:
import _manylinux
return bool(getattr(_manylinux, name + "_compatible"))
except (ImportError, AttributeError):
# Fall through to heuristic check below.
pass
return _have_compatible_glibc(*glibc_version)
def _glibc_version_string():
# Returns glibc version string, or None if not using glibc.
import ctypes
# 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)
def _linux_platforms(is_32bit=_32_BIT_INTERPRETER):
linux = _normalize_string(distutils.util.get_platform())
if linux == "linux_x86_64" and is_32bit:
linux = "linux_i686"
manylinux_support = (
("manylinux2014", (2, 17)), # CentOS 7 w/ glibc 2.17 (PEP 599)
("manylinux2010", (2, 12)), # CentOS 6 w/ glibc 2.12 (PEP 571)
("manylinux1", (2, 5)), # CentOS 5 w/ glibc 2.5 (PEP 513)
)
manylinux_support_iter = iter(manylinux_support)
for name, glibc_version in manylinux_support_iter:
if _is_manylinux_compatible(name, glibc_version):
platforms = [linux.replace("linux", name)]
break
else:
platforms = []
# Support for a later manylinux implies support for an earlier version.
platforms += [linux.replace("linux", name) for name, _ in manylinux_support_iter]
platforms.append(linux)
return platforms
def _generic_platforms():
platform = _normalize_string(distutils.util.get_platform())
return [platform]
def _interpreter_name():
name = platform.python_implementation().lower()
return INTERPRETER_SHORT_NAMES.get(name) or name
def _generic_interpreter(name, py_version):
version = sysconfig.get_config_var("py_version_nodot")
if not version:
version = "".join(map(str, py_version[:2]))
return "{name}{version}".format(name=name, version=version)
def sys_tags():
"""
Returns the sequence of tag triples for the running interpreter.
The order of the sequence corresponds to priority order for the
interpreter, from most to least important.
"""
py_version = sys.version_info[:2]
interpreter_name = _interpreter_name()
if platform.system() == "Darwin":
platforms = _mac_platforms()
elif platform.system() == "Linux":
platforms = _linux_platforms()
else:
platforms = _generic_platforms()
if interpreter_name == "cp":
interpreter = _cpython_interpreter(py_version)
abis = _cpython_abis(py_version)
for tag in _cpython_tags(py_version, interpreter, abis, platforms):
yield tag
elif interpreter_name == "pp":
interpreter = _pypy_interpreter()
abi = _generic_abi()
for tag in _pypy_tags(py_version, interpreter, abi, platforms):
yield tag
else:
interpreter = _generic_interpreter(interpreter_name, py_version)
abi = _generic_abi()
for tag in _generic_tags(interpreter, py_version, abi, platforms):
yield tag
for tag in _independent_tags(interpreter, py_version, platforms):
yield tag
......@@ -5,6 +5,8 @@ from __future__ import absolute_import, division, print_function
import re
from .version import InvalidVersion, Version
_canonicalize_regex = re.compile(r"[-_.]+")
......@@ -12,3 +14,44 @@ _canonicalize_regex = re.compile(r"[-_.]+")
def canonicalize_name(name):
# This is taken from PEP 503.
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
from ._structures import Infinity
__all__ = [
"parse", "Version", "LegacyVersion", "InvalidVersion", "VERSION_PATTERN"
]
__all__ = ["parse", "Version", "LegacyVersion", "InvalidVersion", "VERSION_PATTERN"]
_Version = collections.namedtuple(
"_Version",
["epoch", "release", "dev", "pre", "post", "local"],
"_Version", ["epoch", "release", "dev", "pre", "post", "local"]
)
......@@ -40,7 +37,6 @@ class InvalidVersion(ValueError):
class _BaseVersion(object):
def __hash__(self):
return hash(self._key)
......@@ -70,7 +66,6 @@ class _BaseVersion(object):
class LegacyVersion(_BaseVersion):
def __init__(self, version):
self._version = str(version)
self._key = _legacy_cmpkey(self._version)
......@@ -89,6 +84,26 @@ class LegacyVersion(_BaseVersion):
def base_version(self):
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
def local(self):
return None
......@@ -101,13 +116,19 @@ class LegacyVersion(_BaseVersion):
def is_postrelease(self):
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 = {
"pre": "c", "preview": "c", "-": "final-", "rc": "c", "dev": "@",
"pre": "c",
"preview": "c",
"-": "final-",
"rc": "c",
"dev": "@",
}
......@@ -154,6 +175,7 @@ def _legacy_cmpkey(version):
return epoch, parts
# Deliberately not anchored to the start and end of the string, to make it
# easier for 3rd party code to reuse
VERSION_PATTERN = r"""
......@@ -190,10 +212,7 @@ VERSION_PATTERN = r"""
class Version(_BaseVersion):
_regex = re.compile(
r"^\s*" + VERSION_PATTERN + r"\s*$",
re.VERBOSE | re.IGNORECASE,
)
_regex = re.compile(r"^\s*" + VERSION_PATTERN + r"\s*$", re.VERBOSE | re.IGNORECASE)
def __init__(self, version):
# Validate the version and parse it into pieces
......@@ -205,18 +224,11 @@ class Version(_BaseVersion):
self._version = _Version(
epoch=int(match.group("epoch")) if match.group("epoch") else 0,
release=tuple(int(i) for i in match.group("release").split(".")),
pre=_parse_letter_version(
match.group("pre_l"),
match.group("pre_n"),
),
pre=_parse_letter_version(match.group("pre_l"), match.group("pre_n")),
post=_parse_letter_version(
match.group("post_l"),
match.group("post_n1") or match.group("post_n2"),
),
dev=_parse_letter_version(
match.group("dev_l"),
match.group("dev_n"),
match.group("post_l"), match.group("post_n1") or match.group("post_n2")
),
dev=_parse_letter_version(match.group("dev_l"), match.group("dev_n")),
local=_parse_local_version(match.group("local")),
)
......@@ -237,32 +249,57 @@ class Version(_BaseVersion):
parts = []
# Epoch
if self._version.epoch != 0:
parts.append("{0}!".format(self._version.epoch))
if self.epoch != 0:
parts.append("{0}!".format(self.epoch))
# 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
if self._version.pre is not None:
parts.append("".join(str(x) for x in self._version.pre))
if self.pre is not None:
parts.append("".join(str(x) for x in self.pre))
# Post-release
if self._version.post is not None:
parts.append(".post{0}".format(self._version.post[1]))
if self.post is not None:
parts.append(".post{0}".format(self.post))
# Development release
if self._version.dev is not None:
parts.append(".dev{0}".format(self._version.dev[1]))
if self.dev is not None:
parts.append(".dev{0}".format(self.dev))
# Local version segment
if self._version.local is not None:
parts.append(
"+{0}".format(".".join(str(x) for x in self._version.local))
)
if self.local is not None:
parts.append("+{0}".format(self.local))
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
def public(self):
return str(self).split("+", 1)[0]
......@@ -272,27 +309,25 @@ class Version(_BaseVersion):
parts = []
# Epoch
if self._version.epoch != 0:
parts.append("{0}!".format(self._version.epoch))
if self.epoch != 0:
parts.append("{0}!".format(self.epoch))
# 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)
@property
def local(self):
version_string = str(self)
if "+" in version_string:
return version_string.split("+", 1)[1]
@property
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
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):
......@@ -326,7 +361,7 @@ def _parse_letter_version(letter, number):
return letter, int(number)
_local_version_seperators = re.compile(r"[\._-]")
_local_version_separators = re.compile(r"[\._-]")
def _parse_local_version(local):
......@@ -336,7 +371,7 @@ def _parse_local_version(local):
if local is not None:
return tuple(
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):
# re-reverse it back into the correct order and make it a tuple and use
# that for our sorting key.
release = tuple(
reversed(list(
itertools.dropwhile(
lambda x: x == 0,
reversed(release),
)
))
reversed(list(itertools.dropwhile(lambda x: x == 0, reversed(release))))
)
# We need to "trick" the sorting algorithm to put 1.0.dev0 before 1.0a0.
......@@ -385,9 +415,6 @@ def _cmpkey(epoch, release, pre, post, dev, local):
# - Numeric segments sort numerically
# - Shorter versions sort before longer versions when the prefixes
# match exactly
local = tuple(
(i, "") if isinstance(i, int) else (-Infinity, i)
for i in local
)
local = tuple((i, "") if isinstance(i, int) else (-Infinity, i) for i in local)
return epoch, release, pre, post, dev, local
packaging==16.8
packaging==19.2
pyparsing==2.2.1
six==1.10.0
ordered-set==3.1.1
......@@ -25,7 +25,8 @@ def default_filter(src, 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):
"""Unpack `filename` to `extract_dir`, or raise ``UnrecognizedFormat``
......@@ -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
# 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
if member.issym():
base = posixpath.dirname(member.name)
......
......@@ -48,6 +48,7 @@ __all__ = ['get_requires_for_build_sdist',
'__legacy__',
'SetupRequirementsError']
class SetupRequirementsError(BaseException):
def __init__(self, specifiers):
self.specifiers = specifiers
......@@ -143,7 +144,8 @@ class _BuildMetaBackend(object):
def get_requires_for_build_wheel(self, config_settings=None):
config_settings = self._fix_config(config_settings)
return self._get_build_requires(config_settings, requirements=['wheel'])
return self._get_build_requires(
config_settings, requirements=['wheel'])
def get_requires_for_build_sdist(self, config_settings=None):
config_settings = self._fix_config(config_settings)
......@@ -160,8 +162,10 @@ class _BuildMetaBackend(object):
dist_infos = [f for f in os.listdir(dist_info_directory)
if f.endswith('.dist-info')]
if (len(dist_infos) == 0 and
len(_get_immediate_subdirectories(dist_info_directory)) == 1):
if (
len(dist_infos) == 0 and
len(_get_immediate_subdirectories(dist_info_directory)) == 1
):
dist_info_directory = os.path.join(
dist_info_directory, os.listdir(dist_info_directory)[0])
......@@ -193,7 +197,8 @@ class _BuildMetaBackend(object):
config_settings["--global-option"])
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)
if os.path.exists(result_path):
# os.rename will fail overwriting on non-Unix.
......@@ -202,7 +207,6 @@ class _BuildMetaBackend(object):
return result_basename
def build_wheel(self, wheel_directory, config_settings=None,
metadata_directory=None):
return self._build_with_temp_dir(['bdist_wheel'], '.whl',
......@@ -217,9 +221,12 @@ class _BuildMetaBackend(object):
class _BuildMetaLegacyBackend(_BuildMetaBackend):
"""Compatibility backend for setuptools
This is a version of setuptools.build_meta that endeavors to maintain backwards
compatibility with pre-PEP 517 modes of invocation. It exists as a temporary
bridge between the old packaging mechanism and the new packaging mechanism,
This is a version of setuptools.build_meta that endeavors
to maintain backwards
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.
"""
def run_setup(self, setup_script='setup.py'):
......@@ -232,6 +239,12 @@ class _BuildMetaLegacyBackend(_BuildMetaBackend):
if script_dir not in sys.path:
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:
super(_BuildMetaLegacyBackend,
self).run_setup(setup_script=setup_script)
......@@ -242,6 +255,8 @@ class _BuildMetaLegacyBackend(_BuildMetaBackend):
# the original path so that the path manipulation does not persist
# within the hook after run_setup is called.
sys.path[:] = sys_path
sys.argv[0] = sys_argv_0
# The primary backend
_BACKEND = _BuildMetaBackend()
......
......@@ -25,9 +25,9 @@ class build_clib(orig.build_clib):
sources = build_info.get('sources')
if sources is None or not isinstance(sources, (list, tuple)):
raise DistutilsSetupError(
"in 'libraries' option (library '%s'), "
"'sources' must be present and must be "
"a list of source filenames" % lib_name)
"in 'libraries' option (library '%s'), "
"'sources' must be present and must be "
"a list of source filenames" % lib_name)
sources = list(sources)
log.info("building '%s' library", lib_name)
......@@ -38,9 +38,9 @@ class build_clib(orig.build_clib):
obj_deps = build_info.get('obj_deps', dict())
if not isinstance(obj_deps, dict):
raise DistutilsSetupError(
"in 'libraries' option (library '%s'), "
"'obj_deps' must be a dictionary of "
"type 'source: list'" % lib_name)
"in 'libraries' option (library '%s'), "
"'obj_deps' must be a dictionary of "
"type 'source: list'" % lib_name)
dependencies = []
# Get the global dependencies that are specified by the '' key.
......@@ -48,9 +48,9 @@ class build_clib(orig.build_clib):
global_deps = obj_deps.get('', list())
if not isinstance(global_deps, (list, tuple)):
raise DistutilsSetupError(
"in 'libraries' option (library '%s'), "
"'obj_deps' must be a dictionary of "
"type 'source: list'" % lib_name)
"in 'libraries' option (library '%s'), "
"'obj_deps' must be a dictionary of "
"type 'source: list'" % lib_name)
# Build the list to be used by newer_pairwise_group
# each source will be auto-added to its dependencies.
......@@ -60,39 +60,42 @@ class build_clib(orig.build_clib):
extra_deps = obj_deps.get(source, list())
if not isinstance(extra_deps, (list, tuple)):
raise DistutilsSetupError(
"in 'libraries' option (library '%s'), "
"'obj_deps' must be a dictionary of "
"type 'source: list'" % lib_name)
"in 'libraries' option (library '%s'), "
"'obj_deps' must be a dictionary of "
"type 'source: list'" % lib_name)
src_deps.extend(extra_deps)
dependencies.append(src_deps)
expected_objects = self.compiler.object_filenames(
sources,
output_dir=self.build_temp
)
sources,
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
# directory. (This should probably change to putting object
# files in a temporary build directory.)
macros = build_info.get('macros')
include_dirs = build_info.get('include_dirs')
cflags = build_info.get('cflags')
objects = self.compiler.compile(
sources,
output_dir=self.build_temp,
macros=macros,
include_dirs=include_dirs,
extra_postargs=cflags,
debug=self.debug
)
self.compiler.compile(
sources,
output_dir=self.build_temp,
macros=macros,
include_dirs=include_dirs,
extra_postargs=cflags,
debug=self.debug
)
# Now "link" the object files together into a static library.
# (On Unix at least, this isn't really linking -- it just
# builds an archive. Whatever.)
self.compiler.create_static_lib(
expected_objects,
lib_name,
output_dir=self.build_clib,
debug=self.debug
)
expected_objects,
lib_name,
output_dir=self.build_clib,
debug=self.debug
)
......@@ -14,7 +14,8 @@ from setuptools.extern import six
if six.PY2:
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:
from importlib.machinery import EXTENSION_SUFFIXES
......@@ -29,7 +30,7 @@ except ImportError:
# make sure _config_vars is initialized
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):
......@@ -65,7 +66,9 @@ elif os.name != 'nt':
except ImportError:
pass
if_dl = lambda s: s if have_rtld else ''
def if_dl(s):
return s if have_rtld else ''
def get_abi3_suffix():
......@@ -113,7 +116,7 @@ class build_ext(_build_ext):
if fullname in self.ext_map:
ext = self.ext_map[fullname]
use_abi3 = (
six.PY3
not six.PY2
and getattr(ext, 'py_limited_api')
and get_abi3_suffix()
)
......
......@@ -108,7 +108,7 @@ class develop(namespaces.DevelopInstaller, easy_install):
return path_to_setup
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:
# Ensure metadata is up-to-date
......
......@@ -121,7 +121,8 @@ else:
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):
......@@ -410,7 +411,13 @@ class easy_install(Command):
]
self._expand_attrs(dirs)
def run(self):
def run(self, show_deprecation=True):
if show_deprecation:
self.announce(
"WARNING: The easy_install command is deprecated "
"and will be removed in a future version.",
log.WARN,
)
if self.verbose != self.distribution.verbose:
log.set_verbosity(self.verbose)
try:
......@@ -501,13 +508,13 @@ class easy_install(Command):
the distutils default setting) was:
%s
""").lstrip()
""").lstrip() # noqa
__not_exists_id = textwrap.dedent("""
This directory does not currently exist. Please create it and try again, or
choose a different installation directory (using the -d or --install-dir
option).
""").lstrip()
""").lstrip() # noqa
__access_msg = textwrap.dedent("""
Perhaps your account does not have write access to this directory? If the
......@@ -523,7 +530,7 @@ class easy_install(Command):
https://setuptools.readthedocs.io/en/latest/easy_install.html
Please make the appropriate changes for your system and try again.
""").lstrip()
""").lstrip() # noqa
def cant_write_to_target(self):
msg = self.__cant_write_msg % (sys.exc_info()[1], self.install_dir,)
......@@ -1087,13 +1094,13 @@ class easy_install(Command):
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 version or higher
""").lstrip()
""").lstrip() # noqa
__id_warning = textwrap.dedent("""
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
PYTHONPATH, or by being added to sys.path by your code.)
""")
""") # noqa
def installation_report(self, req, dist, what="Installed"):
"""Helpful installation message for display to package users"""
......@@ -1118,7 +1125,7 @@ class easy_install(Command):
%(python)s setup.py develop
See the setuptools documentation for the "develop" command for more info.
""").lstrip()
""").lstrip() # noqa
def report_editable(self, spec, setup_script):
dirname = os.path.dirname(setup_script)
......@@ -1301,7 +1308,8 @@ class easy_install(Command):
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
......@@ -1561,7 +1569,7 @@ def get_exe_prefixes(exe_filename):
continue
if parts[0].upper() in ('PURELIB', 'PLATLIB'):
contents = z.read(name)
if six.PY3:
if not six.PY2:
contents = contents.decode()
for pth in yield_lines(contents):
pth = pth.strip().replace('\\', '/')
......@@ -2087,7 +2095,8 @@ class ScriptWriter:
@classmethod
def get_script_header(cls, script_text, executable=None, wininst=False):
# for backward compatibility
warnings.warn("Use get_header", EasyInstallDeprecationWarning, stacklevel=2)
warnings.warn(
"Use get_header", EasyInstallDeprecationWarning, stacklevel=2)
if wininst:
executable = "python.exe"
return cls.get_header(script_text, executable)
......@@ -2336,6 +2345,8 @@ def _patch_usage():
finally:
distutils.core.gen_usage = saved
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
from setuptools.extern import packaging
from setuptools import SetuptoolsDeprecationWarning
def translate_pattern(glob):
"""
Translate a file path glob like '*.txt' in to a regular expression.
......@@ -113,7 +114,7 @@ def translate_pattern(glob):
pat += sep
pat += r'\Z'
return re.compile(pat, flags=re.MULTILINE|re.DOTALL)
return re.compile(pat, flags=re.MULTILINE | re.DOTALL)
class InfoCommon:
......@@ -266,7 +267,7 @@ class egg_info(InfoCommon, Command):
to the file.
"""
log.info("writing %s to %s", what, filename)
if six.PY3:
if not six.PY2:
data = data.encode("utf-8")
if not self.dry_run:
f = open(filename, 'wb')
......@@ -637,7 +638,9 @@ def warn_depends_obsolete(cmd, basename, filename):
def _write_requirements(stream, reqs):
lines = yield_lines(reqs or ())
append_cr = lambda line: line + '\n'
def append_cr(line):
return line + '\n'
lines = map(append_cr, lines)
stream.writelines(lines)
......@@ -703,7 +706,8 @@ def get_pkg_info_revision():
Get a -r### off of PKG-INFO Version in case this is an sdist of
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'):
with io.open('PKG-INFO') as f:
for line in f:
......@@ -714,4 +718,4 @@ def get_pkg_info_revision():
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):
args.insert(0, setuptools.bootstrap_install_from)
cmd.args = args
cmd.run()
cmd.run(show_deprecation=False)
setuptools.bootstrap_install_from = None
......
......@@ -77,7 +77,8 @@ class install_lib(orig.install_lib):
if not hasattr(sys, 'implementation'):
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 + '.pyo'
yield base + '.opt-1.pyc'
......
......@@ -132,5 +132,5 @@ class sdist_add_defaults:
if hasattr(sdist.sdist, '_add_defaults_standards'):
# disable the functionality already available upstream
class sdist_add_defaults:
class sdist_add_defaults: # noqa
pass
......@@ -5,7 +5,7 @@ import sys
import io
import contextlib
from setuptools.extern import six
from setuptools.extern import six, ordered_set
from .py36compat import sdist_add_defaults
......@@ -121,19 +121,40 @@ class sdist(sdist_add_defaults, orig.sdist):
if has_leaky_handle:
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):
"""getting python files"""
if self.distribution.has_pure_modules():
build_py = self.get_finalized_command('build_py')
self.filelist.extend(build_py.get_source_files())
# This functionality is incompatible with include_package_data, and
# will in fact create an infinite recursion if include_package_data
# is True. Use of include_package_data will imply that
# distutils-style automatic handling of package_data is disabled
if not self.distribution.include_package_data:
for _, src_dir, _, filenames in build_py.data_files:
self.filelist.extend([os.path.join(src_dir, filename)
for filename in filenames])
self._add_data_files(self._safe_data_files(build_py))
def _safe_data_files(self, build_py):
"""
Extracting data_files from build_py is known to cause
infinite recursion errors when `include_package_data`
is enabled, so suppress it in that case.
"""
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):
try:
......@@ -186,7 +207,7 @@ class sdist(sdist_add_defaults, orig.sdist):
manifest = open(self.manifest, 'rb')
for line in manifest:
# The manifest must contain UTF-8. See #303.
if six.PY3:
if not six.PY2:
try:
line = line.decode('UTF-8')
except UnicodeDecodeError:
......@@ -200,10 +221,12 @@ class sdist(sdist_add_defaults, orig.sdist):
manifest.close()
def check_license(self):
"""Checks if license_file' is configured and adds it to
'self.filelist' if the value contains a valid path.
"""Checks if license_file' or 'license_files' is configured and adds any
valid paths to 'self.filelist'.
"""
files = ordered_set.OrderedSet()
opts = self.distribution.get_option_dict('metadata')
# ignore the source of the value
......@@ -211,11 +234,19 @@ class sdist(sdist_add_defaults, orig.sdist):
if license_file is None:
log.debug("'license_file' option was not specified")
return
else:
files.add(license_file)
if not os.path.exists(license_file):
log.warn("warning: Failed to find the configured license file '%s'",
license_file)
return
try:
files.update(self.distribution.metadata.license_files)
except TypeError:
log.warn("warning: 'license_files' option is malformed")
for f in files:
if not os.path.exists(f):
log.warn(
"warning: Failed to find the configured license file '%s'",
f)
files.remove(f)
self.filelist.append(license_file)
self.filelist.extend(files)
......@@ -129,7 +129,8 @@ class test(Command):
@contextlib.contextmanager
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 we run 2to3 we can not do this inplace:
......@@ -240,7 +241,7 @@ class test(Command):
# Purge modules under test from sys.modules. The test loader will
# re-import them from the build location. Required when 2to3 is used
# 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]
if module in _namespace_packages:
del_modules = []
......
......@@ -24,7 +24,7 @@ from .upload import upload
def _encode(s):
errors = 'surrogateescape' if six.PY3 else 'strict'
errors = 'strict' if six.PY2 else 'surrogateescape'
return s.encode('utf-8', errors)
......@@ -127,8 +127,8 @@ class upload_docs(upload):
"""
Build up the MIME payload for the POST data
"""
boundary = b'--------------GHSKFJDLGDS7543FJKLFHRE75642756743254'
sep_boundary = b'\n--' + boundary
boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254'
sep_boundary = b'\n--' + boundary.encode('ascii')
end_boundary = sep_boundary + b'--'
end_items = end_boundary, b"\n",
builder = functools.partial(
......@@ -138,7 +138,7 @@ class upload_docs(upload):
part_groups = map(builder, data.items())
parts = itertools.chain.from_iterable(part_groups)
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
def upload_file(self, filename):
......@@ -153,7 +153,7 @@ class upload_docs(upload):
# set up the authentication
credentials = _encode(self.username + ':' + self.password)
credentials = standard_b64encode(credentials)
if six.PY3:
if not six.PY2:
credentials = credentials.decode('ascii')
auth = "Basic " + credentials
......
......@@ -483,6 +483,7 @@ class ConfigMetadataHandler(ConfigHandler):
'obsoletes': parse_list,
'classifiers': self._get_parser_compound(parse_file, parse_list),
'license': exclude_files_parser('license'),
'license_files': parse_list,
'description': parse_file,
'long_description': parse_file,
'version': self._parse_version,
......
from distutils.dep_util import newer_group
# yes, this is was almost entirely copy-pasted from
# 'newer_pairwise()', this is just another convenience
# function.
......@@ -10,7 +11,8 @@ def newer_pairwise_group(sources_groups, targets):
of 'newer_group()'.
"""
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
n_sources = []
......
......@@ -162,7 +162,7 @@ def write_pkg_file(self, file):
if self.download_url:
write_field('Download-URL', self.download_url)
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())
write_field('Description', long_desc)
......@@ -409,6 +409,7 @@ class Distribution(_Distribution):
'long_description_content_type': None,
'project_urls': dict,
'provides_extras': ordered_set.OrderedSet,
'license_files': ordered_set.OrderedSet,
}
_patched_dist = None
......@@ -570,7 +571,7 @@ class Distribution(_Distribution):
from setuptools.extern.six.moves.configparser import ConfigParser
# 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 = [
'install-base', 'install-platbase', 'install-lib',
'install-platlib', 'install-purelib', 'install-headers',
......@@ -592,7 +593,7 @@ class Distribution(_Distribution):
with io.open(filename, encoding='utf-8') as reader:
if DEBUG:
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():
options = parser.options(section)
opt_dict = self.get_option_dict(section)
......@@ -635,7 +636,7 @@ class Distribution(_Distribution):
Ref #1653
"""
if six.PY3:
if not six.PY2:
return val
try:
return val.encode()
......@@ -724,15 +725,28 @@ class Distribution(_Distribution):
return resolved_dists
def finalize_options(self):
_Distribution.finalize_options(self)
if self.features:
self._set_global_opts_from_features()
"""
Allow plugins to apply arbitrary operations to the
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'):
value = getattr(self, ep.name, None)
if value is not None:
ep.require(installer=self.fetch_build_egg)
ep.load()(self, ep.name, value)
def _finalize_2to3_doctests(self):
if getattr(self, 'convert_2to3_doctests', None):
# XXX may convert to set here when we can rely on set being builtin
self.convert_2to3_doctests = [
......@@ -759,36 +773,15 @@ class Distribution(_Distribution):
def fetch_build_egg(self, req):
"""Fetch an egg needed for building"""
from setuptools.command.easy_install import easy_install
dist = self.__class__({'script_args': ['easy_install']})
opts = dist.get_option_dict('easy_install')
opts.clear()
opts.update(
(k, v)
for k, v in self.get_option_dict('easy_install').items()
if k in (
# don't use any other settings
'find_links', 'site_dirs', 'index_url',
'optimize', 'site_dirs', 'allow_hosts',
))
if self.dependency_links:
links = self.dependency_links[:]
if 'find_links' in opts:
links = opts['find_links'][1] + links
opts['find_links'] = ('setup', links)
install_dir = self.get_egg_cache_dir()
cmd = easy_install(
dist, args=["x"], install_dir=install_dir,
exclude_scripts=True,
always_copy=False, build_directory=None, editable=False,
upgrade=False, multi_version=True, no_report=True, user=False
)
cmd.ensure_finalized()
return cmd.easy_install(req)
from setuptools.installer import fetch_build_egg
return fetch_build_egg(self, req)
def _set_global_opts_from_features(self):
def _finalize_feature_opts(self):
"""Add --with-X/--without-X options based on optional features"""
if not self.features:
return
go = []
no = self.negative_opt.copy()
......
# 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
......@@ -544,7 +544,7 @@ class SystemInfo:
# Except for VS15+, VC version is aligned with VS version
self.vs_ver = self.vc_ver = (
vc_ver or self._find_latest_available_vs_ver())
vc_ver or self._find_latest_available_vs_ver())
def _find_latest_available_vs_ver(self):
"""
......@@ -1225,7 +1225,7 @@ class EnvironmentInfo:
arch_subdir = self.pi.target_dir(x64=True)
lib = join(self.si.WindowsSdkDir, 'lib')
libver = self._sdk_subdir
return [join(lib, '%sum%s' % (libver , arch_subdir))]
return [join(lib, '%sum%s' % (libver, arch_subdir))]
@property
def OSIncludes(self):
......@@ -1274,13 +1274,16 @@ class EnvironmentInfo:
libpath += [
ref,
join(self.si.WindowsSdkDir, 'UnionMetadata'),
join(ref, 'Windows.Foundation.UniversalApiContract', '1.0.0.0'),
join(
ref, 'Windows.Foundation.UniversalApiContract', '1.0.0.0'),
join(ref, 'Windows.Foundation.FoundationContract', '1.0.0.0'),
join(ref,'Windows.Networking.Connectivity.WwanContract',
'1.0.0.0'),
join(self.si.WindowsSdkDir, 'ExtensionSDKs', 'Microsoft.VCLibs',
'%0.1f' % self.vs_ver, 'References', 'CommonConfiguration',
'neutral'),
join(
ref, 'Windows.Networking.Connectivity.WwanContract',
'1.0.0.0'),
join(
self.si.WindowsSdkDir, 'ExtensionSDKs', 'Microsoft.VCLibs',
'%0.1f' % self.vs_ver, 'References', 'CommonConfiguration',
'neutral'),
]
return libpath
......
......@@ -47,13 +47,17 @@ class Installer:
"p = os.path.join(%(root)s, *%(pth)r)",
"importlib = has_mfs and __import__('importlib.util')",
"has_mfs and __import__('importlib.machinery')",
"m = has_mfs and "
(
"m = has_mfs and "
"sys.modules.setdefault(%(pkg)r, "
"importlib.util.module_from_spec("
"importlib.machinery.PathFinder.find_spec(%(pkg)r, "
"[os.path.dirname(p)])))",
"m = m or "
"sys.modules.setdefault(%(pkg)r, types.ModuleType(%(pkg)r))",
"importlib.util.module_from_spec("
"importlib.machinery.PathFinder.find_spec(%(pkg)r, "
"[os.path.dirname(p)])))"
),
(
"m = m or "
"sys.modules.setdefault(%(pkg)r, types.ModuleType(%(pkg)r))"
),
"mp = (m or []) and m.__dict__.setdefault('__path__',[])",
"(p not in mp) and mp.append(p)",
)
......
......@@ -46,7 +46,8 @@ __all__ = [
_SOCKET_TIMEOUT = 15
_tmpl = "setuptools/{setuptools.__version__} Python-urllib/{py_major}"
user_agent = _tmpl.format(py_major='{}.{}'.format(*sys.version_info), setuptools=setuptools)
user_agent = _tmpl.format(
py_major='{}.{}'.format(*sys.version_info), setuptools=setuptools)
def parse_requirement_arg(spec):
......@@ -1092,7 +1093,8 @@ def open_with_auth(url, opener=urllib.request.urlopen):
# copy of urllib.parse._splituser from Python 3.8
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('@')
return (user if delim else None), host
......
# This file originally from pip:
# https://github.com/pypa/pip/blob/8f4f15a5a95d7d5b511ceaee9ed261176c181970/src/pip/_internal/pep425tags.py
"""Generate and work with PEP 425 Compatibility Tags."""
from __future__ import absolute_import
import distutils.util
from distutils import log
import platform
import re
import sys
import sysconfig
import warnings
from collections import OrderedDict
from .extern import six
from . import glibc
_osx_arch_pat = re.compile(r'(.+)_(\d+)_(\d+)_(.+)')
def get_config_var(var):
try:
return sysconfig.get_config_var(var)
except IOError as e: # Issue #1074
warnings.warn("{}".format(e), RuntimeWarning)
return None
def get_abbr_impl():
"""Return abbreviated implementation name."""
if hasattr(sys, 'pypy_version_info'):
pyimpl = 'pp'
elif sys.platform.startswith('java'):
pyimpl = 'jy'
elif sys.platform == 'cli':
pyimpl = 'ip'
else:
pyimpl = 'cp'
return pyimpl
def get_impl_ver():
"""Return implementation version."""
impl_ver = get_config_var("py_version_nodot")
if not impl_ver or get_abbr_impl() == 'pp':
impl_ver = ''.join(map(str, get_impl_version_info()))
return impl_ver
def get_impl_version_info():
"""Return sys.version_info-like tuple for use in decrementing the minor
version."""
if get_abbr_impl() == 'pp':
# as per https://github.com/pypa/pip/issues/2882
return (sys.version_info[0], sys.pypy_version_info.major,
sys.pypy_version_info.minor)
else:
return sys.version_info[0], sys.version_info[1]
def get_impl_tag():
"""
Returns the Tag for this specific implementation.
"""
return "{}{}".format(get_abbr_impl(), get_impl_ver())
def get_flag(var, fallback, expected=True, warn=True):
"""Use a fallback method for determining SOABI flags if the needed config
var is unset or unavailable."""
val = get_config_var(var)
if val is None:
if warn:
log.debug("Config variable '%s' is unset, Python ABI tag may "
"be incorrect", var)
return fallback()
return val == expected
def get_abi_tag():
"""Return the ABI tag based on SOABI (if available) or emulate SOABI
(CPython 2, PyPy)."""
soabi = get_config_var('SOABI')
impl = get_abbr_impl()
if not soabi and impl in {'cp', 'pp'} and hasattr(sys, 'maxunicode'):
d = ''
m = ''
u = ''
if get_flag('Py_DEBUG',
lambda: hasattr(sys, 'gettotalrefcount'),
warn=(impl == 'cp')):
d = 'd'
if get_flag('WITH_PYMALLOC',
lambda: impl == 'cp',
warn=(impl == 'cp')):
m = 'm'
if get_flag('Py_UNICODE_SIZE',
lambda: sys.maxunicode == 0x10ffff,
expected=4,
warn=(impl == 'cp' and
six.PY2)) \
and six.PY2:
u = 'u'
abi = '%s%s%s%s%s' % (impl, get_impl_ver(), d, m, u)
elif soabi and soabi.startswith('cpython-'):
abi = 'cp' + soabi.split('-')[1]
elif soabi:
abi = soabi.replace('.', '_').replace('-', '_')
else:
abi = None
return abi
def _is_running_32bit():
return sys.maxsize == 2147483647
def get_platform():
"""Return our platform name 'win32', 'linux_x86_64'"""
if sys.platform == 'darwin':
# distutils.util.get_platform() returns the release based on the value
# of MACOSX_DEPLOYMENT_TARGET on which Python was built, which may
# be significantly older than the user's current machine.
release, _, machine = platform.mac_ver()
split_ver = release.split('.')
if machine == "x86_64" and _is_running_32bit():
machine = "i386"
elif machine == "ppc64" and _is_running_32bit():
machine = "ppc"
return 'macosx_{}_{}_{}'.format(split_ver[0], split_ver[1], machine)
# XXX remove distutils dependency
result = distutils.util.get_platform().replace('.', '_').replace('-', '_')
if result == "linux_x86_64" and _is_running_32bit():
# 32 bit Python program (running on a 64 bit Linux): pip should only
# install and run 32 bit compiled extensions in that case.
result = "linux_i686"
return result
def is_manylinux1_compatible():
# Only Linux, and only x86-64 / i686
if get_platform() not in {"linux_x86_64", "linux_i686"}:
return False
# Check for presence of _manylinux module
try:
import _manylinux
return bool(_manylinux.manylinux1_compatible)
except (ImportError, AttributeError):
# Fall through to heuristic check below
pass
# Check glibc version. CentOS 5 uses glibc 2.5.
return glibc.have_compatible_glibc(2, 5)
def get_darwin_arches(major, minor, machine):
"""Return a list of supported arches (including group arches) for
the given major, minor and machine architecture of a macOS machine.
"""
arches = []
def _supports_arch(major, minor, arch):
# Looking at the application support for macOS versions in the chart
# provided by https://en.wikipedia.org/wiki/OS_X#Versions it appears
# our timeline looks roughly like:
#
# 10.0 - Introduces ppc support.
# 10.4 - Introduces ppc64, i386, and x86_64 support, however the ppc64
# and x86_64 support is CLI only, and cannot be used for GUI
# applications.
# 10.5 - Extends ppc64 and x86_64 support to cover GUI applications.
# 10.6 - Drops support for ppc64
# 10.7 - Drops support for ppc
#
# Given that we do not know if we're installing a CLI or a GUI
# application, we must be conservative and assume it might be a GUI
# application and behave as if ppc64 and x86_64 support did not occur
# until 10.5.
#
# Note: The above information is taken from the "Application support"
# column in the chart not the "Processor support" since I believe
# that we care about what instruction sets an application can use
# not which processors the OS supports.
if arch == 'ppc':
return (major, minor) <= (10, 5)
if arch == 'ppc64':
return (major, minor) == (10, 5)
if arch == 'i386':
return (major, minor) >= (10, 4)
if arch == 'x86_64':
return (major, minor) >= (10, 5)
if arch in groups:
for garch in groups[arch]:
if _supports_arch(major, minor, garch):
return True
return False
groups = OrderedDict([
("fat", ("i386", "ppc")),
("intel", ("x86_64", "i386")),
("fat64", ("x86_64", "ppc64")),
("fat32", ("x86_64", "i386", "ppc")),
])
if _supports_arch(major, minor, machine):
arches.append(machine)
for garch in groups:
if machine in groups[garch] and _supports_arch(major, minor, garch):
arches.append(garch)
arches.append('universal')
return arches
def get_supported(versions=None, noarch=False, platform=None,
impl=None, abi=None):
"""Return a list of supported tags for each version specified in
`versions`.
:param versions: a list of string versions, of the form ["33", "32"],
or None. The first version will be assumed to support our ABI.
:param platform: specify the exact platform you want valid
tags for, or None. If None, use the local system platform.
:param impl: specify the exact implementation you want valid
tags for, or None. If None, use the local interpreter impl.
:param abi: specify the exact abi you want valid
tags for, or None. If None, use the local interpreter abi.
"""
supported = []
# Versions must be given with respect to the preference
if versions is None:
versions = []
version_info = get_impl_version_info()
major = version_info[:-1]
# Support all previous minor Python versions.
for minor in range(version_info[-1], -1, -1):
versions.append(''.join(map(str, major + (minor,))))
impl = impl or get_abbr_impl()
abis = []
abi = abi or get_abi_tag()
if abi:
abis[0:0] = [abi]
abi3s = set()
import imp
for suffix in imp.get_suffixes():
if suffix[0].startswith('.abi'):
abi3s.add(suffix[0].split('.', 2)[1])
abis.extend(sorted(list(abi3s)))
abis.append('none')
if not noarch:
arch = platform or get_platform()
if arch.startswith('macosx'):
# support macosx-10.6-intel on macosx-10.9-x86_64
match = _osx_arch_pat.match(arch)
if match:
name, major, minor, actual_arch = match.groups()
tpl = '{}_{}_%i_%s'.format(name, major)
arches = []
for m in reversed(range(int(minor) + 1)):
for a in get_darwin_arches(int(major), m, actual_arch):
arches.append(tpl % (m, a))
else:
# arch pattern didn't match (?!)
arches = [arch]
elif platform is None and is_manylinux1_compatible():
arches = [arch.replace('linux', 'manylinux1'), arch]
else:
arches = [arch]
# Current version, current API (built specifically for our Python):
for abi in abis:
for arch in arches:
supported.append(('%s%s' % (impl, versions[0]), abi, arch))
# abi3 modules compatible with older version of Python
for version in versions[1:]:
# abi3 was introduced in Python 3.2
if version in {'31', '30'}:
break
for abi in abi3s: # empty set if not Python 3
for arch in arches:
supported.append(("%s%s" % (impl, version), abi, arch))
# Has binaries, does not use the Python API:
for arch in arches:
supported.append(('py%s' % (versions[0][0]), 'none', arch))
# No abi / arch, but requires our implementation:
supported.append(('%s%s' % (impl, versions[0]), 'none', 'any'))
# Tagged specifically as being cross-version compatible
# (with just the major version specified)
supported.append(('%s%s' % (impl, versions[0][0]), 'none', 'any'))
# No abi / arch, generic Python
for i, version in enumerate(versions):
supported.append(('py%s' % (version,), 'none', 'any'))
if i == 0:
supported.append(('py%s' % (version[0]), 'none', 'any'))
return supported
implementation_tag = get_impl_tag()
......@@ -16,7 +16,7 @@ def get_all_headers(message, key):
if six.PY2:
def get_all_headers(message, key):
def get_all_headers(message, key): # noqa
return message.getheaders(key)
......
......@@ -13,6 +13,8 @@ from setuptools.extern import six
from setuptools.extern.six.moves import builtins, map
import pkg_resources.py31compat
from distutils.errors import DistutilsError
from pkg_resources import working_set
if sys.platform.startswith('java'):
import org.python.modules.posix.PosixModule as _os
......@@ -23,8 +25,6 @@ try:
except NameError:
_file = None
_open = open
from distutils.errors import DistutilsError
from pkg_resources import working_set
__all__ = [
......@@ -374,7 +374,7 @@ class AbstractSandbox:
if hasattr(os, 'devnull'):
_EXCEPTIONS = [os.devnull,]
_EXCEPTIONS = [os.devnull]
else:
_EXCEPTIONS = []
......@@ -466,7 +466,8 @@ class DirectorySandbox(AbstractSandbox):
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()]
)
......
......@@ -38,22 +38,24 @@ def __boot():
else:
raise ImportError("Couldn't find the real 'site' module")
known_paths = dict([(makepath(item)[1], 1) for item in sys.path]) # 2.2 comp
# 2.2 comp
known_paths = dict([(
makepath(item)[1], 1) for item in sys.path]) # noqa
oldpos = getattr(sys, '__egginsert', 0) # save old insertion position
sys.__egginsert = 0 # and reset the current one
for item in PYTHONPATH:
addsitedir(item)
addsitedir(item) # noqa
sys.__egginsert += oldpos # restore effective old position
d, nd = makepath(stdpath[0])
d, nd = makepath(stdpath[0]) # noqa
insert_at = None
new_path = []
for item in sys.path:
p, np = makepath(item)
p, np = makepath(item) # noqa
if np == nd and insert_at is None:
# We've hit the first 'system' path entry, so added entries go here
......
......@@ -35,7 +35,8 @@ try:
except AttributeError:
HTTPSHandler = HTTPSConnection = object
is_available = ssl is not None and object not in (HTTPSHandler, HTTPSConnection)
is_available = ssl is not None and object not in (
HTTPSHandler, HTTPSConnection)
try:
......@@ -85,8 +86,10 @@ if not match_hostname:
return dn.lower() == hostname.lower()
# RFC 6125, section 6.4.3, subitem 1.
# The client SHOULD NOT attempt to match a presented identifier in which
# the wildcard character comprises a label other than the left-most label.
# The client SHOULD NOT attempt to match a
# presented identifier in which the wildcard
# character comprises a label other than the
# left-most label.
if leftmost == '*':
# When '*' is a fragment by itself, it matches a non-empty dotless
# fragment.
......@@ -137,15 +140,16 @@ if not match_hostname:
return
dnsnames.append(value)
if len(dnsnames) > 1:
raise CertificateError("hostname %r "
"doesn't match either of %s"
raise CertificateError(
"hostname %r doesn't match either of %s"
% (hostname, ', '.join(map(repr, dnsnames))))
elif len(dnsnames) == 1:
raise CertificateError("hostname %r "
"doesn't match %r"
raise CertificateError(
"hostname %r doesn't match %r"
% (hostname, dnsnames[0]))
else:
raise CertificateError("no appropriate commonName or "
raise CertificateError(
"no appropriate commonName or "
"subjectAltName fields were found")
......@@ -158,7 +162,8 @@ class VerifyingHTTPSHandler(HTTPSHandler):
def https_open(self, req):
return self.do_open(
lambda host, **kw: VerifyingHTTPSConn(host, self.ca_bundle, **kw), req
lambda host, **kw: VerifyingHTTPSConn(host, self.ca_bundle, **kw),
req
)
......
......@@ -6,7 +6,7 @@ from setuptools.extern.six import PY2, PY3
__all__ = [
'fail_on_ascii', 'py2_only', 'py3_only'
'fail_on_ascii', 'py2_only', 'py3_only'
]
......
"""Basic http server for tests to simulate PyPI or custom indexes
"""
import os
import time
import threading
from setuptools.extern.six.moves import BaseHTTPServer, SimpleHTTPServer
from setuptools.extern.six.moves.urllib_parse import urljoin
from setuptools.extern.six.moves.urllib.request import pathname2url
class IndexServer(BaseHTTPServer.HTTPServer):
......@@ -69,6 +72,20 @@ class MockServer(BaseHTTPServer.HTTPServer, threading.Thread):
def run(self):
self.serve_forever()
@property
def netloc(self):
return 'localhost:%s' % self.server_port
@property
def url(self):
return 'http://localhost:%(server_port)s/' % vars(self)
return 'http://%s/' % self.netloc
def path_to_url(path, authority=None):
""" Convert a path to a file: URL. """
path = os.path.normpath(os.path.abspath(path))
base = 'file:'
if authority is not None:
base += '//' + authority
url = urljoin(base, pathname2url(path))
return url
......@@ -8,8 +8,7 @@ from setuptools.dist import Distribution
class TestBuildCLib:
@mock.patch(
'setuptools.command.build_clib.newer_pairwise_group'
)
'setuptools.command.build_clib.newer_pairwise_group')
def test_build_libraries(self, mock_newer):
dist = Distribution()
cmd = build_clib(dist)
......
......@@ -23,6 +23,7 @@ class BuildBackendBase:
self.env = env
self.backend_name = backend_name
class BuildBackend(BuildBackendBase):
"""PEP 517 Build Backend"""
......@@ -262,6 +263,27 @@ class TestBuildMetaBackend:
assert os.path.isfile(
os.path.join(os.path.abspath("out_sdist"), sdist_name))
def test_build_sdist_pyproject_toml_exists(self, tmpdir_cwd):
files = {
'setup.py': DALS("""
__import__('setuptools').setup(
name='foo',
version='0.0.0',
py_modules=['hello']
)"""),
'hello.py': '',
'pyproject.toml': DALS("""
[build-system]
requires = ["setuptools", "wheel"]
build-backend = "setuptools.build_meta
"""),
}
build_files(files)
build_backend = self.get_build_backend()
targz_path = build_backend.build_sdist("temp")
with tarfile.open(os.path.join("temp", targz_path)) as tar:
assert any('pyproject.toml' in name for name in tar.getnames())
def test_build_sdist_setup_py_exists(self, tmpdir_cwd):
# If build_sdist is called from a script other than setup.py,
# ensure setup.py is included
......@@ -385,6 +407,28 @@ class TestBuildMetaBackend:
assert expected == sorted(actual)
_sys_argv_0_passthrough = {
'setup.py': DALS("""
import os
import sys
__import__('setuptools').setup(
name='foo',
version='0.0.0',
)
sys_argv = os.path.abspath(sys.argv[0])
file_path = os.path.abspath('setup.py')
assert sys_argv == file_path
""")
}
def test_sys_argv_passthrough(self, tmpdir_cwd):
build_files(self._sys_argv_0_passthrough)
build_backend = self.get_build_backend()
with pytest.raises(AssertionError):
build_backend.build_sdist("temp")
class TestBuildMetaLegacyBackend(TestBuildMetaBackend):
backend_name = 'setuptools.build_meta:__legacy__'
......@@ -396,3 +440,9 @@ class TestBuildMetaLegacyBackend(TestBuildMetaBackend):
build_backend = self.get_build_backend()
build_backend.build_sdist("temp")
def test_sys_argv_passthrough(self, tmpdir_cwd):
build_files(self._sys_argv_0_passthrough)
build_backend = self.get_build_backend()
build_backend.build_sdist("temp")
......@@ -695,7 +695,7 @@ class TestOptions:
)
with get_dist(tmpdir) as dist:
assert set(dist.packages) == set(
['fake_package', 'fake_package.sub_two'])
['fake_package', 'fake_package.sub_two'])
@py2_only
def test_find_namespace_directive_fails_on_py2(self, tmpdir):
......@@ -748,7 +748,7 @@ class TestOptions:
)
with get_dist(tmpdir) as dist:
assert set(dist.packages) == {
'fake_package', 'fake_package.sub_two'
'fake_package', 'fake_package.sub_two'
}
def test_extras_require(self, tmpdir):
......@@ -881,7 +881,7 @@ class TestExternalSetters:
return None
@patch.object(_Distribution, '__init__', autospec=True)
def test_external_setters(self, mock_parent_init, tmpdir):
def test_external_setters(self, mock_parent_init, tmpdir):
mock_parent_init.side_effect = self._fake_distribution_init
dist = Distribution(attrs={
......
......@@ -95,7 +95,7 @@ class TestDevelop:
with io.open(fn) as init_file:
init = init_file.read().strip()
expected = 'print("foo")' if six.PY3 else 'print "foo"'
expected = 'print "foo"' if six.PY2 else 'print("foo")'
assert init == expected
def test_console_scripts(self, tmpdir):
......@@ -161,7 +161,7 @@ class TestNamespaces:
reason="https://github.com/pypa/setuptools/issues/851",
)
@pytest.mark.skipif(
platform.python_implementation() == 'PyPy' and six.PY3,
platform.python_implementation() == 'PyPy' and not six.PY2,
reason="https://github.com/pypa/setuptools/issues/1202",
)
def test_namespace_package_importable(self, tmpdir):
......
......@@ -284,7 +284,7 @@ def test_provides_extras_deterministic_order():
dist = Distribution(attrs)
assert dist.metadata.provides_extras == ['b', 'a']
CHECK_PACKAGE_DATA_TESTS = (
# Valid.
({
......@@ -309,7 +309,8 @@ CHECK_PACKAGE_DATA_TESTS = (
({
'hello': str('*.msg'),
}, (
"\"values of 'package_data' dict\" must be a list of strings (got '*.msg')"
"\"values of 'package_data' dict\" "
"must be a list of strings (got '*.msg')"
)),
# Invalid value type (generators are single use)
({
......@@ -321,10 +322,12 @@ CHECK_PACKAGE_DATA_TESTS = (
)
@pytest.mark.parametrize('package_data, expected_message', CHECK_PACKAGE_DATA_TESTS)
@pytest.mark.parametrize(
'package_data, expected_message', CHECK_PACKAGE_DATA_TESTS)
def test_check_package_data(package_data, expected_message):
if expected_message is None:
assert check_package_data(None, 'package_data', package_data) is None
else:
with pytest.raises(DistutilsSetupError, match=re.escape(expected_message)):
with pytest.raises(
DistutilsSetupError, match=re.escape(expected_message)):
check_package_data(None, str('package_data'), package_data)
......@@ -15,28 +15,29 @@ import distutils.errors
import io
import zipfile
import mock
from setuptools.command.easy_install import (
EasyInstallDeprecationWarning, ScriptWriter, WindowsScriptWriter,
)
import time
from setuptools.extern import six
from setuptools.extern.six.moves import urllib
import pytest
from setuptools import sandbox
from setuptools.sandbox import run_setup
import setuptools.command.easy_install as ei
from setuptools.command.easy_install import PthDistributions
from setuptools.command.easy_install import (
EasyInstallDeprecationWarning, ScriptWriter, PthDistributions,
WindowsScriptWriter,
)
from setuptools.command import easy_install as easy_install_pkg
from setuptools.dist import Distribution
from pkg_resources import normalize_path, working_set
from pkg_resources import Distribution as PRDistribution
import setuptools.tests.server
from setuptools.tests.server import MockServer, path_to_url
from setuptools.tests import fail_on_ascii
import pkg_resources
from . import contexts
from .files import build_files
from .textwrap import DALS
__metaclass__ = type
......@@ -440,35 +441,40 @@ def distutils_package():
yield
@pytest.fixture
def mock_index():
# set up a server which will simulate an alternate package index.
p_index = MockServer()
if p_index.server_port == 0:
# Some platforms (Jython) don't find a port to which to bind,
# so skip test for them.
pytest.skip("could not find a valid port")
p_index.start()
return p_index
class TestDistutilsPackage:
def test_bdist_egg_available_on_distutils_pkg(self, distutils_package):
run_setup('setup.py', ['bdist_egg'])
class TestSetupRequires:
def test_setup_requires_honors_fetch_params(self):
def test_setup_requires_honors_fetch_params(self, mock_index, monkeypatch):
"""
When easy_install installs a source distribution which specifies
setup_requires, it should honor the fetch parameters (such as
allow-hosts, index-url, and find-links).
index-url, and find-links).
"""
# set up a server which will simulate an alternate package index.
p_index = setuptools.tests.server.MockServer()
p_index.start()
netloc = 1
p_index_loc = urllib.parse.urlparse(p_index.url)[netloc]
if p_index_loc.endswith(':0'):
# Some platforms (Jython) don't find a port to which to bind,
# so skip this test for them.
return
monkeypatch.setenv(str('PIP_RETRIES'), str('0'))
monkeypatch.setenv(str('PIP_TIMEOUT'), str('0'))
with contexts.quiet():
# create an sdist that has a build-time dependency.
with TestSetupRequires.create_sdist() as dist_file:
with contexts.tempdir() as temp_install_dir:
with contexts.environment(PYTHONPATH=temp_install_dir):
ei_params = [
'--index-url', p_index.url,
'--allow-hosts', p_index_loc,
'--index-url', mock_index.url,
'--exclude-scripts',
'--install-dir', temp_install_dir,
dist_file,
......@@ -478,10 +484,8 @@ class TestSetupRequires:
# fail because it doesn't exist.
with pytest.raises(SystemExit):
easy_install_pkg.main(ei_params)
# there should have been two or three requests to the server
# (three happens on Python 3.3a)
assert 2 <= len(p_index.requests) <= 3
assert p_index.requests[0].path == '/does-not-exist/'
# there should have been one requests to the server
assert [r.path for r in mock_index.requests] == ['/does-not-exist/']
@staticmethod
@contextlib.contextmanager
......@@ -500,7 +504,9 @@ class TestSetupRequires:
version="1.0",
setup_requires = ['does-not-exist'],
)
"""))])
""")),
('setup.cfg', ''),
])
yield dist_path
use_setup_cfg = (
......@@ -623,7 +629,7 @@ class TestSetupRequires:
test_pkg = create_setup_requires_package(
temp_dir, setup_attrs=dict(version='attr: foobar.version'),
make_package=make_dependency_sdist,
use_setup_cfg=use_setup_cfg+('version',),
use_setup_cfg=use_setup_cfg + ('version',),
)
test_setup_py = os.path.join(test_pkg, 'setup.py')
with contexts.quiet() as (stdout, stderr):
......@@ -632,6 +638,208 @@ class TestSetupRequires:
assert len(lines) > 0
assert lines[-1].strip() == '42'
def test_setup_requires_honors_pip_env(self, mock_index, monkeypatch):
monkeypatch.setenv(str('PIP_RETRIES'), str('0'))
monkeypatch.setenv(str('PIP_TIMEOUT'), str('0'))
monkeypatch.setenv(str('PIP_INDEX_URL'), mock_index.url)
with contexts.save_pkg_resources_state():
with contexts.tempdir() as temp_dir:
test_pkg = create_setup_requires_package(
temp_dir, 'python-xlib', '0.19',
setup_attrs=dict(dependency_links=[]))
test_setup_cfg = os.path.join(test_pkg, 'setup.cfg')
with open(test_setup_cfg, 'w') as fp:
fp.write(DALS(
'''
[easy_install]
index_url = https://pypi.org/legacy/
'''))
test_setup_py = os.path.join(test_pkg, 'setup.py')
with pytest.raises(distutils.errors.DistutilsError):
run_setup(test_setup_py, [str('--version')])
assert len(mock_index.requests) == 1
assert mock_index.requests[0].path == '/python-xlib/'
def test_setup_requires_with_pep508_url(self, mock_index, monkeypatch):
monkeypatch.setenv(str('PIP_RETRIES'), str('0'))
monkeypatch.setenv(str('PIP_TIMEOUT'), str('0'))
monkeypatch.setenv(str('PIP_INDEX_URL'), mock_index.url)
with contexts.save_pkg_resources_state():
with contexts.tempdir() as temp_dir:
dep_sdist = os.path.join(temp_dir, 'dep.tar.gz')
make_trivial_sdist(dep_sdist, 'dependency', '42')
dep_url = path_to_url(dep_sdist, authority='localhost')
test_pkg = create_setup_requires_package(
temp_dir,
# Ignored (overriden by setup_attrs)
'python-xlib', '0.19',
setup_attrs=dict(
setup_requires='dependency @ %s' % dep_url))
test_setup_py = os.path.join(test_pkg, 'setup.py')
run_setup(test_setup_py, [str('--version')])
assert len(mock_index.requests) == 0
def test_setup_requires_with_allow_hosts(self, mock_index):
''' The `allow-hosts` option in not supported anymore. '''
with contexts.save_pkg_resources_state():
with contexts.tempdir() as temp_dir:
test_pkg = os.path.join(temp_dir, 'test_pkg')
test_setup_py = os.path.join(test_pkg, 'setup.py')
test_setup_cfg = os.path.join(test_pkg, 'setup.cfg')
os.mkdir(test_pkg)
with open(test_setup_py, 'w') as fp:
fp.write(DALS(
'''
from setuptools import setup
setup(setup_requires='python-xlib')
'''))
with open(test_setup_cfg, 'w') as fp:
fp.write(DALS(
'''
[easy_install]
allow_hosts = *
'''))
with pytest.raises(distutils.errors.DistutilsError):
run_setup(test_setup_py, [str('--version')])
assert len(mock_index.requests) == 0
def test_setup_requires_with_python_requires(self, monkeypatch, tmpdir):
''' Check `python_requires` is honored. '''
monkeypatch.setenv(str('PIP_RETRIES'), str('0'))
monkeypatch.setenv(str('PIP_TIMEOUT'), str('0'))
monkeypatch.setenv(str('PIP_NO_INDEX'), str('1'))
monkeypatch.setenv(str('PIP_VERBOSE'), str('1'))
dep_1_0_sdist = 'dep-1.0.tar.gz'
dep_1_0_url = path_to_url(str(tmpdir / dep_1_0_sdist))
dep_1_0_python_requires = '>=2.7'
make_python_requires_sdist(
str(tmpdir / dep_1_0_sdist), 'dep', '1.0', dep_1_0_python_requires)
dep_2_0_sdist = 'dep-2.0.tar.gz'
dep_2_0_url = path_to_url(str(tmpdir / dep_2_0_sdist))
dep_2_0_python_requires = '!=' + '.'.join(
map(str, sys.version_info[:2])) + '.*'
make_python_requires_sdist(
str(tmpdir / dep_2_0_sdist), 'dep', '2.0', dep_2_0_python_requires)
index = tmpdir / 'index.html'
index.write_text(DALS(
'''
<!DOCTYPE html>
<html><head><title>Links for dep</title></head>
<body>
<h1>Links for dep</h1>
<a href="{dep_1_0_url}" data-requires-python="{dep_1_0_python_requires}">{dep_1_0_sdist}</a><br/>
<a href="{dep_2_0_url}" data-requires-python="{dep_2_0_python_requires}">{dep_2_0_sdist}</a><br/>
</body>
</html>
''').format( # noqa
dep_1_0_url=dep_1_0_url,
dep_1_0_sdist=dep_1_0_sdist,
dep_1_0_python_requires=dep_1_0_python_requires,
dep_2_0_url=dep_2_0_url,
dep_2_0_sdist=dep_2_0_sdist,
dep_2_0_python_requires=dep_2_0_python_requires,
), 'utf-8')
index_url = path_to_url(str(index))
with contexts.save_pkg_resources_state():
test_pkg = create_setup_requires_package(
str(tmpdir),
'python-xlib', '0.19', # Ignored (overriden by setup_attrs).
setup_attrs=dict(
setup_requires='dep', dependency_links=[index_url]))
test_setup_py = os.path.join(test_pkg, 'setup.py')
run_setup(test_setup_py, [str('--version')])
eggs = list(map(str, pkg_resources.find_distributions(
os.path.join(test_pkg, '.eggs'))))
assert eggs == ['dep 1.0']
@pytest.mark.parametrize(
'use_legacy_installer,with_dependency_links_in_setup_py',
itertools.product((False, True), (False, True)))
def test_setup_requires_with_find_links_in_setup_cfg(
self, monkeypatch, use_legacy_installer,
with_dependency_links_in_setup_py):
monkeypatch.setenv(str('PIP_RETRIES'), str('0'))
monkeypatch.setenv(str('PIP_TIMEOUT'), str('0'))
with contexts.save_pkg_resources_state():
with contexts.tempdir() as temp_dir:
make_trivial_sdist(
os.path.join(temp_dir, 'python-xlib-42.tar.gz'),
'python-xlib',
'42')
test_pkg = os.path.join(temp_dir, 'test_pkg')
test_setup_py = os.path.join(test_pkg, 'setup.py')
test_setup_cfg = os.path.join(test_pkg, 'setup.cfg')
os.mkdir(test_pkg)
with open(test_setup_py, 'w') as fp:
if with_dependency_links_in_setup_py:
dependency_links = [os.path.join(temp_dir, 'links')]
else:
dependency_links = []
fp.write(DALS(
'''
from setuptools import installer, setup
if {use_legacy_installer}:
installer.fetch_build_egg = installer._legacy_fetch_build_egg
setup(setup_requires='python-xlib==42',
dependency_links={dependency_links!r})
''').format(use_legacy_installer=use_legacy_installer, # noqa
dependency_links=dependency_links))
with open(test_setup_cfg, 'w') as fp:
fp.write(DALS(
'''
[easy_install]
index_url = {index_url}
find_links = {find_links}
''').format(index_url=os.path.join(temp_dir, 'index'),
find_links=temp_dir))
run_setup(test_setup_py, [str('--version')])
def test_setup_requires_with_transitive_extra_dependency(
self, monkeypatch):
# Use case: installing a package with a build dependency on
# an already installed `dep[extra]`, which in turn depends
# on `extra_dep` (whose is not already installed).
with contexts.save_pkg_resources_state():
with contexts.tempdir() as temp_dir:
# Create source distribution for `extra_dep`.
make_trivial_sdist(
os.path.join(temp_dir, 'extra_dep-1.0.tar.gz'),
'extra_dep', '1.0')
# Create source tree for `dep`.
dep_pkg = os.path.join(temp_dir, 'dep')
os.mkdir(dep_pkg)
build_files({
'setup.py':
DALS("""
import setuptools
setuptools.setup(
name='dep', version='2.0',
extras_require={'extra': ['extra_dep']},
)
"""),
'setup.cfg': '',
}, prefix=dep_pkg)
# "Install" dep.
run_setup(
os.path.join(dep_pkg, 'setup.py'), [str('dist_info')])
working_set.add_entry(dep_pkg)
# Create source tree for test package.
test_pkg = os.path.join(temp_dir, 'test_pkg')
test_setup_py = os.path.join(test_pkg, 'setup.py')
os.mkdir(test_pkg)
with open(test_setup_py, 'w') as fp:
fp.write(DALS(
'''
from setuptools import installer, setup
setup(setup_requires='dep[extra]')
'''))
# Check...
monkeypatch.setenv(str('PIP_FIND_LINKS'), str(temp_dir))
monkeypatch.setenv(str('PIP_NO_INDEX'), str('1'))
monkeypatch.setenv(str('PIP_RETRIES'), str('0'))
monkeypatch.setenv(str('PIP_TIMEOUT'), str('0'))
run_setup(test_setup_py, [str('--version')])
def make_trivial_sdist(dist_path, distname, version):
"""
......@@ -647,7 +855,9 @@ def make_trivial_sdist(dist_path, distname, version):
name=%r,
version=%r
)
""" % (distname, version)))])
""" % (distname, version))),
('setup.cfg', ''),
])
def make_nspkg_sdist(dist_path, distname, version):
......@@ -683,12 +893,32 @@ def make_nspkg_sdist(dist_path, distname, version):
make_sdist(dist_path, files)
def make_python_requires_sdist(dist_path, distname, version, python_requires):
make_sdist(dist_path, [
(
'setup.py',
DALS("""\
import setuptools
setuptools.setup(
name={name!r},
version={version!r},
python_requires={python_requires!r},
)
""").format(
name=distname, version=version,
python_requires=python_requires)),
('setup.cfg', ''),
])
def make_sdist(dist_path, files):
"""
Create a simple sdist tarball at dist_path, containing the files
listed in ``files`` as ``(filename, content)`` tuples.
"""
# Distributions with only one file don't play well with pip.
assert len(files) > 1
with tarfile.open(dist_path, 'w:gz') as dist:
for filename, content in files:
file_bytes = io.BytesIO(content.encode('utf-8'))
......@@ -721,8 +951,8 @@ def create_setup_requires_package(path, distname='foobar', version='0.1',
test_pkg = os.path.join(path, 'test_pkg')
os.mkdir(test_pkg)
# setup.cfg
if use_setup_cfg:
test_setup_cfg = os.path.join(test_pkg, 'setup.cfg')
options = []
metadata = []
for name in use_setup_cfg:
......@@ -734,27 +964,29 @@ def create_setup_requires_package(path, distname='foobar', version='0.1',
if isinstance(value, (tuple, list)):
value = ';'.join(value)
section.append('%s: %s' % (name, value))
with open(test_setup_cfg, 'w') as f:
f.write(DALS(
"""
[metadata]
{metadata}
[options]
{options}
"""
).format(
options='\n'.join(options),
metadata='\n'.join(metadata),
))
test_setup_py = os.path.join(test_pkg, 'setup.py')
test_setup_cfg_contents = DALS(
"""
[metadata]
{metadata}
[options]
{options}
"""
).format(
options='\n'.join(options),
metadata='\n'.join(metadata),
)
else:
test_setup_cfg_contents = ''
with open(os.path.join(test_pkg, 'setup.cfg'), 'w') as f:
f.write(test_setup_cfg_contents)
# setup.py
if setup_py_template is None:
setup_py_template = DALS("""\
import setuptools
setuptools.setup(**%r)
""")
with open(test_setup_py, 'w') as f:
with open(os.path.join(test_pkg, 'setup.py'), 'w') as f:
f.write(setup_py_template % test_setup_attrs)
foobar_path = os.path.join(path, '%s-%s.tar.gz' % (distname, version))
......
......@@ -524,28 +524,28 @@ class TestEggInfo:
[metadata]
license_file = LICENSE
"""),
'LICENSE': DALS("Test license")
}, True), # with license
'LICENSE': "Test license"
}, True), # with license
({
'setup.cfg': DALS("""
[metadata]
license_file = INVALID_LICENSE
"""),
'LICENSE': DALS("Test license")
}, False), # with an invalid license
'LICENSE': "Test license"
}, False), # with an invalid license
({
'setup.cfg': DALS("""
"""),
'LICENSE': DALS("Test license")
}, False), # no license_file attribute
'LICENSE': "Test license"
}, False), # no license_file attribute
({
'setup.cfg': DALS("""
[metadata]
license_file = LICENSE
"""),
'MANIFEST.in': DALS("exclude LICENSE"),
'LICENSE': DALS("Test license")
}, False) # license file is manually excluded
'MANIFEST.in': "exclude LICENSE",
'LICENSE': "Test license"
}, False) # license file is manually excluded
])
def test_setup_cfg_license_file(
self, tmpdir_cwd, env, files, license_in_sources):
......@@ -565,7 +565,211 @@ class TestEggInfo:
assert 'LICENSE' in sources_text
else:
assert 'LICENSE' not in sources_text
assert 'INVALID_LICENSE' not in sources_text # for invalid license test
# for invalid license test
assert 'INVALID_LICENSE' not in sources_text
@pytest.mark.parametrize("files, incl_licenses, excl_licenses", [
({
'setup.cfg': DALS("""
[metadata]
license_files =
LICENSE-ABC
LICENSE-XYZ
"""),
'LICENSE-ABC': "ABC license",
'LICENSE-XYZ': "XYZ license"
}, ['LICENSE-ABC', 'LICENSE-XYZ'], []), # with licenses
({
'setup.cfg': DALS("""
[metadata]
license_files = LICENSE-ABC, LICENSE-XYZ
"""),
'LICENSE-ABC': "ABC license",
'LICENSE-XYZ': "XYZ license"
}, ['LICENSE-ABC', 'LICENSE-XYZ'], []), # with commas
({
'setup.cfg': DALS("""
[metadata]
license_files =
LICENSE-ABC
"""),
'LICENSE-ABC': "ABC license",
'LICENSE-XYZ': "XYZ license"
}, ['LICENSE-ABC'], ['LICENSE-XYZ']), # with one license
({
'setup.cfg': DALS("""
[metadata]
license_files =
"""),
'LICENSE-ABC': "ABC license",
'LICENSE-XYZ': "XYZ license"
}, [], ['LICENSE-ABC', 'LICENSE-XYZ']), # empty
({
'setup.cfg': DALS("""
[metadata]
license_files = LICENSE-XYZ
"""),
'LICENSE-ABC': "ABC license",
'LICENSE-XYZ': "XYZ license"
}, ['LICENSE-XYZ'], ['LICENSE-ABC']), # on same line
({
'setup.cfg': DALS("""
[metadata]
license_files =
LICENSE-ABC
INVALID_LICENSE
"""),
'LICENSE-ABC': "Test license"
}, ['LICENSE-ABC'], ['INVALID_LICENSE']), # with an invalid license
({
'setup.cfg': DALS("""
"""),
'LICENSE': "Test license"
}, [], ['LICENSE']), # no license_files attribute
({
'setup.cfg': DALS("""
[metadata]
license_files = LICENSE
"""),
'MANIFEST.in': "exclude LICENSE",
'LICENSE': "Test license"
}, [], ['LICENSE']), # license file is manually excluded
({
'setup.cfg': DALS("""
[metadata]
license_files =
LICENSE-ABC
LICENSE-XYZ
"""),
'MANIFEST.in': "exclude LICENSE-XYZ",
'LICENSE-ABC': "ABC license",
'LICENSE-XYZ': "XYZ license"
}, ['LICENSE-ABC'], ['LICENSE-XYZ']) # subset is manually excluded
])
def test_setup_cfg_license_files(
self, tmpdir_cwd, env, files, incl_licenses, excl_licenses):
self._create_project()
build_files(files)
environment.run_setup_py(
cmd=['egg_info'],
pypath=os.pathsep.join([env.paths['lib'], str(tmpdir_cwd)])
)
egg_info_dir = os.path.join('.', 'foo.egg-info')
with open(os.path.join(egg_info_dir, 'SOURCES.txt')) as sources_file:
sources_lines = list(line.strip() for line in sources_file)
for lf in incl_licenses:
assert sources_lines.count(lf) == 1
for lf in excl_licenses:
assert sources_lines.count(lf) == 0
@pytest.mark.parametrize("files, incl_licenses, excl_licenses", [
({
'setup.cfg': DALS("""
[metadata]
license_file =
license_files =
"""),
'LICENSE-ABC': "ABC license",
'LICENSE-XYZ': "XYZ license"
}, [], ['LICENSE-ABC', 'LICENSE-XYZ']), # both empty
({
'setup.cfg': DALS("""
[metadata]
license_file =
LICENSE-ABC
LICENSE-XYZ
"""),
'LICENSE-ABC': "ABC license",
'LICENSE-XYZ': "XYZ license"
# license_file is still singular
}, [], ['LICENSE-ABC', 'LICENSE-XYZ']),
({
'setup.cfg': DALS("""
[metadata]
license_file = LICENSE-ABC
license_files =
LICENSE-XYZ
LICENSE-PQR
"""),
'LICENSE-ABC': "ABC license",
'LICENSE-PQR': "PQR license",
'LICENSE-XYZ': "XYZ license"
}, ['LICENSE-ABC', 'LICENSE-PQR', 'LICENSE-XYZ'], []), # combined
({
'setup.cfg': DALS("""
[metadata]
license_file = LICENSE-ABC
license_files =
LICENSE-ABC
LICENSE-XYZ
LICENSE-PQR
"""),
'LICENSE-ABC': "ABC license",
'LICENSE-PQR': "PQR license",
'LICENSE-XYZ': "XYZ license"
# duplicate license
}, ['LICENSE-ABC', 'LICENSE-PQR', 'LICENSE-XYZ'], []),
({
'setup.cfg': DALS("""
[metadata]
license_file = LICENSE-ABC
license_files =
LICENSE-XYZ
"""),
'LICENSE-ABC': "ABC license",
'LICENSE-PQR': "PQR license",
'LICENSE-XYZ': "XYZ license"
# combined subset
}, ['LICENSE-ABC', 'LICENSE-XYZ'], ['LICENSE-PQR']),
({
'setup.cfg': DALS("""
[metadata]
license_file = LICENSE-ABC
license_files =
LICENSE-XYZ
LICENSE-PQR
"""),
'LICENSE-PQR': "Test license"
# with invalid licenses
}, ['LICENSE-PQR'], ['LICENSE-ABC', 'LICENSE-XYZ']),
({
'setup.cfg': DALS("""
[metadata]
license_file = LICENSE-ABC
license_files =
LICENSE-PQR
LICENSE-XYZ
"""),
'MANIFEST.in': "exclude LICENSE-ABC\nexclude LICENSE-PQR",
'LICENSE-ABC': "ABC license",
'LICENSE-PQR': "PQR license",
'LICENSE-XYZ': "XYZ license"
# manually excluded
}, ['LICENSE-XYZ'], ['LICENSE-ABC', 'LICENSE-PQR'])
])
def test_setup_cfg_license_file_license_files(
self, tmpdir_cwd, env, files, incl_licenses, excl_licenses):
self._create_project()
build_files(files)
environment.run_setup_py(
cmd=['egg_info'],
pypath=os.pathsep.join([env.paths['lib'], str(tmpdir_cwd)])
)
egg_info_dir = os.path.join('.', 'foo.egg-info')
with open(os.path.join(egg_info_dir, 'SOURCES.txt')) as sources_file:
sources_lines = list(line.strip() for line in sources_file)
for lf in incl_licenses:
assert sources_lines.count(lf) == 1
for lf in excl_licenses:
assert sources_lines.count(lf) == 0
def test_long_description_content_type(self, tmpdir_cwd, env):
# Test that specifying a `long_description_content_type` keyword arg to
......
import warnings
import pytest
from setuptools.glibc import check_glibc_version
__metaclass__ = type
@pytest.fixture(params=[
"2.20",
# used by "linaro glibc", see gh-3588
"2.20-2014.11",
# weird possibilities that I just made up
"2.20+dev",
"2.20-custom",
"2.20.1",
])
def two_twenty(request):
return request.param
@pytest.fixture(params=["asdf", "", "foo.bar"])
def bad_string(request):
return request.param
class TestGlibc:
def test_manylinux1_check_glibc_version(self, two_twenty):
"""
Test that the check_glibc_version function is robust against weird
glibc version strings.
"""
assert check_glibc_version(two_twenty, 2, 15)
assert check_glibc_version(two_twenty, 2, 20)
assert not check_glibc_version(two_twenty, 2, 21)
assert not check_glibc_version(two_twenty, 3, 15)
assert not check_glibc_version(two_twenty, 1, 15)
def test_bad_versions(self, bad_string):
"""
For unparseable strings, warn and return False
"""
with warnings.catch_warnings(record=True) as ws:
warnings.filterwarnings("always")
assert not check_glibc_version(bad_string, 2, 5)
for w in ws:
if "Expected glibc version with" in str(w.message):
break
else:
# Didn't find the warning we were expecting
assert False
......@@ -64,7 +64,7 @@ def install_context(request, tmpdir, monkeypatch):
monkeypatch.setattr('site.USER_BASE', user_base.strpath)
monkeypatch.setattr('site.USER_SITE', user_site.strpath)
monkeypatch.setattr('sys.path', sys.path + [install_dir.strpath])
monkeypatch.setenv('PYTHONPATH', os.path.pathsep.join(sys.path))
monkeypatch.setenv(str('PYTHONPATH'), str(os.path.pathsep.join(sys.path)))
# Set up the command for performing the installation.
dist = Distribution()
......
import sys
import pytest
from mock import patch
from setuptools import pep425tags
__metaclass__ = type
class TestPEP425Tags:
def mock_get_config_var(self, **kwd):
"""
Patch sysconfig.get_config_var for arbitrary keys.
"""
get_config_var = pep425tags.sysconfig.get_config_var
def _mock_get_config_var(var):
if var in kwd:
return kwd[var]
return get_config_var(var)
return _mock_get_config_var
def abi_tag_unicode(self, flags, config_vars):
"""
Used to test ABI tags, verify correct use of the `u` flag
"""
config_vars.update({'SOABI': None})
base = pep425tags.get_abbr_impl() + pep425tags.get_impl_ver()
if sys.version_info < (3, 3):
config_vars.update({'Py_UNICODE_SIZE': 2})
mock_gcf = self.mock_get_config_var(**config_vars)
with patch(
'setuptools.pep425tags.sysconfig.get_config_var',
mock_gcf):
abi_tag = pep425tags.get_abi_tag()
assert abi_tag == base + flags
config_vars.update({'Py_UNICODE_SIZE': 4})
mock_gcf = self.mock_get_config_var(**config_vars)
with patch('setuptools.pep425tags.sysconfig.get_config_var',
mock_gcf):
abi_tag = pep425tags.get_abi_tag()
assert abi_tag == base + flags + 'u'
else:
# On Python >= 3.3, UCS-4 is essentially permanently enabled, and
# Py_UNICODE_SIZE is None. SOABI on these builds does not include
# the 'u' so manual SOABI detection should not do so either.
config_vars.update({'Py_UNICODE_SIZE': None})
mock_gcf = self.mock_get_config_var(**config_vars)
with patch('setuptools.pep425tags.sysconfig.get_config_var',
mock_gcf):
abi_tag = pep425tags.get_abi_tag()
assert abi_tag == base + flags
def test_broken_sysconfig(self):
"""
Test that pep425tags still works when sysconfig is broken.
Can be a problem on Python 2.7
Issue #1074.
"""
def raises_ioerror(var):
raise IOError("I have the wrong path!")
with patch('setuptools.pep425tags.sysconfig.get_config_var',
raises_ioerror):
with pytest.warns(RuntimeWarning):
assert len(pep425tags.get_supported())
def test_no_hyphen_tag(self):
"""
Test that no tag contains a hyphen.
"""
mock_gcf = self.mock_get_config_var(SOABI='cpython-35m-darwin')
with patch('setuptools.pep425tags.sysconfig.get_config_var',
mock_gcf):
supported = pep425tags.get_supported()
for (py, abi, plat) in supported:
assert '-' not in py
assert '-' not in abi
assert '-' not in plat
def test_manual_abi_noflags(self):
"""
Test that no flags are set on a non-PyDebug, non-Pymalloc ABI tag.
"""
self.abi_tag_unicode('', {'Py_DEBUG': False, 'WITH_PYMALLOC': False})
def test_manual_abi_d_flag(self):
"""
Test that the `d` flag is set on a PyDebug, non-Pymalloc ABI tag.
"""
self.abi_tag_unicode('d', {'Py_DEBUG': True, 'WITH_PYMALLOC': False})
def test_manual_abi_m_flag(self):
"""
Test that the `m` flag is set on a non-PyDebug, Pymalloc ABI tag.
"""
self.abi_tag_unicode('m', {'Py_DEBUG': False, 'WITH_PYMALLOC': True})
def test_manual_abi_dm_flags(self):
"""
Test that the `dm` flags are set on a PyDebug, Pymalloc ABI tag.
"""
self.abi_tag_unicode('dm', {'Py_DEBUG': True, 'WITH_PYMALLOC': True})
class TestManylinux1Tags:
@patch('setuptools.pep425tags.get_platform', lambda: 'linux_x86_64')
@patch('setuptools.glibc.have_compatible_glibc',
lambda major, minor: True)
def test_manylinux1_compatible_on_linux_x86_64(self):
"""
Test that manylinux1 is enabled on linux_x86_64
"""
assert pep425tags.is_manylinux1_compatible()
@patch('setuptools.pep425tags.get_platform', lambda: 'linux_i686')
@patch('setuptools.glibc.have_compatible_glibc',
lambda major, minor: True)
def test_manylinux1_compatible_on_linux_i686(self):
"""
Test that manylinux1 is enabled on linux_i686
"""
assert pep425tags.is_manylinux1_compatible()
@patch('setuptools.pep425tags.get_platform', lambda: 'linux_x86_64')
@patch('setuptools.glibc.have_compatible_glibc',
lambda major, minor: False)
def test_manylinux1_2(self):
"""
Test that manylinux1 is disabled with incompatible glibc
"""
assert not pep425tags.is_manylinux1_compatible()
@patch('setuptools.pep425tags.get_platform', lambda: 'arm6vl')
@patch('setuptools.glibc.have_compatible_glibc',
lambda major, minor: True)
def test_manylinux1_3(self):
"""
Test that manylinux1 is disabled on arm6vl
"""
assert not pep425tags.is_manylinux1_compatible()
@patch('setuptools.pep425tags.get_platform', lambda: 'linux_x86_64')
@patch('setuptools.glibc.have_compatible_glibc',
lambda major, minor: True)
@patch('sys.platform', 'linux2')
def test_manylinux1_tag_is_first(self):
"""
Test that the more specific tag manylinux1 comes first.
"""
groups = {}
for pyimpl, abi, arch in pep425tags.get_supported():
groups.setdefault((pyimpl, abi), []).append(arch)
for arches in groups.values():
if arches == ['any']:
continue
# Expect the most specific arch first:
if len(arches) == 3:
assert arches == ['manylinux1_x86_64', 'linux_x86_64', 'any']
else:
assert arches == ['manylinux1_x86_64', 'linux_x86_64']
# -*- coding: utf-8 -*-
"""sdist tests"""
from __future__ import print_function, unicode_literals
import os
import shutil
import sys
import tempfile
import unicodedata
......@@ -50,7 +51,7 @@ def quiet():
# Convert to POSIX path
def posix(path):
if six.PY3 and not isinstance(path, str):
if not six.PY2 and not isinstance(path, str):
return path.replace(os.sep.encode('ascii'), b'/')
else:
return path.replace(os.sep, '/')
......@@ -89,30 +90,28 @@ fail_on_latin1_encoded_filenames = pytest.mark.xfail(
)
def touch(path):
path.write_text('', encoding='utf-8')
class TestSdistTest:
def setup_method(self, method):
self.temp_dir = tempfile.mkdtemp()
with open(os.path.join(self.temp_dir, 'setup.py'), 'w') as f:
f.write(SETUP_PY)
@pytest.fixture(autouse=True)
def source_dir(self, tmpdir):
(tmpdir / 'setup.py').write_text(SETUP_PY, encoding='utf-8')
# Set up the rest of the test package
test_pkg = os.path.join(self.temp_dir, 'sdist_test')
os.mkdir(test_pkg)
data_folder = os.path.join(self.temp_dir, "d")
os.mkdir(data_folder)
test_pkg = tmpdir / 'sdist_test'
test_pkg.mkdir()
data_folder = tmpdir / 'd'
data_folder.mkdir()
# *.rst was not included in package_data, so c.rst should not be
# automatically added to the manifest when not under version control
for fname in ['__init__.py', 'a.txt', 'b.txt', 'c.rst',
os.path.join(data_folder, "e.dat")]:
# Just touch the files; their contents are irrelevant
open(os.path.join(test_pkg, fname), 'w').close()
self.old_cwd = os.getcwd()
os.chdir(self.temp_dir)
for fname in ['__init__.py', 'a.txt', 'b.txt', 'c.rst']:
touch(test_pkg / fname)
touch(data_folder / 'e.dat')
def teardown_method(self, method):
os.chdir(self.old_cwd)
shutil.rmtree(self.temp_dir)
with tmpdir.as_cwd():
yield
def test_package_data_in_sdist(self):
"""Regression test for pull request #4: ensures that files listed in
......@@ -175,14 +174,14 @@ class TestSdistTest:
manifest = cmd.filelist.files
assert 'setup.py' not in manifest
def test_defaults_case_sensitivity(self):
def test_defaults_case_sensitivity(self, tmpdir):
"""
Make sure default files (README.*, etc.) are added in a case-sensitive
way to avoid problems with packages built on Windows.
"""
open(os.path.join(self.temp_dir, 'readme.rst'), 'w').close()
open(os.path.join(self.temp_dir, 'SETUP.cfg'), 'w').close()
touch(tmpdir / 'readme.rst')
touch(tmpdir / 'SETUP.cfg')
dist = Distribution(SETUP_ATTRS)
# the extension deliberately capitalized for this test
......@@ -230,10 +229,6 @@ class TestSdistTest:
u_contents = contents.decode('UTF-8')
# The manifest should contain the UTF-8 filename
if six.PY2:
fs_enc = sys.getfilesystemencoding()
filename = filename.decode(fs_enc)
assert posix(filename) in u_contents
@py3_only
......@@ -334,7 +329,7 @@ class TestSdistTest:
cmd.read_manifest()
# The filelist should contain the UTF-8 filename
if six.PY3:
if not six.PY2:
filename = filename.decode('utf-8')
assert filename in cmd.filelist.files
......@@ -374,7 +369,7 @@ class TestSdistTest:
@fail_on_latin1_encoded_filenames
def test_sdist_with_utf8_encoded_filename(self):
# Test for #303.
dist = Distribution(SETUP_ATTRS)
dist = Distribution(self.make_strings(SETUP_ATTRS))
dist.script_name = 'setup.py'
cmd = sdist(dist)
cmd.ensure_finalized()
......@@ -388,7 +383,7 @@ class TestSdistTest:
if sys.platform == 'darwin':
filename = decompose(filename)
if six.PY3:
if not six.PY2:
fs_enc = sys.getfilesystemencoding()
if sys.platform == 'win32':
......@@ -405,10 +400,19 @@ class TestSdistTest:
else:
assert filename in cmd.filelist.files
@classmethod
def make_strings(cls, item):
if isinstance(item, dict):
return {
key: cls.make_strings(value) for key, value in item.items()}
if isinstance(item, list):
return list(map(cls.make_strings, item))
return str(item)
@fail_on_latin1_encoded_filenames
def test_sdist_with_latin1_encoded_filename(self):
# Test for #303.
dist = Distribution(SETUP_ATTRS)
dist = Distribution(self.make_strings(SETUP_ATTRS))
dist.script_name = 'setup.py'
cmd = sdist(dist)
cmd.ensure_finalized()
......@@ -421,7 +425,19 @@ class TestSdistTest:
with quiet():
cmd.run()
if six.PY3:
if six.PY2:
# Under Python 2 there seems to be no decoded string in the
# filelist. However, due to decode and encoding of the
# file name to get utf-8 Manifest the latin1 maybe excluded
try:
# fs_enc should match how one is expect the decoding to
# be proformed for the manifest output.
fs_enc = sys.getfilesystemencoding()
filename.decode(fs_enc)
assert filename in cmd.filelist.files
except UnicodeDecodeError:
filename not in cmd.filelist.files
else:
# not all windows systems have a default FS encoding of cp1252
if sys.platform == 'win32':
# Latin-1 is similar to Windows-1252 however
......@@ -436,18 +452,36 @@ class TestSdistTest:
# The Latin-1 filename should have been skipped
filename = filename.decode('latin-1')
filename not in cmd.filelist.files
else:
# Under Python 2 there seems to be no decoded string in the
# filelist. However, due to decode and encoding of the
# file name to get utf-8 Manifest the latin1 maybe excluded
try:
# fs_enc should match how one is expect the decoding to
# be proformed for the manifest output.
fs_enc = sys.getfilesystemencoding()
filename.decode(fs_enc)
assert filename in cmd.filelist.files
except UnicodeDecodeError:
filename not in cmd.filelist.files
def test_pyproject_toml_in_sdist(self, tmpdir):
"""
Check if pyproject.toml is included in source distribution if present
"""
touch(tmpdir / 'pyproject.toml')
dist = Distribution(SETUP_ATTRS)
dist.script_name = 'setup.py'
cmd = sdist(dist)
cmd.ensure_finalized()
with quiet():
cmd.run()
manifest = cmd.filelist.files
assert 'pyproject.toml' in manifest
def test_pyproject_toml_excluded(self, tmpdir):
"""
Check that pyproject.toml can excluded even if present
"""
touch(tmpdir / 'pyproject.toml')
with open('MANIFEST.in', 'w') as mts:
print('exclude pyproject.toml', file=mts)
dist = Distribution(SETUP_ATTRS)
dist.script_name = 'setup.py'
cmd = sdist(dist)
cmd.ensure_finalized()
with quiet():
cmd.run()
manifest = cmd.filelist.files
assert 'pyproject.toml' not in manifest
def test_default_revctrl():
......
......@@ -15,7 +15,7 @@ class TestEdit:
def parse_config(filename):
parser = configparser.ConfigParser()
with io.open(filename, encoding='utf-8') as reader:
(parser.read_file if six.PY3 else parser.readfp)(reader)
(parser.readfp if six.PY2 else parser.read_file)(reader)
return parser
@staticmethod
......
......@@ -223,10 +223,10 @@ class TestFeatures:
py_modules=['bar_et'], remove=['bar.ext'],
),
'baz': Feature(
"baz", optional=False, packages=['pkg.baz'],
scripts=['scripts/baz_it'],
libraries=[('libfoo', 'foo/foofoo.c')]
),
"baz", optional=False, packages=['pkg.baz'],
scripts=['scripts/baz_it'],
libraries=[('libfoo', 'foo/foofoo.c')]
),
'dwim': Feature("DWIM", available=False, remove='bazish'),
},
script_args=['--without-bar', 'install'],
......
......@@ -12,7 +12,7 @@ from setuptools.command.test import test
from setuptools.dist import Distribution
from .textwrap import DALS
from . import contexts
SETUP_PY = DALS("""
from setuptools import setup
......
......@@ -8,8 +8,6 @@ from pytest_fixture_config import yield_requires_config
import pytest_virtualenv
from setuptools.extern import six
from .textwrap import DALS
from .test_easy_install import make_nspkg_sdist
......@@ -64,7 +62,7 @@ def _get_pip_versions():
from urllib.request import urlopen
from urllib.error import URLError
except ImportError:
from urllib2 import urlopen, URLError # Python 2.7 compat
from urllib2 import urlopen, URLError # Python 2.7 compat
try:
urlopen('https://pypi.org', timeout=1)
......@@ -77,12 +75,9 @@ def _get_pip_versions():
'pip==10.0.1',
'pip==18.1',
'pip==19.0.1',
'https://github.com/pypa/pip/archive/master.zip',
]
# Pip's master dropped support for 3.4.
if not six.PY34:
network_versions.append('https://github.com/pypa/pip/archive/master.zip')
versions = [None] + [
pytest.param(v, **({} if network else {'marks': pytest.mark.skip}))
for v in network_versions
......@@ -121,14 +116,12 @@ def test_pip_upgrade_from_source(pip_version, virtualenv):
virtualenv.run('pip install --no-cache-dir --upgrade ' + sdist)
def test_test_command_install_requirements(bare_virtualenv, tmpdir):
def _check_test_command_install_requirements(virtualenv, tmpdir):
"""
Check the test command will install all required dependencies.
"""
bare_virtualenv.run(' && '.join((
'cd {source}',
'python setup.py develop',
)).format(source=SOURCE_DIR))
# Install setuptools.
virtualenv.run('python setup.py develop', cd=SOURCE_DIR)
def sdist(distname, version):
dist_path = tmpdir.join('%s-%s.tar.gz' % (distname, version))
......@@ -179,13 +172,25 @@ def test_test_command_install_requirements(bare_virtualenv, tmpdir):
open('success', 'w').close()
'''))
# Run test command for test package.
bare_virtualenv.run(' && '.join((
virtualenv.run(' && '.join((
'cd {tmpdir}',
'python setup.py test -s test',
)).format(tmpdir=tmpdir))
assert tmpdir.join('success').check()
def test_test_command_install_requirements(virtualenv, tmpdir):
# Ensure pip/wheel packages are installed.
virtualenv.run(
"python -c \"__import__('pkg_resources').require(['pip', 'wheel'])\"")
_check_test_command_install_requirements(virtualenv, tmpdir)
def test_test_command_install_requirements_when_using_easy_install(
bare_virtualenv, tmpdir):
_check_test_command_install_requirements(bare_virtualenv, tmpdir)
def test_no_missing_dependencies(bare_virtualenv):
"""
Quick and dirty test to ensure all external dependencies are vendored.
......
......@@ -18,6 +18,7 @@ import pytest
from pkg_resources import Distribution, PathMetadata, PY_MAJOR
from setuptools.extern.packaging.utils import canonicalize_name
from setuptools.extern.packaging.tags import parse_tag
from setuptools.wheel import Wheel
from .contexts import tempdir
......@@ -124,11 +125,12 @@ def flatten_tree(tree):
def format_install_tree(tree):
return {x.format(
py_version=PY_MAJOR,
platform=get_platform(),
shlib_ext=get_config_var('EXT_SUFFIX') or get_config_var('SO'))
for x in tree}
return {
x.format(
py_version=PY_MAJOR,
platform=get_platform(),
shlib_ext=get_config_var('EXT_SUFFIX') or get_config_var('SO'))
for x in tree}
def _check_wheel_install(filename, install_dir, install_tree_includes,
......@@ -454,7 +456,8 @@ WHEEL_INSTALL_TESTS = (
id='empty_namespace_package',
file_defs={
'foobar': {
'__init__.py': "__import__('pkg_resources').declare_namespace(__name__)",
'__init__.py':
"__import__('pkg_resources').declare_namespace(__name__)",
},
},
setup_kwargs=dict(
......@@ -571,3 +574,12 @@ def test_wheel_no_dist_dir():
_check_wheel_install(wheel_path, install_dir, None,
project_name,
version, None)
def test_wheel_is_compatible(monkeypatch):
def sys_tags():
for t in parse_tag('cp36-cp36m-manylinux1_x86_64'):
yield t
monkeypatch.setattr('setuptools.wheel.sys_tags', sys_tags)
assert Wheel(
'onnxruntime-0.1.2-cp36-cp36m-manylinux1_x86_64.whl').is_compatible()
......@@ -12,9 +12,9 @@ import zipfile
import pkg_resources
import setuptools
from pkg_resources import parse_version
from setuptools.extern.packaging.tags import sys_tags
from setuptools.extern.packaging.utils import canonicalize_name
from setuptools.extern.six import PY3
from setuptools import pep425tags
from setuptools.command.egg_info import write_requirements
......@@ -77,7 +77,8 @@ class Wheel:
def is_compatible(self):
'''Is the wheel is compatible with the current platform?'''
supported_tags = pep425tags.get_supported()
supported_tags = set(
(t.interpreter, t.abi, t.platform) for t in sys_tags())
return next((True for t in self.tags() if t in supported_tags), False)
def egg_name(self):
......
mock
pytest-flake8; python_version!="3.4"
pytest-flake8<=1.0.0; python_version=="3.4"
pytest-flake8
flake8-2020; python_version>="3.6"
virtualenv>=13.0.0
pytest-virtualenv>=1.2.7
pytest>=3.7
......@@ -9,3 +9,4 @@ 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.
import os
import shutil
import subprocess
import sys
from glob import glob
VIRTUAL_ENV = os.environ['VIRTUAL_ENV']
TOX_PIP_DIR = os.path.join(VIRTUAL_ENV, 'pip')
def remove_setuptools():
"""
Remove setuptools from the current environment.
"""
print("Removing setuptools")
cmd = [sys.executable, '-m', 'pip', 'uninstall', '-y', 'setuptools']
# set cwd to something other than '.' to avoid detecting
# '.' as the installed package.
subprocess.check_call(cmd, cwd='.tox')
def bootstrap():
print("Running bootstrap")
cmd = [sys.executable, '-m', 'bootstrap']
subprocess.check_call(cmd)
def pip(args):
# First things first, get a recent (stable) version of pip.
if not os.path.exists(TOX_PIP_DIR):
subprocess.check_call([sys.executable, '-m', 'pip',
'--disable-pip-version-check',
'install', '-t', TOX_PIP_DIR,
'pip'])
shutil.rmtree(glob(os.path.join(TOX_PIP_DIR, 'pip-*.dist-info'))[0])
# And use that version.
for n, a in enumerate(args):
if not a.startswith('-'):
if a in 'install' and '-e' in args[n:]:
args.insert(n + 1, '--no-use-pep517')
break
subprocess.check_call([sys.executable, os.path.join(TOX_PIP_DIR, 'pip')] + args)
# Honor requires-python when installing test suite dependencies
if any('-r' in arg for arg in args):
os.environ['PIP_IGNORE_REQUIRES_PYTHON'] = '0'
if '.' in args:
remove_setuptools()
bootstrap()
cmd = [sys.executable, '-m', 'pip'] + args
subprocess.check_call(cmd)
if __name__ == '__main__':
......
# Note: Run "python bootstrap.py" before running Tox, to generate metadata.
#
# To run Tox against all supported Python interpreters, you can set:
#
# export TOXENV='py27,py3{4,5,6},pypy,pypy3'
# export TOXENV='py3{5,6,7,8},pypy,pypy3'
[tox]
envlist=python
minversion = 3.2
requires =
tox-pip-version >= 0.0.6
[helpers]
# Wrapper for calls to pip that make sure the version being used is a
# up-to-date, and to prevent the current working directory from being
# added to `sys.path`.
# Custom pip behavior
pip = python {toxinidir}/tools/tox_pip.py
[testenv]
deps=-rtests/requirements.txt
deps=-r{toxinidir}/tests/requirements.txt
pip_version = pip
install_command = {[helpers]pip} install {opts} {packages}
list_dependencies_command = {[helpers]pip} freeze --all
setenv=COVERAGE_FILE={toxworkdir}/.coverage.{envname}
setenv =
COVERAGE_FILE={toxworkdir}/.coverage.{envname}
py27: PIP_IGNORE_REQUIRES_PYTHON=true
# TODO: The passed environment variables came from copying other tox.ini files
# These should probably be individually annotated to explain what needs them.
passenv=APPDATA HOMEDRIVE HOMEPATH windir APPVEYOR APPVEYOR_* CI CODECOV_* TRAVIS TRAVIS_* NETWORK_REQUIRED
......@@ -45,7 +47,7 @@ commands=codecov -X gcov --file {toxworkdir}/coverage.xml
deps = -r{toxinidir}/docs/requirements.txt
skip_install=True
commands =
python {toxinidir}/bootstrap.py
python -m bootstrap
sphinx-build -W -b html -d {envtmpdir}/doctrees docs docs/build/html
sphinx-build -W -b man -d {envtmpdir}/doctrees docs docs/build/man
......@@ -55,3 +57,22 @@ source=
setuptools
omit=
*/_vendor/*
[testenv:release]
skip_install = True
deps =
wheel
twine[keyring]>=1.13
path
jaraco.tidelift
passenv =
TWINE_PASSWORD
TIDELIFT_TOKEN
setenv =
TWINE_USERNAME = {env:TWINE_USERNAME:__token__}
commands =
python -m bootstrap
python -c "import path; path.Path('dist').rmtree_p()"
python setup.py release
python -m twine upload dist/*
python -m jaraco.tidelift.publish-release-notes
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