We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Misleading error message: ProviderNotAvailableError -  This provider (plesk) has required dependencies that are missing. Please install lexicon[plesk] first.
```
(venv) mypc:~/myprojects/dns-lexicon$ pip freeze
certifi==2019.9.11
cffi==1.13.2
chardet==3.0.4
cryptography==2.8
dns-lexicon==3.3.10
future==0.18.2
idna==2.8
pycparser==2.19
pyOpenSSL==19.0.0
PyYAML==5.1.2
requests==2.22.0
requests-file==1.4.3
six==1.13.0
tldextract==2.2.2
urllib3==1.25.7
(venv) mypc:~/myprojects/dns-lexicon$ python main.py 
Traceback (most recent call last):
  File "main.py", line 111, in <module>
    update_server_record(data[0], data[1])
  File "main.py", line 62, in update_server_record
    plesk_record = list_action(filter_name=server_name, filter_type='A')
  File "main.py", line 58, in list_action
    return lexicon.client.Client(config).execute()
  File "/home/joe/myprojects/dns-lexicon/venv/lib/python3.6/site-packages/lexicon/client.py", line 36, in __init__
    self._validate_config()
  File "/home/joe/myprojects/dns-lexicon/venv/lib/python3.6/site-packages/lexicon/client.py", line 105, in _validate_config
    'Please install lexicon[{0}] first.'.format(provider_name))
lexicon.client.ProviderNotAvailableError: This provider (plesk) has required dependencies that are missing. Please install lexicon[plesk] first.

```
Error is misleading because of this part: 
> Please install **lexicon[plesk]** first.

it should be 
> Please install **dns-lexicon[plesk]** first.





