diff --git a/changelog.d/1268.breaking.md b/changelog.d/1268.breaking.md new file mode 100644 index 000000000..25ce9c630 --- /dev/null +++ b/changelog.d/1268.breaking.md @@ -0,0 +1,3 @@ +All packaging metadata except from `__version__` and `__version_info__` has been removed from the `attr` and `attrs` modules (for example, `attrs.__url__`). + +Please use [`importlib.metadata`](https://docs.python.org/3/library/importlib.metadata.html) or [*importlib_metadata*](https://pypi.org/project/importlib-metadata/) instead. diff --git a/src/attr/__init__.py b/src/attr/__init__.py index 9226258a2..f2db09f7c 100644 --- a/src/attr/__init__.py +++ b/src/attr/__init__.py @@ -79,54 +79,21 @@ def _make_getattr(mod_name: str) -> Callable: """ def __getattr__(name: str) -> str: - dunder_to_metadata = { - "__title__": "Name", - "__copyright__": "", - "__version__": "version", - "__version_info__": "version", - "__description__": "summary", - "__uri__": "", - "__url__": "", - "__author__": "", - "__email__": "", - "__license__": "license", - } - if name not in dunder_to_metadata: + if name not in ("__version__", "__version_info__"): msg = f"module {mod_name} has no attribute {name}" raise AttributeError(msg) - import sys - import warnings - - if sys.version_info < (3, 8): - from importlib_metadata import metadata - else: + try: from importlib.metadata import metadata - - if name not in ("__version__", "__version_info__"): - warnings.warn( - f"Accessing {mod_name}.{name} is deprecated and will be " - "removed in a future release. Use importlib.metadata directly " - "to query for attrs's packaging metadata.", - DeprecationWarning, - stacklevel=2, - ) + except ImportError: + from importlib_metadata import metadata meta = metadata("attrs") - if name == "__license__": - return "MIT" - if name == "__copyright__": - return "Copyright (c) 2015 Hynek Schlawack" - if name in ("__uri__", "__url__"): - return meta["Project-URL"].split(" ", 1)[-1] + if name == "__version_info__": return VersionInfo._from_version_string(meta["version"]) - if name == "__author__": - return meta["Author-email"].rsplit(" ", 1)[0] - if name == "__email__": - return meta["Author-email"].rsplit("<", 1)[1][:-1] - return meta[dunder_to_metadata[name]] + return meta["version"] return __getattr__ diff --git a/tests/test_packaging.py b/tests/test_packaging.py index 5a2fdb269..8b270b6b4 100644 --- a/tests/test_packaging.py +++ b/tests/test_packaging.py @@ -20,30 +20,6 @@ def _mod(request): class TestLegacyMetadataHack: - def test_title(self, mod): - """ - __title__ returns attrs. - """ - with pytest.deprecated_call() as ws: - assert "attrs" == mod.__title__ - - assert ( - f"Accessing {mod.__name__}.__title__ is deprecated" - in ws.list[0].message.args[0] - ) - - def test_copyright(self, mod): - """ - __copyright__ returns the correct blurp. - """ - with pytest.deprecated_call() as ws: - assert "Copyright (c) 2015 Hynek Schlawack" == mod.__copyright__ - - assert ( - f"Accessing {mod.__name__}.__copyright__ is deprecated" - in ws.list[0].message.args[0] - ) - def test_version(self, mod, recwarn): """ __version__ returns the correct version and doesn't warn. @@ -52,67 +28,6 @@ def test_version(self, mod, recwarn): assert [] == recwarn.list - def test_description(self, mod): - """ - __description__ returns the correct description. - """ - with pytest.deprecated_call() as ws: - assert "Classes Without Boilerplate" == mod.__description__ - - assert ( - f"Accessing {mod.__name__}.__description__ is deprecated" - in ws.list[0].message.args[0] - ) - - @pytest.mark.parametrize("name", ["__uri__", "__url__"]) - def test_uri(self, mod, name): - """ - __uri__ & __url__ returns the correct project URL. - """ - with pytest.deprecated_call() as ws: - assert "https://www.attrs.org/" == getattr(mod, name) - - assert ( - f"Accessing {mod.__name__}.{name} is deprecated" - in ws.list[0].message.args[0] - ) - - def test_author(self, mod): - """ - __author__ returns Hynek. - """ - with pytest.deprecated_call() as ws: - assert "Hynek Schlawack" == mod.__author__ - - assert ( - f"Accessing {mod.__name__}.__author__ is deprecated" - in ws.list[0].message.args[0] - ) - - def test_email(self, mod): - """ - __email__ returns Hynek's email address. - """ - with pytest.deprecated_call() as ws: - assert "hs@ox.cx" == mod.__email__ - - assert ( - f"Accessing {mod.__name__}.__email__ is deprecated" - in ws.list[0].message.args[0] - ) - - def test_license(self, mod): - """ - __license__ returns MIT. - """ - with pytest.deprecated_call() as ws: - assert "MIT" == mod.__license__ - - assert ( - f"Accessing {mod.__name__}.__license__ is deprecated" - in ws.list[0].message.args[0] - ) - def test_does_not_exist(self, mod): """ Asking for unsupported dunders raises an AttributeError.