Repository: maxcountryman/flask-login (language: python)

Issue:
deprecate `__about__` module
The only thing that might be relevant to runtime inspection is `__version__`, which is already exported. The rest is package metadata that should be in `setup.cfg`, which can be inspected with `importlib.metadata` if needed.


Discussion / hints:

Fix patch:
diff --git a/CHANGES.md b/CHANGES.md
index ec86dfcb..ecc6d58a 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -10,6 +10,8 @@ Unreleased
 - Bump to Flask >= 2.3.0.
 - Bump to Werkzeug >= 2.3.2.
 - Remove previously deprecated code. #694
+- Deprecate the `__about__` module and `__version__` attribute. Use `importlib.metadata`
+  to get library information instead. #701
 - Use modern `pyproject.toml` project metadata. Use flit_core instead of setuptools as
   build backend.
 - Use `datetime.now(timezone.utc)` instead of deprecated `datetime.utcnow`. #758
diff --git a/requirements/dev.txt b/requirements/dev.txt
index 38445ba3..a7865e62 100644
--- a/requirements/dev.txt
+++ b/requirements/dev.txt
@@ -71,7 +71,6 @@ platformdirs==3.11.0
     #   virtualenv
 pluggy==1.3.0
     # via
-    #   -r tests.in
     #   pytest
     #   tox
 pre-commit==3.5.0
@@ -90,8 +89,6 @@ pyyaml==6.0.1
     # via pre-commit
 requests==2.31.0
     # via sphinx
-semantic-version==2.10.0
-    # via -r tests.in
 snowballstemmer==2.2.0
     # via sphinx
 soupsieve==2.5
@@ -122,8 +119,6 @@ sphinxcontrib-serializinghtml==1.1.9
     # via sphinx
 tox==4.11.3
     # via -r dev.in
-typing-extensions==4.8.0
-    # via -r tests.in
 urllib3==2.0.7
     # via requests
 virtualenv==20.24.6
diff --git a/src/flask_login/__about__.py b/src/flask_login/__about__.py
index 411c1ac3..0786894a 100644
--- a/src/flask_login/__about__.py
+++ b/src/flask_login/__about__.py
@@ -1,3 +1,12 @@
+import warnings
+
+warnings.warn(
+    "The '__about__' module is deprecated and will be removed in Flask-Login 1.0. Use"
+    " 'importlib.metadata' instead.",
+    DeprecationWarning,
+    stacklevel=2,
+)
+
 __title__ = "Flask-Login"
 __description__ = "User session management for Flask"
 __url__ = "https://github.com/maxcountryman/flask-login"
diff --git a/src/flask_login/__init__.py b/src/flask_login/__init__.py
index 81f8fda9..35b530ea 100644
--- a/src/flask_login/__init__.py
+++ b/src/flask_login/__init__.py
@@ -1,4 +1,3 @@
-from .__about__ import __version__
 from .config import COOKIE_DURATION
 from .config import COOKIE_HTTPONLY
 from .config import COOKIE_NAME
@@ -36,7 +35,6 @@
 from .utils import set_login_view
 
 __all__ = [
-    "__version__",
     "COOKIE_DURATION",
     "COOKIE_HTTPONLY",
     "COOKIE_NAME",
@@ -73,3 +71,19 @@
     "make_next_param",
     "set_login_view",
 ]
+
+
+def __getattr__(name):
+    if name == "__version__":
+        import importlib.metadata
+        import warnings
+
+        warnings.warn(
+            "'__version__' is deprecated and will be removed in Flask-Login 1.0. Use"
+            " 'importlib.metadata.version(\"flask-login\")' instead.",
+            DeprecationWarning,
+            stacklevel=2,
+        )
+        return importlib.metadata.version("flask-login")
+
+    raise AttributeError(name)


Test patch:
diff --git a/requirements/tests.in b/requirements/tests.in
index ac432c4c..d8b17cf9 100644
--- a/requirements/tests.in
+++ b/requirements/tests.in
@@ -1,7 +1,4 @@
 blinker
 coverage
-asgiref==3.7.2
+asgiref
 pytest
-semantic_version
-pluggy
-typing-extensions
diff --git a/requirements/tests.txt b/requirements/tests.txt
index ff133569..69025910 100644
--- a/requirements/tests.txt
+++ b/requirements/tests.txt
@@ -15,12 +15,6 @@ iniconfig==2.0.0
 packaging==23.2
     # via pytest
 pluggy==1.3.0
-    # via
-    #   -r tests.in
-    #   pytest
+    # via pytest
 pytest==7.4.3
     # via -r tests.in
-semantic-version==2.10.0
-    # via -r tests.in
-typing-extensions==4.8.0
-    # via -r tests.in
diff --git a/tests/test_login.py b/tests/test_login.py
index c9a9e7c4..800a3fa7 100644
--- a/tests/test_login.py
+++ b/tests/test_login.py
@@ -1,4 +1,3 @@
-import sys
 import unittest
 from collections.abc import Hashable
 from contextlib import contextmanager
@@ -15,7 +14,6 @@
 from flask import Response
 from flask import session
 from flask.views import MethodView
-from semantic_version import Version
 from werkzeug.middleware.proxy_fix import ProxyFix
 
 from flask_login import AnonymousUserMixin
@@ -44,25 +42,9 @@
 from flask_login import user_needs_refresh
 from flask_login import user_unauthorized
 from flask_login import UserMixin
-from flask_login.__about__ import __author__
-from flask_login.__about__ import __author_email__
-from flask_login.__about__ import __copyright__
-from flask_login.__about__ import __description__
-from flask_login.__about__ import __license__
-from flask_login.__about__ import __maintainer__
-from flask_login.__about__ import __title__
-from flask_login.__about__ import __url__
-from flask_login.__about__ import __version__
-from flask_login.__about__ import __version_info__
 from flask_login.utils import _secret_key
 from flask_login.utils import _user_context_processor
 
-sys_version = Version(
-    major=sys.version_info.major,
-    minor=sys.version_info.minor,
-    patch=sys.version_info.micro,
-)
-
 
 @contextmanager
 def listen_to(signal):
@@ -140,22 +122,6 @@ def is_active(self):
 USERS = {1: notch, 2: steve, 3: creeper, "佐藤": germanjapanese}
 
 
-class AboutTestCase(unittest.TestCase):
-    """Make sure we can get version and other info."""
-
-    def test_have_about_data(self):
-        self.assertTrue(__title__ is not None)
-        self.assertTrue(__description__ is not None)
-        self.assertTrue(__url__ is not None)
-        self.assertTrue(__version_info__ is not None)
-        self.assertTrue(__version__ is not None)
-        self.assertTrue(__author__ is not None)
-        self.assertTrue(__author_email__ is not None)
-        self.assertTrue(__maintainer__ is not None)
-        self.assertTrue(__license__ is not None)
-        self.assertTrue(__copyright__ is not None)
-
-
 class StaticTestCase(unittest.TestCase):
     def test_static_loads_anonymous(self):
         app = Flask(__name__)