Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.9/dist-packages/pandas/compat/_optional.py: 72%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

46 statements  

1from __future__ import annotations 

2 

3import importlib 

4import sys 

5from typing import TYPE_CHECKING 

6import warnings 

7 

8from pandas.util._exceptions import find_stack_level 

9 

10from pandas.util.version import Version 

11 

12if TYPE_CHECKING: 

13 import types 

14 

15# Update install.rst & setup.cfg when updating versions! 

16 

17VERSIONS = { 

18 "adbc-driver-postgresql": "0.8.0", 

19 "adbc-driver-sqlite": "0.8.0", 

20 "bs4": "4.11.2", 

21 "blosc": "1.21.3", 

22 "bottleneck": "1.3.6", 

23 "dataframe-api-compat": "0.1.7", 

24 "fastparquet": "2022.12.0", 

25 "fsspec": "2022.11.0", 

26 "html5lib": "1.1", 

27 "hypothesis": "6.46.1", 

28 "gcsfs": "2022.11.0", 

29 "jinja2": "3.1.2", 

30 "lxml.etree": "4.9.2", 

31 "matplotlib": "3.6.3", 

32 "numba": "0.56.4", 

33 "numexpr": "2.8.4", 

34 "odfpy": "1.4.1", 

35 "openpyxl": "3.1.0", 

36 "pandas_gbq": "0.19.0", 

37 "psycopg2": "2.9.6", # (dt dec pq3 ext lo64) 

38 "pymysql": "1.0.2", 

39 "pyarrow": "10.0.1", 

40 "pyreadstat": "1.2.0", 

41 "pytest": "7.3.2", 

42 "python-calamine": "0.1.7", 

43 "pyxlsb": "1.0.10", 

44 "s3fs": "2022.11.0", 

45 "scipy": "1.10.0", 

46 "sqlalchemy": "2.0.0", 

47 "tables": "3.8.0", 

48 "tabulate": "0.9.0", 

49 "xarray": "2022.12.0", 

50 "xlrd": "2.0.1", 

51 "xlsxwriter": "3.0.5", 

52 "zstandard": "0.19.0", 

53 "tzdata": "2022.7", 

54 "qtpy": "2.3.0", 

55 "pyqt5": "5.15.9", 

56} 

57 

58# A mapping from import name to package name (on PyPI) for packages where 

59# these two names are different. 

60 

61INSTALL_MAPPING = { 

62 "bs4": "beautifulsoup4", 

63 "bottleneck": "Bottleneck", 

64 "jinja2": "Jinja2", 

65 "lxml.etree": "lxml", 

66 "odf": "odfpy", 

67 "pandas_gbq": "pandas-gbq", 

68 "python_calamine": "python-calamine", 

69 "sqlalchemy": "SQLAlchemy", 

70 "tables": "pytables", 

71} 

72 

73 

74def get_version(module: types.ModuleType) -> str: 

75 version = getattr(module, "__version__", None) 

76 

77 if version is None: 

78 raise ImportError(f"Can't determine version for {module.__name__}") 

79 if module.__name__ == "psycopg2": 

80 # psycopg2 appends " (dt dec pq3 ext lo64)" to it's version 

81 version = version.split()[0] 

82 return version 

83 

84 

85def import_optional_dependency( 

86 name: str, 

87 extra: str = "", 

88 errors: str = "raise", 

89 min_version: str | None = None, 

90): 

91 """ 

92 Import an optional dependency. 

93 

94 By default, if a dependency is missing an ImportError with a nice 

95 message will be raised. If a dependency is present, but too old, 

96 we raise. 

97 

98 Parameters 

99 ---------- 

100 name : str 

101 The module name. 

102 extra : str 

103 Additional text to include in the ImportError message. 

104 errors : str {'raise', 'warn', 'ignore'} 

105 What to do when a dependency is not found or its version is too old. 

106 

107 * raise : Raise an ImportError 

108 * warn : Only applicable when a module's version is to old. 

109 Warns that the version is too old and returns None 

110 * ignore: If the module is not installed, return None, otherwise, 

111 return the module, even if the version is too old. 

112 It's expected that users validate the version locally when 

113 using ``errors="ignore"`` (see. ``io/html.py``) 

114 min_version : str, default None 

115 Specify a minimum version that is different from the global pandas 

116 minimum version required. 

117 Returns 

118 ------- 

119 maybe_module : Optional[ModuleType] 

120 The imported module, when found and the version is correct. 

121 None is returned when the package is not found and `errors` 

122 is False, or when the package's version is too old and `errors` 

123 is ``'warn'`` or ``'ignore'``. 

124 """ 

125 assert errors in {"warn", "raise", "ignore"} 

126 

127 package_name = INSTALL_MAPPING.get(name) 

128 install_name = package_name if package_name is not None else name 

129 

130 msg = ( 

131 f"Missing optional dependency '{install_name}'. {extra} " 

132 f"Use pip or conda to install {install_name}." 

133 ) 

134 try: 

135 module = importlib.import_module(name) 

136 except ImportError: 

137 if errors == "raise": 

138 raise ImportError(msg) 

139 return None 

140 

141 # Handle submodules: if we have submodule, grab parent module from sys.modules 

142 parent = name.split(".")[0] 

143 if parent != name: 

144 install_name = parent 

145 module_to_get = sys.modules[install_name] 

146 else: 

147 module_to_get = module 

148 minimum_version = min_version if min_version is not None else VERSIONS.get(parent) 

149 if minimum_version: 

150 version = get_version(module_to_get) 

151 if version and Version(version) < Version(minimum_version): 

152 msg = ( 

153 f"Pandas requires version '{minimum_version}' or newer of '{parent}' " 

154 f"(version '{version}' currently installed)." 

155 ) 

156 if errors == "warn": 

157 warnings.warn( 

158 msg, 

159 UserWarning, 

160 stacklevel=find_stack_level(), 

161 ) 

162 return None 

163 elif errors == "raise": 

164 raise ImportError(msg) 

165 else: 

166 return None 

167 

168 return module