Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/dask/_compatibility.py: 34%

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

53 statements  

1from __future__ import annotations 

2 

3import importlib 

4import sys 

5import types 

6import warnings 

7from typing import Literal 

8 

9if sys.version_info >= (3, 12): 

10 import importlib.metadata as importlib_metadata 

11else: 

12 import importlib_metadata 

13from packaging.version import Version 

14 

15PY_VERSION = Version(".".join(map(str, sys.version_info[:3]))) 

16 

17EMSCRIPTEN = sys.platform == "emscripten" 

18 

19LINUX = sys.platform == "linux" 

20MACOS = sys.platform == "darwin" 

21WINDOWS = sys.platform == "win32" 

22 

23 

24VERSIONS = { 

25 "numpy": "1.21.0", 

26 "pandas": "2.0.0", 

27 "bokeh": "3.1.0", 

28 "jinja2": "2.10.3", 

29 "pyarrow": "14.0.1", 

30 "lz4": "4.3.2", 

31} 

32 

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

34# these two names are different. 

35 

36INSTALL_MAPPING = { 

37 "sqlalchemy": "SQLAlchemy", 

38 "tables": "pytables", 

39} 

40 

41 

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

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

44 

45 if version is None: 

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

47 if module.__name__ == "psycopg2": 

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

49 version = version.split()[0] 

50 return version 

51 

52 

53def import_optional_dependency( 

54 name: str, 

55 extra: str = "", 

56 min_version: str | None = None, 

57 *, 

58 errors: Literal["raise", "warn", "ignore"] = "raise", 

59) -> types.ModuleType | None: 

60 """ 

61 Import an optional dependency. 

62 

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

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

65 we raise. 

66 

67 Parameters 

68 ---------- 

69 name : str 

70 The module name. 

71 extra : str 

72 Additional text to include in the ImportError message. 

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

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

75 

76 * raise : Raise an ImportError 

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

78 Warns that the version is too old and returns None 

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

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

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

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

83 min_version : str, default None 

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

85 minimum version required. 

86 Returns 

87 ------- 

88 maybe_module : Optional[ModuleType] 

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

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

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

92 is ``'warn'`` or ``'ignore'``. 

93 """ 

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

95 

96 package_name = INSTALL_MAPPING.get(name) 

97 install_name = package_name if package_name is not None else name 

98 

99 msg = ( 

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

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

102 ) 

103 try: 

104 # NOTE: Use `importlib_metadata.distribution` check to differentiate 

105 # between something that's importable (e.g. a directory named `xarray`) 

106 # and the library we want to check for (i.e. the `xarray`` library) 

107 importlib_metadata.distribution(name) 

108 module = importlib.import_module(name) 

109 except (importlib_metadata.PackageNotFoundError, ImportError) as err: 

110 if errors == "raise": 

111 raise ImportError(msg) from err 

112 return None 

113 

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

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

116 if parent != name: 

117 install_name = parent 

118 module_to_get = sys.modules[install_name] 

119 else: 

120 module_to_get = module 

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

122 if minimum_version: 

123 version = get_version(module_to_get) 

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

125 msg = ( 

126 f"Dask requires version '{minimum_version}' or newer of '{parent}' " 

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

128 ) 

129 if errors == "warn": 

130 warnings.warn(msg, UserWarning) 

131 return None 

132 elif errors == "raise": 

133 raise ImportError(msg) 

134 else: 

135 return None 

136 

137 return module