--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `lexicon/client.py`
Content:
```
1 """Main module of Lexicon. Defines the Client class, that holds all Lexicon logic."""
2 import importlib
3 import logging
4 import os
5 from typing import Dict, List, Optional, Type, Union, cast
6 
7 import tldextract  # type: ignore
8 
9 from lexicon import config as helper_config
10 from lexicon import discovery
11 from lexicon.exceptions import ProviderNotAvailableError
12 from lexicon.providers.base import Provider
13 
14 
15 class Client(object):
16     """This is the Lexicon client, that will execute all the logic."""
17 
18     def __init__(
19         self, config: Optional[Union[helper_config.ConfigResolver, Dict]] = None
20     ):
21         if not config:
22             # If there is not config specified, we load a non-interactive configuration.
23             self.config = helper_config.non_interactive_config_resolver()
24         elif not isinstance(config, helper_config.ConfigResolver):
25             # If config is not a ConfigResolver, we are in a legacy situation.
26             # We protect this part of the Client API.
27             self.config = helper_config.legacy_config_resolver(config)
28         else:
29             self.config = config
30 
31         # Validate configuration
32         self._validate_config()
33 
34         runtime_config = {}
35 
36         # Process domain, strip subdomain
37         try:
38             domain_extractor = tldextract.TLDExtract(
39                 cache_dir=_get_tldextract_cache_path(), include_psl_private_domains=True
40             )
41         except TypeError:
42             domain_extractor = tldextract.TLDExtract(
43                 cache_file=_get_tldextract_cache_path(), include_psl_private_domains=True  # type: ignore
44             )
45         domain_parts = domain_extractor(
46             cast(str, self.config.resolve("lexicon:domain"))
47         )
48         runtime_config["domain"] = f"{domain_parts.domain}.{domain_parts.suffix}"
49 
50         delegated = self.config.resolve("lexicon:delegated")
51         if delegated:
52             # handle delegated domain
53             delegated = str(delegated).rstrip(".")
54             initial_domain = str(runtime_config.get("domain"))
55             if delegated != initial_domain:
56                 # convert to relative name
57                 if delegated.endswith(initial_domain):
58                     delegated = delegated[: -len(initial_domain)]
59                     delegated = delegated.rstrip(".")
60                 # update domain
61                 runtime_config["domain"] = f"{delegated}.{initial_domain}"
62 
63         self.action = self.config.resolve("lexicon:action")
64         self.provider_name = self.config.resolve(
65             "lexicon:provider_name"
66         ) or self.config.resolve("lexicon:provider")
67 
68         if not self.provider_name:
69             raise ValueError("Could not resolve provider name.")
70 
71         self.config.add_config_source(helper_config.DictConfigSource(runtime_config), 0)
72 
73         provider_module = importlib.import_module(
74             "lexicon.providers." + self.provider_name
75         )
76         provider_class: Type[Provider] = getattr(provider_module, "Provider")
77         self.provider = provider_class(self.config)
78 
79     def execute(self) -> Union[bool, List[Dict]]:
80         """Execute provided configuration in class constructor to the DNS records"""
81         self.provider.authenticate()
82         identifier = self.config.resolve("lexicon:identifier")
83         record_type = self.config.resolve("lexicon:type")
84         name = self.config.resolve("lexicon:name")
85         content = self.config.resolve("lexicon:content")
86 
87         if self.action == "create":
88             if not record_type or not name or not content:
89                 raise ValueError("Missing record_type, name or content parameters.")
90             return self.provider.create_record(record_type, name, content)
91 
92         if self.action == "list":
93             return self.provider.list_records(record_type, name, content)
94 
95         if self.action == "update":
96             return self.provider.update_record(identifier, record_type, name, content)
97 
98         if self.action == "delete":
99             return self.provider.delete_record(identifier, record_type, name, content)
100 
101         raise ValueError(f"Invalid action statement: {self.action}")
102 
103     def _validate_config(self) -> None:
104         provider_name = self.config.resolve("lexicon:provider_name")
105         if not provider_name:
106             raise AttributeError("provider_name")
107 
108         try:
109             available = discovery.find_providers()[provider_name]
110         except KeyError:
111             raise ProviderNotAvailableError(
112                 f"This provider ({provider_name}) is not supported by Lexicon."
113             )
114         else:
115             if not available:
116                 raise ProviderNotAvailableError(
117                     f"This provider ({provider_name}) has required extra dependencies that are missing. "
118                     f"Please run `pip install lexicon[{provider_name}]` first before using it."
119                 )
120 
121         if not self.config.resolve("lexicon:action"):
122             raise AttributeError("action")
123         if not self.config.resolve("lexicon:domain"):
124             raise AttributeError("domain")
125         if not self.config.resolve("lexicon:type"):
126             raise AttributeError("type")
127 
128 
129 def _get_tldextract_cache_path() -> str:
130     if os.environ.get("TLDEXTRACT_CACHE_FILE"):
131         logging.warning(
132             "TLD_EXTRACT_CACHE_FILE environment variable is deprecated, please use TLDEXTRACT_CACHE_PATH instead."
133         )
134         os.environ["TLDEXTRACT_CACHE_PATH"] = os.environ["TLDEXTRACT_CACHE_FILE"]
135 
136     return os.path.expanduser(
137         os.environ.get("TLDEXTRACT_CACHE_PATH", os.path.join("~", ".lexicon_tld_set"))
138     )
139
```

