Repository: AnalogJ/lexicon

Issue:
RFE: Replace use of `pkg_resources` with `importlib.metadata`
See discussions:
[astropy/astropy#11091](https://github.com/astropy/astropy/pull/11091)
[pypa/pip#7413](https://github.com/pypa/pip/issues/7413)

```console
[tkloczko@devel-g2v lexicon-3.11.0]$ grep -r pkg_resources
lexicon/discovery.py:import pkg_resources
lexicon/discovery.py:        distribution = pkg_resources.get_distribution("dns-lexicon")
lexicon/discovery.py:    except pkg_resources.DistributionNotFound:
lexicon/discovery.py:        return pkg_resources.get_distribution("dns-lexicon").version
lexicon/discovery.py:    except pkg_resources.DistributionNotFound:
lexicon/discovery.py:    provider: str, distribution: pkg_resources.Distribution
lexicon/discovery.py:        requirements: List[pkg_resources.Requirement] = distribution.requires(
lexicon/discovery.py:    except pkg_resources.UnknownExtra:
lexicon/discovery.py:                    pkg_resources.get_distribution(requirement.name)  # type: ignore
lexicon/discovery.py:                    pkg_resources.get_distribution(requirement)
lexicon/discovery.py:        except (pkg_resources.DistributionNotFound, pkg_resources.VersionConflict):
t:| * 90642ca - (origin/fix-provider-disco-legacy, jamin/fix-provider-disco-legacy, giuse/fix-provider-disco-legacy) Support old versions of pkg_resources (2 years, 10 months ago) <Adrien Ferrand>
```



Discussion:
Just in case .. `pkg_resources` is part of setuptools and even if currently `lexicon` is using poetry still setuptools is needed.

Resolving patch:
diff --git a/lexicon/client.py b/lexicon/client.py
--- a/lexicon/client.py
+++ b/lexicon/client.py
@@ -114,8 +114,8 @@ def _validate_config(self) -> None:
         else:
             if not available:
                 raise ProviderNotAvailableError(
-                    f"This provider ({provider_name}) has required dependencies that are missing. "
-                    f"Please install lexicon[{provider_name}] first."
+                    f"This provider ({provider_name}) has required extra dependencies that are missing. "
+                    f"Please run `pip install lexicon[{provider_name}]` first before using it."
                 )
 
         if not self.config.resolve("lexicon:action"):
diff --git a/lexicon/discovery.py b/lexicon/discovery.py
--- a/lexicon/discovery.py
+++ b/lexicon/discovery.py
@@ -4,9 +4,13 @@
 * what is the version of Lexicon
 """
 import pkgutil
-from typing import Dict, List
+import re
+from typing import Dict
 
-import pkg_resources
+try:
+    from importlib.metadata import Distribution, PackageNotFoundError
+except ModuleNotFoundError:
+    from importlib_metadata import Distribution, PackageNotFoundError  # type: ignore[misc]
 
 from lexicon import providers
 
@@ -22,8 +26,8 @@ def find_providers() -> Dict[str, bool]:
     )
 
     try:
-        distribution = pkg_resources.get_distribution("dns-lexicon")
-    except pkg_resources.DistributionNotFound:
+        distribution = Distribution.from_name("dns-lexicon")
+    except PackageNotFoundError:
         return {provider: True for provider in providers_list}
     else:
         return {
@@ -35,31 +39,32 @@ def find_providers() -> Dict[str, bool]:
 def lexicon_version() -> str:
     """Retrieve current Lexicon version"""
     try:
-        return pkg_resources.get_distribution("dns-lexicon").version
-    except pkg_resources.DistributionNotFound:
+        return Distribution.from_name("dns-lexicon").version
+    except PackageNotFoundError:
         return "unknown"
 
 
-def _resolve_requirements(
-    provider: str, distribution: pkg_resources.Distribution
-) -> bool:
-    try:
-        requirements: List[pkg_resources.Requirement] = distribution.requires(
-            extras=(provider,)
-        )
-    except pkg_resources.UnknownExtra:
+def _resolve_requirements(provider: str, distribution: Distribution) -> bool:
+    requires = distribution.requires
+    if requires is None:
+        raise ValueError("Error while trying finding requirements.")
+
+    requirements = [
+        re.sub(r"^(.*)\s\(.*\)(?:;.*|)$", r"\1", requirement)
+        for requirement in requires
+        if f'extra == "{provider}"' in requirement
+    ]
+
+    if not requirements:
         # No extra for this provider
         return True
-    else:
-        # Extra is defined
+
+    for requirement in requirements:
         try:
-            for requirement in requirements:
-                if hasattr(requirement, "name"):
-                    pkg_resources.get_distribution(requirement.name)  # type: ignore
-                else:
-                    pkg_resources.get_distribution(requirement)
-        except (pkg_resources.DistributionNotFound, pkg_resources.VersionConflict):
+            Distribution.from_name(requirement)
+        except PackageNotFoundError:
             # At least one extra requirement is not fulfilled
             return False
 
+    # All extra requirements are fulfilled
     return True
diff --git a/lexicon/parser.py b/lexicon/parser.py
--- a/lexicon/parser.py
+++ b/lexicon/parser.py
@@ -90,7 +90,7 @@ def generate_cli_main_parser() -> argparse.ArgumentParser:
         if not available:
             subparser.epilog = (
                 "WARNING: some required dependencies for this provider are not "
-                f"installed. Please install lexicon[{provider}] first before using it."
+                f"installed. Please run `pip install lexicon[{provider}]` first before using it."
             )
 
     return parser


Test patch: