diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 5b272dc1c..82e39df61 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,15 @@ Changelog --------- +3.21.0 (unreleased) +******************* + +Other changes: + +- *Backwards-incomaptible*: ``__version__``, ``__parsed_version__``, and ``__version_info__`` + attributes are deprecated (:issue:`2227`). Use feature detection or + ``importlib.metadata.version("marshmallow")`` instead. + 3.20.2 (2024-01-09) ******************* diff --git a/src/marshmallow/__init__.py b/src/marshmallow/__init__.py index dd061ff45..98224bde9 100644 --- a/src/marshmallow/__init__.py +++ b/src/marshmallow/__init__.py @@ -1,6 +1,7 @@ from __future__ import annotations import importlib.metadata +import typing from packaging.version import Version @@ -18,14 +19,49 @@ from . import fields -# TODO: Deprecate __version__? -__version__ = importlib.metadata.version("marshmallow") -__parsed_version__ = Version(__version__) -__version_info__: tuple[int, int, int] | tuple[ - int, int, int, str, int -] = __parsed_version__.release # type: ignore[assignment] -if __parsed_version__.pre: - __version_info__ += __parsed_version__.pre # type: ignore[assignment] + +def __getattr__(name: str) -> typing.Any: + import warnings + + if name == "__version__": + warnings.warn( + "The '__version__' attribute is deprecated and will be removed in" + " in a future version. Use feature detection or" + " 'importlib.metadata.version(\"marshmallow\")' instead.", + DeprecationWarning, + stacklevel=2, + ) + return importlib.metadata.version("marshmallow") + + if name == "__parsed_version__": + warnings.warn( + "The '__parsed_version__' attribute is deprecated and will be removed in" + " in a future version. Use feature detection or" + " 'packaging.Version(importlib.metadata.version(\"marshmallow\"))' instead.", + DeprecationWarning, + stacklevel=2, + ) + return Version(importlib.metadata.version("marshmallow")) + + if name == "__version_info__": + warnings.warn( + "The '__version_info__' attribute is deprecated and will be removed in" + " in a future version. Use feature detection or" + " 'packaging.Version(importlib.metadata.version(\"marshmallow\")).release' instead.", + DeprecationWarning, + stacklevel=2, + ) + __parsed_version__ = Version(importlib.metadata.version("marshmallow")) + __version_info__: tuple[int, int, int] | tuple[ + int, int, int, str, int + ] = __parsed_version__.release # type: ignore[assignment] + if __parsed_version__.pre: + __version_info__ += __parsed_version__.pre # type: ignore[assignment] + return __version_info__ + + raise AttributeError(name) + + __all__ = [ "EXCLUDE", "INCLUDE", diff --git a/tests/test_version_attributes.py b/tests/test_version_attributes.py new file mode 100644 index 000000000..d233b39dd --- /dev/null +++ b/tests/test_version_attributes.py @@ -0,0 +1,15 @@ +# ruff: noqa: B018 +import pytest + +import marshmallow + + +def test_version_attributes_deprecated(): + with pytest.warns(DeprecationWarning): + marshmallow.__version__ + + with pytest.warns(DeprecationWarning): + marshmallow.__parsed_version__ + + with pytest.warns(DeprecationWarning): + marshmallow.__version_info__