Path: `lexicon/discovery.py`
Content:
```
1 """
2 This module takes care of finding information about the runtime of Lexicon:
3 * what are the providers installed, and available
4 * what is the version of Lexicon
5 """
6 import pkgutil
7 import re
8 from typing import Dict
9 
10 try:
11     from importlib.metadata import Distribution, PackageNotFoundError
12 except ModuleNotFoundError:
13     from importlib_metadata import Distribution, PackageNotFoundError  # type: ignore[assignment]
14 
15 from lexicon import providers
16 
17 
18 def find_providers() -> Dict[str, bool]:
19     """Find all providers registered in Lexicon, and their availability"""
20     providers_list = sorted(
21         {
22             modname
23             for (_, modname, _) in pkgutil.iter_modules(providers.__path__)  # type: ignore
24             if modname != "base"
25         }
26     )
27 
28     try:
29         distribution = Distribution.from_name("dns-lexicon")
30     except PackageNotFoundError:
31         return {provider: True for provider in providers_list}
32     else:
33         return {
34             provider: _resolve_requirements(provider, distribution)
35             for provider in providers_list
36         }
37 
38 
39 def lexicon_version() -> str:
40     """Retrieve current Lexicon version"""
41     try:
42         return Distribution.from_name("dns-lexicon").version
43     except PackageNotFoundError:
44         return "unknown"
45 
46 
47 def _resolve_requirements(provider: str, distribution: Distribution) -> bool:
48     requires = distribution.requires
49     if requires is None:
50         raise ValueError("Error while trying finding requirements.")
51 
52     requirements = [
53         re.sub(r"^(.*)\s\(.*\)(?:;.*|)$", r"\1", requirement)
54         for requirement in requires
55         if f'extra == "{provider}"' in requirement
56     ]
57 
58     if not requirements:
59         # No extra for this provider
60         return True
61 
62     for requirement in requirements:
63         try:
64             Distribution.from_name(requirement)
65         except PackageNotFoundError:
66             # At least one extra requirement is not fulfilled
67             return False
68 
69     # All extra requirements are fulfilled
70     return True
71
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/lexicon/client.py b/lexicon/client.py
--- a/lexicon/client.py
+++ b/lexicon/client.py
@@ -115,7 +115,7 @@
             if not available:
                 raise ProviderNotAvailableError(
                     f"This provider ({provider_name}) has required extra dependencies that are missing. "
-                    f"Please run `pip install lexicon[{provider_name}]` first before using it."
+                    f"Please run `pip install dns-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
@@ -50,7 +50,7 @@
         raise ValueError("Error while trying finding requirements.")
 
     requirements = [
-        re.sub(r"^(.*)\s\(.*\)(?:;.*|)$", r"\1", requirement)
+        re.sub(r"^(.*)\s\(.*\)(?:\s*;.*|)$", r"\1", requirement)
         for requirement in requires
         if f'extra == "{provider}"' in requirement
     ]

