diff --git a/.changelog/_unreleased.toml b/.changelog/_unreleased.toml index e69de29..9da389f 100644 --- a/.changelog/_unreleased.toml +++ b/.changelog/_unreleased.toml @@ -0,0 +1,6 @@ +[[entries]] +id = "67afd70b-b66e-4aeb-ab30-becf3f121681" +type = "fix" +description = "Fix `Union` entry point discovery by no longer passing the `group` keyword to `importlib.metadata.entry_points()`, drop the deprecated `pkg_resources` fallback (`importlib.metadata` is used on all supported Python versions), and remove the now-unused `setuptools` runtime dependency on Python <3.9" +author = "@NiklasRosenstein" +pr = "https://github.com/NiklasRosenstein/python-databind/pull/84" diff --git a/databind/pyproject.toml b/databind/pyproject.toml index a161ce9..4a02357 100644 --- a/databind/pyproject.toml +++ b/databind/pyproject.toml @@ -14,7 +14,6 @@ dependencies = [ "Deprecated>=1.2.12,<2", "nr-date>=2.0.0,<3", "nr-stream>=1.0.0,<2", - "setuptools>=40.8.0,<82; python_version < '3.9'", "typeapi>=2.0.1,<3", "typing-extensions>=3.10.0,<5", ] diff --git a/databind/src/databind/core/tests/union_test.py b/databind/src/databind/core/tests/union_test.py new file mode 100644 index 0000000..4b979e5 --- /dev/null +++ b/databind/src/databind/core/tests/union_test.py @@ -0,0 +1,45 @@ +from __future__ import annotations + +import typing as t + +from pytest import MonkeyPatch + +import databind.core.union as union_module + + +def test_iter_entry_points_uses_select_api_without_call_arguments(monkeypatch: MonkeyPatch) -> None: + result = [object(), object()] + + class EntryPoints: + def __init__(self) -> None: + self.selected_group: str | None = None + + def select(self, *, group: str) -> list[object]: + self.selected_group = group + return result + + eps = EntryPoints() + + def fake_entry_points(*args: object, **kwargs: object) -> object: + assert args == () + assert kwargs == {} + return eps + + monkeypatch.setattr(union_module, "entry_points", fake_entry_points) + + assert list(union_module.iter_entry_points("my.group")) == t.cast(t.Any, result) + assert eps.selected_group == "my.group" + + +def test_iter_entry_points_falls_back_to_mapping_result(monkeypatch: MonkeyPatch) -> None: + result = [object(), object()] + + def fake_entry_points(*args: object, **kwargs: object) -> object: + assert args == () + assert kwargs == {} + return {"my.group": result} + + monkeypatch.setattr(union_module, "entry_points", fake_entry_points) + + assert list(union_module.iter_entry_points("my.group")) == t.cast(t.Any, result) + assert list(union_module.iter_entry_points("missing")) == [] diff --git a/databind/src/databind/core/union.py b/databind/src/databind/core/union.py index 379ffc8..7f624cc 100644 --- a/databind/src/databind/core/union.py +++ b/databind/src/databind/core/union.py @@ -3,21 +3,20 @@ import abc import dataclasses import importlib -import sys import types import typing as t +from importlib.metadata import EntryPoint, entry_points from typeapi import ClassTypeHint, LiteralTypeHint, TypeHint from databind.core.utils import T -if sys.version_info[:2] < (3, 9): - from pkg_resources import EntryPoint, iter_entry_points # type: ignore[import-not-found,unused-ignore] -else: - from importlib.metadata import EntryPoint, entry_points - def iter_entry_points(group: str) -> t.Iterator[EntryPoint]: - return iter(entry_points(group=group)) +def iter_entry_points(group: str) -> t.Iterator[EntryPoint]: + eps = entry_points() + if hasattr(eps, "select"): + return iter(eps.select(group=group)) + return iter(eps.get(group, ())) __all__ = ["UnionMembers", "StaticUnionMembers", "EntrypointUnionMembers", "ImportUnionMembers", "ChainUnionMembers"] diff --git a/pyproject.toml b/pyproject.toml index e8688a4..9c1bccb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,7 +25,6 @@ dev = [ "mypy>=1.9.0,<2.0.0", "types-dataclasses", "types-deprecated", - "types-setuptools", "types-termcolor", ] docs = [ diff --git a/uv.lock b/uv.lock index 0ac9fb6..a631c9a 100644 --- a/uv.lock +++ b/uv.lock @@ -278,7 +278,6 @@ dependencies = [ { name = "deprecated" }, { name = "nr-date" }, { name = "nr-stream" }, - { name = "setuptools", marker = "python_full_version < '3.9'" }, { name = "typeapi" }, { name = "typing-extensions", version = "4.13.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, { name = "typing-extensions", version = "4.15.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, @@ -289,7 +288,6 @@ requires-dist = [ { name = "deprecated", specifier = ">=1.2.12,<2" }, { name = "nr-date", specifier = ">=2.0.0,<3" }, { name = "nr-stream", specifier = ">=1.0.0,<2" }, - { name = "setuptools", marker = "python_full_version < '3.9'", specifier = ">=40.8.0,<82" }, { name = "typeapi", specifier = ">=2.0.1,<3" }, { name = "typing-extensions", specifier = ">=3.10.0,<5" }, ] @@ -340,9 +338,6 @@ dev = [ { name = "types-dataclasses" }, { name = "types-deprecated", version = "1.2.15.20241117", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, { name = "types-deprecated", version = "1.3.1.20260130", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, - { name = "types-setuptools", version = "75.8.0.20250110", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, - { name = "types-setuptools", version = "81.0.0.20260209", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, - { name = "types-setuptools", version = "82.0.0.20260210", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, { name = "types-termcolor" }, ] docs = [ @@ -368,7 +363,6 @@ dev = [ { name = "pytest", specifier = ">=8.1.1,<9.0.0" }, { name = "types-dataclasses" }, { name = "types-deprecated" }, - { name = "types-setuptools" }, { name = "types-termcolor" }, ] docs = [ @@ -1435,15 +1429,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/56/5d/c814546c2333ceea4ba42262d8c4d55763003e767fa169adc693bd524478/requests-2.33.0-py3-none-any.whl", hash = "sha256:3324635456fa185245e24865e810cecec7b4caf933d7eb133dcde67d48cee69b", size = 65017, upload-time = "2026-03-25T15:10:40.382Z" }, ] -[[package]] -name = "setuptools" -version = "75.3.4" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/0e/93/d2622cbf262418995140dfc1ddb890badd6322893fa122302577c82b9617/setuptools-75.3.4.tar.gz", hash = "sha256:b4ea3f76e1633c4d2d422a5d68ab35fd35402ad71e6acaa5d7e5956eb47e8887", size = 1354595, upload-time = "2026-02-08T14:12:34.287Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cd/b1/961ba076c7d3732e3a97e6681c55ef647afb795fe8bfcd27becec8a762ce/setuptools-75.3.4-py3-none-any.whl", hash = "sha256:2dd50a7f42dddfa1d02a36f275dbe716f38ed250224f609d35fb60a09593d93e", size = 1251633, upload-time = "2026-02-08T14:12:32.364Z" }, -] - [[package]] name = "six" version = "1.17.0" @@ -1556,44 +1541,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d2/b2/6f920582af7efcd37165cd6321707f3ad5839dd24565a8a982f2bd9c6fd1/types_deprecated-1.3.1.20260130-py3-none-any.whl", hash = "sha256:593934d85c38ca321a9d301f00c42ffe13e4cf830b71b10579185ba0ce172d9a", size = 9077, upload-time = "2026-01-30T03:58:16.633Z" }, ] -[[package]] -name = "types-setuptools" -version = "75.8.0.20250110" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.8.1' and python_full_version < '3.9'", - "python_full_version < '3.8.1'", -] -sdist = { url = "https://files.pythonhosted.org/packages/f7/42/5713e90d4f9683f2301d900f33e4fc2405ad8ac224dda30f6cb7f4cd215b/types_setuptools-75.8.0.20250110.tar.gz", hash = "sha256:96f7ec8bbd6e0a54ea180d66ad68ad7a1d7954e7281a710ea2de75e355545271", size = 48185, upload-time = "2025-01-10T02:45:52.085Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cf/a3/dbfd106751b11c728cec21cc62cbfe7ff7391b935c4b6e8f0bdc2e6fd541/types_setuptools-75.8.0.20250110-py3-none-any.whl", hash = "sha256:a9f12980bbf9bcdc23ecd80755789085bad6bfce4060c2275bc2b4ca9f2bc480", size = 71521, upload-time = "2025-01-10T02:45:49.873Z" }, -] - -[[package]] -name = "types-setuptools" -version = "81.0.0.20260209" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version == '3.9.*'", -] -sdist = { url = "https://files.pythonhosted.org/packages/9e/57/f1f7992d6d7bded78d1f14dc23d59e87601920852bf10ece2325e49bacae/types_setuptools-81.0.0.20260209.tar.gz", hash = "sha256:2c2eb64499b41b672c387f6f45678a28d20a143a81b45a5c77acbfd4da0df3e1", size = 43201, upload-time = "2026-02-09T04:14:15.505Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/87/90c9143af95850bdaf7eb0d47c59e5c3a8b55fc5a49aca0eb7f98cb964d5/types_setuptools-81.0.0.20260209-py3-none-any.whl", hash = "sha256:4facf71e3f953f8f5ac0020cd6c1b5e493aaff0183e85830bc34870b6abf8475", size = 64194, upload-time = "2026-02-09T04:14:14.278Z" }, -] - -[[package]] -name = "types-setuptools" -version = "82.0.0.20260210" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.11'", - "python_full_version == '3.10.*'", -] -sdist = { url = "https://files.pythonhosted.org/packages/4b/90/796ac8c774a7f535084aacbaa6b7053d16fff5c630eff87c3ecff7896c37/types_setuptools-82.0.0.20260210.tar.gz", hash = "sha256:d9719fbbeb185254480ade1f25327c4654f8c00efda3fec36823379cebcdee58", size = 44768, upload-time = "2026-02-10T04:22:02.107Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3e/54/3489432b1d9bc713c9d8aa810296b8f5b0088403662959fb63a8acdbd4fc/types_setuptools-82.0.0.20260210-py3-none-any.whl", hash = "sha256:5124a7daf67f195c6054e0f00f1d97c69caad12fdcf9113eba33eff0bce8cd2b", size = 68433, upload-time = "2026-02-10T04:22:00.876Z" }, -] - [[package]] name = "types-termcolor" version = "1.1.6.2"