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

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

49 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 try: 

44 return module.__version__ 

45 except AttributeError as e: # pragma: no cover 

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

47 

48 

49def import_optional_dependency( 

50 name: str, 

51 extra: str = "", 

52 min_version: str | None = None, 

53 *, 

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

55) -> types.ModuleType | None: 

56 """ 

57 Import an optional dependency. 

58 

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

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

61 we raise. 

62 

63 Parameters 

64 ---------- 

65 name : str 

66 The module name. 

67 extra : str 

68 Additional text to include in the ImportError message. 

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

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

71 

72 * raise : Raise an ImportError 

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

74 Warns that the version is too old and returns None 

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

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

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

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

79 min_version : str, default None 

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

81 minimum version required. 

82 Returns 

83 ------- 

84 maybe_module : Optional[ModuleType] 

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

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

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

88 is ``'warn'`` or ``'ignore'``. 

89 """ 

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

91 

92 package_name = INSTALL_MAPPING.get(name) 

93 install_name = package_name if package_name is not None else name 

94 

95 msg = ( 

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

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

98 ) 

99 try: 

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

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

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

103 importlib_metadata.distribution(name) 

104 module = importlib.import_module(name) 

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

106 if errors == "raise": 

107 raise ImportError(msg) from err 

108 return None 

109 

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

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

112 if parent != name: 

113 install_name = parent 

114 module_to_get = sys.modules[install_name] 

115 else: 

116 module_to_get = module 

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

118 if minimum_version: 

119 version = get_version(module_to_get) 

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

121 msg = ( 

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

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

124 ) 

125 if errors == "warn": 

126 warnings.warn(msg, UserWarning) 

127 return None 

128 elif errors == "raise": 

129 raise ImportError(msg) 

130 else: 

131 return None 

132 

133 return module