{"golden_diff": "diff --git a/lexicon/client.py b/lexicon/client.py\n--- a/lexicon/client.py\n+++ b/lexicon/client.py\n@@ -115,7 +115,7 @@\n             if not available:\n                 raise ProviderNotAvailableError(\n                     f\"This provider ({provider_name}) has required extra dependencies that are missing. \"\n-                    f\"Please run `pip install lexicon[{provider_name}]` first before using it.\"\n+                    f\"Please run `pip install dns-lexicon[{provider_name}]` first before using it.\"\n                 )\n \n         if not self.config.resolve(\"lexicon:action\"):\ndiff --git a/lexicon/discovery.py b/lexicon/discovery.py\n--- a/lexicon/discovery.py\n+++ b/lexicon/discovery.py\n@@ -50,7 +50,7 @@\n         raise ValueError(\"Error while trying finding requirements.\")\n \n     requirements = [\n-        re.sub(r\"^(.*)\\s\\(.*\\)(?:;.*|)$\", r\"\\1\", requirement)\n+        re.sub(r\"^(.*)\\s\\(.*\\)(?:\\s*;.*|)$\", r\"\\1\", requirement)\n         for requirement in requires\n         if f'extra == \"{provider}\"' in requirement\n     ]\n", "issue": "Misleading error message: ProviderNotAvailableError -  This provider (plesk) has required dependencies that are missing. Please install lexicon[plesk] first.\n```\r\n(venv) mypc:~/myprojects/dns-lexicon$ pip freeze\r\ncertifi==2019.9.11\r\ncffi==1.13.2\r\nchardet==3.0.4\r\ncryptography==2.8\r\ndns-lexicon==3.3.10\r\nfuture==0.18.2\r\nidna==2.8\r\npycparser==2.19\r\npyOpenSSL==19.0.0\r\nPyYAML==5.1.2\r\nrequests==2.22.0\r\nrequests-file==1.4.3\r\nsix==1.13.0\r\ntldextract==2.2.2\r\nurllib3==1.25.7\r\n(venv) mypc:~/myprojects/dns-lexicon$ python main.py \r\nTraceback (most recent call last):\r\n  File \"main.py\", line 111, in <module>\r\n    update_server_record(data[0], data[1])\r\n  File \"main.py\", line 62, in update_server_record\r\n    plesk_record = list_action(filter_name=server_name, filter_type='A')\r\n  File \"main.py\", line 58, in list_action\r\n    return lexicon.client.Client(config).execute()\r\n  File \"/home/joe/myprojects/dns-lexicon/venv/lib/python3.6/site-packages/lexicon/client.py\", line 36, in __init__\r\n    self._validate_config()\r\n  File \"/home/joe/myprojects/dns-lexicon/venv/lib/python3.6/site-packages/lexicon/client.py\", line 105, in _validate_config\r\n    'Please install lexicon[{0}] first.'.format(provider_name))\r\nlexicon.client.ProviderNotAvailableError: This provider (plesk) has required dependencies that are missing. Please install lexicon[plesk] first.\r\n\r\n```\r\nError is misleading because of this part: \r\n> Please install **lexicon[plesk]** first.\r\n\r\nit should be \r\n> Please install **dns-lexicon[plesk]** first.\r\n\r\n\r\n\r\n\n", "before_files": [{"content": "\"\"\"Main module of Lexicon. Defines the Client class, that holds all Lexicon logic.\"\"\"\nimport importlib\nimport logging\nimport os\nfrom typing import Dict, List, Optional, Type, Union, cast\n\nimport tldextract  # type: ignore\n\nfrom lexicon import config as helper_config\nfrom lexicon import discovery\nfrom lexicon.exceptions import ProviderNotAvailableError\nfrom lexicon.providers.base import Provider\n\n\nclass Client(object):\n    \"\"\"This is the Lexicon client, that will execute all the logic.\"\"\"\n\n    def __init__(\n        self, config: Optional[Union[helper_config.ConfigResolver, Dict]] = None\n    ):\n        if not config:\n            # If there is not config specified, we load a non-interactive configuration.\n            self.config = helper_config.non_interactive_config_resolver()\n        elif not isinstance(config, helper_config.ConfigResolver):\n            # If config is not a ConfigResolver, we are in a legacy situation.\n            # We protect this part of the Client API.\n            self.config = helper_config.legacy_config_resolver(config)\n        else:\n            self.config = config\n\n        # Validate configuration\n        self._validate_config()\n\n        runtime_config = {}\n\n        # Process domain, strip subdomain\n        try:\n            domain_extractor = tldextract.TLDExtract(\n                cache_dir=_get_tldextract_cache_path(), include_psl_private_domains=True\n            )\n        except TypeError:\n            domain_extractor = tldextract.TLDExtract(\n                cache_file=_get_tldextract_cache_path(), include_psl_private_domains=True  # type: ignore\n            )\n        domain_parts = domain_extractor(\n            cast(str, self.config.resolve(\"lexicon:domain\"))\n        )\n        runtime_config[\"domain\"] = f\"{domain_parts.domain}.{domain_parts.suffix}\"\n\n        delegated = self.config.resolve(\"lexicon:delegated\")\n        if delegated:\n            # handle delegated domain\n            delegated = str(delegated).rstrip(\".\")\n            initial_domain = str(runtime_config.get(\"domain\"))\n            if delegated != initial_domain:\n                # convert to relative name\n                if delegated.endswith(initial_domain):\n                    delegated = delegated[: -len(initial_domain)]\n                    delegated = delegated.rstrip(\".\")\n                # update domain\n                runtime_config[\"domain\"] = f\"{delegated}.{initial_domain}\"\n\n        self.action = self.config.resolve(\"lexicon:action\")\n        self.provider_name = self.config.resolve(\n            \"lexicon:provider_name\"\n        ) or self.config.resolve(\"lexicon:provider\")\n\n        if not self.provider_name:\n            raise ValueError(\"Could not resolve provider name.\")\n\n        self.config.add_config_source(helper_config.DictConfigSource(runtime_config), 0)\n\n        provider_module = importlib.import_module(\n            \"lexicon.providers.\" + self.provider_name\n        )\n        provider_class: Type[Provider] = getattr(provider_module, \"Provider\")\n        self.provider = provider_class(self.config)\n\n    def execute(self) -> Union[bool, List[Dict]]:\n        \"\"\"Execute provided configuration in class constructor to the DNS records\"\"\"\n        self.provider.authenticate()\n        identifier = self.config.resolve(\"lexicon:identifier\")\n        record_type = self.config.resolve(\"lexicon:type\")\n        name = self.config.resolve(\"lexicon:name\")\n        content = self.config.resolve(\"lexicon:content\")\n\n        if self.action == \"create\":\n            if not record_type or not name or not content:\n                raise ValueError(\"Missing record_type, name or content parameters.\")\n            return self.provider.create_record(record_type, name, content)\n\n        if self.action == \"list\":\n            return self.provider.list_records(record_type, name, content)\n\n        if self.action == \"update\":\n            return self.provider.update_record(identifier, record_type, name, content)\n\n        if self.action == \"delete\":\n            return self.provider.delete_record(identifier, record_type, name, content)\n\n        raise ValueError(f\"Invalid action statement: {self.action}\")\n\n    def _validate_config(self) -> None:\n        provider_name = self.config.resolve(\"lexicon:provider_name\")\n        if not provider_name:\n            raise AttributeError(\"provider_name\")\n\n        try:\n            available = discovery.find_providers()[provider_name]\n        except KeyError:\n            raise ProviderNotAvailableError(\n                f\"This provider ({provider_name}) is not supported by Lexicon.\"\n            )\n        else:\n            if not available:\n                raise ProviderNotAvailableError(\n                    f\"This provider ({provider_name}) has required extra dependencies that are missing. \"\n                    f\"Please run `pip install lexicon[{provider_name}]` first before using it.\"\n                )\n\n        if not self.config.resolve(\"lexicon:action\"):\n            raise AttributeError(\"action\")\n        if not self.config.resolve(\"lexicon:domain\"):\n            raise AttributeError(\"domain\")\n        if not self.config.resolve(\"lexicon:type\"):\n            raise AttributeError(\"type\")\n\n\ndef _get_tldextract_cache_path() -> str:\n    if os.environ.get(\"TLDEXTRACT_CACHE_FILE\"):\n        logging.warning(\n            \"TLD_EXTRACT_CACHE_FILE environment variable is deprecated, please use TLDEXTRACT_CACHE_PATH instead.\"\n        )\n        os.environ[\"TLDEXTRACT_CACHE_PATH\"] = os.environ[\"TLDEXTRACT_CACHE_FILE\"]\n\n    return os.path.expanduser(\n        os.environ.get(\"TLDEXTRACT_CACHE_PATH\", os.path.join(\"~\", \".lexicon_tld_set\"))\n    )\n", "path": "lexicon/client.py"}, {"content": "\"\"\"\nThis module takes care of finding information about the runtime of Lexicon:\n* what are the providers installed, and available\n* what is the version of Lexicon\n\"\"\"\nimport pkgutil\nimport re\nfrom typing import Dict\n\ntry:\n    from importlib.metadata import Distribution, PackageNotFoundError\nexcept ModuleNotFoundError:\n    from importlib_metadata import Distribution, PackageNotFoundError  # type: ignore[assignment]\n\nfrom lexicon import providers\n\n\ndef find_providers() -> Dict[str, bool]:\n    \"\"\"Find all providers registered in Lexicon, and their availability\"\"\"\n    providers_list = sorted(\n        {\n            modname\n            for (_, modname, _) in pkgutil.iter_modules(providers.__path__)  # type: ignore\n            if modname != \"base\"\n        }\n    )\n\n    try:\n        distribution = Distribution.from_name(\"dns-lexicon\")\n    except PackageNotFoundError:\n        return {provider: True for provider in providers_list}\n    else:\n        return {\n            provider: _resolve_requirements(provider, distribution)\n            for provider in providers_list\n        }\n\n\ndef lexicon_version() -> str:\n    \"\"\"Retrieve current Lexicon version\"\"\"\n    try:\n        return Distribution.from_name(\"dns-lexicon\").version\n    except PackageNotFoundError:\n        return \"unknown\"\n\n\ndef _resolve_requirements(provider: str, distribution: Distribution) -> bool:\n    requires = distribution.requires\n    if requires is None:\n        raise ValueError(\"Error while trying finding requirements.\")\n\n    requirements = [\n        re.sub(r\"^(.*)\\s\\(.*\\)(?:;.*|)$\", r\"\\1\", requirement)\n        for requirement in requires\n        if f'extra == \"{provider}\"' in requirement\n    ]\n\n    if not requirements:\n        # No extra for this provider\n        return True\n\n    for requirement in requirements:\n        try:\n            Distribution.from_name(requirement)\n        except PackageNotFoundError:\n            # At least one extra requirement is not fulfilled\n            return False\n\n    # All extra requirements are fulfilled\n    return True\n", "path": "lexicon/discovery.py"}], "after_files": [{"content": "\"\"\"Main module of Lexicon. Defines the Client class, that holds all Lexicon logic.\"\"\"\nimport importlib\nimport logging\nimport os\nfrom typing import Dict, List, Optional, Type, Union, cast\n\nimport tldextract  # type: ignore\n\nfrom lexicon import config as helper_config\nfrom lexicon import discovery\nfrom lexicon.exceptions import ProviderNotAvailableError\nfrom lexicon.providers.base import Provider\n\n\nclass Client(object):\n    \"\"\"This is the Lexicon client, that will execute all the logic.\"\"\"\n\n    def __init__(\n        self, config: Optional[Union[helper_config.ConfigResolver, Dict]] = None\n    ):\n        if not config:\n            # If there is not config specified, we load a non-interactive configuration.\n            self.config = helper_config.non_interactive_config_resolver()\n        elif not isinstance(config, helper_config.ConfigResolver):\n            # If config is not a ConfigResolver, we are in a legacy situation.\n            # We protect this part of the Client API.\n            self.config = helper_config.legacy_config_resolver(config)\n        else:\n            self.config = config\n\n        # Validate configuration\n        self._validate_config()\n\n        runtime_config = {}\n\n        # Process domain, strip subdomain\n        try:\n            domain_extractor = tldextract.TLDExtract(\n                cache_dir=_get_tldextract_cache_path(), include_psl_private_domains=True\n            )\n        except TypeError:\n            domain_extractor = tldextract.TLDExtract(\n                cache_file=_get_tldextract_cache_path(), include_psl_private_domains=True  # type: ignore\n            )\n        domain_parts = domain_extractor(\n            cast(str, self.config.resolve(\"lexicon:domain\"))\n        )\n        runtime_config[\"domain\"] = f\"{domain_parts.domain}.{domain_parts.suffix}\"\n\n        delegated = self.config.resolve(\"lexicon:delegated\")\n        if delegated:\n            # handle delegated domain\n            delegated = str(delegated).rstrip(\".\")\n            initial_domain = str(runtime_config.get(\"domain\"))\n            if delegated != initial_domain:\n                # convert to relative name\n                if delegated.endswith(initial_domain):\n                    delegated = delegated[: -len(initial_domain)]\n                    delegated = delegated.rstrip(\".\")\n                # update domain\n                runtime_config[\"domain\"] = f\"{delegated}.{initial_domain}\"\n\n        self.action = self.config.resolve(\"lexicon:action\")\n        self.provider_name = self.config.resolve(\n            \"lexicon:provider_name\"\n        ) or self.config.resolve(\"lexicon:provider\")\n\n        if not self.provider_name:\n            raise ValueError(\"Could not resolve provider name.\")\n\n        self.config.add_config_source(helper_config.DictConfigSource(runtime_config), 0)\n\n        provider_module = importlib.import_module(\n            \"lexicon.providers.\" + self.provider_name\n        )\n        provider_class: Type[Provider] = getattr(provider_module, \"Provider\")\n        self.provider = provider_class(self.config)\n\n    def execute(self) -> Union[bool, List[Dict]]:\n        \"\"\"Execute provided configuration in class constructor to the DNS records\"\"\"\n        self.provider.authenticate()\n        identifier = self.config.resolve(\"lexicon:identifier\")\n        record_type = self.config.resolve(\"lexicon:type\")\n        name = self.config.resolve(\"lexicon:name\")\n        content = self.config.resolve(\"lexicon:content\")\n\n        if self.action == \"create\":\n            if not record_type or not name or not content:\n                raise ValueError(\"Missing record_type, name or content parameters.\")\n            return self.provider.create_record(record_type, name, content)\n\n        if self.action == \"list\":\n            return self.provider.list_records(record_type, name, content)\n\n        if self.action == \"update\":\n            return self.provider.update_record(identifier, record_type, name, content)\n\n        if self.action == \"delete\":\n            return self.provider.delete_record(identifier, record_type, name, content)\n\n        raise ValueError(f\"Invalid action statement: {self.action}\")\n\n    def _validate_config(self) -> None:\n        provider_name = self.config.resolve(\"lexicon:provider_name\")\n        if not provider_name:\n            raise AttributeError(\"provider_name\")\n\n        try:\n            available = discovery.find_providers()[provider_name]\n        except KeyError:\n            raise ProviderNotAvailableError(\n                f\"This provider ({provider_name}) is not supported by Lexicon.\"\n            )\n        else:\n            if not available:\n                raise ProviderNotAvailableError(\n                    f\"This provider ({provider_name}) has required extra dependencies that are missing. \"\n                    f\"Please run `pip install dns-lexicon[{provider_name}]` first before using it.\"\n                )\n\n        if not self.config.resolve(\"lexicon:action\"):\n            raise AttributeError(\"action\")\n        if not self.config.resolve(\"lexicon:domain\"):\n            raise AttributeError(\"domain\")\n        if not self.config.resolve(\"lexicon:type\"):\n            raise AttributeError(\"type\")\n\n\ndef _get_tldextract_cache_path() -> str:\n    if os.environ.get(\"TLDEXTRACT_CACHE_FILE\"):\n        logging.warning(\n            \"TLD_EXTRACT_CACHE_FILE environment variable is deprecated, please use TLDEXTRACT_CACHE_PATH instead.\"\n        )\n        os.environ[\"TLDEXTRACT_CACHE_PATH\"] = os.environ[\"TLDEXTRACT_CACHE_FILE\"]\n\n    return os.path.expanduser(\n        os.environ.get(\"TLDEXTRACT_CACHE_PATH\", os.path.join(\"~\", \".lexicon_tld_set\"))\n    )\n", "path": "lexicon/client.py"}, {"content": "\"\"\"\nThis module takes care of finding information about the runtime of Lexicon:\n* what are the providers installed, and available\n* what is the version of Lexicon\n\"\"\"\nimport pkgutil\nimport re\nfrom typing import Dict\n\ntry:\n    from importlib.metadata import Distribution, PackageNotFoundError\nexcept ModuleNotFoundError:\n    from importlib_metadata import Distribution, PackageNotFoundError  # type: ignore[assignment]\n\nfrom lexicon import providers\n\n\ndef find_providers() -> Dict[str, bool]:\n    \"\"\"Find all providers registered in Lexicon, and their availability\"\"\"\n    providers_list = sorted(\n        {\n            modname\n            for (_, modname, _) in pkgutil.iter_modules(providers.__path__)  # type: ignore\n            if modname != \"base\"\n        }\n    )\n\n    try:\n        distribution = Distribution.from_name(\"dns-lexicon\")\n    except PackageNotFoundError:\n        return {provider: True for provider in providers_list}\n    else:\n        return {\n            provider: _resolve_requirements(provider, distribution)\n            for provider in providers_list\n        }\n\n\ndef lexicon_version() -> str:\n    \"\"\"Retrieve current Lexicon version\"\"\"\n    try:\n        return Distribution.from_name(\"dns-lexicon\").version\n    except PackageNotFoundError:\n        return \"unknown\"\n\n\ndef _resolve_requirements(provider: str, distribution: Distribution) -> bool:\n    requires = distribution.requires\n    if requires is None:\n        raise ValueError(\"Error while trying finding requirements.\")\n\n    requirements = [\n        re.sub(r\"^(.*)\\s\\(.*\\)(?:\\s*;.*|)$\", r\"\\1\", requirement)\n        for requirement in requires\n        if f'extra == \"{provider}\"' in requirement\n    ]\n\n    if not requirements:\n        # No extra for this provider\n        return True\n\n    for requirement in requirements:\n        try:\n            Distribution.from_name(requirement)\n        except PackageNotFoundError:\n            # At least one extra requirement is not fulfilled\n            return False\n\n    # All extra requirements are fulfilled\n    return True\n", "path": "lexicon/discovery.py"}]}