From 06acfd262258d809242c74179477af324389e1c7 Mon Sep 17 00:00:00 2001 From: Dimitri Papadopoulos Orfanos <3234522+DimitriPapadopoulos@users.noreply.github.com> Date: Thu, 8 Aug 2024 23:14:35 +0200 Subject: [PATCH 01/69] Update to the latest ruff version (jaraco/skeleton#137) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5a4a7e9..ff54405 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.8 + rev: v0.5.6 hooks: - id: ruff - id: ruff-format From dd30b7600f33ce06a479a73002b950f4a3947759 Mon Sep 17 00:00:00 2001 From: Avasam Date: Thu, 8 Aug 2024 17:19:17 -0400 Subject: [PATCH 02/69] Add Protocols, remove @overload, from `.coveragerc` `exclude_also` (jaraco/skeleton#135) Co-authored-by: Jason R. Coombs --- .coveragerc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.coveragerc b/.coveragerc index 35b98b1..2e3f4dd 100644 --- a/.coveragerc +++ b/.coveragerc @@ -8,6 +8,8 @@ disable_warnings = [report] show_missing = True exclude_also = - # jaraco/skeleton#97 - @overload + # 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: From 3841656c61bad87f922fcba50445b503209b69c2 Mon Sep 17 00:00:00 2001 From: Avasam Date: Mon, 12 Aug 2024 12:13:19 -0400 Subject: [PATCH 03/69] Loosen restrictions on mypy (jaraco/skeleton#136) Based on changes downstream in setuptools. --- mypy.ini | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/mypy.ini b/mypy.ini index b6f9727..83b0d15 100644 --- a/mypy.ini +++ b/mypy.ini @@ -1,5 +1,14 @@ [mypy] -ignore_missing_imports = True -# required to support namespace packages -# https://github.com/python/mypy/issues/14057 +# 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 overload-overlap due to many false-positives +disable_error_code = overload-overlap From 1a27fd5b8815e65571e6c028d6bef2c1daf61688 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 12 Aug 2024 12:16:15 -0400 Subject: [PATCH 04/69] Split the test dependencies into four classes (test, cover, type, check). (jaraco/skeleton#139) --- pyproject.toml | 25 ++++++++++++++++++++----- tox.ini | 4 ++++ 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 1307e1f..31057d8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,14 +28,10 @@ Source = "https://github.com/PROJECT_PATH" test = [ # upstream "pytest >= 6, != 8.1.*", - "pytest-checkdocs >= 2.4", - "pytest-cov", - "pytest-mypy", - "pytest-enabler >= 2.2", - "pytest-ruff >= 0.2.1; sys_platform != 'cygwin'", # local ] + doc = [ # upstream "sphinx >= 3.5", @@ -47,4 +43,23 @@ doc = [ # local ] +check = [ + "pytest-checkdocs >= 2.4", + "pytest-ruff >= 0.2.1; sys_platform != 'cygwin'", +] + +cover = [ + "pytest-cov", +] + +enabler = [ + "pytest-enabler >= 2.2", +] + +type = [ + "pytest-mypy", +] + + + [tool.setuptools_scm] diff --git a/tox.ini b/tox.ini index cc4db36..01f0975 100644 --- a/tox.ini +++ b/tox.ini @@ -8,6 +8,10 @@ commands = usedevelop = True extras = test + check + cover + enabler + type [testenv:diffcov] description = run tests and check that diff from main is covered From f1350e413775a9e79e20779cc9705e28a1c55900 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 21 Aug 2024 07:05:32 -0400 Subject: [PATCH 05/69] Add upstream and local sections for 'type' extra, since many projects will have 'types-*' dependencies. --- pyproject.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 31057d8..3866a32 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -57,7 +57,10 @@ enabler = [ ] type = [ + # upstream "pytest-mypy", + + # local ] From 75b301c95ce3d75626c949a26e650deceba8e62c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 21 Aug 2024 18:02:36 -0400 Subject: [PATCH 06/69] =?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 Ref jaraco/zipp#123 --- mypy.ini | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mypy.ini b/mypy.ini index 83b0d15..32d87a1 100644 --- a/mypy.ini +++ b/mypy.ini @@ -12,3 +12,7 @@ explicit_package_bases = True # Disable overload-overlap due to many false-positives disable_error_code = overload-overlap + +# jaraco/zipp#123 +[mypy-zipp] +ignore_missing_imports = True From 2fecb5e596c7b6ff2d3f98af0ac63c9f38401c5a Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 21 Aug 2024 18:05:35 -0400 Subject: [PATCH 07/69] =?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 Ref jaraco/jaraco.test#7 --- mypy.ini | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mypy.ini b/mypy.ini index 32d87a1..b136fac 100644 --- a/mypy.ini +++ b/mypy.ini @@ -16,3 +16,7 @@ disable_error_code = overload-overlap # jaraco/zipp#123 [mypy-zipp] ignore_missing_imports = True + +# jaraco/jaraco.test#7 +[mypy-jaraco.test.*] +ignore_missing_imports = True From 045dde42b16581b99539667a87d61252bf4d08ac Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 21 Aug 2024 18:06:25 -0400 Subject: [PATCH 08/69] =?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 --- importlib_resources/_common.py | 4 ++-- importlib_resources/compat/py39.py | 4 ++-- importlib_resources/simple.py | 2 +- importlib_resources/tests/test_files.py | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/importlib_resources/_common.py b/importlib_resources/_common.py index e95371c..7071511 100644 --- a/importlib_resources/_common.py +++ b/importlib_resources/_common.py @@ -66,10 +66,10 @@ def get_resource_reader(package: types.ModuleType) -> Optional[ResourceReader]: # zipimport.zipimporter does not support weak references, resulting in a # TypeError. That seems terrible. spec = package.__spec__ - reader = getattr(spec.loader, 'get_resource_reader', None) # type: ignore + reader = getattr(spec.loader, 'get_resource_reader', None) # type: ignore[union-attr] if reader is None: return None - return reader(spec.name) # type: ignore + return reader(spec.name) # type: ignore[union-attr] @functools.singledispatch diff --git a/importlib_resources/compat/py39.py b/importlib_resources/compat/py39.py index ab87b9d..ed5abd5 100644 --- a/importlib_resources/compat/py39.py +++ b/importlib_resources/compat/py39.py @@ -5,6 +5,6 @@ if sys.version_info >= (3, 10): - from zipfile import Path as ZipPath # type: ignore + from zipfile import Path as ZipPath else: - from zipp import Path as ZipPath # type: ignore + from zipp import Path as ZipPath diff --git a/importlib_resources/simple.py b/importlib_resources/simple.py index 96f117f..2e75299 100644 --- a/importlib_resources/simple.py +++ b/importlib_resources/simple.py @@ -77,7 +77,7 @@ class ResourceHandle(Traversable): def __init__(self, parent: ResourceContainer, name: str): self.parent = parent - self.name = name # type: ignore + self.name = name # type: ignore[misc] def is_file(self): return True diff --git a/importlib_resources/tests/test_files.py b/importlib_resources/tests/test_files.py index 9cb55d0..311581c 100644 --- a/importlib_resources/tests/test_files.py +++ b/importlib_resources/tests/test_files.py @@ -86,7 +86,7 @@ def test_module_resources(self): """ A module can have resources found adjacent to the module. """ - import mod + import mod # type: ignore[import-not-found] actual = resources.files(mod).joinpath('res.txt').read_text(encoding='utf-8') assert actual == self.spec['res.txt'] From f3ad28a14feb85dc2631a8e3658f090ed0b7522d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 21 Aug 2024 18:14:58 -0400 Subject: [PATCH 09/69] =?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 Update to jaraco.path 3.7.1 with fixes for mypy nitpicks. --- importlib_resources/tests/_path.py | 50 ++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/importlib_resources/tests/_path.py b/importlib_resources/tests/_path.py index 1f97c96..b144628 100644 --- a/importlib_resources/tests/_path.py +++ b/importlib_resources/tests/_path.py @@ -2,15 +2,44 @@ import functools from typing import Dict, Union +from typing import runtime_checkable +from typing import Protocol #### -# from jaraco.path 3.4.1 +# from jaraco.path 3.7.1 -FilesSpec = Dict[str, Union[str, bytes, 'FilesSpec']] # type: ignore +class Symlink(str): + """ + A string indicating the target of a symlink. + """ + + +FilesSpec = Dict[str, Union[str, bytes, Symlink, 'FilesSpec']] + + +@runtime_checkable +class TreeMaker(Protocol): + def __truediv__(self, *args, **kwargs): ... # pragma: no cover + + def mkdir(self, **kwargs): ... # pragma: no cover + + def write_text(self, content, **kwargs): ... # pragma: no cover + + def write_bytes(self, content): ... # pragma: no cover -def build(spec: FilesSpec, prefix=pathlib.Path()): + def symlink_to(self, target): ... # pragma: no cover + + +def _ensure_tree_maker(obj: Union[str, TreeMaker]) -> TreeMaker: + return obj if isinstance(obj, TreeMaker) else pathlib.Path(obj) # type: ignore[return-value] + + +def build( + spec: FilesSpec, + prefix: Union[str, TreeMaker] = pathlib.Path(), # type: ignore[assignment] +): """ Build a set of files/directories, as described by the spec. @@ -25,21 +54,25 @@ def build(spec: FilesSpec, prefix=pathlib.Path()): ... "__init__.py": "", ... }, ... "baz.py": "# Some code", - ... } + ... "bar.py": Symlink("baz.py"), + ... }, + ... "bing": Symlink("foo"), ... } >>> target = getfixture('tmp_path') >>> build(spec, target) >>> target.joinpath('foo/baz.py').read_text(encoding='utf-8') '# Some code' + >>> target.joinpath('bing/bar.py').read_text(encoding='utf-8') + '# Some code' """ for name, contents in spec.items(): - create(contents, pathlib.Path(prefix) / name) + create(contents, _ensure_tree_maker(prefix) / name) @functools.singledispatch def create(content: Union[str, bytes, FilesSpec], path): path.mkdir(exist_ok=True) - build(content, prefix=path) # type: ignore + build(content, prefix=path) # type: ignore[arg-type] @create.register @@ -52,5 +85,10 @@ def _(content: str, path): path.write_text(content, encoding='utf-8') +@create.register +def _(content: Symlink, path): + path.symlink_to(content) + + # end from jaraco.path #### From 9689f8f82ca838cd58d3d0d80828785ada7798c9 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 21 Aug 2024 18:21:44 -0400 Subject: [PATCH 10/69] Finalize --- NEWS.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/NEWS.rst b/NEWS.rst index 5f1ab11..fff609c 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -1,3 +1,9 @@ +v6.4.4 +====== + +No significant changes. + + v6.4.3 ====== From 6a0b3af5640f4cbe8134440ff723979b470fa173 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 22 Aug 2024 09:43:10 -0400 Subject: [PATCH 11/69] Add reference to development methodology. Ref python/cpython#123144 --- importlib_resources/__init__.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/importlib_resources/__init__.py b/importlib_resources/__init__.py index ec4441c..723c9f9 100644 --- a/importlib_resources/__init__.py +++ b/importlib_resources/__init__.py @@ -1,4 +1,11 @@ -"""Read resources contained within a package.""" +""" +Read resources contained within a package. + +This codebase is shared between importlib.resources in the stdlib +and importlib_resources in PyPI. See +https://github.com/python/importlib_metadata/wiki/Development-Methodology +for more detail. +""" from ._common import ( as_file, From 57b8aa81d77416805dcaaa22d5d45fef3e8b331c Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Sun, 25 Aug 2024 09:29:10 +0100 Subject: [PATCH 12/69] Add `--fix` flag to ruff pre-commit hook for automatic suggestion of fixes (jaraco/skeleton#140) * Add `--fix` flag to ruff pre-commit hook for automatic suggestion of fixes. This is documented in https://github.com/astral-sh/ruff-pre-commit?tab=readme-ov-file#using-ruff-with-pre-commit and should be safe to apply, because it requires the developer to "manually approve" the suggested changes via `git add`. * Add --unsafe-fixes to ruff pre-commit hoot --- .pre-commit-config.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ff54405..8ec58e2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,4 +3,5 @@ repos: rev: v0.5.6 hooks: - id: ruff + args: [--fix, --unsafe-fixes] - id: ruff-format From d3e83beaec3bdf4a628f2f0ae0a52d21c84e346f Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 25 Aug 2024 06:33:23 -0400 Subject: [PATCH 13/69] Disable mypy for now. Ref jaraco/skeleton#143 --- pyproject.toml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 3866a32..1d81b1c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -64,5 +64,8 @@ type = [ ] - [tool.setuptools_scm] + + +[tool.pytest-enabler.mypy] +# Disabled due to jaraco/skeleton#143 From 3fcabf10b810c8585b858fb81fc3cd8c5efe898d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 25 Aug 2024 13:26:38 -0400 Subject: [PATCH 14/69] Move overload-overlap disablement to its own line for easier diffs and simpler relevant comments. Ref #142 --- mypy.ini | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mypy.ini b/mypy.ini index 83b0d15..2806c33 100644 --- a/mypy.ini +++ b/mypy.ini @@ -10,5 +10,6 @@ enable_error_code = ignore-without-code # Support namespace packages per https://github.com/python/mypy/issues/14057 explicit_package_bases = True -# Disable overload-overlap due to many false-positives -disable_error_code = overload-overlap +disable_error_code = + # Disable due to many false positives + overload-overlap From 0c326f3f77b2420163f73d97f8fbd090fa49147d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 29 Aug 2024 13:13:06 -0400 Subject: [PATCH 15/69] Add a degenerate nitpick_ignore for downstream consumers. Add a 'local' comment to delineate where the skeleton ends and the downstream begins. --- docs/conf.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/conf.py b/docs/conf.py index 3215048..3d956a8 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + extensions = [ 'sphinx.ext.autodoc', 'jaraco.packaging.sphinx', @@ -30,6 +33,7 @@ # Be strict about any broken references nitpicky = True +nitpick_ignore: list[tuple[str, str]] = [] # Include Python intersphinx mapping to prevent failures # jaraco/skeleton#51 @@ -40,3 +44,5 @@ # Preserve authored syntax for defaults autodoc_preserve_defaults = True + +# local From 2beb8b0c9d0f7046370e7c58c4e6baaf35154a16 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 29 Aug 2024 16:26:28 -0400 Subject: [PATCH 16/69] Add support for linking usernames. Closes jaraco/skeleton#144 --- docs/conf.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/conf.py b/docs/conf.py index 3d956a8..d5745d6 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -45,4 +45,13 @@ # 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 From 790fa6e6feb9a93d39135494819b12e9df8a7bba Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 29 Aug 2024 16:53:52 -0400 Subject: [PATCH 17/69] Include the trailing slash in disable_error_code(overload-overlap), also required for clean diffs. Ref jaraco/skeleton#142 --- mypy.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy.ini b/mypy.ini index 2806c33..efcb8cb 100644 --- a/mypy.ini +++ b/mypy.ini @@ -12,4 +12,4 @@ explicit_package_bases = True disable_error_code = # Disable due to many false positives - overload-overlap + overload-overlap, From 1a6e38c0bfccd18a01deaca1491bcde3e778404c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 31 Aug 2024 05:50:38 -0400 Subject: [PATCH 18/69] Remove workaround for sphinx-contrib/sphinx-lint#83 --- tox.ini | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tox.ini b/tox.ini index 01f0975..1424305 100644 --- a/tox.ini +++ b/tox.ini @@ -31,9 +31,7 @@ extras = changedir = docs commands = python -m sphinx -W --keep-going . {toxinidir}/build/html - python -m sphinxlint \ - # workaround for sphinx-contrib/sphinx-lint#83 - --jobs 1 + python -m sphinxlint [testenv:finalize] description = assemble changelog and tag a release From d84ca376316016420297fbc310ba181ca7d2864d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 2 Sep 2024 08:22:16 -0400 Subject: [PATCH 19/69] Fix typo in _temp_path comment. Ref python/cpython#123597 --- importlib_resources/_common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/importlib_resources/_common.py b/importlib_resources/_common.py index 7071511..f065d49 100644 --- a/importlib_resources/_common.py +++ b/importlib_resources/_common.py @@ -183,7 +183,7 @@ def _(path): @contextlib.contextmanager def _temp_path(dir: tempfile.TemporaryDirectory): """ - Wrap tempfile.TemporyDirectory to return a pathlib object. + Wrap tempfile.TemporaryDirectory to return a pathlib object. """ with dir as result: yield pathlib.Path(result) From 4875bc5179938324d157d9917b3a0bfb5fca8dd1 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 9 Sep 2024 12:52:25 -0400 Subject: [PATCH 20/69] Add type annotations for _candidate_paths --- importlib_resources/readers.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/importlib_resources/readers.py b/importlib_resources/readers.py index abfcde7..dc649cf 100644 --- a/importlib_resources/readers.py +++ b/importlib_resources/readers.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import collections import contextlib import itertools @@ -5,6 +7,7 @@ import operator import re import warnings +from collections.abc import Iterator from . import abc @@ -150,12 +153,12 @@ def _resolve(cls, path_str) -> abc.Traversable: return dir @classmethod - def _candidate_paths(cls, path_str): + def _candidate_paths(cls, path_str: str) -> Iterator[abc.Traversable]: yield pathlib.Path(path_str) yield from cls._resolve_zip_path(path_str) @staticmethod - def _resolve_zip_path(path_str): + def _resolve_zip_path(path_str: str): for match in reversed(list(re.finditer(r'[\\/]', path_str))): with contextlib.suppress( FileNotFoundError, From 47d73b1e7787cd66ee57be676f2385d2183f78ac Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 20 Aug 2024 11:49:50 -0400 Subject: [PATCH 21/69] Add test capturing failure when resolving the MultiplexedPath for a namespace package with non-path elements in the path. Ref #311 --- importlib_resources/tests/test_files.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/importlib_resources/tests/test_files.py b/importlib_resources/tests/test_files.py index 311581c..6fdcdef 100644 --- a/importlib_resources/tests/test_files.py +++ b/importlib_resources/tests/test_files.py @@ -8,6 +8,8 @@ import importlib import contextlib +import pytest + import importlib_resources as resources from ..abc import Traversable from . import util @@ -60,6 +62,27 @@ class OpenZipTests(FilesTests, util.ZipSetup, unittest.TestCase): class OpenNamespaceTests(FilesTests, util.DiskSetup, unittest.TestCase): MODULE = 'namespacedata01' + @pytest.mark.xfail(reason="#311") + def test_non_paths_in_dunder_path(self): + """ + Non-path items in a namespace package's ``__path__`` are ignored. + + As reported in python/importlib_resources#311, some tools + like Setuptools, when creating editable packages, will inject + non-paths into a namespace package's ``__path__``, a + sentinel like + ``__editable__.sample_namespace-1.0.finder.__path_hook__`` + to cause the ``PathEntryFinder`` to be called when searching + for packages. In that case, resources should still be loadable. + """ + import namespacedata01 + + namespacedata01.__path__.append( + '__editable__.sample_namespace-1.0.finder.__path_hook__' + ) + + resources.files(namespacedata01) + class OpenNamespaceZipTests(FilesTests, util.ZipSetup, unittest.TestCase): ZIP_MODULE = 'namespacedata01' From 2c145c5b1ff95290794b2cb63e5c924e1847456d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 20 Aug 2024 12:11:53 -0400 Subject: [PATCH 22/69] Omit sentinel values from a namespace path. When editable installs create sentinels, as they are not a valid directory, they're unsuitable for constructing a `MultiplexedPath`. Filter them out. Fixes #311 --- importlib_resources/readers.py | 12 ++++++++---- importlib_resources/tests/test_files.py | 3 --- newsfragments/311.bugfix.rst | 1 + 3 files changed, 9 insertions(+), 7 deletions(-) create mode 100644 newsfragments/311.bugfix.rst diff --git a/importlib_resources/readers.py b/importlib_resources/readers.py index dc649cf..4f761c6 100644 --- a/importlib_resources/readers.py +++ b/importlib_resources/readers.py @@ -138,19 +138,23 @@ class NamespaceReader(abc.TraversableResources): def __init__(self, namespace_path): if 'NamespacePath' not in str(namespace_path): raise ValueError('Invalid path') - self.path = MultiplexedPath(*map(self._resolve, namespace_path)) + self.path = MultiplexedPath(*filter(bool, map(self._resolve, namespace_path))) @classmethod - def _resolve(cls, path_str) -> abc.Traversable: + def _resolve(cls, path_str) -> abc.Traversable | None: r""" Given an item from a namespace path, resolve it to a Traversable. path_str might be a directory on the filesystem or a path to a zipfile plus the path within the zipfile, e.g. ``/foo/bar`` or ``/foo/baz.zip/inner_dir`` or ``foo\baz.zip\inner_dir\sub``. + + path_str might also be a sentinel used by editable packages to + trigger other behaviors (see python/importlib_resources#311). + In that case, return None. """ - (dir,) = (cand for cand in cls._candidate_paths(path_str) if cand.is_dir()) - return dir + dirs = (cand for cand in cls._candidate_paths(path_str) if cand.is_dir()) + return next(dirs, None) @classmethod def _candidate_paths(cls, path_str: str) -> Iterator[abc.Traversable]: diff --git a/importlib_resources/tests/test_files.py b/importlib_resources/tests/test_files.py index 6fdcdef..f1fe233 100644 --- a/importlib_resources/tests/test_files.py +++ b/importlib_resources/tests/test_files.py @@ -8,8 +8,6 @@ import importlib import contextlib -import pytest - import importlib_resources as resources from ..abc import Traversable from . import util @@ -62,7 +60,6 @@ class OpenZipTests(FilesTests, util.ZipSetup, unittest.TestCase): class OpenNamespaceTests(FilesTests, util.DiskSetup, unittest.TestCase): MODULE = 'namespacedata01' - @pytest.mark.xfail(reason="#311") def test_non_paths_in_dunder_path(self): """ Non-path items in a namespace package's ``__path__`` are ignored. diff --git a/newsfragments/311.bugfix.rst b/newsfragments/311.bugfix.rst new file mode 100644 index 0000000..56655f7 --- /dev/null +++ b/newsfragments/311.bugfix.rst @@ -0,0 +1 @@ +Omit sentinel values from a namespace path. \ No newline at end of file From 284148b005b57031a354402c446473f53cab2c49 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 9 Sep 2024 13:00:41 -0400 Subject: [PATCH 23/69] Finalize --- NEWS.rst | 9 +++++++++ newsfragments/311.bugfix.rst | 1 - 2 files changed, 9 insertions(+), 1 deletion(-) delete mode 100644 newsfragments/311.bugfix.rst diff --git a/NEWS.rst b/NEWS.rst index fff609c..36aac37 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -1,3 +1,12 @@ +v6.4.5 +====== + +Bugfixes +-------- + +- Omit sentinel values from a namespace path. (#311) + + v6.4.4 ====== diff --git a/newsfragments/311.bugfix.rst b/newsfragments/311.bugfix.rst deleted file mode 100644 index 56655f7..0000000 --- a/newsfragments/311.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Omit sentinel values from a namespace path. \ No newline at end of file From a675458e1a7d6ae81d0d441338a74dc98ffc5a61 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 7 Sep 2024 10:16:01 -0400 Subject: [PATCH 24/69] Allow the workflow to be triggered manually. --- .github/workflows/main.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ac0ff69..441b93e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -10,6 +10,7 @@ on: # required if branches-ignore is supplied (jaraco/skeleton#103) - '**' pull_request: + workflow_dispatch: permissions: contents: read From 81b766c06cc83679c4a04c2bfa6d2c8cc559bf33 Mon Sep 17 00:00:00 2001 From: Avasam Date: Wed, 11 Sep 2024 18:14:38 -0400 Subject: [PATCH 25/69] Fix an incompatibility (and source of merge conflicts) with projects using Ruff/isort. Remove extra line after imports in conf.py (jaraco/skeleton#147) --- docs/conf.py | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index d5745d6..240329c 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,6 +1,5 @@ from __future__ import annotations - extensions = [ 'sphinx.ext.autodoc', 'jaraco.packaging.sphinx', From 3fe8c5ba792fd58a5a24eef4e8a845f3b5dd6c2c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 11 Sep 2024 18:14:58 -0400 Subject: [PATCH 26/69] Add Python 3.13 and 3.14 into the matrix. (jaraco/skeleton#146) --- .github/workflows/main.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 441b93e..251b9c1 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -36,7 +36,7 @@ jobs: matrix: python: - "3.8" - - "3.12" + - "3.13" platform: - ubuntu-latest - macos-latest @@ -48,10 +48,14 @@ jobs: 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.13' }} + continue-on-error: ${{ matrix.python == '3.14' }} steps: - uses: actions/checkout@v4 - name: Setup Python From 8684c7a028b65381ec6c6724e2f9c9bea7df0aee Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 11 Sep 2024 22:27:33 -0400 Subject: [PATCH 27/69] Add workaround for broken importlib.resources when an editable sentinel is present. Closes #318 --- importlib_resources/future/adapters.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/importlib_resources/future/adapters.py b/importlib_resources/future/adapters.py index 0e9764b..5cf1755 100644 --- a/importlib_resources/future/adapters.py +++ b/importlib_resources/future/adapters.py @@ -23,6 +23,13 @@ def wrapper(*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'): From c00a2b75d0604db2e159838761a79d93b658f1e2 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 12 Sep 2024 05:00:20 -0400 Subject: [PATCH 28/69] Separate bpo from Python issue numbers. --- docs/conf.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index a8abd1d..570346b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -27,8 +27,12 @@ url='https://peps.python.org/pep-{pep_number:0>4}/', ), dict( - pattern=r'(python/cpython#|Python #|bpo-)(?P\d+)', - url='http://bugs.python.org/issue{python}', + 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}', ), ], ), From 313bed20576c7ba6097ea00aa1c488602ee7e29d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 12 Sep 2024 10:14:46 -0400 Subject: [PATCH 29/69] gh-123994: Generate utf-16 file using little endian and BOM. (#123995) --- importlib_resources/tests/util.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/importlib_resources/tests/util.py b/importlib_resources/tests/util.py index a4eafac..5f75517 100644 --- a/importlib_resources/tests/util.py +++ b/importlib_resources/tests/util.py @@ -145,7 +145,7 @@ def test_useless_loader(self): data01={ '__init__.py': '', 'binary.file': bytes(range(4)), - 'utf-16.file': 'Hello, UTF-16 world!\n'.encode('utf-16'), + 'utf-16.file': '\ufeffHello, UTF-16 world!\n'.encode('utf-16-le'), 'utf-8.file': 'Hello, UTF-8 world!\n'.encode('utf-8'), 'subdirectory': { '__init__.py': '', @@ -160,7 +160,7 @@ def test_useless_loader(self): }, namespacedata01={ 'binary.file': bytes(range(4)), - 'utf-16.file': 'Hello, UTF-16 world!\n'.encode('utf-16'), + 'utf-16.file': '\ufeffHello, UTF-16 world!\n'.encode('utf-16-le'), 'utf-8.file': 'Hello, UTF-8 world!\n'.encode('utf-8'), 'subdirectory': { 'binary.file': bytes(range(12, 16)), From cc5d9cbb6e76cee1dc1c75401a1fa605de333016 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Wed, 18 Sep 2024 14:14:34 +0200 Subject: [PATCH 30/69] gh-123085: _compile_importlib: Avoid copying sources before compilation (GH-124131) Co-authored-by: Jason R. Coombs --- importlib_resources/tests/test_files.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/importlib_resources/tests/test_files.py b/importlib_resources/tests/test_files.py index f1fe233..bca626c 100644 --- a/importlib_resources/tests/test_files.py +++ b/importlib_resources/tests/test_files.py @@ -154,18 +154,17 @@ def test_implicit_files_submodule(self): def _compile_importlib(self): """ Make a compiled-only copy of the importlib resources package. + + Currently only code is copied, as importlib resources doesn't itself + have any resources. """ bin_site = self.fixtures.enter_context(os_helper.temp_dir()) c_resources = pathlib.Path(bin_site, 'c_resources') sources = pathlib.Path(resources.__file__).parent - shutil.copytree(sources, c_resources, ignore=lambda *_: ['__pycache__']) - - for dirpath, _, filenames in os.walk(c_resources): - for filename in filenames: - source_path = pathlib.Path(dirpath) / filename - cfile = source_path.with_suffix('.pyc') - py_compile.compile(source_path, cfile) - pathlib.Path.unlink(source_path) + + for source_path in sources.glob('**/*.py'): + c_path = c_resources.joinpath(source_path.relative_to(sources)).with_suffix('.pyc') + py_compile.compile(source_path, c_path) self.fixtures.enter_context(import_helper.DirsOnSysPath(bin_site)) def test_implicit_files_with_compiled_importlib(self): From 9d4af05bac271932e3d6ab9307383da34b575e07 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 18 Sep 2024 21:40:41 -0400 Subject: [PATCH 31/69] =?UTF-8?q?=F0=9F=91=B9=20Feed=20the=20hobgoblins=20?= =?UTF-8?q?(delint).?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- importlib_resources/tests/test_files.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/importlib_resources/tests/test_files.py b/importlib_resources/tests/test_files.py index bca626c..f45a229 100644 --- a/importlib_resources/tests/test_files.py +++ b/importlib_resources/tests/test_files.py @@ -163,7 +163,9 @@ def _compile_importlib(self): sources = pathlib.Path(resources.__file__).parent for source_path in sources.glob('**/*.py'): - c_path = c_resources.joinpath(source_path.relative_to(sources)).with_suffix('.pyc') + c_path = c_resources.joinpath(source_path.relative_to(sources)).with_suffix( + '.pyc' + ) py_compile.compile(source_path, c_path) self.fixtures.enter_context(import_helper.DirsOnSysPath(bin_site)) From 62b6678a32087ed3bfc8ff19761764340295834e Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Sat, 26 Oct 2024 00:12:59 +0100 Subject: [PATCH 32/69] Bump pre-commit hook for ruff to avoid clashes with pytest-ruff (jaraco/skeleton#150) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8ec58e2..04870d1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.5.6 + rev: v0.7.1 hooks: - id: ruff args: [--fix, --unsafe-fixes] From db4dfc495552aca8d6f05ed58441fa65fdc2ed9c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 28 Oct 2024 09:11:52 -0700 Subject: [PATCH 33/69] Add Python 3.13 and 3.14 into the matrix. (jaraco/skeleton#151) From e61a9df7cdc9c8d1b56c30b7b3f94a7cdac14414 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 28 Oct 2024 12:19:31 -0400 Subject: [PATCH 34/69] Include pyproject.toml in ruff.toml. Closes jaraco/skeleton#119. Workaround for astral-sh/ruff#10299. --- ruff.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ruff.toml b/ruff.toml index 922aa1f..8b22940 100644 --- a/ruff.toml +++ b/ruff.toml @@ -1,3 +1,6 @@ +# include pyproject.toml for requires-python (workaround astral-sh/ruff#10299) +include = "pyproject.toml" + [lint] extend-select = [ "C901", From 750a1891ec4a1c0602050e3463e9593a8c13aa14 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 28 Oct 2024 12:22:50 -0400 Subject: [PATCH 35/69] Require Python 3.9 or later now that Python 3.8 is EOL. --- .github/workflows/main.yml | 4 +--- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 251b9c1..9c01fc4 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -35,15 +35,13 @@ jobs: # https://blog.jaraco.com/efficient-use-of-ci-resources/ matrix: python: - - "3.8" + - "3.9" - "3.13" platform: - ubuntu-latest - macos-latest - windows-latest include: - - python: "3.9" - platform: ubuntu-latest - python: "3.10" platform: ubuntu-latest - python: "3.11" diff --git a/pyproject.toml b/pyproject.toml index 1d81b1c..328b98c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,7 +16,7 @@ classifiers = [ "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", ] -requires-python = ">=3.8" +requires-python = ">=3.9" dependencies = [ ] dynamic = ["version"] From 5c34e69568f23a524af4fa9dad3f5e80f22ec3e6 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 1 Nov 2024 22:35:31 -0400 Subject: [PATCH 36/69] Use extend for proper workaround. Closes jaraco/skeleton#152 Workaround for astral-sh/ruff#10299 --- ruff.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ruff.toml b/ruff.toml index 8b22940..9379d6e 100644 --- a/ruff.toml +++ b/ruff.toml @@ -1,5 +1,5 @@ -# include pyproject.toml for requires-python (workaround astral-sh/ruff#10299) -include = "pyproject.toml" +# extend pyproject.toml for requires-python (workaround astral-sh/ruff#10299) +extend = "pyproject.toml" [lint] extend-select = [ From 4ed8bffcbc354ccfc67031b86d0848cb61712c11 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 23 Dec 2024 16:19:37 -0500 Subject: [PATCH 37/69] =?UTF-8?q?=F0=9F=91=B9=20Feed=20the=20hobgoblins=20?= =?UTF-8?q?(delint).?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- importlib_resources/tests/test_functional.py | 22 +++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/importlib_resources/tests/test_functional.py b/importlib_resources/tests/test_functional.py index 1851edf..ffe12db 100644 --- a/importlib_resources/tests/test_functional.py +++ b/importlib_resources/tests/test_functional.py @@ -182,17 +182,23 @@ def test_contents(self): set(c), {'utf-8.file', 'utf-16.file', 'binary.file', 'subdirectory'}, ) - with self.assertRaises(OSError), warnings_helper.check_warnings(( - ".*contents.*", - DeprecationWarning, - )): + with ( + self.assertRaises(OSError), + warnings_helper.check_warnings(( + ".*contents.*", + DeprecationWarning, + )), + ): list(resources.contents(self.anchor01, 'utf-8.file')) for path_parts in self._gen_resourcetxt_path_parts(): - with self.assertRaises(OSError), warnings_helper.check_warnings(( - ".*contents.*", - DeprecationWarning, - )): + with ( + self.assertRaises(OSError), + warnings_helper.check_warnings(( + ".*contents.*", + DeprecationWarning, + )), + ): list(resources.contents(self.anchor01, *path_parts)) with warnings_helper.check_warnings((".*contents.*", DeprecationWarning)): c = resources.contents(self.anchor01, 'subdirectory') From ef8977791bf94b18aa71922d1d58f3ffc8acd161 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 3 Jan 2025 09:53:54 -0500 Subject: [PATCH 38/69] Remove unused imports. --- importlib_resources/tests/test_files.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/importlib_resources/tests/test_files.py b/importlib_resources/tests/test_files.py index f45a229..b762d4d 100644 --- a/importlib_resources/tests/test_files.py +++ b/importlib_resources/tests/test_files.py @@ -1,7 +1,5 @@ -import os import pathlib import py_compile -import shutil import textwrap import unittest import warnings From 39a607d25def76ef760334a494554847da8c8f0f Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 3 Jan 2025 10:23:13 -0500 Subject: [PATCH 39/69] Bump badge for 2025. --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index efabeee..4d3cabe 100644 --- a/README.rst +++ b/README.rst @@ -14,5 +14,5 @@ .. .. image:: https://readthedocs.org/projects/PROJECT_RTD/badge/?version=latest .. :target: https://PROJECT_RTD.readthedocs.io/en/latest/?badge=latest -.. image:: https://img.shields.io/badge/skeleton-2024-informational +.. image:: https://img.shields.io/badge/skeleton-2025-informational :target: https://blog.jaraco.com/skeleton From 2a95f57deb157f48d81e3e6b38345e5615e744ee Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 3 Jan 2025 10:23:36 -0500 Subject: [PATCH 40/69] Re-enable type checks. --- importlib_resources/tests/test_files.py | 2 +- pyproject.toml | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/importlib_resources/tests/test_files.py b/importlib_resources/tests/test_files.py index b762d4d..8d87a01 100644 --- a/importlib_resources/tests/test_files.py +++ b/importlib_resources/tests/test_files.py @@ -70,7 +70,7 @@ def test_non_paths_in_dunder_path(self): to cause the ``PathEntryFinder`` to be called when searching for packages. In that case, resources should still be loadable. """ - import namespacedata01 + import namespacedata01 # type: ignore[import-not-found] namespacedata01.__path__.append( '__editable__.sample_namespace-1.0.finder.__path_hook__' diff --git a/pyproject.toml b/pyproject.toml index d08547f..608e751 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -74,7 +74,3 @@ type = [ [tool.setuptools_scm] - - -[tool.pytest-enabler.mypy] -# Disabled due to jaraco/skeleton#143 From 2917dc4a76219e3d0b13a9e5f85fd7079062c824 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 3 Jan 2025 10:32:51 -0500 Subject: [PATCH 41/69] Apply import sort fixers from ruff. --- importlib_resources/__init__.py | 7 ++----- importlib_resources/_common.py | 12 ++++++------ importlib_resources/_functional.py | 3 +-- importlib_resources/abc.py | 14 +++++++++++--- importlib_resources/compat/py38.py | 2 -- importlib_resources/compat/py39.py | 1 - importlib_resources/future/adapters.py | 2 +- importlib_resources/readers.py | 3 +-- importlib_resources/tests/_path.py | 8 ++------ .../tests/test_compatibilty_files.py | 1 - importlib_resources/tests/test_contents.py | 1 + importlib_resources/tests/test_custom.py | 5 +++-- importlib_resources/tests/test_files.py | 7 ++++--- importlib_resources/tests/test_functional.py | 7 +++---- importlib_resources/tests/test_open.py | 1 + importlib_resources/tests/test_path.py | 1 + importlib_resources/tests/test_read.py | 3 ++- importlib_resources/tests/test_reader.py | 2 +- importlib_resources/tests/test_resource.py | 3 ++- importlib_resources/tests/util.py | 12 +++++------- 20 files changed, 47 insertions(+), 48 deletions(-) diff --git a/importlib_resources/__init__.py b/importlib_resources/__init__.py index 723c9f9..27d6c7f 100644 --- a/importlib_resources/__init__.py +++ b/importlib_resources/__init__.py @@ -8,12 +8,11 @@ """ from ._common import ( + Anchor, + Package, as_file, files, - Package, - Anchor, ) - from ._functional import ( contents, is_resource, @@ -23,10 +22,8 @@ read_binary, read_text, ) - from .abc import ResourceReader - __all__ = [ 'Package', 'Anchor', diff --git a/importlib_resources/_common.py b/importlib_resources/_common.py index f065d49..5f41c26 100644 --- a/importlib_resources/_common.py +++ b/importlib_resources/_common.py @@ -1,15 +1,15 @@ +import contextlib +import functools +import importlib +import inspect +import itertools import os import pathlib import tempfile -import functools -import contextlib import types -import importlib -import inspect import warnings -import itertools +from typing import Optional, Union, cast -from typing import Union, Optional, cast from .abc import ResourceReader, Traversable Package = Union[types.ModuleType, str] diff --git a/importlib_resources/_functional.py b/importlib_resources/_functional.py index f59416f..5a3ca0a 100644 --- a/importlib_resources/_functional.py +++ b/importlib_resources/_functional.py @@ -2,8 +2,7 @@ import warnings -from ._common import files, as_file - +from ._common import as_file, files _MISSING = object() diff --git a/importlib_resources/abc.py b/importlib_resources/abc.py index 7a58dd2..c0a4cb9 100644 --- a/importlib_resources/abc.py +++ b/importlib_resources/abc.py @@ -2,12 +2,20 @@ import io import itertools import pathlib -from typing import Any, BinaryIO, Iterable, Iterator, NoReturn, Text, Optional -from typing import runtime_checkable, Protocol +from typing import ( + Any, + BinaryIO, + Iterable, + Iterator, + NoReturn, + Optional, + Protocol, + Text, + runtime_checkable, +) from .compat.py38 import StrPath - __all__ = ["ResourceReader", "Traversable", "TraversableResources"] diff --git a/importlib_resources/compat/py38.py b/importlib_resources/compat/py38.py index 4d54825..80940e9 100644 --- a/importlib_resources/compat/py38.py +++ b/importlib_resources/compat/py38.py @@ -1,9 +1,7 @@ import os import sys - from typing import Union - if sys.version_info >= (3, 9): StrPath = Union[str, os.PathLike[str]] else: diff --git a/importlib_resources/compat/py39.py b/importlib_resources/compat/py39.py index ed5abd5..684d3c6 100644 --- a/importlib_resources/compat/py39.py +++ b/importlib_resources/compat/py39.py @@ -1,6 +1,5 @@ import sys - __all__ = ['ZipPath'] diff --git a/importlib_resources/future/adapters.py b/importlib_resources/future/adapters.py index 5cf1755..239e52b 100644 --- a/importlib_resources/future/adapters.py +++ b/importlib_resources/future/adapters.py @@ -3,7 +3,7 @@ from contextlib import suppress from types import SimpleNamespace -from .. import readers, _adapters +from .. import _adapters, readers def _block_standard(reader_getter): diff --git a/importlib_resources/readers.py b/importlib_resources/readers.py index 4f761c6..99884b6 100644 --- a/importlib_resources/readers.py +++ b/importlib_resources/readers.py @@ -3,14 +3,13 @@ import collections import contextlib import itertools -import pathlib import operator +import pathlib import re import warnings from collections.abc import Iterator from . import abc - from ._itertools import only from .compat.py39 import ZipPath diff --git a/importlib_resources/tests/_path.py b/importlib_resources/tests/_path.py index b144628..0033983 100644 --- a/importlib_resources/tests/_path.py +++ b/importlib_resources/tests/_path.py @@ -1,10 +1,6 @@ -import pathlib import functools - -from typing import Dict, Union -from typing import runtime_checkable -from typing import Protocol - +import pathlib +from typing import Dict, Protocol, Union, runtime_checkable #### # from jaraco.path 3.7.1 diff --git a/importlib_resources/tests/test_compatibilty_files.py b/importlib_resources/tests/test_compatibilty_files.py index 13ad0df..e8aac28 100644 --- a/importlib_resources/tests/test_compatibilty_files.py +++ b/importlib_resources/tests/test_compatibilty_files.py @@ -2,7 +2,6 @@ import unittest import importlib_resources as resources - from importlib_resources._adapters import ( CompatibilityFiles, wrap_spec, diff --git a/importlib_resources/tests/test_contents.py b/importlib_resources/tests/test_contents.py index 741a740..dcb872e 100644 --- a/importlib_resources/tests/test_contents.py +++ b/importlib_resources/tests/test_contents.py @@ -1,4 +1,5 @@ import unittest + import importlib_resources as resources from . import util diff --git a/importlib_resources/tests/test_custom.py b/importlib_resources/tests/test_custom.py index 86c6567..25ae0e7 100644 --- a/importlib_resources/tests/test_custom.py +++ b/importlib_resources/tests/test_custom.py @@ -1,10 +1,11 @@ -import unittest import contextlib import pathlib +import unittest import importlib_resources as resources + from .. import abc -from ..abc import TraversableResources, ResourceReader +from ..abc import ResourceReader, TraversableResources from . import util from .compat.py39 import os_helper diff --git a/importlib_resources/tests/test_files.py b/importlib_resources/tests/test_files.py index 8d87a01..be20660 100644 --- a/importlib_resources/tests/test_files.py +++ b/importlib_resources/tests/test_files.py @@ -1,15 +1,16 @@ +import contextlib +import importlib import pathlib import py_compile import textwrap import unittest import warnings -import importlib -import contextlib import importlib_resources as resources + from ..abc import Traversable from . import util -from .compat.py39 import os_helper, import_helper +from .compat.py39 import import_helper, os_helper @contextlib.contextmanager diff --git a/importlib_resources/tests/test_functional.py b/importlib_resources/tests/test_functional.py index ffe12db..2285389 100644 --- a/importlib_resources/tests/test_functional.py +++ b/importlib_resources/tests/test_functional.py @@ -1,12 +1,11 @@ -import unittest -import os import importlib - -from .compat.py39 import warnings_helper +import os +import unittest import importlib_resources as resources from . import util +from .compat.py39 import warnings_helper # Since the functional API forwards to Traversable, we only test # filesystem resources here -- not zip files, namespace packages etc. diff --git a/importlib_resources/tests/test_open.py b/importlib_resources/tests/test_open.py index c40bb8c..8a4b68e 100644 --- a/importlib_resources/tests/test_open.py +++ b/importlib_resources/tests/test_open.py @@ -1,6 +1,7 @@ import unittest import importlib_resources as resources + from . import util diff --git a/importlib_resources/tests/test_path.py b/importlib_resources/tests/test_path.py index 1e30f2b..0be673d 100644 --- a/importlib_resources/tests/test_path.py +++ b/importlib_resources/tests/test_path.py @@ -3,6 +3,7 @@ import unittest import importlib_resources as resources + from . import util diff --git a/importlib_resources/tests/test_read.py b/importlib_resources/tests/test_read.py index 6780a2d..216c8fe 100644 --- a/importlib_resources/tests/test_read.py +++ b/importlib_resources/tests/test_read.py @@ -1,8 +1,9 @@ import unittest +from importlib import import_module + import importlib_resources as resources from . import util -from importlib import import_module class CommonBinaryTests(util.CommonTests, unittest.TestCase): diff --git a/importlib_resources/tests/test_reader.py b/importlib_resources/tests/test_reader.py index 0a77eb4..f8cfd8d 100644 --- a/importlib_resources/tests/test_reader.py +++ b/importlib_resources/tests/test_reader.py @@ -1,8 +1,8 @@ import os.path import pathlib import unittest - from importlib import import_module + from importlib_resources.readers import MultiplexedPath, NamespaceReader from . import util diff --git a/importlib_resources/tests/test_resource.py b/importlib_resources/tests/test_resource.py index a0da6a3..c80afdc 100644 --- a/importlib_resources/tests/test_resource.py +++ b/importlib_resources/tests/test_resource.py @@ -1,8 +1,9 @@ import unittest +from importlib import import_module + import importlib_resources as resources from . import util -from importlib import import_module class ResourceTests: diff --git a/importlib_resources/tests/util.py b/importlib_resources/tests/util.py index 5f75517..07d1529 100644 --- a/importlib_resources/tests/util.py +++ b/importlib_resources/tests/util.py @@ -1,18 +1,16 @@ import abc +import contextlib import importlib import io +import pathlib import sys import types -import pathlib -import contextlib +from importlib.machinery import ModuleSpec from ..abc import ResourceReader -from .compat.py39 import import_helper, os_helper -from . import zip as zip_ from . import _path - - -from importlib.machinery import ModuleSpec +from . import zip as zip_ +from .compat.py39 import import_helper, os_helper class Reader(ResourceReader): From 8e9503d90943335369219ba3bc088d2bcd4bca7f Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 3 Jan 2025 09:54:39 -0500 Subject: [PATCH 42/69] Add type annotations for Traversable.open. Closes #317. --- importlib_resources/abc.py | 18 +++++++++++++++--- newsfragments/317.feature.rst | 1 + 2 files changed, 16 insertions(+), 3 deletions(-) create mode 100644 newsfragments/317.feature.rst diff --git a/importlib_resources/abc.py b/importlib_resources/abc.py index c0a4cb9..39b0d37 100644 --- a/importlib_resources/abc.py +++ b/importlib_resources/abc.py @@ -1,5 +1,4 @@ import abc -import io import itertools import pathlib from typing import ( @@ -11,9 +10,14 @@ Optional, Protocol, Text, + TextIO, + Union, + overload, runtime_checkable, ) +from typing_extensions import Literal + from .compat.py38 import StrPath __all__ = ["ResourceReader", "Traversable", "TraversableResources"] @@ -138,8 +142,16 @@ def __truediv__(self, child: StrPath) -> "Traversable": """ return self.joinpath(child) + @overload + def open(self, mode: Literal['r'] = 'r', *args: Any, **kwargs: Any) -> TextIO: ... + + @overload + def open(self, mode: Literal['rb'], *args: Any, **kwargs: Any) -> BinaryIO: ... + @abc.abstractmethod - def open(self, mode='r', *args, **kwargs): + def open( + self, mode: str = 'r', *args: Any, **kwargs: Any + ) -> Union[TextIO, BinaryIO]: """ mode may be 'r' or 'rb' to open as text or binary. Return a handle suitable for reading (same as pathlib.Path.open). @@ -166,7 +178,7 @@ class TraversableResources(ResourceReader): def files(self) -> "Traversable": """Return a Traversable object for the loaded package.""" - def open_resource(self, resource: StrPath) -> io.BufferedReader: + def open_resource(self, resource: StrPath) -> BinaryIO: return self.files().joinpath(resource).open('rb') def resource_path(self, resource: Any) -> NoReturn: diff --git a/newsfragments/317.feature.rst b/newsfragments/317.feature.rst new file mode 100644 index 0000000..25b1a97 --- /dev/null +++ b/newsfragments/317.feature.rst @@ -0,0 +1 @@ +Add type annotations for Traversable.open. From 355a10db1644edcd92f876bd9aec4dd3839f7e75 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 3 Jan 2025 10:37:31 -0500 Subject: [PATCH 43/69] Remove Python 3.8 compatibility code. --- importlib_resources/abc.py | 3 ++- importlib_resources/compat/py38.py | 9 --------- newsfragments/+3f742cf7.feature.rst | 1 + 3 files changed, 3 insertions(+), 10 deletions(-) delete mode 100644 importlib_resources/compat/py38.py create mode 100644 newsfragments/+3f742cf7.feature.rst diff --git a/importlib_resources/abc.py b/importlib_resources/abc.py index 39b0d37..84631c7 100644 --- a/importlib_resources/abc.py +++ b/importlib_resources/abc.py @@ -1,5 +1,6 @@ import abc import itertools +import os import pathlib from typing import ( Any, @@ -18,7 +19,7 @@ from typing_extensions import Literal -from .compat.py38 import StrPath +StrPath = Union[str, os.PathLike[str]] __all__ = ["ResourceReader", "Traversable", "TraversableResources"] diff --git a/importlib_resources/compat/py38.py b/importlib_resources/compat/py38.py deleted file mode 100644 index 80940e9..0000000 --- a/importlib_resources/compat/py38.py +++ /dev/null @@ -1,9 +0,0 @@ -import os -import sys -from typing import Union - -if sys.version_info >= (3, 9): - StrPath = Union[str, os.PathLike[str]] -else: - # PathLike is only subscriptable at runtime in 3.9+ - StrPath = Union[str, "os.PathLike[str]"] diff --git a/newsfragments/+3f742cf7.feature.rst b/newsfragments/+3f742cf7.feature.rst new file mode 100644 index 0000000..f171cdd --- /dev/null +++ b/newsfragments/+3f742cf7.feature.rst @@ -0,0 +1 @@ +Require Python 3.9 or later. From fa27acb836d98ffa87794de37d33b7188aad082f Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 3 Jan 2025 10:38:40 -0500 Subject: [PATCH 44/69] Finalize --- NEWS.rst | 10 ++++++++++ newsfragments/+3f742cf7.feature.rst | 1 - newsfragments/317.feature.rst | 1 - 3 files changed, 10 insertions(+), 2 deletions(-) delete mode 100644 newsfragments/+3f742cf7.feature.rst delete mode 100644 newsfragments/317.feature.rst diff --git a/NEWS.rst b/NEWS.rst index 36aac37..c3612c1 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -1,3 +1,13 @@ +v6.5.0 +====== + +Features +-------- + +- Add type annotations for Traversable.open. (#317) +- Require Python 3.9 or later. + + v6.4.5 ====== diff --git a/newsfragments/+3f742cf7.feature.rst b/newsfragments/+3f742cf7.feature.rst deleted file mode 100644 index f171cdd..0000000 --- a/newsfragments/+3f742cf7.feature.rst +++ /dev/null @@ -1 +0,0 @@ -Require Python 3.9 or later. diff --git a/newsfragments/317.feature.rst b/newsfragments/317.feature.rst deleted file mode 100644 index 25b1a97..0000000 --- a/newsfragments/317.feature.rst +++ /dev/null @@ -1 +0,0 @@ -Add type annotations for Traversable.open. From 912a9e5dc1bc15352d053fa55c02c8a90eb7b3c6 Mon Sep 17 00:00:00 2001 From: Kurt McKee Date: Mon, 25 Nov 2024 11:13:49 -0600 Subject: [PATCH 45/69] Demonstrate python/cpython#127012 This adds an in-memory finder, loader, and traversable implementation, which allows the `Traversable` protocol and concrete methods to be tested. This additional infrastructure demonstrates python/cpython#127012, but also highlights that the `Traversable.joinpath()` concrete method raises `TraversalError` which is not getting caught in several places. --- importlib_resources/tests/test_functional.py | 21 ++-- importlib_resources/tests/test_util.py | 35 +++++++ importlib_resources/tests/util.py | 105 ++++++++++++++++++- 3 files changed, 153 insertions(+), 8 deletions(-) create mode 100644 importlib_resources/tests/test_util.py diff --git a/importlib_resources/tests/test_functional.py b/importlib_resources/tests/test_functional.py index 2285389..d65a52c 100644 --- a/importlib_resources/tests/test_functional.py +++ b/importlib_resources/tests/test_functional.py @@ -7,10 +7,6 @@ from . import util from .compat.py39 import warnings_helper -# Since the functional API forwards to Traversable, we only test -# filesystem resources here -- not zip files, namespace packages etc. -# We do test for two kinds of Anchor, though. - class StringAnchorMixin: anchor01 = 'data01' @@ -27,7 +23,7 @@ def anchor02(self): return importlib.import_module('data02') -class FunctionalAPIBase(util.DiskSetup): +class FunctionalAPIBase: def setUp(self): super().setUp() self.load_fixture('data02') @@ -244,17 +240,28 @@ def test_text_errors(self): ) -class FunctionalAPITest_StringAnchor( +class FunctionalAPITest_StringAnchor_Disk( StringAnchorMixin, FunctionalAPIBase, + util.DiskSetup, unittest.TestCase, ): pass -class FunctionalAPITest_ModuleAnchor( +class FunctionalAPITest_ModuleAnchor_Disk( ModuleAnchorMixin, FunctionalAPIBase, + util.DiskSetup, + unittest.TestCase, +): + pass + + +class FunctionalAPITest_StringAnchor_Memory( + StringAnchorMixin, + FunctionalAPIBase, + util.MemorySetup, unittest.TestCase, ): pass diff --git a/importlib_resources/tests/test_util.py b/importlib_resources/tests/test_util.py new file mode 100644 index 0000000..ba3ba28 --- /dev/null +++ b/importlib_resources/tests/test_util.py @@ -0,0 +1,35 @@ +import unittest + +from .util import MemorySetup, Traversable + + +class TestMemoryTraversableImplementation(unittest.TestCase): + def test_concrete_methods_are_not_overridden(self): + """`MemoryTraversable` must not override `Traversable` concrete methods. + + This test is not an attempt to enforce a particular `Traversable` protocol; + it merely catches changes in the `Traversable` abstract/concrete methods + that have not been mirrored in the `MemoryTraversable` subclass. + """ + + traversable_concrete_methods = { + method + for method, value in Traversable.__dict__.items() + if callable(value) and method not in Traversable.__abstractmethods__ + } + memory_traversable_concrete_methods = { + method + for method, value in MemorySetup.MemoryTraversable.__dict__.items() + if callable(value) and not method.startswith("__") + } + overridden_methods = ( + memory_traversable_concrete_methods & traversable_concrete_methods + ) + + if overridden_methods: + raise AssertionError( + "MemorySetup.MemoryTraversable overrides Traversable concrete methods, " + "which may mask problems in the Traversable protocol. " + "Please remove the following methods in MemoryTraversable: " + + ", ".join(overridden_methods) + ) diff --git a/importlib_resources/tests/util.py b/importlib_resources/tests/util.py index 07d1529..2f709d3 100644 --- a/importlib_resources/tests/util.py +++ b/importlib_resources/tests/util.py @@ -1,5 +1,6 @@ import abc import contextlib +import functools import importlib import io import pathlib @@ -7,7 +8,7 @@ import types from importlib.machinery import ModuleSpec -from ..abc import ResourceReader +from ..abc import ResourceReader, Traversable, TraversableResources from . import _path from . import zip as zip_ from .compat.py39 import import_helper, os_helper @@ -200,5 +201,107 @@ def tree_on_path(self, spec): self.fixtures.enter_context(import_helper.DirsOnSysPath(temp_dir)) +class MemorySetup(ModuleSetup): + """Support loading a module in memory.""" + + MODULE = 'data01' + + def load_fixture(self, module): + self.fixtures.enter_context(self.augment_sys_metapath(module)) + return importlib.import_module(module) + + @contextlib.contextmanager + def augment_sys_metapath(self, module): + finder_instance = self.MemoryFinder(module) + sys.meta_path.append(finder_instance) + yield + sys.meta_path.remove(finder_instance) + + class MemoryFinder(importlib.abc.MetaPathFinder): + def __init__(self, module): + self._module = module + + def find_spec(self, fullname, path, target=None): + if fullname != self._module: + return None + + return importlib.machinery.ModuleSpec( + name=fullname, + loader=MemorySetup.MemoryLoader(self._module), + is_package=True, + ) + + class MemoryLoader(importlib.abc.Loader): + def __init__(self, module): + self._module = module + + def exec_module(self, module): + pass + + def get_resource_reader(self, fullname): + return MemorySetup.MemoryTraversableResources(self._module, fullname) + + class MemoryTraversableResources(TraversableResources): + def __init__(self, module, fullname): + self._module = module + self._fullname = fullname + + def files(self): + return MemorySetup.MemoryTraversable(self._module, self._fullname) + + class MemoryTraversable(Traversable): + """Implement only the abstract methods of `Traversable`. + + Besides `.__init__()`, no other methods may be implemented or overridden. + This is critical for validating the concrete `Traversable` implementations. + """ + + def __init__(self, module, fullname): + self._module = module + self._fullname = fullname + + def iterdir(self): + path = pathlib.PurePosixPath(self._fullname) + directory = functools.reduce(lambda d, p: d[p], path.parts, fixtures) + if not isinstance(directory, dict): + # Filesystem openers raise OSError, and that exception is mirrored here. + raise OSError(f"{self._fullname} is not a directory") + for path in directory: + yield MemorySetup.MemoryTraversable( + self._module, f"{self._fullname}/{path}" + ) + + def is_dir(self) -> bool: + path = pathlib.PurePosixPath(self._fullname) + # Fully traverse the `fixtures` dictionary. + # This should be wrapped in a `try/except KeyError` + # but it is not currently needed, and lowers the code coverage numbers. + directory = functools.reduce(lambda d, p: d[p], path.parts, fixtures) + return isinstance(directory, dict) + + def is_file(self) -> bool: + path = pathlib.PurePosixPath(self._fullname) + directory = functools.reduce(lambda d, p: d[p], path.parts, fixtures) + return not isinstance(directory, dict) + + def open(self, mode='r', encoding=None, errors=None, *_, **__): + path = pathlib.PurePosixPath(self._fullname) + contents = functools.reduce(lambda d, p: d[p], path.parts, fixtures) + if isinstance(contents, dict): + # Filesystem openers raise OSError when attempting to open a directory, + # and that exception is mirrored here. + raise OSError(f"{self._fullname} is a directory") + if isinstance(contents, str): + contents = contents.encode("utf-8") + result = io.BytesIO(contents) + if "b" in mode: + return result + return io.TextIOWrapper(result, encoding=encoding, errors=errors) + + @property + def name(self): + return pathlib.PurePosixPath(self._fullname).name + + class CommonTests(DiskSetup, CommonTestsBase): pass From f10a2e9b2608cdf5006608dd7f2b29d3f0b1cbd2 Mon Sep 17 00:00:00 2001 From: Kurt McKee Date: Mon, 25 Nov 2024 11:22:52 -0600 Subject: [PATCH 46/69] Catch `TraversalError`, raised by `Traversable.joinpath()` Exercising the `Traversable` protocol's concrete methods has highlighted that `.joinpath()` raises `TraversalError`, which needs to be caught in several places. This is primarily resolved within the test suite, but implicates the `is_resource()` function as well. --- importlib_resources/_functional.py | 6 +++++- importlib_resources/tests/test_functional.py | 6 +++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/importlib_resources/_functional.py b/importlib_resources/_functional.py index 5a3ca0a..b08a5c6 100644 --- a/importlib_resources/_functional.py +++ b/importlib_resources/_functional.py @@ -3,6 +3,7 @@ import warnings from ._common import as_file, files +from .abc import TraversalError _MISSING = object() @@ -41,7 +42,10 @@ def is_resource(anchor, *path_names): Otherwise returns ``False``. """ - return _get_resource(anchor, path_names).is_file() + try: + return _get_resource(anchor, path_names).is_file() + except TraversalError: + return False def contents(anchor, *path_names): diff --git a/importlib_resources/tests/test_functional.py b/importlib_resources/tests/test_functional.py index d65a52c..9eb2d81 100644 --- a/importlib_resources/tests/test_functional.py +++ b/importlib_resources/tests/test_functional.py @@ -72,7 +72,7 @@ def test_read_text(self): # fail with PermissionError rather than IsADirectoryError with self.assertRaises(OSError): resources.read_text(self.anchor01) - with self.assertRaises(OSError): + with self.assertRaises((OSError, resources.abc.TraversalError)): resources.read_text(self.anchor01, 'no-such-file') with self.assertRaises(UnicodeDecodeError): resources.read_text(self.anchor01, 'utf-16.file') @@ -120,7 +120,7 @@ def test_open_text(self): # fail with PermissionError rather than IsADirectoryError with self.assertRaises(OSError): resources.open_text(self.anchor01) - with self.assertRaises(OSError): + with self.assertRaises((OSError, resources.abc.TraversalError)): resources.open_text(self.anchor01, 'no-such-file') with resources.open_text(self.anchor01, 'utf-16.file') as f: with self.assertRaises(UnicodeDecodeError): @@ -188,7 +188,7 @@ def test_contents(self): for path_parts in self._gen_resourcetxt_path_parts(): with ( - self.assertRaises(OSError), + self.assertRaises((OSError, resources.abc.TraversalError)), warnings_helper.check_warnings(( ".*contents.*", DeprecationWarning, From d001110ce83b32ad5fa7bae3932a8db1faccbb8b Mon Sep 17 00:00:00 2001 From: Kurt McKee Date: Mon, 25 Nov 2024 11:29:05 -0600 Subject: [PATCH 47/69] Resolve a `TypeError` lurking in the `read_text()` functional API `importlib_resources.read_text()` calls the `Traversable.read_text()` concrete method with an `errors` argument that doesn't exist in the method signature, resulting in an `TypeError`. This is resolved by adding an `errors` parameter to `Traversable.read_text()`. Fixes python/cpython#127012 --- importlib_resources/abc.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/importlib_resources/abc.py b/importlib_resources/abc.py index 84631c7..befe799 100644 --- a/importlib_resources/abc.py +++ b/importlib_resources/abc.py @@ -93,11 +93,13 @@ def read_bytes(self) -> bytes: with self.open('rb') as strm: return strm.read() - def read_text(self, encoding: Optional[str] = None) -> str: + def read_text( + self, encoding: Optional[str] = None, errors: Optional[str] = None + ) -> str: """ Read contents of self as text """ - with self.open(encoding=encoding) as strm: + with self.open(encoding=encoding, errors=errors) as strm: return strm.read() @abc.abstractmethod From 72d550dfabc4a56fa22ec1e347b2672d546a39a4 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 3 Jan 2025 11:33:06 -0500 Subject: [PATCH 48/69] Consolidate MemoryTraversable._resolve. --- importlib_resources/tests/util.py | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/importlib_resources/tests/util.py b/importlib_resources/tests/util.py index 2f709d3..0340c15 100644 --- a/importlib_resources/tests/util.py +++ b/importlib_resources/tests/util.py @@ -260,9 +260,18 @@ def __init__(self, module, fullname): self._module = module self._fullname = fullname - def iterdir(self): + def _resolve(self): + """ + Fully traverse the `fixtures` dictionary. + + This should be wrapped in a `try/except KeyError` + but it is not currently needed and lowers the code coverage numbers. + """ path = pathlib.PurePosixPath(self._fullname) - directory = functools.reduce(lambda d, p: d[p], path.parts, fixtures) + return functools.reduce(lambda d, p: d[p], path.parts, fixtures) + + def iterdir(self): + directory = self._resolve() if not isinstance(directory, dict): # Filesystem openers raise OSError, and that exception is mirrored here. raise OSError(f"{self._fullname} is not a directory") @@ -272,21 +281,13 @@ def iterdir(self): ) def is_dir(self) -> bool: - path = pathlib.PurePosixPath(self._fullname) - # Fully traverse the `fixtures` dictionary. - # This should be wrapped in a `try/except KeyError` - # but it is not currently needed, and lowers the code coverage numbers. - directory = functools.reduce(lambda d, p: d[p], path.parts, fixtures) - return isinstance(directory, dict) + return isinstance(self._resolve(), dict) def is_file(self) -> bool: - path = pathlib.PurePosixPath(self._fullname) - directory = functools.reduce(lambda d, p: d[p], path.parts, fixtures) - return not isinstance(directory, dict) + return not self.is_dir() def open(self, mode='r', encoding=None, errors=None, *_, **__): - path = pathlib.PurePosixPath(self._fullname) - contents = functools.reduce(lambda d, p: d[p], path.parts, fixtures) + contents = self._resolve() if isinstance(contents, dict): # Filesystem openers raise OSError when attempting to open a directory, # and that exception is mirrored here. From cf269ce50f496671f3b7fbc5e6292946ecc70e7d Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 3 Jan 2025 11:42:13 -0500 Subject: [PATCH 49/69] Replace unreachable block with simple assertion. Fixes diffcov failure. --- importlib_resources/tests/test_util.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/importlib_resources/tests/test_util.py b/importlib_resources/tests/test_util.py index ba3ba28..de304b6 100644 --- a/importlib_resources/tests/test_util.py +++ b/importlib_resources/tests/test_util.py @@ -26,10 +26,4 @@ def test_concrete_methods_are_not_overridden(self): memory_traversable_concrete_methods & traversable_concrete_methods ) - if overridden_methods: - raise AssertionError( - "MemorySetup.MemoryTraversable overrides Traversable concrete methods, " - "which may mask problems in the Traversable protocol. " - "Please remove the following methods in MemoryTraversable: " - + ", ".join(overridden_methods) - ) + assert not overridden_methods From 9a872e5dbceff32260e8ff19d039236304ee150c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 3 Jan 2025 11:46:18 -0500 Subject: [PATCH 50/69] Add news fragment. --- newsfragments/321.bugfix.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 newsfragments/321.bugfix.rst diff --git a/newsfragments/321.bugfix.rst b/newsfragments/321.bugfix.rst new file mode 100644 index 0000000..788cd32 --- /dev/null +++ b/newsfragments/321.bugfix.rst @@ -0,0 +1 @@ +Updated ``Traversable.read_text()`` to reflect the ``errors`` parameter (python/cpython#127012). From 78c4bda73c5d671cbbcfdf1430b6f2da03aeb04f Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 3 Jan 2025 11:54:59 -0500 Subject: [PATCH 51/69] Finalize --- NEWS.rst | 9 +++++++++ newsfragments/321.bugfix.rst | 1 - 2 files changed, 9 insertions(+), 1 deletion(-) delete mode 100644 newsfragments/321.bugfix.rst diff --git a/NEWS.rst b/NEWS.rst index c3612c1..48899f0 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -1,3 +1,12 @@ +v6.5.1 +====== + +Bugfixes +-------- + +- Updated ``Traversable.read_text()`` to reflect the ``errors`` parameter (python/cpython#127012). (#321) + + v6.5.0 ====== diff --git a/newsfragments/321.bugfix.rst b/newsfragments/321.bugfix.rst deleted file mode 100644 index 788cd32..0000000 --- a/newsfragments/321.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Updated ``Traversable.read_text()`` to reflect the ``errors`` parameter (python/cpython#127012). From a61a948b6293ed46972909652909e2540242f197 Mon Sep 17 00:00:00 2001 From: "Peter St. John" Date: Fri, 3 Jan 2025 10:14:43 -0700 Subject: [PATCH 52/69] Add typing-extensions as a dependency --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 608e751..1e15158 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,7 +21,7 @@ classifiers = [ ] requires-python = ">=3.9" dependencies = [ - "zipp >= 3.1.0; python_version < '3.10'", + "zipp >= 3.1.0; typing-extensions >= 3.10; python_version < '3.10'", ] dynamic = ["version"] From 883380a5fcc482b67014b90b9aa7250d868c795a Mon Sep 17 00:00:00 2001 From: "Peter St. John" Date: Fri, 3 Jan 2025 11:45:51 -0700 Subject: [PATCH 53/69] Update pyproject.toml --- pyproject.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 1e15158..25487d5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,7 +21,8 @@ classifiers = [ ] requires-python = ">=3.9" dependencies = [ - "zipp >= 3.1.0; typing-extensions >= 3.10; python_version < '3.10'", + "zipp >= 3.1.0; python_version < '3.10'", + "typing-extensions >= 3.10", ] dynamic = ["version"] From 6c1bc3cdb8f8a7d29252becc201508712040facc Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 3 Jan 2025 13:45:20 -0500 Subject: [PATCH 54/69] Rely on Literal from stdlib. --- importlib_resources/abc.py | 3 +-- pyproject.toml | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/importlib_resources/abc.py b/importlib_resources/abc.py index befe799..883d332 100644 --- a/importlib_resources/abc.py +++ b/importlib_resources/abc.py @@ -8,6 +8,7 @@ Iterable, Iterator, NoReturn, + Literal, Optional, Protocol, Text, @@ -17,8 +18,6 @@ runtime_checkable, ) -from typing_extensions import Literal - StrPath = Union[str, os.PathLike[str]] __all__ = ["ResourceReader", "Traversable", "TraversableResources"] diff --git a/pyproject.toml b/pyproject.toml index 25487d5..608e751 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,7 +22,6 @@ classifiers = [ requires-python = ">=3.9" dependencies = [ "zipp >= 3.1.0; python_version < '3.10'", - "typing-extensions >= 3.10", ] dynamic = ["version"] From 6569354ad2cf38b202fdeaf11b49e639b1a00fbc Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 3 Jan 2025 13:46:39 -0500 Subject: [PATCH 55/69] Add news fragment. --- newsfragments/323.bugfix.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 newsfragments/323.bugfix.rst diff --git a/newsfragments/323.bugfix.rst b/newsfragments/323.bugfix.rst new file mode 100644 index 0000000..964c119 --- /dev/null +++ b/newsfragments/323.bugfix.rst @@ -0,0 +1 @@ +Replaced reference to typing_extensions with stdlib Literal. From 7d3b2bfa1d8c53ecb64246ba16d8c632f8fcb6bf Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 3 Jan 2025 13:48:58 -0500 Subject: [PATCH 56/69] Finalize --- NEWS.rst | 9 +++++++++ newsfragments/323.bugfix.rst | 1 - 2 files changed, 9 insertions(+), 1 deletion(-) delete mode 100644 newsfragments/323.bugfix.rst diff --git a/NEWS.rst b/NEWS.rst index 48899f0..a7c95e8 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -1,3 +1,12 @@ +v6.5.2 +====== + +Bugfixes +-------- + +- Replaced reference to typing_extensions with stdlib Literal. (#323) + + v6.5.1 ====== diff --git a/newsfragments/323.bugfix.rst b/newsfragments/323.bugfix.rst deleted file mode 100644 index 964c119..0000000 --- a/newsfragments/323.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Replaced reference to typing_extensions with stdlib Literal. From aee344d781920bba42ddbee4b4b44af29d7bab6e Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 12 Feb 2025 10:44:24 -0500 Subject: [PATCH 57/69] Removing dependabot config. Closes jaraco/skeleton#156 --- .github/dependabot.yml | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index 89ff339..0000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,8 +0,0 @@ -version: 2 -updates: - - package-ecosystem: "pip" - directory: "/" - schedule: - interval: "daily" - allow: - - dependency-type: "all" From 75ce9aba3ed9f4002fa01db0287dfdb1600fb635 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 23 Feb 2025 18:57:40 -0500 Subject: [PATCH 58/69] Add support for building lxml on pre-release Pythons. Closes jaraco/skeleton#161 --- .github/workflows/main.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9c01fc4..5841cc3 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -56,6 +56,13 @@ jobs: continue-on-error: ${{ matrix.python == '3.14' }} steps: - uses: actions/checkout@v4 + - name: Install build dependencies + # Install dependencies for building packages on pre-release Pythons + # jaraco/skeleton#161 + if: matrix.python == '3.14' && matrix.platform == 'ubuntu-latest' + run: | + sudo apt update + sudo apt install -y libxml2-dev libxslt-dev - name: Setup Python uses: actions/setup-python@v4 with: From 1c9467fdec1cc1456772cd71c7e740f048ce86fc Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Mon, 24 Feb 2025 22:00:11 +0000 Subject: [PATCH 59/69] Fix new mandatory configuration field for RTD (jaraco/skeleton#159) This field is now required and prevents the build from running if absent. Details in https://about.readthedocs.com/blog/2024/12/deprecate-config-files-without-sphinx-or-mkdocs-config/ --- .readthedocs.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index dc8516a..7243706 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -5,6 +5,9 @@ python: extra_requirements: - doc +sphinx: + configuration: docs/conf.py + # required boilerplate readthedocs/readthedocs.org#10401 build: os: ubuntu-lts-latest From 1a2f93053d789f041d88c97c5da4eea9e949bdfe Mon Sep 17 00:00:00 2001 From: Avasam Date: Tue, 25 Feb 2025 13:21:13 -0500 Subject: [PATCH 60/69] Select Ruff rules for modern type annotations (jaraco/skeleton#160) * Select Ruff rules for modern type annotations Ensure modern type annotation syntax and best practices Not including those covered by type-checkers or exclusive to Python 3.11+ Not including rules currently in preview either. These are the same set of rules I have in pywin32 as of https://github.com/mhammond/pywin32/pull/2458 setuptools has all the same rules enabled (except it also includes the `UP` group directly) * Add PYI011 ignore and #local section * Update ruff.toml Co-authored-by: Jason R. Coombs * Add # upstream --------- Co-authored-by: Jason R. Coombs --- ruff.toml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/ruff.toml b/ruff.toml index 9379d6e..1d65c7c 100644 --- a/ruff.toml +++ b/ruff.toml @@ -3,11 +3,32 @@ extend = "pyproject.toml" [lint] extend-select = [ + # upstream + "C901", "PERF401", "W", + + # Ensure modern type annotation syntax and best practices + # Not including those covered by type-checkers or exclusive to Python 3.11+ + "FA", # flake8-future-annotations + "F404", # late-future-import + "PYI", # flake8-pyi + "UP006", # non-pep585-annotation + "UP007", # non-pep604-annotation + "UP010", # unnecessary-future-import + "UP035", # deprecated-import + "UP037", # quoted-annotation + "UP043", # unnecessary-default-type-args + + # local ] ignore = [ + # upstream + + # Typeshed rejects complex or non-literal defaults for maintenance and testing reasons, + # irrelevant to this project. + "PYI011", # typed-argument-default-in-stub # https://docs.astral.sh/ruff/formatter/#conflicting-lint-rules "W191", "E111", @@ -23,6 +44,8 @@ ignore = [ "COM819", "ISC001", "ISC002", + + # local ] [format] From aa891069099398fe2eb294ac4b781460d8c0a39b Mon Sep 17 00:00:00 2001 From: Avasam Date: Wed, 26 Feb 2025 17:56:42 -0500 Subject: [PATCH 61/69] Consistent import sorting (isort) (jaraco/skeleton#157) --- ruff.toml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ruff.toml b/ruff.toml index 1d65c7c..b52a6d7 100644 --- a/ruff.toml +++ b/ruff.toml @@ -5,9 +5,10 @@ extend = "pyproject.toml" extend-select = [ # upstream - "C901", - "PERF401", - "W", + "C901", # complex-structure + "I", # isort + "PERF401", # manual-list-comprehension + "W", # pycodestyle Warning # Ensure modern type annotation syntax and best practices # Not including those covered by type-checkers or exclusive to Python 3.11+ From 8f42595ca65133aeb4b75f38183233c27b2e6247 Mon Sep 17 00:00:00 2001 From: Dimitri Papadopoulos Orfanos <3234522+DimitriPapadopoulos@users.noreply.github.com> Date: Fri, 28 Feb 2025 00:19:07 +0100 Subject: [PATCH 62/69] Enable ruff rules ISC001/ISC002 (jaraco/skeleton#158) Starting with ruff 0.9.1, they are compatible with the ruff formatter when used together. --- ruff.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/ruff.toml b/ruff.toml index b52a6d7..2b67926 100644 --- a/ruff.toml +++ b/ruff.toml @@ -43,8 +43,6 @@ ignore = [ "Q003", "COM812", "COM819", - "ISC001", - "ISC002", # local ] From b7d4b6ee00804bef36a8c398676e207813540c3b Mon Sep 17 00:00:00 2001 From: Avasam Date: Tue, 4 Mar 2025 03:24:14 -0500 Subject: [PATCH 63/69] remove extra spaces in ruff.toml (jaraco/skeleton#164) --- ruff.toml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ruff.toml b/ruff.toml index 2b67926..1e95284 100644 --- a/ruff.toml +++ b/ruff.toml @@ -4,13 +4,13 @@ extend = "pyproject.toml" [lint] extend-select = [ # upstream - + "C901", # complex-structure "I", # isort "PERF401", # manual-list-comprehension "W", # pycodestyle Warning - - # Ensure modern type annotation syntax and best practices + + # Ensure modern type annotation syntax and best practices # Not including those covered by type-checkers or exclusive to Python 3.11+ "FA", # flake8-future-annotations "F404", # late-future-import @@ -26,7 +26,7 @@ extend-select = [ ] ignore = [ # upstream - + # Typeshed rejects complex or non-literal defaults for maintenance and testing reasons, # irrelevant to this project. "PYI011", # typed-argument-default-in-stub @@ -44,7 +44,7 @@ ignore = [ "COM812", "COM819", - # local + # local ] [format] From b00e9dd730423a399c1d3c3d5621687adff0c5a5 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 7 Mar 2025 09:05:55 -0500 Subject: [PATCH 64/69] Remove pycodestyle warnings, no longer meaningful when using ruff formatter. Ref https://github.com/jaraco/skeleton/commit/d1c5444126aeacefee3949b30136446ab99979d8#commitcomment-153409678 --- ruff.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/ruff.toml b/ruff.toml index 1e95284..267a1ba 100644 --- a/ruff.toml +++ b/ruff.toml @@ -8,7 +8,6 @@ extend-select = [ "C901", # complex-structure "I", # isort "PERF401", # manual-list-comprehension - "W", # pycodestyle Warning # Ensure modern type annotation syntax and best practices # Not including those covered by type-checkers or exclusive to Python 3.11+ From d587ff737ee89778cf6f4bbd249e770c965fee06 Mon Sep 17 00:00:00 2001 From: Dimitri Papadopoulos Orfanos <3234522+DimitriPapadopoulos@users.noreply.github.com> Date: Fri, 7 Mar 2025 15:08:11 +0100 Subject: [PATCH 65/69] Update to the latest ruff version (jaraco/skeleton#166) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 04870d1..633e364 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.7.1 + rev: v0.9.9 hooks: - id: ruff args: [--fix, --unsafe-fixes] From ad84110008b826efd6e39bcc39b9998b4f1cc767 Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Fri, 21 Mar 2025 00:14:38 +0000 Subject: [PATCH 66/69] Remove deprecated license classifier (PEP 639) (jaraco/skeleton#170) --- pyproject.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 328b98c..71b1a7d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,7 +12,6 @@ readme = "README.rst" classifiers = [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", ] From 1ebb559a507f97ece7342d7f1532a49188cade33 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Thu, 20 Mar 2025 20:56:31 -0400 Subject: [PATCH 67/69] Remove workaround and update badge. Closes jaraco/skeleton#155 --- README.rst | 2 +- ruff.toml | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/README.rst b/README.rst index 4d3cabe..3000f5a 100644 --- a/README.rst +++ b/README.rst @@ -7,7 +7,7 @@ :target: https://github.com/PROJECT_PATH/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 +.. image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json :target: https://github.com/astral-sh/ruff :alt: Ruff diff --git a/ruff.toml b/ruff.toml index 267a1ba..63c0825 100644 --- a/ruff.toml +++ b/ruff.toml @@ -1,6 +1,3 @@ -# extend pyproject.toml for requires-python (workaround astral-sh/ruff#10299) -extend = "pyproject.toml" - [lint] extend-select = [ # upstream From 979e626055ab60095b37be04555a01a40f62e470 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 22 Mar 2025 05:33:58 -0400 Subject: [PATCH 68/69] Remove PIP_NO_PYTHON_VERSION_WARNING. Ref pypa/pip#13154 --- .github/workflows/main.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5841cc3..928acf2 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -21,7 +21,6 @@ env: # 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 From 9a81db3c77bc106017dcd4b0853a5a94f43ae33c Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 3 May 2025 03:57:47 -0400 Subject: [PATCH 69/69] Replace copy of license with an SPDX identifier. (jaraco/skeleton#171) --- LICENSE | 17 ----------------- pyproject.toml | 1 + 2 files changed, 1 insertion(+), 17 deletions(-) delete mode 100644 LICENSE diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 1bb5a44..0000000 --- a/LICENSE +++ /dev/null @@ -1,17 +0,0 @@ -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/pyproject.toml b/pyproject.toml index 71b1a7d..fa0c801 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,6 +16,7 @@ classifiers = [ "Programming Language :: Python :: 3 :: Only", ] requires-python = ">=3.9" +license = "MIT" dependencies = [ ] dynamic = ["version"]