[bumpversion]
-current_version = 44.1.1
+current_version = 45.0.0
commit = True
tag = True
[bumpversion:file:setup.cfg]
+
jobs:
fast_finish: true
include:
- - &latest_py2
- python: 2.7
- - <<: *latest_py2
- env: LANG=C
- - python: pypy
- env: DISABLE_COVERAGE=1 # Don't run coverage on pypy (too slow).
- python: pypy3
- env: DISABLE_COVERAGE=1
+ env: DISABLE_COVERAGE=1 # Don't run coverage on pypy (too slow).
- python: 3.5
- python: 3.6
- python: 3.7
if: tag IS present
script: tox -e release
after_success: skip
- allow_failures:
- # suppress failures due to pypa/setuptools#2000
- - python: pypy3
cache: pip
- pip freeze --all
- env
+# update egg_info based on setup.py in checkout
+- python bootstrap.py
- "! grep pyc setuptools.egg-info/SOURCES.txt"
script:
-v44.1.1
+v45.0.0
-------
-* #2158: Avoid loading working set during ``Distribution.finalize_options`` prior to invoking ``_install_setup_requires``, broken since v42.0.0.
-
-
-v44.1.0
--------
-
-* #1704: Set sys.argv[0] in setup script run by build_meta.__legacy__
+* #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
-* #1994: Fixed a bug in the "setuptools.finalize_distribution_options" hook that lead to ignoring the order attribute of entry points managed by this hook.
v44.0.0
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
- 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:
+++ /dev/null
-# 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')
# 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, '-m', 'bootstrap'],
+ [sys.executable, 'bootstrap.py'],
cwd=os.path.join(os.path.dirname(__file__), os.path.pardir),
)
Testing
-------
-The primary tests are run using tox. Make sure you have tox installed,
-and invoke it::
+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::
$ tox
===============
In order to allow for rapid, predictable releases, Setuptools uses a
-mechanical technique for releases, enacted on tagged commits by
-continuous integration.
-
-To finalize a release, run ``tox -e finalize``, review, then push
-the changes.
-
-If tests pass, the release will be uploaded to PyPI.
+mechanical technique for releases, enacted by Travis following a
+successful build of a tagged release per
+`PyPI deployment <https://docs.travis-ci.com/user/deployment/pypi>`_.
+
+Prior to cutting a release, please use `towncrier`_ to update
+``CHANGES.rst`` to summarize the changes since the last release.
+To update the changelog:
+
+1. Install towncrier via ``pip install towncrier`` if not already installed.
+2. Preview the new ``CHANGES.rst`` entry by running
+ ``towncrier --draft --version {new.version.number}`` (enter the desired
+ version number for the next release). If any changes are needed, make
+ them and generate a new preview until the output is acceptable. Run
+ ``git add`` for any modified files.
+3. Run ``towncrier --version {new.version.number}`` to stage the changelog
+ updates in git.
+4. Verify that there are no remaining ``changelog.d/*.rst`` files. If a
+ file was named incorrectly, it may be ignored by towncrier.
+5. Review the updated ``CHANGES.rst`` file. If any changes are needed,
+ make the edits and stage them via ``git add CHANGES.rst``.
+
+Once the changelog edits are staged and ready to commit, cut a release by
+installing and running ``bump2version --allow-dirty {part}`` where ``part``
+is major, minor, or patch based on the scope of the changes in the
+release. Then, push the commits to the master branch::
+
+ $ git push origin master
+ $ git push --tags
+
+If tests pass, the release will be uploaded to PyPI (from the Python 3.6
+tests).
+
+.. _towncrier: https://pypi.org/project/towncrier/
Release Frequency
-----------------
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
Now, to upload these archives, run::
- twine upload --repository-url https://test.pypi.org/simple/ dist/*
+ twine upload --repository-url https://test.pypi.org/legacy/ dist/*
To install your newly uploaded package ``example_pkg``, you can use pip::
__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
--- /dev/null
+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. 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.
+ """)
+
+sys.version_info < (3,) and warnings.warn("*" * 60 + msg + "*" * 60)
[metadata]
name = setuptools
-version = 44.1.1
+version = 45.0.0
description = Easily download, build, install, upgrade, and uninstall Python packages
author = Python Packaging Authority
author_email = distutils-sig@python.org
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.5
Programming Language :: Python :: 3.6
[options]
zip_safe = True
-python_requires = >=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*
+python_requires = >=3.5
py_modules = easy_install
packages = find:
def _install_setup_requires(attrs):
# Note: do not use `setuptools.Distribution` directly, as
# our PEP 517 backend patch `distutils.core.Distribution`.
- class MinimalDistribution(distutils.core.Distribution):
- """
- A minimal version of a distribution for supporting the
- fetch_build_eggs interface.
- """
- def __init__(self, attrs):
- _incl = 'dependency_links', 'setup_requires'
- filtered = {
- k: attrs[k]
- for k in set(_incl) & set(attrs)
- }
- distutils.core.Distribution.__init__(self, filtered)
-
- def finalize_options(self):
- """
- Disable finalize_options to avoid building the working set.
- Ref #2158.
- """
-
- dist = MinimalDistribution(attrs)
-
+ dist = distutils.core.Distribution(dict(
+ (k, v) for k, v in attrs.items()
+ if k in ('dependency_links', 'setup_requires')
+ ))
# Honor setup.cfg's options.
dist.parse_config_files(ignore_option_errors=True)
if dist.setup_requires:
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)
# 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()
to influence the order of execution. Smaller numbers
go first and the default is 0.
"""
- group = 'setuptools.finalize_distribution_options'
+ hook_key = 'setuptools.finalize_distribution_options'
def by_order(hook):
return getattr(hook, 'order', 0)
- eps = map(lambda e: e.load(), pkg_resources.iter_entry_points(group))
+ eps = pkg_resources.iter_entry_points(hook_key)
for ep in sorted(eps, key=by_order):
- ep(self)
+ ep.load()(self)
def _finalize_setup_keywords(self):
for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'):
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__'
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")
+++ /dev/null
-"""
-Finalize the repo for a release. Invokes towncrier and bumpversion.
-"""
-
-__requires__ = ['bump2version', 'towncrier']
-
-
-import subprocess
-import pathlib
-import re
-import sys
-
-
-def release_kind():
- """
- Determine which release to make based on the files in the
- changelog.
- """
- # use min here as 'major' < 'minor' < 'patch'
- return min(
- 'major' if 'breaking' in file.name else
- 'minor' if 'change' in file.name else
- 'patch'
- for file in pathlib.Path('changelog.d').iterdir()
- )
-
-
-bump_version_command = [
- sys.executable,
- '-m', 'bumpversion',
- release_kind(),
-]
-
-
-def get_version():
- cmd = bump_version_command + ['--dry-run', '--verbose']
- out = subprocess.check_output(cmd, text=True)
- return re.search('^new_version=(.*)', out, re.MULTILINE).group(1)
-
-
-def update_changelog():
- cmd = [
- sys.executable, '-m',
- 'towncrier',
- '--version', get_version(),
- '--yes',
- ]
- subprocess.check_call(cmd)
-
-
-def bump_version():
- cmd = bump_version_command + ['--allow-dirty']
- subprocess.check_call(cmd)
-
-
-if __name__ == '__main__':
- print("Cutting release at", get_version())
- update_changelog()
- bump_version()
+import os
+import shutil
import subprocess
import sys
+from glob import glob
-
-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)
+VIRTUAL_ENV = os.environ['VIRTUAL_ENV']
+TOX_PIP_DIR = os.path.join(VIRTUAL_ENV, 'pip')
def pip(args):
- if '.' in args:
- remove_setuptools()
- bootstrap()
-
- cmd = [sys.executable, '-m', 'pip'] + args
- subprocess.check_call(cmd)
+ # First things first, get a recent (stable) version of pip.
+ if not os.path.exists(TOX_PIP_DIR):
+ subprocess.check_call([sys.executable, '-m', 'pip',
+ '--disable-pip-version-check',
+ 'install', '-t', TOX_PIP_DIR,
+ 'pip'])
+ shutil.rmtree(glob(os.path.join(TOX_PIP_DIR, 'pip-*.dist-info'))[0])
+ # And use that version.
+ pypath = os.environ.get('PYTHONPATH')
+ pypath = pypath.split(os.pathsep) if pypath is not None else []
+ pypath.insert(0, TOX_PIP_DIR)
+ os.environ['PYTHONPATH'] = os.pathsep.join(pypath)
+ # Fix call for setuptools editable install.
+ for n, a in enumerate(args):
+ if a == '.':
+ args[n] = os.getcwd()
+ subprocess.check_call([sys.executable, '-m', 'pip'] + args, cwd=TOX_PIP_DIR)
if __name__ == '__main__':
+# 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{5,6,7,8},pypy,pypy3'
[tox]
envlist=python
-minversion = 3.2
-requires =
- tox-pip-version >= 0.0.6
- # workaround for #1998
- virtualenv < 20
[helpers]
-# Custom pip behavior
+# Wrapper for calls to pip that make sure the version being used is a
+# up-to-date, and to prevent the current working directory from being
+# added to `sys.path`.
pip = python {toxinidir}/tools/tox_pip.py
[testenv]
deps=-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}
deps = -r{toxinidir}/docs/requirements.txt
skip_install=True
commands =
- python -m bootstrap
+ python {toxinidir}/bootstrap.py
sphinx-build -W -b html -d {envtmpdir}/doctrees docs docs/build/html
sphinx-build -W -b man -d {envtmpdir}/doctrees docs docs/build/man
omit=
*/_vendor/*
-[testenv:finalize]
-skip_install = True
-deps =
- towncrier
- bump2version
-commands =
- python tools/finalize.py
-
[testenv:release]
skip_install = True
deps =