From 5ad18328e8f38abcc5a0fc4a549e7a3c40697d4e Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 19 Dec 2015 22:29:56 -0500 Subject: [PATCH 001/323] Generate project skeleton --- .hgignore | 2 ++ .travis.yml | 8 +++++++ CHANGES.rst | 0 README.rst | 2 ++ docs/conf.py | 19 +++++++++++++++++ docs/history.rst | 8 +++++++ docs/index.rst | 22 +++++++++++++++++++ pytest.ini | 4 ++++ setup.cfg | 6 ++++++ setup.py | 51 ++++++++++++++++++++++++++++++++++++++++++++ skeleton/__init__.py | 0 11 files changed, 122 insertions(+) create mode 100644 .hgignore create mode 100644 .travis.yml create mode 100644 CHANGES.rst create mode 100644 README.rst create mode 100644 docs/conf.py create mode 100644 docs/history.rst create mode 100644 docs/index.rst create mode 100644 pytest.ini create mode 100644 setup.cfg create mode 100644 setup.py create mode 100644 skeleton/__init__.py diff --git a/.hgignore b/.hgignore new file mode 100644 index 00000000..9d0b71a3 --- /dev/null +++ b/.hgignore @@ -0,0 +1,2 @@ +build +dist diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..6e5e969d --- /dev/null +++ b/.travis.yml @@ -0,0 +1,8 @@ +sudo: false +language: python +python: + - 2.7 + - 3.5 +script: + - pip install -U pytest + - python setup.py test diff --git a/CHANGES.rst b/CHANGES.rst new file mode 100644 index 00000000..e69de29b diff --git a/README.rst b/README.rst new file mode 100644 index 00000000..217a0758 --- /dev/null +++ b/README.rst @@ -0,0 +1,2 @@ +skeleton +======== diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 00000000..c8348485 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import setuptools_scm + +extensions = [ + 'sphinx.ext.autodoc', +] + +# General information about the project. +project = 'skeleton' +copyright = '2015 Jason R. Coombs' + +# The short X.Y version. +version = setuptools_scm.get_version(root='..', relative_to=__file__) +# The full version, including alpha/beta/rc tags. +release = version + +master_doc = 'index' diff --git a/docs/history.rst b/docs/history.rst new file mode 100644 index 00000000..907000bf --- /dev/null +++ b/docs/history.rst @@ -0,0 +1,8 @@ +:tocdepth: 2 + +.. _changes: + +History +******* + +.. include:: ../CHANGES.rst diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 00000000..d14131b0 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,22 @@ +Welcome to skeleton documentation! +======================================== + +.. toctree:: + :maxdepth: 1 + + history + + +.. automodule:: skeleton + :members: + :undoc-members: + :show-inheritance: + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 00000000..9752c365 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,4 @@ +[pytest] +norecursedirs=*.egg .eggs dist build +addopts=--doctest-modules +doctest_optionflags=ALLOW_UNICODE ELLIPSIS diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 00000000..445263a6 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,6 @@ +[aliases] +release = sdist bdist_wheel build_sphinx upload upload_docs +test = pytest + +[wheel] +universal = 1 diff --git a/setup.py b/setup.py new file mode 100644 index 00000000..e6edf34c --- /dev/null +++ b/setup.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python +# Generated by jaraco.develop 2.27.1 +# https://pypi.python.org/pypi/jaraco.develop + +import io +import sys + +import setuptools + +with io.open('README.rst', encoding='utf-8') as readme: + long_description = readme.read() + +needs_pytest = {'pytest', 'test'}.intersection(sys.argv) +pytest_runner = ['pytest_runner'] if needs_pytest else [] +needs_sphinx = {'release', 'build_sphinx', 'upload_docs'}.intersection(sys.argv) +sphinx = ['sphinx'] if needs_sphinx else [] +needs_wheel = {'release', 'bdist_wheel'}.intersection(sys.argv) +wheel = ['wheel'] if needs_wheel else [] + +setup_params = dict( + name='skeleton', + use_scm_version=True, + author="Jason R. Coombs", + author_email="jaraco@jaraco.com", + description="skeleton", + long_description=long_description, + url="https://github.com/jaraco/skeleton", + packages=setuptools.find_packages(), + include_package_data=True, + install_requires=[ + ], + extras_require={ + }, + setup_requires=[ + 'setuptools_scm>=1.9', + ] + pytest_runner + sphinx + wheel, + tests_require=[ + 'pytest>=2.8', + ], + classifiers=[ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Programming Language :: Python :: 2.7", + "Programming Language :: Python :: 3", + ], + entry_points={ + }, +) +if __name__ == '__main__': + setuptools.setup(**setup_params) diff --git a/skeleton/__init__.py b/skeleton/__init__.py new file mode 100644 index 00000000..e69de29b From 52df9f0ff993963ad519495ec882513d4018dc8f Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 26 Dec 2015 08:46:57 -0500 Subject: [PATCH 002/323] Remove the package from the skeleton. It has no value. --- skeleton/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 skeleton/__init__.py diff --git a/skeleton/__init__.py b/skeleton/__init__.py deleted file mode 100644 index e69de29b..00000000 From 5782d0536efe0d5c516ed9badeba1947208a22b0 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 1 Jan 2016 08:30:19 -0500 Subject: [PATCH 003/323] Add gitignore. Make .hgignore empty - there's nothing here that's project specific. --- .gitignore | 0 .hgignore | 2 -- 2 files changed, 2 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..e69de29b diff --git a/.hgignore b/.hgignore index 9d0b71a3..e69de29b 100644 --- a/.hgignore +++ b/.hgignore @@ -1,2 +0,0 @@ -build -dist From 8277707492656a3ade588e8826c02a11c3cad191 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 1 Jan 2016 08:35:39 -0500 Subject: [PATCH 004/323] Upon further reading, hg-git supports .gitignore, so omit .hgignore. --- .hgignore | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .hgignore diff --git a/.hgignore b/.hgignore deleted file mode 100644 index e69de29b..00000000 From 6d571b47f84e8ee87decbaffbd577c5f556efb4b Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 1 Jan 2016 09:03:11 -0500 Subject: [PATCH 005/323] Update copyright --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index c8348485..18740743 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -9,7 +9,7 @@ # General information about the project. project = 'skeleton' -copyright = '2015 Jason R. Coombs' +copyright = '2016 Jason R. Coombs' # The short X.Y version. version = setuptools_scm.get_version(root='..', relative_to=__file__) From b5982a5472720f001fe52f0defc2c2d804d3501e Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 15 Feb 2016 04:59:44 -0500 Subject: [PATCH 006/323] Learning from lessons in the keyring 8.4 release (https://github.com/jaraco/keyring/issues/210), always clean the build artifacts before cutting a release. --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 445263a6..8004dcb6 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [aliases] -release = sdist bdist_wheel build_sphinx upload upload_docs +release = clean --all sdist bdist_wheel build_sphinx upload upload_docs test = pytest [wheel] From 65b649869bb4f0ab1aad5deb3a30973a45082d4a Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 5 Mar 2016 08:56:02 -0500 Subject: [PATCH 007/323] Derive description, url, and namespace_packages from name --- setup.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index e6edf34c..e825a5d7 100644 --- a/setup.py +++ b/setup.py @@ -17,16 +17,20 @@ needs_wheel = {'release', 'bdist_wheel'}.intersection(sys.argv) wheel = ['wheel'] if needs_wheel else [] +name = 'skeleton' +description = '' + setup_params = dict( - name='skeleton', + name=name, use_scm_version=True, author="Jason R. Coombs", author_email="jaraco@jaraco.com", - description="skeleton", + description=description or name, long_description=long_description, - url="https://github.com/jaraco/skeleton", + url="https://github.com/jaraco/" + name, packages=setuptools.find_packages(), include_package_data=True, + namespace_packages=name.split('.')[:-1], install_requires=[ ], extras_require={ From 752b1096b8d126df040847c19c82aa042ccfe77a Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 2 Apr 2016 21:46:39 -0400 Subject: [PATCH 008/323] Add PyPI deployment --- .travis.yml | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6e5e969d..a5b29eeb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,18 @@ sudo: false language: python python: - - 2.7 - - 3.5 +- 2.7 +- 3.5 script: - - pip install -U pytest - - python setup.py test +- pip install -U pytest +- python setup.py test +deploy: + provider: pypi + on: + tags: true + all_branches: true + user: jaraco + provider: pypi + # supply password with `travis encrypt --add deploy.password` + distributions: release + python: 3.5 From 58f71d1e2bad2392cde6cfabef4fc9cfc8bfec28 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 2 Apr 2016 22:03:05 -0400 Subject: [PATCH 009/323] Remove duplicate provider line --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a5b29eeb..7ff32e91 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,7 +12,6 @@ deploy: tags: true all_branches: true user: jaraco - provider: pypi # supply password with `travis encrypt --add deploy.password` distributions: release python: 3.5 From 01e953bac9ac132fba90550492ee9f7eedfce7e0 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 2 Apr 2016 22:23:15 -0400 Subject: [PATCH 010/323] Add support for linking to issues and adding datestamps to changelog entries. --- docs/conf.py | 20 ++++++++++++++++++++ docs/history.rst | 2 +- setup.py | 2 +- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 18740743..9c7ad1b0 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -5,6 +5,7 @@ extensions = [ 'sphinx.ext.autodoc', + 'rst.linker', ] # General information about the project. @@ -17,3 +18,22 @@ release = version master_doc = 'index' + +link_files = { + 'CHANGES.rst': dict( + using=dict( + GH='https://github.com', + project=project, + ), + replace=[ + dict( + pattern=r"(Issue )?#(?P\d+)", + url='{GH}/jaraco/{project}/issues/{issue}', + ), + dict( + pattern=r"^(?m)((?Pv?\d+(\.\d+){1,2}))\n[-=]+\n", + with_scm="{text}\n{rev[timestamp]:%d %b %Y}\n", + ), + ], + ), +} diff --git a/docs/history.rst b/docs/history.rst index 907000bf..8e217503 100644 --- a/docs/history.rst +++ b/docs/history.rst @@ -5,4 +5,4 @@ History ******* -.. include:: ../CHANGES.rst +.. include:: ../CHANGES (links).rst diff --git a/setup.py b/setup.py index e825a5d7..99d6c9c9 100644 --- a/setup.py +++ b/setup.py @@ -13,7 +13,7 @@ needs_pytest = {'pytest', 'test'}.intersection(sys.argv) pytest_runner = ['pytest_runner'] if needs_pytest else [] needs_sphinx = {'release', 'build_sphinx', 'upload_docs'}.intersection(sys.argv) -sphinx = ['sphinx'] if needs_sphinx else [] +sphinx = ['sphinx', 'rst.linker'] if needs_sphinx else [] needs_wheel = {'release', 'bdist_wheel'}.intersection(sys.argv) wheel = ['wheel'] if needs_wheel else [] From 04528bdf294e0b9eb4920d4b4a8637b6871b1606 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 16 Apr 2016 15:22:52 +0100 Subject: [PATCH 011/323] Move Python 3.5 condition to 'on' section --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 7ff32e91..c5f3495b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,7 @@ deploy: on: tags: true all_branches: true + python: 3.5 user: jaraco # supply password with `travis encrypt --add deploy.password` distributions: release - python: 3.5 From 29d9ebee0154e77e416162061752833410e98cbd Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 29 Apr 2016 09:32:33 -0400 Subject: [PATCH 012/323] Update comment to reflect the Github-backed skeleton model (preferred to the generation library-backed model). --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 99d6c9c9..91e4110c 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -# Generated by jaraco.develop 2.27.1 -# https://pypi.python.org/pypi/jaraco.develop + +# Project skeleton maintained at https://github.com/jaraco/skeleton import io import sys From b93b3a0348e9a17ec323f74eb9eb0ec8e82367ff Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 24 May 2016 12:21:58 -0400 Subject: [PATCH 013/323] Exclude the skeleton branch from testing --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index c5f3495b..bb6d47e0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,9 @@ python: script: - pip install -U pytest - python setup.py test +branches: + except: + - skeleton deploy: provider: pypi on: From efa552e7ee31d0fb1dab3d1a2986cad0834b04f6 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 3 Aug 2016 09:51:36 -0400 Subject: [PATCH 014/323] Add badges for PyPI, downloads, and Travis-CI. --- README.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.rst b/README.rst index 217a0758..e739bf50 100644 --- a/README.rst +++ b/README.rst @@ -1,2 +1,11 @@ skeleton ======== + +.. image:: https://badge.fury.io/py/skeleton.svg + :target: https://badge.fury.io/py/skeleton + +.. image:: https://pypip.in/d/skeleton/badge.png + :target: https://crate.io/packages/skeleton/ + +.. image:: https://secure.travis-ci.org/jaraco/skeleton.png + :target: http://travis-ci.org/jaraco/skeleton From e2900e901e9c24eb7ebf59792dc198bf0bd27cc8 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 3 Aug 2016 09:54:22 -0400 Subject: [PATCH 015/323] Change indentation to match that which the travis tool generates when adding the password. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index bb6d47e0..4abbe308 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,7 @@ script: - python setup.py test branches: except: - - skeleton + - skeleton deploy: provider: pypi on: From c8c034e68873e40ed55f0b9f04afc5949eb54727 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 3 Aug 2016 10:07:15 -0400 Subject: [PATCH 016/323] Use shields.io, as some of these other providers seem to have gone out of business. --- README.rst | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/README.rst b/README.rst index e739bf50..33249644 100644 --- a/README.rst +++ b/README.rst @@ -1,11 +1,10 @@ skeleton ======== -.. image:: https://badge.fury.io/py/skeleton.svg - :target: https://badge.fury.io/py/skeleton +.. image:: https://img.shields.io/pypi/v/skeleton.svg + :target: https://pypi.io/project/skeleton -.. image:: https://pypip.in/d/skeleton/badge.png - :target: https://crate.io/packages/skeleton/ +.. image:: https://img.shields.io/pypi/dm/skeleton.svg -.. image:: https://secure.travis-ci.org/jaraco/skeleton.png - :target: http://travis-ci.org/jaraco/skeleton +.. image:: https://img.shields.io/travis/jaraco/skeleton/master.svg + :target: http://travis-ci.org/jaraco/skeleton From 3f61a73b657a7a845f0f7fdbcebbf92c7f8e6c22 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 3 Aug 2016 10:13:55 -0400 Subject: [PATCH 017/323] Also add pyversions --- README.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.rst b/README.rst index 33249644..db95581e 100644 --- a/README.rst +++ b/README.rst @@ -4,6 +4,8 @@ skeleton .. image:: https://img.shields.io/pypi/v/skeleton.svg :target: https://pypi.io/project/skeleton +.. image:: https://img.shields.io/pypi/pyversions/skeleton.svg + .. image:: https://img.shields.io/pypi/dm/skeleton.svg .. image:: https://img.shields.io/travis/jaraco/skeleton/master.svg From dfb1a9424d373fb2f949f2d45f79d8008ede276b Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 8 Aug 2016 14:49:32 -0400 Subject: [PATCH 018/323] Path is now .org --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index db95581e..75c0b4f0 100644 --- a/README.rst +++ b/README.rst @@ -2,7 +2,7 @@ skeleton ======== .. image:: https://img.shields.io/pypi/v/skeleton.svg - :target: https://pypi.io/project/skeleton + :target: https://pypi.org/project/skeleton .. image:: https://img.shields.io/pypi/pyversions/skeleton.svg From 7edaa321dead30e33accdb7512f9e95bbef9fe38 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 9 Aug 2016 09:50:49 -0400 Subject: [PATCH 019/323] Update release process to use warehouse rather than legacy PyPI. Ref pypa/warehouse#1422. --- .travis.yml | 3 ++- setup.cfg | 6 +++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4abbe308..9f4c5178 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,10 +11,11 @@ branches: - skeleton deploy: provider: pypi + server: https://upload.pypi.org/legacy/ on: tags: true all_branches: true python: 3.5 user: jaraco # supply password with `travis encrypt --add deploy.password` - distributions: release + distributions: dists diff --git a/setup.cfg b/setup.cfg index 8004dcb6..dcd8d122 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,10 @@ [aliases] -release = clean --all sdist bdist_wheel build_sphinx upload upload_docs +release = dists build_sphinx upload upload_docs +dists = clean --all sdist bdist_wheel test = pytest [wheel] universal = 1 + +[upload] +repository = https://upload.pypi.org/legacy/ From d024388cac7d3804c763e6f5656e75a6bde7d33c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 15 Aug 2016 09:51:41 -0400 Subject: [PATCH 020/323] The name of the project need not be in the README --- README.rst | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.rst b/README.rst index 75c0b4f0..1c5d10ec 100644 --- a/README.rst +++ b/README.rst @@ -1,6 +1,3 @@ -skeleton -======== - .. image:: https://img.shields.io/pypi/v/skeleton.svg :target: https://pypi.org/project/skeleton From 629d80f45dedc801e3fe19215ba50114b4c7b949 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 4 Sep 2016 13:06:52 -0400 Subject: [PATCH 021/323] No need for a .gitignore file; projects may want to add one, but I recommend not having one unless the project has project-specific files to ignore. --- .gitignore | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .gitignore diff --git a/.gitignore b/.gitignore deleted file mode 100644 index e69de29b..00000000 From 03c1cc86843bcfbb6c2a9366d285427ac006aeee Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 14 Sep 2016 21:00:50 -0400 Subject: [PATCH 022/323] Remove support for building docs, now that docs support for pypi is deprecated. I hope at some point RTD comes up with an API that once again allows automatic building of docs. --- setup.cfg | 2 +- setup.py | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/setup.cfg b/setup.cfg index dcd8d122..f5ee6072 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [aliases] -release = dists build_sphinx upload upload_docs +release = dists upload dists = clean --all sdist bdist_wheel test = pytest diff --git a/setup.py b/setup.py index 91e4110c..1f815f2e 100644 --- a/setup.py +++ b/setup.py @@ -12,9 +12,7 @@ needs_pytest = {'pytest', 'test'}.intersection(sys.argv) pytest_runner = ['pytest_runner'] if needs_pytest else [] -needs_sphinx = {'release', 'build_sphinx', 'upload_docs'}.intersection(sys.argv) -sphinx = ['sphinx', 'rst.linker'] if needs_sphinx else [] -needs_wheel = {'release', 'bdist_wheel'}.intersection(sys.argv) +needs_wheel = {'release', 'bdist_wheel', 'dists'}.intersection(sys.argv) wheel = ['wheel'] if needs_wheel else [] name = 'skeleton' @@ -37,7 +35,7 @@ }, setup_requires=[ 'setuptools_scm>=1.9', - ] + pytest_runner + sphinx + wheel, + ] + pytest_runner + wheel, tests_require=[ 'pytest>=2.8', ], From 750a2b38964adc868b1a7f4570afa1532418b12c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 22 Sep 2016 10:50:07 -0500 Subject: [PATCH 023/323] Use tox instead of pytest-runner --- .travis.yml | 3 +-- pytest.ini | 2 +- setup.cfg | 1 - setup.py | 7 +------ tests/requirements.txt | 1 + tox.ini | 5 +++++ 6 files changed, 9 insertions(+), 10 deletions(-) create mode 100644 tests/requirements.txt create mode 100644 tox.ini diff --git a/.travis.yml b/.travis.yml index 9f4c5178..6c9a2cff 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,8 +4,7 @@ python: - 2.7 - 3.5 script: -- pip install -U pytest -- python setup.py test +- tox branches: except: - skeleton diff --git a/pytest.ini b/pytest.ini index 9752c365..56a87745 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,4 +1,4 @@ [pytest] -norecursedirs=*.egg .eggs dist build +norecursedirs=dist build .tox addopts=--doctest-modules doctest_optionflags=ALLOW_UNICODE ELLIPSIS diff --git a/setup.cfg b/setup.cfg index f5ee6072..4659acce 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,7 +1,6 @@ [aliases] release = dists upload dists = clean --all sdist bdist_wheel -test = pytest [wheel] universal = 1 diff --git a/setup.py b/setup.py index 1f815f2e..ec848c10 100644 --- a/setup.py +++ b/setup.py @@ -10,8 +10,6 @@ with io.open('README.rst', encoding='utf-8') as readme: long_description = readme.read() -needs_pytest = {'pytest', 'test'}.intersection(sys.argv) -pytest_runner = ['pytest_runner'] if needs_pytest else [] needs_wheel = {'release', 'bdist_wheel', 'dists'}.intersection(sys.argv) wheel = ['wheel'] if needs_wheel else [] @@ -35,10 +33,7 @@ }, setup_requires=[ 'setuptools_scm>=1.9', - ] + pytest_runner + wheel, - tests_require=[ - 'pytest>=2.8', - ], + ] + wheel, classifiers=[ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", diff --git a/tests/requirements.txt b/tests/requirements.txt new file mode 100644 index 00000000..70bc02f1 --- /dev/null +++ b/tests/requirements.txt @@ -0,0 +1 @@ +pytest >= 2.8 diff --git a/tox.ini b/tox.ini new file mode 100644 index 00000000..dea83741 --- /dev/null +++ b/tox.ini @@ -0,0 +1,5 @@ +[testenv] +deps = + -r tests/requirements.txt + +commands = py.test From cc80be915b6912056990bf71324826e244432533 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 23 Sep 2016 10:07:39 -0500 Subject: [PATCH 024/323] Use pkg_resources to resolve the version. Requires that the necessary package metadata have been built before building docs. --- docs/conf.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 9c7ad1b0..5abe25ae 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -import setuptools_scm +import pkg_resources extensions = [ 'sphinx.ext.autodoc', @@ -13,7 +13,7 @@ copyright = '2016 Jason R. Coombs' # The short X.Y version. -version = setuptools_scm.get_version(root='..', relative_to=__file__) +version = pkg_resources.require(project)[0].version # The full version, including alpha/beta/rc tags. release = version From 8b4139a8132c330623631f84528a3cd8f186df9a Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 23 Sep 2016 10:08:41 -0500 Subject: [PATCH 025/323] Each requirement line is passed as a single parameter to pip, so you can't have a space separating the option and its value. --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index dea83741..fa7284b8 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [testenv] deps = - -r tests/requirements.txt + -rtests/requirements.txt commands = py.test From 4d382b3dee98d155f4057759ff015c3b6f0a15ed Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 28 Sep 2016 12:29:05 -0400 Subject: [PATCH 026/323] Python Packaging -- never do with one command what you can do with two. --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 6c9a2cff..d7871d87 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,8 @@ language: python python: - 2.7 - 3.5 +install: +- pip install tox script: - tox branches: From 12196ba3c3e116a2514ed9fd22c6ed60539e9160 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 30 Sep 2016 15:52:25 -0400 Subject: [PATCH 027/323] Provide a reference to the license declaration in the readme. Fixes jaraco/skeleton#1. --- README.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.rst b/README.rst index 1c5d10ec..bb3d9127 100644 --- a/README.rst +++ b/README.rst @@ -7,3 +7,7 @@ .. image:: https://img.shields.io/travis/jaraco/skeleton/master.svg :target: http://travis-ci.org/jaraco/skeleton + +License is indicated in the project metadata (typically one or more +of the Trove classifiers). For more details, see `this explanation +`_. From c4fd3f3cf414e2ee08ad53bd71cf9c201c69ca6f Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 30 Sep 2016 16:14:13 -0400 Subject: [PATCH 028/323] Use usedevelop to workaround tox-dev/tox#373 --- tox.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/tox.ini b/tox.ini index fa7284b8..564f2050 100644 --- a/tox.ini +++ b/tox.ini @@ -3,3 +3,4 @@ deps = -rtests/requirements.txt commands = py.test +usedevelop = True From 96984072229ae07471373da73cad377a0cb324ef Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 2 Oct 2016 09:28:19 -0500 Subject: [PATCH 029/323] Incorporate pre-release of setuptools to cause releases to include the PEP-420 deferral. --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index d7871d87..87881dbc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,7 @@ python: - 3.5 install: - pip install tox +- pip install https://github.com/pypa/setuptools/releases/download/v28.2.0b1/setuptools-28.2.0b1-py2.py3-none-any.whl script: - tox branches: From 1d7afbebd6530015d76ce93d88aa7a7c48c29717 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 7 Oct 2016 12:16:38 -0700 Subject: [PATCH 030/323] Just upgrade to released setuptools now. --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 87881dbc..0c33b1ce 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,8 +4,7 @@ python: - 2.7 - 3.5 install: -- pip install tox -- pip install https://github.com/pypa/setuptools/releases/download/v28.2.0b1/setuptools-28.2.0b1-py2.py3-none-any.whl +- pip install tox "setuptools>=28.2" script: - tox branches: From 9be6e615930bdecb69cf4da887eefd0d53c425bd Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 14 Oct 2016 21:40:51 -0400 Subject: [PATCH 031/323] Exclude versions of setuptools_scm due to pypa/setuptools_scm#109. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index ec848c10..27ace5fd 100644 --- a/setup.py +++ b/setup.py @@ -32,7 +32,7 @@ extras_require={ }, setup_requires=[ - 'setuptools_scm>=1.9', + 'setuptools_scm>=1.9,!=1.13.1,!=1.14.0', ] + wheel, classifiers=[ "Development Status :: 5 - Production/Stable", From aa1f8ebe0d2d3f49a36535b61824f2fece3bdd46 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 14 Oct 2016 23:03:42 -0400 Subject: [PATCH 032/323] Allow passing posargs --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 564f2050..d740130c 100644 --- a/tox.ini +++ b/tox.ini @@ -2,5 +2,5 @@ deps = -rtests/requirements.txt -commands = py.test +commands = py.test {posargs} usedevelop = True From 60c7c186c133551cf0637354a642e49406f814b7 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 15 Oct 2016 20:16:20 -0400 Subject: [PATCH 033/323] Need a later version of setuptools_scm until it's released. --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 0c33b1ce..0a6cb29f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,6 +10,8 @@ script: branches: except: - skeleton +before_deploy: +- pip install https://dl.dropboxusercontent.com/u/54081/cheeseshop/setuptools_scm-1.14.1b1.tar.gz deploy: provider: pypi server: https://upload.pypi.org/legacy/ From 42ecbe7706cd756c5c3dff103fa3ff65e8a02349 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 20 Oct 2016 15:38:47 -0400 Subject: [PATCH 034/323] Update to setuptools_scm 1.15.0rc1 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 0a6cb29f..6effc440 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,7 @@ branches: except: - skeleton before_deploy: -- pip install https://dl.dropboxusercontent.com/u/54081/cheeseshop/setuptools_scm-1.14.1b1.tar.gz +- pip install https://github.com/pypa/setuptools_scm/archive/v1.15.0rc1.tar.gz deploy: provider: pypi server: https://upload.pypi.org/legacy/ From 95fd34c61f8d9df2e9c559b3978c85e7d03cd8d8 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 20 Oct 2016 17:13:15 -0400 Subject: [PATCH 035/323] Gotta get an sdist - so use one jaraco built --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 6effc440..cad38c8f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,7 @@ branches: except: - skeleton before_deploy: -- pip install https://github.com/pypa/setuptools_scm/archive/v1.15.0rc1.tar.gz +- pip install https://dl.dropboxusercontent.com/u/54081/cheeseshop/setuptools_scm-1.15.0rc1.tar.gz deploy: provider: pypi server: https://upload.pypi.org/legacy/ From 200e6a525161b355d37862c9aee22c84e1413af4 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 24 Oct 2016 10:07:33 -0400 Subject: [PATCH 036/323] Bump to setuptools_scm 1.15.0. --- .travis.yml | 2 -- setup.py | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index cad38c8f..0c33b1ce 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,8 +10,6 @@ script: branches: except: - skeleton -before_deploy: -- pip install https://dl.dropboxusercontent.com/u/54081/cheeseshop/setuptools_scm-1.15.0rc1.tar.gz deploy: provider: pypi server: https://upload.pypi.org/legacy/ diff --git a/setup.py b/setup.py index 27ace5fd..83a22f68 100644 --- a/setup.py +++ b/setup.py @@ -32,7 +32,7 @@ extras_require={ }, setup_requires=[ - 'setuptools_scm>=1.9,!=1.13.1,!=1.14.0', + 'setuptools_scm>=1.15.0', ] + wheel, classifiers=[ "Development Status :: 5 - Production/Stable", From 4ee40ca2d13c2c8b544ad5f880193f5c0864648a Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 4 Nov 2016 09:49:30 -0400 Subject: [PATCH 037/323] Update config to support building on ReadTheDocs --- docs/conf.py | 2 +- docs/requirements.txt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 docs/requirements.txt diff --git a/docs/conf.py b/docs/conf.py index 5abe25ae..aa34defc 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -20,7 +20,7 @@ master_doc = 'index' link_files = { - 'CHANGES.rst': dict( + '../CHANGES.rst': dict( using=dict( GH='https://github.com', project=project, diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 00000000..442df9fa --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1 @@ +rst.linker From 18cb65f8f1c1eaf7b79a33fb5a2b7cbd1f851868 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 4 Nov 2016 10:16:12 -0400 Subject: [PATCH 038/323] Add note about the broken docs problem. --- README.rst | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.rst b/README.rst index bb3d9127..5196a108 100644 --- a/README.rst +++ b/README.rst @@ -8,6 +8,20 @@ .. image:: https://img.shields.io/travis/jaraco/skeleton/master.svg :target: http://travis-ci.org/jaraco/skeleton + +License +======= + License is indicated in the project metadata (typically one or more of the Trove classifiers). For more details, see `this explanation `_. + +Docs +==== + +There's `no good mechanism for publishing documentation +`_ +easily. If there's a documentation link above, it's probably +stale because PyPI-based documentation is deprecated. This +project may have documentation published at ReadTheDocs, but +probably not. Good luck finding it. From a50fb1c894f7985bb71edf8d0ce60ef4f350b745 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 15 Dec 2016 14:40:27 -0500 Subject: [PATCH 039/323] Skip upload docs as it's deprecated anyway --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 0c33b1ce..13ebb8aa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,3 +20,4 @@ deploy: user: jaraco # supply password with `travis encrypt --add deploy.password` distributions: dists + skip_upload_docs: true From 99ffa27f0e7bd2eae63c84a0ded567eba4a2394b Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 23 Dec 2016 08:25:01 -0500 Subject: [PATCH 040/323] Remove rant about docs. If there's no link to the docs, then this is the docs. --- README.rst | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/README.rst b/README.rst index 5196a108..0e0e26ee 100644 --- a/README.rst +++ b/README.rst @@ -15,13 +15,3 @@ License License is indicated in the project metadata (typically one or more of the Trove classifiers). For more details, see `this explanation `_. - -Docs -==== - -There's `no good mechanism for publishing documentation -`_ -easily. If there's a documentation link above, it's probably -stale because PyPI-based documentation is deprecated. This -project may have documentation published at ReadTheDocs, but -probably not. Good luck finding it. From 6245d0966d8dfb0fa2893c8a3e7d760c31d134d7 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 23 Dec 2016 08:28:03 -0500 Subject: [PATCH 041/323] Prefer get_distribution --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index aa34defc..46d614b9 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -13,7 +13,7 @@ copyright = '2016 Jason R. Coombs' # The short X.Y version. -version = pkg_resources.require(project)[0].version +version = pkg_resources.get_distribution(project).version # The full version, including alpha/beta/rc tags. release = version From 3da8cf4a6f14abf5da05c9d46f3362dcc43d71a4 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 23 Dec 2016 08:42:35 -0500 Subject: [PATCH 042/323] No longer rely on the package being installed to retrieve the version. Instead, load the project name and version by invoking the setup script. --- docs/conf.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 46d614b9..adc9df77 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,7 +1,9 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -import pkg_resources +import os +import sys +import subprocess extensions = [ 'sphinx.ext.autodoc', @@ -9,11 +11,15 @@ ] # General information about the project. -project = 'skeleton' + +root = os.path.join(os.path.dirname(__file__), '..') +setup_script = os.path.join(root, 'setup.py') +dist_info_cmd = [sys.executable, setup_script, '--name', '--version'] +output_bytes = subprocess.check_output(dist_info_cmd, cwd=root) +project, version = output_bytes.decode('utf-8').split() + copyright = '2016 Jason R. Coombs' -# The short X.Y version. -version = pkg_resources.get_distribution(project).version # The full version, including alpha/beta/rc tags. release = version From fbadf0344d4b9ac6917e8546b5529c20082f4733 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 23 Dec 2016 08:44:55 -0500 Subject: [PATCH 043/323] Also get the URL from the project metadata --- docs/conf.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index adc9df77..d52e40d3 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -14,9 +14,9 @@ root = os.path.join(os.path.dirname(__file__), '..') setup_script = os.path.join(root, 'setup.py') -dist_info_cmd = [sys.executable, setup_script, '--name', '--version'] +dist_info_cmd = [sys.executable, setup_script, '--name', '--version', '--url'] output_bytes = subprocess.check_output(dist_info_cmd, cwd=root) -project, version = output_bytes.decode('utf-8').split() +project, version, url = output_bytes.decode('utf-8').split() copyright = '2016 Jason R. Coombs' @@ -30,11 +30,12 @@ using=dict( GH='https://github.com', project=project, + url=url, ), replace=[ dict( pattern=r"(Issue )?#(?P\d+)", - url='{GH}/jaraco/{project}/issues/{issue}', + url='{url}/issues/{issue}', ), dict( pattern=r"^(?m)((?Pv?\d+(\.\d+){1,2}))\n[-=]+\n", From b2c592d84bc5f7c16a70b6d593fb320c5559eeee Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 23 Dec 2016 08:47:03 -0500 Subject: [PATCH 044/323] Also grab the author from the package metadata --- docs/conf.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index d52e40d3..23a24476 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -14,11 +14,12 @@ root = os.path.join(os.path.dirname(__file__), '..') setup_script = os.path.join(root, 'setup.py') -dist_info_cmd = [sys.executable, setup_script, '--name', '--version', '--url'] +fields = ['--name', '--version', '--url', '--author'] +dist_info_cmd = [sys.executable, setup_script] + fields output_bytes = subprocess.check_output(dist_info_cmd, cwd=root) -project, version, url = output_bytes.decode('utf-8').split() +project, version, url, author = output_bytes.decode('utf-8').split() -copyright = '2016 Jason R. Coombs' +copyright = '2016 ' + author # The full version, including alpha/beta/rc tags. release = version From b1133de832c3960777b9db80c070885c4bedd7c4 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 23 Dec 2016 08:57:20 -0500 Subject: [PATCH 045/323] Strip the trailing newline and then split on newline. --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 23a24476..7402f72f 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -17,7 +17,7 @@ fields = ['--name', '--version', '--url', '--author'] dist_info_cmd = [sys.executable, setup_script] + fields output_bytes = subprocess.check_output(dist_info_cmd, cwd=root) -project, version, url, author = output_bytes.decode('utf-8').split() +project, version, url, author = output_bytes.decode('utf-8').strip().split('\n') copyright = '2016 ' + author From 84b53d90527040c58c4236698c95cb6cd1f2736d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 2 Jan 2017 15:54:14 -0500 Subject: [PATCH 046/323] Default upload URL is now in Python 3.6. Use that. --- setup.cfg | 3 --- 1 file changed, 3 deletions(-) diff --git a/setup.cfg b/setup.cfg index 4659acce..e0803242 100644 --- a/setup.cfg +++ b/setup.cfg @@ -4,6 +4,3 @@ dists = clean --all sdist bdist_wheel [wheel] universal = 1 - -[upload] -repository = https://upload.pypi.org/legacy/ From 5853c7e7e738bc641f95835f239b04d8c7a853e3 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 5 Jan 2017 09:31:05 -0500 Subject: [PATCH 047/323] setup is already present in the module name. Just call them params. --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 83a22f68..45243679 100644 --- a/setup.py +++ b/setup.py @@ -16,7 +16,7 @@ name = 'skeleton' description = '' -setup_params = dict( +params = dict( name=name, use_scm_version=True, author="Jason R. Coombs", @@ -45,4 +45,4 @@ }, ) if __name__ == '__main__': - setuptools.setup(**setup_params) + setuptools.setup(**params) From 746dd7999f9db23276144ee2160920bd01ed860c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 17 Jan 2017 19:56:28 -0500 Subject: [PATCH 048/323] Use Python 3.6 by default --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 13ebb8aa..91ba39a9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,7 @@ sudo: false language: python python: - 2.7 -- 3.5 +- 3.6 install: - pip install tox "setuptools>=28.2" script: @@ -16,7 +16,7 @@ deploy: on: tags: true all_branches: true - python: 3.5 + python: 3.6 user: jaraco # supply password with `travis encrypt --add deploy.password` distributions: dists From 9f6eea591eaae483be11d13ebad06958a6a1e2c8 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 19 Jan 2017 11:13:08 -0500 Subject: [PATCH 049/323] No longer rely on setup_requires for wheel. --- setup.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/setup.py b/setup.py index 45243679..157ecb2e 100644 --- a/setup.py +++ b/setup.py @@ -3,16 +3,12 @@ # Project skeleton maintained at https://github.com/jaraco/skeleton import io -import sys import setuptools with io.open('README.rst', encoding='utf-8') as readme: long_description = readme.read() -needs_wheel = {'release', 'bdist_wheel', 'dists'}.intersection(sys.argv) -wheel = ['wheel'] if needs_wheel else [] - name = 'skeleton' description = '' @@ -33,7 +29,7 @@ }, setup_requires=[ 'setuptools_scm>=1.15.0', - ] + wheel, + ], classifiers=[ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", From 21a4e86e6fef7fba92afb4bcb49d79859d0cb2b2 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 23 Jan 2017 07:10:36 -0500 Subject: [PATCH 050/323] Add PEP substitution in changelog. --- docs/conf.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/conf.py b/docs/conf.py index 7402f72f..8639b2cc 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -42,6 +42,10 @@ pattern=r"^(?m)((?Pv?\d+(\.\d+){1,2}))\n[-=]+\n", with_scm="{text}\n{rev[timestamp]:%d %b %Y}\n", ), + dict( + pattern=r"PEP[- ](?P\d+)", + url='https://www.python.org/dev/peps/pep-{pep_number:0>4}/', + ), ], ), } From ee0d8647d8537b9de2aeafeba4acd74910f98a4f Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 24 Jan 2017 20:59:15 -0500 Subject: [PATCH 051/323] Add support for Python 2.6 in docs conf --- docs/conf.py | 3 +++ tests/requirements.txt | 1 + 2 files changed, 4 insertions(+) diff --git a/docs/conf.py b/docs/conf.py index 8639b2cc..7402c7a1 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -5,6 +5,9 @@ import sys import subprocess +if 'check_output' not in dir(subprocess): + import subprocess32 as subprocess + extensions = [ 'sphinx.ext.autodoc', 'rst.linker', diff --git a/tests/requirements.txt b/tests/requirements.txt index 70bc02f1..ab484054 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -1 +1,2 @@ pytest >= 2.8 +subprocess32; python_version=="2.6" From e690b031cc3c07b657dc235252a284d8023a38dc Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 9 Feb 2017 12:29:49 -0500 Subject: [PATCH 052/323] Set the origin date once and forget it. --- docs/conf.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 7402c7a1..bf6ae64f 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -4,6 +4,7 @@ import os import sys import subprocess +import datetime if 'check_output' not in dir(subprocess): import subprocess32 as subprocess @@ -22,7 +23,10 @@ output_bytes = subprocess.check_output(dist_info_cmd, cwd=root) project, version, url, author = output_bytes.decode('utf-8').strip().split('\n') -copyright = '2016 ' + author +origin_date = datetime.date(2017,1,1) +today = datetime.date.today() + +copyright = '{origin_date.year}-{today.year} {author}'.format(**locals()) # The full version, including alpha/beta/rc tags. release = version From b728c5892b394392044b245cba43f46740efb851 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 12 Mar 2017 13:33:19 -0400 Subject: [PATCH 053/323] Add python_requires directive. --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 157ecb2e..892b6b35 100644 --- a/setup.py +++ b/setup.py @@ -23,6 +23,7 @@ packages=setuptools.find_packages(), include_package_data=True, namespace_packages=name.split('.')[:-1], + python_requires='>=2.7', install_requires=[ ], extras_require={ From 59c37d70f1140bf18b9a48398cc4502ebce91b5e Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 29 Mar 2017 14:36:54 -0400 Subject: [PATCH 054/323] Don't bother with copyright year(s). Let the repository history track the changes and copyright years. YAGNI. --- docs/conf.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index bf6ae64f..fc947971 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -23,10 +23,7 @@ output_bytes = subprocess.check_output(dist_info_cmd, cwd=root) project, version, url, author = output_bytes.decode('utf-8').strip().split('\n') -origin_date = datetime.date(2017,1,1) -today = datetime.date.today() - -copyright = '{origin_date.year}-{today.year} {author}'.format(**locals()) +copyright = author # The full version, including alpha/beta/rc tags. release = version From 049284c6c440217c4f686b61c0980b5e0100626b Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 29 Mar 2017 14:39:02 -0400 Subject: [PATCH 055/323] Include the project (for docstrings). Include Sphinx (for environments where it's not an implied provision). --- docs/requirements.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/requirements.txt b/docs/requirements.txt index 442df9fa..c11e7555 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1 +1,3 @@ +. +sphinx rst.linker From b9bcd869482ea0ff636c8848896a94e24a6fbfca Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 29 Mar 2017 14:40:02 -0400 Subject: [PATCH 056/323] Include pytest-sugar for nicer test output. --- tests/requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/requirements.txt b/tests/requirements.txt index ab484054..6d65b375 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -1,2 +1,3 @@ pytest >= 2.8 +pytest-sugar subprocess32; python_version=="2.6" From 908cf4ad0e27813933ada7cc9f16ebce9ac0c6cc Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 30 Mar 2017 04:36:53 -0400 Subject: [PATCH 057/323] Rely on jaraco.packaging for loading the package metadata from the package for Sphinx. --- docs/conf.py | 27 ++------------------------- docs/requirements.txt | 3 ++- 2 files changed, 4 insertions(+), 26 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index fc947971..0e11c827 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,46 +1,23 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -import os -import sys -import subprocess -import datetime - -if 'check_output' not in dir(subprocess): - import subprocess32 as subprocess - extensions = [ 'sphinx.ext.autodoc', + 'jaraco.packaging.sphinx', 'rst.linker', ] -# General information about the project. - -root = os.path.join(os.path.dirname(__file__), '..') -setup_script = os.path.join(root, 'setup.py') -fields = ['--name', '--version', '--url', '--author'] -dist_info_cmd = [sys.executable, setup_script] + fields -output_bytes = subprocess.check_output(dist_info_cmd, cwd=root) -project, version, url, author = output_bytes.decode('utf-8').strip().split('\n') - -copyright = author - -# The full version, including alpha/beta/rc tags. -release = version - master_doc = 'index' link_files = { '../CHANGES.rst': dict( using=dict( GH='https://github.com', - project=project, - url=url, ), replace=[ dict( pattern=r"(Issue )?#(?P\d+)", - url='{url}/issues/{issue}', + url='{package_url}/issues/{issue}', ), dict( pattern=r"^(?m)((?Pv?\d+(\.\d+){1,2}))\n[-=]+\n", diff --git a/docs/requirements.txt b/docs/requirements.txt index c11e7555..e7b6a745 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,3 +1,4 @@ . sphinx -rst.linker +jaraco.packaging>=3.2 +rst.linker>=1.9 From 689f700fcfcdfcdc7d027f204a9654b101ac9ecb Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 30 Mar 2017 04:43:46 -0400 Subject: [PATCH 058/323] Use single-quotes to satisfy the style nazis. --- docs/conf.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 0e11c827..8bc82981 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -16,15 +16,15 @@ ), replace=[ dict( - pattern=r"(Issue )?#(?P\d+)", + pattern=r'(Issue )?#(?P\d+)', url='{package_url}/issues/{issue}', ), dict( - pattern=r"^(?m)((?Pv?\d+(\.\d+){1,2}))\n[-=]+\n", - with_scm="{text}\n{rev[timestamp]:%d %b %Y}\n", + pattern=r'^(?m)((?Pv?\d+(\.\d+){1,2}))\n[-=]+\n', + with_scm='{text}\n{rev[timestamp]:%d %b %Y}\n', ), dict( - pattern=r"PEP[- ](?P\d+)", + pattern=r'PEP[- ](?P\d+)', url='https://www.python.org/dev/peps/pep-{pep_number:0>4}/', ), ], From 23dae906a2563a2da30e0d67490fa009576f6439 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 30 Mar 2017 20:41:18 -0400 Subject: [PATCH 059/323] The requirement is no longer needed for tests. --- tests/requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/requirements.txt b/tests/requirements.txt index 6d65b375..d9e0f332 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -1,3 +1,2 @@ pytest >= 2.8 pytest-sugar -subprocess32; python_version=="2.6" From fbe7cb7fa1c0f4e30f6ac6e886c49ab6491aa959 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 21 Apr 2017 12:21:45 -0400 Subject: [PATCH 060/323] Add readthedocs yml file --- .readthedocs.yml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .readthedocs.yml diff --git a/.readthedocs.yml b/.readthedocs.yml new file mode 100644 index 00000000..e83d731b --- /dev/null +++ b/.readthedocs.yml @@ -0,0 +1,3 @@ +requirements_file: docs/requirements.txt +python: + version: 3 From 243e44fdb797ae54a08eb02d924f88e775e74ba9 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 21 Apr 2017 12:31:54 -0400 Subject: [PATCH 061/323] Move requirements for docs and testing into extras --- .readthedocs.yml | 4 +++- docs/requirements.txt | 4 ---- setup.py | 9 +++++++++ tests/requirements.txt | 2 -- tox.ini | 4 +--- 5 files changed, 13 insertions(+), 10 deletions(-) delete mode 100644 docs/requirements.txt delete mode 100644 tests/requirements.txt diff --git a/.readthedocs.yml b/.readthedocs.yml index e83d731b..8ae44684 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -1,3 +1,5 @@ -requirements_file: docs/requirements.txt python: version: 3 + extra_requirements: + - docs + pip_install: true diff --git a/docs/requirements.txt b/docs/requirements.txt deleted file mode 100644 index e7b6a745..00000000 --- a/docs/requirements.txt +++ /dev/null @@ -1,4 +0,0 @@ -. -sphinx -jaraco.packaging>=3.2 -rst.linker>=1.9 diff --git a/setup.py b/setup.py index 892b6b35..2bed3f7c 100644 --- a/setup.py +++ b/setup.py @@ -27,6 +27,15 @@ install_requires=[ ], extras_require={ + 'testing': [ + 'pytest>=2.8', + 'pytest-sugar', + ], + 'docs': [ + 'sphinx', + 'jaraco.packaging>=3.2', + 'rst.linker>=1.9', + ], }, setup_requires=[ 'setuptools_scm>=1.15.0', diff --git a/tests/requirements.txt b/tests/requirements.txt deleted file mode 100644 index d9e0f332..00000000 --- a/tests/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -pytest >= 2.8 -pytest-sugar diff --git a/tox.ini b/tox.ini index d740130c..8efcba6f 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,4 @@ [testenv] -deps = - -rtests/requirements.txt - commands = py.test {posargs} usedevelop = True +extras = testing From 3383a3aceb435cef929c13dff3e54e46af01cf49 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 26 Apr 2017 10:29:35 -0400 Subject: [PATCH 062/323] Add appveyor script for CI testing on Windows. --- appveyor.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 appveyor.yml diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 00000000..e856af3b --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,19 @@ +environment: + + APPVEYOR: true + + matrix: + - PYTHON: "C:\\Python36-x64" + - PYTHON: "C:\\Python27-x64" + +install: + # symlink python from a directory with a space + - "mklink /d \"C:\\Program Files\\Python\" %PYTHON%" + - "SET PYTHON=\"C:\\Program Files\\Python\"" + - "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%" + +build: off + +test_script: + - "python -m pip install tox" + - "tox" From 110cb56c59e99c5d0c630612f8593a7ef55ce732 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 28 Apr 2017 19:21:34 -0400 Subject: [PATCH 063/323] Require tox 2.4 or later; fixes #2. --- tox.ini | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tox.ini b/tox.ini index 8efcba6f..1ae06efe 100644 --- a/tox.ini +++ b/tox.ini @@ -1,3 +1,6 @@ +[tox] +minversion = 2.4 + [testenv] commands = py.test {posargs} usedevelop = True From c84284022a198d560e685c5a687458a5be4c5fe6 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 12 May 2017 21:42:53 -0400 Subject: [PATCH 064/323] Remove namespace_packages declaration, no longer needed. --- setup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/setup.py b/setup.py index 2bed3f7c..a7aeae12 100644 --- a/setup.py +++ b/setup.py @@ -22,7 +22,6 @@ url="https://github.com/jaraco/" + name, packages=setuptools.find_packages(), include_package_data=True, - namespace_packages=name.split('.')[:-1], python_requires='>=2.7', install_requires=[ ], From 31fb9d19ee7751ced7a0339268b2cd7b3ceb4701 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 4 Jun 2017 14:02:51 -0400 Subject: [PATCH 065/323] Use a simple build number rather than prefixing with '1.0.' --- appveyor.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/appveyor.yml b/appveyor.yml index e856af3b..0a8ce5c5 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -17,3 +17,5 @@ build: off test_script: - "python -m pip install tox" - "tox" + +version: '{build}' From 16d68a9fd19e6a726cc86fd1e3ec5f2d24788345 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 14 Aug 2017 08:14:57 -0400 Subject: [PATCH 066/323] Restore support for namespace package declaration, selected on a 'nspkg_technique' setting --- setup.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/setup.py b/setup.py index a7aeae12..72d901c4 100644 --- a/setup.py +++ b/setup.py @@ -11,6 +11,11 @@ name = 'skeleton' description = '' +nspkg_technique = 'native' +""" +Does this package use "native" namespace packages or +pkg_resources "managed" namespace packages? +""" params = dict( name=name, @@ -22,6 +27,10 @@ url="https://github.com/jaraco/" + name, packages=setuptools.find_packages(), include_package_data=True, + namespace_packages=( + name.split('.')[:-1] if nspkg_technique == 'managed' + else [] + ), python_requires='>=2.7', install_requires=[ ], From 250cb960021233160e78a6f2c2780cfc1c964b9c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 26 Aug 2017 15:35:09 -0400 Subject: [PATCH 067/323] Inspired by pypa/setuptools#1059, use the preferred bdist_wheel heading. --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index e0803242..b0c90cbf 100644 --- a/setup.cfg +++ b/setup.cfg @@ -2,5 +2,5 @@ release = dists upload dists = clean --all sdist bdist_wheel -[wheel] +[bdist_wheel] universal = 1 From 88d315ae9adab430bd36722da8c6ab74c2e79cf0 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 13 Sep 2017 04:24:11 -0400 Subject: [PATCH 068/323] Check the docs during tests --- setup.py | 1 + tox.ini | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 72d901c4..75f23712 100644 --- a/setup.py +++ b/setup.py @@ -38,6 +38,7 @@ 'testing': [ 'pytest>=2.8', 'pytest-sugar', + 'collective.checkdocs', ], 'docs': [ 'sphinx', diff --git a/tox.ini b/tox.ini index 1ae06efe..16bf78a7 100644 --- a/tox.ini +++ b/tox.ini @@ -2,6 +2,8 @@ minversion = 2.4 [testenv] -commands = py.test {posargs} +commands = + py.test {posargs} + python setup.py checkdocs usedevelop = True extras = testing From 835393c93e4fec867b5e2e0a638e7a14994c6b1d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 18 Sep 2017 18:05:16 -0400 Subject: [PATCH 069/323] Use stages in travis to have deployment depend on success in all Python versions. --- .travis.yml | 49 +++++++++++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/.travis.yml b/.travis.yml index 91ba39a9..e2914e2b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,23 +1,32 @@ +dist: trusty sudo: false language: python -python: -- 2.7 -- 3.6 + +jobs: + fast_finish: true + include: + - python: 2.7 + - python: &latest_py3 3.6 + - stage: deploy + if: tag IS present + python: *latest_py3 + install: skip + script: skip + before_deploy: python bootstrap.py + deploy: + provider: pypi + on: + tags: true + all_branches: true + user: jaraco + # supply password with `travis encrypt --add deploy.password` + distributions: dists + skip_cleanup: true + skip_upload_docs: true + +cache: pip + install: -- pip install tox "setuptools>=28.2" -script: -- tox -branches: - except: - - skeleton -deploy: - provider: pypi - server: https://upload.pypi.org/legacy/ - on: - tags: true - all_branches: true - python: 3.6 - user: jaraco - # supply password with `travis encrypt --add deploy.password` - distributions: dists - skip_upload_docs: true +- pip install tox + +script: tox From a419524c69e7c2f8b9327ff2f6ec9f61e89c9c30 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 19 Sep 2017 19:46:21 -0400 Subject: [PATCH 070/323] Remove 'bootstrap', artifact from setuptools --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index e2914e2b..7932518e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,7 +12,6 @@ jobs: python: *latest_py3 install: skip script: skip - before_deploy: python bootstrap.py deploy: provider: pypi on: From a953d1baa6c6cd2b4b9e5f06378a706b3555259d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 21 Sep 2017 16:36:17 -0400 Subject: [PATCH 071/323] --add doesn't work in a list --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 7932518e..d4eecec2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,7 +18,8 @@ jobs: tags: true all_branches: true user: jaraco - # supply password with `travis encrypt --add deploy.password` + password: + secure: ... # encrypt password with `travis encrypt` distributions: dists skip_cleanup: true skip_upload_docs: true From 5dc924c102a23d06cafd8e0850f0f35582cbd9aa Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 26 Sep 2017 12:02:30 +0100 Subject: [PATCH 072/323] Add a license file. Fixes jaraco/skeleton#1. --- LICENSE | 7 +++++++ README.rst | 8 -------- 2 files changed, 7 insertions(+), 8 deletions(-) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..5e795a61 --- /dev/null +++ b/LICENSE @@ -0,0 +1,7 @@ +Copyright Jason R. Coombs + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.rst b/README.rst index 0e0e26ee..1c5d10ec 100644 --- a/README.rst +++ b/README.rst @@ -7,11 +7,3 @@ .. image:: https://img.shields.io/travis/jaraco/skeleton/master.svg :target: http://travis-ci.org/jaraco/skeleton - - -License -======= - -License is indicated in the project metadata (typically one or more -of the Trove classifiers). For more details, see `this explanation -`_. From d149ed4d5dc659c81d0567b227523d95ee8e04c3 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 16 Oct 2017 12:13:14 -0400 Subject: [PATCH 073/323] Remove downloads shield, no longer available. --- README.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.rst b/README.rst index 1c5d10ec..5161ae54 100644 --- a/README.rst +++ b/README.rst @@ -3,7 +3,5 @@ .. image:: https://img.shields.io/pypi/pyversions/skeleton.svg -.. image:: https://img.shields.io/pypi/dm/skeleton.svg - .. image:: https://img.shields.io/travis/jaraco/skeleton/master.svg :target: http://travis-ci.org/jaraco/skeleton From 34958ccc94b8c473ebf8adcdc35b82b6023d8702 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 17 Oct 2017 19:47:02 -0400 Subject: [PATCH 074/323] Add documentation badge. --- README.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.rst b/README.rst index 5161ae54..bcdb31e5 100644 --- a/README.rst +++ b/README.rst @@ -5,3 +5,6 @@ .. image:: https://img.shields.io/travis/jaraco/skeleton/master.svg :target: http://travis-ci.org/jaraco/skeleton + +.. image:: https://readthedocs.org/projects/skeleton/badge/?version=latest + :target: http://skeleton.readthedocs.io/en/latest/?badge=latest From 6c36336e9fc45048ad43e4ff494c9d1ffc14fc49 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 13 Nov 2017 09:14:44 -0500 Subject: [PATCH 075/323] Normalize indentation in docs/conf.py --- docs/conf.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 8bc82981..14744ee8 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -2,9 +2,9 @@ # -*- coding: utf-8 -*- extensions = [ - 'sphinx.ext.autodoc', - 'jaraco.packaging.sphinx', - 'rst.linker', + 'sphinx.ext.autodoc', + 'jaraco.packaging.sphinx', + 'rst.linker', ] master_doc = 'index' From 99622ab0e3d295a3ec17f69fb21dc68c94cc7fda Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 15 Nov 2017 10:26:38 -0500 Subject: [PATCH 076/323] Declare 'python' factor at top level --- .travis.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index d4eecec2..c7ed90b2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,11 +2,12 @@ dist: trusty sudo: false language: python +python: +- 2.7 +- &latest_py3 3.6 + jobs: fast_finish: true - include: - - python: 2.7 - - python: &latest_py3 3.6 - stage: deploy if: tag IS present python: *latest_py3 From ce5e0eb6ef5943bfa15edf5f9ea3f74e71ab00e4 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 16 Nov 2017 13:17:16 -0500 Subject: [PATCH 077/323] Correct travis syntax --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index c7ed90b2..61180dba 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,7 @@ python: jobs: fast_finish: true + include: - stage: deploy if: tag IS present python: *latest_py3 From b4b4c1116886f7cb10729a2d42272e41618ca20f Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 16 Nov 2017 15:45:57 -0500 Subject: [PATCH 078/323] reference the license file in metadata --- setup.cfg | 3 +++ 1 file changed, 3 insertions(+) diff --git a/setup.cfg b/setup.cfg index b0c90cbf..378a8e4f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -4,3 +4,6 @@ dists = clean --all sdist bdist_wheel [bdist_wheel] universal = 1 + +[metadata] +license_file = LICENSE From dd475b7ad1784a564929c711781eebd9b85ba063 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 2 Jan 2018 19:32:19 -0500 Subject: [PATCH 079/323] Use https --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index bcdb31e5..043561df 100644 --- a/README.rst +++ b/README.rst @@ -4,7 +4,7 @@ .. image:: https://img.shields.io/pypi/pyversions/skeleton.svg .. image:: https://img.shields.io/travis/jaraco/skeleton/master.svg - :target: http://travis-ci.org/jaraco/skeleton + :target: https://travis-ci.org/jaraco/skeleton .. image:: https://readthedocs.org/projects/skeleton/badge/?version=latest - :target: http://skeleton.readthedocs.io/en/latest/?badge=latest + :target: https://skeleton.readthedocs.io/en/latest/?badge=latest From 97c492d425da509f96f554a66499d13723d147bf Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 2 Jan 2018 19:32:38 -0500 Subject: [PATCH 080/323] Add build-docs env in tox. --- tox.ini | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tox.ini b/tox.ini index 16bf78a7..c3b6d2e6 100644 --- a/tox.ini +++ b/tox.ini @@ -7,3 +7,11 @@ commands = python setup.py checkdocs usedevelop = True extras = testing + +[testenv:build-docs] +extras = + docs + testing +changedir = docs +commands = + python -m sphinx . {toxinidir}/build/html From 63659653697e9037ed5cb5a770dc00b07c77e5a9 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 3 Jan 2018 15:34:05 -0500 Subject: [PATCH 081/323] Run only default environment by default. --- tox.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/tox.ini b/tox.ini index c3b6d2e6..7cccd421 100644 --- a/tox.ini +++ b/tox.ini @@ -1,4 +1,5 @@ [tox] +envlist = python minversion = 2.4 [testenv] From 99d850f0ed9852993626d4869e9f096e1643be6d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 17 Jan 2018 09:55:12 -0500 Subject: [PATCH 082/323] To support namespace packages, Setuptools must be 31.0.1. This change is necessary with the adoption of tox-venv, which uses Python's venv, which does not install the latest setuptools by default. --- tox.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tox.ini b/tox.ini index 7cccd421..df1b0eff 100644 --- a/tox.ini +++ b/tox.ini @@ -3,6 +3,8 @@ envlist = python minversion = 2.4 [testenv] +deps = + setuptools>=31.0.1 commands = py.test {posargs} python setup.py checkdocs From bbc018de3cbee4bccdcbb58b637c0c659e6e37e1 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 26 Jan 2018 11:59:01 -0500 Subject: [PATCH 083/323] Need to avoid .eggs in recursing dirs. Ref pypa/setuptools_scm#212. --- pytest.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pytest.ini b/pytest.ini index 56a87745..1b2f6247 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,4 +1,4 @@ [pytest] -norecursedirs=dist build .tox +norecursedirs=dist build .tox .eggs addopts=--doctest-modules doctest_optionflags=ALLOW_UNICODE ELLIPSIS From b9aba822c7e154d1ad185585fe7642947b3c5265 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 29 Jan 2018 09:06:00 -0500 Subject: [PATCH 084/323] Use tox-venv for future compatibility. --- .travis.yml | 2 +- appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 61180dba..85540ec0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,6 +29,6 @@ jobs: cache: pip install: -- pip install tox +- pip install tox tox-venv script: tox diff --git a/appveyor.yml b/appveyor.yml index 0a8ce5c5..3d55a92b 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -15,7 +15,7 @@ install: build: off test_script: - - "python -m pip install tox" + - "python -m pip install tox tox-venv" - "tox" version: '{build}' From 34ab781f8768ab5101f087cfffe7e38b94048a7f Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 1 Feb 2018 09:28:53 -0500 Subject: [PATCH 085/323] Disable pytest-sugar until Frozenball/pytest-sugar#133 is addressed. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 75f23712..e07ba771 100644 --- a/setup.py +++ b/setup.py @@ -37,7 +37,7 @@ extras_require={ 'testing': [ 'pytest>=2.8', - 'pytest-sugar', + # 'pytest-sugar', 'collective.checkdocs', ], 'docs': [ From 3902aabd7f5c4cb0f4aba8d2785da98e87cb7d6a Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 10 Feb 2018 13:24:23 -0500 Subject: [PATCH 086/323] Bring back pytest-sugar with a minimum version to support Pytest 3.4. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index e07ba771..9e73e231 100644 --- a/setup.py +++ b/setup.py @@ -37,7 +37,7 @@ extras_require={ 'testing': [ 'pytest>=2.8', - # 'pytest-sugar', + 'pytest-sugar>=0.9.1', 'collective.checkdocs', ], 'docs': [ From a8f66602f22459be95f8463e8cf6de1e653b352c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 20 Feb 2018 08:45:54 -0500 Subject: [PATCH 087/323] Save the pip cache across builds. Ref pypa/setuptools#1279. --- appveyor.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/appveyor.yml b/appveyor.yml index 3d55a92b..2b7808f9 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -14,6 +14,9 @@ install: build: off +cache: + - '%LOCALAPPDATA%\pip\Cache' + test_script: - "python -m pip install tox tox-venv" - "tox" From 41b814aa6cff3c46788a1d410095061a82af2076 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 28 Feb 2018 09:43:16 -0500 Subject: [PATCH 088/323] Add workaround for build failures on Python 3.7 (yaml/pyyaml#126). --- tox.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tox.ini b/tox.ini index df1b0eff..47eae514 100644 --- a/tox.ini +++ b/tox.ini @@ -5,6 +5,8 @@ minversion = 2.4 [testenv] deps = setuptools>=31.0.1 + # workaround for yaml/pyyaml#126 + # git+https://github.com/yaml/pyyaml@master#egg=pyyaml commands = py.test {posargs} python setup.py checkdocs From 40da2c65007ccc250c7d897d497ef2b3fb58f3d4 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 3 Mar 2018 13:33:07 -0500 Subject: [PATCH 089/323] Run flake8 with tests. Add flake8 config to ignore common exclusions. Add comments to testing and docs extras to aid with merges. --- .flake8 | 2 ++ pytest.ini | 2 +- setup.py | 7 +++++++ 3 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 .flake8 diff --git a/.flake8 b/.flake8 new file mode 100644 index 00000000..e9955e71 --- /dev/null +++ b/.flake8 @@ -0,0 +1,2 @@ +[flake8] +ignore = W191,W503 diff --git a/pytest.ini b/pytest.ini index 1b2f6247..0ba22c33 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,4 +1,4 @@ [pytest] norecursedirs=dist build .tox .eggs -addopts=--doctest-modules +addopts=--doctest-modules --flake8 doctest_optionflags=ALLOW_UNICODE ELLIPSIS diff --git a/setup.py b/setup.py index 9e73e231..c5ad4b1b 100644 --- a/setup.py +++ b/setup.py @@ -36,14 +36,21 @@ ], extras_require={ 'testing': [ + # upstream 'pytest>=2.8', 'pytest-sugar>=0.9.1', 'collective.checkdocs', + 'pytest-flake8', + + # local ], 'docs': [ + # upstream 'sphinx', 'jaraco.packaging>=3.2', 'rst.linker>=1.9', + + # local ], }, setup_requires=[ From d5d22342cfd4bd0ebebefa98765ae1dfc5770bb2 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 5 Mar 2018 09:50:36 -0500 Subject: [PATCH 090/323] Add appveyor badge (commented). Disable RTD by default. --- README.rst | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 043561df..78750be7 100644 --- a/README.rst +++ b/README.rst @@ -6,5 +6,8 @@ .. image:: https://img.shields.io/travis/jaraco/skeleton/master.svg :target: https://travis-ci.org/jaraco/skeleton -.. image:: https://readthedocs.org/projects/skeleton/badge/?version=latest - :target: https://skeleton.readthedocs.io/en/latest/?badge=latest +.. .. image:: https://img.shields.io/appveyor/ci/jaraco/skeleton/master.svg +.. :target: https://ci.appveyor.com/project/jaraco/skeleton/branch/master + +.. .. image:: https://readthedocs.org/projects/skeleton/badge/?version=latest +.. :target: https://skeleton.readthedocs.io/en/latest/?badge=latest From cd31f81d564c1cd93c7cbf0b1b19c034e310ad52 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 7 Mar 2018 17:28:14 -0500 Subject: [PATCH 091/323] Limit workaround to affected Python --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 47eae514..c2bac458 100644 --- a/tox.ini +++ b/tox.ini @@ -6,7 +6,7 @@ minversion = 2.4 deps = setuptools>=31.0.1 # workaround for yaml/pyyaml#126 - # git+https://github.com/yaml/pyyaml@master#egg=pyyaml + # git+https://github.com/yaml/pyyaml@master#egg=pyyaml;python_version=="3.7" commands = py.test {posargs} python setup.py checkdocs From 021b18b89cf83977397350ebe54603032086baf6 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 27 Mar 2018 15:57:25 -0400 Subject: [PATCH 092/323] Bump minimum pytest version --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index c5ad4b1b..62211249 100644 --- a/setup.py +++ b/setup.py @@ -37,7 +37,7 @@ extras_require={ 'testing': [ # upstream - 'pytest>=2.8', + 'pytest>=3.5', 'pytest-sugar>=0.9.1', 'collective.checkdocs', 'pytest-flake8', From e302df43fc90a3db2bc9119c9e0dad08a754c0f5 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 10 Apr 2018 09:19:48 -0400 Subject: [PATCH 093/323] Add pyproject.toml declaring build dependencies. --- pyproject.toml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 pyproject.toml diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..3ef243cc --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,2 @@ +[build-system] +requires = ["setuptools", "wheel", "setuptools_scm>=1.15"] From 9f06de212eb53c35ea52781796c58761fcd06de3 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 12 Apr 2018 11:36:04 -0400 Subject: [PATCH 094/323] When ignoring linter warnings, document the reason. --- .flake8 | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.flake8 b/.flake8 index e9955e71..df5f5271 100644 --- a/.flake8 +++ b/.flake8 @@ -1,2 +1,6 @@ [flake8] -ignore = W191,W503 +ignore = + # Allow tabs for indentation + W191 + # W503 violates spec https://github.com/PyCQA/pycodestyle/issues/513 + W503 From 53c017416d74f96c49fde361c0a5b774ceac00c6 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 9 Jun 2018 15:31:39 -0400 Subject: [PATCH 095/323] Disable the (broken) IPv6 in Travis. Ref travis-ci/travis-ci#8361. --- .travis.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.travis.yml b/.travis.yml index 85540ec0..e22ab6ff 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,4 +31,9 @@ cache: pip install: - pip install tox tox-venv +before_script: + # Disable IPv6. Ref travis-ci/travis-ci#8361 + - if [ "${TRAVIS_OS_NAME}" == "linux" ]; then + sudo sh -c 'echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6'; + fi script: tox From a4bdfe7caddd6b1bf4149f2d02adee727168ff8a Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 11 Jun 2018 08:59:18 -0400 Subject: [PATCH 096/323] Don't match issues if preceeded by some other indicator. --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 14744ee8..aeda56c0 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -16,7 +16,7 @@ ), replace=[ dict( - pattern=r'(Issue )?#(?P\d+)', + pattern=r'(Issue #|\B#)(?P\d+)', url='{package_url}/issues/{issue}', ), dict( From 7c9ad053c3ef0b66cc71431fb619da8e3a12bc26 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 14 Jun 2018 08:25:24 -0400 Subject: [PATCH 097/323] skip_upload_docs is default --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index e22ab6ff..2560fc39 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,7 +24,6 @@ jobs: secure: ... # encrypt password with `travis encrypt` distributions: dists skip_cleanup: true - skip_upload_docs: true cache: pip From 67c79e3182614e96fd4cf3a4813932b1edeff262 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 14 Jun 2018 12:23:57 -0400 Subject: [PATCH 098/323] Drop the dot; http://blog.pytest.org/2016/whats-new-in-pytest-30/ --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index c2bac458..c6c14f07 100644 --- a/tox.ini +++ b/tox.ini @@ -8,7 +8,7 @@ deps = # workaround for yaml/pyyaml#126 # git+https://github.com/yaml/pyyaml@master#egg=pyyaml;python_version=="3.7" commands = - py.test {posargs} + pytest {posargs} python setup.py checkdocs usedevelop = True extras = testing From 440adac3c3f91519a1ff47114774dbd1d5baf676 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 14 Jun 2018 15:09:33 -0400 Subject: [PATCH 099/323] Rely on declarative config to create long_description. --- pyproject.toml | 2 +- setup.cfg | 1 + setup.py | 6 ------ 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 3ef243cc..1af54cbd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,2 +1,2 @@ [build-system] -requires = ["setuptools", "wheel", "setuptools_scm>=1.15"] +requires = ["setuptools>=30.3", "wheel", "setuptools_scm>=1.15"] diff --git a/setup.cfg b/setup.cfg index 378a8e4f..ff90351b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -7,3 +7,4 @@ universal = 1 [metadata] license_file = LICENSE +long_description = file:README.rst diff --git a/setup.py b/setup.py index 62211249..4afc6282 100644 --- a/setup.py +++ b/setup.py @@ -2,13 +2,8 @@ # Project skeleton maintained at https://github.com/jaraco/skeleton -import io - import setuptools -with io.open('README.rst', encoding='utf-8') as readme: - long_description = readme.read() - name = 'skeleton' description = '' nspkg_technique = 'native' @@ -23,7 +18,6 @@ author="Jason R. Coombs", author_email="jaraco@jaraco.com", description=description or name, - long_description=long_description, url="https://github.com/jaraco/" + name, packages=setuptools.find_packages(), include_package_data=True, From 15024f12b5d4e90aee4f9a780efa263f47865d96 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 27 Jun 2018 21:34:07 -0400 Subject: [PATCH 100/323] Remove workaround for pyyaml 126. --- tox.ini | 2 -- 1 file changed, 2 deletions(-) diff --git a/tox.ini b/tox.ini index c6c14f07..41e20a33 100644 --- a/tox.ini +++ b/tox.ini @@ -5,8 +5,6 @@ minversion = 2.4 [testenv] deps = setuptools>=31.0.1 - # workaround for yaml/pyyaml#126 - # git+https://github.com/yaml/pyyaml@master#egg=pyyaml;python_version=="3.7" commands = pytest {posargs} python setup.py checkdocs From f8462db925cbfa2ca0721c84376c23026633a730 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 3 Jul 2018 11:13:00 -0400 Subject: [PATCH 101/323] Revert "Remove workaround for pyyaml 126." This reverts commit 15024f12b5d4e90aee4f9a780efa263f47865d96. --- tox.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tox.ini b/tox.ini index 41e20a33..c6c14f07 100644 --- a/tox.ini +++ b/tox.ini @@ -5,6 +5,8 @@ minversion = 2.4 [testenv] deps = setuptools>=31.0.1 + # workaround for yaml/pyyaml#126 + # git+https://github.com/yaml/pyyaml@master#egg=pyyaml;python_version=="3.7" commands = pytest {posargs} python setup.py checkdocs From ed475c925d31ec146979f12b0ebf1e1021335e31 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 3 Jul 2018 11:15:33 -0400 Subject: [PATCH 102/323] We're getting close, but Python 3.7 still requires a workaround --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index c6c14f07..5925d3e1 100644 --- a/tox.ini +++ b/tox.ini @@ -6,7 +6,7 @@ minversion = 2.4 deps = setuptools>=31.0.1 # workaround for yaml/pyyaml#126 - # git+https://github.com/yaml/pyyaml@master#egg=pyyaml;python_version=="3.7" + # pyyaml>=4.2b2;python_version=="3.7" commands = pytest {posargs} python setup.py checkdocs From beb0e0eb774dd15e79574ace338b885101d86d4b Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 8 Aug 2018 15:14:55 -0400 Subject: [PATCH 103/323] Use xenial to include support for Python 3.7. --- .travis.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2560fc39..b54e8e52 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,11 @@ -dist: trusty +dist: xenial sudo: false language: python python: - 2.7 -- &latest_py3 3.6 +- 3.6 +- &latest_py3 3.7 jobs: fast_finish: true From 5d245bb8ca22194dbf17e69f7db5f082101f931c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 20 Aug 2018 17:15:48 -0400 Subject: [PATCH 104/323] Remove release, no longer needed. Use twine instead. --- setup.cfg | 1 - 1 file changed, 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index ff90351b..8fdad4fa 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,4 @@ [aliases] -release = dists upload dists = clean --all sdist bdist_wheel [bdist_wheel] From bf8c57034ab857eb5b642fbc137a811276e8067a Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 20 Aug 2018 17:17:12 -0400 Subject: [PATCH 105/323] Also ignore W504 in flake8, following the indication in OCA/maintainer-quality-tools that neither W503 nor W504 are worthwhile in general. --- .flake8 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.flake8 b/.flake8 index df5f5271..c85d34a7 100644 --- a/.flake8 +++ b/.flake8 @@ -4,3 +4,5 @@ ignore = W191 # W503 violates spec https://github.com/PyCQA/pycodestyle/issues/513 W503 + # W504 has issues https://github.com/OCA/maintainer-quality-tools/issues/545 + W504 From 8cd0459a9012ad5070c5b2364d9835653e6d58b7 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 28 Aug 2018 08:54:42 -0400 Subject: [PATCH 106/323] Release of pyyaml 3.13 seems to have fixed install issues on Python 3.7. --- tox.ini | 2 -- 1 file changed, 2 deletions(-) diff --git a/tox.ini b/tox.ini index 5925d3e1..41e20a33 100644 --- a/tox.ini +++ b/tox.ini @@ -5,8 +5,6 @@ minversion = 2.4 [testenv] deps = setuptools>=31.0.1 - # workaround for yaml/pyyaml#126 - # pyyaml>=4.2b2;python_version=="3.7" commands = pytest {posargs} python setup.py checkdocs From 5633116de34f53c892d1f2c6d0f7de14c965cfa7 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 28 Aug 2018 12:59:25 -0400 Subject: [PATCH 107/323] Block pytest 3.7.3 due to pytest-dev/pytest#3888. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 4afc6282..ba0fb89a 100644 --- a/setup.py +++ b/setup.py @@ -31,7 +31,7 @@ extras_require={ 'testing': [ # upstream - 'pytest>=3.5', + 'pytest>=3.5,!=3.7.3', 'pytest-sugar>=0.9.1', 'collective.checkdocs', 'pytest-flake8', From 9cdf6ef1f4401b1ec4b032f1b61c0f4f7fd78b8d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 17 Sep 2018 10:08:54 -0400 Subject: [PATCH 108/323] Move most package config to declarative config --- setup.cfg | 38 ++++++++++++++++++++++++++++++++++++ setup.py | 58 +------------------------------------------------------ 2 files changed, 39 insertions(+), 57 deletions(-) diff --git a/setup.cfg b/setup.cfg index 8fdad4fa..adaed86d 100644 --- a/setup.cfg +++ b/setup.cfg @@ -6,4 +6,42 @@ universal = 1 [metadata] license_file = LICENSE +name = skeleton +author = Jason R. Coombs +author_email = jaraco@jaraco.com +description = skeleton long_description = file:README.rst +url = https://github.com/jaraco/skeleton +classifiers = + Development Status :: 5 - Production/Stable + Intended Audience :: Developers + License :: OSI Approved :: MIT License + Programming Language :: Python :: 2.7 + Programming Language :: Python :: 3 + +[options] +packages = find: +include_package_data = true +python_requires = >=2.7 +install_requires = +setup_requires = setuptools_scm >= 1.15.0 + +[options.extras_require] +testing = + # upstream + pytest >= 3.5, !=3.7.3 + pytest-sugar >= 0.9.1 + collective.checkdocs + pytest-flake8 + + # local + +docs = + # upstream + sphinx + jaraco.packaging >= 3.2 + rst.linker >= 1.9 + + # local + +[options.entry_points] diff --git a/setup.py b/setup.py index ba0fb89a..c990c529 100644 --- a/setup.py +++ b/setup.py @@ -4,61 +4,5 @@ import setuptools -name = 'skeleton' -description = '' -nspkg_technique = 'native' -""" -Does this package use "native" namespace packages or -pkg_resources "managed" namespace packages? -""" - -params = dict( - name=name, - use_scm_version=True, - author="Jason R. Coombs", - author_email="jaraco@jaraco.com", - description=description or name, - url="https://github.com/jaraco/" + name, - packages=setuptools.find_packages(), - include_package_data=True, - namespace_packages=( - name.split('.')[:-1] if nspkg_technique == 'managed' - else [] - ), - python_requires='>=2.7', - install_requires=[ - ], - extras_require={ - 'testing': [ - # upstream - 'pytest>=3.5,!=3.7.3', - 'pytest-sugar>=0.9.1', - 'collective.checkdocs', - 'pytest-flake8', - - # local - ], - 'docs': [ - # upstream - 'sphinx', - 'jaraco.packaging>=3.2', - 'rst.linker>=1.9', - - # local - ], - }, - setup_requires=[ - 'setuptools_scm>=1.15.0', - ], - classifiers=[ - "Development Status :: 5 - Production/Stable", - "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", - "Programming Language :: Python :: 2.7", - "Programming Language :: Python :: 3", - ], - entry_points={ - }, -) if __name__ == '__main__': - setuptools.setup(**params) + setuptools.setup(use_scm_version=True) From f8a537f300727a87c1a4a663a5df8b6e05e1020d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 19 Sep 2018 22:31:38 -0400 Subject: [PATCH 109/323] Ignore pycodestyle warning. Seems it's not going to be fixed anytime soon. --- pytest.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pytest.ini b/pytest.ini index 0ba22c33..d0ba39d3 100644 --- a/pytest.ini +++ b/pytest.ini @@ -2,3 +2,5 @@ norecursedirs=dist build .tox .eggs addopts=--doctest-modules --flake8 doctest_optionflags=ALLOW_UNICODE ELLIPSIS +filterwarnings= + ignore:Possible nested set::pycodestyle:113 From 106c755f83d701003afca33e0ba22c20c3a31c97 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 24 Sep 2018 18:08:59 -0400 Subject: [PATCH 110/323] Also ignore flake8 error --- pytest.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/pytest.ini b/pytest.ini index d0ba39d3..61dab3d4 100644 --- a/pytest.ini +++ b/pytest.ini @@ -4,3 +4,4 @@ addopts=--doctest-modules --flake8 doctest_optionflags=ALLOW_UNICODE ELLIPSIS filterwarnings= ignore:Possible nested set::pycodestyle:113 + ignore:Using or importing the ABCs::flake8:410 From 59cceeb35bd1005a16bd9985e623b10c82527e58 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 28 Oct 2018 23:24:08 -0400 Subject: [PATCH 111/323] Require setuptools 34.4 to support python_requires in declarative config. --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 1af54cbd..65d74263 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,2 +1,2 @@ [build-system] -requires = ["setuptools>=30.3", "wheel", "setuptools_scm>=1.15"] +requires = ["setuptools>=34.4", "wheel", "setuptools_scm>=1.15"] From 166b43e1429fa1b9b467da82109151222719cc20 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 7 Nov 2018 17:47:20 -0500 Subject: [PATCH 112/323] Add workaround for Frozenball/pytest-sugar#159. --- tox.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/tox.ini b/tox.ini index 41e20a33..78cc7f9f 100644 --- a/tox.ini +++ b/tox.ini @@ -5,6 +5,7 @@ minversion = 2.4 [testenv] deps = setuptools>=31.0.1 + pytest-sugar-bugfix159 commands = pytest {posargs} python setup.py checkdocs From d0f07a4e7ad465b0935bf85da94b12b9b8cc2e77 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 9 Nov 2018 22:17:36 -0500 Subject: [PATCH 113/323] Add black config, pre-commit including black, check code with black. --- .flake8 | 5 +++-- .pre-commit-config.yaml | 5 +++++ README.rst | 4 ++++ docs/conf.py | 44 ++++++++++++++++++++--------------------- pyproject.toml | 3 +++ pytest.ini | 2 +- setup.cfg | 1 + setup.py | 2 +- 8 files changed, 40 insertions(+), 26 deletions(-) create mode 100644 .pre-commit-config.yaml diff --git a/.flake8 b/.flake8 index c85d34a7..790c109f 100644 --- a/.flake8 +++ b/.flake8 @@ -1,8 +1,9 @@ [flake8] +max-line-length = 88 ignore = - # Allow tabs for indentation - W191 # W503 violates spec https://github.com/PyCQA/pycodestyle/issues/513 W503 # W504 has issues https://github.com/OCA/maintainer-quality-tools/issues/545 W504 + # Black creates whitespace before colon + E203 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..922d9424 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,5 @@ +repos: +- repo: https://github.com/ambv/black + rev: 18.9b0 + hooks: + - id: black diff --git a/README.rst b/README.rst index 78750be7..7050da33 100644 --- a/README.rst +++ b/README.rst @@ -6,6 +6,10 @@ .. image:: https://img.shields.io/travis/jaraco/skeleton/master.svg :target: https://travis-ci.org/jaraco/skeleton +.. image:: https://img.shields.io/badge/code%20style-black-000000.svg + :target: https://github.com/ambv/black + :alt: Code style: Black + .. .. image:: https://img.shields.io/appveyor/ci/jaraco/skeleton/master.svg .. :target: https://ci.appveyor.com/project/jaraco/skeleton/branch/master diff --git a/docs/conf.py b/docs/conf.py index aeda56c0..d9ea1a63 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -2,31 +2,31 @@ # -*- coding: utf-8 -*- extensions = [ - 'sphinx.ext.autodoc', - 'jaraco.packaging.sphinx', - 'rst.linker', + 'sphinx.ext.autodoc', + 'jaraco.packaging.sphinx', + 'rst.linker', ] master_doc = 'index' link_files = { - '../CHANGES.rst': dict( - using=dict( - GH='https://github.com', - ), - replace=[ - dict( - pattern=r'(Issue #|\B#)(?P\d+)', - url='{package_url}/issues/{issue}', - ), - dict( - pattern=r'^(?m)((?Pv?\d+(\.\d+){1,2}))\n[-=]+\n', - with_scm='{text}\n{rev[timestamp]:%d %b %Y}\n', - ), - dict( - pattern=r'PEP[- ](?P\d+)', - url='https://www.python.org/dev/peps/pep-{pep_number:0>4}/', - ), - ], - ), + '../CHANGES.rst': dict( + using=dict( + GH='https://github.com', + ), + replace=[ + dict( + pattern=r'(Issue #|\B#)(?P\d+)', + url='{package_url}/issues/{issue}', + ), + dict( + pattern=r'^(?m)((?Pv?\d+(\.\d+){1,2}))\n[-=]+\n', + with_scm='{text}\n{rev[timestamp]:%d %b %Y}\n', + ), + dict( + pattern=r'PEP[- ](?P\d+)', + url='https://www.python.org/dev/peps/pep-{pep_number:0>4}/', + ), + ], + ), } diff --git a/pyproject.toml b/pyproject.toml index 65d74263..a8b44c14 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,2 +1,5 @@ [build-system] requires = ["setuptools>=34.4", "wheel", "setuptools_scm>=1.15"] + +[tool.black] +skip-string-normalization = true diff --git a/pytest.ini b/pytest.ini index 61dab3d4..15bb8b72 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,6 +1,6 @@ [pytest] norecursedirs=dist build .tox .eggs -addopts=--doctest-modules --flake8 +addopts=--doctest-modules --flake8 --black doctest_optionflags=ALLOW_UNICODE ELLIPSIS filterwarnings= ignore:Possible nested set::pycodestyle:113 diff --git a/setup.cfg b/setup.cfg index adaed86d..78a0e465 100644 --- a/setup.cfg +++ b/setup.cfg @@ -33,6 +33,7 @@ testing = pytest-sugar >= 0.9.1 collective.checkdocs pytest-flake8 + pytest-black # local diff --git a/setup.py b/setup.py index c990c529..3435b2ca 100644 --- a/setup.py +++ b/setup.py @@ -5,4 +5,4 @@ import setuptools if __name__ == '__main__': - setuptools.setup(use_scm_version=True) + setuptools.setup(use_scm_version=True) From 8a08fefa8561407bee150a7e6c0c9d5117ac5e7b Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 12 Nov 2018 12:29:28 -0500 Subject: [PATCH 114/323] Remove workaround for pytest-sugar 159, now fixed. --- tox.ini | 1 - 1 file changed, 1 deletion(-) diff --git a/tox.ini b/tox.ini index 78cc7f9f..41e20a33 100644 --- a/tox.ini +++ b/tox.ini @@ -5,7 +5,6 @@ minversion = 2.4 [testenv] deps = setuptools>=31.0.1 - pytest-sugar-bugfix159 commands = pytest {posargs} python setup.py checkdocs From 6de738440c6333e0f5e7b2447d2b5c05785481db Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 12 Nov 2018 12:31:25 -0500 Subject: [PATCH 115/323] Remove pytest-sugar plugin from standard pipelines as recommended in Frozenball/pytest-sugar#159. --- setup.cfg | 1 - 1 file changed, 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index adaed86d..2ea2224f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -30,7 +30,6 @@ setup_requires = setuptools_scm >= 1.15.0 testing = # upstream pytest >= 3.5, !=3.7.3 - pytest-sugar >= 0.9.1 collective.checkdocs pytest-flake8 From 95af04d3fcf70a487f59c854d802d9bac193de53 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 28 Nov 2018 12:44:40 -0500 Subject: [PATCH 116/323] Prefer pytest-checkdocs to collective.checkdocs --- setup.cfg | 2 +- tox.ini | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index 2ea2224f..30f3c087 100644 --- a/setup.cfg +++ b/setup.cfg @@ -30,7 +30,7 @@ setup_requires = setuptools_scm >= 1.15.0 testing = # upstream pytest >= 3.5, !=3.7.3 - collective.checkdocs + pytest-checkdocs pytest-flake8 # local diff --git a/tox.ini b/tox.ini index 41e20a33..4121a91f 100644 --- a/tox.ini +++ b/tox.ini @@ -7,7 +7,6 @@ deps = setuptools>=31.0.1 commands = pytest {posargs} - python setup.py checkdocs usedevelop = True extras = testing From 5c200dd4b98b91911ef9b7403373b84d017e42c0 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 1 Dec 2018 10:46:24 -0500 Subject: [PATCH 117/323] Suppress deprecation warning in docutils --- pytest.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pytest.ini b/pytest.ini index 61dab3d4..bbea8b12 100644 --- a/pytest.ini +++ b/pytest.ini @@ -5,3 +5,5 @@ doctest_optionflags=ALLOW_UNICODE ELLIPSIS filterwarnings= ignore:Possible nested set::pycodestyle:113 ignore:Using or importing the ABCs::flake8:410 + # workaround for https://sourceforge.net/p/docutils/bugs/348/ + ignore:'U' mode is deprecated::docutils.io From 2c91e8ec0d99f9ca354b7f913d61720925bb98bc Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 1 Dec 2018 11:58:52 -0500 Subject: [PATCH 118/323] Remove use of setup_requires. Builders now require pip 10 or later to build/install from sdist. Older installers will still install the packages from wheels. Ref tox-dev/tox#809. --- setup.cfg | 1 - 1 file changed, 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 30f3c087..e0395d78 100644 --- a/setup.cfg +++ b/setup.cfg @@ -24,7 +24,6 @@ packages = find: include_package_data = true python_requires = >=2.7 install_requires = -setup_requires = setuptools_scm >= 1.15.0 [options.extras_require] testing = From 216c4336ddb5b498e429219ef765fa1ae857febd Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 11 Dec 2018 14:21:46 -0500 Subject: [PATCH 119/323] Revert "Remove use of setup_requires. Builders now require pip 10 or later to build/install from sdist. Older installers will still install the packages from wheels. Ref tox-dev/tox#809." This reverts commit 2c91e8ec0d99f9ca354b7f913d61720925bb98bc. --- setup.cfg | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.cfg b/setup.cfg index e0395d78..30f3c087 100644 --- a/setup.cfg +++ b/setup.cfg @@ -24,6 +24,7 @@ packages = find: include_package_data = true python_requires = >=2.7 install_requires = +setup_requires = setuptools_scm >= 1.15.0 [options.extras_require] testing = From 32b254dee01b5ef2b695ae04889af482c6cb28c3 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 12 Dec 2018 13:26:23 -0500 Subject: [PATCH 120/323] Indicate build backend of setuptools --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 65d74263..efae667c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,2 +1,3 @@ [build-system] requires = ["setuptools>=34.4", "wheel", "setuptools_scm>=1.15"] +build-backend = 'setuptools.build_meta' From a8bca166266fa2eeab931f6f20eef8e50048dddf Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 12 Dec 2018 17:08:36 -0500 Subject: [PATCH 121/323] Add support for cutting releases without DPL and using pep517. --- .travis.yml | 19 +++++++------------ install-pip-master.py | 21 +++++++++++++++++++++ setup.cfg | 3 --- tox.ini | 15 +++++++++++++++ 4 files changed, 43 insertions(+), 15 deletions(-) create mode 100644 install-pip-master.py diff --git a/.travis.yml b/.travis.yml index b54e8e52..16363054 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,18 +13,13 @@ jobs: - stage: deploy if: tag IS present python: *latest_py3 - install: skip - script: skip - deploy: - provider: pypi - on: - tags: true - all_branches: true - user: jaraco - password: - secure: ... # encrypt password with `travis encrypt` - distributions: dists - skip_cleanup: true + before_script: skip + env: + - TWINE_USERNAME=jaraco + # TWINE_PASSWORD + - secure: ... # encrypt `TWINE_PASSWORD="{password}"` with `travis encrypt` + - TOX_TESTENV_PASSENV="TWINE_USERNAME TWINE_PASSWORD" + script: tox -e release cache: pip diff --git a/install-pip-master.py b/install-pip-master.py new file mode 100644 index 00000000..d62d20f3 --- /dev/null +++ b/install-pip-master.py @@ -0,0 +1,21 @@ +""" +In order to support installation of pep517 from source, +pip from master must be installed. +""" + +import subprocess +import sys + + +def main(): + cmd = [ + sys.executable, + '-m', 'pip', 'install', + 'git+https://github.com/pypa/pip', + ] + subprocess.run(cmd) + cmd[-1:] = sys.argv[1:] + subprocess.run(cmd) + + +__name__ == '__main__' and main() diff --git a/setup.cfg b/setup.cfg index 30f3c087..726b307e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,3 @@ -[aliases] -dists = clean --all sdist bdist_wheel - [bdist_wheel] universal = 1 diff --git a/tox.ini b/tox.ini index 4121a91f..853d7def 100644 --- a/tox.ini +++ b/tox.ini @@ -17,3 +17,18 @@ extras = changedir = docs commands = python -m sphinx . {toxinidir}/build/html + +[testenv:release] +skip_install = True +# workaround for pep517 build support +install_command = python install-pip-master.py {opts} {packages} +deps = + # pull from feature branch for feature + git+https://github.com/pypa/pep517@feature/build-command + # workaround for https://github.com/pypa/twine/issues/423 + git+https://github.com/pypa/twine + path.py +commands = + python -c "import path; path.Path('dist').rmtree_p()" + python -m pep517.build . + python -m twine upload dist/* From bc8a6cdf948376e1c846a121a4e8e4a699c66909 Mon Sep 17 00:00:00 2001 From: Sebastian Kriems Date: Fri, 14 Dec 2018 16:19:36 +0100 Subject: [PATCH 122/323] spaces, style and formatters (#4) use spaces, fixed indentation, format using autopep8 --- docs/conf.py | 44 +++++++++++++++++++------------------------- setup.py | 4 ++-- 2 files changed, 21 insertions(+), 27 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index aeda56c0..49a855ff 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,32 +1,26 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -extensions = [ - 'sphinx.ext.autodoc', - 'jaraco.packaging.sphinx', - 'rst.linker', -] +extensions = ["sphinx.ext.autodoc", "jaraco.packaging.sphinx", "rst.linker"] -master_doc = 'index' +master_doc = "index" link_files = { - '../CHANGES.rst': dict( - using=dict( - GH='https://github.com', - ), - replace=[ - dict( - pattern=r'(Issue #|\B#)(?P\d+)', - url='{package_url}/issues/{issue}', - ), - dict( - pattern=r'^(?m)((?Pv?\d+(\.\d+){1,2}))\n[-=]+\n', - with_scm='{text}\n{rev[timestamp]:%d %b %Y}\n', - ), - dict( - pattern=r'PEP[- ](?P\d+)', - url='https://www.python.org/dev/peps/pep-{pep_number:0>4}/', - ), - ], - ), + "../CHANGES.rst": dict( + using=dict(GH="https://github.com"), + replace=[ + dict( + pattern=r"(Issue #|\B#)(?P\d+)", + url="{package_url}/issues/{issue}", + ), + dict( + pattern=r"^(?m)((?Pv?\d+(\.\d+){1,2}))\n[-=]+\n", + with_scm="{text}\n{rev[timestamp]:%d %b %Y}\n", + ), + dict( + pattern=r"PEP[- ](?P\d+)", + url="https://www.python.org/dev/peps/pep-{pep_number:0>4}/", + ), + ], + ) } diff --git a/setup.py b/setup.py index c990c529..50e9f0c4 100644 --- a/setup.py +++ b/setup.py @@ -4,5 +4,5 @@ import setuptools -if __name__ == '__main__': - setuptools.setup(use_scm_version=True) +if __name__ == "__main__": + setuptools.setup(use_scm_version=True) From 939c515f2cc01525cbbd71f26e71d21471abdc93 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 17 Dec 2018 12:17:02 -0500 Subject: [PATCH 123/323] Rely on pep517 0.5 --- install-pip-master.py | 21 --------------------- tox.ini | 5 +---- 2 files changed, 1 insertion(+), 25 deletions(-) delete mode 100644 install-pip-master.py diff --git a/install-pip-master.py b/install-pip-master.py deleted file mode 100644 index d62d20f3..00000000 --- a/install-pip-master.py +++ /dev/null @@ -1,21 +0,0 @@ -""" -In order to support installation of pep517 from source, -pip from master must be installed. -""" - -import subprocess -import sys - - -def main(): - cmd = [ - sys.executable, - '-m', 'pip', 'install', - 'git+https://github.com/pypa/pip', - ] - subprocess.run(cmd) - cmd[-1:] = sys.argv[1:] - subprocess.run(cmd) - - -__name__ == '__main__' and main() diff --git a/tox.ini b/tox.ini index 853d7def..70b0be7a 100644 --- a/tox.ini +++ b/tox.ini @@ -20,11 +20,8 @@ commands = [testenv:release] skip_install = True -# workaround for pep517 build support -install_command = python install-pip-master.py {opts} {packages} deps = - # pull from feature branch for feature - git+https://github.com/pypa/pep517@feature/build-command + pep517>=0.5 # workaround for https://github.com/pypa/twine/issues/423 git+https://github.com/pypa/twine path.py From 192dafa3e9943e971a004d404be1b8e0d20691f2 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 20 Dec 2018 18:11:27 -0500 Subject: [PATCH 124/323] Add documentation on the skeleton. Fixes #5. --- .travis.yml | 2 +- setup.py | 2 - skeleton.md | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 127 insertions(+), 3 deletions(-) create mode 100644 skeleton.md diff --git a/.travis.yml b/.travis.yml index 16363054..8fc89320 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,7 +17,7 @@ jobs: env: - TWINE_USERNAME=jaraco # TWINE_PASSWORD - - secure: ... # encrypt `TWINE_PASSWORD="{password}"` with `travis encrypt` + - secure: ... - TOX_TESTENV_PASSENV="TWINE_USERNAME TWINE_PASSWORD" script: tox -e release diff --git a/setup.py b/setup.py index 50e9f0c4..827e955f 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,5 @@ #!/usr/bin/env python -# Project skeleton maintained at https://github.com/jaraco/skeleton - import setuptools if __name__ == "__main__": diff --git a/skeleton.md b/skeleton.md new file mode 100644 index 00000000..bc78f37c --- /dev/null +++ b/skeleton.md @@ -0,0 +1,126 @@ +# Overview + +This project is merged with [skeleton](https://github.com/jaraco/skeleton). What is skeleton? It's the scaffolding of a Python project jaraco [introduced in his blog](https://blog.jaraco.com/a-project-skeleton-for-python-projects/). It seeks to provide a means to re-use techniques and inherit advances when managing projects for distribution. + +## An SCM Managed Approach + +While maintaining dozens of projects in PyPI, jaraco derives best practices for project distribution and publishes them in the [skeleton repo](https://github.com/jaraco/skeleton), a git repo capturing the evolution and culmination of these best practices. + +It's intended to be used by a new or existing project to adopt these practices and honed and proven techniques. Adopters are encouraged to use the project directly and maintain a small deviation from the technique, make their own fork for more substantial changes unique to their environment or preferences, or simply adopt the skeleton once and abandon it thereafter. + +The primary advantage to using an SCM for maintaining these techniques is that those tools help facilitate the merge between the template and its adopting projects. + +# Usage + +## new projects + +To use skeleton for a new project, simply pull the skeleton into a new project: + +``` +$ git init my-new-project +$ cd my-new-project +$ git pull gh://jaraco/skeleton +``` + +Now customize the project to suit your individual project needs. + +## existing projects + +If you have an existing project, you can still incorporate the skeleton by merging it into the codebase. + +``` +$ git merge skeleton --allow-unrelated-histories +``` + +The `--allow-unrelated-histories` is necessary because the history from the skeleton was previously unrelated to the existing codebase. Resolve any merge conflicts and commit to the master, and now the project is based on the shared skeleton. + +## Updating + +Whenever a change is needed or desired for the general technique for packaging, it can be made in the skeleton project and then merged into each of the derived projects as needed, recommended before each release. As a result, features and best practices for packaging are centrally maintained and readily trickle into a whole suite of packages. This technique lowers the amount of tedious work necessary to create or maintain a project, and coupled with other techniques like continuous integration and deployment, lowers the cost of creating and maintaining refined Python projects to just a few, familiar git operations. + +Thereafter, the target project can make whatever customizations it deems relevant to the scaffolding. The project may even at some point decide that the divergence is too great to merit renewed merging with the original skeleton. This approach applies maximal guidance while creating minimal constraints. + +# Features + +The features/techniques employed by the skeleton include: + +- PEP 517/518 based build relying on setuptools as the build tool +- setuptools declarative configuration using setup.cfg +- tox for running tests +- A README.rst as reStructuredText with some popular badges, but with readthedocs and appveyor badges commented out +- A CHANGES.rst file intended for publishing release notes about the project. + +## Packaging Conventions + +A pyproject.toml is included to enable PEP 517 and PEP 518 compatibility and declares the requirements necessary to build the project on setuptools (a minimum version compatible with setup.cfg declarative config). + +The setup.cfg file implements the following features: + +- Assumes universal wheel for release +- Advertises the project's LICENSE file (MIT by default) +- Reads the README.rst file into the long description +- Some common Trove classifiers +- Includes all packages discovered in the repo +- Data files in the package are also included (not just Python files) +- Declares the required Python versions +- Declares install requirements (empty by default) +- Declares setup requirements for legacy environments +- Supplies two 'extras': + - testing: requirements for running tests + - docs: requirements for building docs + - these extras split the declaration into "upstream" (requirements as declared by the skeleton) and "local" (those specific to the local project) +- Placeholder for defining entry points + +Additionally, the setup.py file declares `use_scm_version` which relies on [setuptools_scm](https://pypi.org/project/setuptools_scm) to do two things: + +- derive the project version from SCM tags +- ensure that all files committed to the repo are automatically included in releases + +## Running Tests + +The skeleton assumes the developer has [tox](https://pypi.org/project/tox) installed. The developer is expected to run `tox` to run tests on the current Python version using [pytest](https://pypi.org/project/pytest). + +Other environments (invoked with `tox -e {name}`) supplied include: + + - a `build-docs` environment to build the documentation + - a `release` environment to publish the package to PyPI + +A pytest.ini is included to define common options around running tests. In particular: + +- rely on default test discovery in the current directory +- avoid recursing into common directories not containing tests +- run doctests on modules and invoke flake8 tests +- in doctests, allow unicode literals and regular literals to match, allowing for doctests to run on Python 2 and 3. Also enable ELLIPSES, a default that would be undone by supplying the prior option. +- filters out known warnings caused by libraries/functionality included by the skeleton + +Relies a .flake8 file to correct some default behaviors: + +- allow tabs for indentation (legacy for jaraco projects) +- disable mutually incompatible rules W503 and W504. + +## Continuous Integration + +The project is pre-configured to run tests in [Travis-CI](https://travis-ci.org) (.travis.yml). Any new project must be enabled either through their web site or with the `travis enable` command. In addition to running tests, an additional deploy stage is configured to automatically release tagged commits. The username and password for PyPI must be configured for each project using the `travis` command and only after the travis project is created. As releases are cut with [twine](https://pypi.org/project/twine), the two values are supplied through the `TWINE_USERNAME` and `TWINE_PASSWORD`. To configure the latter as a secret, run the following command: + +``` +echo "TWINE_PASSWORD={password}" | travis encrypt +``` + +Or disable it in the CI definition and configure it through the web UI. + +Features include: +- test against Python 2 and 3 +- run on Ubuntu Xenial +- correct for broken IPv6 + +Also provided is a minimal template for running under Appveyor (Windows). + +## Building Documentation + +Documentation is automatically built by [Read the Docs](https://readthedocs.org) when the project is registered with it, by way of the .readthedocs.yml file. To test the docs build manually, a tox env may be invoked as `tox -e build-docs`. Both techniques rely on the dependencies declared in `setup.cfg/options.extras_require.docs`. + +In addition to building the sphinx docs scaffolded in `docs/`, the docs build a `history.html` file that first injects release dates and hyperlinks into the CHANGES.rst before incorporating it as history in the docs. + +## Cutting releases + +By default, tagged commits are released through the continuous integration deploy stage. From 5b4c2503ce84744c0cdf398316d6b18863905297 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 27 Dec 2018 11:42:55 -0500 Subject: [PATCH 125/323] Add workaround for DeprecationWarning in flake8 --- pytest.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pytest.ini b/pytest.ini index bbea8b12..9b3c1ecd 100644 --- a/pytest.ini +++ b/pytest.ini @@ -7,3 +7,5 @@ filterwarnings= ignore:Using or importing the ABCs::flake8:410 # workaround for https://sourceforge.net/p/docutils/bugs/348/ ignore:'U' mode is deprecated::docutils.io + # workaround for https://gitlab.com/pycqa/flake8/issues/275 + ignore:You passed a bytestring as `filenames`.::flake8 From 8ac0f8736c746a829e6393ca5ba00fa8d042d426 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 28 Dec 2018 21:57:02 -0500 Subject: [PATCH 126/323] Use consistent encoding quoting in pyproject.toml --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index efae667c..6f0a5168 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,3 @@ [build-system] requires = ["setuptools>=34.4", "wheel", "setuptools_scm>=1.15"] -build-backend = 'setuptools.build_meta' +build-backend = "setuptools.build_meta" From 4310c976400dc2eab8d8597b0dffaa7b787cff71 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 28 Dec 2018 22:00:08 -0500 Subject: [PATCH 127/323] Clarify purpose of local/upstream extras --- skeleton.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/skeleton.md b/skeleton.md index bc78f37c..0e0c0eff 100644 --- a/skeleton.md +++ b/skeleton.md @@ -68,7 +68,7 @@ The setup.cfg file implements the following features: - Supplies two 'extras': - testing: requirements for running tests - docs: requirements for building docs - - these extras split the declaration into "upstream" (requirements as declared by the skeleton) and "local" (those specific to the local project) + - these extras split the declaration into "upstream" (requirements as declared by the skeleton) and "local" (those specific to the local project); these markers help avoid merge conflicts - Placeholder for defining entry points Additionally, the setup.py file declares `use_scm_version` which relies on [setuptools_scm](https://pypi.org/project/setuptools_scm) to do two things: From 12eed1326e1bc26ce256e7b3f8cd8d3a5beab2d5 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 6 Feb 2019 09:54:00 -0500 Subject: [PATCH 128/323] Suppress E117 as workaround for PyCQA/pycodestyle#836 --- .flake8 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.flake8 b/.flake8 index c85d34a7..04d2d97a 100644 --- a/.flake8 +++ b/.flake8 @@ -2,6 +2,8 @@ ignore = # Allow tabs for indentation W191 + # Workaround for https://github.com/PyCQA/pycodestyle/issues/836 + E117 # W503 violates spec https://github.com/PyCQA/pycodestyle/issues/513 W503 # W504 has issues https://github.com/OCA/maintainer-quality-tools/issues/545 From 8186f76e906f80d678e895f6627afefee5617888 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 6 Feb 2019 09:58:28 -0500 Subject: [PATCH 129/323] Amend skeleton documentation to expand on the value of the approach. --- skeleton.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/skeleton.md b/skeleton.md index 0e0c0eff..09485cca 100644 --- a/skeleton.md +++ b/skeleton.md @@ -10,6 +10,8 @@ It's intended to be used by a new or existing project to adopt these practices a The primary advantage to using an SCM for maintaining these techniques is that those tools help facilitate the merge between the template and its adopting projects. +Another advantage to using an SCM-managed approach is that tools like GitHub recognize that a change in the skeleton is the _same change_ across all projects that merge with that skeleton. Without the ancestry, with a traditional copy/paste approach, a [commit like this](https://github.com/jaraco/skeleton/commit/12eed1326e1bc26ce256e7b3f8cd8d3a5beab2d5) would produce notifications in the upstream project issue for each and every application, but because it's centralized, GitHub provides just the one notification when the change is added to the skeleton. + # Usage ## new projects From cdff6c8f0fa2a0439adc219a40bc2b11bb95d29d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 22 Mar 2019 11:49:13 -0400 Subject: [PATCH 130/323] Remove sudo declaration in Travis config. See https://blog.travis-ci.com/2018-11-19-required-linux-infrastructure-migration for more details. --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 8fc89320..17d02624 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,4 @@ dist: xenial -sudo: false language: python python: From bb4c6091319fc3d33d4aebd15da483bb90acdbc9 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 17 Apr 2019 15:22:26 -0400 Subject: [PATCH 131/323] Enable tox-pip-extensions ext_venv_update if available. Fixes jaraco/skeleton#6 --- tox.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tox.ini b/tox.ini index 70b0be7a..5ce2047b 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,8 @@ [tox] envlist = python minversion = 2.4 +# https://github.com/jaraco/skeleton/issues/6 +tox_pip_extensions_ext_venv_update = true [testenv] deps = From 5bd3e6069e32cc94725fa389758e055126f3cdc5 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 24 Apr 2019 09:18:44 -0400 Subject: [PATCH 132/323] Rely on tox 3.2 and pip 10 or later for all builds --- tox.ini | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 5ce2047b..78161a56 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,8 @@ [tox] envlist = python -minversion = 2.4 +minversion = 3.2 +requires = + pip >= 10 # https://github.com/jaraco/skeleton/issues/6 tox_pip_extensions_ext_venv_update = true From 51f138939c98a4f616c702bc2f080504395fbbd6 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 24 Apr 2019 11:17:47 -0400 Subject: [PATCH 133/323] It adds no value to add a pip requirement for the tox install --- tox.ini | 2 -- 1 file changed, 2 deletions(-) diff --git a/tox.ini b/tox.ini index 78161a56..8a3bf67c 100644 --- a/tox.ini +++ b/tox.ini @@ -1,8 +1,6 @@ [tox] envlist = python minversion = 3.2 -requires = - pip >= 10 # https://github.com/jaraco/skeleton/issues/6 tox_pip_extensions_ext_venv_update = true From 123b0b20d6e0bc9ffd00d7fb8c2e1a3ceee7475a Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 24 Apr 2019 13:20:53 -0400 Subject: [PATCH 134/323] Pin to pip 19.0 for now for pypa/pip#6434. --- pin-pip.py | 20 ++++++++++++++++++++ tox.ini | 1 + 2 files changed, 21 insertions(+) create mode 100644 pin-pip.py diff --git a/pin-pip.py b/pin-pip.py new file mode 100644 index 00000000..4cf0383c --- /dev/null +++ b/pin-pip.py @@ -0,0 +1,20 @@ +""" +Downgrade to pip 19.0 before installing requirements, working +around limitations introduced in 19.1 (ref +https://github.com/pypa/pip/issues/6434) +""" + +import sys +import subprocess +import shlex + + +def main(): + subprocess.check_call(shlex.split( + 'python -m pip install pip<19.1' + )) + subprocess.check_call(shlex.split( + 'python -m pip install') + sys.argv[1:]) + + +__name__ == '__main__' and main() diff --git a/tox.ini b/tox.ini index 8a3bf67c..3d1ae59d 100644 --- a/tox.ini +++ b/tox.ini @@ -5,6 +5,7 @@ minversion = 3.2 tox_pip_extensions_ext_venv_update = true [testenv] +install_command = python pin-pip.py {opts} {packages} deps = setuptools>=31.0.1 commands = From 4186b77c3f225a5845ac9072b167427c91f1d6fd Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 9 May 2019 08:55:31 -0400 Subject: [PATCH 135/323] Revert "Pin to pip 19.0 for now for pypa/pip#6434." This reverts commit 123b0b20d6e0bc9ffd00d7fb8c2e1a3ceee7475a. --- pin-pip.py | 20 -------------------- tox.ini | 1 - 2 files changed, 21 deletions(-) delete mode 100644 pin-pip.py diff --git a/pin-pip.py b/pin-pip.py deleted file mode 100644 index 4cf0383c..00000000 --- a/pin-pip.py +++ /dev/null @@ -1,20 +0,0 @@ -""" -Downgrade to pip 19.0 before installing requirements, working -around limitations introduced in 19.1 (ref -https://github.com/pypa/pip/issues/6434) -""" - -import sys -import subprocess -import shlex - - -def main(): - subprocess.check_call(shlex.split( - 'python -m pip install pip<19.1' - )) - subprocess.check_call(shlex.split( - 'python -m pip install') + sys.argv[1:]) - - -__name__ == '__main__' and main() diff --git a/tox.ini b/tox.ini index 3d1ae59d..8a3bf67c 100644 --- a/tox.ini +++ b/tox.ini @@ -5,7 +5,6 @@ minversion = 3.2 tox_pip_extensions_ext_venv_update = true [testenv] -install_command = python pin-pip.py {opts} {packages} deps = setuptools>=31.0.1 commands = From 4399038f2eab21f942a5462e0f5b1351e6203873 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 15 May 2019 19:45:15 -0400 Subject: [PATCH 136/323] Only install and invoke pytest-black on Python 3 --- pytest.ini | 2 +- setup.cfg | 2 +- tox.ini | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/pytest.ini b/pytest.ini index 10681adf..9b3c1ecd 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,6 +1,6 @@ [pytest] norecursedirs=dist build .tox .eggs -addopts=--doctest-modules --flake8 --black +addopts=--doctest-modules --flake8 doctest_optionflags=ALLOW_UNICODE ELLIPSIS filterwarnings= ignore:Possible nested set::pycodestyle:113 diff --git a/setup.cfg b/setup.cfg index a3eb3c94..235303f0 100644 --- a/setup.cfg +++ b/setup.cfg @@ -29,7 +29,7 @@ testing = pytest >= 3.5, !=3.7.3 pytest-checkdocs pytest-flake8 - pytest-black + pytest-black; python_version >= "3" # local diff --git a/tox.ini b/tox.ini index 8a3bf67c..8fa79660 100644 --- a/tox.ini +++ b/tox.ini @@ -8,7 +8,8 @@ tox_pip_extensions_ext_venv_update = true deps = setuptools>=31.0.1 commands = - pytest {posargs} + !py27: pytest --black {posargs} + py27: pytest {posargs} usedevelop = True extras = testing From d4c65e6784e783549bfe5bba1ccbc7be76eb41ff Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 15 May 2019 21:21:57 -0400 Subject: [PATCH 137/323] Use pytest-black-multipy to enable simple support for pytest-black where available. Ref pytest-dev/pytest#5272. --- pytest.ini | 2 +- setup.cfg | 2 +- tox.ini | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/pytest.ini b/pytest.ini index 9b3c1ecd..10681adf 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,6 +1,6 @@ [pytest] norecursedirs=dist build .tox .eggs -addopts=--doctest-modules --flake8 +addopts=--doctest-modules --flake8 --black doctest_optionflags=ALLOW_UNICODE ELLIPSIS filterwarnings= ignore:Possible nested set::pycodestyle:113 diff --git a/setup.cfg b/setup.cfg index 235303f0..9345869b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -29,7 +29,7 @@ testing = pytest >= 3.5, !=3.7.3 pytest-checkdocs pytest-flake8 - pytest-black; python_version >= "3" + pytest-black-multipy # local diff --git a/tox.ini b/tox.ini index 8fa79660..8a3bf67c 100644 --- a/tox.ini +++ b/tox.ini @@ -8,8 +8,7 @@ tox_pip_extensions_ext_venv_update = true deps = setuptools>=31.0.1 commands = - !py27: pytest --black {posargs} - py27: pytest {posargs} + pytest {posargs} usedevelop = True extras = testing From 79733f08c43f9b2e0fd1830b37311fa52a16537c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 16 May 2019 10:21:15 -0400 Subject: [PATCH 138/323] Update skeleton documentation to reflect black adoption. --- skeleton.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/skeleton.md b/skeleton.md index 09485cca..72494070 100644 --- a/skeleton.md +++ b/skeleton.md @@ -50,7 +50,8 @@ The features/techniques employed by the skeleton include: - setuptools declarative configuration using setup.cfg - tox for running tests - A README.rst as reStructuredText with some popular badges, but with readthedocs and appveyor badges commented out -- A CHANGES.rst file intended for publishing release notes about the project. +- A CHANGES.rst file intended for publishing release notes about the project +- Use of [black](https://black.readthedocs.io/en/stable/) for code formatting (disabled on unsupported Python 3.5 and earlier) ## Packaging Conventions @@ -97,8 +98,8 @@ A pytest.ini is included to define common options around running tests. In parti Relies a .flake8 file to correct some default behaviors: -- allow tabs for indentation (legacy for jaraco projects) -- disable mutually incompatible rules W503 and W504. +- disable mutually incompatible rules W503 and W504 +- support for black format ## Continuous Integration From 2ab127d2bc47ffd747afe3059b3a5b08254a5415 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 23 May 2019 08:09:02 -0400 Subject: [PATCH 139/323] Rely on twine 1.13 or later --- tox.ini | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index 8a3bf67c..5f1d1b7f 100644 --- a/tox.ini +++ b/tox.ini @@ -24,8 +24,7 @@ commands = skip_install = True deps = pep517>=0.5 - # workaround for https://github.com/pypa/twine/issues/423 - git+https://github.com/pypa/twine + twine>=1.13 path.py commands = python -c "import path; path.Path('dist').rmtree_p()" From 054ea7dbbaabf257e7c3c6276d889cc178a19340 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 30 Jun 2019 16:38:07 -0400 Subject: [PATCH 140/323] Upgrade tox and virtualenv to ensure that environments get recent pips --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 2b7808f9..f35aa27d 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -18,7 +18,7 @@ cache: - '%LOCALAPPDATA%\pip\Cache' test_script: - - "python -m pip install tox tox-venv" + - "python -m pip install -U tox tox-venv virtualenv" - "tox" version: '{build}' From 20964edcfd5b35c6006bc6425a5587004ed76eec Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 21 Aug 2019 22:39:34 -0400 Subject: [PATCH 141/323] Define passenv in tox release section. Rely on __token__ for default username. --- .travis.yml | 5 ----- tox.ini | 4 ++++ 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 17d02624..6ccac8f2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,11 +13,6 @@ jobs: if: tag IS present python: *latest_py3 before_script: skip - env: - - TWINE_USERNAME=jaraco - # TWINE_PASSWORD - - secure: ... - - TOX_TESTENV_PASSENV="TWINE_USERNAME TWINE_PASSWORD" script: tox -e release cache: pip diff --git a/tox.ini b/tox.ini index 5f1d1b7f..4f3341f8 100644 --- a/tox.ini +++ b/tox.ini @@ -26,6 +26,10 @@ deps = pep517>=0.5 twine>=1.13 path.py +passenv = + TWINE_PASSWORD +setenv = + TWINE_USERNAME = {env:TWINE_USERNAME:__token__} commands = python -c "import path; path.Path('dist').rmtree_p()" python -m pep517.build . From 4baec898fdfef6da27653d21fdf223da10b13342 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 22 Aug 2019 16:40:51 -0400 Subject: [PATCH 142/323] Update docs to reflect changes to deployment. --- skeleton.md | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/skeleton.md b/skeleton.md index 72494070..52b97f09 100644 --- a/skeleton.md +++ b/skeleton.md @@ -103,13 +103,7 @@ Relies a .flake8 file to correct some default behaviors: ## Continuous Integration -The project is pre-configured to run tests in [Travis-CI](https://travis-ci.org) (.travis.yml). Any new project must be enabled either through their web site or with the `travis enable` command. In addition to running tests, an additional deploy stage is configured to automatically release tagged commits. The username and password for PyPI must be configured for each project using the `travis` command and only after the travis project is created. As releases are cut with [twine](https://pypi.org/project/twine), the two values are supplied through the `TWINE_USERNAME` and `TWINE_PASSWORD`. To configure the latter as a secret, run the following command: - -``` -echo "TWINE_PASSWORD={password}" | travis encrypt -``` - -Or disable it in the CI definition and configure it through the web UI. +The project is pre-configured to run tests in [Travis-CI](https://travis-ci.org) (.travis.yml). Any new project must be enabled either through their web site or with the `travis enable` command. Features include: - test against Python 2 and 3 @@ -118,6 +112,14 @@ Features include: Also provided is a minimal template for running under Appveyor (Windows). +### Continuous Deployments + +In addition to running tests, an additional deploy stage is configured to automatically release tagged commits to PyPI using [API tokens](https://pypi.org/help/#apitoken). The release process expects an authorized token to be configured with Travis as the TWINE_PASSWORD environment variable. After the Travis project is created, configure the token through the web UI or with a command like the following (bash syntax): + +``` +TWINE_PASSWORD={token} travis env copy TWINE_PASSWORD +``` + ## Building Documentation Documentation is automatically built by [Read the Docs](https://readthedocs.org) when the project is registered with it, by way of the .readthedocs.yml file. To test the docs build manually, a tox env may be invoked as `tox -e build-docs`. Both techniques rely on the dependencies declared in `setup.cfg/options.extras_require.docs`. @@ -127,3 +129,9 @@ In addition to building the sphinx docs scaffolded in `docs/`, the docs build a ## Cutting releases By default, tagged commits are released through the continuous integration deploy stage. + +Releases may also be cut manually by invoking the tox environment `release` with the PyPI token set as the TWINE_PASSWORD: + +``` +TWINE_PASSWORD={token} tox -e release +``` From 05a3c52b4d41690e0471a2e283cffb500dc0329a Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 11 Sep 2019 11:25:50 +0100 Subject: [PATCH 143/323] Python 3 only --- .travis.yml | 1 - appveyor.yml | 2 +- setup.cfg | 3 +-- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6ccac8f2..8b607a65 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,6 @@ dist: xenial language: python python: -- 2.7 - 3.6 - &latest_py3 3.7 diff --git a/appveyor.yml b/appveyor.yml index f35aa27d..bfd57529 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -4,7 +4,7 @@ environment: matrix: - PYTHON: "C:\\Python36-x64" - - PYTHON: "C:\\Python27-x64" + - PYTHON: "C:\\Python37-x64" install: # symlink python from a directory with a space diff --git a/setup.cfg b/setup.cfg index 9345869b..8dc6d4ec 100644 --- a/setup.cfg +++ b/setup.cfg @@ -13,13 +13,12 @@ classifiers = Development Status :: 5 - Production/Stable Intended Audience :: Developers License :: OSI Approved :: MIT License - Programming Language :: Python :: 2.7 Programming Language :: Python :: 3 [options] packages = find: include_package_data = true -python_requires = >=2.7 +python_requires = >=3.6 install_requires = setup_requires = setuptools_scm >= 1.15.0 From a28efc59c12e16a02ec98659b660e5b5809af650 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 15 Sep 2019 21:36:25 +0200 Subject: [PATCH 144/323] Enable coverage reporting on project --- .coveragerc | 2 ++ pytest.ini | 2 +- setup.cfg | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 .coveragerc diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 00000000..896b501e --- /dev/null +++ b/.coveragerc @@ -0,0 +1,2 @@ +[run] +omit = .tox/* diff --git a/pytest.ini b/pytest.ini index 10681adf..a86fb660 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,6 +1,6 @@ [pytest] norecursedirs=dist build .tox .eggs -addopts=--doctest-modules --flake8 --black +addopts=--doctest-modules --flake8 --black --cov doctest_optionflags=ALLOW_UNICODE ELLIPSIS filterwarnings= ignore:Possible nested set::pycodestyle:113 diff --git a/setup.cfg b/setup.cfg index 9345869b..77df5051 100644 --- a/setup.cfg +++ b/setup.cfg @@ -30,6 +30,7 @@ testing = pytest-checkdocs pytest-flake8 pytest-black-multipy + pytest-cov # local From cc1a1c9be39ba29e90d6d9d8ab5d6d1768a50594 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 22 Sep 2019 14:25:32 +0200 Subject: [PATCH 145/323] Report the lines missing coverage --- .coveragerc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.coveragerc b/.coveragerc index 896b501e..45823064 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,2 +1,5 @@ [run] omit = .tox/* + +[report] +show_missing = True From 9314eb458311dfd8981a6378b8498017c89ea2f7 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 29 Sep 2019 22:46:03 -0400 Subject: [PATCH 146/323] Ensure that a late version of pip is installed without special versions of tox-venv. --- tox.ini | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tox.ini b/tox.ini index 4f3341f8..ab6cd407 100644 --- a/tox.ini +++ b/tox.ini @@ -3,10 +3,16 @@ envlist = python minversion = 3.2 # https://github.com/jaraco/skeleton/issues/6 tox_pip_extensions_ext_venv_update = true +# ensure that a late version of pip is used even on tox-venv +requires = + tox-pip-version + tox-venv + [testenv] deps = setuptools>=31.0.1 +pip_version = pip commands = pytest {posargs} usedevelop = True From 1cfcc4214082bcb6bec9ea51ad91f15446488e9b Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 20 Oct 2019 17:25:23 -0400 Subject: [PATCH 147/323] Disable tox-pip-version as it interacts badly with tox-venv causing tox to use the wrong Python version to install packages and run tests. Ref pglass/tox-pip-version#20 and tox-dev/tox-venv#40. --- tox.ini | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index ab6cd407..1d81b812 100644 --- a/tox.ini +++ b/tox.ini @@ -3,9 +3,10 @@ envlist = python minversion = 3.2 # https://github.com/jaraco/skeleton/issues/6 tox_pip_extensions_ext_venv_update = true -# ensure that a late version of pip is used even on tox-venv +# Ensure that a late version of pip is used even on tox-venv. +# Disabled due to pglass/tox-pip-version#20. requires = - tox-pip-version +# tox-pip-version tox-venv From c169e5e50fd5f18dfe554d06bfe3940cc950b13e Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 25 Oct 2019 19:39:34 -0400 Subject: [PATCH 148/323] Bring back tox-pip-version now that pglass/tox-pip-version#20 is fixed. --- tox.ini | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index 1d81b812..d267e16d 100644 --- a/tox.ini +++ b/tox.ini @@ -4,9 +4,8 @@ minversion = 3.2 # https://github.com/jaraco/skeleton/issues/6 tox_pip_extensions_ext_venv_update = true # Ensure that a late version of pip is used even on tox-venv. -# Disabled due to pglass/tox-pip-version#20. requires = -# tox-pip-version + tox-pip-version>=0.0.6 tox-venv From 34ecf58479ad45fc6bfa5c8b476b719cf5720c14 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 27 Oct 2019 12:30:25 -0400 Subject: [PATCH 149/323] Test/release on Python 3.8 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 6ccac8f2..b7d8f3ac 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ language: python python: - 2.7 - 3.6 -- &latest_py3 3.7 +- &latest_py3 3.8 jobs: fast_finish: true From eaeb9ec2eb542a04948c6c9742fb90069f37de33 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 27 Oct 2019 13:29:45 -0400 Subject: [PATCH 150/323] Apply black to docs/conf.py --- docs/conf.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 77cef345..41b53557 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,19 +1,13 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -extensions = [ - 'sphinx.ext.autodoc', - 'jaraco.packaging.sphinx', - 'rst.linker', -] +extensions = ['sphinx.ext.autodoc', 'jaraco.packaging.sphinx', 'rst.linker'] master_doc = "index" link_files = { '../CHANGES.rst': dict( - using=dict( - GH='https://github.com', - ), + using=dict(GH='https://github.com'), replace=[ dict( pattern=r'(Issue #|\B#)(?P\d+)', @@ -28,5 +22,5 @@ url='https://www.python.org/dev/peps/pep-{pep_number:0>4}/', ), ], - ), + ) } From 174f0fd7cf349c277ade401ddb88dde530723053 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 27 Oct 2019 13:36:04 -0400 Subject: [PATCH 151/323] Update black version and links --- .pre-commit-config.yaml | 4 ++-- README.rst | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 922d9424..e16c59ac 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,5 +1,5 @@ repos: -- repo: https://github.com/ambv/black - rev: 18.9b0 +- repo: https://github.com/psf/black + rev: 19.3b0 hooks: - id: black diff --git a/README.rst b/README.rst index 7050da33..50eba567 100644 --- a/README.rst +++ b/README.rst @@ -7,7 +7,7 @@ :target: https://travis-ci.org/jaraco/skeleton .. image:: https://img.shields.io/badge/code%20style-black-000000.svg - :target: https://github.com/ambv/black + :target: https://github.com/psf/black :alt: Code style: Black .. .. image:: https://img.shields.io/appveyor/ci/jaraco/skeleton/master.svg From 03d825d44a003d08cb54708fd87fccd161806d3f Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 31 Oct 2019 16:14:40 -0400 Subject: [PATCH 152/323] Expect flake8 3.6 or later and remove suppression of warnings from Flake8 prior to 3.6. --- pytest.ini | 2 -- 1 file changed, 2 deletions(-) diff --git a/pytest.ini b/pytest.ini index a86fb660..54f1b9aa 100644 --- a/pytest.ini +++ b/pytest.ini @@ -3,8 +3,6 @@ norecursedirs=dist build .tox .eggs addopts=--doctest-modules --flake8 --black --cov doctest_optionflags=ALLOW_UNICODE ELLIPSIS filterwarnings= - ignore:Possible nested set::pycodestyle:113 - ignore:Using or importing the ABCs::flake8:410 # workaround for https://sourceforge.net/p/docutils/bugs/348/ ignore:'U' mode is deprecated::docutils.io # workaround for https://gitlab.com/pycqa/flake8/issues/275 From 48d361a4146d6bbc52c96718ec83cf56410af73a Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 31 Oct 2019 16:30:21 -0400 Subject: [PATCH 153/323] Rely on pytest-checkdocs 1.2.3, eliminating workaround for docutils warning. --- pytest.ini | 2 -- setup.cfg | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/pytest.ini b/pytest.ini index 54f1b9aa..4ffa6f29 100644 --- a/pytest.ini +++ b/pytest.ini @@ -3,7 +3,5 @@ norecursedirs=dist build .tox .eggs addopts=--doctest-modules --flake8 --black --cov doctest_optionflags=ALLOW_UNICODE ELLIPSIS filterwarnings= - # workaround for https://sourceforge.net/p/docutils/bugs/348/ - ignore:'U' mode is deprecated::docutils.io # workaround for https://gitlab.com/pycqa/flake8/issues/275 ignore:You passed a bytestring as `filenames`.::flake8 diff --git a/setup.cfg b/setup.cfg index 77df5051..63b865ca 100644 --- a/setup.cfg +++ b/setup.cfg @@ -27,7 +27,7 @@ setup_requires = setuptools_scm >= 1.15.0 testing = # upstream pytest >= 3.5, !=3.7.3 - pytest-checkdocs + pytest-checkdocs >= 1.2.3 pytest-flake8 pytest-black-multipy pytest-cov From f10294a99395a64370695e43521175bb93e4b4f3 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 1 Nov 2019 11:54:25 -0400 Subject: [PATCH 154/323] Remove workaround for gitlab.com/pycqa/flake8/issues/275, apparently no longer necessary. --- pytest.ini | 2 -- 1 file changed, 2 deletions(-) diff --git a/pytest.ini b/pytest.ini index 4ffa6f29..7b9b714f 100644 --- a/pytest.ini +++ b/pytest.ini @@ -3,5 +3,3 @@ norecursedirs=dist build .tox .eggs addopts=--doctest-modules --flake8 --black --cov doctest_optionflags=ALLOW_UNICODE ELLIPSIS filterwarnings= - # workaround for https://gitlab.com/pycqa/flake8/issues/275 - ignore:You passed a bytestring as `filenames`.::flake8 From 8e7c267538204284067e1fa70d53fce9f78c60f7 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 2 Nov 2019 22:11:19 -0500 Subject: [PATCH 155/323] Normalize indentation --- tox.ini | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tox.ini b/tox.ini index d267e16d..64eb21d5 100644 --- a/tox.ini +++ b/tox.ini @@ -20,11 +20,11 @@ extras = testing [testenv:build-docs] extras = - docs - testing + docs + testing changedir = docs commands = - python -m sphinx . {toxinidir}/build/html + python -m sphinx . {toxinidir}/build/html [testenv:release] skip_install = True From a0651976d78d84a22a5d06807d46194218a1fefa Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 7 Nov 2019 10:54:31 -0800 Subject: [PATCH 156/323] Include keyring support from twine --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 64eb21d5..d86e4ad9 100644 --- a/tox.ini +++ b/tox.ini @@ -30,7 +30,7 @@ commands = skip_install = True deps = pep517>=0.5 - twine>=1.13 + twine[keyring]>=1.13 path.py passenv = TWINE_PASSWORD From 757b121d940f0daf7fbe4f494b47cb1b0ed6e0c2 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 29 Nov 2019 10:31:33 -0500 Subject: [PATCH 157/323] Rename 'build-docs' to simply 'docs' (matching more popular convention). --- skeleton.md | 4 ++-- tox.ini | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/skeleton.md b/skeleton.md index 52b97f09..ee26e14b 100644 --- a/skeleton.md +++ b/skeleton.md @@ -85,7 +85,7 @@ The skeleton assumes the developer has [tox](https://pypi.org/project/tox) insta Other environments (invoked with `tox -e {name}`) supplied include: - - a `build-docs` environment to build the documentation + - a `docs` environment to build the documentation - a `release` environment to publish the package to PyPI A pytest.ini is included to define common options around running tests. In particular: @@ -122,7 +122,7 @@ TWINE_PASSWORD={token} travis env copy TWINE_PASSWORD ## Building Documentation -Documentation is automatically built by [Read the Docs](https://readthedocs.org) when the project is registered with it, by way of the .readthedocs.yml file. To test the docs build manually, a tox env may be invoked as `tox -e build-docs`. Both techniques rely on the dependencies declared in `setup.cfg/options.extras_require.docs`. +Documentation is automatically built by [Read the Docs](https://readthedocs.org) when the project is registered with it, by way of the .readthedocs.yml file. To test the docs build manually, a tox env may be invoked as `tox -e docs`. Both techniques rely on the dependencies declared in `setup.cfg/options.extras_require.docs`. In addition to building the sphinx docs scaffolded in `docs/`, the docs build a `history.html` file that first injects release dates and hyperlinks into the CHANGES.rst before incorporating it as history in the docs. diff --git a/tox.ini b/tox.ini index d86e4ad9..889af7a4 100644 --- a/tox.ini +++ b/tox.ini @@ -18,7 +18,7 @@ commands = usedevelop = True extras = testing -[testenv:build-docs] +[testenv:docs] extras = docs testing From 9ef4b6e60389a8f39cc04e466d12f42861c26472 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 30 Nov 2019 21:08:31 -0500 Subject: [PATCH 158/323] Prefer 'path' to 'path.py' --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 889af7a4..fa2a72f4 100644 --- a/tox.ini +++ b/tox.ini @@ -31,7 +31,7 @@ skip_install = True deps = pep517>=0.5 twine[keyring]>=1.13 - path.py + path passenv = TWINE_PASSWORD setenv = From 266a9c112ce2fb38365a13f57122fa73754555ce Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 8 Dec 2019 12:25:04 -0500 Subject: [PATCH 159/323] Cover Python 3.8 in Windows tests --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index bfd57529..6a1c99a9 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -4,7 +4,7 @@ environment: matrix: - PYTHON: "C:\\Python36-x64" - - PYTHON: "C:\\Python37-x64" + - PYTHON: "C:\\Python38-x64" install: # symlink python from a directory with a space From 76786bdd11eac589bdaeb9a8cea5dcacaf613225 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 20 Dec 2019 23:49:53 -0500 Subject: [PATCH 160/323] Update black in pre-commit and add blacken-docs. --- .pre-commit-config.yaml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e16c59ac..fe46b8c5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,5 +1,10 @@ repos: - repo: https://github.com/psf/black - rev: 19.3b0 + rev: 19.10b0 hooks: - id: black + +- repo: https://github.com/asottile/blacken-docs + rev: v1.4.0 + hooks: + - id: blacken-docs From 051ce14211226aa34e11dc963cef508dd8ccdc53 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 4 Jan 2020 16:39:37 -0500 Subject: [PATCH 161/323] Test and release using Azure Pipelines --- .travis.yml | 9 ------ README.rst | 3 ++ azure-pipelines.yml | 71 +++++++++++++++++++++++++++++++++++++++++++++ skeleton.md | 34 ++++++++++++++++++---- 4 files changed, 103 insertions(+), 14 deletions(-) create mode 100644 azure-pipelines.yml diff --git a/.travis.yml b/.travis.yml index 45cbcf94..17e45a67 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,15 +5,6 @@ python: - 3.6 - &latest_py3 3.8 -jobs: - fast_finish: true - include: - - stage: deploy - if: tag IS present - python: *latest_py3 - before_script: skip - script: tox -e release - cache: pip install: diff --git a/README.rst b/README.rst index 50eba567..a234ec9b 100644 --- a/README.rst +++ b/README.rst @@ -3,6 +3,9 @@ .. image:: https://img.shields.io/pypi/pyversions/skeleton.svg +.. image:: https://dev.azure.com/jaraco/skeleton/_apis/build/status/jaraco.skeleton?branchName=master + :target: https://dev.azure.com/jaraco/skeleton/_build/latest?definitionId=1&branchName=master + .. image:: https://img.shields.io/travis/jaraco/skeleton/master.svg :target: https://travis-ci.org/jaraco/skeleton diff --git a/azure-pipelines.yml b/azure-pipelines.yml new file mode 100644 index 00000000..d461bd00 --- /dev/null +++ b/azure-pipelines.yml @@ -0,0 +1,71 @@ +# Create the project in Azure with: +# az 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') diff --git a/skeleton.md b/skeleton.md index ee26e14b..7e9955c9 100644 --- a/skeleton.md +++ b/skeleton.md @@ -103,23 +103,47 @@ Relies a .flake8 file to correct some default behaviors: ## Continuous Integration -The project is pre-configured to run tests in [Travis-CI](https://travis-ci.org) (.travis.yml). Any new project must be enabled either through their web site or with the `travis enable` command. +The project is pre-configured to run tests through multiple CI providers. + +### Azure Pipelines + +[Azure Pipelines](https://azure.microsoft.com/en-us/services/devops/pipelines/) are the preferred provider as they provide free, fast, multi-platform services. See azure-pipelines.yml for more details. + +Features include: + +- test against multiple Python versions +- run on Ubuntu Bionic + +### Travis CI + +[Travis-CI](https://travis-ci.org) is configured through .travis.yml. Any new project must be enabled either through their web site or with the `travis enable` command. Features include: -- test against Python 2 and 3 +- test against 3 - run on Ubuntu Xenial - correct for broken IPv6 -Also provided is a minimal template for running under Appveyor (Windows). +### Appveyor + +A minimal template for running under Appveyor (Windows) is provided. ### Continuous Deployments -In addition to running tests, an additional deploy stage is configured to automatically release tagged commits to PyPI using [API tokens](https://pypi.org/help/#apitoken). The release process expects an authorized token to be configured with Travis as the TWINE_PASSWORD environment variable. After the Travis project is created, configure the token through the web UI or with a command like the following (bash syntax): +In addition to running tests, an additional deploy stage is configured to automatically release tagged commits to PyPI using [API tokens](https://pypi.org/help/#apitoken). The release process expects an authorized token to be configured with Azure as the `Azure secrets` variable group. This variable group needs to be created only once per organization. For example: ``` -TWINE_PASSWORD={token} travis env copy TWINE_PASSWORD +# create a resource group if none exists +az group create --name main --location eastus2 +# create the vault (try different names until something works) +az keyvault create --name secrets007 --resource-group main +# create the secret +az keyvault secret set --vault-name secrets007 --name PyPI-token --value $token ``` +Then, in the web UI for the project's Pipelines Library, create the `Azure secrets` variable group referencing the key vault name. + +For more details, see [this blog entry](https://blog.jaraco.com/configuring-azure-pipelines-with-secets/). + ## Building Documentation Documentation is automatically built by [Read the Docs](https://readthedocs.org) when the project is registered with it, by way of the .readthedocs.yml file. To test the docs build manually, a tox env may be invoked as `tox -e docs`. Both techniques rely on the dependencies declared in `setup.cfg/options.extras_require.docs`. From b07b27332dcefc9ae9ad0a4d35ca4f39fa358233 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 11 Jan 2020 17:18:12 -0500 Subject: [PATCH 162/323] Correct guidance on project creation. --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index d461bd00..3e80bf44 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1,5 +1,5 @@ # Create the project in Azure with: -# az project create --name $name --organization https://dev.azure.com/$org/ --visibility public +# az devops project create --name $name --organization https://dev.azure.com/$org/ --visibility public # then configure the pipelines (through web UI) trigger: From 0bf3d43d97d5466fddac708e7ca38ba95281c6e7 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 18 Jan 2020 13:36:43 -0500 Subject: [PATCH 163/323] Rely on setuptools_scm 3.4 and setuptools 42. Now setup.py is optional. Remove setuptools from test environment. --- pyproject.toml | 4 +++- setup.cfg | 2 +- setup.py | 2 +- tox.ini | 1 - 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 3afc8c33..74cff744 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,8 @@ [build-system] -requires = ["setuptools>=34.4", "wheel", "setuptools_scm>=1.15"] +requires = ["setuptools>=42", "wheel", "setuptools_scm>=3.4"] build-backend = "setuptools.build_meta" [tool.black] skip-string-normalization = true + +[tool.setuptools_scm] diff --git a/setup.cfg b/setup.cfg index 2c0f7b1f..bc7a9d5d 100644 --- a/setup.cfg +++ b/setup.cfg @@ -20,7 +20,7 @@ packages = find: include_package_data = true python_requires = >=3.6 install_requires = -setup_requires = setuptools_scm >= 1.15.0 +setup_requires = setuptools_scm >= 3.4 [options.extras_require] testing = diff --git a/setup.py b/setup.py index 827e955f..bac24a43 100644 --- a/setup.py +++ b/setup.py @@ -3,4 +3,4 @@ import setuptools if __name__ == "__main__": - setuptools.setup(use_scm_version=True) + setuptools.setup() diff --git a/tox.ini b/tox.ini index fa2a72f4..fb98930e 100644 --- a/tox.ini +++ b/tox.ini @@ -11,7 +11,6 @@ requires = [testenv] deps = - setuptools>=31.0.1 pip_version = pip commands = pytest {posargs} From d9934dc8f5dafb93ca565eb0f8ec9e0c245b8d68 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Sat, 18 Jan 2020 20:50:32 +0200 Subject: [PATCH 164/323] Spelling and capitalisation (#8) Co-authored-by: Jason R. Coombs --- skeleton.md | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/skeleton.md b/skeleton.md index 7e9955c9..439df2f0 100644 --- a/skeleton.md +++ b/skeleton.md @@ -2,9 +2,9 @@ This project is merged with [skeleton](https://github.com/jaraco/skeleton). What is skeleton? It's the scaffolding of a Python project jaraco [introduced in his blog](https://blog.jaraco.com/a-project-skeleton-for-python-projects/). It seeks to provide a means to re-use techniques and inherit advances when managing projects for distribution. -## An SCM Managed Approach +## An SCM-Managed Approach -While maintaining dozens of projects in PyPI, jaraco derives best practices for project distribution and publishes them in the [skeleton repo](https://github.com/jaraco/skeleton), a git repo capturing the evolution and culmination of these best practices. +While maintaining dozens of projects in PyPI, jaraco derives best practices for project distribution and publishes them in the [skeleton repo](https://github.com/jaraco/skeleton), a Git repo capturing the evolution and culmination of these best practices. It's intended to be used by a new or existing project to adopt these practices and honed and proven techniques. Adopters are encouraged to use the project directly and maintain a small deviation from the technique, make their own fork for more substantial changes unique to their environment or preferences, or simply adopt the skeleton once and abandon it thereafter. @@ -38,7 +38,7 @@ The `--allow-unrelated-histories` is necessary because the history from the skel ## Updating -Whenever a change is needed or desired for the general technique for packaging, it can be made in the skeleton project and then merged into each of the derived projects as needed, recommended before each release. As a result, features and best practices for packaging are centrally maintained and readily trickle into a whole suite of packages. This technique lowers the amount of tedious work necessary to create or maintain a project, and coupled with other techniques like continuous integration and deployment, lowers the cost of creating and maintaining refined Python projects to just a few, familiar git operations. +Whenever a change is needed or desired for the general technique for packaging, it can be made in the skeleton project and then merged into each of the derived projects as needed, recommended before each release. As a result, features and best practices for packaging are centrally maintained and readily trickle into a whole suite of packages. This technique lowers the amount of tedious work necessary to create or maintain a project, and coupled with other techniques like continuous integration and deployment, lowers the cost of creating and maintaining refined Python projects to just a few, familiar Git operations. Thereafter, the target project can make whatever customizations it deems relevant to the scaffolding. The project may even at some point decide that the divergence is too great to merit renewed merging with the original skeleton. This approach applies maximal guidance while creating minimal constraints. @@ -46,16 +46,16 @@ Thereafter, the target project can make whatever customizations it deems relevan The features/techniques employed by the skeleton include: -- PEP 517/518 based build relying on setuptools as the build tool -- setuptools declarative configuration using setup.cfg +- PEP 517/518-based build relying on Setuptools as the build tool +- Setuptools declarative configuration using setup.cfg - tox for running tests -- A README.rst as reStructuredText with some popular badges, but with readthedocs and appveyor badges commented out +- A README.rst as reStructuredText with some popular badges, but with Read the Docs and AppVeyor badges commented out - A CHANGES.rst file intended for publishing release notes about the project -- Use of [black](https://black.readthedocs.io/en/stable/) for code formatting (disabled on unsupported Python 3.5 and earlier) +- Use of [Black](https://black.readthedocs.io/en/stable/) for code formatting (disabled on unsupported Python 3.5 and earlier) ## Packaging Conventions -A pyproject.toml is included to enable PEP 517 and PEP 518 compatibility and declares the requirements necessary to build the project on setuptools (a minimum version compatible with setup.cfg declarative config). +A pyproject.toml is included to enable PEP 517 and PEP 518 compatibility and declares the requirements necessary to build the project on Setuptools (a minimum version compatible with setup.cfg declarative config). The setup.cfg file implements the following features: @@ -92,14 +92,14 @@ A pytest.ini is included to define common options around running tests. In parti - rely on default test discovery in the current directory - avoid recursing into common directories not containing tests -- run doctests on modules and invoke flake8 tests -- in doctests, allow unicode literals and regular literals to match, allowing for doctests to run on Python 2 and 3. Also enable ELLIPSES, a default that would be undone by supplying the prior option. +- run doctests on modules and invoke Flake8 tests +- in doctests, allow Unicode literals and regular literals to match, allowing for doctests to run on Python 2 and 3. Also enable ELLIPSES, a default that would be undone by supplying the prior option. - filters out known warnings caused by libraries/functionality included by the skeleton -Relies a .flake8 file to correct some default behaviors: +Relies on a .flake8 file to correct some default behaviors: - disable mutually incompatible rules W503 and W504 -- support for black format +- support for Black format ## Continuous Integration @@ -116,10 +116,10 @@ Features include: ### Travis CI -[Travis-CI](https://travis-ci.org) is configured through .travis.yml. Any new project must be enabled either through their web site or with the `travis enable` command. +[Travis CI](https://travis-ci.org) is configured through .travis.yml. Any new project must be enabled either through their web site or with the `travis enable` command. Features include: -- test against 3 +- test against Python 3 - run on Ubuntu Xenial - correct for broken IPv6 @@ -148,7 +148,7 @@ For more details, see [this blog entry](https://blog.jaraco.com/configuring-azur Documentation is automatically built by [Read the Docs](https://readthedocs.org) when the project is registered with it, by way of the .readthedocs.yml file. To test the docs build manually, a tox env may be invoked as `tox -e docs`. Both techniques rely on the dependencies declared in `setup.cfg/options.extras_require.docs`. -In addition to building the sphinx docs scaffolded in `docs/`, the docs build a `history.html` file that first injects release dates and hyperlinks into the CHANGES.rst before incorporating it as history in the docs. +In addition to building the Sphinx docs scaffolded in `docs/`, the docs build a `history.html` file that first injects release dates and hyperlinks into the CHANGES.rst before incorporating it as history in the docs. ## Cutting releases From 7558cfe2eb2f1ffe3676905e9871466cbc9da24f Mon Sep 17 00:00:00 2001 From: johnthagen Date: Tue, 14 Jan 2020 07:53:19 -0500 Subject: [PATCH 165/323] Line wrap LICENSE file --- LICENSE | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/LICENSE b/LICENSE index 5e795a61..353924be 100644 --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,19 @@ Copyright Jason R. Coombs -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. From 479ac2149d872757160732bc977977ee0192ac51 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 19 Jan 2020 10:51:33 -0500 Subject: [PATCH 166/323] Finish dropping support for Python 2 (I hope). --- setup.cfg | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/setup.cfg b/setup.cfg index bc7a9d5d..3e621072 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,3 @@ -[bdist_wheel] -universal = 1 - [metadata] license_file = LICENSE name = skeleton @@ -14,6 +11,7 @@ classifiers = Intended Audience :: Developers License :: OSI Approved :: MIT License Programming Language :: Python :: 3 + Programming Language :: Python :: 3 :: Only [options] packages = find: From 29c497286e9f8e835c68b7b4ddb9ff3635a273dc Mon Sep 17 00:00:00 2001 From: layday <31134424+layday@users.noreply.github.com> Date: Thu, 23 Jan 2020 03:48:38 +0200 Subject: [PATCH 167/323] Require toml extra for setuptools_scm (#12) * Require toml extra for setuptools_scm setuptools_scm does not know to invoke itself if it can't read pyproject.toml. This broke sdist installs for projects deriving from skeleton: $ python -m pip install zipp --no-binary zipp Collecting zipp [...] Successfully installed zipp-0.0.0 Note the version number defaulting to '0.0.0'. Building locally only works because pep517, the build tool, depends on toml which it exposes to the build environment. * Require setuptools_scm 3.4.1 at a minimum A bare [tool.setuptools_scm] does not work in 3.4.0. * fixup! Require toml extra for setuptools_scm * fixup! Require setuptools_scm 3.4.1 at a minimum --- pyproject.toml | 2 +- setup.cfg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 74cff744..6ee7df23 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["setuptools>=42", "wheel", "setuptools_scm>=3.4"] +requires = ["setuptools>=42", "wheel", "setuptools_scm[toml]>=3.4.1"] build-backend = "setuptools.build_meta" [tool.black] diff --git a/setup.cfg b/setup.cfg index 3e621072..c20fa103 100644 --- a/setup.cfg +++ b/setup.cfg @@ -18,7 +18,7 @@ packages = find: include_package_data = true python_requires = >=3.6 install_requires = -setup_requires = setuptools_scm >= 3.4 +setup_requires = setuptools_scm[toml] >= 3.4.1 [options.extras_require] testing = From 9a7d846ae8775e5c49cc9933fa897cc761c3b20b Mon Sep 17 00:00:00 2001 From: Hugo Date: Sat, 18 Jan 2020 20:53:39 +0200 Subject: [PATCH 168/323] Fix AppVeyor typo --- skeleton.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/skeleton.md b/skeleton.md index 439df2f0..340b65cf 100644 --- a/skeleton.md +++ b/skeleton.md @@ -123,9 +123,9 @@ Features include: - run on Ubuntu Xenial - correct for broken IPv6 -### Appveyor +### AppVeyor -A minimal template for running under Appveyor (Windows) is provided. +A minimal template for running under AppVeyor (Windows) is provided. ### Continuous Deployments From 0aa6c5c191ad22bbc4ad30d0880e531d4e66c0b3 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Thu, 23 Jan 2020 22:51:29 +0200 Subject: [PATCH 169/323] Link badge to PyPI rather than static image And DRY the link --- README.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index a234ec9b..4c7fd554 100644 --- a/README.rst +++ b/README.rst @@ -1,7 +1,10 @@ .. image:: https://img.shields.io/pypi/v/skeleton.svg - :target: https://pypi.org/project/skeleton + :target: `PyPI link`_ .. image:: https://img.shields.io/pypi/pyversions/skeleton.svg + :target: `PyPI link`_ + +.. _PyPI link: https://pypi.org/project/skeleton .. image:: https://dev.azure.com/jaraco/skeleton/_apis/build/status/jaraco.skeleton?branchName=master :target: https://dev.azure.com/jaraco/skeleton/_build/latest?definitionId=1&branchName=master From 0b681b040f3e13793c61c1dfe6e510d9e3a8870a Mon Sep 17 00:00:00 2001 From: Vincent Fazio Date: Tue, 4 Feb 2020 14:51:03 -0600 Subject: [PATCH 170/323] setup.cfg: let python-tag mirror python_requires In order to generate a wheel in accordance with PEP 425 to restrict the minimum required version of Python (3.6), the `python-tag` bdist_wheel option needs to be specified so the wheel gets tagged properly. Before: zipp-x.x.x-py3-none-any.whl After: zipp-x.x.x-py36-none-any.whl Signed-off-by: Vincent Fazio --- setup.cfg | 3 +++ 1 file changed, 3 insertions(+) diff --git a/setup.cfg b/setup.cfg index c20fa103..f2643ea2 100644 --- a/setup.cfg +++ b/setup.cfg @@ -13,6 +13,9 @@ classifiers = Programming Language :: Python :: 3 Programming Language :: Python :: 3 :: Only +[bdist_wheel] +python-tag=py36 + [options] packages = find: include_package_data = true From eb00cd0636bc3e4cef9217b2c5eccdcaebbe5d65 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 9 Feb 2020 09:29:18 -0500 Subject: [PATCH 171/323] Normalize whitespace --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index f2643ea2..3436e6b0 100644 --- a/setup.cfg +++ b/setup.cfg @@ -14,7 +14,7 @@ classifiers = Programming Language :: Python :: 3 :: Only [bdist_wheel] -python-tag=py36 +python-tag = py36 [options] packages = find: From 4f845452dd0f7e95b7959b8e0ea50374c73b7920 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 17 Feb 2020 11:23:25 -0500 Subject: [PATCH 172/323] Revert "setup.cfg: let python-tag mirror python_requires" This reverts commit 0b681b040f3e13793c61c1dfe6e510d9e3a8870a. Ref jaraco/zipp#42 --- setup.cfg | 3 --- 1 file changed, 3 deletions(-) diff --git a/setup.cfg b/setup.cfg index 3436e6b0..c20fa103 100644 --- a/setup.cfg +++ b/setup.cfg @@ -13,9 +13,6 @@ classifiers = Programming Language :: Python :: 3 Programming Language :: Python :: 3 :: Only -[bdist_wheel] -python-tag = py36 - [options] packages = find: include_package_data = true From 8fbd841d17a029232577d71587c02aa429874287 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 28 Feb 2020 17:36:22 -0600 Subject: [PATCH 173/323] Update to bionic for Travis. Correct comment about IPv6 workaround. --- .travis.yml | 4 ++-- skeleton.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 17e45a67..923377f6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -dist: xenial +dist: bionic language: python python: @@ -11,7 +11,7 @@ install: - pip install tox tox-venv before_script: - # Disable IPv6. Ref travis-ci/travis-ci#8361 + # Enable IPv6. Ref travis-ci/travis-ci#8361 - if [ "${TRAVIS_OS_NAME}" == "linux" ]; then sudo sh -c 'echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6'; fi diff --git a/skeleton.md b/skeleton.md index 340b65cf..4544158f 100644 --- a/skeleton.md +++ b/skeleton.md @@ -120,7 +120,7 @@ Features include: Features include: - test against Python 3 -- run on Ubuntu Xenial +- run on Ubuntu Bionic - correct for broken IPv6 ### AppVeyor From b4dd44cc7c8da44bcc8f2674d2a5d764091f63c6 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 15 Mar 2020 15:05:37 -0400 Subject: [PATCH 174/323] Suppress warnings in pytest-flake8, pytest-black, and pytest-checkdocs. --- pytest.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pytest.ini b/pytest.ini index 7b9b714f..60d9bf76 100644 --- a/pytest.ini +++ b/pytest.ini @@ -3,3 +3,5 @@ norecursedirs=dist build .tox .eggs addopts=--doctest-modules --flake8 --black --cov doctest_optionflags=ALLOW_UNICODE ELLIPSIS filterwarnings= + # https://github.com/pytest-dev/pytest/issues/6928 + ignore:direct construction of .*Item has been deprecated:DeprecationWarning From 2c2fee097e1898a8c00b4434f74063302aeb96e9 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 27 Mar 2020 09:22:01 -0400 Subject: [PATCH 175/323] Prefer pytest-black to pytest-black-multipy --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index c20fa103..3f887a64 100644 --- a/setup.cfg +++ b/setup.cfg @@ -26,7 +26,7 @@ testing = pytest >= 3.5, !=3.7.3 pytest-checkdocs >= 1.2.3 pytest-flake8 - pytest-black-multipy + pytest-black >= 0.3.7 pytest-cov # local From 045e6ad88af135f85365248812cbe5ddb3faf355 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 5 Apr 2020 14:51:43 -0400 Subject: [PATCH 176/323] Test against Windows and Mac --- azure-pipelines.yml | 80 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 azure-pipelines.yml diff --git a/azure-pipelines.yml b/azure-pipelines.yml new file mode 100644 index 00000000..25e638b5 --- /dev/null +++ b/azure-pipelines.yml @@ -0,0 +1,80 @@ +# 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: $(pool_vm_image) + +variables: +- group: Azure secrets + +stages: +- stage: Test + jobs: + + - job: 'Test' + strategy: + matrix: + Bionic Python 3.6: + python.version: '3.6' + pool_vm_image: Ubuntu-18.04 + Bionic Python 3.8: + python.version: '3.8' + pool_vm_image: Ubuntu-18.04 + Windows: + python.version: '3.8' + pool_vm_image: vs2017-win2016 + MacOS: + python.version: '3.8' + pool_vm_image: macos-10.15 + + 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') From bb96dc3fcd738e3202423a0ed09ea9f5dd4ce50b Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 25 Apr 2020 09:54:59 -0400 Subject: [PATCH 177/323] Define a default pool_vm_image --- azure-pipelines.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 25e638b5..fd432962 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -15,6 +15,8 @@ pool: variables: - group: Azure secrets +- name: pool_vm_image + value: Ubuntu-18.04 stages: - stage: Test @@ -25,10 +27,8 @@ stages: matrix: Bionic Python 3.6: python.version: '3.6' - pool_vm_image: Ubuntu-18.04 Bionic Python 3.8: python.version: '3.8' - pool_vm_image: Ubuntu-18.04 Windows: python.version: '3.8' pool_vm_image: vs2017-win2016 From f66d87899937361bea35f437ba293774f0375ca2 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 27 Apr 2020 18:49:51 -0400 Subject: [PATCH 178/323] Remove tox-venv and tox-pip-version. Tox-venv is discouraged (https://github.com/tox-dev/tox-venv/issues/48#issuecomment-620227405) and tox-pip-version was only there to support tox-venv. venv is dead; long live virtualenv. --- tox.ini | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tox.ini b/tox.ini index fb98930e..ba5857a3 100644 --- a/tox.ini +++ b/tox.ini @@ -3,15 +3,10 @@ envlist = python minversion = 3.2 # https://github.com/jaraco/skeleton/issues/6 tox_pip_extensions_ext_venv_update = true -# Ensure that a late version of pip is used even on tox-venv. -requires = - tox-pip-version>=0.0.6 - tox-venv [testenv] deps = -pip_version = pip commands = pytest {posargs} usedevelop = True From 0df8947fc97f0f0fab6e680d20d8affe5838aec3 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 1 May 2020 23:15:53 -0400 Subject: [PATCH 179/323] Remove more references to tox-venv --- .travis.yml | 2 +- appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 923377f6..37fa499d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,7 @@ python: cache: pip install: -- pip install tox tox-venv +- pip install tox before_script: # Enable IPv6. Ref travis-ci/travis-ci#8361 diff --git a/appveyor.yml b/appveyor.yml index 6a1c99a9..c6f46e4f 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -18,7 +18,7 @@ cache: - '%LOCALAPPDATA%\pip\Cache' test_script: - - "python -m pip install -U tox tox-venv virtualenv" + - "python -m pip install -U tox virtualenv" - "tox" version: '{build}' From 649bc7934a13b78eb1283cad9919b9f26c5426e9 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 2 May 2020 07:25:43 -0400 Subject: [PATCH 180/323] Add workaround for warning emitted when junitxml is used. Ref pytest-dev/pytest#6178. --- pytest.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pytest.ini b/pytest.ini index 60d9bf76..62c0f365 100644 --- a/pytest.ini +++ b/pytest.ini @@ -2,6 +2,8 @@ norecursedirs=dist build .tox .eggs addopts=--doctest-modules --flake8 --black --cov doctest_optionflags=ALLOW_UNICODE ELLIPSIS +# workaround for warning pytest-dev/pytest#6178 +junit_family=xunit2 filterwarnings= # https://github.com/pytest-dev/pytest/issues/6928 ignore:direct construction of .*Item has been deprecated:DeprecationWarning From 7455f2f25310b2d778a648e45d32033ccc790946 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 3 May 2020 15:31:25 -0400 Subject: [PATCH 181/323] Include mypy for type checking during tests. --- mypy.ini | 2 ++ pytest.ini | 2 +- setup.cfg | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 mypy.ini diff --git a/mypy.ini b/mypy.ini new file mode 100644 index 00000000..976ba029 --- /dev/null +++ b/mypy.ini @@ -0,0 +1,2 @@ +[mypy] +ignore_missing_imports = True diff --git a/pytest.ini b/pytest.ini index 62c0f365..381b3271 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,6 +1,6 @@ [pytest] norecursedirs=dist build .tox .eggs -addopts=--doctest-modules --flake8 --black --cov +addopts=--doctest-modules --flake8 --black --cov --mypy doctest_optionflags=ALLOW_UNICODE ELLIPSIS # workaround for warning pytest-dev/pytest#6178 junit_family=xunit2 diff --git a/setup.cfg b/setup.cfg index 3f887a64..e2dcebb1 100644 --- a/setup.cfg +++ b/setup.cfg @@ -28,6 +28,7 @@ testing = pytest-flake8 pytest-black >= 0.3.7 pytest-cov + pytest-mypy # local From 172c52b03a77e70e92a1ee491b059464e938fde3 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 22 May 2020 11:31:51 -0400 Subject: [PATCH 182/323] Ensure virtualenv is upgraded when installing tox. Fixes jaraco/path#188. --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 37fa499d..eed7b0a4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,8 @@ python: cache: pip install: -- pip install tox +# ensure virtualenv is upgraded to avoid issues like jaraco/path#188 +- pip install -U --upgrade-strategy=eager tox before_script: # Enable IPv6. Ref travis-ci/travis-ci#8361 From c764baf26c001ca5828fe99b8aa33aa55e9fd554 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 22 May 2020 23:17:49 -0400 Subject: [PATCH 183/323] Run tests on prereleases of Python on Windows. Fixes jaraco/skeleton#17. --- azure-pipelines.yml | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index fd432962..2be55e73 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -29,9 +29,12 @@ stages: python.version: '3.6' Bionic Python 3.8: python.version: '3.8' - Windows: + Windows Python 3.8: python.version: '3.8' pool_vm_image: vs2017-win2016 + Windows Python Prerelease: + python.version: '3.9' + pool_vm_image: vs2017-win2016 MacOS: python.version: '3.8' pool_vm_image: macos-10.15 @@ -39,10 +42,21 @@ stages: maxParallel: 4 steps: + - task: NuGetToolInstaller@1 + displayName: 'Install NuGet' + condition: eq(variables['pool_vm_image'], 'vs2017-win2016') + + - powershell: | + nuget install python -Prerelease -OutputDirectory "$(Build.BinariesDirectory)" -ExcludeVersion -NonInteractive + Write-Host "##vso[task.prependpath]$(Build.BinariesDirectory)\python\tools" + Write-Host "##vso[task.prependpath]$(Build.BinariesDirectory)\python\tools\Scripts" + condition: and(succeeded(), and(eq(variables['python.version'], '3.9'), eq(variables['pool_vm_image'], 'vs2017-win2016'))) + - task: UsePythonVersion@0 inputs: versionSpec: '$(python.version)' architecture: 'x64' + condition: and(succeeded(), ne(variables['python.version'], '3.9')) - script: python -m pip install tox displayName: 'Install tox' From fc0162baafbd984f245de359b5a64c2d997a0714 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 23 May 2020 11:52:32 -0400 Subject: [PATCH 184/323] Add workaround for python/mypy#8627. Fixes jaraco/skeleton#18. --- setup.cfg | 2 ++ 1 file changed, 2 insertions(+) diff --git a/setup.cfg b/setup.cfg index e2dcebb1..0cf95493 100644 --- a/setup.cfg +++ b/setup.cfg @@ -29,6 +29,8 @@ testing = pytest-black >= 0.3.7 pytest-cov pytest-mypy + # workaround for python/mypy#8627 + mypy@git+https://github.com/python/mypy # local From 814eb669c2e5436d8ebce1be845fa99e0687317b Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 23 May 2020 12:17:23 -0400 Subject: [PATCH 185/323] Add 'refresh.svg' demonstrating an example of refreshing a project with the latest skeleton. Ref #7. --- docs/refresh.svg | 193 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 193 insertions(+) create mode 100644 docs/refresh.svg diff --git a/docs/refresh.svg b/docs/refresh.svg new file mode 100644 index 00000000..04e62f00 --- /dev/null +++ b/docs/refresh.svg @@ -0,0 +1,193 @@ + + + + + + + + + + + path master $ path master $ git diff ...FETCH path master $ git diff ...FETCH path master $ git diff ...FETCH path master $ git diff ...FETCH path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton From https://github.com/jaraco/skeleton * branch HEAD -> FETCH_HEADpath master $ path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton path master $ git diff ...FETCH path master $ git diff ...FETCH path master $ git diff ...FETCH path master $ git diff ...FETCH path master $ git diff ...FETCH path master $ git diff ...FETCH path master $ git diff ...FETCH path master $ git diff ...FETCH path master $ git diff ...FETCH path master $ git diff ...FETCH path master $ git diff ...FETCH path master $ git diff ...FETCH path master $ git diff ...FETCH path master $ git diff ...FETCH_ path master $ git diff ...FETCH_H path master $ git diff ...FETCH_HE path master $ git diff ...FETCH_HEA path master $ git diff ...FETCH_HEAD path master $ git diff ...FETCH_HEAD diff --git a/azure-pipelines.yml b/azure-pipelines.yml index fd43296..2be55e7 100644--- a/azure-pipelines.yml+++ b/azure-pipelines.yml@@ -29,9 +29,12 @@ stages: python.version: '3.6' Bionic Python 3.8: python.version: '3.8'- Windows:+ Windows Python 3.8: pool_vm_image: vs2017-win2016+ Windows Python Prerelease:+ python.version: '3.9'+ pool_vm_image: vs2017-win2016 MacOS: pool_vm_image: macos-10.15@@ -39,10 +42,21 @@ stages: maxParallel: 4 steps:+ - task: NuGetToolInstaller@1+ displayName: 'Install NuGet'+ condition: eq(variables['pool_vm_image'], 'vs2017-win2016')++ - powershell: |+ nuget install python -Prerelease -OutputDi + nuget install python -Prerelease -OutputDirectory "$(Build.BinariesDirectory)" -ExcludeVersion -NonInteractive+ Write-Host "##vso[task.prependpath]$(Build.BinariesDirectory)\python\tools"+ Write-Host "##vso[task.prependpath]$(Build.BinariesDirectory)\python\tools\Scripts"+ condition: and(succeeded(), and(eq(variables['python.version'], '3.9'), eq(variables['pool_vm_image'], 'vs2017-win2016'))) - task: UsePythonVersion@0 inputs: versionSpec: '$(python.version)' architecture: 'x64'+ condition: and(succeeded(), ne(variables['python.version'], '3.9')) - script: python -m pip install tox displayName: 'Install tox'diff --git a/setup.cfg b/setup.cfgindex e2dcebb..0cf9549 100644--- a/setup.cfg+++ b/setup.cfg@@ -29,6 +29,8 @@ testing = pytest-black >= 0.3.7 pytest-cov pytest-mypy+ # workaround for python/mypy#8627: + mypy@git+https://github.com/python/mypy # local(END) path master $ git diff ...FETCH_HEAD path master $ git diff ...FETCH_HEAD path master $ git diff ...FETCH_HEAD path master $ git diff ...FETCH_HEAD path master $ git push path master $ git push path master $ git pull path master $ git pull path master $ git pull gh://jaraco/skeleton path master $ git pull gh://jaraco/skeleton path master $ git pull gh://jaraco/skeleton path master $ git pull gh://jaraco/skeleton path master $ git pull gh://jaraco/skeleton path master $ git pull gh://jaraco/skeleton path master $ git pull gh://jaraco/skeleton path master $ git pull gh://jaraco/skeleton path master $ git pull gh://jaraco/skeleton path master $ git pull gh://jaraco/skeleton path master $ git pull gh://jaraco/skeleton path master $ git pull gh://jaraco/skeleton path master $ git pull gh://jaraco/skeleton path master $ git pull gh://jaraco/skeleton path master $ git pull gh://jaraco/skeleton path master $ git pull gh://jaraco/skeleton path master $ git pull gh://jaraco/skeleton path master $ git pull gh://jaraco/skeleton path master $ git pull gh://jaraco/skeleton path master $ git pull gh://jaraco/skeleton path master $ git pull gh://jaraco/skeleton path master $ git pull gh://jaraco/skeleton Auto-merging setup.cfgAuto-merging azure-pipelines.ymlMerge made by the 'recursive' strategy. azure-pipelines.yml | 16 +++++++++++++++- setup.cfg | 2 ++ 2 files changed, 17 insertions(+), 1 deletion(-)path master $ git pull gh://jaraco/skeleton path master $ git pull gh://jaraco/skeleton path master $ git pull gh://jaraco/skeleton path master $ git pull gh://jaraco/skeleton path master $ git pull gh://jaraco/skeleton path master $ git pull gh://jaraco/skeleton path master $ git push path master $ git push path master $ git push Enumerating objects: 10, done. Counting objects: 50% (5/10)Counting objects: 100% (10/10), done.Delta compression using up to 8 threadsCompressing objects: 100% (4/4), done.Writing objects: 100% (4/4), 512 bytes | 512.00 KiB/s, done.Total 4 (delta 3), reused 0 (delta 0), pack-reused 0remote: Resolving deltas: 100% (3/3), completed with 3 local objects. To https://github.com/jaraco/path ff4d395..bd18026 master -> masterpath master $ + \ No newline at end of file From 169dad91b8316a7138bec2f7b51db80cab0aadc4 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 23 May 2020 22:40:39 -0400 Subject: [PATCH 186/323] Move workaround for python/mypy#8627 to tox.ini, as adding it to setup.cfg prevents releases to PyPI. Fixes jaraco/skeleton#19. --- setup.cfg | 2 -- tox.ini | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index 0cf95493..e2dcebb1 100644 --- a/setup.cfg +++ b/setup.cfg @@ -29,8 +29,6 @@ testing = pytest-black >= 0.3.7 pytest-cov pytest-mypy - # workaround for python/mypy#8627 - mypy@git+https://github.com/python/mypy # local diff --git a/tox.ini b/tox.ini index ba5857a3..97cc4261 100644 --- a/tox.ini +++ b/tox.ini @@ -7,6 +7,8 @@ tox_pip_extensions_ext_venv_update = true [testenv] deps = + # workaround for python/mypy#8627 + mypy@git+https://github.com/python/mypy commands = pytest {posargs} usedevelop = True From a2fc27929fe7f7d6dc87700560d58abd08ddad46 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 26 Jul 2020 03:33:59 -0400 Subject: [PATCH 187/323] Remove workaround for python/mypy#8627. Ref jaraco/skeleton#18. --- tox.ini | 2 -- 1 file changed, 2 deletions(-) diff --git a/tox.ini b/tox.ini index 97cc4261..ba5857a3 100644 --- a/tox.ini +++ b/tox.ini @@ -7,8 +7,6 @@ tox_pip_extensions_ext_venv_update = true [testenv] deps = - # workaround for python/mypy#8627 - mypy@git+https://github.com/python/mypy commands = pytest {posargs} usedevelop = True From 30b11308288ad31f0b7afa1d5a9c1934f1cb84e0 Mon Sep 17 00:00:00 2001 From: Jon Dufresne Date: Fri, 14 Aug 2020 18:34:14 -0700 Subject: [PATCH 188/323] Move tests and docs directories outside of source directory These directories were previously included in the wheel package. Now, they are not. This had resulted in the files being unnecessarily downloaded and installed to virtual environments when using pip. However, these files go unused production installations. These files are only necessary for the development of importlib_metadata itself, so they now live at the root of the project. This was more noticeable when pip dependencies are vendored or bundled, as it adds unnecessary bloat to these bundles. The directories are still included in the sdist, so packagers and library users can run the tests locally if they choose, they just wont be installed to the site-packages directory. --- {importlib_metadata/docs => docs}/__init__.py | 0 .../docs => docs}/changelog.rst | 0 {importlib_metadata/docs => docs}/conf.py | 0 {importlib_metadata/docs => docs}/index.rst | 0 {importlib_metadata/docs => docs}/using.rst | 0 setup.cfg | 17 +---------------- .../tests => tests}/__init__.py | 0 .../tests => tests}/data/__init__.py | 0 .../data/example-21.12-py3-none-any.whl | Bin .../data/example-21.12-py3.6.egg | Bin .../tests => tests}/fixtures.py | 2 +- .../tests => tests}/test_api.py | 2 +- .../tests => tests}/test_integration.py | 2 +- .../tests => tests}/test_main.py | 2 +- .../tests => tests}/test_zip.py | 4 ++-- tox.ini | 2 +- 16 files changed, 8 insertions(+), 23 deletions(-) rename {importlib_metadata/docs => docs}/__init__.py (100%) rename {importlib_metadata/docs => docs}/changelog.rst (100%) rename {importlib_metadata/docs => docs}/conf.py (100%) rename {importlib_metadata/docs => docs}/index.rst (100%) rename {importlib_metadata/docs => docs}/using.rst (100%) rename {importlib_metadata/tests => tests}/__init__.py (100%) rename {importlib_metadata/tests => tests}/data/__init__.py (100%) rename {importlib_metadata/tests => tests}/data/example-21.12-py3-none-any.whl (100%) rename {importlib_metadata/tests => tests}/data/example-21.12-py3.6.egg (100%) rename {importlib_metadata/tests => tests}/fixtures.py (98%) rename {importlib_metadata/tests => tests}/test_api.py (99%) rename {importlib_metadata/tests => tests}/test_integration.py (97%) rename {importlib_metadata/tests => tests}/test_main.py (99%) rename {importlib_metadata/tests => tests}/test_zip.py (97%) diff --git a/importlib_metadata/docs/__init__.py b/docs/__init__.py similarity index 100% rename from importlib_metadata/docs/__init__.py rename to docs/__init__.py diff --git a/importlib_metadata/docs/changelog.rst b/docs/changelog.rst similarity index 100% rename from importlib_metadata/docs/changelog.rst rename to docs/changelog.rst diff --git a/importlib_metadata/docs/conf.py b/docs/conf.py similarity index 100% rename from importlib_metadata/docs/conf.py rename to docs/conf.py diff --git a/importlib_metadata/docs/index.rst b/docs/index.rst similarity index 100% rename from importlib_metadata/docs/index.rst rename to docs/index.rst diff --git a/importlib_metadata/docs/using.rst b/docs/using.rst similarity index 100% rename from importlib_metadata/docs/using.rst rename to docs/using.rst diff --git a/setup.cfg b/setup.cfg index eee6caf2..d993f5cd 100644 --- a/setup.cfg +++ b/setup.cfg @@ -22,16 +22,7 @@ install_requires = pathlib2; python_version < '3' contextlib2; python_version < '3' configparser>=3.5; python_version < '3' -packages = find: - -[options.package_data] -* = *.zip, *.file, *.txt, *.toml -importlib_metadata = - docs/* - docs/_static/* -importlib_metadata.tests.data = - *.egg - *.whl +packages = importlib_metadata [mypy] ignore_missing_imports = True @@ -42,12 +33,6 @@ ignore_missing_imports = True # that package.__spec__ is not None. strict_optional = False -[mypy-importlib_metadata.docs.*] -ignore_errors: True - -[mypy-importlib_metadata.tests.*] -ignore_errors: True - [wheel] universal=1 diff --git a/importlib_metadata/tests/__init__.py b/tests/__init__.py similarity index 100% rename from importlib_metadata/tests/__init__.py rename to tests/__init__.py diff --git a/importlib_metadata/tests/data/__init__.py b/tests/data/__init__.py similarity index 100% rename from importlib_metadata/tests/data/__init__.py rename to tests/data/__init__.py diff --git a/importlib_metadata/tests/data/example-21.12-py3-none-any.whl b/tests/data/example-21.12-py3-none-any.whl similarity index 100% rename from importlib_metadata/tests/data/example-21.12-py3-none-any.whl rename to tests/data/example-21.12-py3-none-any.whl diff --git a/importlib_metadata/tests/data/example-21.12-py3.6.egg b/tests/data/example-21.12-py3.6.egg similarity index 100% rename from importlib_metadata/tests/data/example-21.12-py3.6.egg rename to tests/data/example-21.12-py3.6.egg diff --git a/importlib_metadata/tests/fixtures.py b/tests/fixtures.py similarity index 98% rename from importlib_metadata/tests/fixtures.py rename to tests/fixtures.py index 20982fa1..506d20ff 100644 --- a/importlib_metadata/tests/fixtures.py +++ b/tests/fixtures.py @@ -7,7 +7,7 @@ import textwrap import test.support -from .._compat import pathlib, contextlib +from importlib_metadata._compat import pathlib, contextlib __metaclass__ = type diff --git a/importlib_metadata/tests/test_api.py b/tests/test_api.py similarity index 99% rename from importlib_metadata/tests/test_api.py rename to tests/test_api.py index eb0ff53b..9ef1e733 100644 --- a/importlib_metadata/tests/test_api.py +++ b/tests/test_api.py @@ -3,7 +3,7 @@ import unittest from . import fixtures -from .. import ( +from importlib_metadata import ( Distribution, PackageNotFoundError, distribution, entry_points, files, metadata, requires, version, ) diff --git a/importlib_metadata/tests/test_integration.py b/tests/test_integration.py similarity index 97% rename from importlib_metadata/tests/test_integration.py rename to tests/test_integration.py index cbb940bd..377574c4 100644 --- a/importlib_metadata/tests/test_integration.py +++ b/tests/test_integration.py @@ -7,7 +7,7 @@ import packaging.version from . import fixtures -from .. import ( +from importlib_metadata import ( Distribution, _compat, version, diff --git a/importlib_metadata/tests/test_main.py b/tests/test_main.py similarity index 99% rename from importlib_metadata/tests/test_main.py rename to tests/test_main.py index 4ffdd5d6..847750bc 100644 --- a/importlib_metadata/tests/test_main.py +++ b/tests/test_main.py @@ -11,7 +11,7 @@ import pyfakefs.fake_filesystem_unittest as ffs from . import fixtures -from .. import ( +from importlib_metadata import ( Distribution, EntryPoint, MetadataPathFinder, PackageNotFoundError, distributions, entry_points, metadata, version, diff --git a/importlib_metadata/tests/test_zip.py b/tests/test_zip.py similarity index 97% rename from importlib_metadata/tests/test_zip.py rename to tests/test_zip.py index 4aae933d..5cebcd02 100644 --- a/importlib_metadata/tests/test_zip.py +++ b/tests/test_zip.py @@ -1,7 +1,7 @@ import sys import unittest -from .. import ( +from importlib_metadata import ( distribution, entry_points, files, PackageNotFoundError, version, distributions, ) @@ -20,7 +20,7 @@ class TestZip(unittest.TestCase): - root = 'importlib_metadata.tests.data' + root = 'tests.data' def _fixture_on_path(self, filename): pkg_file = resources.files(self.root).joinpath(filename) diff --git a/tox.ini b/tox.ini index b2775cd1..1f0e9757 100644 --- a/tox.ini +++ b/tox.ini @@ -55,7 +55,7 @@ extras = [testenv:docs] basepython = python3 commands = - sphinx-build importlib_metadata/docs build/sphinx/html + sphinx-build docs build/sphinx/html extras = docs From e630a0c8f213e4013b57a695187755641f7537a3 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 30 Aug 2020 14:30:41 -0400 Subject: [PATCH 189/323] Create Github releases when releasing the package. Fixes jaraco/skeleton#23. --- tox.ini | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tox.ini b/tox.ini index ba5857a3..7347d95f 100644 --- a/tox.ini +++ b/tox.ini @@ -26,11 +26,14 @@ deps = pep517>=0.5 twine[keyring]>=1.13 path + jaraco.develop>=7.1 passenv = TWINE_PASSWORD + GITHUB_TOKEN setenv = TWINE_USERNAME = {env:TWINE_USERNAME:__token__} commands = python -c "import path; path.Path('dist').rmtree_p()" python -m pep517.build . python -m twine upload dist/* + python -m jaraco.develop.create-github-release From 8032e1d3fb114bb93736d5b6668adc4d44f6c00b Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 30 Aug 2020 15:26:29 -0400 Subject: [PATCH 190/323] Moved refresh.svg to another branch. Reference the animation from the docs. Ref jaraco/skeleton#7. --- docs/refresh.svg | 193 ----------------------------------------------- skeleton.md | 4 + 2 files changed, 4 insertions(+), 193 deletions(-) delete mode 100644 docs/refresh.svg diff --git a/docs/refresh.svg b/docs/refresh.svg deleted file mode 100644 index 04e62f00..00000000 --- a/docs/refresh.svg +++ /dev/null @@ -1,193 +0,0 @@ - - - - - - - - - - - path master $ path master $ git diff ...FETCH path master $ git diff ...FETCH path master $ git diff ...FETCH path master $ git diff ...FETCH path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton From https://github.com/jaraco/skeleton * branch HEAD -> FETCH_HEADpath master $ path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton path master $ git fetch gh://jaraco/skeleton path master $ git diff ...FETCH path master $ git diff ...FETCH path master $ git diff ...FETCH path master $ git diff ...FETCH path master $ git diff ...FETCH path master $ git diff ...FETCH path master $ git diff ...FETCH path master $ git diff ...FETCH path master $ git diff ...FETCH path master $ git diff ...FETCH path master $ git diff ...FETCH path master $ git diff ...FETCH path master $ git diff ...FETCH path master $ git diff ...FETCH_ path master $ git diff ...FETCH_H path master $ git diff ...FETCH_HE path master $ git diff ...FETCH_HEA path master $ git diff ...FETCH_HEAD path master $ git diff ...FETCH_HEAD diff --git a/azure-pipelines.yml b/azure-pipelines.yml index fd43296..2be55e7 100644--- a/azure-pipelines.yml+++ b/azure-pipelines.yml@@ -29,9 +29,12 @@ stages: python.version: '3.6' Bionic Python 3.8: python.version: '3.8'- Windows:+ Windows Python 3.8: pool_vm_image: vs2017-win2016+ Windows Python Prerelease:+ python.version: '3.9'+ pool_vm_image: vs2017-win2016 MacOS: pool_vm_image: macos-10.15@@ -39,10 +42,21 @@ stages: maxParallel: 4 steps:+ - task: NuGetToolInstaller@1+ displayName: 'Install NuGet'+ condition: eq(variables['pool_vm_image'], 'vs2017-win2016')++ - powershell: |+ nuget install python -Prerelease -OutputDi + nuget install python -Prerelease -OutputDirectory "$(Build.BinariesDirectory)" -ExcludeVersion -NonInteractive+ Write-Host "##vso[task.prependpath]$(Build.BinariesDirectory)\python\tools"+ Write-Host "##vso[task.prependpath]$(Build.BinariesDirectory)\python\tools\Scripts"+ condition: and(succeeded(), and(eq(variables['python.version'], '3.9'), eq(variables['pool_vm_image'], 'vs2017-win2016'))) - task: UsePythonVersion@0 inputs: versionSpec: '$(python.version)' architecture: 'x64'+ condition: and(succeeded(), ne(variables['python.version'], '3.9')) - script: python -m pip install tox displayName: 'Install tox'diff --git a/setup.cfg b/setup.cfgindex e2dcebb..0cf9549 100644--- a/setup.cfg+++ b/setup.cfg@@ -29,6 +29,8 @@ testing = pytest-black >= 0.3.7 pytest-cov pytest-mypy+ # workaround for python/mypy#8627: + mypy@git+https://github.com/python/mypy # local(END) path master $ git diff ...FETCH_HEAD path master $ git diff ...FETCH_HEAD path master $ git diff ...FETCH_HEAD path master $ git diff ...FETCH_HEAD path master $ git push path master $ git push path master $ git pull path master $ git pull path master $ git pull gh://jaraco/skeleton path master $ git pull gh://jaraco/skeleton path master $ git pull gh://jaraco/skeleton path master $ git pull gh://jaraco/skeleton path master $ git pull gh://jaraco/skeleton path master $ git pull gh://jaraco/skeleton path master $ git pull gh://jaraco/skeleton path master $ git pull gh://jaraco/skeleton path master $ git pull gh://jaraco/skeleton path master $ git pull gh://jaraco/skeleton path master $ git pull gh://jaraco/skeleton path master $ git pull gh://jaraco/skeleton path master $ git pull gh://jaraco/skeleton path master $ git pull gh://jaraco/skeleton path master $ git pull gh://jaraco/skeleton path master $ git pull gh://jaraco/skeleton path master $ git pull gh://jaraco/skeleton path master $ git pull gh://jaraco/skeleton path master $ git pull gh://jaraco/skeleton path master $ git pull gh://jaraco/skeleton path master $ git pull gh://jaraco/skeleton path master $ git pull gh://jaraco/skeleton Auto-merging setup.cfgAuto-merging azure-pipelines.ymlMerge made by the 'recursive' strategy. azure-pipelines.yml | 16 +++++++++++++++- setup.cfg | 2 ++ 2 files changed, 17 insertions(+), 1 deletion(-)path master $ git pull gh://jaraco/skeleton path master $ git pull gh://jaraco/skeleton path master $ git pull gh://jaraco/skeleton path master $ git pull gh://jaraco/skeleton path master $ git pull gh://jaraco/skeleton path master $ git pull gh://jaraco/skeleton path master $ git push path master $ git push path master $ git push Enumerating objects: 10, done. Counting objects: 50% (5/10)Counting objects: 100% (10/10), done.Delta compression using up to 8 threadsCompressing objects: 100% (4/4), done.Writing objects: 100% (4/4), 512 bytes | 512.00 KiB/s, done.Total 4 (delta 3), reused 0 (delta 0), pack-reused 0remote: Resolving deltas: 100% (3/3), completed with 3 local objects. To https://github.com/jaraco/path ff4d395..bd18026 master -> masterpath master $ - \ No newline at end of file diff --git a/skeleton.md b/skeleton.md index 4544158f..17a94ed7 100644 --- a/skeleton.md +++ b/skeleton.md @@ -40,6 +40,10 @@ The `--allow-unrelated-histories` is necessary because the history from the skel Whenever a change is needed or desired for the general technique for packaging, it can be made in the skeleton project and then merged into each of the derived projects as needed, recommended before each release. As a result, features and best practices for packaging are centrally maintained and readily trickle into a whole suite of packages. This technique lowers the amount of tedious work necessary to create or maintain a project, and coupled with other techniques like continuous integration and deployment, lowers the cost of creating and maintaining refined Python projects to just a few, familiar Git operations. +For example, here's a session of the [path project](https://pypi.org/project/path) pulling non-conflicting changes from the skeleton: + + + Thereafter, the target project can make whatever customizations it deems relevant to the scaffolding. The project may even at some point decide that the divergence is too great to merit renewed merging with the original skeleton. This approach applies maximal guidance while creating minimal constraints. # Features From 6a13942eff3b62a7b4017101c33ae752e69fbc89 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 1 Sep 2020 22:00:43 -0400 Subject: [PATCH 191/323] Add the env var mapping too. --- azure-pipelines.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 2be55e73..fdad0e5d 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -89,6 +89,7 @@ stages: tox -e release env: TWINE_PASSWORD: $(PyPI-token) + GITHUB_TOKEN: $(Github-token) displayName: 'publish to PyPI' condition: contains(variables['Build.SourceBranch'], 'tags') From 15f6272d1eb253940f82737b4f340365ee9879a9 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 4 Sep 2020 18:33:17 -0400 Subject: [PATCH 192/323] Disable pytest-black and pytest-mypy on PyPy. Fixes jaraco/skeleton#22. Ref pytest-dev/pytest#7675. --- pyproject.toml | 8 ++++++++ setup.cfg | 6 ++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 6ee7df23..9b02ee75 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,3 +6,11 @@ build-backend = "setuptools.build_meta" skip-string-normalization = true [tool.setuptools_scm] + +# jaraco/skeleton#22 +[tool.jaraco.pytest.opts.--black] +action = "store_true" + +# jaraco/skeleton#22 +[tool.jaraco.pytest.opts.--mypy] +action = "store_true" diff --git a/setup.cfg b/setup.cfg index e2dcebb1..e9dd1772 100644 --- a/setup.cfg +++ b/setup.cfg @@ -26,9 +26,11 @@ testing = pytest >= 3.5, !=3.7.3 pytest-checkdocs >= 1.2.3 pytest-flake8 - pytest-black >= 0.3.7 + pytest-black >= 0.3.7; python_implementation != "PyPy" pytest-cov - pytest-mypy + pytest-mypy; python_implementation != "PyPy" + # jaraco/skeleton#22 + jaraco.test >= 3 # local From 38207546aea22e38433a316c6ad9bf024de61ef3 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 8 Sep 2020 19:55:53 -0400 Subject: [PATCH 193/323] Bump black and blacken-docs to latest stable versions. --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index fe46b8c5..6639c78c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,10 +1,10 @@ repos: - repo: https://github.com/psf/black - rev: 19.10b0 + rev: stable hooks: - id: black - repo: https://github.com/asottile/blacken-docs - rev: v1.4.0 + rev: v1.8.0 hooks: - id: blacken-docs From 8117892ea5da9969f49756933bcbaa576ee7c5d7 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 12 Sep 2020 14:18:23 -0400 Subject: [PATCH 194/323] Use enabled plugin configuration to enable mypy and black when the plugin is present. Ref jaraco/skeleton#22. --- pyproject.toml | 8 ++++---- pytest.ini | 2 +- setup.cfg | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 9b02ee75..9cd13ba4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,9 +8,9 @@ skip-string-normalization = true [tool.setuptools_scm] # jaraco/skeleton#22 -[tool.jaraco.pytest.opts.--black] -action = "store_true" +[tool.jaraco.pytest.plugins.black] +addopts = "--black" # jaraco/skeleton#22 -[tool.jaraco.pytest.opts.--mypy] -action = "store_true" +[tool.jaraco.pytest.plugins.mypy] +addopts = "--mypy" diff --git a/pytest.ini b/pytest.ini index 381b3271..5ffd7f73 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,6 +1,6 @@ [pytest] norecursedirs=dist build .tox .eggs -addopts=--doctest-modules --flake8 --black --cov --mypy +addopts=--doctest-modules --flake8 --cov doctest_optionflags=ALLOW_UNICODE ELLIPSIS # workaround for warning pytest-dev/pytest#6178 junit_family=xunit2 diff --git a/setup.cfg b/setup.cfg index e9dd1772..eb834fef 100644 --- a/setup.cfg +++ b/setup.cfg @@ -30,7 +30,7 @@ testing = pytest-cov pytest-mypy; python_implementation != "PyPy" # jaraco/skeleton#22 - jaraco.test >= 3 + jaraco.test >= 3.1.1 # local From 678e1a973a0139c0e0ab40395dfbada6c3ea72b9 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 12 Sep 2020 19:02:55 -0400 Subject: [PATCH 195/323] Also enable flake8 and cov when the plugins are present. --- pyproject.toml | 6 ++++++ pytest.ini | 2 +- setup.cfg | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 9cd13ba4..79f088a9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,3 +14,9 @@ addopts = "--black" # jaraco/skeleton#22 [tool.jaraco.pytest.plugins.mypy] addopts = "--mypy" + +[tool.jaraco.pytest.plugins.flake8] +addopts = "--flake8" + +[tool.jaraco.pytest.plugins.cov] +addopts = "--cov" diff --git a/pytest.ini b/pytest.ini index 5ffd7f73..d7f0b115 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,6 +1,6 @@ [pytest] norecursedirs=dist build .tox .eggs -addopts=--doctest-modules --flake8 --cov +addopts=--doctest-modules doctest_optionflags=ALLOW_UNICODE ELLIPSIS # workaround for warning pytest-dev/pytest#6178 junit_family=xunit2 diff --git a/setup.cfg b/setup.cfg index eb834fef..6321ca77 100644 --- a/setup.cfg +++ b/setup.cfg @@ -30,7 +30,7 @@ testing = pytest-cov pytest-mypy; python_implementation != "PyPy" # jaraco/skeleton#22 - jaraco.test >= 3.1.1 + jaraco.test >= 3.2.0 # local From 107f9029fd5807c6579b881db19e11a0488f0675 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 23 Sep 2020 09:47:24 -0400 Subject: [PATCH 196/323] Drop support for sunset Pythons (3.5 and earlier). --- .gitlab-ci.yml | 6 +++--- docs/changelog.rst | 5 +++++ setup.cfg | 6 +----- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a8d1bd28..dd3a5cc3 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -13,11 +13,11 @@ qa: tests: script: - - tox -e py27,py35,py36,py37,py38 + - tox -e py36,py37,py38 coverage: script: - - tox -e py27-cov,py35-cov,py36-cov,py37-cov,py38-cov + - tox -e py36-cov,py37-cov,py38-cov artifacts: paths: - coverage.xml @@ -28,7 +28,7 @@ benchmark: diffcov: script: - - tox -e py27-diffcov,py35-diffcov,py36-diffcov,py37-diffcov,py38-diffcov + - tox -e py36-diffcov,py37-diffcov,py38-diffcov docs: script: diff --git a/docs/changelog.rst b/docs/changelog.rst index b7e93b5d..5f9728c0 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -2,6 +2,11 @@ importlib_metadata NEWS ========================= +v3.0.0 +====== + +* Require Python 3.6 or later. + v2.0.0 ====== diff --git a/setup.cfg b/setup.cfg index d993f5cd..03415b9d 100644 --- a/setup.cfg +++ b/setup.cfg @@ -12,16 +12,12 @@ classifiers = License :: OSI Approved :: Apache Software License Topic :: Software Development :: Libraries Programming Language :: Python :: 3 - Programming Language :: Python :: 2 [options] -python_requires = >=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.* +python_requires = >=3.6 setup_requires = setuptools-scm install_requires = zipp>=0.5 - pathlib2; python_version < '3' - contextlib2; python_version < '3' - configparser>=3.5; python_version < '3' packages = importlib_metadata [mypy] From ab33995a8a78108ef0551629abc91d0ddd34daed Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 23 Sep 2020 09:58:21 -0400 Subject: [PATCH 197/323] Remove compatibility code. --- importlib_metadata/__init__.py | 32 ++++--------- importlib_metadata/_compat.py | 83 +--------------------------------- tests/fixtures.py | 9 +--- tests/test_api.py | 14 +----- tests/test_integration.py | 4 -- tests/test_main.py | 10 +--- tests/test_zip.py | 6 +-- 7 files changed, 17 insertions(+), 141 deletions(-) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index 7031323d..6c3511c7 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals, absolute_import - import io import os import re @@ -7,35 +5,25 @@ import csv import sys import zipp +import email +import pathlib import operator import functools import itertools import posixpath import collections +from configparser import ConfigParser +from contextlib import suppress +from importlib import import_module +from importlib.abc import MetaPathFinder +from itertools import starmap + from ._compat import ( install, NullFinder, - ConfigParser, - suppress, - map, - FileNotFoundError, - IsADirectoryError, - NotADirectoryError, - PermissionError, - pathlib, - ModuleNotFoundError, - MetaPathFinder, - email_message_from_string, PyPy_repr, - unique_ordered, - str, ) -from importlib import import_module -from itertools import starmap - - -__metaclass__ = type __all__ = [ @@ -277,7 +265,7 @@ def metadata(self): # (which points to the egg-info file) attribute unchanged. or self.read_text('') ) - return email_message_from_string(text) + return email.message_from_string(text) @property def version(self): @@ -457,7 +445,7 @@ def zip_children(self): names = zip_path.root.namelist() self.joinpath = zip_path.joinpath - return unique_ordered( + return dict.fromkeys( child.split(posixpath.sep, 1)[0] for child in names ) diff --git a/importlib_metadata/_compat.py b/importlib_metadata/_compat.py index 303d4a22..2b8d7301 100644 --- a/importlib_metadata/_compat.py +++ b/importlib_metadata/_compat.py @@ -1,59 +1,7 @@ -from __future__ import absolute_import, unicode_literals - -import io -import abc import sys -import email - - -if sys.version_info > (3,): # pragma: nocover - import builtins - from configparser import ConfigParser - import contextlib - FileNotFoundError = builtins.FileNotFoundError - IsADirectoryError = builtins.IsADirectoryError - NotADirectoryError = builtins.NotADirectoryError - PermissionError = builtins.PermissionError - map = builtins.map - from itertools import filterfalse -else: # pragma: nocover - from backports.configparser import ConfigParser - from itertools import imap as map # type: ignore - from itertools import ifilterfalse as filterfalse - import contextlib2 as contextlib - FileNotFoundError = IOError, OSError - IsADirectoryError = IOError, OSError - NotADirectoryError = IOError, OSError - PermissionError = IOError, OSError - -str = type('') - -suppress = contextlib.suppress - -if sys.version_info > (3, 5): # pragma: nocover - import pathlib -else: # pragma: nocover - import pathlib2 as pathlib - -try: - ModuleNotFoundError = builtins.FileNotFoundError -except (NameError, AttributeError): # pragma: nocover - ModuleNotFoundError = ImportError # type: ignore - - -if sys.version_info >= (3,): # pragma: nocover - from importlib.abc import MetaPathFinder -else: # pragma: nocover - class MetaPathFinder(object): - __metaclass__ = abc.ABCMeta -__metaclass__ = type -__all__ = [ - 'install', 'NullFinder', 'MetaPathFinder', 'ModuleNotFoundError', - 'pathlib', 'ConfigParser', 'map', 'suppress', 'FileNotFoundError', - 'NotADirectoryError', 'email_message_from_string', - ] +__all__ = ['install', 'NullFinder', 'PyPy_repr'] def install(cls): @@ -104,20 +52,6 @@ def find_spec(*args, **kwargs): find_module = find_spec -def py2_message_from_string(text): # nocoverpy3 - # Work around https://bugs.python.org/issue25545 where - # email.message_from_string cannot handle Unicode on Python 2. - io_buffer = io.StringIO(text) - return email.message_from_file(io_buffer) - - -email_message_from_string = ( - py2_message_from_string - if sys.version_info < (3,) else - email.message_from_string - ) - - class PyPy_repr: """ Override repr for EntryPoint objects on PyPy to avoid __iter__ access. @@ -135,18 +69,3 @@ def make_param(name): if affected: # pragma: nocover __repr__ = __compat_repr__ del affected - - -# from itertools recipes -def unique_everseen(iterable): # pragma: nocover - "List unique elements, preserving order. Remember all elements ever seen." - seen = set() - seen_add = seen.add - - for element in filterfalse(seen.__contains__, iterable): - seen_add(element) - yield element - - -unique_ordered = ( - unique_everseen if sys.version_info < (3, 7) else dict.fromkeys) diff --git a/tests/fixtures.py b/tests/fixtures.py index 506d20ff..64b5e6e6 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -1,17 +1,12 @@ -from __future__ import unicode_literals - import os import sys import shutil +import pathlib import tempfile import textwrap +import contextlib import test.support -from importlib_metadata._compat import pathlib, contextlib - - -__metaclass__ = type - @contextlib.contextmanager def tempdir(): diff --git a/tests/test_api.py b/tests/test_api.py index 9ef1e733..8c708c5b 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -8,16 +8,6 @@ entry_points, files, metadata, requires, version, ) -try: - from collections.abc import Iterator -except ImportError: - from collections import Iterator # noqa: F401 - -try: - from builtins import str as text -except ImportError: - from __builtin__ import unicode as text - class APITests( fixtures.EggInfoPkg, @@ -29,12 +19,12 @@ class APITests( def test_retrieves_version_of_self(self): pkg_version = version('egginfo-pkg') - assert isinstance(pkg_version, text) + assert isinstance(pkg_version, str) assert re.match(self.version_pattern, pkg_version) def test_retrieves_version_of_distinfo_pkg(self): pkg_version = version('distinfo-pkg') - assert isinstance(pkg_version, text) + assert isinstance(pkg_version, str) assert re.match(self.version_pattern, pkg_version) def test_for_name_does_not_exist(self): diff --git a/tests/test_integration.py b/tests/test_integration.py index 377574c4..2db88717 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -1,7 +1,3 @@ -# coding: utf-8 - -from __future__ import unicode_literals - import unittest import packaging.requirements import packaging.version diff --git a/tests/test_main.py b/tests/test_main.py index 847750bc..112be7d3 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -1,6 +1,3 @@ -# coding: utf-8 -from __future__ import unicode_literals - import re import json import pickle @@ -17,18 +14,13 @@ entry_points, metadata, version, ) -try: - from builtins import str as text -except ImportError: - from __builtin__ import unicode as text - class BasicTests(fixtures.DistInfoPkg, unittest.TestCase): version_pattern = r'\d+\.\d+(\.\d)?' def test_retrieves_version_of_self(self): dist = Distribution.from_name('distinfo-pkg') - assert isinstance(dist.version, text) + assert isinstance(dist.version, str) assert re.match(self.version_pattern, dist.version) def test_for_name_does_not_exist(self): diff --git a/tests/test_zip.py b/tests/test_zip.py index 5cebcd02..7cbeab12 100644 --- a/tests/test_zip.py +++ b/tests/test_zip.py @@ -1,6 +1,7 @@ import sys import unittest +from contextlib import ExitStack from importlib_metadata import ( distribution, entry_points, files, PackageNotFoundError, version, distributions, @@ -13,11 +14,6 @@ except (ImportError, AttributeError): import importlib_resources as resources -try: - from contextlib import ExitStack -except ImportError: - from contextlib2 import ExitStack - class TestZip(unittest.TestCase): root = 'tests.data' From ea912cf6598bd882a723518eb5cc01f1c8397094 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 10 Oct 2020 18:09:29 -0400 Subject: [PATCH 198/323] Add workflows for running tests. Ref jaraco/skeleton#24. --- .github/workflows/main.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .github/workflows/main.yml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 00000000..a1897983 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,22 @@ +name: Main + +on: [push, pull_request] + +jobs: + test: + strategy: + matrix: + python: [3.6, 3.7, 3.8] + platform: [ubuntu-latest, macos-latest, windows-latest] + runs-on: ${{ matrix.platform }} + steps: + - uses: actions/checkout@v2 + - name: Setup Python + uses: actions/setup-python@v1 + with: + python-version: ${{ matrix.python }} + - name: Install tox + run: | + python -m pip install tox + - name: Run tests + run: tox From ce34be2f7548dd68f74e5a0fb98f5b796b076900 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 10 Oct 2020 19:55:49 -0400 Subject: [PATCH 199/323] Cut releases from Github Actions instead of Azure Pipelines. Ref jaraco/skeleton#24. --- .github/workflows/main.yml | 22 +++++++++++++++++++++- azure-pipelines.yml | 23 ----------------------- 2 files changed, 21 insertions(+), 24 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a1897983..b3dd81fc 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,4 +1,4 @@ -name: Main +name: Automated Tests on: [push, pull_request] @@ -20,3 +20,23 @@ jobs: python -m pip install tox - name: Run tests run: tox + + release: + needs: test + if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Setup Python + uses: actions/setup-python@v1 + with: + python-version: 3.8 + - name: Install tox + run: | + python -m pip install tox + - name: Release + run: tox -e release + env: + TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/azure-pipelines.yml b/azure-pipelines.yml index fdad0e5d..6d318994 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -70,26 +70,3 @@ stages: 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) - GITHUB_TOKEN: $(Github-token) - displayName: 'publish to PyPI' - - condition: contains(variables['Build.SourceBranch'], 'tags') From dcd7cbda05754898cc9723c9b36e41a92cb3e139 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 11 Oct 2020 06:12:09 -0400 Subject: [PATCH 200/323] Refresh docs to prefer Github Actions to Azure Pipelines. Ref jaraco/skeleton#24. --- skeleton.md | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/skeleton.md b/skeleton.md index 17a94ed7..c6b0cd0c 100644 --- a/skeleton.md +++ b/skeleton.md @@ -56,6 +56,7 @@ The features/techniques employed by the skeleton include: - A README.rst as reStructuredText with some popular badges, but with Read the Docs and AppVeyor badges commented out - A CHANGES.rst file intended for publishing release notes about the project - Use of [Black](https://black.readthedocs.io/en/stable/) for code formatting (disabled on unsupported Python 3.5 and earlier) +- Integrated type checking through [mypy](https://github.com/python/mypy/). ## Packaging Conventions @@ -109,9 +110,20 @@ Relies on a .flake8 file to correct some default behaviors: The project is pre-configured to run tests through multiple CI providers. +### Github Actions + +[Github Actions](https://docs.github.com/en/free-pro-team@latest/actions) are the preferred provider as they provide free, fast, multi-platform services with straightforward configuration. Configured in `.github/workflows`. + +Features include: +- test against multiple Python versions +- run on late (and updated) platform versions +- automated releases of tagged commits + ### Azure Pipelines -[Azure Pipelines](https://azure.microsoft.com/en-us/services/devops/pipelines/) are the preferred provider as they provide free, fast, multi-platform services. See azure-pipelines.yml for more details. +[Azure Pipelines](https://azure.microsoft.com/en-us/services/devops/pipelines/) were adopted for free, fast, multi-platform services. See azure-pipelines.yml for more details. + +Azure Pipelines require many [complicated setup steps](https://github.com/Azure/azure-devops-cli-extension/issues/968) that have not been readily automated. Features include: @@ -133,20 +145,13 @@ A minimal template for running under AppVeyor (Windows) is provided. ### Continuous Deployments -In addition to running tests, an additional deploy stage is configured to automatically release tagged commits to PyPI using [API tokens](https://pypi.org/help/#apitoken). The release process expects an authorized token to be configured with Azure as the `Azure secrets` variable group. This variable group needs to be created only once per organization. For example: +In addition to running tests, an additional publish stage is configured to automatically release tagged commits to PyPI using [API tokens](https://pypi.org/help/#apitoken). The release process expects an authorized token to be configured with each Github project (or org) `PYPI_TOKEN` [secret](https://docs.github.com/en/free-pro-team@latest/actions/reference/encrypted-secrets). Example: ``` -# create a resource group if none exists -az group create --name main --location eastus2 -# create the vault (try different names until something works) -az keyvault create --name secrets007 --resource-group main -# create the secret -az keyvault secret set --vault-name secrets007 --name PyPI-token --value $token +pip-run -q setuptools jaraco.develop -- -m jaraco.develop.add-github-secret PYPI_TOKEN $TOKEN --project org/repo ``` -Then, in the web UI for the project's Pipelines Library, create the `Azure secrets` variable group referencing the key vault name. - -For more details, see [this blog entry](https://blog.jaraco.com/configuring-azure-pipelines-with-secets/). + ## Building Documentation From 5474714ed2622af66674fafe62ae01180c0adf81 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 18 Oct 2020 14:20:21 -0400 Subject: [PATCH 201/323] Use RTD v2 config --- .readthedocs.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.readthedocs.yml b/.readthedocs.yml index 8ae44684..cc698548 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -1,5 +1,6 @@ +version: 2 python: - version: 3 - extra_requirements: - - docs - pip_install: true + install: + - path: . + extra_requirements: + - docs From 4daebe860ef9abdd02198c9be85c306fe6da5b56 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 19 Oct 2020 16:23:20 -0400 Subject: [PATCH 202/323] FS_NONASCII moves to test.support.os_helper in Python 3.10, so add a compatibility shim to support both sides. --- tests/fixtures.py | 5 +++-- tests/py39compat.py | 4 ++++ 2 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 tests/py39compat.py diff --git a/tests/fixtures.py b/tests/fixtures.py index 506d20ff..f206f365 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -5,7 +5,8 @@ import shutil import tempfile import textwrap -import test.support + +from .py39compat import FS_NONASCII from importlib_metadata._compat import pathlib, contextlib @@ -218,7 +219,7 @@ def build_files(file_defs, prefix=pathlib.Path()): class FileBuilder: def unicode_filename(self): - return test.support.FS_NONASCII or \ + return FS_NONASCII or \ self.skip("File system does not support non-ascii.") diff --git a/tests/py39compat.py b/tests/py39compat.py new file mode 100644 index 00000000..a175d4c3 --- /dev/null +++ b/tests/py39compat.py @@ -0,0 +1,4 @@ +try: + from test.support.os_helpers import FS_NONASCII +except ImportError: + from test.support import FS_NONASCII # noqa From 6ad08b8489ac1c1eba37d32525fa7fa8465076c9 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 22 Oct 2020 14:08:14 -0400 Subject: [PATCH 203/323] Test on Python 3.9. Skip 3.7 to avoid creating too many builds. Release on 3.9. --- .github/workflows/main.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b3dd81fc..8c5c232c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -6,13 +6,13 @@ jobs: test: strategy: matrix: - python: [3.6, 3.7, 3.8] + python: [3.6, 3.8, 3.9] platform: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.platform }} steps: - uses: actions/checkout@v2 - name: Setup Python - uses: actions/setup-python@v1 + uses: actions/setup-python@v2 with: python-version: ${{ matrix.python }} - name: Install tox @@ -29,9 +29,9 @@ jobs: steps: - uses: actions/checkout@v2 - name: Setup Python - uses: actions/setup-python@v1 + uses: actions/setup-python@v2 with: - python-version: 3.8 + python-version: 3.9 - name: Install tox run: | python -m pip install tox From c484c781d360eb944abb46c05af8d88a2bfcb634 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 23 Oct 2020 09:33:29 -0400 Subject: [PATCH 204/323] Replace GitLab CI with Github workflow --- .github/workflows/main.yml | 42 ++++++++++++++++++++++++++++++++ .gitlab-ci.yml | 50 -------------------------------------- 2 files changed, 42 insertions(+), 50 deletions(-) create mode 100644 .github/workflows/main.yml delete mode 100644 .gitlab-ci.yml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 00000000..8c5c232c --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,42 @@ +name: Automated Tests + +on: [push, pull_request] + +jobs: + test: + strategy: + matrix: + python: [3.6, 3.8, 3.9] + platform: [ubuntu-latest, macos-latest, windows-latest] + runs-on: ${{ matrix.platform }} + steps: + - uses: actions/checkout@v2 + - name: Setup Python + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python }} + - name: Install tox + run: | + python -m pip install tox + - name: Run tests + run: tox + + release: + needs: test + if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Setup Python + uses: actions/setup-python@v2 + with: + python-version: 3.9 + - name: Install tox + run: | + python -m pip install tox + - name: Release + run: tox -e release + env: + TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index a8d1bd28..00000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,50 +0,0 @@ -image: quay.io/python-devs/ci-image - -stages: - - test - - qa - - docs - - codecov - - deploy - -qa: - script: - - tox -e qa - -tests: - script: - - tox -e py27,py35,py36,py37,py38 - -coverage: - script: - - tox -e py27-cov,py35-cov,py36-cov,py37-cov,py38-cov - artifacts: - paths: - - coverage.xml - -benchmark: - script: - - tox -e perf - -diffcov: - script: - - tox -e py27-diffcov,py35-diffcov,py36-diffcov,py37-diffcov,py38-diffcov - -docs: - script: - - tox -e docs - -codecov: - stage: codecov - dependencies: - - coverage - script: - - codecov - when: on_success - -release: - stage: deploy - only: - - /^v\d+\.\d+(\.\d+)?([abc]\d*)?$/ - script: - - tox -e release From 137026d0964c42d6922192d52060f1a454159802 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 23 Oct 2020 09:40:26 -0400 Subject: [PATCH 205/323] Update URLs. --- README.rst | 8 ++++---- docs/index.rst | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.rst b/README.rst index 2bdd4b8a..6829603b 100644 --- a/README.rst +++ b/README.rst @@ -30,7 +30,7 @@ tools (or other conforming packages). It does not support: Project details =============== - * Project home: https://gitlab.com/python-devs/importlib_metadata - * Report bugs at: https://gitlab.com/python-devs/importlib_metadata/issues - * Code hosting: https://gitlab.com/python-devs/importlib_metadata.git - * Documentation: http://importlib_metadata.readthedocs.io/ + * Project home: https://github.com/python/importlib_metadata + * Report bugs at: https://github.com/python/importlib_metadata/issues + * Code hosting: https://github.com/python/importlib_metadata + * Documentation: https://importlib_metadata.readthedocs.io/ diff --git a/docs/index.rst b/docs/index.rst index 530197cf..b1fe02e2 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -32,10 +32,10 @@ The documentation here includes a general :ref:`usage ` guide. Project details =============== - * Project home: https://gitlab.com/python-devs/importlib_metadata - * Report bugs at: https://gitlab.com/python-devs/importlib_metadata/issues - * Code hosting: https://gitlab.com/python-devs/importlib_metadata.git - * Documentation: http://importlib_metadata.readthedocs.io/ + * Project home: https://github.com/python/importlib_metadata + * Report bugs at: https://github.com/python/importlib_metadata/issues + * Code hosting: https://github.com/python/importlib_metadata + * Documentation: https://importlib_metadata.readthedocs.io/ Indices and tables From 5b73f25b7b895354d92a6ec8073a373ff225b13c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 23 Oct 2020 09:49:18 -0400 Subject: [PATCH 206/323] Add Python versions, specify specific env for tests, add jobs. --- .github/workflows/main.yml | 84 +++++++++++++++++++++++++++++++++++++- 1 file changed, 83 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8c5c232c..172a52c7 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -6,7 +6,7 @@ jobs: test: strategy: matrix: - python: [3.6, 3.8, 3.9] + python: [2.7, 3.5, 3.6, 3.7, 3.8, 3.9] platform: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.platform }} steps: @@ -20,6 +20,88 @@ jobs: python -m pip install tox - name: Run tests run: tox + env: + TOXENV: python + + qa: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Setup Python + uses: actions/setup-python@v2 + with: + python-version: 3.9 + - name: Install tox + run: | + python -m pip install tox + - name: Run checks + run: tox + env: + TOXENV: qa + + coverage: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Setup Python + uses: actions/setup-python@v2 + with: + python-version: 3.9 + - name: Install tox + run: | + python -m pip install tox + - name: Evaluate coverage + run: tox + env: + TOXENV: cov + + benchmark: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Setup Python + uses: actions/setup-python@v2 + with: + python-version: 3.9 + - name: Install tox + run: | + python -m pip install tox + - name: Run benchmarks + run: tox + env: + TOXENV: perf + + diffcov: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Setup Python + uses: actions/setup-python@v2 + with: + python-version: 3.9 + - name: Install tox + run: | + python -m pip install tox + - name: Evaluate coverage + run: tox + env: + TOXENV: diffcov + + docs: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Setup Python + uses: actions/setup-python@v2 + with: + python-version: 3.9 + - name: Install tox + run: | + python -m pip install tox + - name: Build docs + run: tox + env: + TOXENV: docs release: needs: test From ca9ad41aeb98be511d9451706a9e29dd5016df00 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 22 Oct 2020 14:09:25 -0400 Subject: [PATCH 207/323] Drop tests on Travis, Appveyor, and Azure Pipelines. --- .travis.yml | 19 ------------ README.rst | 11 ++----- appveyor.yml | 24 --------------- azure-pipelines.yml | 72 --------------------------------------------- skeleton.md | 26 +--------------- 5 files changed, 4 insertions(+), 148 deletions(-) delete mode 100644 .travis.yml delete mode 100644 appveyor.yml delete mode 100644 azure-pipelines.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index eed7b0a4..00000000 --- a/.travis.yml +++ /dev/null @@ -1,19 +0,0 @@ -dist: bionic -language: python - -python: -- 3.6 -- &latest_py3 3.8 - -cache: pip - -install: -# ensure virtualenv is upgraded to avoid issues like jaraco/path#188 -- pip install -U --upgrade-strategy=eager tox - -before_script: - # Enable IPv6. Ref travis-ci/travis-ci#8361 - - if [ "${TRAVIS_OS_NAME}" == "linux" ]; then - sudo sh -c 'echo 0 > /proc/sys/net/ipv6/conf/all/disable_ipv6'; - fi -script: tox diff --git a/README.rst b/README.rst index 4c7fd554..69554ef8 100644 --- a/README.rst +++ b/README.rst @@ -6,18 +6,13 @@ .. _PyPI link: https://pypi.org/project/skeleton -.. image:: https://dev.azure.com/jaraco/skeleton/_apis/build/status/jaraco.skeleton?branchName=master - :target: https://dev.azure.com/jaraco/skeleton/_build/latest?definitionId=1&branchName=master - -.. image:: https://img.shields.io/travis/jaraco/skeleton/master.svg - :target: https://travis-ci.org/jaraco/skeleton +.. image:: https://github.com/jaraco/skeleton/workflows/Automated%20Tests/badge.svg + :target: https://github.com/jaraco/skeleton/actions?query=workflow%3A%22Automated+Tests%22 + :alt: Automated Tests .. image:: https://img.shields.io/badge/code%20style-black-000000.svg :target: https://github.com/psf/black :alt: Code style: Black -.. .. image:: https://img.shields.io/appveyor/ci/jaraco/skeleton/master.svg -.. :target: https://ci.appveyor.com/project/jaraco/skeleton/branch/master - .. .. image:: https://readthedocs.org/projects/skeleton/badge/?version=latest .. :target: https://skeleton.readthedocs.io/en/latest/?badge=latest diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index c6f46e4f..00000000 --- a/appveyor.yml +++ /dev/null @@ -1,24 +0,0 @@ -environment: - - APPVEYOR: true - - matrix: - - PYTHON: "C:\\Python36-x64" - - PYTHON: "C:\\Python38-x64" - -install: - # symlink python from a directory with a space - - "mklink /d \"C:\\Program Files\\Python\" %PYTHON%" - - "SET PYTHON=\"C:\\Program Files\\Python\"" - - "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%" - -build: off - -cache: - - '%LOCALAPPDATA%\pip\Cache' - -test_script: - - "python -m pip install -U tox virtualenv" - - "tox" - -version: '{build}' diff --git a/azure-pipelines.yml b/azure-pipelines.yml deleted file mode 100644 index 6d318994..00000000 --- a/azure-pipelines.yml +++ /dev/null @@ -1,72 +0,0 @@ -# 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: $(pool_vm_image) - -variables: -- group: Azure secrets -- name: pool_vm_image - value: Ubuntu-18.04 - -stages: -- stage: Test - jobs: - - - job: 'Test' - strategy: - matrix: - Bionic Python 3.6: - python.version: '3.6' - Bionic Python 3.8: - python.version: '3.8' - Windows Python 3.8: - python.version: '3.8' - pool_vm_image: vs2017-win2016 - Windows Python Prerelease: - python.version: '3.9' - pool_vm_image: vs2017-win2016 - MacOS: - python.version: '3.8' - pool_vm_image: macos-10.15 - - maxParallel: 4 - - steps: - - task: NuGetToolInstaller@1 - displayName: 'Install NuGet' - condition: eq(variables['pool_vm_image'], 'vs2017-win2016') - - - powershell: | - nuget install python -Prerelease -OutputDirectory "$(Build.BinariesDirectory)" -ExcludeVersion -NonInteractive - Write-Host "##vso[task.prependpath]$(Build.BinariesDirectory)\python\tools" - Write-Host "##vso[task.prependpath]$(Build.BinariesDirectory)\python\tools\Scripts" - condition: and(succeeded(), and(eq(variables['python.version'], '3.9'), eq(variables['pool_vm_image'], 'vs2017-win2016'))) - - - task: UsePythonVersion@0 - inputs: - versionSpec: '$(python.version)' - architecture: 'x64' - condition: and(succeeded(), ne(variables['python.version'], '3.9')) - - - 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() diff --git a/skeleton.md b/skeleton.md index c6b0cd0c..7c3956c7 100644 --- a/skeleton.md +++ b/skeleton.md @@ -108,7 +108,7 @@ Relies on a .flake8 file to correct some default behaviors: ## Continuous Integration -The project is pre-configured to run tests through multiple CI providers. +The project is pre-configured to run Continuous Integration tests. ### Github Actions @@ -119,30 +119,6 @@ Features include: - run on late (and updated) platform versions - automated releases of tagged commits -### Azure Pipelines - -[Azure Pipelines](https://azure.microsoft.com/en-us/services/devops/pipelines/) were adopted for free, fast, multi-platform services. See azure-pipelines.yml for more details. - -Azure Pipelines require many [complicated setup steps](https://github.com/Azure/azure-devops-cli-extension/issues/968) that have not been readily automated. - -Features include: - -- test against multiple Python versions -- run on Ubuntu Bionic - -### Travis CI - -[Travis CI](https://travis-ci.org) is configured through .travis.yml. Any new project must be enabled either through their web site or with the `travis enable` command. - -Features include: -- test against Python 3 -- run on Ubuntu Bionic -- correct for broken IPv6 - -### AppVeyor - -A minimal template for running under AppVeyor (Windows) is provided. - ### Continuous Deployments In addition to running tests, an additional publish stage is configured to automatically release tagged commits to PyPI using [API tokens](https://pypi.org/help/#apitoken). The release process expects an authorized token to be configured with each Github project (or org) `PYPI_TOKEN` [secret](https://docs.github.com/en/free-pro-team@latest/actions/reference/encrypted-secrets). Example: From 1311cecaad5e176eb7604a045d16dcd6c7353a45 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 23 Oct 2020 10:18:23 -0400 Subject: [PATCH 208/323] use add-github-secrets, which infers the secrets needed from the github workflow. --- skeleton.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/skeleton.md b/skeleton.md index 7c3956c7..ec421c25 100644 --- a/skeleton.md +++ b/skeleton.md @@ -124,11 +124,9 @@ Features include: In addition to running tests, an additional publish stage is configured to automatically release tagged commits to PyPI using [API tokens](https://pypi.org/help/#apitoken). The release process expects an authorized token to be configured with each Github project (or org) `PYPI_TOKEN` [secret](https://docs.github.com/en/free-pro-team@latest/actions/reference/encrypted-secrets). Example: ``` -pip-run -q setuptools jaraco.develop -- -m jaraco.develop.add-github-secret PYPI_TOKEN $TOKEN --project org/repo +pip-run -q jaraco.develop -- -m jaraco.develop.add-github-secrets ``` - - ## Building Documentation Documentation is automatically built by [Read the Docs](https://readthedocs.org) when the project is registered with it, by way of the .readthedocs.yml file. To test the docs build manually, a tox env may be invoked as `tox -e docs`. Both techniques rely on the dependencies declared in `setup.cfg/options.extras_require.docs`. From 747f6833eb1409e01399a56cfa7d3ce384c444c9 Mon Sep 17 00:00:00 2001 From: Julian Berman Date: Sat, 24 Oct 2020 12:54:01 -0400 Subject: [PATCH 209/323] Clarify that this has been added to the stdlib. --- README.rst | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 6829603b..61437464 100644 --- a/README.rst +++ b/README.rst @@ -2,8 +2,13 @@ ``importlib_metadata`` ========================= -``importlib_metadata`` is a library to access the metadata for a Python -package. It is intended to be ported to Python 3.8. +``importlib_metadata`` is a library to access the metadata for a +Python package. + +It has been `added to the Python standard library +`_ as of +Python 3.8, but exists here to support backported usage in earlier +versions. Usage From 7c2164b5857d3ff70171898c66e8fff56647fd61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filipe=20La=C3=ADns?= Date: Sat, 24 Oct 2020 22:36:26 +0100 Subject: [PATCH 210/323] ci: fix diffcov MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Filipe Laíns --- .github/workflows/main.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 172a52c7..b56320fd 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -75,6 +75,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 + with: + fetch-depth: 0 - name: Setup Python uses: actions/setup-python@v2 with: From 8fb96b0d97294e2e331f8862e7805c3275d61690 Mon Sep 17 00:00:00 2001 From: Tzu-ping Chung Date: Mon, 26 Oct 2020 04:11:37 +0800 Subject: [PATCH 211/323] Fix dot handling in dist-info directory name When searching for a matching dist-info directory, try to escape dots in the name segment. This makes functions like distribution() able to accept PEP 503 normalized names to locate a distribution's dist-info directory if the distribution's name contains dots. --- importlib_metadata/__init__.py | 2 +- tests/fixtures.py | 15 +++++++++++++++ tests/test_api.py | 4 ++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index 7031323d..9155ee05 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -473,7 +473,7 @@ def search(self, name): for child in self.children(): n_low = child.lower() if (n_low in name.exact_matches - or n_low.startswith(name.prefix) + or n_low.replace('.', '_').startswith(name.prefix) and n_low.endswith(name.suffixes) # legacy case: or self.is_egg(name) and n_low == 'egg-info'): diff --git a/tests/fixtures.py b/tests/fixtures.py index f206f365..d6961409 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -104,6 +104,21 @@ def setUp(self): build_files(DistInfoPkg.files, self.site_dir) +class DistInfoPkgWithDot(OnSysPath, SiteDir): + files = { + "pkg.dot-1.0.0.dist-info": { + "METADATA": """ + Name: pkg.dot + Version: 1.0.0 + """, + }, + } + + def setUp(self): + super(DistInfoPkgWithDot, self).setUp() + build_files(DistInfoPkgWithDot.files, self.site_dir) + + class DistInfoPkgOffPath(SiteDir): def setUp(self): super(DistInfoPkgOffPath, self).setUp() diff --git a/tests/test_api.py b/tests/test_api.py index 9ef1e733..3f406ee2 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -22,6 +22,7 @@ class APITests( fixtures.EggInfoPkg, fixtures.DistInfoPkg, + fixtures.DistInfoPkgWithDot, fixtures.EggInfoFile, unittest.TestCase): @@ -41,6 +42,9 @@ def test_for_name_does_not_exist(self): with self.assertRaises(PackageNotFoundError): distribution('does-not-exist') + def test_for_name_containing_dot(self): + assert distribution('pkg-dot').metadata['Name'] == 'pkg.dot' + def test_for_top_level(self): self.assertEqual( distribution('egginfo-pkg').read_text('top_level.txt').strip(), From 95ce6f33cc095df6d0a5f239e075a610eefbe262 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 28 Oct 2020 20:58:20 -0400 Subject: [PATCH 212/323] Use inline flags with local scope. --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 41b53557..433d185d 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -14,7 +14,7 @@ url='{package_url}/issues/{issue}', ), dict( - pattern=r'^(?m)((?Pv?\d+(\.\d+){1,2}))\n[-=]+\n', + pattern=r'(?m:^((?Pv?\d+(\.\d+){1,2}))\n[-=]+\n)', with_scm='{text}\n{rev[timestamp]:%d %b %Y}\n', ), dict( From 27f7c53a66c077cea17896496330bab97f1db54b Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 19 Nov 2020 22:02:41 -0500 Subject: [PATCH 213/323] Honor TOX_WORK_DIR if set. Workaround for tox-dev/tox#20. --- tox.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/tox.ini b/tox.ini index 7347d95f..7233b942 100644 --- a/tox.ini +++ b/tox.ini @@ -3,6 +3,7 @@ envlist = python minversion = 3.2 # https://github.com/jaraco/skeleton/issues/6 tox_pip_extensions_ext_venv_update = true +toxworkdir={env:TOX_WORK_DIR:.tox} [testenv] From 216397d6767a28af99b80dd7d7e8e1f77c19a1ec Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 22 Nov 2020 03:17:35 -0500 Subject: [PATCH 214/323] Reword backport philosophy to capture intention on subsequent Python releases. Fixes #254. --- README.rst | 9 +++++---- docs/index.rst | 8 +++++--- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/README.rst b/README.rst index 61437464..5655d9ab 100644 --- a/README.rst +++ b/README.rst @@ -5,10 +5,11 @@ ``importlib_metadata`` is a library to access the metadata for a Python package. -It has been `added to the Python standard library -`_ as of -Python 3.8, but exists here to support backported usage in earlier -versions. +As of Python 3.8, this functionality has been added to the +`Python standard library +`_. +This package supplies backports of that functionality including +improvements added to subsequent Python versions. Usage diff --git a/docs/index.rst b/docs/index.rst index b1fe02e2..57332f5e 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -10,9 +10,11 @@ Python 3.7 and newer (backported as :doc:`importlib_resources ` module for Python 2.7, and 3.4 through 3.7. Users of -Python 3.8 and beyond are encouraged to use the standard library module. +``importlib_metadata`` supplies a backport of +:doc:`importlib.metadata ` as found in +Python 3.8 and later for earlier Python releases. Users of +Python 3.8 and beyond are encouraged to use the standard library module +when possible and fall back to ``importlib_metadata`` when necessary. When imported on Python 3.8 and later, ``importlib_metadata`` replaces the DistributionFinder behavior from the stdlib, but leaves the API in tact. Developers looking for detailed API descriptions should refer to the Python From d027af68f05084c2cabc29164e4546110e116eeb Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 22 Nov 2020 04:38:37 -0500 Subject: [PATCH 215/323] Update changelog. --- docs/changelog.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index b7e93b5d..39653574 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -2,6 +2,13 @@ importlib_metadata NEWS ========================= +v2.1.0 +====== + +* #253: When querying for package metadata, the lookup + now honors + `package normalization rules `_. + v2.0.0 ====== From dbdef6ba3576f55cac49451364854e09511ad33b Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 22 Nov 2020 05:05:35 -0500 Subject: [PATCH 216/323] Capture the target expectation of normalized names. --- importlib_metadata/__init__.py | 11 +++++++++-- tests/fixtures.py | 2 +- tests/test_api.py | 7 +++++-- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index 9155ee05..fc4cf1f8 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -473,7 +473,7 @@ def search(self, name): for child in self.children(): n_low = child.lower() if (n_low in name.exact_matches - or n_low.replace('.', '_').startswith(name.prefix) + or n_low.startswith(name.prefix) and n_low.endswith(name.suffixes) # legacy case: or self.is_egg(name) and n_low == 'egg-info'): @@ -494,12 +494,19 @@ def __init__(self, name): self.name = name if name is None: return - self.normalized = name.lower().replace('-', '_') + self.normalized = self.normalize(name) self.prefix = self.normalized + '-' self.exact_matches = [ self.normalized + suffix for suffix in self.suffixes] self.versionless_egg_name = self.normalized + '.egg' + @staticmethod + def normalize(name): + """ + PEP 503 normalization plus dashes as underscores. + """ + return re.sub(r"[-_.]+", "-", name).lower().replace('-', '_') + @install class MetadataPathFinder(NullFinder, DistributionFinder): diff --git a/tests/fixtures.py b/tests/fixtures.py index d6961409..2ed9bd39 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -106,7 +106,7 @@ def setUp(self): class DistInfoPkgWithDot(OnSysPath, SiteDir): files = { - "pkg.dot-1.0.0.dist-info": { + "pkg_dot-1.0.0.dist-info": { "METADATA": """ Name: pkg.dot Version: 1.0.0 diff --git a/tests/test_api.py b/tests/test_api.py index 3f406ee2..a62e1b88 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -42,8 +42,11 @@ def test_for_name_does_not_exist(self): with self.assertRaises(PackageNotFoundError): distribution('does-not-exist') - def test_for_name_containing_dot(self): - assert distribution('pkg-dot').metadata['Name'] == 'pkg.dot' + def test_name_normalization(self): + names = 'pkg.dot', 'pkg_dot', 'pkg-dot', 'pkg..dot', 'Pkg.Dot' + for name in names: + with self.subTest(name): + assert distribution(name).metadata['Name'] == 'pkg.dot' def test_for_top_level(self): self.assertEqual( From e7d59299d5250c286b038a1131840d76d2007e95 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 22 Nov 2020 05:10:21 -0500 Subject: [PATCH 217/323] Add test to capture legacy expectation where distinfo might appear with a dot in the name. --- tests/fixtures.py | 15 +++++++++++++++ tests/test_api.py | 8 ++++++++ 2 files changed, 23 insertions(+) diff --git a/tests/fixtures.py b/tests/fixtures.py index 2ed9bd39..0d834c65 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -119,6 +119,21 @@ def setUp(self): build_files(DistInfoPkgWithDot.files, self.site_dir) +class DistInfoPkgWithDotLegacy(OnSysPath, SiteDir): + files = { + "pkg.dot-1.0.0.dist-info": { + "METADATA": """ + Name: pkg.dot + Version: 1.0.0 + """, + }, + } + + def setUp(self): + super(DistInfoPkgWithDotLegacy, self).setUp() + build_files(DistInfoPkgWithDotLegacy.files, self.site_dir) + + class DistInfoPkgOffPath(SiteDir): def setUp(self): super(DistInfoPkgOffPath, self).setUp() diff --git a/tests/test_api.py b/tests/test_api.py index a62e1b88..ca6f11ba 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -163,6 +163,14 @@ def test_more_complex_deps_requires_text(self): assert deps == expected +class LegacyDots(fixtures.DistInfoPkgWithDotLegacy, unittest.TestCase): + def test_name_normalization(self): + names = 'pkg.dot', 'pkg_dot', 'pkg-dot', 'pkg..dot', 'Pkg.Dot' + for name in names: + with self.subTest(name): + assert distribution(name).metadata['Name'] == 'pkg.dot' + + class OffSysPathTests(fixtures.DistInfoPkgOffPath, unittest.TestCase): def test_find_distributions_specified_path(self): dists = Distribution.discover(path=[str(self.site_dir)]) From 1028cb9b9e3f1307cc77a2c7ae48e3049d0839b1 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 22 Nov 2020 05:15:36 -0500 Subject: [PATCH 218/323] Support legacy filenames with an unnormalized dot. --- importlib_metadata/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index fc4cf1f8..d5cbc2d0 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -473,7 +473,7 @@ def search(self, name): for child in self.children(): n_low = child.lower() if (n_low in name.exact_matches - or n_low.startswith(name.prefix) + or n_low.replace('.', '_').startswith(name.prefix) and n_low.endswith(name.suffixes) # legacy case: or self.is_egg(name) and n_low == 'egg-info'): From b072a4649dc5a9c4edfa74e9133f7fdff8d2b4b4 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 22 Nov 2020 05:25:35 -0500 Subject: [PATCH 219/323] Restore Python 2 compatibility. --- setup.cfg | 1 + tests/test_api.py | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index d993f5cd..fa10c8d3 100644 --- a/setup.cfg +++ b/setup.cfg @@ -41,6 +41,7 @@ testing = importlib_resources>=1.3; python_version < "3.9" packaging pep517 + unittest2; python_version < "3" docs = sphinx rst.linker diff --git a/tests/test_api.py b/tests/test_api.py index ca6f11ba..efa97996 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -1,6 +1,10 @@ import re import textwrap -import unittest + +try: + import unittest2 as unittest +except ImportError: + import unittest from . import fixtures from importlib_metadata import ( From ab04693589e3d1e494c313fefa2a2802e1c3af5d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 22 Nov 2020 12:48:16 -0500 Subject: [PATCH 220/323] =?UTF-8?q?=F0=9F=91=B9=20Feed=20the=20hobgoblins?= =?UTF-8?q?=20(delint).?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- importlib_metadata/__init__.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index 6c3511c7..206ebbab 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -13,18 +13,18 @@ import posixpath import collections +from ._compat import ( + NullFinder, + PyPy_repr, + install, + ) + from configparser import ConfigParser from contextlib import suppress from importlib import import_module from importlib.abc import MetaPathFinder from itertools import starmap -from ._compat import ( - install, - NullFinder, - PyPy_repr, - ) - __all__ = [ 'Distribution', From 3aa1141a28e15c42628e0efde0878f138208a3cd Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 22 Nov 2020 12:50:54 -0500 Subject: [PATCH 221/323] Require Python 3.6 in tox and github actions --- .github/workflows/main.yml | 2 +- coverage.ini | 1 - tox.ini | 4 +--- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 172a52c7..5845be6e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -6,7 +6,7 @@ jobs: test: strategy: matrix: - python: [2.7, 3.5, 3.6, 3.7, 3.8, 3.9] + python: [3.6, 3.7, 3.8, 3.9] platform: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.platform }} steps: diff --git a/coverage.ini b/coverage.ini index b4d3102f..13b66896 100644 --- a/coverage.ini +++ b/coverage.ini @@ -17,7 +17,6 @@ exclude_lines = raise NotImplementedError raise AssertionError assert\s - nocoverpy${PYV} [paths] source = diff --git a/tox.ini b/tox.ini index 1f0e9757..ea5fb4a8 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = {py27,py35,py36,py37,py38}{,-cov,-diffcov},qa,docs,perf +envlist = {py36,py37,py38}{,-cov,-diffcov},qa,docs,perf skip_missing_interpreters = True minversion = 3.2 # Ensure that a late version of pip is used even on tox-venv. @@ -32,8 +32,6 @@ setenv = cov: COVERAGE_PROCESS_START={[coverage]rcfile} cov: COVERAGE_OPTIONS="-p" cov: COVERAGE_FILE={toxinidir}/.coverage - py27: PYV=2 - py35,py36,py37,py38: PYV=3 # workaround deprecation warnings in pip's vendored packages PYTHONWARNINGS=ignore:Using or importing the ABCs:DeprecationWarning:pip._vendor extras = From 2bc49891396c3debe5a75ad4040d564dfab31cbb Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 22 Nov 2020 13:03:32 -0500 Subject: [PATCH 222/323] Add Only tag or users will complain. --- setup.cfg | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.cfg b/setup.cfg index 03415b9d..b6eea26a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -12,6 +12,7 @@ classifiers = License :: OSI Approved :: Apache Software License Topic :: Software Development :: Libraries Programming Language :: Python :: 3 + Programming Language :: Python :: 3 :: Only [options] python_requires = >=3.6 From f0f202407c5f430f4197e7b548acae1f3558c8f1 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 22 Nov 2020 14:46:09 -0500 Subject: [PATCH 223/323] =?UTF-8?q?=E2=9A=AB=20Fade=20to=20black.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- importlib_metadata/__init__.py | 87 ++++++++++++++++++---------------- importlib_metadata/_compat.py | 12 +++-- prepare/example/setup.py | 5 +- tests/fixtures.py | 19 ++++---- tests/test_api.py | 57 ++++++++++------------ tests/test_integration.py | 9 ++-- tests/test_main.py | 52 ++++++++++---------- tests/test_zip.py | 11 +++-- 8 files changed, 126 insertions(+), 126 deletions(-) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index 206ebbab..fb894023 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -17,7 +17,7 @@ NullFinder, PyPy_repr, install, - ) +) from configparser import ConfigParser from contextlib import suppress @@ -37,7 +37,7 @@ 'metadata', 'requires', 'version', - ] +] class PackageNotFoundError(ModuleNotFoundError): @@ -49,13 +49,13 @@ def __str__(self): @property def name(self): - name, = self.args + (name,) = self.args return name class EntryPoint( - PyPy_repr, - collections.namedtuple('EntryPointBase', 'name value group')): + PyPy_repr, collections.namedtuple('EntryPointBase', 'name value group') +): """An entry point as defined by Python packaging conventions. See `the packaging docs on entry points @@ -67,7 +67,7 @@ class EntryPoint( r'(?P[\w.]+)\s*' r'(:\s*(?P[\w.]+))?\s*' r'(?P\[.*\])?\s*$' - ) + ) """ A regular expression describing the syntax for an entry point, which might look like: @@ -115,7 +115,7 @@ def _from_config(cls, config): cls(name, value, group) for group in config.sections() for name, value in config.items(group) - ] + ] @classmethod def _from_text(cls, text): @@ -139,7 +139,7 @@ def __reduce__(self): return ( self.__class__, (self.name, self.value, self.group), - ) + ) class PackagePath(pathlib.PurePosixPath): @@ -217,9 +217,8 @@ def discover(cls, **kwargs): raise ValueError("cannot accept context and kwargs") context = context or DistributionFinder.Context(**kwargs) return itertools.chain.from_iterable( - resolver(context) - for resolver in cls._discover_resolvers() - ) + resolver(context) for resolver in cls._discover_resolvers() + ) @staticmethod def at(path): @@ -234,20 +233,20 @@ def at(path): def _discover_resolvers(): """Search the meta_path for resolvers.""" declared = ( - getattr(finder, 'find_distributions', None) - for finder in sys.meta_path - ) + getattr(finder, 'find_distributions', None) for finder in sys.meta_path + ) return filter(None, declared) @classmethod def _local(cls, root='.'): from pep517 import build, meta + system = build.compat_system(root) builder = functools.partial( meta.build, source_dir=root, system=system, - ) + ) return PathDistribution(zipp.Path(meta.build_as_zip(builder))) @property @@ -264,7 +263,7 @@ def metadata(self): # effect is to just end up using the PathDistribution's self._path # (which points to the egg-info file) attribute unchanged. or self.read_text('') - ) + ) return email.message_from_string(text) @property @@ -331,9 +330,10 @@ def _deps_from_requires_text(cls, source): section_pairs = cls._read_sections(source.splitlines()) sections = { section: list(map(operator.itemgetter('line'), results)) - for section, results in - itertools.groupby(section_pairs, operator.itemgetter('section')) - } + for section, results in itertools.groupby( + section_pairs, operator.itemgetter('section') + ) + } return cls._convert_egg_info_reqs_to_simple_reqs(sections) @staticmethod @@ -357,6 +357,7 @@ def _convert_egg_info_reqs_to_simple_reqs(sections): requirement. This method converts the former to the latter. See _test_deps_from_requires_text for an example. """ + def make_condition(name): return name and 'extra == "{name}"'.format(name=name) @@ -445,26 +446,27 @@ def zip_children(self): names = zip_path.root.namelist() self.joinpath = zip_path.joinpath - return dict.fromkeys( - child.split(posixpath.sep, 1)[0] - for child in names - ) + return dict.fromkeys(child.split(posixpath.sep, 1)[0] for child in names) def is_egg(self, search): base = self.base return ( base == search.versionless_egg_name or base.startswith(search.prefix) - and base.endswith('.egg')) + and base.endswith('.egg') + ) def search(self, name): for child in self.children(): n_low = child.lower() - if (n_low in name.exact_matches - or n_low.startswith(name.prefix) - and n_low.endswith(name.suffixes) - # legacy case: - or self.is_egg(name) and n_low == 'egg-info'): + if ( + n_low in name.exact_matches + or n_low.startswith(name.prefix) + and n_low.endswith(name.suffixes) + # legacy case: + or self.is_egg(name) + and n_low == 'egg-info' + ): yield self.joinpath(child) @@ -472,6 +474,7 @@ class Prepared: """ A prepared search for metadata on a possibly-named package. """ + normalized = '' prefix = '' suffixes = '.dist-info', '.egg-info' @@ -484,8 +487,7 @@ def __init__(self, name): return self.normalized = name.lower().replace('-', '_') self.prefix = self.normalized + '-' - self.exact_matches = [ - self.normalized + suffix for suffix in self.suffixes] + self.exact_matches = [self.normalized + suffix for suffix in self.suffixes] self.versionless_egg_name = self.normalized + '.egg' @@ -513,9 +515,8 @@ def find_distributions(self, context=DistributionFinder.Context()): def _search_paths(cls, name, paths): """Find metadata directories in paths heuristically.""" return itertools.chain.from_iterable( - path.search(Prepared(name)) - for path in map(FastPath, paths) - ) + path.search(Prepared(name)) for path in map(FastPath, paths) + ) class PathDistribution(Distribution): @@ -528,9 +529,15 @@ def __init__(self, path): self._path = path def read_text(self, filename): - with suppress(FileNotFoundError, IsADirectoryError, KeyError, - NotADirectoryError, PermissionError): + with suppress( + FileNotFoundError, + IsADirectoryError, + KeyError, + NotADirectoryError, + PermissionError, + ): return self._path.joinpath(filename).read_text(encoding='utf-8') + read_text.__doc__ = Distribution.read_text.__doc__ def locate_file(self, path): @@ -578,15 +585,11 @@ def entry_points(): :return: EntryPoint objects for all installed packages. """ - eps = itertools.chain.from_iterable( - dist.entry_points for dist in distributions()) + eps = itertools.chain.from_iterable(dist.entry_points for dist in distributions()) by_group = operator.attrgetter('group') ordered = sorted(eps, key=by_group) grouped = itertools.groupby(ordered, by_group) - return { - group: tuple(eps) - for group, eps in grouped - } + return {group: tuple(eps) for group, eps in grouped} def files(distribution_name): diff --git a/importlib_metadata/_compat.py b/importlib_metadata/_compat.py index 2b8d7301..c1362d53 100644 --- a/importlib_metadata/_compat.py +++ b/importlib_metadata/_compat.py @@ -25,11 +25,12 @@ def disable_stdlib_finder(): See #91 for more background for rationale on this sketchy behavior. """ + def matches(finder): - return ( - getattr(finder, '__module__', None) == '_frozen_importlib_external' - and hasattr(finder, 'find_distributions') - ) + return getattr( + finder, '__module__', None + ) == '_frozen_importlib_external' and hasattr(finder, 'find_distributions') + for finder in filter(matches, sys.meta_path): # pragma: nocover del finder.find_distributions @@ -39,6 +40,7 @@ class NullFinder: A "Finder" (aka "MetaClassFinder") that never finds any modules, but may find distributions. """ + @staticmethod def find_spec(*args, **kwargs): return None @@ -57,12 +59,14 @@ class PyPy_repr: Override repr for EntryPoint objects on PyPy to avoid __iter__ access. Ref #97, #102. """ + affected = hasattr(sys, 'pypy_version_info') def __compat_repr__(self): # pragma: nocover def make_param(name): value = getattr(self, name) return '{name}={value!r}'.format(**locals()) + params = ', '.join(map(make_param, self._fields)) return 'EntryPoint({params})'.format(**locals()) diff --git a/prepare/example/setup.py b/prepare/example/setup.py index 8663ad38..479488a0 100644 --- a/prepare/example/setup.py +++ b/prepare/example/setup.py @@ -1,4 +1,5 @@ from setuptools import setup + setup( name='example', version='21.12', @@ -6,5 +7,5 @@ packages=['example'], entry_points={ 'console_scripts': ['example = example:main', 'Example=example:main'], - }, - ) + }, +) diff --git a/tests/fixtures.py b/tests/fixtures.py index 2112f47a..f48354e7 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -86,13 +86,13 @@ class DistInfoPkg(OnSysPath, SiteDir): [entries] main = mod:main ns:sub = mod:main - """ - }, + """, + }, "mod.py": """ def main(): print("hello world") """, - } + } def setUp(self): super(DistInfoPkg, self).setUp() @@ -129,13 +129,13 @@ class EggInfoPkg(OnSysPath, SiteDir): [test] pytest """, - "top_level.txt": "mod\n" - }, + "top_level.txt": "mod\n", + }, "mod.py": """ def main(): print("hello world") """, - } + } def setUp(self): super(EggInfoPkg, self).setUp() @@ -156,7 +156,7 @@ class EggInfoFile(OnSysPath, SiteDir): Description: UNKNOWN Platform: UNKNOWN """, - } + } def setUp(self): super(EggInfoFile, self).setUp() @@ -169,7 +169,7 @@ class LocalPackage: import setuptools setuptools.setup(name="local-pkg", version="2.0.1") """, - } + } def setUp(self): self.fixtures = contextlib.ExitStack() @@ -214,8 +214,7 @@ def build_files(file_defs, prefix=pathlib.Path()): class FileBuilder: def unicode_filename(self): - return FS_NONASCII or \ - self.skip("File system does not support non-ascii.") + return FS_NONASCII or self.skip("File system does not support non-ascii.") def DALS(str): diff --git a/tests/test_api.py b/tests/test_api.py index 8c708c5b..58a5f3f9 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -4,16 +4,20 @@ from . import fixtures from importlib_metadata import ( - Distribution, PackageNotFoundError, distribution, - entry_points, files, metadata, requires, version, - ) + Distribution, + PackageNotFoundError, + distribution, + entry_points, + files, + metadata, + requires, + version, +) class APITests( - fixtures.EggInfoPkg, - fixtures.DistInfoPkg, - fixtures.EggInfoFile, - unittest.TestCase): + fixtures.EggInfoPkg, fixtures.DistInfoPkg, fixtures.EggInfoFile, unittest.TestCase +): version_pattern = r'\d+\.\d+(\.\d)?' @@ -33,14 +37,13 @@ def test_for_name_does_not_exist(self): def test_for_top_level(self): self.assertEqual( - distribution('egginfo-pkg').read_text('top_level.txt').strip(), - 'mod') + distribution('egginfo-pkg').read_text('top_level.txt').strip(), 'mod' + ) def test_read_text(self): top_level = [ - path for path in files('egginfo-pkg') - if path.name == 'top_level.txt' - ][0] + path for path in files('egginfo-pkg') if path.name == 'top_level.txt' + ][0] self.assertEqual(top_level.read_text(), 'mod\n') def test_entry_points(self): @@ -81,13 +84,8 @@ def test_file_hash_repr(self): # Python 2 assertRegex = self.assertRegexpMatches - util = [ - p for p in files('distinfo-pkg') - if p.name == 'mod.py' - ][0] - assertRegex( - repr(util.hash), - '') + util = [p for p in files('distinfo-pkg') if p.name == 'mod.py'][0] + assertRegex(repr(util.hash), '') def test_files_dist_info(self): self._test_files(files('distinfo-pkg')) @@ -105,10 +103,7 @@ def test_requires_egg_info_file(self): def test_requires_egg_info(self): deps = requires('egginfo-pkg') assert len(deps) == 2 - assert any( - dep == 'wheel >= 1.0; python_version >= "2.7"' - for dep in deps - ) + assert any(dep == 'wheel >= 1.0; python_version >= "2.7"' for dep in deps) def test_requires_dist_info(self): deps = requires('distinfo-pkg') @@ -118,7 +113,8 @@ def test_requires_dist_info(self): assert "pytest; extra == 'test'" in deps def test_more_complex_deps_requires_text(self): - requires = textwrap.dedent(""" + requires = textwrap.dedent( + """ dep1 dep2 @@ -130,7 +126,8 @@ def test_more_complex_deps_requires_text(self): [extra2:python_version < "3"] dep5 - """) + """ + ) deps = sorted(Distribution._deps_from_requires_text(requires)) expected = [ 'dep1', @@ -138,7 +135,7 @@ def test_more_complex_deps_requires_text(self): 'dep3; python_version < "3"', 'dep4; extra == "extra1"', 'dep5; (python_version < "3") and extra == "extra2"', - ] + ] # It's important that the environment marker expression be # wrapped in parentheses to avoid the following 'and' binding more # tightly than some other part of the environment expression. @@ -149,14 +146,10 @@ def test_more_complex_deps_requires_text(self): class OffSysPathTests(fixtures.DistInfoPkgOffPath, unittest.TestCase): def test_find_distributions_specified_path(self): dists = Distribution.discover(path=[str(self.site_dir)]) - assert any( - dist.metadata['Name'] == 'distinfo-pkg' - for dist in dists - ) + assert any(dist.metadata['Name'] == 'distinfo-pkg' for dist in dists) def test_distribution_at_pathlib(self): - """Demonstrate how to load metadata direct from a directory. - """ + """Demonstrate how to load metadata direct from a directory.""" dist_info_path = self.site_dir / 'distinfo_pkg-1.0.0.dist-info' dist = Distribution.at(dist_info_path) assert dist.version == '1.0.0' diff --git a/tests/test_integration.py b/tests/test_integration.py index 2db88717..816a5938 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -7,16 +7,16 @@ Distribution, _compat, version, - ) +) class IntegrationTests(fixtures.DistInfoPkg, unittest.TestCase): - def test_package_spec_installed(self): """ Illustrate the recommended procedure to determine if a specified version of a package is installed. """ + def is_installed(package_spec): req = packaging.requirements.Requirement(package_spec) return version(req.name) in req.specifier @@ -27,19 +27,18 @@ def is_installed(package_spec): class FinderTests(fixtures.Fixtures, unittest.TestCase): - def test_finder_without_module(self): class ModuleFreeFinder(fixtures.NullFinder): """ A finder without an __module__ attribute """ + def __getattribute__(self, name): if name == '__module__': raise AttributeError(name) return super().__getattribute__(name) - self.fixtures.enter_context( - fixtures.install_finder(ModuleFreeFinder())) + self.fixtures.enter_context(fixtures.install_finder(ModuleFreeFinder())) _compat.disable_stdlib_finder() diff --git a/tests/test_main.py b/tests/test_main.py index 112be7d3..8205b024 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -9,10 +9,15 @@ from . import fixtures from importlib_metadata import ( - Distribution, EntryPoint, MetadataPathFinder, - PackageNotFoundError, distributions, - entry_points, metadata, version, - ) + Distribution, + EntryPoint, + MetadataPathFinder, + PackageNotFoundError, + distributions, + entry_points, + metadata, + version, +) class BasicTests(fixtures.DistInfoPkg, unittest.TestCase): @@ -66,12 +71,11 @@ def test_resolve_without_attr(self): name='ep', value='importlib_metadata', group='grp', - ) + ) assert ep.load() is importlib_metadata -class NameNormalizationTests( - fixtures.OnSysPath, fixtures.SiteDir, unittest.TestCase): +class NameNormalizationTests(fixtures.OnSysPath, fixtures.SiteDir, unittest.TestCase): @staticmethod def pkg_with_dashes(site_dir): """ @@ -140,11 +144,15 @@ def pkg_with_non_ascii_description_egg_info(site_dir): metadata_dir.mkdir() metadata = metadata_dir / 'METADATA' with metadata.open('w', encoding='utf-8') as fp: - fp.write(textwrap.dedent(""" + fp.write( + textwrap.dedent( + """ Name: portend pôrˈtend - """).lstrip()) + """ + ).lstrip() + ) return 'portend' def test_metadata_loads(self): @@ -158,24 +166,12 @@ def test_metadata_loads_egg_info(self): assert meta.get_payload() == 'pôrˈtend\n' -class DiscoveryTests(fixtures.EggInfoPkg, - fixtures.DistInfoPkg, - unittest.TestCase): - +class DiscoveryTests(fixtures.EggInfoPkg, fixtures.DistInfoPkg, unittest.TestCase): def test_package_discovery(self): dists = list(distributions()) - assert all( - isinstance(dist, Distribution) - for dist in dists - ) - assert any( - dist.metadata['Name'] == 'egginfo-pkg' - for dist in dists - ) - assert any( - dist.metadata['Name'] == 'distinfo-pkg' - for dist in dists - ) + assert all(isinstance(dist, Distribution) for dist in dists) + assert any(dist.metadata['Name'] == 'egginfo-pkg' for dist in dists) + assert any(dist.metadata['Name'] == 'distinfo-pkg' for dist in dists) def test_invalid_usage(self): with self.assertRaises(ValueError): @@ -263,8 +259,8 @@ def test_attr(self): class FileSystem( - fixtures.OnSysPath, fixtures.SiteDir, fixtures.FileBuilder, - unittest.TestCase): + fixtures.OnSysPath, fixtures.SiteDir, fixtures.FileBuilder, unittest.TestCase +): def test_unicode_dir_on_sys_path(self): """ Ensure a Unicode subdirectory of a directory on sys.path @@ -273,5 +269,5 @@ def test_unicode_dir_on_sys_path(self): fixtures.build_files( {self.unicode_filename(): {}}, prefix=self.site_dir, - ) + ) list(distributions()) diff --git a/tests/test_zip.py b/tests/test_zip.py index 7cbeab12..c6117c6e 100644 --- a/tests/test_zip.py +++ b/tests/test_zip.py @@ -3,12 +3,17 @@ from contextlib import ExitStack from importlib_metadata import ( - distribution, entry_points, files, PackageNotFoundError, - version, distributions, - ) + distribution, + entry_points, + files, + PackageNotFoundError, + version, + distributions, +) try: from importlib import resources + getattr(resources, 'files') getattr(resources, 'as_file') except (ImportError, AttributeError): From cb7e8fc3c26deedfbee8c46a4aa60ea30a352e39 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 22 Nov 2020 14:47:35 -0500 Subject: [PATCH 224/323] =?UTF-8?q?=F0=9F=91=B9=20Feed=20the=20hobgoblins?= =?UTF-8?q?=20(delint).?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_zip.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_zip.py b/tests/test_zip.py index c6117c6e..5f56155d 100644 --- a/tests/test_zip.py +++ b/tests/test_zip.py @@ -3,12 +3,12 @@ from contextlib import ExitStack from importlib_metadata import ( + PackageNotFoundError, distribution, + distributions, entry_points, files, - PackageNotFoundError, version, - distributions, ) try: From 505395a09a93367234b23ca565012fdbee92d964 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 22 Nov 2020 15:18:35 -0500 Subject: [PATCH 225/323] =?UTF-8?q?=F0=9F=A7=8E=E2=80=8D=E2=99=80=EF=B8=8F?= =?UTF-8?q?=20Genuflect=20to=20the=20types.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/fixtures.py | 14 ++++++++++---- tests/test_zip.py | 2 +- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/tests/fixtures.py b/tests/fixtures.py index f48354e7..4facb699 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -7,6 +7,7 @@ import contextlib from .py39compat import FS_NONASCII +from typing import Dict, Union @contextlib.contextmanager @@ -71,8 +72,13 @@ def setUp(self): self.fixtures.enter_context(self.add_sys_path(self.site_dir)) +# Except for python/mypy#731, prefer to define +# FilesDef = Dict[str, Union['FilesDef', str]] +FilesDef = Dict[str, Union[Dict[str, Union[Dict[str, str], str]], str]] + + class DistInfoPkg(OnSysPath, SiteDir): - files = { + files: FilesDef = { "distinfo_pkg-1.0.0.dist-info": { "METADATA": """ Name: distinfo-pkg @@ -106,7 +112,7 @@ def setUp(self): class EggInfoPkg(OnSysPath, SiteDir): - files = { + files: FilesDef = { "egginfo_pkg.egg-info": { "PKG-INFO": """ Name: egginfo-pkg @@ -143,7 +149,7 @@ def setUp(self): class EggInfoFile(OnSysPath, SiteDir): - files = { + files: FilesDef = { "egginfo_file.egg-info": """ Metadata-Version: 1.0 Name: egginfo_file @@ -164,7 +170,7 @@ def setUp(self): class LocalPackage: - files = { + files: FilesDef = { "setup.py": """ import setuptools setuptools.setup(name="local-pkg", version="2.0.1") diff --git a/tests/test_zip.py b/tests/test_zip.py index 5f56155d..67311da2 100644 --- a/tests/test_zip.py +++ b/tests/test_zip.py @@ -17,7 +17,7 @@ getattr(resources, 'files') getattr(resources, 'as_file') except (ImportError, AttributeError): - import importlib_resources as resources + import importlib_resources as resources # type: ignore class TestZip(unittest.TestCase): From a5bbdd4e1e85b25fa6c95ad3bc650306dec3811d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 22 Nov 2020 15:19:43 -0500 Subject: [PATCH 226/323] Remove manifest --- MANIFEST.in | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 MANIFEST.in diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 3fcf6d63..00000000 --- a/MANIFEST.in +++ /dev/null @@ -1,5 +0,0 @@ -include *.py MANIFEST.in LICENSE README.rst -global-include *.txt *.rst *.ini *.cfg *.toml *.whl *.egg -exclude .gitignore -prune build -prune .tox From 96c829ada0b4d3f50d563c24ec440ad704f8eb75 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 22 Nov 2020 15:28:13 -0500 Subject: [PATCH 227/323] Exclude tests and docs from wheel. --- setup.cfg | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/setup.cfg b/setup.cfg index 33c19e8a..5d7d1aed 100644 --- a/setup.cfg +++ b/setup.cfg @@ -21,9 +21,10 @@ install_requires = zipp>=0.5 setup_requires = setuptools_scm[toml] >= 3.4.1 - -[wheel] -universal=1 +[options.packages.find] +exclude = + tests* + docs [options.extras_require] testing = From 03901b2992f6f7bfede4733829ea3aeecd22bda5 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 22 Nov 2020 15:31:11 -0500 Subject: [PATCH 228/323] Exclude prepare.example.setup to avoid spurious mypy errors. --- conftest.py | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 conftest.py diff --git a/conftest.py b/conftest.py new file mode 100644 index 00000000..512c16c8 --- /dev/null +++ b/conftest.py @@ -0,0 +1,4 @@ +collect_ignore = [ + # this module fails mypy tests because 'setup.py' matches './setup.py' + 'prepare/example/setup.py', +] From 67cb1233ccfe5925903f45dc53da83d2935005a8 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 23 Nov 2020 19:45:14 -0500 Subject: [PATCH 229/323] Run tests to exercise coverage --- tox.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/tox.ini b/tox.ini index 89f79c86..a00e8a93 100644 --- a/tox.ini +++ b/tox.ini @@ -23,6 +23,7 @@ commands = [testenv:diffcov] commands = + python -m coverage run pytest {posargs} python -m coverage xml diff-cover coverage.xml --html-report diffcov.html diff-cover coverage.xml --fail-under=100 From d5a5c90be1931860b44d1f06ccd1fa75b4919ac5 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 23 Nov 2020 20:14:10 -0500 Subject: [PATCH 230/323] Bring back perf --- tox.ini | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tox.ini b/tox.ini index a00e8a93..d1e2473e 100644 --- a/tox.ini +++ b/tox.ini @@ -28,6 +28,13 @@ commands = diff-cover coverage.xml --html-report diffcov.html diff-cover coverage.xml --fail-under=100 +[testenv:perf] +use_develop = False +deps = + ipython +commands = + python -m timeit -s 'import importlib_metadata' -- 'importlib_metadata.distribution("ipython")' + [testenv:release] skip_install = True deps = From b29baea2e60bc4b04642051fe5d16f85e0d05c8e Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 23 Nov 2020 20:19:21 -0500 Subject: [PATCH 231/323] Correct syntax for invoking pytest --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index d1e2473e..3fe6584a 100644 --- a/tox.ini +++ b/tox.ini @@ -23,7 +23,7 @@ commands = [testenv:diffcov] commands = - python -m coverage run pytest {posargs} + python -m coverage run -m pytest {posargs} python -m coverage xml diff-cover coverage.xml --html-report diffcov.html diff-cover coverage.xml --fail-under=100 From eabd24d22731d5e7e957467e2435d4b3c2917a7b Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 23 Nov 2020 20:35:00 -0500 Subject: [PATCH 232/323] Restore diffcov --- tox.ini | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index 3fe6584a..34cec06a 100644 --- a/tox.ini +++ b/tox.ini @@ -22,9 +22,10 @@ commands = python -m sphinx . {toxinidir}/build/html [testenv:diffcov] +deps = + diff-cover commands = - python -m coverage run -m pytest {posargs} - python -m coverage xml + pytest {posargs} --cov-report xml diff-cover coverage.xml --html-report diffcov.html diff-cover coverage.xml --fail-under=100 From ed672eed37aefba5ec49e7e9ff7aca235011cdcf Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 23 Nov 2020 20:59:22 -0500 Subject: [PATCH 233/323] Consolidate coverage files --- .coveragerc | 5 ++++- coverage.ini | 23 ----------------------- coverplug.py | 21 --------------------- 3 files changed, 4 insertions(+), 45 deletions(-) delete mode 100644 coverage.ini delete mode 100644 coverplug.py diff --git a/.coveragerc b/.coveragerc index 45823064..8d6674cd 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,5 +1,8 @@ [run] -omit = .tox/* +omit = + */.tox/* + tests/* + prepare/* [report] show_missing = True diff --git a/coverage.ini b/coverage.ini deleted file mode 100644 index 13b66896..00000000 --- a/coverage.ini +++ /dev/null @@ -1,23 +0,0 @@ -[run] -branch = true -parallel = true -omit = - setup* - .tox/*/lib/python* - */tests/*.py - */testing/*.py - /usr/local/* - */mod.py -plugins = - coverplug - -[report] -exclude_lines = - pragma: nocover - raise NotImplementedError - raise AssertionError - assert\s - -[paths] -source = - importlib_metadata diff --git a/coverplug.py b/coverplug.py deleted file mode 100644 index 0b0c7cb5..00000000 --- a/coverplug.py +++ /dev/null @@ -1,21 +0,0 @@ -"""Coverage plugin to add exclude lines based on the Python version.""" - -import sys - -from coverage import CoveragePlugin - - -class MyConfigPlugin(CoveragePlugin): - def configure(self, config): - opt_name = 'report:exclude_lines' - exclude_lines = config.get_option(opt_name) - # Python >= 3.6 has os.PathLike. - if sys.version_info >= (3, 6): - exclude_lines.append('pragma: >=36') - else: - exclude_lines.append('pragma: <=35') - config.set_option(opt_name, exclude_lines) - - -def coverage_init(reg, options): - reg.add_configurer(MyConfigPlugin()) From 9aee90b6bb0690d0a81e0f544e5d44c9101b37b3 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 23 Nov 2020 21:20:46 -0500 Subject: [PATCH 234/323] Fix syntax in changelog --- CHANGES.rst | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index ae18e447..5fe1beb9 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -135,6 +135,7 @@ v1.0.0 0.23 ==== + * Added a compatibility shim to prevent failures on beta releases of Python before the signature changed to accept the "context" parameter on find_distributions. This workaround @@ -143,6 +144,7 @@ v1.0.0 0.22 ==== + * Renamed ``package`` parameter to ``distribution_name`` as `recommended `_ in the following functions: ``distribution``, ``metadata``, @@ -153,6 +155,7 @@ v1.0.0 0.21 ==== + * ``importlib.metadata`` now exposes the ``DistributionFinder`` metaclass and references it in the docs for extending the search algorithm. @@ -170,6 +173,7 @@ v1.0.0 0.20 ==== + * Clarify in the docs that calls to ``.files`` could return ``None`` when the metadata is not present. Closes #69. * Return all requirements and not just the first for dist-info @@ -177,28 +181,34 @@ v1.0.0 0.19 ==== + * Restrain over-eager egg metadata resolution. * Add support for entry points with colons in the name. Closes #75. 0.18 ==== + * Parse entry points case sensitively. Closes #68 * Add a version constraint on the backport configparser package. Closes #66 0.17 ==== + * Fix a permission problem in the tests on Windows. 0.16 ==== + * Don't crash if there exists an EGG-INFO directory on sys.path. 0.15 ==== + * Fix documentation. 0.14 ==== + * Removed ``local_distribution`` function from the API. **This backward-incompatible change removes this behavior summarily**. Projects should remove their @@ -208,21 +218,25 @@ v1.0.0 0.13 ==== + * Update docstrings to match PEP 8. Closes #63. * Merged modules into one module. Closes #62. 0.12 ==== + * Add support for eggs. !65; Closes #19. 0.11 ==== + * Support generic zip files (not just wheels). Closes #59 * Support zip files with multiple distributions in them. Closes #60 * Fully expose the public API in ``importlib_metadata.__all__``. 0.10 ==== + * The ``Distribution`` ABC is now officially part of the public API. Closes #37. * Fixed support for older single file egg-info formats. Closes #43. @@ -231,6 +245,7 @@ v1.0.0 0.9 === + * Fixed issue where entry points without an attribute would raise an Exception. Closes #40. * Removed unused ``name`` parameter from ``entry_points()``. Closes #44. @@ -239,6 +254,7 @@ v1.0.0 0.8 === + * This library can now discover/enumerate all installed packages. **This backward-incompatible change alters the protocol finders must implement to support distribution package discovery.** Closes #24. @@ -267,6 +283,7 @@ v1.0.0 0.7 === + * Fixed issue where packages with dashes in their names would not be discovered. Closes #21. * Distribution lookup is now case-insensitive. Closes #20. @@ -276,6 +293,7 @@ v1.0.0 0.6 === + * Removed ``importlib_metadata.distribution`` function. Now the public interface is primarily the utility functions exposed in ``importlib_metadata.__all__``. Closes #14. @@ -284,6 +302,7 @@ v1.0.0 0.5 === + * Updated README and removed details about Distribution class, now considered private. Closes #15. * Added test suite support for Python 3.4+. @@ -292,21 +311,25 @@ v1.0.0 0.4 === + * Housekeeping. 0.3 === + * Added usage documentation. Closes #8 * Add support for getting metadata from wheels on ``sys.path``. Closes #9 0.2 === + * Added ``importlib_metadata.entry_points()``. Closes #1 * Added ``importlib_metadata.resolve()``. Closes #12 * Add support for Python 2.7. Closes #4 0.1 === + * Initial release. From 4335defc4efcd827ba300917e74e8e449eda4d50 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 30 Nov 2020 21:46:05 -0500 Subject: [PATCH 235/323] Add test capturing expectation where versionless metadata exists but hasn't been updated to the new normalization technique. Ref #261. --- tests/fixtures.py | 6 ++++++ tests/test_api.py | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/tests/fixtures.py b/tests/fixtures.py index 0d834c65..71e17275 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -127,6 +127,12 @@ class DistInfoPkgWithDotLegacy(OnSysPath, SiteDir): Version: 1.0.0 """, }, + "pkg.lot.egg-info": { + "METADATA": """ + Name: pkg.lot + Version: 1.0.0 + """, + }, } def setUp(self): diff --git a/tests/test_api.py b/tests/test_api.py index efa97996..3bec91b6 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -174,6 +174,12 @@ def test_name_normalization(self): with self.subTest(name): assert distribution(name).metadata['Name'] == 'pkg.dot' + def test_name_normalization_versionless_egg_info(self): + names = 'pkg.lot', 'pkg_lot', 'pkg-lot', 'pkg..lot', 'Pkg.Lot' + for name in names: + with self.subTest(name): + assert distribution(name).metadata['Name'] == 'pkg.lot' + class OffSysPathTests(fixtures.DistInfoPkgOffPath, unittest.TestCase): def test_find_distributions_specified_path(self): From 313535ae43536aa7907bb3e38fc165a166d2076c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 30 Nov 2020 22:09:16 -0500 Subject: [PATCH 236/323] Extract method for matching a name in a prepared search. --- importlib_metadata/__init__.py | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index d5cbc2d0..6f0778fb 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -470,14 +470,11 @@ def is_egg(self, search): and base.endswith('.egg')) def search(self, name): - for child in self.children(): - n_low = child.lower() - if (n_low in name.exact_matches - or n_low.replace('.', '_').startswith(name.prefix) - and n_low.endswith(name.suffixes) - # legacy case: - or self.is_egg(name) and n_low == 'egg-info'): - yield self.joinpath(child) + return ( + self.joinpath(child) + for child in self.children() + if name.matches(child, self) + ) class Prepared: @@ -507,6 +504,16 @@ def normalize(name): """ return re.sub(r"[-_.]+", "-", name).lower().replace('-', '_') + def matches(self, name, path): + n_low = name.lower() + return ( + n_low in self.exact_matches + or n_low.replace('.', '_').startswith(self.prefix) + and n_low.endswith(self.suffixes) + # legacy case: + or path.is_egg(self) and n_low == 'egg-info' + ) + @install class MetadataPathFinder(NullFinder, DistributionFinder): From fea6e754a1d60c558ef6a8158e73550befb812e9 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 30 Nov 2020 22:27:34 -0500 Subject: [PATCH 237/323] Move is_egg into prepared. --- importlib_metadata/__init__.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index 6f0778fb..ce330aaf 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -462,18 +462,11 @@ def zip_children(self): for child in names ) - def is_egg(self, search): - base = self.base - return ( - base == search.versionless_egg_name - or base.startswith(search.prefix) - and base.endswith('.egg')) - def search(self, name): return ( self.joinpath(child) for child in self.children() - if name.matches(child, self) + if name.matches(child, self.base) ) @@ -504,16 +497,22 @@ def normalize(name): """ return re.sub(r"[-_.]+", "-", name).lower().replace('-', '_') - def matches(self, name, path): + def matches(self, name, base): n_low = name.lower() return ( n_low in self.exact_matches or n_low.replace('.', '_').startswith(self.prefix) and n_low.endswith(self.suffixes) # legacy case: - or path.is_egg(self) and n_low == 'egg-info' + or self.is_egg(base) and n_low == 'egg-info' ) + def is_egg(self, base): + return ( + base == self.versionless_egg_name + or base.startswith(self.prefix) + and base.endswith('.egg')) + @install class MetadataPathFinder(NullFinder, DistributionFinder): From 595eb5f6671a40e304ad35791689f4a9feec42bb Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 30 Nov 2020 22:47:40 -0500 Subject: [PATCH 238/323] Compare the name against self.normalized. Fixes #261 but also will cause 'lib' to match 'lib_foo'. --- importlib_metadata/__init__.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index ce330aaf..a70a48e5 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -497,14 +497,16 @@ def normalize(name): """ return re.sub(r"[-_.]+", "-", name).lower().replace('-', '_') - def matches(self, name, base): - n_low = name.lower() + def matches(self, cand, base): + low = cand.lower() + pre, ext = os.path.splitext(low) + name, sep, rest = pre.partition('-') return ( - n_low in self.exact_matches - or n_low.replace('.', '_').startswith(self.prefix) - and n_low.endswith(self.suffixes) + low in self.exact_matches + or name.replace('.', '_').startswith(self.normalized) + and ext in self.suffixes # legacy case: - or self.is_egg(base) and n_low == 'egg-info' + or self.is_egg(base) and low == 'egg-info' ) def is_egg(self, base): From 530f5da8dd3a255f1198f29ea0126b8b25e644d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filipe=20La=C3=ADns?= Date: Tue, 1 Dec 2020 13:37:59 +0000 Subject: [PATCH 239/323] importlib_metadata: define protocol for Distribution.metadata MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Filipe Laíns --- importlib_metadata/__init__.py | 23 ++++++++++++++++++++++- importlib_metadata/_compat.py | 13 ++++++++++++- setup.cfg | 1 + 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index c844a4ff..e98c9ef6 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -17,6 +17,7 @@ NullFinder, PyPy_repr, install, + Protocol, ) from configparser import ConfigParser @@ -24,6 +25,7 @@ from importlib import import_module from importlib.abc import MetaPathFinder from itertools import starmap +from typing import Any, List, TypeVar, Union __all__ = [ @@ -166,6 +168,25 @@ def __repr__(self): return ''.format(self.mode, self.value) +_T = TypeVar("_T") + + +class PackageMetadata(Protocol): + def __len__(self) -> int: + ... # pragma: no cover + + def __contains__(self, item: str) -> bool: + ... # pragma: no cover + + def __getitem__(self, key: str) -> str: + ... # pragma: no cover + + def get_all(self, name: str, failobj: _T = ...) -> Union[List[Any], _T]: + """ + Return all values associated with a possibly multi-valued key. + """ + + class Distribution: """A Python distribution package.""" @@ -250,7 +271,7 @@ def _local(cls, root='.'): return PathDistribution(zipp.Path(meta.build_as_zip(builder))) @property - def metadata(self): + def metadata(self) -> PackageMetadata: """Return the parsed metadata for this Distribution. The returned object will have keys that name the various bits of diff --git a/importlib_metadata/_compat.py b/importlib_metadata/_compat.py index c1362d53..01128cce 100644 --- a/importlib_metadata/_compat.py +++ b/importlib_metadata/_compat.py @@ -1,7 +1,18 @@ import sys -__all__ = ['install', 'NullFinder', 'PyPy_repr'] +__all__ = ['install', 'NullFinder', 'PyPy_repr', 'Protocol'] + + +try: + from typing import Protocol +except ImportError: # pragma: no cover + """ + pytest-mypy complains here because: + error: Incompatible import of "Protocol" (imported name has type + "typing_extensions._SpecialForm", local name has type "typing._SpecialForm") + """ + from typing_extensions import Protocol # type: ignore def install(cls): diff --git a/setup.cfg b/setup.cfg index 5d7d1aed..5926f4b6 100644 --- a/setup.cfg +++ b/setup.cfg @@ -19,6 +19,7 @@ include_package_data = true python_requires = >=3.6 install_requires = zipp>=0.5 + typing-extensions>=3.6.4; python_version < "3.8" setup_requires = setuptools_scm[toml] >= 3.4.1 [options.packages.find] From 7bdbb572462cb6626eec5efca5a9899dc5fa6c8b Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 1 Dec 2020 19:19:22 -0500 Subject: [PATCH 240/323] Add test to ensure that a prefix isn't matched --- tests/test_api.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/test_api.py b/tests/test_api.py index 3bec91b6..f36da1c5 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -52,6 +52,13 @@ def test_name_normalization(self): with self.subTest(name): assert distribution(name).metadata['Name'] == 'pkg.dot' + def test_prefix_not_matched(self): + prefixes = 'p', 'pkg', 'pkg.' + for prefix in prefixes: + with self.subTest(prefix): + with self.assertRaises(PackageNotFoundError): + distribution(prefix) + def test_for_top_level(self): self.assertEqual( distribution('egginfo-pkg').read_text('top_level.txt').strip(), From 4098b519652fd0cedc2849929b5e8e64005c92ae Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 1 Dec 2020 19:25:56 -0500 Subject: [PATCH 241/323] Perform exact match on Prepared.normalized, and then add a separate check for an empty self.normalized instead of relying on a degenerate result from startswith. --- importlib_metadata/__init__.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index a70a48e5..adf57a87 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -474,7 +474,7 @@ class Prepared: """ A prepared search for metadata on a possibly-named package. """ - normalized = '' + normalized = None prefix = '' suffixes = '.dist-info', '.egg-info' exact_matches = [''][:0] @@ -503,8 +503,10 @@ def matches(self, cand, base): name, sep, rest = pre.partition('-') return ( low in self.exact_matches - or name.replace('.', '_').startswith(self.normalized) - and ext in self.suffixes + or ext in self.suffixes and ( + not self.normalized or + name.replace('.', '_') == self.normalized + ) # legacy case: or self.is_egg(base) and low == 'egg-info' ) From 6036a37a728e8732b1d4b93860e1825a803a22c7 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 1 Dec 2020 19:54:37 -0500 Subject: [PATCH 242/323] Avoid relying on new-style normalization for legacy eggs. --- importlib_metadata/__init__.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index adf57a87..e296a2c7 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -475,20 +475,16 @@ class Prepared: A prepared search for metadata on a possibly-named package. """ normalized = None - prefix = '' suffixes = '.dist-info', '.egg-info' exact_matches = [''][:0] - versionless_egg_name = '' def __init__(self, name): self.name = name if name is None: return self.normalized = self.normalize(name) - self.prefix = self.normalized + '-' self.exact_matches = [ self.normalized + suffix for suffix in self.suffixes] - self.versionless_egg_name = self.normalized + '.egg' @staticmethod def normalize(name): @@ -497,6 +493,14 @@ def normalize(name): """ return re.sub(r"[-_.]+", "-", name).lower().replace('-', '_') + @staticmethod + def legacy_normalize(name): + """ + Normalize the package name as found in the convention in + older packaging tools versions and specs. + """ + return name.lower().replace('-', '_') + def matches(self, cand, base): low = cand.lower() pre, ext = os.path.splitext(low) @@ -512,9 +516,12 @@ def matches(self, cand, base): ) def is_egg(self, base): + normalized = self.legacy_normalize(self.name or '') + prefix = normalized + '-' if normalized else '' + versionless_egg_name = normalized + '.egg' if self.name else '' return ( - base == self.versionless_egg_name - or base.startswith(self.prefix) + base == versionless_egg_name + or base.startswith(prefix) and base.endswith('.egg')) From 4cb3bd016f4e18f6fb9e564041a7d288a9a5ccf6 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 1 Dec 2020 20:40:21 -0500 Subject: [PATCH 243/323] Update changelog. Ref #261. --- docs/changelog.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index 39653574..532ec971 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -2,6 +2,13 @@ importlib_metadata NEWS ========================= +v2.1.1 +====== + +* #261: Restored compatibility for package discovery for + metadata without version in the name and for legacy + eggs. + v2.1.0 ====== From eb839a69b05f58eff076c1741d5c9ff79e878edd Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 6 Dec 2020 14:20:11 -0500 Subject: [PATCH 244/323] Add test to ensure that EntryPoints are sortable. Fixes #267. --- tests/test_main.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/test_main.py b/tests/test_main.py index 8205b024..74979be8 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -257,6 +257,17 @@ def test_module(self): def test_attr(self): assert self.ep.attr is None + def test_sortable(self): + """ + EntryPoint objects are sortable, but result is undefined. + """ + sorted( + [ + EntryPoint('b', 'val', 'group'), + EntryPoint('a', 'val', 'group'), + ] + ) + class FileSystem( fixtures.OnSysPath, fixtures.SiteDir, fixtures.FileBuilder, unittest.TestCase From 96cc3283a25ee7324cf1238e843fee97454ab6bb Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 6 Dec 2020 14:43:05 -0500 Subject: [PATCH 245/323] Remove Python 2 compatibility code --- importlib_metadata/__init__.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index eec91953..49c49ad7 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -1,4 +1,3 @@ -import io import os import re import abc @@ -122,11 +121,7 @@ def _from_text(cls, text): config = ConfigParser(delimiters='=') # case sensitive: https://stackoverflow.com/q/1611799/812183 config.optionxform = str - try: - config.read_string(text) - except AttributeError: # pragma: nocover - # Python 2 has no read_string - config.readfp(io.StringIO(text)) + config.read_string(text) return EntryPoint._from_config(config) def __iter__(self): From bd5cac5ced706ccd30dfa2a783253f2a9390f373 Mon Sep 17 00:00:00 2001 From: Pedro Algarvio Date: Tue, 17 Nov 2020 07:47:17 +0000 Subject: [PATCH 246/323] Make sure each `EntryPoint` carries it's `Distribution` information --- importlib_metadata/__init__.py | 17 +++++++++++------ tests/test_api.py | 7 +++++++ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index 49c49ad7..35849448 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -53,7 +53,7 @@ def name(self): class EntryPoint( - PyPy_repr, collections.namedtuple('EntryPointBase', 'name value group') + PyPy_repr, collections.namedtuple('EntryPointBase', 'dist name value group') ): """An entry point as defined by Python packaging conventions. @@ -109,20 +109,20 @@ def extras(self): return list(re.finditer(r'\w+', match.group('extras') or '')) @classmethod - def _from_config(cls, config): + def _from_config(cls, dist, config): return [ - cls(name, value, group) + cls(dist, name, value, group) for group in config.sections() for name, value in config.items(group) ] @classmethod - def _from_text(cls, text): + def _from_text(cls, dist, text): config = ConfigParser(delimiters='=') # case sensitive: https://stackoverflow.com/q/1611799/812183 config.optionxform = str config.read_string(text) - return EntryPoint._from_config(config) + return EntryPoint._from_config(dist, config) def __iter__(self): """ @@ -261,6 +261,11 @@ def metadata(self): ) return email.message_from_string(text) + @property + def name(self): + """Return the 'Name' metadata for the distribution package.""" + return self.metadata['Name'] + @property def version(self): """Return the 'Version' metadata for the distribution package.""" @@ -268,7 +273,7 @@ def version(self): @property def entry_points(self): - return EntryPoint._from_text(self.read_text('entry_points.txt')) + return EntryPoint._from_text(self, self.read_text('entry_points.txt')) @property def files(self): diff --git a/tests/test_api.py b/tests/test_api.py index 7aa33a3c..ee4f83e4 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -69,6 +69,13 @@ def test_entry_points(self): self.assertEqual(ep.value, 'mod:main') self.assertEqual(ep.extras, []) + def test_entry_points_distribution(self): + entries = dict(entry_points()['entries']) + for entry in ("main", "ns:sub"): + ep = entries[entry] + self.assertEqual(ep.dist.name, "distinfo-pkg") + self.assertEqual(ep.dist.version, "1.0.0") + def test_metadata_for_this_package(self): md = metadata('egginfo-pkg') assert md['author'] == 'Steven Ma' From ad7c371daf44119b24049b0828d9862c843b2ff3 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 6 Dec 2020 14:42:45 -0500 Subject: [PATCH 247/323] Make 'dist' a single, optional attribute on an EntryPoint. --- importlib_metadata/__init__.py | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index 35849448..240d2c01 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -23,6 +23,7 @@ from importlib import import_module from importlib.abc import MetaPathFinder from itertools import starmap +from typing import Optional __all__ = [ @@ -53,7 +54,7 @@ def name(self): class EntryPoint( - PyPy_repr, collections.namedtuple('EntryPointBase', 'dist name value group') + PyPy_repr, collections.namedtuple('EntryPointBase', 'name value group') ): """An entry point as defined by Python packaging conventions. @@ -83,6 +84,8 @@ class EntryPoint( following the attr, and following any extras. """ + dist: Optional['Distribution'] = None + def load(self): """Load the entry point from its definition. If only a module is indicated by the value, return that module. Otherwise, @@ -109,20 +112,28 @@ def extras(self): return list(re.finditer(r'\w+', match.group('extras') or '')) @classmethod - def _from_config(cls, dist, config): - return [ - cls(dist, name, value, group) + def _from_config(cls, config): + return ( + cls(name, value, group) for group in config.sections() for name, value in config.items(group) - ] + ) @classmethod - def _from_text(cls, dist, text): + def _from_text(cls, text): config = ConfigParser(delimiters='=') # case sensitive: https://stackoverflow.com/q/1611799/812183 config.optionxform = str config.read_string(text) - return EntryPoint._from_config(dist, config) + return cls._from_config(config) + + @classmethod + def _from_text_for(cls, text, dist): + return (ep._for(dist) for ep in cls._from_text(text)) + + def _for(self, dist): + self.dist = dist + return self def __iter__(self): """ @@ -273,7 +284,7 @@ def version(self): @property def entry_points(self): - return EntryPoint._from_text(self, self.read_text('entry_points.txt')) + return list(EntryPoint._from_text_for(self.read_text('entry_points.txt'), self)) @property def files(self): From 84d69614808122e029e4144e1bad5caa881af412 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 6 Dec 2020 14:49:47 -0500 Subject: [PATCH 248/323] The distribution name can be egginfo too --- tests/test_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_api.py b/tests/test_api.py index ee4f83e4..a386551f 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -73,7 +73,7 @@ def test_entry_points_distribution(self): entries = dict(entry_points()['entries']) for entry in ("main", "ns:sub"): ep = entries[entry] - self.assertEqual(ep.dist.name, "distinfo-pkg") + self.assertIn(ep.dist.name, ('distinfo-pkg', 'egginfo-pkg')) self.assertEqual(ep.dist.version, "1.0.0") def test_metadata_for_this_package(self): From c5dbfdb347e2ae8cc105f6cced8b93400c63acc7 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 6 Dec 2020 14:55:13 -0500 Subject: [PATCH 249/323] Update changelog. --- CHANGES.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index fec26796..31647c8e 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,10 @@ +v3.2.0 +====== + +* #265: ``EntryPoint`` objects now expose a ``.dist`` object + referencing the ``Distribution`` when constructed from a + Distribution. + v3.1.1 ====== From c681f6748acaea1bf0b706528c36327cc94a6eed Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 8 Dec 2020 16:29:09 -0500 Subject: [PATCH 250/323] Collapse skeleton history from archive/2020-12 --- .coveragerc | 5 ++ .flake8 | 9 +++ .github/workflows/main.yml | 42 +++++++++++ .pre-commit-config.yaml | 10 +++ .readthedocs.yml | 6 ++ CHANGES.rst | 0 LICENSE | 19 +++++ README.rst | 18 +++++ docs/conf.py | 26 +++++++ docs/history.rst | 8 +++ docs/index.rst | 22 ++++++ mypy.ini | 2 + pyproject.toml | 22 ++++++ pytest.ini | 9 +++ setup.cfg | 45 ++++++++++++ setup.py | 6 ++ skeleton.md | 144 +++++++++++++++++++++++++++++++++++++ tox.ini | 40 +++++++++++ 18 files changed, 433 insertions(+) create mode 100644 .coveragerc create mode 100644 .flake8 create mode 100644 .github/workflows/main.yml create mode 100644 .pre-commit-config.yaml create mode 100644 .readthedocs.yml create mode 100644 CHANGES.rst create mode 100644 LICENSE create mode 100644 README.rst create mode 100644 docs/conf.py create mode 100644 docs/history.rst create mode 100644 docs/index.rst create mode 100644 mypy.ini create mode 100644 pyproject.toml create mode 100644 pytest.ini create mode 100644 setup.cfg create mode 100644 setup.py create mode 100644 skeleton.md create mode 100644 tox.ini diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 00000000..45823064 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,5 @@ +[run] +omit = .tox/* + +[report] +show_missing = True diff --git a/.flake8 b/.flake8 new file mode 100644 index 00000000..790c109f --- /dev/null +++ b/.flake8 @@ -0,0 +1,9 @@ +[flake8] +max-line-length = 88 +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 + # Black creates whitespace before colon + E203 diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 00000000..8c5c232c --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,42 @@ +name: Automated Tests + +on: [push, pull_request] + +jobs: + test: + strategy: + matrix: + python: [3.6, 3.8, 3.9] + platform: [ubuntu-latest, macos-latest, windows-latest] + runs-on: ${{ matrix.platform }} + steps: + - uses: actions/checkout@v2 + - name: Setup Python + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python }} + - name: Install tox + run: | + python -m pip install tox + - name: Run tests + run: tox + + release: + needs: test + if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Setup Python + uses: actions/setup-python@v2 + with: + python-version: 3.9 + - name: Install tox + run: | + python -m pip install tox + - name: Release + run: tox -e release + env: + TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..6639c78c --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,10 @@ +repos: +- repo: https://github.com/psf/black + rev: stable + hooks: + - id: black + +- repo: https://github.com/asottile/blacken-docs + rev: v1.8.0 + hooks: + - id: blacken-docs diff --git a/.readthedocs.yml b/.readthedocs.yml new file mode 100644 index 00000000..cc698548 --- /dev/null +++ b/.readthedocs.yml @@ -0,0 +1,6 @@ +version: 2 +python: + install: + - path: . + extra_requirements: + - docs diff --git a/CHANGES.rst b/CHANGES.rst new file mode 100644 index 00000000..e69de29b diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..353924be --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright Jason R. Coombs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. diff --git a/README.rst b/README.rst new file mode 100644 index 00000000..69554ef8 --- /dev/null +++ b/README.rst @@ -0,0 +1,18 @@ +.. image:: https://img.shields.io/pypi/v/skeleton.svg + :target: `PyPI link`_ + +.. image:: https://img.shields.io/pypi/pyversions/skeleton.svg + :target: `PyPI link`_ + +.. _PyPI link: https://pypi.org/project/skeleton + +.. image:: https://github.com/jaraco/skeleton/workflows/Automated%20Tests/badge.svg + :target: https://github.com/jaraco/skeleton/actions?query=workflow%3A%22Automated+Tests%22 + :alt: Automated Tests + +.. image:: https://img.shields.io/badge/code%20style-black-000000.svg + :target: https://github.com/psf/black + :alt: Code style: Black + +.. .. image:: https://readthedocs.org/projects/skeleton/badge/?version=latest +.. :target: https://skeleton.readthedocs.io/en/latest/?badge=latest diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 00000000..433d185d --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +extensions = ['sphinx.ext.autodoc', 'jaraco.packaging.sphinx', 'rst.linker'] + +master_doc = "index" + +link_files = { + '../CHANGES.rst': dict( + using=dict(GH='https://github.com'), + replace=[ + dict( + pattern=r'(Issue #|\B#)(?P\d+)', + url='{package_url}/issues/{issue}', + ), + dict( + pattern=r'(?m:^((?Pv?\d+(\.\d+){1,2}))\n[-=]+\n)', + with_scm='{text}\n{rev[timestamp]:%d %b %Y}\n', + ), + dict( + pattern=r'PEP[- ](?P\d+)', + url='https://www.python.org/dev/peps/pep-{pep_number:0>4}/', + ), + ], + ) +} diff --git a/docs/history.rst b/docs/history.rst new file mode 100644 index 00000000..8e217503 --- /dev/null +++ b/docs/history.rst @@ -0,0 +1,8 @@ +:tocdepth: 2 + +.. _changes: + +History +******* + +.. include:: ../CHANGES (links).rst diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 00000000..d14131b0 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,22 @@ +Welcome to skeleton documentation! +======================================== + +.. toctree:: + :maxdepth: 1 + + history + + +.. automodule:: skeleton + :members: + :undoc-members: + :show-inheritance: + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + diff --git a/mypy.ini b/mypy.ini new file mode 100644 index 00000000..976ba029 --- /dev/null +++ b/mypy.ini @@ -0,0 +1,2 @@ +[mypy] +ignore_missing_imports = True diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..79f088a9 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,22 @@ +[build-system] +requires = ["setuptools>=42", "wheel", "setuptools_scm[toml]>=3.4.1"] +build-backend = "setuptools.build_meta" + +[tool.black] +skip-string-normalization = true + +[tool.setuptools_scm] + +# jaraco/skeleton#22 +[tool.jaraco.pytest.plugins.black] +addopts = "--black" + +# jaraco/skeleton#22 +[tool.jaraco.pytest.plugins.mypy] +addopts = "--mypy" + +[tool.jaraco.pytest.plugins.flake8] +addopts = "--flake8" + +[tool.jaraco.pytest.plugins.cov] +addopts = "--cov" diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 00000000..d7f0b115 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,9 @@ +[pytest] +norecursedirs=dist build .tox .eggs +addopts=--doctest-modules +doctest_optionflags=ALLOW_UNICODE ELLIPSIS +# workaround for warning pytest-dev/pytest#6178 +junit_family=xunit2 +filterwarnings= + # https://github.com/pytest-dev/pytest/issues/6928 + ignore:direct construction of .*Item has been deprecated:DeprecationWarning diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 00000000..6321ca77 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,45 @@ +[metadata] +license_file = LICENSE +name = skeleton +author = Jason R. Coombs +author_email = jaraco@jaraco.com +description = skeleton +long_description = file:README.rst +url = https://github.com/jaraco/skeleton +classifiers = + Development Status :: 5 - Production/Stable + Intended Audience :: Developers + License :: OSI Approved :: MIT License + Programming Language :: Python :: 3 + Programming Language :: Python :: 3 :: Only + +[options] +packages = find: +include_package_data = true +python_requires = >=3.6 +install_requires = +setup_requires = setuptools_scm[toml] >= 3.4.1 + +[options.extras_require] +testing = + # upstream + pytest >= 3.5, !=3.7.3 + pytest-checkdocs >= 1.2.3 + pytest-flake8 + pytest-black >= 0.3.7; python_implementation != "PyPy" + pytest-cov + pytest-mypy; python_implementation != "PyPy" + # jaraco/skeleton#22 + jaraco.test >= 3.2.0 + + # local + +docs = + # upstream + sphinx + jaraco.packaging >= 3.2 + rst.linker >= 1.9 + + # local + +[options.entry_points] diff --git a/setup.py b/setup.py new file mode 100644 index 00000000..bac24a43 --- /dev/null +++ b/setup.py @@ -0,0 +1,6 @@ +#!/usr/bin/env python + +import setuptools + +if __name__ == "__main__": + setuptools.setup() diff --git a/skeleton.md b/skeleton.md new file mode 100644 index 00000000..ec421c25 --- /dev/null +++ b/skeleton.md @@ -0,0 +1,144 @@ +# Overview + +This project is merged with [skeleton](https://github.com/jaraco/skeleton). What is skeleton? It's the scaffolding of a Python project jaraco [introduced in his blog](https://blog.jaraco.com/a-project-skeleton-for-python-projects/). It seeks to provide a means to re-use techniques and inherit advances when managing projects for distribution. + +## An SCM-Managed Approach + +While maintaining dozens of projects in PyPI, jaraco derives best practices for project distribution and publishes them in the [skeleton repo](https://github.com/jaraco/skeleton), a Git repo capturing the evolution and culmination of these best practices. + +It's intended to be used by a new or existing project to adopt these practices and honed and proven techniques. Adopters are encouraged to use the project directly and maintain a small deviation from the technique, make their own fork for more substantial changes unique to their environment or preferences, or simply adopt the skeleton once and abandon it thereafter. + +The primary advantage to using an SCM for maintaining these techniques is that those tools help facilitate the merge between the template and its adopting projects. + +Another advantage to using an SCM-managed approach is that tools like GitHub recognize that a change in the skeleton is the _same change_ across all projects that merge with that skeleton. Without the ancestry, with a traditional copy/paste approach, a [commit like this](https://github.com/jaraco/skeleton/commit/12eed1326e1bc26ce256e7b3f8cd8d3a5beab2d5) would produce notifications in the upstream project issue for each and every application, but because it's centralized, GitHub provides just the one notification when the change is added to the skeleton. + +# Usage + +## new projects + +To use skeleton for a new project, simply pull the skeleton into a new project: + +``` +$ git init my-new-project +$ cd my-new-project +$ git pull gh://jaraco/skeleton +``` + +Now customize the project to suit your individual project needs. + +## existing projects + +If you have an existing project, you can still incorporate the skeleton by merging it into the codebase. + +``` +$ git merge skeleton --allow-unrelated-histories +``` + +The `--allow-unrelated-histories` is necessary because the history from the skeleton was previously unrelated to the existing codebase. Resolve any merge conflicts and commit to the master, and now the project is based on the shared skeleton. + +## Updating + +Whenever a change is needed or desired for the general technique for packaging, it can be made in the skeleton project and then merged into each of the derived projects as needed, recommended before each release. As a result, features and best practices for packaging are centrally maintained and readily trickle into a whole suite of packages. This technique lowers the amount of tedious work necessary to create or maintain a project, and coupled with other techniques like continuous integration and deployment, lowers the cost of creating and maintaining refined Python projects to just a few, familiar Git operations. + +For example, here's a session of the [path project](https://pypi.org/project/path) pulling non-conflicting changes from the skeleton: + + + +Thereafter, the target project can make whatever customizations it deems relevant to the scaffolding. The project may even at some point decide that the divergence is too great to merit renewed merging with the original skeleton. This approach applies maximal guidance while creating minimal constraints. + +# Features + +The features/techniques employed by the skeleton include: + +- PEP 517/518-based build relying on Setuptools as the build tool +- Setuptools declarative configuration using setup.cfg +- tox for running tests +- A README.rst as reStructuredText with some popular badges, but with Read the Docs and AppVeyor badges commented out +- A CHANGES.rst file intended for publishing release notes about the project +- Use of [Black](https://black.readthedocs.io/en/stable/) for code formatting (disabled on unsupported Python 3.5 and earlier) +- Integrated type checking through [mypy](https://github.com/python/mypy/). + +## Packaging Conventions + +A pyproject.toml is included to enable PEP 517 and PEP 518 compatibility and declares the requirements necessary to build the project on Setuptools (a minimum version compatible with setup.cfg declarative config). + +The setup.cfg file implements the following features: + +- Assumes universal wheel for release +- Advertises the project's LICENSE file (MIT by default) +- Reads the README.rst file into the long description +- Some common Trove classifiers +- Includes all packages discovered in the repo +- Data files in the package are also included (not just Python files) +- Declares the required Python versions +- Declares install requirements (empty by default) +- Declares setup requirements for legacy environments +- Supplies two 'extras': + - testing: requirements for running tests + - docs: requirements for building docs + - these extras split the declaration into "upstream" (requirements as declared by the skeleton) and "local" (those specific to the local project); these markers help avoid merge conflicts +- Placeholder for defining entry points + +Additionally, the setup.py file declares `use_scm_version` which relies on [setuptools_scm](https://pypi.org/project/setuptools_scm) to do two things: + +- derive the project version from SCM tags +- ensure that all files committed to the repo are automatically included in releases + +## Running Tests + +The skeleton assumes the developer has [tox](https://pypi.org/project/tox) installed. The developer is expected to run `tox` to run tests on the current Python version using [pytest](https://pypi.org/project/pytest). + +Other environments (invoked with `tox -e {name}`) supplied include: + + - a `docs` environment to build the documentation + - a `release` environment to publish the package to PyPI + +A pytest.ini is included to define common options around running tests. In particular: + +- rely on default test discovery in the current directory +- avoid recursing into common directories not containing tests +- run doctests on modules and invoke Flake8 tests +- in doctests, allow Unicode literals and regular literals to match, allowing for doctests to run on Python 2 and 3. Also enable ELLIPSES, a default that would be undone by supplying the prior option. +- filters out known warnings caused by libraries/functionality included by the skeleton + +Relies on a .flake8 file to correct some default behaviors: + +- disable mutually incompatible rules W503 and W504 +- support for Black format + +## Continuous Integration + +The project is pre-configured to run Continuous Integration tests. + +### Github Actions + +[Github Actions](https://docs.github.com/en/free-pro-team@latest/actions) are the preferred provider as they provide free, fast, multi-platform services with straightforward configuration. Configured in `.github/workflows`. + +Features include: +- test against multiple Python versions +- run on late (and updated) platform versions +- automated releases of tagged commits + +### Continuous Deployments + +In addition to running tests, an additional publish stage is configured to automatically release tagged commits to PyPI using [API tokens](https://pypi.org/help/#apitoken). The release process expects an authorized token to be configured with each Github project (or org) `PYPI_TOKEN` [secret](https://docs.github.com/en/free-pro-team@latest/actions/reference/encrypted-secrets). Example: + +``` +pip-run -q jaraco.develop -- -m jaraco.develop.add-github-secrets +``` + +## Building Documentation + +Documentation is automatically built by [Read the Docs](https://readthedocs.org) when the project is registered with it, by way of the .readthedocs.yml file. To test the docs build manually, a tox env may be invoked as `tox -e docs`. Both techniques rely on the dependencies declared in `setup.cfg/options.extras_require.docs`. + +In addition to building the Sphinx docs scaffolded in `docs/`, the docs build a `history.html` file that first injects release dates and hyperlinks into the CHANGES.rst before incorporating it as history in the docs. + +## Cutting releases + +By default, tagged commits are released through the continuous integration deploy stage. + +Releases may also be cut manually by invoking the tox environment `release` with the PyPI token set as the TWINE_PASSWORD: + +``` +TWINE_PASSWORD={token} tox -e release +``` diff --git a/tox.ini b/tox.ini new file mode 100644 index 00000000..7233b942 --- /dev/null +++ b/tox.ini @@ -0,0 +1,40 @@ +[tox] +envlist = python +minversion = 3.2 +# https://github.com/jaraco/skeleton/issues/6 +tox_pip_extensions_ext_venv_update = true +toxworkdir={env:TOX_WORK_DIR:.tox} + + +[testenv] +deps = +commands = + pytest {posargs} +usedevelop = True +extras = testing + +[testenv:docs] +extras = + docs + testing +changedir = docs +commands = + python -m sphinx . {toxinidir}/build/html + +[testenv:release] +skip_install = True +deps = + pep517>=0.5 + twine[keyring]>=1.13 + path + jaraco.develop>=7.1 +passenv = + TWINE_PASSWORD + GITHUB_TOKEN +setenv = + TWINE_USERNAME = {env:TWINE_USERNAME:__token__} +commands = + python -c "import path; path.Path('dist').rmtree_p()" + python -m pep517.build . + python -m twine upload dist/* + python -m jaraco.develop.create-github-release From 2667241f44fed464948cbd140bed1b17cfe4e826 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 12 Dec 2020 23:29:03 -0500 Subject: [PATCH 251/323] Update skeleton description to describe the periodic collapse. Fixes #27. --- skeleton.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/skeleton.md b/skeleton.md index ec421c25..dd8ec014 100644 --- a/skeleton.md +++ b/skeleton.md @@ -46,6 +46,26 @@ For example, here's a session of the [path project](https://pypi.org/project/pat Thereafter, the target project can make whatever customizations it deems relevant to the scaffolding. The project may even at some point decide that the divergence is too great to merit renewed merging with the original skeleton. This approach applies maximal guidance while creating minimal constraints. +## Periodic Collapse + +In late 2020, this project [introduced](https://github.com/jaraco/skeleton/issues/27) the idea of a periodic but infrequent (O(years)) collapse of commits to limit the number of commits a new consumer will need to accept to adopt the skeleton. + +The full history of commits is collapsed into a single commit and that commit becomes the new mainline head. + +When one of these collapse operations happens, any project that previously pulled from the skeleton will no longer have a related history with that new main branch. For those projects, the skeleton provides a "handoff" branch that reconciles the two branches. Any project that has previously merged with the skeleton but now gets an error "fatal: refusing to merge unrelated histories" should instead use the handoff branch once to incorporate the new main branch. + +``` +$ git pull https://github.com/jaraco/skeleton 2020-handoff +``` + +This handoff needs to be pulled just once and thereafter the project can pull from the main head. + +The archive and handoff branches from prior collapses are indicate here: + +| refresh | archive | handoff | +|---------|-----------------|--------------| +| 2020-12 | archive/2020-12 | 2020-handoff | + # Features The features/techniques employed by the skeleton include: From ede59665e9279aee376d784b4f7192343ced4274 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 13 Dec 2020 13:36:46 -0500 Subject: [PATCH 252/323] Update changelog. --- CHANGES.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 5fe1beb9..f568843d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,11 @@ +v3.2.0 +====== + +* The object returned by ``metadata()`` now has a + formally-defined protocol called ``PackageMetadata`` + with declared support for the ``.get_all()`` method. + Fixes #126. + v3.1.0 ====== From dcd7bd549b3e8e32d3c1072cc54c66a414570497 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 13 Dec 2020 13:41:08 -0500 Subject: [PATCH 253/323] Update docs and interface spec on metadata function to honor the new protocol. --- docs/using.rst | 5 +++-- importlib_metadata/__init__.py | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/using.rst b/docs/using.rst index 11965147..6b7ec6af 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -108,8 +108,9 @@ Every distribution includes some metadata, which you can extract using the >>> wheel_metadata = metadata('wheel') -The keys of the returned data structure [#f1]_ name the metadata keywords, and -their values are returned unparsed from the distribution metadata:: +The keys of the returned data structure, a ``PackageMetadata``, +name the metadata keywords, and +the values are returned unparsed from the distribution metadata:: >>> wheel_metadata['Requires-Python'] '>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*' diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index e98c9ef6..368108c4 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -589,11 +589,11 @@ def distributions(**kwargs): return Distribution.discover(**kwargs) -def metadata(distribution_name): +def metadata(distribution_name) -> PackageMetadata: """Get the metadata for the named package. :param distribution_name: The name of the distribution package to query. - :return: An email.Message containing the parsed metadata. + :return: A PackageMetadata containing the parsed metadata. """ return Distribution.from_name(distribution_name).metadata From e15726d1fcff949d5d08874a6b1478ee1a2a813a Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 13 Dec 2020 14:03:23 -0500 Subject: [PATCH 254/323] Enable automerge --- .github/workflows/automerge.yml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 .github/workflows/automerge.yml diff --git a/.github/workflows/automerge.yml b/.github/workflows/automerge.yml new file mode 100644 index 00000000..4f70acfb --- /dev/null +++ b/.github/workflows/automerge.yml @@ -0,0 +1,27 @@ +name: automerge +on: + pull_request: + types: + - labeled + - unlabeled + - synchronize + - opened + - edited + - ready_for_review + - reopened + - unlocked + pull_request_review: + types: + - submitted + check_suite: + types: + - completed + status: {} +jobs: + automerge: + runs-on: ubuntu-latest + steps: + - name: automerge + uses: "pascalgn/automerge-action@v0.12.0" + env: + GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" From 877d351e397a909272500fa69828d445051d507d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 13 Dec 2020 14:50:20 -0500 Subject: [PATCH 255/323] Specify main as the default branch. Fixes #269. --- tox.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index 34cec06a..066ba888 100644 --- a/tox.ini +++ b/tox.ini @@ -26,8 +26,8 @@ deps = diff-cover commands = pytest {posargs} --cov-report xml - diff-cover coverage.xml --html-report diffcov.html - diff-cover coverage.xml --fail-under=100 + diff-cover coverage.xml --compare-branch=origin/main --html-report diffcov.html + diff-cover coverage.xml --compare-branch=origin/main --fail-under=100 [testenv:perf] use_develop = False From 150321caba0dc73489b61d6b5bbfbed52b795ae7 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 13 Dec 2020 14:03:23 -0500 Subject: [PATCH 256/323] Enable automerge --- .github/workflows/automerge.yml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 .github/workflows/automerge.yml diff --git a/.github/workflows/automerge.yml b/.github/workflows/automerge.yml new file mode 100644 index 00000000..4f70acfb --- /dev/null +++ b/.github/workflows/automerge.yml @@ -0,0 +1,27 @@ +name: automerge +on: + pull_request: + types: + - labeled + - unlabeled + - synchronize + - opened + - edited + - ready_for_review + - reopened + - unlocked + pull_request_review: + types: + - submitted + check_suite: + types: + - completed + status: {} +jobs: + automerge: + runs-on: ubuntu-latest + steps: + - name: automerge + uses: "pascalgn/automerge-action@v0.12.0" + env: + GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" From a19e27bf8a1156ac3a60dfd274b83508e57223aa Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 13 Dec 2020 21:44:48 -0500 Subject: [PATCH 257/323] Fix syntax in changelog --- CHANGES.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 8caf2473..2549c557 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,7 +1,7 @@ v3.3.0 ====== -* * #265: ``EntryPoint`` objects now expose a ``.dist`` object +* #265: ``EntryPoint`` objects now expose a ``.dist`` object referencing the ``Distribution`` when constructed from a Distribution. From d17c474462960abb768d95a5bc8f24633a46e788 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 16 Dec 2020 20:47:44 -0500 Subject: [PATCH 258/323] Remove footnote --- docs/using.rst | 6 ------ 1 file changed, 6 deletions(-) diff --git a/docs/using.rst b/docs/using.rst index 6b7ec6af..efa40f86 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -253,9 +253,3 @@ a custom finder, return instances of this derived ``Distribution`` in the .. rubric:: Footnotes - -.. [#f1] Technically, the returned distribution metadata object is an - :class:`email.message.EmailMessage` - instance, but this is an implementation detail, and not part of the - stable API. You should only use dictionary-like methods and syntax - to access the metadata contents. From 4b1334629e1cb254a1b6853f045f2615b79ec9e1 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 29 Dec 2020 09:56:52 -0500 Subject: [PATCH 259/323] Automatically inject project name in docs heading. --- docs/index.rst | 4 ++-- setup.cfg | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index d14131b0..325842bb 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,5 +1,5 @@ -Welcome to skeleton documentation! -======================================== +Welcome to |project| documentation! +=================================== .. toctree:: :maxdepth: 1 diff --git a/setup.cfg b/setup.cfg index 6321ca77..4fc095b3 100644 --- a/setup.cfg +++ b/setup.cfg @@ -37,7 +37,7 @@ testing = docs = # upstream sphinx - jaraco.packaging >= 3.2 + jaraco.packaging >= 8.2 rst.linker >= 1.9 # local From cfe99a5a7941f9f8785dd3ec12d1df94e9134411 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 29 Dec 2020 21:27:53 -0500 Subject: [PATCH 260/323] pre-commit autoupdate --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6639c78c..c15ab0c9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,10 +1,10 @@ repos: - repo: https://github.com/psf/black - rev: stable + rev: 20.8b1 hooks: - id: black - repo: https://github.com/asottile/blacken-docs - rev: v1.8.0 + rev: v1.9.1 hooks: - id: blacken-docs From 060d491a9aaacfe457ad365cfd60b611fc9f5bcf Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 30 Dec 2020 10:57:25 -0500 Subject: [PATCH 261/323] Rename 'Automated Tests' to simply 'tests' --- .github/workflows/main.yml | 2 +- README.rst | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8c5c232c..6a8ff006 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,4 +1,4 @@ -name: Automated Tests +name: tests on: [push, pull_request] diff --git a/README.rst b/README.rst index 69554ef8..128e61e4 100644 --- a/README.rst +++ b/README.rst @@ -6,9 +6,9 @@ .. _PyPI link: https://pypi.org/project/skeleton -.. image:: https://github.com/jaraco/skeleton/workflows/Automated%20Tests/badge.svg - :target: https://github.com/jaraco/skeleton/actions?query=workflow%3A%22Automated+Tests%22 - :alt: Automated Tests +.. image:: https://github.com/jaraco/skeleton/workflows/tests/badge.svg + :target: https://github.com/jaraco/skeleton/actions?query=workflow%3A%22tests%22 + :alt: tests .. image:: https://img.shields.io/badge/code%20style-black-000000.svg :target: https://github.com/psf/black From 2b839bad1c2189f4eeb0f74c4a2455ba6687741b Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 30 Dec 2020 12:06:13 -0500 Subject: [PATCH 262/323] Add note about automatic merging of PRs and the requirements and limitations. --- skeleton.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/skeleton.md b/skeleton.md index dd8ec014..0938f892 100644 --- a/skeleton.md +++ b/skeleton.md @@ -138,6 +138,8 @@ Features include: - test against multiple Python versions - run on late (and updated) platform versions - automated releases of tagged commits +- [automatic merging of PRs](https://github.com/marketplace/actions/merge-pull-requests) (requires [protecting branches with required status checks](https://docs.github.com/en/free-pro-team@latest/github/administering-a-repository/enabling-required-status-checks), [not possible through API](https://github.community/t/set-all-status-checks-to-be-required-as-branch-protection-using-the-github-api/119493)) + ### Continuous Deployments From a36768aa363c8f7b54aae00e11f895ff06337532 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 30 Dec 2020 22:20:46 -0500 Subject: [PATCH 263/323] Prefer pytest-enabler to jaraco.test --- pyproject.toml | 10 ++++------ setup.cfg | 3 +-- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 79f088a9..b6ebc0be 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,16 +7,14 @@ skip-string-normalization = true [tool.setuptools_scm] -# jaraco/skeleton#22 -[tool.jaraco.pytest.plugins.black] +[pytest.enabler.black] addopts = "--black" -# jaraco/skeleton#22 -[tool.jaraco.pytest.plugins.mypy] +[pytest.enabler.mypy] addopts = "--mypy" -[tool.jaraco.pytest.plugins.flake8] +[pytest.enabler.flake8] addopts = "--flake8" -[tool.jaraco.pytest.plugins.cov] +[pytest.enabler.cov] addopts = "--cov" diff --git a/setup.cfg b/setup.cfg index 4fc095b3..d5010f70 100644 --- a/setup.cfg +++ b/setup.cfg @@ -29,8 +29,7 @@ testing = pytest-black >= 0.3.7; python_implementation != "PyPy" pytest-cov pytest-mypy; python_implementation != "PyPy" - # jaraco/skeleton#22 - jaraco.test >= 3.2.0 + pytest-enabler # local From 3e876d7906fa6387ab6ac9a9bff8659762363017 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 8 Jan 2021 23:14:07 -0500 Subject: [PATCH 264/323] Enable complexity limit. Fixes jaraco/skeleton#34. --- .flake8 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.flake8 b/.flake8 index 790c109f..59a51f86 100644 --- a/.flake8 +++ b/.flake8 @@ -1,5 +1,9 @@ [flake8] max-line-length = 88 + +# jaraco/skeleton#34 +max-complexity = 10 + ignore = # W503 violates spec https://github.com/PyCQA/pycodestyle/issues/513 W503 From 1731fbebe9f6655a203e6e08ab309f9916ea6f65 Mon Sep 17 00:00:00 2001 From: Sviatoslav Sydorenko Date: Sat, 9 Jan 2021 05:21:12 +0100 Subject: [PATCH 265/323] Replace pep517.build with build (#37) * Replace pep517.build with build Resolves #30 * Prefer simple usage Co-authored-by: Jason R. Coombs --- tox.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index 7233b942..249f97c2 100644 --- a/tox.ini +++ b/tox.ini @@ -24,7 +24,7 @@ commands = [testenv:release] skip_install = True deps = - pep517>=0.5 + build twine[keyring]>=1.13 path jaraco.develop>=7.1 @@ -35,6 +35,6 @@ setenv = TWINE_USERNAME = {env:TWINE_USERNAME:__token__} commands = python -c "import path; path.Path('dist').rmtree_p()" - python -m pep517.build . + python -m build python -m twine upload dist/* python -m jaraco.develop.create-github-release From a9b3f681dea9728235c2a9c68165f7b5cbf350ab Mon Sep 17 00:00:00 2001 From: Sviatoslav Sydorenko Date: Sat, 9 Jan 2021 05:27:24 +0100 Subject: [PATCH 266/323] Use license_files instead of license_file in meta (#35) Singular `license_file` is deprecated since wheel v0.32.0. Refs: * https://wheel.readthedocs.io/en/stable/news.html * https://wheel.readthedocs.io/en/stable/user_guide.html#including-license-files-in-the-generated-wheel-file --- setup.cfg | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index d5010f70..88bc263a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,6 @@ [metadata] -license_file = LICENSE +license_files = + LICENSE name = skeleton author = Jason R. Coombs author_email = jaraco@jaraco.com From 77fbe1df4af6d8f75f44440e89ee1bc249c9f2e0 Mon Sep 17 00:00:00 2001 From: Sviatoslav Sydorenko Date: Sat, 9 Jan 2021 05:37:11 +0100 Subject: [PATCH 267/323] Use `extend-ignore` in flake8 config (#33) * Use `extend-ignore` in flake8 config This option allows to add extra ignored rules to the default list instead of replacing it. The default exclusions are: E121, E123, E126, E226, E24, E704, W503 and W504. Fixes #28. Refs: * https://github.com/pypa/setuptools/pull/2486/files#r541943356 * https://flake8.pycqa.org/en/latest/user/options.html#cmdoption-flake8-extend-ignore * https://flake8.pycqa.org/en/latest/user/options.html#cmdoption-flake8-ignore * Enable complexity limit. Fixes jaraco/skeleton#34. * Replace pep517.build with build (#37) * Replace pep517.build with build Resolves #30 * Prefer simple usage Co-authored-by: Jason R. Coombs * Use license_files instead of license_file in meta (#35) Singular `license_file` is deprecated since wheel v0.32.0. Refs: * https://wheel.readthedocs.io/en/stable/news.html * https://wheel.readthedocs.io/en/stable/user_guide.html#including-license-files-in-the-generated-wheel-file Co-authored-by: Jason R. Coombs --- .flake8 | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.flake8 b/.flake8 index 59a51f86..48b2e246 100644 --- a/.flake8 +++ b/.flake8 @@ -4,10 +4,6 @@ max-line-length = 88 # jaraco/skeleton#34 max-complexity = 10 -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 +extend-ignore = # Black creates whitespace before colon E203 From b017516eca2fca16196510191c15399780d5b57c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 10 Jan 2021 13:15:24 -0500 Subject: [PATCH 268/323] Declare project as typed. Closes #10. --- CHANGES.rst | 5 +++++ importlib_metadata/py.typed | 0 2 files changed, 5 insertions(+) create mode 100644 importlib_metadata/py.typed diff --git a/CHANGES.rst b/CHANGES.rst index 2549c557..9241a3ff 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,8 @@ +v3.4.0 +====== + +* #10: Project now declares itself as being typed. + v3.3.0 ====== diff --git a/importlib_metadata/py.typed b/importlib_metadata/py.typed new file mode 100644 index 00000000..e69de29b From c1af1d6c23938f78cf209062cf92dee5866d7b09 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Sun, 10 Jan 2021 19:27:06 +0100 Subject: [PATCH 269/323] Avoid needlessly reinstantiating Prepareds. --- importlib_metadata/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index cb20875b..10e57c96 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -565,8 +565,9 @@ def find_distributions(self, context=DistributionFinder.Context()): @classmethod def _search_paths(cls, name, paths): """Find metadata directories in paths heuristically.""" + prepared = Prepared(name) return itertools.chain.from_iterable( - path.search(Prepared(name)) for path in map(FastPath, paths) + path.search(prepared) for path in map(FastPath, paths) ) From c9c0909656d84f689b7826dc19d5e50e6bdf7e1d Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Sun, 10 Jan 2021 19:28:03 +0100 Subject: [PATCH 270/323] Switch from the very slow os.path.splitext to str.rpartition. As a consequence, `ext` doesn't include the leading dot anymore, so change `suffixes` accordingly too. --- importlib_metadata/__init__.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index 10e57c96..c39596a2 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -493,7 +493,7 @@ class Prepared: """ normalized = None - suffixes = '.dist-info', '.egg-info' + suffixes = 'dist-info', 'egg-info' exact_matches = [''][:0] def __init__(self, name): @@ -501,7 +501,9 @@ def __init__(self, name): if name is None: return self.normalized = self.normalize(name) - self.exact_matches = [self.normalized + suffix for suffix in self.suffixes] + self.exact_matches = [ + self.normalized + '.' + suffix for suffix in self.suffixes + ] @staticmethod def normalize(name): @@ -520,8 +522,10 @@ def legacy_normalize(name): def matches(self, cand, base): low = cand.lower() - pre, ext = os.path.splitext(low) - name, sep, rest = pre.partition('-') + # rpartition is like os.path.splitext, but much faster. They'd only + # differ if pre is empty, but in that case we don't have a match anyways. + pre, _, ext = low.rpartition('.') + name, _, rest = pre.partition('-') return ( low in self.exact_matches or ext in self.suffixes From 7fc4f119985990190554992532286455bd70590b Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Sun, 10 Jan 2021 19:31:07 +0100 Subject: [PATCH 271/323] Precompute egg_prefix and versionless_egg_name. --- importlib_metadata/__init__.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index c39596a2..1a1c951b 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -495,6 +495,8 @@ class Prepared: normalized = None suffixes = 'dist-info', 'egg-info' exact_matches = [''][:0] + egg_prefix = '' + versionless_egg_name = '' def __init__(self, name): self.name = name @@ -504,6 +506,9 @@ def __init__(self, name): self.exact_matches = [ self.normalized + '.' + suffix for suffix in self.suffixes ] + legacy_normalized = self.legacy_normalize(self.name) + self.egg_prefix = legacy_normalized + '-' + self.versionless_egg_name = legacy_normalized + '.egg' @staticmethod def normalize(name): @@ -536,12 +541,9 @@ def matches(self, cand, base): ) def is_egg(self, base): - normalized = self.legacy_normalize(self.name or '') - prefix = normalized + '-' if normalized else '' - versionless_egg_name = normalized + '.egg' if self.name else '' return ( - base == versionless_egg_name - or base.startswith(prefix) + base == self.versionless_egg_name + or base.startswith(self.egg_prefix) and base.endswith('.egg') ) From c769ba8fae245265bdb3f173a41e0e8c4f2a2d4b Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 10 Jan 2021 14:00:59 -0500 Subject: [PATCH 272/323] Add recommended workaround for resolver issues (pypa/pip#9143). Fixes #273. --- tox.ini | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tox.ini b/tox.ini index d5de1d16..11f52d7a 100644 --- a/tox.ini +++ b/tox.ini @@ -12,6 +12,10 @@ commands = pytest {posargs} usedevelop = True extras = testing +setenv = + # workaround pypa/pip#9143 + PIP_USE_DEPRECATED=legacy-resolver + [testenv:docs] extras = From 4b469526c375dffd5c63b8e3795d59fb42d8c0bf Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 10 Jan 2021 14:38:25 -0500 Subject: [PATCH 273/323] Tweak comment --- importlib_metadata/__init__.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index 1a1c951b..079cc0f9 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -527,8 +527,7 @@ def legacy_normalize(name): def matches(self, cand, base): low = cand.lower() - # rpartition is like os.path.splitext, but much faster. They'd only - # differ if pre is empty, but in that case we don't have a match anyways. + # rpartition is faster than splitext and suitable for this purpose. pre, _, ext = low.rpartition('.') name, _, rest = pre.partition('-') return ( From cbb4583c1c325089a1b6d36f85067b6cf033ba03 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 10 Jan 2021 14:40:37 -0500 Subject: [PATCH 274/323] Update changelog. --- CHANGES.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 9241a3ff..79bb5dc1 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,8 @@ v3.4.0 ====== * #10: Project now declares itself as being typed. +* #272: Additional performance enhancements to distribution + discovery. v3.3.0 ====== From c066e68a3bc78c12ff826626474348dd32491477 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 10 Jan 2021 16:07:35 -0500 Subject: [PATCH 275/323] Add test ensuring MetadataPathFinder._search_paths is available for PyPA projects. Fixes #111. --- CHANGES.rst | 3 +++ tests/test_integration.py | 12 ++++++++++++ 2 files changed, 15 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 79bb5dc1..57901f23 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,9 @@ v3.4.0 * #10: Project now declares itself as being typed. * #272: Additional performance enhancements to distribution discovery. +* #111: For PyPA projects, add test ensuring that + ``MetadataPathFinder._search_paths`` honors the needed + interface. Method is still private. v3.3.0 ====== diff --git a/tests/test_integration.py b/tests/test_integration.py index 816a5938..11835135 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -5,6 +5,7 @@ from . import fixtures from importlib_metadata import ( Distribution, + MetadataPathFinder, _compat, version, ) @@ -47,3 +48,14 @@ def test_find_local(self): dist = Distribution._local() assert dist.metadata['Name'] == 'local-pkg' assert dist.version == '2.0.1' + + +class DistSearch(unittest.TestCase): + def test_search_dist_dirs(self): + """ + Pip needs the _search_paths interface to locate + distribution metadata dirs. Protect it for PyPA + use-cases (only). Ref python/importlib_metadata#111. + """ + res = MetadataPathFinder._search_paths('any-name', []) + assert list(res) == [] From bde4e157b4ac1924a953897c92ed0b2638cdb229 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 15 Jan 2021 12:34:13 -0500 Subject: [PATCH 276/323] Fix typo in docs. Ref bpo-42728. --- docs/using.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/using.rst b/docs/using.rst index efa40f86..00409867 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -200,9 +200,9 @@ Thus, an alternative way to get the version number is through the There are all kinds of additional metadata available on the ``Distribution`` instance:: - >>> d.metadata['Requires-Python'] + >>> dist.metadata['Requires-Python'] '>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*' - >>> d.metadata['License'] + >>> dist.metadata['License'] 'MIT' The full set of available metadata is not described here. See :pep:`566` From 9950845a8b3f35843057d3708ed75ef30dd62659 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 21 Jan 2021 09:34:23 -0500 Subject: [PATCH 277/323] Add doctest illustrating the usage of constructing dict from EntryPoints. --- importlib_metadata/__init__.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index 079cc0f9..fac3063b 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -138,7 +138,11 @@ def _for(self, dist): def __iter__(self): """ - Supply iter so one may construct dicts of EntryPoints easily. + Supply iter so one may construct dicts of EntryPoints by name. + + >>> eps = [EntryPoint('a', 'b', 'c'), EntryPoint('d', 'e', 'f')] + >>> dict(eps)['a'] + EntryPoint(name='a', value='b', group='c') """ return iter((self.name, self)) From a49252b513cf1e25e3885c60f046269ea293f13e Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 22 Jan 2021 21:21:37 -0500 Subject: [PATCH 278/323] Add explicit interfaces for loaded entrypoints, resolvable first by group then by name. --- importlib_metadata/__init__.py | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index fac3063b..7ca3e938 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -153,6 +153,27 @@ def __reduce__(self): ) +class EntryPoints(tuple): + """ + A collection of EntryPoint objects, retrievable by name. + """ + + def __getitem__(self, name) -> EntryPoint: + try: + return next(ep for ep in self if ep.name == name) + except Exception: + raise KeyError(name) + + +class GroupedEntryPoints(tuple): + """ + A collection of EntryPoint objects, retrievable by group. + """ + + def __getitem__(self, group) -> EntryPoints: + return EntryPoints(ep for ep in self if ep.group == group) + + class PackagePath(pathlib.PurePosixPath): """A reference to a path in a package""" @@ -308,7 +329,8 @@ def version(self): @property def entry_points(self): - return list(EntryPoint._from_text_for(self.read_text('entry_points.txt'), self)) + eps = EntryPoint._from_text_for(self.read_text('entry_points.txt'), self) + return GroupedEntryPoints(eps) @property def files(self): @@ -647,10 +669,7 @@ def entry_points(): :return: EntryPoint objects for all installed packages. """ eps = itertools.chain.from_iterable(dist.entry_points for dist in distributions()) - by_group = operator.attrgetter('group') - ordered = sorted(eps, key=by_group) - grouped = itertools.groupby(ordered, by_group) - return {group: tuple(eps) for group, eps in grouped} + return GroupedEntryPoints(eps) def files(distribution_name): From 6596183f79a3973698c4b2b825b12682ac6e7d96 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 22 Jan 2021 21:29:26 -0500 Subject: [PATCH 279/323] Update tests to use new preferred API. --- tests/test_api.py | 12 +++++++++--- tests/test_main.py | 6 ++---- tests/test_zip.py | 2 +- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/tests/test_api.py b/tests/test_api.py index a386551f..e6a5adeb 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -64,18 +64,24 @@ def test_read_text(self): self.assertEqual(top_level.read_text(), 'mod\n') def test_entry_points(self): - entries = dict(entry_points()['entries']) - ep = entries['main'] + ep = entry_points()['entries']['main'] self.assertEqual(ep.value, 'mod:main') self.assertEqual(ep.extras, []) def test_entry_points_distribution(self): - entries = dict(entry_points()['entries']) + entries = entry_points()['entries'] for entry in ("main", "ns:sub"): ep = entries[entry] self.assertIn(ep.dist.name, ('distinfo-pkg', 'egginfo-pkg')) self.assertEqual(ep.dist.version, "1.0.0") + def test_entry_points_missing_name(self): + with self.assertRaises(KeyError): + entry_points()['entries']['missing'] + + def test_entry_points_missing_group(self): + assert entry_points()['missing'] == () + def test_metadata_for_this_package(self): md = metadata('egginfo-pkg') assert md['author'] == 'Steven Ma' diff --git a/tests/test_main.py b/tests/test_main.py index 74979be8..566262f6 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -57,13 +57,11 @@ def test_import_nonexistent_module(self): importlib.import_module('does_not_exist') def test_resolve(self): - entries = dict(entry_points()['entries']) - ep = entries['main'] + ep = entry_points()['entries']['main'] self.assertEqual(ep.load().__name__, "main") def test_entrypoint_with_colon_in_name(self): - entries = dict(entry_points()['entries']) - ep = entries['ns:sub'] + ep = entry_points()['entries']['ns:sub'] self.assertEqual(ep.value, 'mod:main') def test_resolve_without_attr(self): diff --git a/tests/test_zip.py b/tests/test_zip.py index 67311da2..5a63465f 100644 --- a/tests/test_zip.py +++ b/tests/test_zip.py @@ -45,7 +45,7 @@ def test_zip_version_does_not_match(self): version('definitely-not-installed') def test_zip_entry_points(self): - scripts = dict(entry_points()['console_scripts']) + scripts = entry_points()['console_scripts'] entry_point = scripts['example'] self.assertEqual(entry_point.value, 'example:main') entry_point = scripts['Example'] From b5081fa78358a9bb7c47eedfe084b3f96c024c63 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 22 Jan 2021 21:33:37 -0500 Subject: [PATCH 280/323] Capture the legacy expectation. --- tests/test_api.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/test_api.py b/tests/test_api.py index e6a5adeb..c3e8b532 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -82,6 +82,16 @@ def test_entry_points_missing_name(self): def test_entry_points_missing_group(self): assert entry_points()['missing'] == () + def test_entry_points_dict_construction(self): + """ + Prior versions of entry_points() returned simple lists and + allowed casting those lists into maps by name using ``dict()``. + Capture this now deprecated use-case. + """ + eps = dict(entry_points()['entries']) + assert 'main' in eps + assert eps['main'] == entry_points()['entries']['main'] + def test_metadata_for_this_package(self): md = metadata('egginfo-pkg') assert md['author'] == 'Steven Ma' From 99dd2242ab9c8c0b4a082e135f6bbda11c19540a Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 22 Jan 2021 21:50:02 -0500 Subject: [PATCH 281/323] Deprecate dict construction from EntryPoint items. --- importlib_metadata/__init__.py | 10 ++++++---- tests/test_api.py | 10 +++++++++- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index 7ca3e938..681743dc 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -7,6 +7,7 @@ import email import pathlib import operator +import warnings import functools import itertools import posixpath @@ -139,11 +140,12 @@ def _for(self, dist): def __iter__(self): """ Supply iter so one may construct dicts of EntryPoints by name. - - >>> eps = [EntryPoint('a', 'b', 'c'), EntryPoint('d', 'e', 'f')] - >>> dict(eps)['a'] - EntryPoint(name='a', value='b', group='c') """ + msg = ( + "Construction of dict of EntryPoints is deprecated in " + "favor of EntryPoints." + ) + warnings.warn(msg, DeprecationWarning) return iter((self.name, self)) def __reduce__(self): diff --git a/tests/test_api.py b/tests/test_api.py index c3e8b532..8ce2e468 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -1,6 +1,7 @@ import re import textwrap import unittest +import warnings from . import fixtures from importlib_metadata import ( @@ -88,10 +89,17 @@ def test_entry_points_dict_construction(self): allowed casting those lists into maps by name using ``dict()``. Capture this now deprecated use-case. """ - eps = dict(entry_points()['entries']) + with warnings.catch_warnings(record=True) as caught: + eps = dict(entry_points()['entries']) + assert 'main' in eps assert eps['main'] == entry_points()['entries']['main'] + # check warning + expected = next(iter(caught)) + assert expected.category is DeprecationWarning + assert "Construction of dict of EntryPoints is deprecated" in str(expected) + def test_metadata_for_this_package(self): md = metadata('egginfo-pkg') assert md['author'] == 'Steven Ma' From 2eeb629021d7218516f5ee43de51b8d93d32828a Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 22 Jan 2021 21:54:11 -0500 Subject: [PATCH 282/323] Suppress warning in test_json_dump. --- tests/test_main.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_main.py b/tests/test_main.py index 566262f6..b778572c 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -3,6 +3,7 @@ import pickle import textwrap import unittest +import warnings import importlib import importlib_metadata import pyfakefs.fake_filesystem_unittest as ffs @@ -247,7 +248,8 @@ def test_json_dump(self): json should not expect to be able to dump an EntryPoint """ with self.assertRaises(Exception): - json.dumps(self.ep) + with warnings.catch_warnings(record=True): + json.dumps(self.ep) def test_module(self): assert self.ep.module == 'value' From eedd810b90083fd5a2b0bb398478527011c474eb Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 22 Jan 2021 22:06:29 -0500 Subject: [PATCH 283/323] Add 'groups' and 'names' to EntryPoints collections. --- importlib_metadata/__init__.py | 8 ++++++++ tests/test_api.py | 6 +++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index 681743dc..60967cd4 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -166,6 +166,10 @@ def __getitem__(self, name) -> EntryPoint: except Exception: raise KeyError(name) + @property + def names(self): + return set(ep.name for ep in self) + class GroupedEntryPoints(tuple): """ @@ -175,6 +179,10 @@ class GroupedEntryPoints(tuple): def __getitem__(self, group) -> EntryPoints: return EntryPoints(ep for ep in self if ep.group == group) + @property + def groups(self): + return set(ep.group for ep in self) + class PackagePath(pathlib.PurePosixPath): """A reference to a path in a package""" diff --git a/tests/test_api.py b/tests/test_api.py index 8ce2e468..7672556a 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -65,7 +65,11 @@ def test_read_text(self): self.assertEqual(top_level.read_text(), 'mod\n') def test_entry_points(self): - ep = entry_points()['entries']['main'] + eps = entry_points() + assert 'entries' in eps.groups + entries = eps['entries'] + assert 'main' in entries.names + ep = entries['main'] self.assertEqual(ep.value, 'mod:main') self.assertEqual(ep.extras, []) From 720362fe25dd0211432784de02dd483b53ee7be8 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 22 Jan 2021 22:10:34 -0500 Subject: [PATCH 284/323] Update documentation on EntryPoints to reflect the new, preferred accessors. --- docs/using.rst | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/using.rst b/docs/using.rst index 00409867..534c1dea 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -67,7 +67,7 @@ This package provides the following functionality via its public API. Entry points ------------ -The ``entry_points()`` function returns a dictionary of all entry points, +The ``entry_points()`` function returns a sequence of all entry points, keyed by group. Entry points are represented by ``EntryPoint`` instances; each ``EntryPoint`` has a ``.name``, ``.group``, and ``.value`` attributes and a ``.load()`` method to resolve the value. There are also ``.module``, @@ -75,10 +75,12 @@ a ``.load()`` method to resolve the value. There are also ``.module``, ``.value`` attribute:: >>> eps = entry_points() - >>> list(eps) + >>> sorted(eps.groups) ['console_scripts', 'distutils.commands', 'distutils.setup_keywords', 'egg_info.writers', 'setuptools.installation'] >>> scripts = eps['console_scripts'] - >>> wheel = [ep for ep in scripts if ep.name == 'wheel'][0] + >>> 'wheel' in scripts.names + True + >>> wheel = scripts['wheel'] >>> wheel EntryPoint(name='wheel', value='wheel.cli:main', group='console_scripts') >>> wheel.module From 28adeb8f84ac3e5052ea24c93b4fa3816e1fe4e6 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 22 Jan 2021 22:13:52 -0500 Subject: [PATCH 285/323] Update changelog. --- CHANGES.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 57901f23..02900674 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,16 @@ +v3.5.0 +====== + +* ``entry_points()`` now returns an ``GroupedEntryPoints`` + object, a tuple of all entry points but with a convenience + property ``groups`` and ``__getitem__`` accessor. Further, + accessing a group returns an ``EntryPoints`` object, + another tuple of entry points in the group, accessible by + name. Construction of entry points using + ``dict([EntryPoint, ...])`` is now deprecated and raises + an appropriate DeprecationWarning and will be removed in + a future version. + v3.4.0 ====== From 342a94ba5c373b01f3c5b827da1d4bd76ff2b04f Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 22 Jan 2021 22:54:23 -0500 Subject: [PATCH 286/323] Add deprecated .get to GroupedEntryPoints and test to capture expectation. --- importlib_metadata/__init__.py | 8 ++++++++ tests/test_api.py | 11 +++++++++++ 2 files changed, 19 insertions(+) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index 60967cd4..edcf2691 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -183,6 +183,14 @@ def __getitem__(self, group) -> EntryPoints: def groups(self): return set(ep.group for ep in self) + def get(self, group, default=None): + """ + For backward compatibility, supply .get + """ + msg = "GroupedEntryPoints.get is deprecated. Just use __getitem__." + warnings.warn(msg, DeprecationWarning) + return self[group] or default + class PackagePath(pathlib.PurePosixPath): """A reference to a path in a package""" diff --git a/tests/test_api.py b/tests/test_api.py index 7672556a..dc0c7870 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -104,6 +104,17 @@ def test_entry_points_dict_construction(self): assert expected.category is DeprecationWarning assert "Construction of dict of EntryPoints is deprecated" in str(expected) + def test_entry_points_groups_get(self): + """ + Prior versions of entry_points() returned a dict. Ensure + that callers using '.get()' are supported but warned to + migrate. + """ + with warnings.catch_warnings(record=True): + entry_points().get('missing', 'default') == 'default' + entry_points().get('entries', 'default') == entry_points()['entries'] + entry_points().get('missing', ()) == entry_points()['missing'] + def test_metadata_for_this_package(self): md = metadata('egginfo-pkg') assert md['author'] == 'Steven Ma' From 4e2603f5bd0b6c86a365e98e3e891fdcdfa9bc13 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Sat, 23 Jan 2021 23:30:33 +0100 Subject: [PATCH 287/323] Separately profile cached and uncached lookup performance. ... in preparation of adding a lookup cache. --- tox.ini | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 11f52d7a..f1586632 100644 --- a/tox.ini +++ b/tox.ini @@ -38,7 +38,10 @@ use_develop = False deps = ipython commands = - python -m timeit -s 'import importlib_metadata' -- 'importlib_metadata.distribution("ipython")' + python -c 'print("Cached lookup performance")' + python -m timeit -s 'import importlib_metadata; importlib_metadata.distribution("ipython")' -- 'importlib_metadata.distribution("ipython")' + python -c 'print("Uncached lookup performance")' + python -m timeit -s 'import importlib, importlib_metadata' -- 'importlib.invalidate_caches(); importlib_metadata.distribution("ipython")' [testenv:release] skip_install = True From 35bc40379340e2b23d56a57c5c782ffe93a53396 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 26 Jan 2021 15:38:00 -0500 Subject: [PATCH 288/323] When resolving entry points globally, only expose entry points for unique distributions. Fixes #280. --- .coveragerc | 1 + importlib_metadata/__init__.py | 7 ++++++- importlib_metadata/_itertools.py | 19 +++++++++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 importlib_metadata/_itertools.py diff --git a/.coveragerc b/.coveragerc index 66d32472..e91bbd6b 100644 --- a/.coveragerc +++ b/.coveragerc @@ -3,6 +3,7 @@ omit = */.tox/* tests/* prepare/* + */_itertools.py [report] show_missing = True diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index fac3063b..4c420a55 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -19,6 +19,8 @@ Protocol, ) +from ._itertools import unique_everseen + from configparser import ConfigParser from contextlib import suppress from importlib import import_module @@ -646,7 +648,10 @@ def entry_points(): :return: EntryPoint objects for all installed packages. """ - eps = itertools.chain.from_iterable(dist.entry_points for dist in distributions()) + unique = functools.partial(unique_everseen, key=operator.attrgetter('name')) + eps = itertools.chain.from_iterable( + dist.entry_points for dist in unique(distributions()) + ) by_group = operator.attrgetter('group') ordered = sorted(eps, key=by_group) grouped = itertools.groupby(ordered, by_group) diff --git a/importlib_metadata/_itertools.py b/importlib_metadata/_itertools.py new file mode 100644 index 00000000..dd45f2f0 --- /dev/null +++ b/importlib_metadata/_itertools.py @@ -0,0 +1,19 @@ +from itertools import filterfalse + + +def unique_everseen(iterable, key=None): + "List unique elements, preserving order. Remember all elements ever seen." + # unique_everseen('AAAABBBCCDAABBB') --> A B C D + # unique_everseen('ABBCcAD', str.lower) --> A B C D + seen = set() + seen_add = seen.add + if key is None: + for element in filterfalse(seen.__contains__, iterable): + seen_add(element) + yield element + else: + for element in iterable: + k = key(element) + if k not in seen: + seen_add(k) + yield element From 8935288354ba3b843a13cdb5577c3cdb7a672e0b Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 26 Jan 2021 18:49:51 -0500 Subject: [PATCH 289/323] Add test capturing failed expectation. Ref #280. --- tests/test_api.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/test_api.py b/tests/test_api.py index a386551f..04a5b9d3 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -76,6 +76,34 @@ def test_entry_points_distribution(self): self.assertIn(ep.dist.name, ('distinfo-pkg', 'egginfo-pkg')) self.assertEqual(ep.dist.version, "1.0.0") + def test_entry_points_unique_packages(self): + """ + Entry points should only be exposed for the first package + on sys.path with a given name. + """ + alt_site_dir = self.fixtures.enter_context(fixtures.tempdir()) + self.fixtures.enter_context(self.add_sys_path(alt_site_dir)) + alt_pkg = { + "distinfo_pkg-1.1.0.dist-info": { + "METADATA": """ + Name: distinfo-pkg + Version: 1.1.0 + """, + "entry_points.txt": """ + [entries] + main = mod:altmain + """, + }, + } + fixtures.build_files(alt_pkg, alt_site_dir) + entries = dict(entry_points()['entries']) + assert not any( + ep.dist.name == 'distinfo-pkg' and ep.dist.version == '1.0.0' + for ep in entries.values() + ) + # ns:sub doesn't exist in alt_pkg + assert 'ns:sub' not in entries + def test_metadata_for_this_package(self): md = metadata('egginfo-pkg') assert md['author'] == 'Steven Ma' From 6cb27d381c0330c65fb07ce5a68d4525dea94600 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 26 Jan 2021 20:28:36 -0500 Subject: [PATCH 290/323] Update changelog --- CHANGES.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 57901f23..2bc3a356 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,9 @@ +v3.5.0 +====== + +* #280: ``entry_points`` now only returns entry points for + unique distributions (by name). + v3.4.0 ====== From 2b64fa218dcb902baa7bcc2da42e834eec6bdab8 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 26 Jan 2021 20:33:10 -0500 Subject: [PATCH 291/323] Add performance test for entry_points --- tox.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/tox.ini b/tox.ini index 11f52d7a..32796a47 100644 --- a/tox.ini +++ b/tox.ini @@ -39,6 +39,7 @@ deps = ipython commands = python -m timeit -s 'import importlib_metadata' -- 'importlib_metadata.distribution("ipython")' + python -m timeit -s 'import importlib_metadata' -- 'importlib_metadata.entry_points()' [testenv:release] skip_install = True From 9448e13a10648ae5a086247dea8a17efff31b816 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 15 Feb 2021 18:05:18 -0500 Subject: [PATCH 292/323] Make entry point collections (more) immutable. --- importlib_metadata/__init__.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index edcf2691..4c8188ae 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -157,9 +157,11 @@ def __reduce__(self): class EntryPoints(tuple): """ - A collection of EntryPoint objects, retrievable by name. + An immutable collection of EntryPoint objects, retrievable by name. """ + __slots__ = () + def __getitem__(self, name) -> EntryPoint: try: return next(ep for ep in self if ep.name == name) @@ -173,9 +175,11 @@ def names(self): class GroupedEntryPoints(tuple): """ - A collection of EntryPoint objects, retrievable by group. + An immutable collection of EntryPoint objects, retrievable by group. """ + __slots__ = () + def __getitem__(self, group) -> EntryPoints: return EntryPoints(ep for ep in self if ep.group == group) From 71fd4a7b6a8141becd431edf51dac590493d61c2 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 15 Feb 2021 21:28:16 -0500 Subject: [PATCH 293/323] Hide the deprecation warning from flake8 users --- importlib_metadata/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index 4c8188ae..f9af7824 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -5,6 +5,7 @@ import sys import zipp import email +import inspect import pathlib import operator import warnings @@ -191,8 +192,9 @@ def get(self, group, default=None): """ For backward compatibility, supply .get """ + is_flake8 = any('flake8' in str(frame) for frame in inspect.stack()) msg = "GroupedEntryPoints.get is deprecated. Just use __getitem__." - warnings.warn(msg, DeprecationWarning) + is_flake8 or warnings.warn(msg, DeprecationWarning) return self[group] or default From 8320adef797d5f14d9fff7b58ebc2a31a2a6a437 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 16 Feb 2021 22:01:49 -0500 Subject: [PATCH 294/323] Instead of presenting separate contexts for EntryPoints, unify into a single collection that can select on 'name' or 'group' or possibly other attributes. Expose that selection in the 'entry_points' function. --- docs/using.rst | 2 +- importlib_metadata/__init__.py | 56 +++++++++++++++++++--------------- tests/test_api.py | 26 +++++++++++----- tests/test_main.py | 4 +-- tests/test_zip.py | 2 +- 5 files changed, 54 insertions(+), 36 deletions(-) diff --git a/docs/using.rst b/docs/using.rst index 534c1dea..bdfe3e82 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -77,7 +77,7 @@ a ``.load()`` method to resolve the value. There are also ``.module``, >>> eps = entry_points() >>> sorted(eps.groups) ['console_scripts', 'distutils.commands', 'distutils.setup_keywords', 'egg_info.writers', 'setuptools.installation'] - >>> scripts = eps['console_scripts'] + >>> scripts = eps.select(group='console_scripts') >>> 'wheel' in scripts.names True >>> wheel = scripts['wheel'] diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index f9af7824..77057703 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -130,10 +130,6 @@ def _from_text(cls, text): config.read_string(text) return cls._from_config(config) - @classmethod - def _from_text_for(cls, text, dist): - return (ep._for(dist) for ep in cls._from_text(text)) - def _for(self, dist): self.dist = dist return self @@ -155,35 +151,42 @@ def __reduce__(self): (self.name, self.value, self.group), ) + def matches(self, **params): + attrs = (getattr(self, param) for param in params) + return all(map(operator.eq, params.values(), attrs)) + class EntryPoints(tuple): """ - An immutable collection of EntryPoint objects, retrievable by name. + An immutable collection of selectable EntryPoint objects. """ __slots__ = () - def __getitem__(self, name) -> EntryPoint: + def __getitem__(self, name) -> Union[EntryPoint, 'EntryPoints']: try: - return next(ep for ep in self if ep.name == name) - except Exception: + match = next(iter(self.select(name=name))) + return match + except StopIteration: + if name in self.groups: + return self._group_getitem(name) raise KeyError(name) + def _group_getitem(self, name): + """ + For backward compatability, supply .__getitem__ for groups. + """ + msg = "GroupedEntryPoints.__getitem__ is deprecated for groups. Use select." + warnings.warn(msg, DeprecationWarning) + return self.select(group=name) + + def select(self, **params): + return EntryPoints(ep for ep in self if ep.matches(**params)) + @property def names(self): return set(ep.name for ep in self) - -class GroupedEntryPoints(tuple): - """ - An immutable collection of EntryPoint objects, retrievable by group. - """ - - __slots__ = () - - def __getitem__(self, group) -> EntryPoints: - return EntryPoints(ep for ep in self if ep.group == group) - @property def groups(self): return set(ep.group for ep in self) @@ -193,9 +196,13 @@ def get(self, group, default=None): For backward compatibility, supply .get """ is_flake8 = any('flake8' in str(frame) for frame in inspect.stack()) - msg = "GroupedEntryPoints.get is deprecated. Just use __getitem__." + msg = "GroupedEntryPoints.get is deprecated. Use select." is_flake8 or warnings.warn(msg, DeprecationWarning) - return self[group] or default + return self.select(group=group) or default + + @classmethod + def _from_text_for(cls, text, dist): + return cls(ep._for(dist) for ep in EntryPoint._from_text(text)) class PackagePath(pathlib.PurePosixPath): @@ -353,8 +360,7 @@ def version(self): @property def entry_points(self): - eps = EntryPoint._from_text_for(self.read_text('entry_points.txt'), self) - return GroupedEntryPoints(eps) + return EntryPoints._from_text_for(self.read_text('entry_points.txt'), self) @property def files(self): @@ -687,13 +693,13 @@ def version(distribution_name): return distribution(distribution_name).version -def entry_points(): +def entry_points(**params): """Return EntryPoint objects for all installed packages. :return: EntryPoint objects for all installed packages. """ eps = itertools.chain.from_iterable(dist.entry_points for dist in distributions()) - return GroupedEntryPoints(eps) + return EntryPoints(eps).select(**params) def files(distribution_name): diff --git a/tests/test_api.py b/tests/test_api.py index dc0c7870..a6466309 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -67,14 +67,14 @@ def test_read_text(self): def test_entry_points(self): eps = entry_points() assert 'entries' in eps.groups - entries = eps['entries'] + entries = eps.select(group='entries') assert 'main' in entries.names ep = entries['main'] self.assertEqual(ep.value, 'mod:main') self.assertEqual(ep.extras, []) def test_entry_points_distribution(self): - entries = entry_points()['entries'] + entries = entry_points(group='entries') for entry in ("main", "ns:sub"): ep = entries[entry] self.assertIn(ep.dist.name, ('distinfo-pkg', 'egginfo-pkg')) @@ -82,10 +82,10 @@ def test_entry_points_distribution(self): def test_entry_points_missing_name(self): with self.assertRaises(KeyError): - entry_points()['entries']['missing'] + entry_points(group='entries')['missing'] def test_entry_points_missing_group(self): - assert entry_points()['missing'] == () + assert entry_points(group='missing') == () def test_entry_points_dict_construction(self): """ @@ -94,16 +94,28 @@ def test_entry_points_dict_construction(self): Capture this now deprecated use-case. """ with warnings.catch_warnings(record=True) as caught: - eps = dict(entry_points()['entries']) + eps = dict(entry_points(group='entries')) assert 'main' in eps - assert eps['main'] == entry_points()['entries']['main'] + assert eps['main'] == entry_points(group='entries')['main'] # check warning expected = next(iter(caught)) assert expected.category is DeprecationWarning assert "Construction of dict of EntryPoints is deprecated" in str(expected) + def test_entry_points_groups_getitem(self): + """ + Prior versions of entry_points() returned a dict. Ensure + that callers using '.__getitem__()' are supported but warned to + migrate. + """ + with warnings.catch_warnings(record=True): + entry_points()['entries'] == entry_points(group='entries') + + with self.assertRaises(KeyError): + entry_points()['missing'] + def test_entry_points_groups_get(self): """ Prior versions of entry_points() returned a dict. Ensure @@ -113,7 +125,7 @@ def test_entry_points_groups_get(self): with warnings.catch_warnings(record=True): entry_points().get('missing', 'default') == 'default' entry_points().get('entries', 'default') == entry_points()['entries'] - entry_points().get('missing', ()) == entry_points()['missing'] + entry_points().get('missing', ()) == () def test_metadata_for_this_package(self): md = metadata('egginfo-pkg') diff --git a/tests/test_main.py b/tests/test_main.py index b778572c..e8a66c0d 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -58,11 +58,11 @@ def test_import_nonexistent_module(self): importlib.import_module('does_not_exist') def test_resolve(self): - ep = entry_points()['entries']['main'] + ep = entry_points(group='entries')['main'] self.assertEqual(ep.load().__name__, "main") def test_entrypoint_with_colon_in_name(self): - ep = entry_points()['entries']['ns:sub'] + ep = entry_points(group='entries')['ns:sub'] self.assertEqual(ep.value, 'mod:main') def test_resolve_without_attr(self): diff --git a/tests/test_zip.py b/tests/test_zip.py index 5a63465f..4279046d 100644 --- a/tests/test_zip.py +++ b/tests/test_zip.py @@ -45,7 +45,7 @@ def test_zip_version_does_not_match(self): version('definitely-not-installed') def test_zip_entry_points(self): - scripts = entry_points()['console_scripts'] + scripts = entry_points(group='console_scripts') entry_point = scripts['example'] self.assertEqual(entry_point.value, 'example:main') entry_point = scripts['Example'] From 61a265c18bc481c1e49fded0476a04ba5f75b750 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 21 Feb 2021 12:32:34 -0500 Subject: [PATCH 295/323] Fix perf tests --- tox.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index a7476d12..5466077e 100644 --- a/tox.ini +++ b/tox.ini @@ -38,9 +38,9 @@ use_develop = False deps = ipython commands = - python -m 'print("Simple discovery performance")' + python -c 'print("Simple discovery performance")' python -m timeit -s 'import importlib_metadata' -- 'importlib_metadata.distribution("ipython")' - python -m 'print("Entry point discovery performance")' + python -c 'print("Entry point discovery performance")' python -m timeit -s 'import importlib_metadata' -- 'importlib_metadata.entry_points()' python -c 'print("Cached lookup performance")' python -m timeit -s 'import importlib_metadata; importlib_metadata.distribution("ipython")' -- 'importlib_metadata.distribution("ipython")' From e3d1b935b3a2185461aadca34192b93bfdeaa9ca Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 21 Feb 2021 12:27:41 -0500 Subject: [PATCH 296/323] Update changelog. --- CHANGES.rst | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 368723d4..2f5b1ec5 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,18 +1,34 @@ -v3.5.0 +v4.0.0 ====== -* #280: ``entry_points`` now only returns entry points for - unique distributions (by name). -* ``entry_points()`` now returns an ``GroupedEntryPoints`` - object, a tuple of all entry points but with a convenience - property ``groups`` and ``__getitem__`` accessor. Further, - accessing a group returns an ``EntryPoints`` object, - another tuple of entry points in the group, accessible by - name. Construction of entry points using +* #284: Introduces new ``EntryPoints`` object, a tuple of + ``EntryPoint`` objects but with convenience properties for + selecting and inspecting the results: + + - ``.select()`` accepts ``group`` or ``name`` keyword + parameters and returns a new ``EntryPoints`` tuple + with only those that match the selection. + - ``.groups`` property presents all of the group names. + - ``.names`` property presents the names of the entry points. + - Item access (e.g. ``eps[name]``) retrieves a single + entry point by name. + + ``entry_points()`` now returns an ``EntryPoints`` + object, but provides for backward compatibility with + a ``__getitem__`` accessor by group and a ``get()`` + method. + + Construction of entry points using ``dict([EntryPoint, ...])`` is now deprecated and raises an appropriate DeprecationWarning and will be removed in a future version. +v3.5.0 +====== + +* #280: ``entry_points`` now only returns entry points for + unique distributions (by name). + v3.4.0 ====== From 9d55a331c7d77025054e85f23bc23c614fab6856 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 22 Feb 2021 08:47:11 -0500 Subject: [PATCH 297/323] Separate compatibility shim from canonical EntryPoints container. --- importlib_metadata/__init__.py | 41 ++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index 313beca2..94a82ffe 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -165,23 +165,12 @@ class EntryPoints(tuple): __slots__ = () - def __getitem__(self, name) -> Union[EntryPoint, 'EntryPoints']: + def __getitem__(self, name): # -> EntryPoint: try: - match = next(iter(self.select(name=name))) - return match + return next(iter(self.select(name=name))) except StopIteration: - if name in self.groups: - return self._group_getitem(name) raise KeyError(name) - def _group_getitem(self, name): - """ - For backward compatability, supply .__getitem__ for groups. - """ - msg = "GroupedEntryPoints.__getitem__ is deprecated for groups. Use select." - warnings.warn(msg, DeprecationWarning) - return self.select(group=name) - def select(self, **params): return EntryPoints(ep for ep in self if ep.matches(**params)) @@ -193,6 +182,23 @@ def names(self): def groups(self): return set(ep.group for ep in self) + @classmethod + def _from_text_for(cls, text, dist): + return cls(ep._for(dist) for ep in EntryPoint._from_text(text)) + + +class LegacyGroupedEntryPoints(EntryPoints): + def __getitem__(self, name) -> Union[EntryPoint, 'EntryPoints']: + try: + return super().__getitem__(name) + except KeyError: + if name not in self.groups: + raise + + msg = "GroupedEntryPoints.__getitem__ is deprecated for groups. Use select." + warnings.warn(msg, DeprecationWarning) + return self.select(group=name) + def get(self, group, default=None): """ For backward compatibility, supply .get @@ -202,9 +208,10 @@ def get(self, group, default=None): is_flake8 or warnings.warn(msg, DeprecationWarning) return self.select(group=group) or default - @classmethod - def _from_text_for(cls, text, dist): - return cls(ep._for(dist) for ep in EntryPoint._from_text(text)) + def select(self, **params): + if not params: + return self + return super().select(**params) class PackagePath(pathlib.PurePosixPath): @@ -704,7 +711,7 @@ def entry_points(**params): eps = itertools.chain.from_iterable( dist.entry_points for dist in unique(distributions()) ) - return EntryPoints(eps).select(**params) + return LegacyGroupedEntryPoints(eps).select(**params) def files(distribution_name): From d6f7c201b15c79bce7c4e27784a2bd61bdc43555 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 22 Feb 2021 20:49:20 -0500 Subject: [PATCH 298/323] Add docstrings to the compatibility shim. Give primacy to group lookup in compatibility shim. --- importlib_metadata/__init__.py | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index 94a82ffe..bd7d4d7e 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -188,27 +188,38 @@ def _from_text_for(cls, text, dist): class LegacyGroupedEntryPoints(EntryPoints): + """ + Compatibility wrapper around EntryPoints to provide + much of the 'dict' interface previously returned by + entry_points. + """ + def __getitem__(self, name) -> Union[EntryPoint, 'EntryPoints']: - try: - return super().__getitem__(name) - except KeyError: - if name not in self.groups: - raise + """ + When accessed by name that matches a group, return the group. + """ + group = self.select(group=name) + if group: + msg = "GroupedEntryPoints.__getitem__ is deprecated for groups. Use select." + warnings.warn(msg, DeprecationWarning, stacklevel=2) + return group - msg = "GroupedEntryPoints.__getitem__ is deprecated for groups. Use select." - warnings.warn(msg, DeprecationWarning) - return self.select(group=name) + return super().__getitem__(name) def get(self, group, default=None): """ - For backward compatibility, supply .get + For backward compatibility, supply .get. """ is_flake8 = any('flake8' in str(frame) for frame in inspect.stack()) msg = "GroupedEntryPoints.get is deprecated. Use select." - is_flake8 or warnings.warn(msg, DeprecationWarning) + is_flake8 or warnings.warn(msg, DeprecationWarning, stacklevel=2) return self.select(group=group) or default def select(self, **params): + """ + Prevent transform to EntryPoints during call to entry_points if + no selection parameters were passed. + """ if not params: return self return super().select(**params) From 2db4dada379822b4767809a5c4e2436f32908658 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 23 Feb 2021 09:58:19 -0500 Subject: [PATCH 299/323] Introduce SelectableGroups, created for the 3.x line to provide forward compatibilty to the new interfaces without sacrificing backward compatibility. --- CHANGES.rst | 21 ++++++++++---- docs/using.rst | 4 +-- importlib_metadata/__init__.py | 50 ++++++++++++++++++++++++++++++++-- 3 files changed, 65 insertions(+), 10 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 2f5b1ec5..f8df681d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,4 +1,4 @@ -v4.0.0 +v3.6.0 ====== * #284: Introduces new ``EntryPoints`` object, a tuple of @@ -13,10 +13,21 @@ v4.0.0 - Item access (e.g. ``eps[name]``) retrieves a single entry point by name. - ``entry_points()`` now returns an ``EntryPoints`` - object, but provides for backward compatibility with - a ``__getitem__`` accessor by group and a ``get()`` - method. + ``entry_points`` now accepts "selection parameters", + same as ``EntryPoint.select()``. + + ``entry_points()`` now provides a future-compatible + ``SelectableGroups`` object that supplies the above interface + but remains a dict for compatibility. + + In the future, ``entry_points()`` will return an + ``EntryPoints`` object, but provide for backward + compatibility with a deprecated ``__getitem__`` + accessor by group and a ``get()`` method. + + If passing selection parameters to ``entry_points``, the + future behavior is invoked and an ``EntryPoints`` is the + result. Construction of entry points using ``dict([EntryPoint, ...])`` is now deprecated and raises diff --git a/docs/using.rst b/docs/using.rst index bdfe3e82..97941452 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -67,8 +67,8 @@ This package provides the following functionality via its public API. Entry points ------------ -The ``entry_points()`` function returns a sequence of all entry points, -keyed by group. Entry points are represented by ``EntryPoint`` instances; +The ``entry_points()`` function returns a collection of entry points. +Entry points are represented by ``EntryPoint`` instances; each ``EntryPoint`` has a ``.name``, ``.group``, and ``.value`` attributes and a ``.load()`` method to resolve the value. There are also ``.module``, ``.attr``, and ``.extras`` attributes for getting the components of the diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index bd7d4d7e..f2dc9c07 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -187,6 +187,37 @@ def _from_text_for(cls, text, dist): return cls(ep._for(dist) for ep in EntryPoint._from_text(text)) +class SelectableGroups(dict): + """ + A backward- and forward-compatible result from + entry_points that fully implements the dict interface. + """ + + @classmethod + def load(cls, eps): + by_group = operator.attrgetter('group') + ordered = sorted(eps, key=by_group) + grouped = itertools.groupby(ordered, by_group) + return cls((group, EntryPoints(eps)) for group, eps in grouped) + + @property + def groups(self): + return self.keys() + + @property + def names(self): + return (ep.name for ep in self._all) + + @property + def _all(self): + return itertools.chain.from_iterable(self.values()) + + def select(self, **params): + if not params: + return self + return EntryPoints(self._all).select(**params) + + class LegacyGroupedEntryPoints(EntryPoints): """ Compatibility wrapper around EntryPoints to provide @@ -713,16 +744,29 @@ def version(distribution_name): return distribution(distribution_name).version -def entry_points(**params): +def entry_points(**params) -> Union[EntryPoints, SelectableGroups]: """Return EntryPoint objects for all installed packages. - :return: EntryPoint objects for all installed packages. + Pass selection parameters (group or name) to filter the + result to entry points matching those properties (see + EntryPoints.select()). + + For compatibility, returns ``SelectableGroups`` object unless + selection parameters are supplied. In the future, this function + will return ``LegacyGroupedEntryPoints`` instead of + ``SelectableGroups`` and eventually will only return + ``EntryPoints``. + + For maximum future compatibility, pass selection parameters + or invoke ``.select`` with parameters on the result. + + :return: EntryPoints or SelectableGroups for all installed packages. """ unique = functools.partial(unique_everseen, key=operator.attrgetter('name')) eps = itertools.chain.from_iterable( dist.entry_points for dist in unique(distributions()) ) - return LegacyGroupedEntryPoints(eps).select(**params) + return SelectableGroups.load(eps).select(**params) def files(distribution_name): From 2def046c694cddfbd1967575f8ce7da95680c9c3 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 23 Feb 2021 13:07:55 -0500 Subject: [PATCH 300/323] Address coverage misses, ignored for LegacyGroupedEntryPoints. --- importlib_metadata/__init__.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index f2dc9c07..5f156ae6 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -180,6 +180,11 @@ def names(self): @property def groups(self): + """ + For coverage while SelectableGroups is present. + >>> EntryPoints().groups + set() + """ return set(ep.group for ep in self) @classmethod @@ -202,11 +207,16 @@ def load(cls, eps): @property def groups(self): - return self.keys() + return set(self.keys()) @property def names(self): - return (ep.name for ep in self._all) + """ + for coverage: + >>> SelectableGroups().names + set() + """ + return set(ep.name for ep in self._all) @property def _all(self): @@ -218,7 +228,7 @@ def select(self, **params): return EntryPoints(self._all).select(**params) -class LegacyGroupedEntryPoints(EntryPoints): +class LegacyGroupedEntryPoints(EntryPoints): # pragma: nocover """ Compatibility wrapper around EntryPoints to provide much of the 'dict' interface previously returned by From dd8da47fdf97d4420cca557742f8f075da2123e4 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 23 Feb 2021 13:12:42 -0500 Subject: [PATCH 301/323] Leverage EntryPoints interfaces in SelectableGroups --- importlib_metadata/__init__.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index 5f156ae6..b0b1ae0e 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -205,9 +205,13 @@ def load(cls, eps): grouped = itertools.groupby(ordered, by_group) return cls((group, EntryPoints(eps)) for group, eps in grouped) + @property + def _all(self): + return EntryPoints(itertools.chain.from_iterable(self.values())) + @property def groups(self): - return set(self.keys()) + return self._all.groups @property def names(self): @@ -216,16 +220,12 @@ def names(self): >>> SelectableGroups().names set() """ - return set(ep.name for ep in self._all) - - @property - def _all(self): - return itertools.chain.from_iterable(self.values()) + return self._all.names def select(self, **params): if not params: return self - return EntryPoints(self._all).select(**params) + return self._all.select(**params) class LegacyGroupedEntryPoints(EntryPoints): # pragma: nocover From 466cd3c8e6036cbd16584629fa0e54d6c0d6b027 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 24 Feb 2021 11:42:19 -0500 Subject: [PATCH 302/323] Add 'packages_distributions'. Fixes #131. --- CHANGES.rst | 6 ++++++ docs/using.rst | 11 +++++++++++ importlib_metadata/__init__.py | 20 ++++++++++++++++++-- 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index f8df681d..a4e468c0 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,9 @@ +v3.7.0 +====== + +* #131: Added ``packages_distributions`` to conveniently + resolve a top-level package or module to its distribution(s). + v3.6.0 ====== diff --git a/docs/using.rst b/docs/using.rst index 97941452..18aa2fda 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -182,6 +182,17 @@ function:: ["pytest (>=3.0.0) ; extra == 'test'", "pytest-cov ; extra == 'test'"] +Package distributions +--------------------- + +A convience method to resolve the distribution or +distributions (in the case of a namespace package) for top-level +Python packages or modules:: + + >>> packages_distributions() + {'importlib_metadata': ['importlib-metadata'], 'yaml': ['PyYAML'], 'jaraco': ['jaraco.classes', 'jaraco.functools'], ...} + + Distributions ============= diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index b0b1ae0e..5c19c237 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -12,7 +12,7 @@ import functools import itertools import posixpath -import collections +import collections.abc from ._compat import ( NullFinder, @@ -28,7 +28,7 @@ from importlib import import_module from importlib.abc import MetaPathFinder from itertools import starmap -from typing import Any, List, Optional, TypeVar, Union +from typing import Any, List, Mapping, Optional, TypeVar, Union __all__ = [ @@ -796,3 +796,19 @@ def requires(distribution_name): packaging.requirement.Requirement. """ return distribution(distribution_name).requires + + +def packages_distributions() -> Mapping[str, List[str]]: + """ + Return a mapping of top-level packages to their + distributions. + + >>> pkgs = packages_distributions() + >>> all(isinstance(dist, collections.abc.Sequence) for dist in pkgs.values()) + True + """ + pkg_to_dist = collections.defaultdict(list) + for dist in distributions(): + for pkg in (dist.read_text('top_level.txt') or '').split(): + pkg_to_dist[pkg].append(dist.metadata['Name']) + return dict(pkg_to_dist) From 8c4cff1a2ffea6b4fa59d4a86c6608bb19861a92 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 4 Mar 2021 04:14:02 -0500 Subject: [PATCH 303/323] Convert LegacyGroupedEntryPoints into simpler dict interface deprecation. --- importlib_metadata/__init__.py | 38 ++++++++-------------------------- 1 file changed, 9 insertions(+), 29 deletions(-) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index 5c19c237..162656a8 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -228,42 +228,22 @@ def select(self, **params): return self._all.select(**params) -class LegacyGroupedEntryPoints(EntryPoints): # pragma: nocover +class DeprecatedDict(dict): # pragma: nocover """ - Compatibility wrapper around EntryPoints to provide - much of the 'dict' interface previously returned by - entry_points. + Compatibility wrapper around dict to indicate that + Mapping behavior is deprecated. """ - def __getitem__(self, name) -> Union[EntryPoint, 'EntryPoints']: - """ - When accessed by name that matches a group, return the group. - """ - group = self.select(group=name) - if group: - msg = "GroupedEntryPoints.__getitem__ is deprecated for groups. Use select." - warnings.warn(msg, DeprecationWarning, stacklevel=2) - return group - + def __getitem__(self, name): + msg = "SelectableGroups.__getitem__ is deprecated. Use select." + warnings.warn(msg, DeprecationWarning, stacklevel=2) return super().__getitem__(name) - def get(self, group, default=None): - """ - For backward compatibility, supply .get. - """ + def get(self, name, default=None): is_flake8 = any('flake8' in str(frame) for frame in inspect.stack()) - msg = "GroupedEntryPoints.get is deprecated. Use select." + msg = "SelectableGroups.get is deprecated. Use select." is_flake8 or warnings.warn(msg, DeprecationWarning, stacklevel=2) - return self.select(group=group) or default - - def select(self, **params): - """ - Prevent transform to EntryPoints during call to entry_points if - no selection parameters were passed. - """ - if not params: - return self - return super().select(**params) + return super().get(name, default) class PackagePath(pathlib.PurePosixPath): From b022ae991389755055cec67f5112c283b2413fe1 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 4 Mar 2021 04:20:28 -0500 Subject: [PATCH 304/323] Extract warning as a method. --- importlib_metadata/__init__.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index 162656a8..5d85be62 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -234,15 +234,17 @@ class DeprecatedDict(dict): # pragma: nocover Mapping behavior is deprecated. """ + def _warn(self): + msg = "SelectableGroups dict interface is deprecated. Use select." + warnings.warn(msg, DeprecationWarning, stacklevel=3) + def __getitem__(self, name): - msg = "SelectableGroups.__getitem__ is deprecated. Use select." - warnings.warn(msg, DeprecationWarning, stacklevel=2) + self._warn() return super().__getitem__(name) def get(self, name, default=None): is_flake8 = any('flake8' in str(frame) for frame in inspect.stack()) - msg = "SelectableGroups.get is deprecated. Use select." - is_flake8 or warnings.warn(msg, DeprecationWarning, stacklevel=2) + is_flake8 or self._warn() return super().get(name, default) From 1f463549a246ec9f855bea04b20080f3236a9cdc Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 4 Mar 2021 04:39:04 -0500 Subject: [PATCH 305/323] Remove flake8 bypass and implement warning as a partial. --- importlib_metadata/__init__.py | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index 5d85be62..26d11c2b 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -5,7 +5,6 @@ import sys import zipp import email -import inspect import pathlib import operator import warnings @@ -228,23 +227,33 @@ def select(self, **params): return self._all.select(**params) -class DeprecatedDict(dict): # pragma: nocover +class DeprecatedDict(dict): """ Compatibility wrapper around dict to indicate that Mapping behavior is deprecated. + + >>> recwarn = getfixture('recwarn') + >>> dd = DeprecatedDict(foo='bar') + >>> dd.get('baz', None) + >>> dd['foo'] + 'bar' + >>> len(recwarn) + 1 """ - def _warn(self): - msg = "SelectableGroups dict interface is deprecated. Use select." - warnings.warn(msg, DeprecationWarning, stacklevel=3) + _warn = functools.partial( + warnings.warn, + "SelectableGroups dict interface is deprecated. Use select.", + DeprecationWarning, + stacklevel=3, + ) def __getitem__(self, name): self._warn() return super().__getitem__(name) def get(self, name, default=None): - is_flake8 = any('flake8' in str(frame) for frame in inspect.stack()) - is_flake8 or self._warn() + self._warn() return super().get(name, default) From 537c55da0cf507b1e800e0d7785ae983f8b1f1fb Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 4 Mar 2021 04:50:12 -0500 Subject: [PATCH 306/323] Reimplement flake8 bypass as a decorator. --- importlib_metadata/__init__.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index 26d11c2b..d100d960 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -5,12 +5,14 @@ import sys import zipp import email +import inspect import pathlib import operator import warnings import functools import itertools import posixpath +import contextlib import collections.abc from ._compat import ( @@ -227,6 +229,13 @@ def select(self, **params): return self._all.select(**params) +class Flake8Bypass(warnings.catch_warnings, contextlib.ContextDecorator): + def __enter__(self): + super().__enter__() + is_flake8 = any('flake8' in str(frame) for frame in inspect.stack()) + is_flake8 and warnings.simplefilter('ignore', DeprecationWarning) + + class DeprecatedDict(dict): """ Compatibility wrapper around dict to indicate that @@ -252,6 +261,7 @@ def __getitem__(self, name): self._warn() return super().__getitem__(name) + @Flake8Bypass() def get(self, name, default=None): self._warn() return super().get(name, default) From 1f2a89cf9a6fe71dbcdacdfd040a2abbd4ece842 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 4 Mar 2021 04:54:48 -0500 Subject: [PATCH 307/323] Also deprecate iter, contains, keys, and values --- importlib_metadata/__init__.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index d100d960..00f758a3 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -246,6 +246,14 @@ class DeprecatedDict(dict): >>> dd.get('baz', None) >>> dd['foo'] 'bar' + >>> list(dd) + ['foo'] + >>> list(dd.keys()) + ['foo'] + >>> 'foo' in dd + True + >>> list(dd.values()) + ['bar'] >>> len(recwarn) 1 """ @@ -266,6 +274,22 @@ def get(self, name, default=None): self._warn() return super().get(name, default) + def __iter__(self): + self._warn() + return super().__iter__() + + def __contains__(self, *args): + self._warn() + return super().__contains__(*args) + + def keys(self): + self._warn() + return super().keys() + + def values(self): + self._warn() + return super().values() + class PackagePath(pathlib.PurePosixPath): """A reference to a path in a package""" From 401c041d15772869a6337aa7c3ddfde77cadcfb4 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 4 Mar 2021 04:57:43 -0500 Subject: [PATCH 308/323] Move selectable groups after DeprecatedDict --- importlib_metadata/__init__.py | 72 +++++++++++++++++----------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index 00f758a3..fb30e2cb 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -193,42 +193,6 @@ def _from_text_for(cls, text, dist): return cls(ep._for(dist) for ep in EntryPoint._from_text(text)) -class SelectableGroups(dict): - """ - A backward- and forward-compatible result from - entry_points that fully implements the dict interface. - """ - - @classmethod - def load(cls, eps): - by_group = operator.attrgetter('group') - ordered = sorted(eps, key=by_group) - grouped = itertools.groupby(ordered, by_group) - return cls((group, EntryPoints(eps)) for group, eps in grouped) - - @property - def _all(self): - return EntryPoints(itertools.chain.from_iterable(self.values())) - - @property - def groups(self): - return self._all.groups - - @property - def names(self): - """ - for coverage: - >>> SelectableGroups().names - set() - """ - return self._all.names - - def select(self, **params): - if not params: - return self - return self._all.select(**params) - - class Flake8Bypass(warnings.catch_warnings, contextlib.ContextDecorator): def __enter__(self): super().__enter__() @@ -291,6 +255,42 @@ def values(self): return super().values() +class SelectableGroups(dict): + """ + A backward- and forward-compatible result from + entry_points that fully implements the dict interface. + """ + + @classmethod + def load(cls, eps): + by_group = operator.attrgetter('group') + ordered = sorted(eps, key=by_group) + grouped = itertools.groupby(ordered, by_group) + return cls((group, EntryPoints(eps)) for group, eps in grouped) + + @property + def _all(self): + return EntryPoints(itertools.chain.from_iterable(self.values())) + + @property + def groups(self): + return self._all.groups + + @property + def names(self): + """ + for coverage: + >>> SelectableGroups().names + set() + """ + return self._all.names + + def select(self, **params): + if not params: + return self + return self._all.select(**params) + + class PackagePath(pathlib.PurePosixPath): """A reference to a path in a package""" From 7e7fc8c8f379df4a3d47258015de8e7ae4cd54c5 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 4 Mar 2021 05:22:38 -0500 Subject: [PATCH 309/323] Just check the filename in the frame. Otherwise, it'll match on the current line. --- importlib_metadata/__init__.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index fb30e2cb..5dc51d3e 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -196,7 +196,9 @@ def _from_text_for(cls, text, dist): class Flake8Bypass(warnings.catch_warnings, contextlib.ContextDecorator): def __enter__(self): super().__enter__() - is_flake8 = any('flake8' in str(frame) for frame in inspect.stack()) + is_flake8 = any( + 'flake8' in str(frame.filename) for frame in inspect.stack()[:5] + ) is_flake8 and warnings.simplefilter('ignore', DeprecationWarning) @@ -219,7 +221,7 @@ class DeprecatedDict(dict): >>> list(dd.values()) ['bar'] >>> len(recwarn) - 1 + 2 """ _warn = functools.partial( From ed33213268c4cda0079649a410cfbfc679a90313 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 4 Mar 2021 05:26:12 -0500 Subject: [PATCH 310/323] Wrap function rather than decorating method. Avoids varying stack depths. --- importlib_metadata/__init__.py | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index 5dc51d3e..44160596 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -12,7 +12,6 @@ import functools import itertools import posixpath -import contextlib import collections.abc from ._compat import ( @@ -193,13 +192,9 @@ def _from_text_for(cls, text, dist): return cls(ep._for(dist) for ep in EntryPoint._from_text(text)) -class Flake8Bypass(warnings.catch_warnings, contextlib.ContextDecorator): - def __enter__(self): - super().__enter__() - is_flake8 = any( - 'flake8' in str(frame.filename) for frame in inspect.stack()[:5] - ) - is_flake8 and warnings.simplefilter('ignore', DeprecationWarning) +def flake8_bypass(func): + is_flake8 = any('flake8' in str(frame.filename) for frame in inspect.stack()[:5]) + return func if not is_flake8 else lambda: None class DeprecatedDict(dict): @@ -221,7 +216,7 @@ class DeprecatedDict(dict): >>> list(dd.values()) ['bar'] >>> len(recwarn) - 2 + 1 """ _warn = functools.partial( @@ -235,9 +230,8 @@ def __getitem__(self, name): self._warn() return super().__getitem__(name) - @Flake8Bypass() def get(self, name, default=None): - self._warn() + flake8_bypass(self._warn)() return super().get(name, default) def __iter__(self): From d940e63e562e66ab2aaf9bdf8444fc40a5637966 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 4 Mar 2021 05:28:30 -0500 Subject: [PATCH 311/323] Instead of subclassing dict, make Deprecated a mix-in. --- importlib_metadata/__init__.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index 44160596..8ab38328 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -197,12 +197,13 @@ def flake8_bypass(func): return func if not is_flake8 else lambda: None -class DeprecatedDict(dict): +class Deprecated: """ - Compatibility wrapper around dict to indicate that - Mapping behavior is deprecated. + Compatibility add-in for mapping to indicate that + mapping behavior is deprecated. >>> recwarn = getfixture('recwarn') + >>> class DeprecatedDict(Deprecated, dict): pass >>> dd = DeprecatedDict(foo='bar') >>> dd.get('baz', None) >>> dd['foo'] From 0460524e44896b9e5c746a21e1f06efe9b5ed475 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 4 Mar 2021 05:35:44 -0500 Subject: [PATCH 312/323] stacklevel of 2 is the right place --- importlib_metadata/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index 8ab38328..b02dc159 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -224,7 +224,7 @@ class Deprecated: warnings.warn, "SelectableGroups dict interface is deprecated. Use select.", DeprecationWarning, - stacklevel=3, + stacklevel=2, ) def __getitem__(self, name): From a54488dca687fbd4e3d35bcddadc26fba836183c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 4 Mar 2021 05:38:51 -0500 Subject: [PATCH 313/323] Querying missing key will also be deprecated. --- tests/test_api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_api.py b/tests/test_api.py index a2810acc..8c8d9abb 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -141,8 +141,8 @@ def test_entry_points_groups_getitem(self): with warnings.catch_warnings(record=True): entry_points()['entries'] == entry_points(group='entries') - with self.assertRaises(KeyError): - entry_points()['missing'] + with self.assertRaises(KeyError): + entry_points()['missing'] def test_entry_points_groups_get(self): """ From cc40cd56bfd2ced7e90616149d5450e06877dbde Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 4 Mar 2021 05:39:46 -0500 Subject: [PATCH 314/323] Update docstring --- importlib_metadata/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index b02dc159..e06c70b6 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -267,6 +267,9 @@ def load(cls, eps): @property def _all(self): + """ + Reconstruct a list of all entrypoints from the groups. + """ return EntryPoints(itertools.chain.from_iterable(self.values())) @property From 56d312b95217ece0191d4b587b3da81e6d9a71db Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 7 Mar 2021 17:10:53 -0500 Subject: [PATCH 315/323] Update changelog. --- CHANGES.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index a4e468c0..ba99d6d5 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,9 @@ +v3.7.1 +====== + +* Internal refactoring to facilitate ``entry_points() -> dict`` + deprecation. + v3.7.0 ====== From 1e2381fe101fd70742a0171e51c1be82aedf519b Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 7 Mar 2021 18:48:21 -0500 Subject: [PATCH 316/323] Remove latent reference to LegacyGroupedEntryPoints. --- CHANGES.rst | 5 +++++ importlib_metadata/__init__.py | 5 ++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index ba99d6d5..2792caf8 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,8 @@ +v3.7.2 +====== + +* Cleaned up cruft in entry_points docstring. + v3.7.1 ====== diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index e06c70b6..95087b55 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -788,9 +788,8 @@ def entry_points(**params) -> Union[EntryPoints, SelectableGroups]: For compatibility, returns ``SelectableGroups`` object unless selection parameters are supplied. In the future, this function - will return ``LegacyGroupedEntryPoints`` instead of - ``SelectableGroups`` and eventually will only return - ``EntryPoints``. + will return ``EntryPoints`` instead of ``SelectableGroups`` + even when no selection parameters are supplied. For maximum future compatibility, pass selection parameters or invoke ``.select`` with parameters on the result. From 7bdeaa45e3710e33735a9632c65ab7916c06c410 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 14 Mar 2021 09:24:04 -0400 Subject: [PATCH 317/323] Add packages_distributions to __all__ --- importlib_metadata/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index 95087b55..f583dc91 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -40,6 +40,7 @@ 'entry_points', 'files', 'metadata', + 'packages_distributions', 'requires', 'version', ] From bb2437066473ed10dff16564d731917345251e88 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 14 Mar 2021 09:57:53 -0400 Subject: [PATCH 318/323] Expand docs to explain more about the interfaces and to include a compatibility note. --- docs/using.rst | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/docs/using.rst b/docs/using.rst index 18aa2fda..53d83959 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -72,15 +72,43 @@ Entry points are represented by ``EntryPoint`` instances; each ``EntryPoint`` has a ``.name``, ``.group``, and ``.value`` attributes and a ``.load()`` method to resolve the value. There are also ``.module``, ``.attr``, and ``.extras`` attributes for getting the components of the -``.value`` attribute:: +``.value`` attribute. + +Query all entry points:: >>> eps = entry_points() + +The ``entry_points()`` function returns an ``EntryPoints`` object, +a sequence of all ``EntryPoint`` objects with ``names`` and ``groups`` +attributes for convenience. + >>> sorted(eps.groups) ['console_scripts', 'distutils.commands', 'distutils.setup_keywords', 'egg_info.writers', 'setuptools.installation'] + +``EntryPoints`` has a ``select`` method to select entry points +matching specific properties. Select entry points in the +``console_scripts`` group:: + >>> scripts = eps.select(group='console_scripts') + +Equivalently, since ``entry_points`` passes keyword arguments +through to select:: + + >>> scripts = entry_points(group='console_scripts') + +Pick out a specific script named "wheel" (found in the wheel project):: + >>> 'wheel' in scripts.names True >>> wheel = scripts['wheel'] + +Equivalently, query for that entry point during selection:: + + >>> (wheel,) = entry_points(group='console_scripts', name='wheel') + >>> (wheel,) = entry_points().select(group='console_scripts', name='wheel') + +Inspect the resolved entry point:: + >>> wheel EntryPoint(name='wheel', value='wheel.cli:main', group='console_scripts') >>> wheel.module @@ -99,6 +127,17 @@ group. Read `the setuptools docs `_ for more information on entry points, their definition, and usage. +*Compatibility Note* + +The "selectable" entry points were introduced in ``importlib_metadata`` +3.6 and Python 3.10. Prior to those changes, ``entry_points`` accepted +no parameters and always returned a dictionary of entry points, keyed +by group. For compatibility, if no parameters are passed to entry_points, +a ``SelectableGroups`` object is returned, implementing that dict +interface. In the future, calling ``entry_points`` with no parameters +will return an ``EntryPoints`` object. Users should rely on the selection +interface to retrieve entry points by group. + .. _metadata: From bf777ae8030fd5fe778778ed159a484129083ece Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 14 Mar 2021 10:10:20 -0400 Subject: [PATCH 319/323] Expand docs on EntryPoints --- importlib_metadata/__init__.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index f583dc91..46203448 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -167,21 +167,33 @@ class EntryPoints(tuple): __slots__ = () def __getitem__(self, name): # -> EntryPoint: + """ + Get the EntryPoint in self matching name. + """ try: return next(iter(self.select(name=name))) except StopIteration: raise KeyError(name) def select(self, **params): + """ + Select entry points from self that match the + given parameters (typically group and/or name). + """ return EntryPoints(ep for ep in self if ep.matches(**params)) @property def names(self): + """ + Return the set of all names of all entry points. + """ return set(ep.name for ep in self) @property def groups(self): """ + Return the set of all groups of all entry points. + For coverage while SelectableGroups is present. >>> EntryPoints().groups set() From 60f2791a2f0aa0007cb39c46b28ad687cef8bd8a Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 14 Mar 2021 10:13:19 -0400 Subject: [PATCH 320/323] Importing inspect is expensive. Defer it unless needed. --- importlib_metadata/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index 46203448..832a7116 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -5,7 +5,6 @@ import sys import zipp import email -import inspect import pathlib import operator import warnings @@ -206,6 +205,9 @@ def _from_text_for(cls, text, dist): def flake8_bypass(func): + # defer inspect import as performance optimization. + import inspect + is_flake8 = any('flake8' in str(frame.filename) for frame in inspect.stack()[:5]) return func if not is_flake8 else lambda: None From d176331ff44cb2636a7b285cfc70371890b6d95b Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 14 Mar 2021 10:15:18 -0400 Subject: [PATCH 321/323] Only import collections.abc in doctests. --- importlib_metadata/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/importlib_metadata/__init__.py b/importlib_metadata/__init__.py index 832a7116..112373e5 100644 --- a/importlib_metadata/__init__.py +++ b/importlib_metadata/__init__.py @@ -11,7 +11,7 @@ import functools import itertools import posixpath -import collections.abc +import collections from ._compat import ( NullFinder, @@ -842,6 +842,7 @@ def packages_distributions() -> Mapping[str, List[str]]: Return a mapping of top-level packages to their distributions. + >>> import collections.abc >>> pkgs = packages_distributions() >>> all(isinstance(dist, collections.abc.Sequence) for dist in pkgs.values()) True From da0bc8969757cbe4d1e38aeac837450ff54816f4 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 14 Mar 2021 10:20:41 -0400 Subject: [PATCH 322/323] Update changelog. Ref python/cpython#24782. --- CHANGES.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 2792caf8..5777e56e 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,9 @@ +v3.7.3 +====== + +* Docs enhancements and cleanup following review in + `GH-24782 `_. + v3.7.2 ====== From 5c9198cd8212d56db8ee9c49ee39a0980d11f5ff Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 14 Mar 2021 10:32:55 -0400 Subject: [PATCH 323/323] Indicate code block consistently. --- docs/using.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/using.rst b/docs/using.rst index 53d83959..17d6f590 100644 --- a/docs/using.rst +++ b/docs/using.rst @@ -80,7 +80,7 @@ Query all entry points:: The ``entry_points()`` function returns an ``EntryPoints`` object, a sequence of all ``EntryPoint`` objects with ``names`` and ``groups`` -attributes for convenience. +attributes for convenience:: >>> sorted(eps.groups) ['console_scripts', 'distutils.commands', 'distutils.setup_keywords', 'egg_info.writers', 'setuptools.installation']