diff --git a/.coveragerc b/.coveragerc deleted file mode 100644 index 054b5e59..00000000 --- a/.coveragerc +++ /dev/null @@ -1,19 +0,0 @@ -[run] -omit = - # leading `*/` for pytest-dev/pytest-cov#456 - */.tox/* - */_itertools.py - */_legacy.py - */simple.py - */_path.py -disable_warnings = - couldnt-parse - -[report] -show_missing = True -exclude_also = - # Exclude common false positives per - # https://coverage.readthedocs.io/en/latest/excluding.html#advanced-exclusion - # Ref jaraco/skeleton#97 and jaraco/skeleton#135 - class .*\bProtocol\): - if TYPE_CHECKING: diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index 304196f8..00000000 --- a/.editorconfig +++ /dev/null @@ -1,19 +0,0 @@ -root = true - -[*] -charset = utf-8 -indent_style = tab -indent_size = 4 -insert_final_newline = true -end_of_line = lf - -[*.py] -indent_style = space -max_line_length = 88 - -[*.{yml,yaml}] -indent_style = space -indent_size = 2 - -[*.rst] -indent_style = space diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index e6805180..00000000 --- a/.gitattributes +++ /dev/null @@ -1,2 +0,0 @@ -*.file binary -*.zip binary diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index 8ebda610..00000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1 +0,0 @@ -tidelift: pypi/importlib-resources diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index 89ff3396..00000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,8 +0,0 @@ -version: 2 -updates: - - package-ecosystem: "pip" - directory: "/" - schedule: - interval: "daily" - allow: - - dependency-type: "all" diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml deleted file mode 100644 index 9c01fc4d..00000000 --- a/.github/workflows/main.yml +++ /dev/null @@ -1,125 +0,0 @@ -name: tests - -on: - merge_group: - push: - branches-ignore: - # temporary GH branches relating to merge queues (jaraco/skeleton#93) - - gh-readonly-queue/** - tags: - # required if branches-ignore is supplied (jaraco/skeleton#103) - - '**' - pull_request: - workflow_dispatch: - -permissions: - contents: read - -env: - # Environment variable to support color support (jaraco/skeleton#66) - FORCE_COLOR: 1 - - # Suppress noisy pip warnings - PIP_DISABLE_PIP_VERSION_CHECK: 'true' - PIP_NO_PYTHON_VERSION_WARNING: 'true' - PIP_NO_WARN_SCRIPT_LOCATION: 'true' - - # Ensure tests can sense settings about the environment - TOX_OVERRIDE: >- - testenv.pass_env+=GITHUB_*,FORCE_COLOR - - -jobs: - test: - strategy: - # https://blog.jaraco.com/efficient-use-of-ci-resources/ - matrix: - python: - - "3.9" - - "3.13" - platform: - - ubuntu-latest - - macos-latest - - windows-latest - include: - - python: "3.10" - platform: ubuntu-latest - - python: "3.11" - platform: ubuntu-latest - - python: "3.12" - platform: ubuntu-latest - - python: "3.14" - platform: ubuntu-latest - - python: pypy3.10 - platform: ubuntu-latest - runs-on: ${{ matrix.platform }} - continue-on-error: ${{ matrix.python == '3.14' }} - steps: - - uses: actions/checkout@v4 - - name: Setup Python - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python }} - allow-prereleases: true - - name: Install tox - run: python -m pip install tox - - name: Run - run: tox - - collateral: - strategy: - fail-fast: false - matrix: - job: - - diffcov - - docs - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: Setup Python - uses: actions/setup-python@v4 - with: - python-version: 3.x - - name: Install tox - run: python -m pip install tox - - name: Eval ${{ matrix.job }} - run: tox -e ${{ matrix.job }} - - check: # This job does nothing and is only used for the branch protection - if: always() - - needs: - - test - - collateral - - runs-on: ubuntu-latest - - steps: - - name: Decide whether the needed jobs succeeded or failed - uses: re-actors/alls-green@release/v1 - with: - jobs: ${{ toJSON(needs) }} - - release: - permissions: - contents: write - needs: - - check - if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - - name: Setup Python - uses: actions/setup-python@v4 - with: - python-version: 3.x - - name: Install tox - run: python -m pip install tox - - name: Run - run: tox -e release - env: - TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 87274500..00000000 --- a/.gitignore +++ /dev/null @@ -1,102 +0,0 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -env/ -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -*.egg-info/ -.installed.cfg -*.egg - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -.hypothesis/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# pyenv -.python-version - -# celery beat schedule file -celerybeat-schedule - -# SageMath parsed files -*.sage.py - -# dotenv -.env - -# virtualenv -.venv -venv/ -ENV/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ -/diffcov.html diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml deleted file mode 100644 index 04870d16..00000000 --- a/.pre-commit-config.yaml +++ /dev/null @@ -1,7 +0,0 @@ -repos: -- repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.7.1 - hooks: - - id: ruff - args: [--fix, --unsafe-fixes] - - id: ruff-format diff --git a/.readthedocs.yaml b/.readthedocs.yaml deleted file mode 100644 index dc8516ac..00000000 --- a/.readthedocs.yaml +++ /dev/null @@ -1,16 +0,0 @@ -version: 2 -python: - install: - - path: . - extra_requirements: - - doc - -# required boilerplate readthedocs/readthedocs.org#10401 -build: - os: ubuntu-lts-latest - tools: - python: latest - # post-checkout job to ensure the clone isn't shallow jaraco/skeleton#114 - jobs: - post_checkout: - - git fetch --unshallow || true diff --git a/LICENSE b/LICENSE deleted file mode 100644 index d6456956..00000000 --- a/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/importlib_resources/__init__.py b/Lib/importlib/resources/__init__.py similarity index 100% rename from importlib_resources/__init__.py rename to Lib/importlib/resources/__init__.py diff --git a/importlib_resources/_adapters.py b/Lib/importlib/resources/_adapters.py similarity index 100% rename from importlib_resources/_adapters.py rename to Lib/importlib/resources/_adapters.py diff --git a/importlib_resources/_common.py b/Lib/importlib/resources/_common.py similarity index 99% rename from importlib_resources/_common.py rename to Lib/importlib/resources/_common.py index 5f41c265..438d46ff 100644 --- a/importlib_resources/_common.py +++ b/Lib/importlib/resources/_common.py @@ -111,7 +111,7 @@ def from_package(package: types.ModuleType): """ # deferred for performance (python/cpython#109829) - from .future.adapters import wrap_spec + from ._adapters import wrap_spec spec = wrap_spec(package) reader = spec.loader.get_resource_reader(spec.name) diff --git a/importlib_resources/_functional.py b/Lib/importlib/resources/_functional.py similarity index 100% rename from importlib_resources/_functional.py rename to Lib/importlib/resources/_functional.py diff --git a/importlib_resources/_itertools.py b/Lib/importlib/resources/_itertools.py similarity index 100% rename from importlib_resources/_itertools.py rename to Lib/importlib/resources/_itertools.py diff --git a/importlib_resources/abc.py b/Lib/importlib/resources/abc.py similarity index 100% rename from importlib_resources/abc.py rename to Lib/importlib/resources/abc.py index 883d3328..64a6d843 100644 --- a/importlib_resources/abc.py +++ b/Lib/importlib/resources/abc.py @@ -7,8 +7,8 @@ BinaryIO, Iterable, Iterator, - NoReturn, Literal, + NoReturn, Optional, Protocol, Text, diff --git a/importlib_resources/readers.py b/Lib/importlib/resources/readers.py similarity index 97% rename from importlib_resources/readers.py rename to Lib/importlib/resources/readers.py index 99884b6a..5d0ae46d 100644 --- a/importlib_resources/readers.py +++ b/Lib/importlib/resources/readers.py @@ -7,11 +7,11 @@ import pathlib import re import warnings +import zipfile from collections.abc import Iterator from . import abc from ._itertools import only -from .compat.py39 import ZipPath def remove_duplicates(items): @@ -57,7 +57,7 @@ def is_resource(self, path): return target.is_file() and target.exists() def files(self): - return ZipPath(self.archive, self.prefix) + return zipfile.Path(self.archive, self.prefix) class MultiplexedPath(abc.Traversable): @@ -170,7 +170,7 @@ def _resolve_zip_path(path_str: str): PermissionError, ): inner = path_str[match.end() :].replace('\\', '/') + '/' - yield ZipPath(path_str[: match.start()], inner.lstrip('/')) + yield zipfile.Path(path_str[: match.start()], inner.lstrip('/')) def resource_path(self, resource): """ diff --git a/importlib_resources/simple.py b/Lib/importlib/resources/simple.py similarity index 100% rename from importlib_resources/simple.py rename to Lib/importlib/resources/simple.py diff --git a/importlib_resources/compat/__init__.py b/Lib/test/test_importlib/resources/__init__.py similarity index 100% rename from importlib_resources/compat/__init__.py rename to Lib/test/test_importlib/resources/__init__.py diff --git a/importlib_resources/tests/_path.py b/Lib/test/test_importlib/resources/_path.py similarity index 100% rename from importlib_resources/tests/_path.py rename to Lib/test/test_importlib/resources/_path.py diff --git a/importlib_resources/future/__init__.py b/Lib/test/test_importlib/resources/data01/__init__.py similarity index 100% rename from importlib_resources/future/__init__.py rename to Lib/test/test_importlib/resources/data01/__init__.py diff --git a/importlib_resources/tests/__init__.py b/Lib/test/test_importlib/resources/data01/subdirectory/__init__.py similarity index 100% rename from importlib_resources/tests/__init__.py rename to Lib/test/test_importlib/resources/data01/subdirectory/__init__.py diff --git a/importlib_resources/tests/compat/__init__.py b/Lib/test/test_importlib/resources/data02/__init__.py similarity index 100% rename from importlib_resources/tests/compat/__init__.py rename to Lib/test/test_importlib/resources/data02/__init__.py diff --git a/importlib_resources/py.typed b/Lib/test/test_importlib/resources/data02/one/__init__.py similarity index 100% rename from importlib_resources/py.typed rename to Lib/test/test_importlib/resources/data02/one/__init__.py diff --git a/Lib/test/test_importlib/resources/data02/two/__init__.py b/Lib/test/test_importlib/resources/data02/two/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/Lib/test/test_importlib/resources/data03/__init__.py b/Lib/test/test_importlib/resources/data03/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/Lib/test/test_importlib/resources/data03/namespace/portion1/__init__.py b/Lib/test/test_importlib/resources/data03/namespace/portion1/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/Lib/test/test_importlib/resources/data03/namespace/portion2/__init__.py b/Lib/test/test_importlib/resources/data03/namespace/portion2/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/Lib/test/test_importlib/resources/data03/namespace/resource1.txt b/Lib/test/test_importlib/resources/data03/namespace/resource1.txt new file mode 100644 index 00000000..e69de29b diff --git a/importlib_resources/tests/test_compatibilty_files.py b/Lib/test/test_importlib/resources/test_compatibilty_files.py similarity index 97% rename from importlib_resources/tests/test_compatibilty_files.py rename to Lib/test/test_importlib/resources/test_compatibilty_files.py index e8aac284..113f9ae6 100644 --- a/importlib_resources/tests/test_compatibilty_files.py +++ b/Lib/test/test_importlib/resources/test_compatibilty_files.py @@ -1,8 +1,7 @@ +import importlib.resources as resources import io import unittest - -import importlib_resources as resources -from importlib_resources._adapters import ( +from importlib.resources._adapters import ( CompatibilityFiles, wrap_spec, ) diff --git a/importlib_resources/tests/test_contents.py b/Lib/test/test_importlib/resources/test_contents.py similarity index 95% rename from importlib_resources/tests/test_contents.py rename to Lib/test/test_importlib/resources/test_contents.py index dcb872ec..bdc158d8 100644 --- a/importlib_resources/tests/test_contents.py +++ b/Lib/test/test_importlib/resources/test_contents.py @@ -1,7 +1,6 @@ +import importlib.resources as resources import unittest -import importlib_resources as resources - from . import util diff --git a/importlib_resources/tests/test_custom.py b/Lib/test/test_importlib/resources/test_custom.py similarity index 87% rename from importlib_resources/tests/test_custom.py rename to Lib/test/test_importlib/resources/test_custom.py index 25ae0e75..97ba55c1 100644 --- a/importlib_resources/tests/test_custom.py +++ b/Lib/test/test_importlib/resources/test_custom.py @@ -1,13 +1,11 @@ import contextlib +import importlib.resources as resources import pathlib import unittest +from importlib.resources import abc +from importlib.resources.abc import ResourceReader, TraversableResources -import importlib_resources as resources - -from .. import abc -from ..abc import ResourceReader, TraversableResources from . import util -from .compat.py39 import os_helper class SimpleLoader: diff --git a/importlib_resources/tests/test_files.py b/Lib/test/test_importlib/resources/test_files.py similarity index 97% rename from importlib_resources/tests/test_files.py rename to Lib/test/test_importlib/resources/test_files.py index be206603..8644de48 100644 --- a/importlib_resources/tests/test_files.py +++ b/Lib/test/test_importlib/resources/test_files.py @@ -1,16 +1,16 @@ import contextlib import importlib +import importlib.resources as resources import pathlib import py_compile import textwrap import unittest import warnings +from importlib.resources.abc import Traversable -import importlib_resources as resources +from test.support import import_helper, os_helper -from ..abc import Traversable from . import util -from .compat.py39 import import_helper, os_helper @contextlib.contextmanager diff --git a/importlib_resources/tests/test_functional.py b/Lib/test/test_importlib/resources/test_functional.py similarity index 99% rename from importlib_resources/tests/test_functional.py rename to Lib/test/test_importlib/resources/test_functional.py index 9eb2d815..9e1a3a0e 100644 --- a/importlib_resources/tests/test_functional.py +++ b/Lib/test/test_importlib/resources/test_functional.py @@ -1,11 +1,11 @@ import importlib +import importlib.resources as resources import os import unittest -import importlib_resources as resources +from test.support import warnings_helper from . import util -from .compat.py39 import warnings_helper class StringAnchorMixin: diff --git a/importlib_resources/tests/test_open.py b/Lib/test/test_importlib/resources/test_open.py similarity index 98% rename from importlib_resources/tests/test_open.py rename to Lib/test/test_importlib/resources/test_open.py index 8a4b68e3..b5a8949d 100644 --- a/importlib_resources/tests/test_open.py +++ b/Lib/test/test_importlib/resources/test_open.py @@ -1,7 +1,6 @@ +import importlib.resources as resources import unittest -import importlib_resources as resources - from . import util diff --git a/importlib_resources/tests/test_path.py b/Lib/test/test_importlib/resources/test_path.py similarity index 89% rename from importlib_resources/tests/test_path.py rename to Lib/test/test_importlib/resources/test_path.py index 0be673d2..3d158d95 100644 --- a/importlib_resources/tests/test_path.py +++ b/Lib/test/test_importlib/resources/test_path.py @@ -1,9 +1,8 @@ +import importlib.resources as resources import io import pathlib import unittest -import importlib_resources as resources - from . import util @@ -27,11 +26,9 @@ def test_reading(self): class PathDiskTests(PathTests, util.DiskSetup, unittest.TestCase): def test_natural_path(self): - """ - Guarantee the internal implementation detail that - file-system-backed resources do not get the tempdir - treatment. - """ + # Guarantee the internal implementation detail that + # file-system-backed resources do not get the tempdir + # treatment. target = resources.files(self.data) / 'utf-8.file' with resources.as_file(target) as path: assert 'data' in str(path) diff --git a/importlib_resources/tests/test_read.py b/Lib/test/test_importlib/resources/test_read.py similarity index 98% rename from importlib_resources/tests/test_read.py rename to Lib/test/test_importlib/resources/test_read.py index 216c8feb..cd1cc6dd 100644 --- a/importlib_resources/tests/test_read.py +++ b/Lib/test/test_importlib/resources/test_read.py @@ -1,8 +1,7 @@ +import importlib.resources as resources import unittest from importlib import import_module -import importlib_resources as resources - from . import util diff --git a/importlib_resources/tests/test_reader.py b/Lib/test/test_importlib/resources/test_reader.py similarity index 98% rename from importlib_resources/tests/test_reader.py rename to Lib/test/test_importlib/resources/test_reader.py index f8cfd8de..cf23f38f 100644 --- a/importlib_resources/tests/test_reader.py +++ b/Lib/test/test_importlib/resources/test_reader.py @@ -2,8 +2,7 @@ import pathlib import unittest from importlib import import_module - -from importlib_resources.readers import MultiplexedPath, NamespaceReader +from importlib.resources.readers import MultiplexedPath, NamespaceReader from . import util diff --git a/importlib_resources/tests/test_resource.py b/Lib/test/test_importlib/resources/test_resource.py similarity index 99% rename from importlib_resources/tests/test_resource.py rename to Lib/test/test_importlib/resources/test_resource.py index c80afdc7..ef69cd04 100644 --- a/importlib_resources/tests/test_resource.py +++ b/Lib/test/test_importlib/resources/test_resource.py @@ -1,8 +1,7 @@ +import importlib.resources as resources import unittest from importlib import import_module -import importlib_resources as resources - from . import util diff --git a/importlib_resources/tests/test_util.py b/Lib/test/test_importlib/resources/test_util.py similarity index 100% rename from importlib_resources/tests/test_util.py rename to Lib/test/test_importlib/resources/test_util.py diff --git a/importlib_resources/tests/util.py b/Lib/test/test_importlib/resources/util.py similarity index 98% rename from importlib_resources/tests/util.py rename to Lib/test/test_importlib/resources/util.py index 0340c150..d6a99289 100644 --- a/importlib_resources/tests/util.py +++ b/Lib/test/test_importlib/resources/util.py @@ -7,11 +7,12 @@ import sys import types from importlib.machinery import ModuleSpec +from importlib.resources.abc import ResourceReader, Traversable, TraversableResources + +from test.support import import_helper, os_helper -from ..abc import ResourceReader, Traversable, TraversableResources from . import _path from . import zip as zip_ -from .compat.py39 import import_helper, os_helper class Reader(ResourceReader): diff --git a/importlib_resources/tests/zip.py b/Lib/test/test_importlib/resources/zip.py similarity index 91% rename from importlib_resources/tests/zip.py rename to Lib/test/test_importlib/resources/zip.py index 51ee5648..fc453f02 100755 --- a/importlib_resources/tests/zip.py +++ b/Lib/test/test_importlib/resources/zip.py @@ -4,8 +4,6 @@ import zipfile -import zipp - def make_zip_file(tree, dst): """ @@ -14,7 +12,7 @@ def make_zip_file(tree, dst): with zipfile.ZipFile(dst, 'w') as zf: for name, contents in walk(tree): zf.writestr(name, contents) - zipp.CompleteDirs.inject(zf) + zipfile._path.CompleteDirs.inject(zf) return dst diff --git a/Lib/test/test_importlib/resources/zipdata01/__init__.py b/Lib/test/test_importlib/resources/zipdata01/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/Lib/test/test_importlib/resources/zipdata02/__init__.py b/Lib/test/test_importlib/resources/zipdata02/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/NEWS.rst b/NEWS.rst deleted file mode 100644 index a7c95e80..00000000 --- a/NEWS.rst +++ /dev/null @@ -1,636 +0,0 @@ -v6.5.2 -====== - -Bugfixes --------- - -- Replaced reference to typing_extensions with stdlib Literal. (#323) - - -v6.5.1 -====== - -Bugfixes --------- - -- Updated ``Traversable.read_text()`` to reflect the ``errors`` parameter (python/cpython#127012). (#321) - - -v6.5.0 -====== - -Features --------- - -- Add type annotations for Traversable.open. (#317) -- Require Python 3.9 or later. - - -v6.4.5 -====== - -Bugfixes --------- - -- Omit sentinel values from a namespace path. (#311) - - -v6.4.4 -====== - -No significant changes. - - -v6.4.3 -====== - -Bugfixes --------- - -- When inferring the caller in ``files()`` correctly detect one's own module even when the resources package source is not present. (python/cpython#123085) - - -v6.4.2 -====== - -Bugfixes --------- - -- Merged fix for UTF-16 BOM handling in functional tests. (#312) - - -v6.4.1 -====== - -Bugfixes --------- - -- When constructing ZipReader, only append the name if the indicated module is a package. (python/cpython#121735) - - -v6.4.0 -====== - -Features --------- - -- The functions - ``is_resource()``, - ``open_binary()``, - ``open_text()``, - ``path()``, - ``read_binary()``, and - ``read_text()`` are un-deprecated, and support - subdirectories via multiple positional arguments. - The ``contents()`` function also allows subdirectories, - but remains deprecated. (#303) -- Deferred select imports in for a speedup (python/cpython#109829). - - -v6.3.2 -====== - -Bugfixes --------- - -- Restored expectation that local standard readers are preferred over degenerate readers. (#298) - - -v6.3.1 -====== - -Bugfixes --------- - -- Restored expectation that stdlib readers are suppressed on Python 3.10. (#257) - - -v6.3.0 -====== - -Features --------- - -- Add ``Anchor`` to ``importlib.resources`` (in order for the code to comply with the documentation) - - -v6.2.0 -====== - -Features --------- - -- Future compatibility adapters now ensure that standard library readers are replaced without overriding non-standard readers. (#295) - - -v6.1.3 -====== - -No significant changes. - - -v6.1.2 -====== - -Bugfixes --------- - -- Fixed NotADirectoryError when calling files on a subdirectory of a namespace package. (#293) - - -v6.1.1 -====== - -Bugfixes --------- - -- Added missed stream argument in simple.ResourceHandle. Ref python/cpython#111775. - - -v6.1.0 -====== - -Features --------- - -- MultiplexedPath now expects Traversable paths. String arguments to MultiplexedPath are now deprecated. - - -Bugfixes --------- - -- Enabled support for resources in namespace packages in zip files. (#287) - - -v6.0.1 -====== - -Bugfixes --------- - -- Restored Apache license. (#285) - - -v6.0.0 -====== - -Deprecations and Removals -------------------------- - -- Removed legacy functions deprecated in 5.3. (#80) - - -v5.13.0 -======= - -Features --------- - -- Require Python 3.8 or later. - - -v5.12.0 -======= - -* #257: ``importlib_resources`` (backport) now gives - precedence to built-in readers (file system, zip, - namespace packages), providing forward-compatibility - of behaviors like ``MultiplexedPath``. - -v5.11.1 -======= - -v5.10.4 -======= - -* #280: Fixed one more ``EncodingWarning`` in test suite. - -v5.11.0 -======= - -* #265: ``MultiplexedPath`` now honors multiple subdirectories - in ``iterdir`` and ``joinpath``. - -v5.10.3 -======= - -* Packaging refresh, including fixing EncodingWarnings - and some tests cleanup. - -v5.10.2 -======= - -* #274: Prefer ``write_bytes`` to context manager as - proposed in gh-100586. - -v5.10.1 -======= - -* #274: Fixed ``ResourceWarning`` in ``_common``. - -v5.10.0 -======= - -* #203: Lifted restriction on modules passed to ``files``. - Now modules need not be a package and if a non-package - module is passed, resources will be resolved adjacent to - those modules, even for modules not found in any package. - For example, ``files(import_module('mod.py'))`` will - resolve resources found at the root. The parameter to - files was renamed from 'package' to 'anchor', with a - compatibility shim for those passing by keyword. - -* #259: ``files`` no longer requires the anchor to be - specified and can infer the anchor from the caller's scope - (defaults to the caller's module). - -v5.9.0 -====== - -* #228: ``as_file`` now also supports a ``Traversable`` - representing a directory and (when needed) renders the - full tree to a temporary directory. - -v5.8.1 -====== - -* #253: In ``MultiplexedPath``, restore expectation that - a compound path with a non-existent directory does not - raise an exception. - -v5.8.0 -====== - -* #250: Now ``Traversable.joinpath`` provides a concrete - implementation, replacing the implementation in ``.simple`` - and converging with the behavior in ``MultiplexedPath``. - -v5.7.1 -====== - -* #249: In ``simple.ResourceContainer.joinpath``, honor - names split by ``posixpath.sep``. - -v5.7.0 -====== - -* #248: ``abc.Traversable.joinpath`` now allows for multiple - arguments and specifies that ``posixpath.sep`` is allowed - in any argument to accept multiple arguments, matching the - behavior found in ``zipfile.Path`` and ``pathlib.Path``. - - ``simple.ResourceContainer`` now honors this behavior. - -v5.6.0 -====== - -* #244: Add type declarations in ABCs. - -v5.5.0 -====== - -* Require Python 3.7 or later. -* #243: Fix error when no ``__pycache__`` directories exist - when testing ``update-zips``. - -v5.4.0 -====== - -* #80: Test suite now relies entirely on the traversable - API. - -v5.3.0 -====== - -* #80: Now raise a ``DeprecationWarning`` for all legacy - functions. Instead, users should rely on the ``files()`` - API introduced in importlib_resources 1.3. See - `Migrating from Legacy `_ - for guidance on avoiding the deprecated functions. - -v5.2.3 -====== - -* Updated readme to reflect current behavior and show - which versions correspond to which behavior in CPython. - -v5.0.7 -====== - -* bpo-45419: Correct ``DegenerateFiles.Path`` ``.name`` - and ``.open()`` interfaces to match ``Traversable``. - -v5.2.2 -====== - -* #234: Fix refleak in ``as_file`` caught by CPython tests. - -v5.2.1 -====== - -* bpo-38291: Avoid DeprecationWarning on ``typing.io``. - -v5.2.0 -====== - -* #80 via #221: Legacy API (``path``, ``contents``, ...) - is now supported entirely by the ``.files()`` API with - a compatibility shim supplied for resource loaders without - that functionality. - -v5.0.6 -====== - -* bpo-38693: Prefer f-strings to ``.format`` calls. - -v5.1.4 -====== - -* #225: Require - `zipp 3.1.0 `_ - or later on Python prior to 3.10 to incorporate those fixes. - -v5.0.5 -====== - -* #216: Make MultiplexedPath.name a property per the - spec. - -v5.1.3 -====== - -* Refresh packaging and improve tests. -* #216: Make MultiplexedPath.name a property per the - spec. - -v5.1.2 -====== - -* Re-release with changes from 5.0.4. - -v5.0.4 -====== - -* Fixed non-hermetic test in test_reader, revealed by - GH-24670. - -v5.1.1 -====== - -* Re-release with changes from 5.0.3. - -v5.0.3 -====== - -* Simplified DegenerateFiles.Path. - -v5.0.2 -====== - -* #214: Added ``_adapters`` module to ensure that degenerate - ``files`` behavior can be made available for legacy loaders - whose resource readers don't implement it. Fixes issue where - backport compatibility module was masking this fallback - behavior only to discover the defect when applying changes to - CPython. - -v5.1.0 -====== - -* Added ``simple`` module implementing adapters from - a low-level resource reader interface to a - ``TraversableResources`` interface. Closes #90. - -v5.0.1 -====== - -* Remove pyinstaller hook for hidden 'trees' module. - -v5.0.0 -====== - -* Removed ``importlib_resources.trees``, deprecated since 1.3.0. - -v4.1.1 -====== - -* Fixed badges in README. - -v4.1.0 -====== - -* #209: Adopt - `jaraco/skeleton `_. - -* Cleaned up some straggling Python 2 compatibility code. - -* Refreshed test zip files without .pyc and .pyo files. - -v4.0.0 -====== - -* #108: Drop support for Python 2.7. Now requires Python 3.6+. - -v3.3.1 -====== - -* Minor cleanup. - -v3.3.0 -====== - -* #107: Drop support for Python 3.5. Now requires Python 2.7 or 3.6+. - -v3.2.1 -====== - -* #200: Minor fixes and improved tests for namespace package support. - -v3.2.0 -====== - -* #68: Resources in PEP 420 Namespace packages are now supported. - -v3.1.1 -====== - -* bpo-41490: ``contents`` is now also more aggressive about - consuming any iterator from the ``Reader``. - -v3.1.0 -====== - -* #110 and bpo-41490: ``path`` method is more aggressive about - releasing handles to zipfile objects early, enabling use-cases - like ``certifi`` to leave the context open but delete the underlying - zip file. - -v3.0.0 -====== - -* Package no longer exposes ``importlib_resources.__version__``. - Users that wish to inspect the version of ``importlib_resources`` - should instead invoke ``.version('importlib_resources')`` from - ``importlib-metadata`` ( - `stdlib `_ - or `backport `_) - directly. This change eliminates the dependency on - ``importlib_metadata``. Closes #100. -* Package now always includes its data. Closes #93. -* Declare hidden imports for PyInstaller. Closes #101. - -v2.0.1 -====== - -* Select pathlib and contextlib imports based on Python version - and avoid pulling in deprecated - [pathlib](https://pypi.org/project/pathlib). Closes #97. - -v2.0.0 -====== - -* Loaders are no longer expected to implement the - ``abc.TraversableResources`` interface, but are instead - expected to return ``TraversableResources`` from their - ``get_resource_reader`` method. - -v1.5.0 -====== - -* Traversable is now a Protocol instead of an Abstract Base - Class (Python 2.7 and Python 3.8+). - -* Traversable objects now require a ``.name`` property. - -v1.4.0 -====== - -* #79: Temporary files created will now reflect the filename of - their origin. - -v1.3.1 -====== - -* For improved compatibility, ``importlib_resources.trees`` is - now imported implicitly. Closes #88. - -v1.3.0 -====== - -* Add extensibility support for non-standard loaders to supply - ``Traversable`` resources. Introduces a new abstract base - class ``abc.TraversableResources`` that supersedes (but - implements for compatibility) ``abc.ResourceReader``. Any - loader that implements (implicitly or explicitly) the - ``TraversableResources.files`` method will be capable of - supplying resources with subdirectory support. Closes #77. -* Preferred way to access ``as_file`` is now from top-level module. - ``importlib_resources.trees.as_file`` is deprecated and discouraged. - Closes #86. -* Moved ``Traversable`` abc to ``abc`` module. Closes #87. - -v1.2.0 -====== - -* Traversable now requires an ``open`` method. Closes #81. -* Fixed error on ``Python 3.5.{0,3}``. Closes #83. -* Updated packaging to resolve version from package metadata. - Closes #82. - -v1.1.0 -====== - -* Add support for retrieving resources from subdirectories of packages - through the new ``files()`` function, which returns a ``Traversable`` - object with ``joinpath`` and ``read_*`` interfaces matching those - of ``pathlib.Path`` objects. This new function supersedes all of the - previous functionality as it provides a more general-purpose access - to a package's resources. - - With this function, subdirectories are supported (Closes #58). - - The - documentation has been updated to reflect that this function is now - the preferred interface for loading package resources. It does not, - however, support resources from arbitrary loaders. It currently only - supports resources from file system path and zipfile packages (a - consequence of the ResourceReader interface only operating on - Python packages). - -1.0.2 -===== - -* Fix ``setup_requires`` and ``install_requires`` metadata in ``setup.cfg``. - Given by Anthony Sottile. - -1.0.1 -===== - -* Update Trove classifiers. Closes #63 - -1.0 -=== - -* Backport fix for test isolation from Python 3.8/3.7. Closes #61 - -0.8 -=== - -* Strip ``importlib_resources.__version__``. Closes #56 -* Fix a metadata problem with older setuptools. Closes #57 -* Add an ``__all__`` to ``importlib_resources``. Closes #59 - -0.7 -=== - -* Fix ``setup.cfg`` metadata bug. Closes #55 - -0.6 -=== - -* Move everything from ``pyproject.toml`` to ``setup.cfg``, with the added - benefit of fixing the PyPI metadata. Closes #54 -* Turn off mypy's ``strict_optional`` setting for now. - -0.5 -=== - -* Resynchronize with Python 3.7; changes the return type of ``contents()`` to - be an ``Iterable``. Closes #52 - -0.4 -=== - -* Correctly find resources in subpackages inside a zip file. Closes #51 - -0.3 -=== - -* The API, implementation, and documentation is synchronized with the Python - 3.7 standard library. Closes #47 -* When run under Python 3.7 this API shadows the stdlib versions. Closes #50 - -0.2 -=== - -* **Backward incompatible change**. Split the ``open()`` and ``read()`` calls - into separate binary and text versions, i.e. ``open_binary()``, - ``open_text()``, ``read_binary()``, and ``read_text()``. Closes #41 -* Fix a bug where unrelated resources could be returned from ``contents()``. - Closes #44 -* Correctly prevent namespace packages from containing resources. Closes #20 - -0.1 -=== - -* Initial release. - - -.. - Local Variables: - mode: change-log-mode - indent-tabs-mode: nil - sentence-end-double-space: t - fill-column: 78 - coding: utf-8 - End: diff --git a/README.rst b/README.rst deleted file mode 100644 index 4f9fa024..00000000 --- a/README.rst +++ /dev/null @@ -1,66 +0,0 @@ -.. image:: https://img.shields.io/pypi/v/importlib_resources.svg - :target: https://pypi.org/project/importlib_resources - -.. image:: https://img.shields.io/pypi/pyversions/importlib_resources.svg - -.. image:: https://github.com/python/importlib_resources/actions/workflows/main.yml/badge.svg - :target: https://github.com/python/importlib_resources/actions?query=workflow%3A%22tests%22 - :alt: tests - -.. image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v2.json - :target: https://github.com/astral-sh/ruff - :alt: Ruff - -.. image:: https://readthedocs.org/projects/importlib-resources/badge/?version=latest - :target: https://importlib-resources.readthedocs.io/en/latest/?badge=latest - -.. image:: https://img.shields.io/badge/skeleton-2025-informational - :target: https://blog.jaraco.com/skeleton - -.. image:: https://tidelift.com/badges/package/pypi/importlib-resources - :target: https://tidelift.com/subscription/pkg/pypi-importlib-resources?utm_source=pypi-importlib-resources&utm_medium=readme - -``importlib_resources`` is a backport of Python standard library -`importlib.resources -`_ -module for older Pythons. - -The key goal of this module is to replace parts of `pkg_resources -`_ with a -solution in Python's stdlib that relies on well-defined APIs. This makes -reading resources included in packages easier, with more stable and consistent -semantics. - -Compatibility -============= - -New features are introduced in this third-party library and later merged -into CPython. The following table indicates which versions of this library -were contributed to different versions in the standard library: - -.. list-table:: - :header-rows: 1 - - * - importlib_resources - - stdlib - * - 6.0 - - 3.13 - * - 5.12 - - 3.12 - * - 5.7 - - 3.11 - * - 5.0 - - 3.10 - * - 1.3 - - 3.9 - * - 0.5 (?) - - 3.7 - -For Enterprise -============== - -Available as part of the Tidelift Subscription. - -This project and the maintainers of thousands of other packages are working with Tidelift to deliver one enterprise subscription that covers all of the open source you use. - -`Learn more `_. diff --git a/SECURITY.md b/SECURITY.md deleted file mode 100644 index 54f99acb..00000000 --- a/SECURITY.md +++ /dev/null @@ -1,3 +0,0 @@ -# Security Contact - -To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure. diff --git a/codecov.yml b/codecov.yml deleted file mode 100644 index 66c7f4bd..00000000 --- a/codecov.yml +++ /dev/null @@ -1,2 +0,0 @@ -codecov: - token: 5eb1bc45-1b7f-43e6-8bc1-f2b02833dba9 diff --git a/docs/api.rst b/docs/api.rst deleted file mode 100644 index 739be9ed..00000000 --- a/docs/api.rst +++ /dev/null @@ -1,26 +0,0 @@ -============= -API Reference -============= - -``importlib_resources`` module ------------------------------- - -.. automodule:: importlib_resources - :members: - :undoc-members: - :show-inheritance: - -.. automodule:: importlib_resources.abc - :members: - :undoc-members: - :show-inheritance: - -.. automodule:: importlib_resources.readers - :members: - :undoc-members: - :show-inheritance: - -.. automodule:: importlib_resources.simple - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/conf.py b/docs/conf.py deleted file mode 100644 index 570346b6..00000000 --- a/docs/conf.py +++ /dev/null @@ -1,71 +0,0 @@ -from __future__ import annotations - -extensions = [ - 'sphinx.ext.autodoc', - 'jaraco.packaging.sphinx', -] - -master_doc = "index" -html_theme = "furo" - -# Link dates and other references in the changelog -extensions += ['rst.linker'] -link_files = { - '../NEWS.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://peps.python.org/pep-{pep_number:0>4}/', - ), - dict( - pattern=r'(python/cpython#|Python #)(?P\d+)', - url='https://github.com/python/cpython/issues/{python}', - ), - dict( - pattern=r'bpo-(?P\d+)', - url='http://bugs.python.org/issue{bpo}', - ), - ], - ), -} - -# Be strict about any broken references -nitpicky = True -nitpick_ignore: list[tuple[str, str]] = [] - -# Include Python intersphinx mapping to prevent failures -# jaraco/skeleton#51 -extensions += ['sphinx.ext.intersphinx'] -intersphinx_mapping = { - 'python': ('https://docs.python.org/3', None), -} - -# Preserve authored syntax for defaults -autodoc_preserve_defaults = True - -# Add support for linking usernames, PyPI projects, Wikipedia pages -github_url = 'https://github.com/' -extlinks = { - 'user': (f'{github_url}%s', '@%s'), - 'pypi': ('https://pypi.org/project/%s', '%s'), - 'wiki': ('https://wikipedia.org/wiki/%s', '%s'), -} -extensions += ['sphinx.ext.extlinks'] - -# local - -extensions += ['jaraco.tidelift'] - -nitpick_ignore.extend([ - ('py:class', 'module'), - ('py:class', '_io.BufferedReader'), -]) diff --git a/docs/history.rst b/docs/history.rst deleted file mode 100644 index 5bdc2320..00000000 --- a/docs/history.rst +++ /dev/null @@ -1,8 +0,0 @@ -:tocdepth: 2 - -.. _changes: - -History -******* - -.. include:: ../NEWS (links).rst diff --git a/docs/index.rst b/docs/index.rst deleted file mode 100644 index eee051cc..00000000 --- a/docs/index.rst +++ /dev/null @@ -1,52 +0,0 @@ -Welcome to |project| documentation! -=================================== - -.. sidebar-links:: - :home: - :pypi: - -``importlib_resources`` is a library which provides for access to *resources* -in Python packages. It provides functionality similar to ``pkg_resources`` -`Basic Resource Access`_ API, but without all of the overhead and performance -problems of ``pkg_resources``. - -In our terminology, a *resource* is a file tree that is located alongside an -importable `Python module`_. Resources can live on the file system or in a -zip file, with support for other loader_ classes that implement the appropriate -API for reading resources. - -``importlib_resources`` supplies a backport of :mod:`importlib.resources`, -enabling early access to features of future Python versions and making -functionality available for older Python versions. Users are encouraged to -use the Python standard library where suitable and fall back to -this library for future compatibility. Developers looking for detailed API -descriptions should refer to the standard library documentation. - -The documentation here includes a general :ref:`usage ` guide and a -:ref:`migration ` guide for projects that want to adopt -``importlib_resources`` instead of ``pkg_resources``. - - -.. toctree:: - :maxdepth: 2 - :caption: Contents: - - using - api - migration - history - -.. tidelift-referral-banner:: - - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` - - -.. _`Basic Resource Access`: http://setuptools.readthedocs.io/en/latest/pkg_resources.html#basic-resource-access -.. _`Python module`: https://docs.python.org/3/glossary.html#term-module -.. _loader: https://docs.python.org/3/reference/import.html#finders-and-loaders diff --git a/docs/migration.rst b/docs/migration.rst deleted file mode 100644 index 5121d068..00000000 --- a/docs/migration.rst +++ /dev/null @@ -1,155 +0,0 @@ -.. _migration: - -================= - Migration guide -================= - -The following guide will help you migrate common ``pkg_resources`` APIs to -``importlib_resources``. Only a small number of the most common APIs are -supported by ``importlib_resources``, so projects that use other features -(e.g. entry points) will have to find other solutions. -``importlib_resources`` primarily supports the following `basic resource -access`_ APIs: - -* ``pkg_resources.resource_filename()`` -* ``pkg_resources.resource_stream()`` -* ``pkg_resources.resource_string()`` -* ``pkg_resources.resource_listdir()`` -* ``pkg_resources.resource_isdir()`` - -Note that although the steps below provide a drop-in replacement for the -above methods, for many use-cases, a better approach is to use the -``Traversable`` path from ``files()`` directly. - - -pkg_resources.resource_filename() -================================= - -``resource_filename()`` is one of the more interesting APIs because it -guarantees that the return value names a file on the file system. This means -that if the resource is in a zip file, ``pkg_resources`` will extract the -file and return the name of the temporary file it created. The problem is -that ``pkg_resources`` also *implicitly* cleans up this temporary file, -without control over its lifetime by the programmer. - -``importlib_resources`` takes a different approach. Its equivalent API is the -``files()`` function, which returns a Traversable object implementing a -subset of the -:py:class:`pathlib.Path` interface suitable for reading the contents and -provides a wrapper for creating a temporary file on the system in a -context whose lifetime is managed by the user. Note though -that if the resource is *already* on the file system, ``importlib_resources`` -still returns a context manager, but nothing needs to get cleaned up. - -Here's an example from ``pkg_resources``:: - - path = pkg_resources.resource_filename('my.package', 'resource.dat') - -The best way to convert this is with the following idiom:: - - ref = importlib_resources.files('my.package') / 'resource.dat' - with importlib_resources.as_file(ref) as path: - # Do something with path. After the with-statement exits, any - # temporary file created will be immediately cleaned up. - -That's all fine if you only need the file temporarily, but what if you need it -to stick around for a while? One way of doing this is to use an -:py:class:`contextlib.ExitStack` instance and manage the resource explicitly:: - - from contextlib import ExitStack - file_manager = ExitStack() - ref = importlib_resources.files('my.package') / 'resource.dat' - path = file_manager.enter_context( - importlib_resources.as_file(ref)) - -Now ``path`` will continue to exist until you explicitly call -``file_manager.close()``. What if you want the file to exist until the -process exits, or you can't pass ``file_manager`` around in your code? Use an -:py:mod:`atexit` handler:: - - import atexit - file_manager = ExitStack() - atexit.register(file_manager.close) - ref = importlib_resources.files('my.package') / 'resource.dat' - path = file_manager.enter_context( - importlib_resources.as_file(ref)) - -Assuming your Python interpreter exits gracefully, the temporary file will be -cleaned up when Python exits. - - -pkg_resources.resource_stream() -=============================== - -``pkg_resources.resource_stream()`` returns a readable file-like object opened -in binary mode. When you read from the returned file-like object, you get -bytes. E.g.:: - - with pkg_resources.resource_stream('my.package', 'resource.dat') as fp: - my_bytes = fp.read() - -The equivalent code in ``importlib_resources`` is pretty straightforward:: - - ref = importlib_resources.files('my.package').joinpath('resource.dat') - with ref.open('rb') as fp: - my_bytes = fp.read() - - -pkg_resources.resource_string() -=============================== - -In Python 2, ``pkg_resources.resource_string()`` returns the contents of a -resource as a ``str``. In Python 3, this function is a misnomer; it actually -returns the contents of the named resource as ``bytes``. That's why the -following example is often written for clarity as:: - - from pkg_resources import resource_string as resource_bytes - contents = resource_bytes('my.package', 'resource.dat') - -This can be easily rewritten like so:: - - ref = importlib_resources.files('my.package').joinpath('resource.dat') - contents = ref.read_bytes() - - -pkg_resources.resource_listdir() -================================ - -This function lists the entries in the package, both files and directories, -but it does not recurse into subdirectories, e.g.:: - - for entry in pkg_resources.resource_listdir('my.package', 'subpackage'): - print(entry) - -This is easily rewritten using the following idiom:: - - for entry in importlib_resources.files('my.package.subpackage').iterdir(): - print(entry.name) - -Note: - -* ``Traversable.iterdir()`` returns *all* the entries in the - subpackage, i.e. both resources (files) and non-resources (directories). -* ``Traversable.iterdir()`` returns additional traversable objects, which if - directories can also be iterated over (recursively). -* ``Traversable.iterdir()``, like ``pathlib.Path`` returns an iterator, not a - concrete sequence. -* The order in which the elements are returned is undefined. - - -pkg_resources.resource_isdir() -============================== - -You can ask ``pkg_resources`` to tell you whether a particular resource inside -a package is a directory or not:: - - if pkg_resources.resource_isdir('my.package', 'resource'): - print('A directory') - -The ``importlib_resources`` equivalent is straightforward:: - - if importlib_resources.files('my.package').joinpath('resource').is_dir(): - print('A directory') - - -.. _`basic resource access`: http://setuptools.readthedocs.io/en/latest/pkg_resources.html#basic-resource-access diff --git a/docs/using.rst b/docs/using.rst deleted file mode 100644 index 0c66810f..00000000 --- a/docs/using.rst +++ /dev/null @@ -1,207 +0,0 @@ -.. _using: - -=========================== - Using importlib_resources -=========================== - -``importlib_resources`` is a library that leverages Python's import system to -provide access to *resources* within *packages* and alongside *modules*. Given -that this library is built on top of the import system, it is highly efficient -and easy to use. This library's philosophy is that, if one can import a -module, one can access resources associated with that module. Resources can be -opened or read, in either binary or text mode. - -What exactly do we mean by "a resource"? It's easiest to think about the -metaphor of files and directories on the file system, though it's important to -keep in mind that this is just a metaphor. Resources and packages **do not** -have to exist as physical files and directories on the file system. - -If you have a file system layout such as:: - - data/ - __init__.py - one/ - __init__.py - resource1.txt - module1.py - resources1/ - resource1.1.txt - two/ - __init__.py - resource2.txt - standalone.py - resource3.txt - -then the directories are ``data``, ``data/one``, and ``data/two``. Each of -these are also Python packages by virtue of the fact that they all contain -``__init__.py`` files. That means that in Python, all of these import -statements work:: - - import data - import data.one - from data import two - -Each import statement gives you a Python *module* corresponding to the -``__init__.py`` file in each of the respective directories. These modules are -packages since packages are just special module instances that have an -additional attribute, namely a ``__path__`` [#fn1]_. - -In this analogy then, resources are just files or directories contained in a -package directory, so -``data/one/resource1.txt`` and ``data/two/resource2.txt`` are both resources, -as are the ``__init__.py`` files in all the directories. - -Resources in packages are always accessed relative to the package that they -live in. ``resource1.txt`` and ``resources1/resource1.1.txt`` are resources -within the ``data.one`` package, and ``two/resource2.txt`` is a resource -within the ``data`` package. - -Resources may also be referenced relative to another *anchor*, a module in a -package (``data.one.module1``) or a standalone module (``standalone``). In -this case, resources are loaded from the same loader that loaded that module. - - -Example -======= - -Let's say you are writing an email parsing library and in your test suite you -have a sample email message in a file called ``message.eml``. You would like -to access the contents of this file for your tests, so you put this in your -project under the ``email/tests/data/message.eml`` path. Let's say your unit -tests live in ``email/tests/test_email.py``. - -Your test could read the data file by doing something like:: - - data_dir = os.path.join(os.path.dirname(__file__), 'tests', 'data') - data_path = os.path.join(data_dir, 'message.eml') - with open(data_path, encoding='utf-8') as fp: - eml = fp.read() - -But there's a problem with this! The use of ``__file__`` doesn't work if your -package lives inside a zip file, since in that case this code does not live on -the file system. - -You could use the `pkg_resources API`_ like so:: - - # In Python 3, resource_string() actually returns bytes! - from pkg_resources import resource_string as resource_bytes - eml = resource_bytes('email.tests.data', 'message.eml').decode('utf-8') - -This requires you to make Python packages of both ``email/tests`` and -``email/tests/data``, by placing an empty ``__init__.py`` files in each of -those directories. - -The problem with the ``pkg_resources`` approach is that, depending on the -packages in your environment, ``pkg_resources`` can be expensive -just to import. This behavior -can have a serious negative impact on things like command line startup time -for Python implement commands. - -``importlib_resources`` solves this performance challenge by being built -entirely on the back of the -stdlib :py:mod:`importlib`. By taking advantage of all the efficiencies in -Python's import system, and the fact that it's built into Python, using -``importlib_resources`` can be much more performant. The equivalent code -using ``importlib_resources`` would look like:: - - from importlib_resources import files - # Reads contents with UTF-8 encoding and returns str. - eml = files('email.tests.data').joinpath('message.eml').read_text() - - -Anchors -======= - -The ``importlib_resources`` ``files`` API takes an *anchor* as its first -parameter, which can either be a package name (as a ``str``) or an actual -module object. If a string is passed in, it must name an importable Python -module, which is imported prior to loading any resources. Thus the above -example could also be written as:: - - import email.tests.data - eml = files(email.tests.data).joinpath('message.eml').read_text() - - -Namespace Packages -================== - -``importlib_resources`` supports namespace packages as anchors just like -any other package. Similar to modules in a namespace package, -resources in a namespace package are not allowed to collide by name. -For example, if two packages both expose ``nspkg/data/foo.txt``, those -resources are unsupported by this library. The package will also likely -experience problems due to the collision with installers. - -It's perfectly valid, however, for two packages to present different resources -in the same namespace package, regular package, or subdirectory. -For example, one package could expose ``nspkg/data/foo.txt`` and another -expose ``nspkg/data/bar.txt`` and those two packages could be installed -into separate paths, and the resources should be queryable:: - - data = importlib_resources.files('nspkg').joinpath('data') - data.joinpath('foo.txt').read_text() - data.joinpath('bar.txt').read_text() - - -File system or zip file -======================= - -A consumer need not worry whether any given package is on the file system -or in a zip file, as the ``importlib_resources`` APIs abstracts those details. -Sometimes though, the user needs a path to an actual file on the file system. -For example, some SSL APIs require a certificate file to be specified by a -real file system path, and C's ``dlopen()`` function also requires a real file -system path. - -To support this need, ``importlib_resources`` provides an API to extract the -resource from a zip file to a temporary file or folder and return the file -system path to this materialized resource as a :py:class:`pathlib.Path` -object. In order to properly clean up this temporary file, what's actually -returned is a context manager for use in a ``with``-statement:: - - from importlib_resources import files, as_file - - source = files(email.tests.data).joinpath('message.eml') - with as_file(source) as eml: - third_party_api_requiring_file_system_path(eml) - -Use all the standard :py:mod:`contextlib` APIs to manage this context manager. - - -Migrating from Legacy -===================== - -Starting with Python 3.9 and ``importlib_resources`` 1.4, this package -introduced the ``files()`` API, to be preferred over the legacy API, -i.e. the functions ``open_binary``, ``open_text``, ``path``, -``contents``, ``read_text``, ``read_binary``, and ``is_resource``. - -To port to the ``files()`` API, refer to the -`_legacy module `_ -to see simple wrappers that enable drop-in replacement based on the -preferred API, and either copy those or adapt the usage to utilize the -``files`` and -`Traversable `_ -interfaces directly. - - -Extending -========= - -Starting with Python 3.9 and ``importlib_resources`` 2.0, this package -provides an interface for non-standard loaders, such as those used by -executable bundlers, to supply resources. These loaders should supply a -``get_resource_reader`` method, which is passed a module name and -should return a ``TraversableResources`` instance. - - -.. rubric:: Footnotes - -.. [#fn1] As of `PEP 451 `_ this - information is also available on the module's - ``__spec__.submodule_search_locations`` attribute, which will not be - ``None`` for packages. - -.. _`pkg_resources API`: http://setuptools.readthedocs.io/en/latest/pkg_resources.html#basic-resource-access -.. _`loader`: https://docs.python.org/3/reference/import.html#finders-and-loaders -.. _`ResourceReader`: https://docs.python.org/3.7/library/importlib.html#importlib.abc.ResourceReader diff --git a/importlib_resources/compat/py39.py b/importlib_resources/compat/py39.py deleted file mode 100644 index 684d3c63..00000000 --- a/importlib_resources/compat/py39.py +++ /dev/null @@ -1,9 +0,0 @@ -import sys - -__all__ = ['ZipPath'] - - -if sys.version_info >= (3, 10): - from zipfile import Path as ZipPath -else: - from zipp import Path as ZipPath diff --git a/importlib_resources/future/adapters.py b/importlib_resources/future/adapters.py deleted file mode 100644 index 239e52b7..00000000 --- a/importlib_resources/future/adapters.py +++ /dev/null @@ -1,102 +0,0 @@ -import functools -import pathlib -from contextlib import suppress -from types import SimpleNamespace - -from .. import _adapters, readers - - -def _block_standard(reader_getter): - """ - Wrap _adapters.TraversableResourcesLoader.get_resource_reader - and intercept any standard library readers. - """ - - @functools.wraps(reader_getter) - def wrapper(*args, **kwargs): - """ - If the reader is from the standard library, return None to allow - allow likely newer implementations in this library to take precedence. - """ - try: - reader = reader_getter(*args, **kwargs) - except NotADirectoryError: - # MultiplexedPath may fail on zip subdirectory - return - except ValueError as exc: - # NamespaceReader in stdlib may fail for editable installs - # (python/importlib_resources#311, python/importlib_resources#318) - # Remove after bugfix applied to Python 3.13. - if "not enough values to unpack" not in str(exc): - raise - return - # Python 3.10+ - mod_name = reader.__class__.__module__ - if mod_name.startswith('importlib.') and mod_name.endswith('readers'): - return - # Python 3.8, 3.9 - if isinstance(reader, _adapters.CompatibilityFiles) and ( - reader.spec.loader.__class__.__module__.startswith('zipimport') - or reader.spec.loader.__class__.__module__.startswith( - '_frozen_importlib_external' - ) - ): - return - return reader - - return wrapper - - -def _skip_degenerate(reader): - """ - Mask any degenerate reader. Ref #298. - """ - is_degenerate = ( - isinstance(reader, _adapters.CompatibilityFiles) and not reader._reader - ) - return reader if not is_degenerate else None - - -class TraversableResourcesLoader(_adapters.TraversableResourcesLoader): - """ - Adapt loaders to provide TraversableResources and other - compatibility. - - Ensures the readers from importlib_resources are preferred - over stdlib readers. - """ - - def get_resource_reader(self, name): - return ( - _skip_degenerate(_block_standard(super().get_resource_reader)(name)) - or self._standard_reader() - or super().get_resource_reader(name) - ) - - def _standard_reader(self): - return self._zip_reader() or self._namespace_reader() or self._file_reader() - - def _zip_reader(self): - with suppress(AttributeError): - return readers.ZipReader(self.spec.loader, self.spec.name) - - def _namespace_reader(self): - with suppress(AttributeError, ValueError): - return readers.NamespaceReader(self.spec.submodule_search_locations) - - def _file_reader(self): - try: - path = pathlib.Path(self.spec.origin) - except TypeError: - return None - if path.exists(): - return readers.FileReader(SimpleNamespace(path=path)) - - -def wrap_spec(package): - """ - Override _adapters.wrap_spec to use TraversableResourcesLoader - from above. Ensures that future behavior is always available on older - Pythons. - """ - return _adapters.SpecLoaderAdapter(package.__spec__, TraversableResourcesLoader) diff --git a/importlib_resources/tests/compat/py312.py b/importlib_resources/tests/compat/py312.py deleted file mode 100644 index ea9a58ba..00000000 --- a/importlib_resources/tests/compat/py312.py +++ /dev/null @@ -1,18 +0,0 @@ -import contextlib - -from .py39 import import_helper - - -@contextlib.contextmanager -def isolated_modules(): - """ - Save modules on entry and cleanup on exit. - """ - (saved,) = import_helper.modules_setup() - try: - yield - finally: - import_helper.modules_cleanup(saved) - - -vars(import_helper).setdefault('isolated_modules', isolated_modules) diff --git a/importlib_resources/tests/compat/py39.py b/importlib_resources/tests/compat/py39.py deleted file mode 100644 index e01d276b..00000000 --- a/importlib_resources/tests/compat/py39.py +++ /dev/null @@ -1,13 +0,0 @@ -""" -Backward-compatability shims to support Python 3.9 and earlier. -""" - -from jaraco.test.cpython import from_test_support, try_import - -import_helper = try_import('import_helper') or from_test_support( - 'modules_setup', 'modules_cleanup', 'DirsOnSysPath' -) -os_helper = try_import('os_helper') or from_test_support('temp_dir') -warnings_helper = try_import('warnings_helper') or from_test_support( - 'ignore_warnings', 'check_warnings' -) diff --git a/mypy.ini b/mypy.ini deleted file mode 100644 index d8f1411c..00000000 --- a/mypy.ini +++ /dev/null @@ -1,23 +0,0 @@ -[mypy] -# Is the project well-typed? -strict = False - -# Early opt-in even when strict = False -warn_unused_ignores = True -warn_redundant_casts = True -enable_error_code = ignore-without-code - -# Support namespace packages per https://github.com/python/mypy/issues/14057 -explicit_package_bases = True - -disable_error_code = - # Disable due to many false positives - overload-overlap, - -# jaraco/zipp#123 -[mypy-zipp] -ignore_missing_imports = True - -# jaraco/jaraco.test#7 -[mypy-jaraco.test.*] -ignore_missing_imports = True diff --git a/pyproject.toml b/pyproject.toml deleted file mode 100644 index 608e7515..00000000 --- a/pyproject.toml +++ /dev/null @@ -1,76 +0,0 @@ -[build-system] -requires = ["setuptools>=61.2", "setuptools_scm[toml]>=3.4.1"] -build-backend = "setuptools.build_meta" - -[project] -name = "importlib_resources" -authors = [ - { name = "Barry Warsaw", email = "barry@python.org" }, -] -maintainers = [ - { name = "Jason R. Coombs", email = "jaraco@jaraco.com" }, -] -description = "Read resources from Python packages" -readme = "README.rst" -classifiers = [ - "Development Status :: 5 - Production/Stable", - "Intended Audience :: Developers", - "License :: OSI Approved :: Apache Software License", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3 :: Only", -] -requires-python = ">=3.9" -dependencies = [ - "zipp >= 3.1.0; python_version < '3.10'", -] -dynamic = ["version"] - -[project.urls] -Source = "https://github.com/python/importlib_resources" - -[project.optional-dependencies] -test = [ - # upstream - "pytest >= 6, != 8.1.*", - - # local - "zipp >= 3.17", - "jaraco.test >= 5.4", -] - -doc = [ - # upstream - "sphinx >= 3.5", - "jaraco.packaging >= 9.3", - "rst.linker >= 1.9", - "furo", - "sphinx-lint", - - # tidelift - "jaraco.tidelift >= 1.4", - - # local -] - -check = [ - "pytest-checkdocs >= 2.4", - "pytest-ruff >= 0.2.1; sys_platform != 'cygwin'", -] - -cover = [ - "pytest-cov", -] - -enabler = [ - "pytest-enabler >= 2.2", -] - -type = [ - # upstream - "pytest-mypy", - - # local -] - - -[tool.setuptools_scm] diff --git a/pytest.ini b/pytest.ini deleted file mode 100644 index 9a0f3bce..00000000 --- a/pytest.ini +++ /dev/null @@ -1,25 +0,0 @@ -[pytest] -norecursedirs=dist build .tox .eggs -addopts= - --doctest-modules - --import-mode importlib -consider_namespace_packages=true -filterwarnings= - ## upstream - - # Ensure ResourceWarnings are emitted - default::ResourceWarning - - # realpython/pytest-mypy#152 - ignore:'encoding' argument not specified::pytest_mypy - - # python/cpython#100750 - ignore:'encoding' argument not specified::platform - - # pypa/build#615 - ignore:'encoding' argument not specified::build.env - - # dateutil/dateutil#1284 - ignore:datetime.datetime.utcfromtimestamp:DeprecationWarning:dateutil.tz.tz - - ## end upstream diff --git a/ruff.toml b/ruff.toml deleted file mode 100644 index 9379d6e1..00000000 --- a/ruff.toml +++ /dev/null @@ -1,33 +0,0 @@ -# extend pyproject.toml for requires-python (workaround astral-sh/ruff#10299) -extend = "pyproject.toml" - -[lint] -extend-select = [ - "C901", - "PERF401", - "W", -] -ignore = [ - # https://docs.astral.sh/ruff/formatter/#conflicting-lint-rules - "W191", - "E111", - "E114", - "E117", - "D206", - "D300", - "Q000", - "Q001", - "Q002", - "Q003", - "COM812", - "COM819", - "ISC001", - "ISC002", -] - -[format] -# Enable preview to get hugged parenthesis unwrapping and other nice surprises -# See https://github.com/jaraco/skeleton/pull/133#issuecomment-2239538373 -preview = true -# https://docs.astral.sh/ruff/settings/#format_quote-style -quote-style = "preserve" diff --git a/towncrier.toml b/towncrier.toml deleted file mode 100644 index 6fa480e4..00000000 --- a/towncrier.toml +++ /dev/null @@ -1,2 +0,0 @@ -[tool.towncrier] -title_format = "{version}" diff --git a/tox.ini b/tox.ini deleted file mode 100644 index 14243051..00000000 --- a/tox.ini +++ /dev/null @@ -1,63 +0,0 @@ -[testenv] -description = perform primary checks (tests, style, types, coverage) -deps = -setenv = - PYTHONWARNDEFAULTENCODING = 1 -commands = - pytest {posargs} -usedevelop = True -extras = - test - check - cover - enabler - type - -[testenv:diffcov] -description = run tests and check that diff from main is covered -deps = - {[testenv]deps} - diff-cover -commands = - pytest {posargs} --cov-report xml - diff-cover coverage.xml --compare-branch=origin/main --html-report diffcov.html - diff-cover coverage.xml --compare-branch=origin/main --fail-under=100 - -[testenv:docs] -description = build the documentation -extras = - doc - test -changedir = docs -commands = - python -m sphinx -W --keep-going . {toxinidir}/build/html - python -m sphinxlint - -[testenv:finalize] -description = assemble changelog and tag a release -skip_install = True -deps = - towncrier - jaraco.develop >= 7.23 -pass_env = * -commands = - python -m jaraco.develop.finalize - - -[testenv:release] -description = publish the package to PyPI and GitHub -skip_install = True -deps = - build - twine>=3 - jaraco.develop>=7.1 -pass_env = - TWINE_PASSWORD - GITHUB_TOKEN -setenv = - TWINE_USERNAME = {env:TWINE_USERNAME:__token__} -commands = - python -c "import shutil; shutil.rmtree('dist', ignore_errors=True)" - python -m build - python -m twine upload dist/* - python -m jaraco.develop.create-github-release