Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/pandas/compat/_optional.py: 30%
49 statements
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
1from __future__ import annotations
3import importlib
4import sys
5import types
6import warnings
8from pandas.util._exceptions import find_stack_level
10from pandas.util.version import Version
12# Update install.rst when updating versions!
14VERSIONS = {
15 "bs4": "4.9.3",
16 "blosc": "1.21.0",
17 "bottleneck": "1.3.2",
18 "brotli": "0.7.0",
19 "fastparquet": "0.4.0",
20 "fsspec": "2021.07.0",
21 "html5lib": "1.1",
22 "hypothesis": "6.13.0",
23 "gcsfs": "2021.07.0",
24 "jinja2": "3.0.0",
25 "lxml.etree": "4.6.3",
26 "matplotlib": "3.3.2",
27 "numba": "0.53.1",
28 "numexpr": "2.7.3",
29 "odfpy": "1.4.1",
30 "openpyxl": "3.0.7",
31 "pandas_gbq": "0.15.0",
32 "psycopg2": "2.8.6", # (dt dec pq3 ext lo64)
33 "pymysql": "1.0.2",
34 "pyarrow": "1.0.1",
35 "pyreadstat": "1.1.2",
36 "pytest": "6.0",
37 "pyxlsb": "1.0.8",
38 "s3fs": "2021.08.0",
39 "scipy": "1.7.1",
40 "snappy": "0.6.0",
41 "sqlalchemy": "1.4.16",
42 "tables": "3.6.1",
43 "tabulate": "0.8.9",
44 "xarray": "0.19.0",
45 "xlrd": "2.0.1",
46 "xlwt": "1.3.0",
47 "xlsxwriter": "1.4.3",
48 "zstandard": "0.15.2",
49 "tzdata": "2022.1",
50}
52# A mapping from import name to package name (on PyPI) for packages where
53# these two names are different.
55INSTALL_MAPPING = {
56 "bs4": "beautifulsoup4",
57 "bottleneck": "Bottleneck",
58 "brotli": "brotlipy",
59 "jinja2": "Jinja2",
60 "lxml.etree": "lxml",
61 "odf": "odfpy",
62 "pandas_gbq": "pandas-gbq",
63 "snappy": "python-snappy",
64 "sqlalchemy": "SQLAlchemy",
65 "tables": "pytables",
66}
69def get_version(module: types.ModuleType) -> str:
70 version = getattr(module, "__version__", None)
71 if version is None:
72 # xlrd uses a capitalized attribute name
73 version = getattr(module, "__VERSION__", None)
75 if version is None:
76 if module.__name__ == "brotli":
77 # brotli doesn't contain attributes to confirm it's version
78 return ""
79 if module.__name__ == "snappy":
80 # snappy doesn't contain attributes to confirm it's version
81 # See https://github.com/andrix/python-snappy/pull/119
82 return ""
83 raise ImportError(f"Can't determine version for {module.__name__}")
84 if module.__name__ == "psycopg2":
85 # psycopg2 appends " (dt dec pq3 ext lo64)" to it's version
86 version = version.split()[0]
87 return version
90def import_optional_dependency(
91 name: str,
92 extra: str = "",
93 errors: str = "raise",
94 min_version: str | None = None,
95):
96 """
97 Import an optional dependency.
99 By default, if a dependency is missing an ImportError with a nice
100 message will be raised. If a dependency is present, but too old,
101 we raise.
103 Parameters
104 ----------
105 name : str
106 The module name.
107 extra : str
108 Additional text to include in the ImportError message.
109 errors : str {'raise', 'warn', 'ignore'}
110 What to do when a dependency is not found or its version is too old.
112 * raise : Raise an ImportError
113 * warn : Only applicable when a module's version is to old.
114 Warns that the version is too old and returns None
115 * ignore: If the module is not installed, return None, otherwise,
116 return the module, even if the version is too old.
117 It's expected that users validate the version locally when
118 using ``errors="ignore"`` (see. ``io/html.py``)
119 min_version : str, default None
120 Specify a minimum version that is different from the global pandas
121 minimum version required.
122 Returns
123 -------
124 maybe_module : Optional[ModuleType]
125 The imported module, when found and the version is correct.
126 None is returned when the package is not found and `errors`
127 is False, or when the package's version is too old and `errors`
128 is ``'warn'``.
129 """
131 assert errors in {"warn", "raise", "ignore"}
133 package_name = INSTALL_MAPPING.get(name)
134 install_name = package_name if package_name is not None else name
136 msg = (
137 f"Missing optional dependency '{install_name}'. {extra} "
138 f"Use pip or conda to install {install_name}."
139 )
140 try:
141 module = importlib.import_module(name)
142 except ImportError:
143 if errors == "raise": 143 ↛ 144line 143 didn't jump to line 144, because the condition on line 143 was never true
144 raise ImportError(msg)
145 else:
146 return None
148 # Handle submodules: if we have submodule, grab parent module from sys.modules
149 parent = name.split(".")[0]
150 if parent != name:
151 install_name = parent
152 module_to_get = sys.modules[install_name]
153 else:
154 module_to_get = module
155 minimum_version = min_version if min_version is not None else VERSIONS.get(parent)
156 if minimum_version:
157 version = get_version(module_to_get)
158 if version and Version(version) < Version(minimum_version):
159 msg = (
160 f"Pandas requires version '{minimum_version}' or newer of '{parent}' "
161 f"(version '{version}' currently installed)."
162 )
163 if errors == "warn":
164 warnings.warn(
165 msg,
166 UserWarning,
167 stacklevel=find_stack_level(),
168 )
169 return None
170 elif errors == "raise":
171 raise ImportError(msg)
173 return module