scikit-base 0.8.1__tar.gz → 0.8.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {scikit_base-0.8.1/scikit_base.egg-info → scikit_base-0.8.2}/PKG-INFO +4 -4
- {scikit_base-0.8.1 → scikit_base-0.8.2}/README.md +1 -1
- {scikit_base-0.8.1 → scikit_base-0.8.2}/pyproject.toml +3 -3
- {scikit_base-0.8.1 → scikit_base-0.8.2/scikit_base.egg-info}/PKG-INFO +4 -4
- {scikit_base-0.8.1 → scikit_base-0.8.2}/scikit_base.egg-info/requires.txt +2 -2
- {scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/__init__.py +1 -1
- {scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/tests/conftest.py +5 -0
- scikit_base-0.8.2/skbase/utils/dependencies/_dependencies.py +542 -0
- scikit_base-0.8.1/skbase/utils/dependencies/_dependencies.py +0 -341
- {scikit_base-0.8.1 → scikit_base-0.8.2}/LICENSE +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/docs/source/conf.py +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/scikit_base.egg-info/SOURCES.txt +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/scikit_base.egg-info/dependency_links.txt +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/scikit_base.egg-info/top_level.txt +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/scikit_base.egg-info/zip-safe +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/setup.cfg +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/_exceptions.py +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/_nopytest_tests.py +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/base/__init__.py +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/base/_base.py +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/base/_meta.py +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/base/_pretty_printing/__init__.py +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/base/_pretty_printing/_object_html_repr.py +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/base/_pretty_printing/_pprint.py +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/base/_pretty_printing/tests/__init__.py +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/base/_pretty_printing/tests/test_pprint.py +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/base/_tagmanager.py +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/lookup/__init__.py +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/lookup/_lookup.py +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/lookup/tests/__init__.py +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/lookup/tests/test_lookup.py +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/testing/__init__.py +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/testing/test_all_objects.py +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/testing/utils/__init__.py +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/testing/utils/_conditional_fixtures.py +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/testing/utils/inspect.py +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/tests/__init__.py +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/tests/mock_package/__init__.py +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/tests/mock_package/test_mock_package.py +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/tests/test_base.py +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/tests/test_baseestimator.py +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/tests/test_exceptions.py +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/tests/test_meta.py +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/utils/__init__.py +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/utils/_check.py +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/utils/_iter.py +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/utils/_nested_iter.py +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/utils/_utils.py +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/utils/deep_equals/__init__.py +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/utils/deep_equals/_common.py +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/utils/deep_equals/_deep_equals.py +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/utils/dependencies/__init__.py +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/utils/dependencies/tests/__init__.py +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/utils/dependencies/tests/test_check_dependencies.py +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/utils/random_state.py +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/utils/stdout_mute.py +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/utils/tests/__init__.py +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/utils/tests/test_check.py +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/utils/tests/test_deep_equals.py +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/utils/tests/test_iter.py +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/utils/tests/test_nested_iter.py +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/utils/tests/test_random_state.py +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/utils/tests/test_utils.py +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/validate/__init__.py +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/validate/_named_objects.py +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/validate/_types.py +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/validate/tests/__init__.py +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/validate/tests/test_iterable_named_objects.py +0 -0
- {scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/validate/tests/test_type_validations.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: scikit-base
|
3
|
-
Version: 0.8.
|
3
|
+
Version: 0.8.2
|
4
4
|
Summary: Base classes for sklearn-like parametric objects
|
5
5
|
Author-email: sktime developers <sktime.toolbox@gmail.com>
|
6
6
|
Maintainer: Franz Király
|
@@ -90,10 +90,10 @@ Requires-Dist: nbsphinx>=0.8.6; extra == "docs"
|
|
90
90
|
Requires-Dist: numpydoc; extra == "docs"
|
91
91
|
Requires-Dist: pydata-sphinx-theme; extra == "docs"
|
92
92
|
Requires-Dist: sphinx-issues<5.0.0; extra == "docs"
|
93
|
-
Requires-Dist: sphinx-gallery<0.
|
93
|
+
Requires-Dist: sphinx-gallery<0.18.0; extra == "docs"
|
94
94
|
Requires-Dist: sphinx-panels; extra == "docs"
|
95
95
|
Requires-Dist: sphinx-design<0.7.0; extra == "docs"
|
96
|
-
Requires-Dist: Sphinx!=7.2.0,<
|
96
|
+
Requires-Dist: Sphinx!=7.2.0,<9.0.0; extra == "docs"
|
97
97
|
Requires-Dist: tabulate; extra == "docs"
|
98
98
|
Provides-Extra: test
|
99
99
|
Requires-Dist: pytest; extra == "test"
|
@@ -114,7 +114,7 @@ Requires-Dist: scikit-learn>=0.24.0; extra == "test"
|
|
114
114
|
`skbase` provides base classes for creating scikit-learn-like parametric objects,
|
115
115
|
along with tools to make it easier to build your own packages that follow these design patterns.
|
116
116
|
|
117
|
-
:rocket: Version 0.8.
|
117
|
+
:rocket: Version 0.8.2 is now available. Check out our
|
118
118
|
[release notes](https://skbase.readthedocs.io/en/latest/changelog.html).
|
119
119
|
|
120
120
|
| Overview | |
|
@@ -7,7 +7,7 @@
|
|
7
7
|
`skbase` provides base classes for creating scikit-learn-like parametric objects,
|
8
8
|
along with tools to make it easier to build your own packages that follow these design patterns.
|
9
9
|
|
10
|
-
:rocket: Version 0.8.
|
10
|
+
:rocket: Version 0.8.2 is now available. Check out our
|
11
11
|
[release notes](https://skbase.readthedocs.io/en/latest/changelog.html).
|
12
12
|
|
13
13
|
| Overview | |
|
@@ -1,6 +1,6 @@
|
|
1
1
|
[project]
|
2
2
|
name = "scikit-base"
|
3
|
-
version = "0.8.
|
3
|
+
version = "0.8.2"
|
4
4
|
description = "Base classes for sklearn-like parametric objects"
|
5
5
|
authors = [
|
6
6
|
{name = "sktime developers", email = "sktime.toolbox@gmail.com"},
|
@@ -71,10 +71,10 @@ docs = [
|
|
71
71
|
"numpydoc",
|
72
72
|
"pydata-sphinx-theme",
|
73
73
|
"sphinx-issues<5.0.0",
|
74
|
-
"sphinx-gallery<0.
|
74
|
+
"sphinx-gallery<0.18.0",
|
75
75
|
"sphinx-panels",
|
76
76
|
"sphinx-design<0.7.0",
|
77
|
-
"Sphinx
|
77
|
+
"Sphinx!=7.2.0,<9.0.0",
|
78
78
|
"tabulate",
|
79
79
|
]
|
80
80
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: scikit-base
|
3
|
-
Version: 0.8.
|
3
|
+
Version: 0.8.2
|
4
4
|
Summary: Base classes for sklearn-like parametric objects
|
5
5
|
Author-email: sktime developers <sktime.toolbox@gmail.com>
|
6
6
|
Maintainer: Franz Király
|
@@ -90,10 +90,10 @@ Requires-Dist: nbsphinx>=0.8.6; extra == "docs"
|
|
90
90
|
Requires-Dist: numpydoc; extra == "docs"
|
91
91
|
Requires-Dist: pydata-sphinx-theme; extra == "docs"
|
92
92
|
Requires-Dist: sphinx-issues<5.0.0; extra == "docs"
|
93
|
-
Requires-Dist: sphinx-gallery<0.
|
93
|
+
Requires-Dist: sphinx-gallery<0.18.0; extra == "docs"
|
94
94
|
Requires-Dist: sphinx-panels; extra == "docs"
|
95
95
|
Requires-Dist: sphinx-design<0.7.0; extra == "docs"
|
96
|
-
Requires-Dist: Sphinx!=7.2.0,<
|
96
|
+
Requires-Dist: Sphinx!=7.2.0,<9.0.0; extra == "docs"
|
97
97
|
Requires-Dist: tabulate; extra == "docs"
|
98
98
|
Provides-Extra: test
|
99
99
|
Requires-Dist: pytest; extra == "test"
|
@@ -114,7 +114,7 @@ Requires-Dist: scikit-learn>=0.24.0; extra == "test"
|
|
114
114
|
`skbase` provides base classes for creating scikit-learn-like parametric objects,
|
115
115
|
along with tools to make it easier to build your own packages that follow these design patterns.
|
116
116
|
|
117
|
-
:rocket: Version 0.8.
|
117
|
+
:rocket: Version 0.8.2 is now available. Check out our
|
118
118
|
[release notes](https://skbase.readthedocs.io/en/latest/changelog.html).
|
119
119
|
|
120
120
|
| Overview | |
|
@@ -251,7 +251,12 @@ SKBASE_FUNCTIONS_BY_MODULE.update(
|
|
251
251
|
"skbase.utils.dependencies._dependencies": (
|
252
252
|
"_check_soft_dependencies",
|
253
253
|
"_check_python_version",
|
254
|
+
"_check_env_marker",
|
254
255
|
"_check_estimator_deps",
|
256
|
+
"_get_pkg_version",
|
257
|
+
"_get_installed_packages",
|
258
|
+
"_normalize_requirement",
|
259
|
+
"_raise_at_severity",
|
255
260
|
),
|
256
261
|
"skbase.utils.random_state": (
|
257
262
|
"check_random_state",
|
@@ -0,0 +1,542 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
"""Utility to check soft dependency imports, and raise warnings or errors."""
|
3
|
+
import sys
|
4
|
+
import warnings
|
5
|
+
from functools import lru_cache
|
6
|
+
from importlib.metadata import distributions
|
7
|
+
from inspect import isclass
|
8
|
+
|
9
|
+
from packaging.markers import InvalidMarker, Marker
|
10
|
+
from packaging.requirements import InvalidRequirement, Requirement
|
11
|
+
from packaging.specifiers import InvalidSpecifier, Specifier, SpecifierSet
|
12
|
+
from packaging.version import InvalidVersion, Version
|
13
|
+
|
14
|
+
|
15
|
+
# todo 0.10.0: remove suppress_import_stdout argument
|
16
|
+
def _check_soft_dependencies(
|
17
|
+
*packages,
|
18
|
+
package_import_alias="deprecated",
|
19
|
+
severity="error",
|
20
|
+
obj=None,
|
21
|
+
msg=None,
|
22
|
+
suppress_import_stdout="deprecated",
|
23
|
+
):
|
24
|
+
"""Check if required soft dependencies are installed and raise error or warning.
|
25
|
+
|
26
|
+
Parameters
|
27
|
+
----------
|
28
|
+
packages : str or list/tuple of str, or length-1-tuple containing list/tuple of str
|
29
|
+
str should be package names and/or package version specifications to check.
|
30
|
+
Each str must be a PEP 440 compatible specifier string, for a single package.
|
31
|
+
For instance, the PEP 440 compatible package name such as ``"pandas"``;
|
32
|
+
or a package requirement specifier string such as ``"pandas>1.2.3"``.
|
33
|
+
arg can be str, kwargs tuple, or tuple/list of str, following calls are valid:
|
34
|
+
``_check_soft_dependencies("package1")``
|
35
|
+
``_check_soft_dependencies("package1", "package2")``
|
36
|
+
``_check_soft_dependencies(("package1", "package2"))``
|
37
|
+
``_check_soft_dependencies(["package1", "package2"])``
|
38
|
+
|
39
|
+
package_import_alias : ignored, present only for backwards compatibility
|
40
|
+
|
41
|
+
severity : str, "error" (default), "warning", "none"
|
42
|
+
whether the check should raise an error, a warning, or nothing
|
43
|
+
|
44
|
+
* "error" - raises a ``ModuleNotFoundError`` if one of packages is not installed
|
45
|
+
* "warning" - raises a warning if one of packages is not installed
|
46
|
+
function returns False if one of packages is not installed, otherwise True
|
47
|
+
* "none" - does not raise exception or warning
|
48
|
+
function returns False if one of packages is not installed, otherwise True
|
49
|
+
|
50
|
+
obj : python class, object, str, or None, default=None
|
51
|
+
if self is passed here when _check_soft_dependencies is called within __init__,
|
52
|
+
or a class is passed when it is called at the start of a single-class module,
|
53
|
+
the error message is more informative and will refer to the class/object;
|
54
|
+
if str is passed, will be used as name of the class/object or module
|
55
|
+
|
56
|
+
msg : str, or None, default=None
|
57
|
+
if str, will override the error message or warning shown with msg
|
58
|
+
|
59
|
+
Raises
|
60
|
+
------
|
61
|
+
InvalidRequirement
|
62
|
+
if package requirement strings are not PEP 440 compatible
|
63
|
+
ModuleNotFoundError
|
64
|
+
error with informative message, asking to install required soft dependencies
|
65
|
+
TypeError, ValueError
|
66
|
+
on invalid arguments
|
67
|
+
|
68
|
+
Returns
|
69
|
+
-------
|
70
|
+
boolean - whether all packages are installed, only if no exception is raised
|
71
|
+
"""
|
72
|
+
# todo 0.10.0: remove this warning
|
73
|
+
if suppress_import_stdout != "deprecated":
|
74
|
+
warnings.warn(
|
75
|
+
"In skbase _check_soft_dependencies, the suppress_import_stdout argument "
|
76
|
+
"is deprecated and no longer has any effect. "
|
77
|
+
"The argument will be removed in version 0.10.0, so users of the "
|
78
|
+
"_check_soft_dependencies utility should not pass this argument anymore. "
|
79
|
+
"The _check_soft_dependencies utility also no longer causes imports, "
|
80
|
+
"hence no stdout "
|
81
|
+
"output is created from imports, for any setting of the "
|
82
|
+
"suppress_import_stdout argument. If you wish to import packages "
|
83
|
+
"and make use of stdout prints, import the package directly instead.",
|
84
|
+
DeprecationWarning,
|
85
|
+
stacklevel=2,
|
86
|
+
)
|
87
|
+
|
88
|
+
if len(packages) == 1 and isinstance(packages[0], (tuple, list)):
|
89
|
+
packages = packages[0]
|
90
|
+
if not all(isinstance(x, str) for x in packages):
|
91
|
+
raise TypeError(
|
92
|
+
"packages argument of _check_soft_dependencies must be str or tuple of "
|
93
|
+
f"str, but found packages argument of type {type(packages)}"
|
94
|
+
)
|
95
|
+
|
96
|
+
if obj is None:
|
97
|
+
class_name = "This functionality"
|
98
|
+
elif not isclass(obj):
|
99
|
+
class_name = type(obj).__name__
|
100
|
+
elif isclass(obj):
|
101
|
+
class_name = obj.__name__
|
102
|
+
elif isinstance(obj, str):
|
103
|
+
class_name = obj
|
104
|
+
else:
|
105
|
+
raise TypeError(
|
106
|
+
"obj argument of _check_soft_dependencies must be a class, an object,"
|
107
|
+
" a str, or None, but found obj of type"
|
108
|
+
f" {type(obj)}"
|
109
|
+
)
|
110
|
+
|
111
|
+
if msg is not None and not isinstance(msg, str):
|
112
|
+
raise TypeError(
|
113
|
+
"msg argument of _check_soft_dependencies must be a str, "
|
114
|
+
f"or None, but found msg of type {type(msg)}"
|
115
|
+
)
|
116
|
+
|
117
|
+
for package in packages:
|
118
|
+
try:
|
119
|
+
req = Requirement(package)
|
120
|
+
req = _normalize_requirement(req)
|
121
|
+
except InvalidRequirement:
|
122
|
+
msg_version = (
|
123
|
+
f"wrong format for package requirement string, "
|
124
|
+
f"passed via packages argument of _check_soft_dependencies, "
|
125
|
+
f'must be PEP 440 compatible requirement string, e.g., "pandas"'
|
126
|
+
f' or "pandas>1.1", but found {package!r}'
|
127
|
+
)
|
128
|
+
raise InvalidRequirement(msg_version) from None
|
129
|
+
|
130
|
+
package_name = req.name
|
131
|
+
package_version_req = req.specifier
|
132
|
+
|
133
|
+
pkg_env_version = _get_pkg_version(package_name)
|
134
|
+
|
135
|
+
# if package not present, make the user aware of installation reqs
|
136
|
+
if pkg_env_version is None:
|
137
|
+
if obj is None and msg is None:
|
138
|
+
msg = (
|
139
|
+
f"{class_name} requires package {package!r} to be present "
|
140
|
+
f"in the python environment, but {package!r} was not found. "
|
141
|
+
)
|
142
|
+
elif msg is None: # obj is not None, msg is None
|
143
|
+
msg = (
|
144
|
+
f"{class_name} requires package {package!r} to be present "
|
145
|
+
f"in the python environment, but {package!r} was not found. "
|
146
|
+
f"{package!r} is a dependency of {class_name} and required "
|
147
|
+
f"to construct it. "
|
148
|
+
)
|
149
|
+
msg = msg + (
|
150
|
+
f"Please run: `pip install {package}` to "
|
151
|
+
f"install the {package} package. "
|
152
|
+
)
|
153
|
+
# if msg is not None, none of the above is executed,
|
154
|
+
# so if msg is passed it overrides the default messages
|
155
|
+
|
156
|
+
_raise_at_severity(msg, severity, caller="_check_soft_dependencies")
|
157
|
+
return False
|
158
|
+
|
159
|
+
# now we check compatibility with the version specifier if non-empty
|
160
|
+
if package_version_req != SpecifierSet(""):
|
161
|
+
msg = (
|
162
|
+
f"{class_name} requires package {package!r} to be present "
|
163
|
+
f"in the python environment, with version {package_version_req}, "
|
164
|
+
f"but incompatible version {pkg_env_version} was found. "
|
165
|
+
)
|
166
|
+
if obj is not None:
|
167
|
+
msg = msg + (
|
168
|
+
f"{package!r}, with version {package_version_req},"
|
169
|
+
f"is a dependency of {class_name} and required to construct it. "
|
170
|
+
)
|
171
|
+
|
172
|
+
# raise error/warning or return False if version is incompatible
|
173
|
+
if pkg_env_version not in package_version_req:
|
174
|
+
_raise_at_severity(msg, severity, caller="_check_soft_dependencies")
|
175
|
+
return False
|
176
|
+
|
177
|
+
# if package can be imported and no version issue was caught for any string,
|
178
|
+
# then obj is compatible with the requirements and we should return True
|
179
|
+
return True
|
180
|
+
|
181
|
+
|
182
|
+
@lru_cache
|
183
|
+
def _get_installed_packages_private():
|
184
|
+
"""Get a dictionary of installed packages and their versions.
|
185
|
+
|
186
|
+
Same as _get_installed_packages, but internal to avoid mutating the lru_cache
|
187
|
+
by accident.
|
188
|
+
"""
|
189
|
+
dists = distributions()
|
190
|
+
packages = {dist.metadata["Name"]: dist.version for dist in dists}
|
191
|
+
return packages
|
192
|
+
|
193
|
+
|
194
|
+
def _get_installed_packages():
|
195
|
+
"""Get a dictionary of installed packages and their versions.
|
196
|
+
|
197
|
+
Returns
|
198
|
+
-------
|
199
|
+
dict : dictionary of installed packages and their versions
|
200
|
+
keys are PEP 440 compatible package names, values are package versions
|
201
|
+
MAJOR.MINOR.PATCH version format is used for versions, e.g., "1.2.3"
|
202
|
+
"""
|
203
|
+
return _get_installed_packages_private().copy()
|
204
|
+
|
205
|
+
|
206
|
+
def _get_pkg_version(package_name):
|
207
|
+
"""Check whether package is available in environment, and return its version if yes.
|
208
|
+
|
209
|
+
Returns ``Version`` object from ``lru_cache``, this should not be mutated.
|
210
|
+
|
211
|
+
Parameters
|
212
|
+
----------
|
213
|
+
package_name : str, optional, default=None
|
214
|
+
name of package to check,
|
215
|
+
PEP 440 compatibe specifier string, e.g., "pandas" or "sklearn".
|
216
|
+
This is the pypi package name, not the import name, e.g.,
|
217
|
+
``scikit-learn``, not ``sklearn``.
|
218
|
+
|
219
|
+
Returns
|
220
|
+
-------
|
221
|
+
None, if package is not found in python environment.
|
222
|
+
``importlib`` ``Version`` of package, if present in environment.
|
223
|
+
"""
|
224
|
+
pkgs = _get_installed_packages()
|
225
|
+
pkg_vers_str = pkgs.get(package_name, None)
|
226
|
+
if pkg_vers_str is None:
|
227
|
+
return None
|
228
|
+
try:
|
229
|
+
pkg_env_version = Version(pkg_vers_str)
|
230
|
+
except InvalidVersion:
|
231
|
+
pkg_env_version = None
|
232
|
+
return pkg_env_version
|
233
|
+
|
234
|
+
|
235
|
+
def _check_python_version(obj, package=None, msg=None, severity="error"):
|
236
|
+
"""Check if system python version is compatible with requirements of obj.
|
237
|
+
|
238
|
+
Parameters
|
239
|
+
----------
|
240
|
+
obj : BaseObject descendant
|
241
|
+
used to check python version
|
242
|
+
|
243
|
+
package : str, default = None
|
244
|
+
if given, will be used in error message as package name
|
245
|
+
|
246
|
+
msg : str, optional, default = default message (msg below)
|
247
|
+
error message to be returned in the ``ModuleNotFoundError``, overrides default
|
248
|
+
|
249
|
+
severity : str, "error" (default), "warning", "none"
|
250
|
+
whether the check should raise an error, a warning, or nothing
|
251
|
+
|
252
|
+
* "error" - raises a ``ModuleNotFoundError`` if one of packages is not installed
|
253
|
+
* "warning" - raises a warning if one of packages is not installed
|
254
|
+
function returns False if one of packages is not installed, otherwise True
|
255
|
+
* "none" - does not raise exception or warning
|
256
|
+
function returns False if one of packages is not installed, otherwise True
|
257
|
+
|
258
|
+
Returns
|
259
|
+
-------
|
260
|
+
compatible : bool, whether obj is compatible with system python version
|
261
|
+
check is using the python_version tag of obj
|
262
|
+
|
263
|
+
Raises
|
264
|
+
------
|
265
|
+
ModuleNotFoundError
|
266
|
+
User friendly error if obj has python_version tag that is
|
267
|
+
incompatible with the system python version. If package is given,
|
268
|
+
error message gives package as the reason for incompatibility.
|
269
|
+
"""
|
270
|
+
est_specifier_tag = obj.get_class_tag("python_version", tag_value_default="None")
|
271
|
+
if est_specifier_tag in ["None", None]:
|
272
|
+
return True
|
273
|
+
|
274
|
+
try:
|
275
|
+
est_specifier = SpecifierSet(est_specifier_tag)
|
276
|
+
except InvalidSpecifier:
|
277
|
+
msg_version = (
|
278
|
+
f"wrong format for python_version tag, "
|
279
|
+
f'must be PEP 440 compatible specifier string, e.g., "<3.9, >= 3.6.3",'
|
280
|
+
f" but found {est_specifier_tag!r}"
|
281
|
+
)
|
282
|
+
raise InvalidSpecifier(msg_version) from None
|
283
|
+
|
284
|
+
# python sys version, e.g., "3.8.12"
|
285
|
+
sys_version = sys.version.split(" ")[0]
|
286
|
+
|
287
|
+
if sys_version in est_specifier:
|
288
|
+
return True
|
289
|
+
# now we know that est_version is not compatible with sys_version
|
290
|
+
|
291
|
+
if isclass(obj):
|
292
|
+
class_name = obj.__name__
|
293
|
+
else:
|
294
|
+
class_name = type(obj).__name__
|
295
|
+
|
296
|
+
if not isinstance(msg, str):
|
297
|
+
msg = (
|
298
|
+
f"{class_name} requires python version to be {est_specifier},"
|
299
|
+
f" but system python version is {sys.version}."
|
300
|
+
)
|
301
|
+
|
302
|
+
if package is not None:
|
303
|
+
msg += (
|
304
|
+
f" This is due to python version requirements of the {package} package."
|
305
|
+
)
|
306
|
+
|
307
|
+
_raise_at_severity(msg, severity, caller="_check_python_version")
|
308
|
+
return False
|
309
|
+
|
310
|
+
|
311
|
+
def _check_env_marker(obj, package=None, msg=None, severity="error"):
|
312
|
+
"""Check if packaging marker tag is with requirements of obj.
|
313
|
+
|
314
|
+
Parameters
|
315
|
+
----------
|
316
|
+
obj : BaseObject descendant
|
317
|
+
used to check python version
|
318
|
+
package : str, default = None
|
319
|
+
if given, will be used in error message as package name
|
320
|
+
msg : str, optional, default = default message (msg below)
|
321
|
+
error message to be returned in the `ModuleNotFoundError`, overrides default
|
322
|
+
|
323
|
+
severity : str, "error" (default), "warning", "none"
|
324
|
+
whether the check should raise an error, a warning, or nothing
|
325
|
+
|
326
|
+
* "error" - raises a ``ModuleNotFoundError`` if one of packages is not installed
|
327
|
+
* "warning" - raises a warning if one of packages is not installed
|
328
|
+
function returns False if one of packages is not installed, otherwise True
|
329
|
+
* "none" - does not raise exception or warning
|
330
|
+
function returns False if one of packages is not installed, otherwise True
|
331
|
+
|
332
|
+
Returns
|
333
|
+
-------
|
334
|
+
compatible : bool, whether obj is compatible with system python version
|
335
|
+
check is using the python_version tag of obj
|
336
|
+
|
337
|
+
Raises
|
338
|
+
------
|
339
|
+
InvalidMarker
|
340
|
+
User friendly error if obj has env_marker tag that is not a
|
341
|
+
packaging compatible marker string
|
342
|
+
ModuleNotFoundError
|
343
|
+
User friendly error if obj has an env_marker tag that is
|
344
|
+
incompatible with the python environment. If package is given,
|
345
|
+
error message gives package as the reason for incompatibility.
|
346
|
+
"""
|
347
|
+
est_marker_tag = obj.get_class_tag("env_marker", tag_value_default="None")
|
348
|
+
if est_marker_tag in ["None", None]:
|
349
|
+
return True
|
350
|
+
|
351
|
+
try:
|
352
|
+
est_marker = Marker(est_marker_tag)
|
353
|
+
except InvalidMarker:
|
354
|
+
msg_version = (
|
355
|
+
f"wrong format for env_marker tag, "
|
356
|
+
f"must be PEP 508 compatible specifier string, e.g., "
|
357
|
+
f'platform_system!="windows", but found {est_marker_tag!r}'
|
358
|
+
)
|
359
|
+
raise InvalidMarker(msg_version) from None
|
360
|
+
|
361
|
+
if est_marker.evaluate():
|
362
|
+
return True
|
363
|
+
# now we know that est_marker is not compatible with the environment
|
364
|
+
|
365
|
+
if isclass(obj):
|
366
|
+
class_name = obj.__name__
|
367
|
+
else:
|
368
|
+
class_name = type(obj).__name__
|
369
|
+
|
370
|
+
if not isinstance(msg, str):
|
371
|
+
msg = (
|
372
|
+
f"{class_name} requires an environment to satisfy "
|
373
|
+
f"packaging marker spec {est_marker}, but environment does not satisfy it."
|
374
|
+
)
|
375
|
+
|
376
|
+
if package is not None:
|
377
|
+
msg += f" This is due to requirements of the {package} package."
|
378
|
+
|
379
|
+
_raise_at_severity(msg, severity, caller="_check_env_marker")
|
380
|
+
return False
|
381
|
+
|
382
|
+
|
383
|
+
def _check_estimator_deps(obj, msg=None, severity="error"):
|
384
|
+
"""Check if object/estimator's package & python requirements are met by python env.
|
385
|
+
|
386
|
+
Convenience wrapper around `_check_python_version` and `_check_soft_dependencies`,
|
387
|
+
checking against estimator tags `"python_version"`, `"python_dependencies"`.
|
388
|
+
|
389
|
+
Checks whether dependency requirements of `BaseObject`-s in `obj`
|
390
|
+
are satisfied by the current python environment.
|
391
|
+
|
392
|
+
Parameters
|
393
|
+
----------
|
394
|
+
obj : BaseObject descendant, instance or class, or list/tuple thereof
|
395
|
+
object(s) that this function checks compatibility of, with the python env
|
396
|
+
|
397
|
+
msg : str, optional, default = default message (msg below)
|
398
|
+
error message to be returned in the ``ModuleNotFoundError``, overrides default
|
399
|
+
|
400
|
+
severity : str, "error" (default), "warning", "none"
|
401
|
+
whether the check should raise an error, a warning, or nothing
|
402
|
+
|
403
|
+
* "error" - raises a ``ModuleNotFoundError`` if one of packages is not installed
|
404
|
+
* "warning" - raises a warning if one of packages is not installed
|
405
|
+
function returns False if one of packages is not installed, otherwise True
|
406
|
+
* "none" - does not raise exception or warning
|
407
|
+
function returns False if one of packages is not installed, otherwise True
|
408
|
+
|
409
|
+
Returns
|
410
|
+
-------
|
411
|
+
compatible : bool, whether `obj` is compatible with python environment
|
412
|
+
False is returned only if no exception is raised by the function
|
413
|
+
checks for python version using the python_version tag of obj
|
414
|
+
checks for soft dependencies present using the python_dependencies tag of obj
|
415
|
+
if `obj` contains multiple `BaseObject`-s, checks whether all are compatible
|
416
|
+
|
417
|
+
Raises
|
418
|
+
------
|
419
|
+
ModuleNotFoundError
|
420
|
+
User friendly error if obj has python_version tag that is
|
421
|
+
incompatible with the system python version.
|
422
|
+
Compatible python versions are determined by the "python_version" tag of obj.
|
423
|
+
User friendly error if obj has package dependencies that are not satisfied.
|
424
|
+
Packages are determined based on the "python_dependencies" tag of obj.
|
425
|
+
"""
|
426
|
+
compatible = True
|
427
|
+
|
428
|
+
# if list or tuple, recurse & iterate over element, and return conjunction
|
429
|
+
if isinstance(obj, (list, tuple)):
|
430
|
+
for x in obj:
|
431
|
+
x_chk = _check_estimator_deps(x, msg=msg, severity=severity)
|
432
|
+
compatible = compatible and x_chk
|
433
|
+
return compatible
|
434
|
+
|
435
|
+
compatible = compatible and _check_python_version(obj, severity=severity)
|
436
|
+
compatible = compatible and _check_env_marker(obj, severity=severity)
|
437
|
+
|
438
|
+
pkg_deps = obj.get_class_tag("python_dependencies", None)
|
439
|
+
pck_alias = obj.get_class_tag("python_dependencies_alias", None)
|
440
|
+
if pkg_deps is not None and not isinstance(pkg_deps, list):
|
441
|
+
pkg_deps = [pkg_deps]
|
442
|
+
if pkg_deps is not None:
|
443
|
+
pkg_deps_ok = _check_soft_dependencies(
|
444
|
+
*pkg_deps, severity=severity, obj=obj, package_import_alias=pck_alias
|
445
|
+
)
|
446
|
+
compatible = compatible and pkg_deps_ok
|
447
|
+
|
448
|
+
return compatible
|
449
|
+
|
450
|
+
|
451
|
+
def _normalize_requirement(req):
|
452
|
+
"""Normalize packaging Requirement by removing build metadata from versions.
|
453
|
+
|
454
|
+
Parameters
|
455
|
+
----------
|
456
|
+
req : packaging.requirements.Requirement
|
457
|
+
requirement string to normalize, e.g., Requirement("pandas>1.2.3+foobar")
|
458
|
+
|
459
|
+
Returns
|
460
|
+
-------
|
461
|
+
normalized_req : packaging.requirements.Requirement
|
462
|
+
normalized requirement object with build metadata removed from versions,
|
463
|
+
e.g., Requirement("pandas>1.2.3")
|
464
|
+
"""
|
465
|
+
# Process each specifier in the requirement
|
466
|
+
normalized_specs = []
|
467
|
+
for spec in req.specifier:
|
468
|
+
# Parse the version and remove the build metadata
|
469
|
+
spec_v = Version(spec.version)
|
470
|
+
version_wo_build_metadata = f"{spec_v.major}.{spec_v.minor}.{spec_v.micro}"
|
471
|
+
|
472
|
+
# Create a new specifier without the build metadata
|
473
|
+
normalized_spec = Specifier(f"{spec.operator}{version_wo_build_metadata}")
|
474
|
+
normalized_specs.append(normalized_spec)
|
475
|
+
|
476
|
+
# Reconstruct the specifier set
|
477
|
+
normalized_specifier_set = SpecifierSet(",".join(str(s) for s in normalized_specs))
|
478
|
+
|
479
|
+
# Create a new Requirement object with the normalized specifiers
|
480
|
+
normalized_req = Requirement(f"{req.name}{normalized_specifier_set}")
|
481
|
+
|
482
|
+
return normalized_req
|
483
|
+
|
484
|
+
|
485
|
+
def _raise_at_severity(
|
486
|
+
msg,
|
487
|
+
severity,
|
488
|
+
exception_type=None,
|
489
|
+
warning_type=None,
|
490
|
+
stacklevel=2,
|
491
|
+
caller="_raise_at_severity",
|
492
|
+
):
|
493
|
+
"""Raise exception or warning or take no action, based on severity.
|
494
|
+
|
495
|
+
Parameters
|
496
|
+
----------
|
497
|
+
msg : str
|
498
|
+
message to raise or warn
|
499
|
+
|
500
|
+
severity : str, "error" (default), "warning", "none"
|
501
|
+
whether the check should raise an error, a warning, or nothing
|
502
|
+
|
503
|
+
* "error" - raises a ``ModuleNotFoundError`` if one of packages is not installed
|
504
|
+
* "warning" - raises a warning if one of packages is not installed
|
505
|
+
function returns False if one of packages is not installed, otherwise True
|
506
|
+
* "none" - does not raise exception or warning
|
507
|
+
function returns False if one of packages is not installed, otherwise True
|
508
|
+
|
509
|
+
exception_type : Exception, default=ModuleNotFoundError
|
510
|
+
exception type to raise if severity="severity"
|
511
|
+
warning_type : warning, default=Warning
|
512
|
+
warning type to raise if severity="warning"
|
513
|
+
stacklevel : int, default=2
|
514
|
+
stacklevel for warnings, if severity="warning"
|
515
|
+
caller : str, default="_raise_at_severity"
|
516
|
+
caller name, used in exception if severity not in ["error", "warning", "none"]
|
517
|
+
|
518
|
+
Returns
|
519
|
+
-------
|
520
|
+
None
|
521
|
+
|
522
|
+
Raises
|
523
|
+
------
|
524
|
+
exception : exception_type, if severity="error"
|
525
|
+
warning : warning+type, if severity="warning"
|
526
|
+
ValueError : if severity not in ["error", "warning", "none"]
|
527
|
+
"""
|
528
|
+
if exception_type is None:
|
529
|
+
exception_type = ModuleNotFoundError
|
530
|
+
|
531
|
+
if severity == "error":
|
532
|
+
raise exception_type(msg)
|
533
|
+
elif severity == "warning":
|
534
|
+
warnings.warn(msg, category=warning_type, stacklevel=stacklevel)
|
535
|
+
elif severity == "none":
|
536
|
+
return None
|
537
|
+
else:
|
538
|
+
raise ValueError(
|
539
|
+
f"Error in calling {caller}, severity "
|
540
|
+
f'argument must be "error", "warning", or "none", found {severity!r}.'
|
541
|
+
)
|
542
|
+
return None
|
@@ -1,341 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
"""Utility to check soft dependency imports, and raise warnings or errors."""
|
3
|
-
import sys
|
4
|
-
import warnings
|
5
|
-
from importlib import import_module
|
6
|
-
from inspect import isclass
|
7
|
-
from typing import List
|
8
|
-
|
9
|
-
from packaging.requirements import InvalidRequirement, Requirement
|
10
|
-
from packaging.specifiers import InvalidSpecifier, SpecifierSet
|
11
|
-
|
12
|
-
from skbase.utils.stdout_mute import StdoutMute
|
13
|
-
|
14
|
-
__author__: List[str] = ["fkiraly", "mloning"]
|
15
|
-
|
16
|
-
|
17
|
-
def _check_soft_dependencies(
|
18
|
-
*packages,
|
19
|
-
package_import_alias=None,
|
20
|
-
severity="error",
|
21
|
-
obj=None,
|
22
|
-
msg=None,
|
23
|
-
suppress_import_stdout=False,
|
24
|
-
):
|
25
|
-
"""Check if required soft dependencies are installed and raise error or warning.
|
26
|
-
|
27
|
-
Parameters
|
28
|
-
----------
|
29
|
-
packages : str or list/tuple of str, or length-1-tuple containing list/tuple of str
|
30
|
-
str should be package names and/or package version specifications to check.
|
31
|
-
Each str must be a PEP 440 compatible specifier string, for a single package.
|
32
|
-
For instance, the PEP 440 compatible package name such as "pandas";
|
33
|
-
or a package requirement specifier string such as "pandas>1.2.3".
|
34
|
-
arg can be str, kwargs tuple, or tuple/list of str, following calls are valid:
|
35
|
-
`_check_soft_dependencies("package1")`
|
36
|
-
`_check_soft_dependencies("package1", "package2")`
|
37
|
-
`_check_soft_dependencies(("package1", "package2"))`
|
38
|
-
`_check_soft_dependencies(["package1", "package2"])`
|
39
|
-
package_import_alias : dict with str keys and values, optional, default=empty
|
40
|
-
key-value pairs are package name, import name
|
41
|
-
import name is str used in python import, i.e., from import_name import ...
|
42
|
-
should be provided if import name differs from package name
|
43
|
-
severity : str, "error" (default), "warning", "none"
|
44
|
-
behaviour for raising errors or warnings
|
45
|
-
"error" - raises a `ModuleNotFoundError` if one of packages is not installed
|
46
|
-
"warning" - raises a warning if one of packages is not installed
|
47
|
-
function returns False if one of packages is not installed, otherwise True
|
48
|
-
"none" - does not raise exception or warning
|
49
|
-
function returns False if one of packages is not installed, otherwise True
|
50
|
-
obj : python class, object, str, or None, default=None
|
51
|
-
if self is passed here when _check_soft_dependencies is called within __init__,
|
52
|
-
or a class is passed when it is called at the start of a single-class module,
|
53
|
-
the error message is more informative and will refer to the class/object;
|
54
|
-
if str is passed, will be used as name of the class/object or module
|
55
|
-
msg : str, or None, default=None
|
56
|
-
if str, will override the error message or warning shown with msg
|
57
|
-
suppress_import_stdout : bool, optional. Default=False
|
58
|
-
whether to suppress stdout printout upon import.
|
59
|
-
|
60
|
-
Raises
|
61
|
-
------
|
62
|
-
ModuleNotFoundError
|
63
|
-
error with informative message, asking to install required soft dependencies
|
64
|
-
|
65
|
-
Returns
|
66
|
-
-------
|
67
|
-
boolean - whether all packages are installed, only if no exception is raised
|
68
|
-
"""
|
69
|
-
if len(packages) == 1 and isinstance(packages[0], (tuple, list)):
|
70
|
-
packages = packages[0]
|
71
|
-
if not all(isinstance(x, str) for x in packages):
|
72
|
-
raise TypeError(
|
73
|
-
"packages argument of _check_soft_dependencies must be str or tuple of "
|
74
|
-
f"str, but found packages argument of type {type(packages)}"
|
75
|
-
)
|
76
|
-
|
77
|
-
if package_import_alias is None:
|
78
|
-
package_import_alias = {}
|
79
|
-
msg_pkg_import_alias = (
|
80
|
-
"package_import_alias argument of _check_soft_dependencies must "
|
81
|
-
"be a dict with str keys and values, but found "
|
82
|
-
f"package_import_alias of type {type(package_import_alias)}"
|
83
|
-
)
|
84
|
-
if not isinstance(package_import_alias, dict):
|
85
|
-
raise TypeError(msg_pkg_import_alias)
|
86
|
-
if not all(isinstance(x, str) for x in package_import_alias.keys()):
|
87
|
-
raise TypeError(msg_pkg_import_alias)
|
88
|
-
if not all(isinstance(x, str) for x in package_import_alias.values()):
|
89
|
-
raise TypeError(msg_pkg_import_alias)
|
90
|
-
|
91
|
-
if obj is None:
|
92
|
-
class_name = "This functionality"
|
93
|
-
elif not isclass(obj):
|
94
|
-
class_name = type(obj).__name__
|
95
|
-
elif isclass(obj):
|
96
|
-
class_name = obj.__name__
|
97
|
-
elif isinstance(obj, str):
|
98
|
-
class_name = obj
|
99
|
-
else:
|
100
|
-
raise TypeError(
|
101
|
-
"obj argument of _check_soft_dependencies must be a class, an object,"
|
102
|
-
" a str, or None, but found obj of type"
|
103
|
-
f" {type(obj)}"
|
104
|
-
)
|
105
|
-
|
106
|
-
if msg is not None and not isinstance(msg, str):
|
107
|
-
raise TypeError(
|
108
|
-
"msg argument of _check_soft_dependencies must be a str, "
|
109
|
-
f"or None, but found msg of type {type(msg)}"
|
110
|
-
)
|
111
|
-
|
112
|
-
for package in packages:
|
113
|
-
try:
|
114
|
-
req = Requirement(package)
|
115
|
-
except InvalidRequirement:
|
116
|
-
msg_version = (
|
117
|
-
f"wrong format for package requirement string, "
|
118
|
-
f"passed via packages argument of _check_soft_dependencies, "
|
119
|
-
f'must be PEP 440 compatible requirement string, e.g., "pandas"'
|
120
|
-
f' or "pandas>1.1", but found {package!r}'
|
121
|
-
)
|
122
|
-
raise InvalidRequirement(msg_version) from None
|
123
|
-
|
124
|
-
package_name = req.name
|
125
|
-
package_version_req = req.specifier
|
126
|
-
|
127
|
-
# determine the package import
|
128
|
-
if package_name in package_import_alias.keys():
|
129
|
-
package_import_name = package_import_alias[package_name]
|
130
|
-
else:
|
131
|
-
package_import_name = package_name
|
132
|
-
# attempt import - if not possible, we know we need to raise warning/exception
|
133
|
-
try:
|
134
|
-
with StdoutMute(active=suppress_import_stdout):
|
135
|
-
pkg_ref = import_module(package_import_name)
|
136
|
-
# if package cannot be imported, make the user aware of installation requirement
|
137
|
-
except ModuleNotFoundError as e:
|
138
|
-
if msg is None:
|
139
|
-
msg = (
|
140
|
-
f"{e}. "
|
141
|
-
f"{class_name} requires package {package!r} to be present "
|
142
|
-
f"in the python environment, but {package!r} was not found. "
|
143
|
-
)
|
144
|
-
if obj is not None:
|
145
|
-
msg = msg + (
|
146
|
-
f"{package!r} is a dependency of {class_name} and required "
|
147
|
-
f"to construct it. "
|
148
|
-
)
|
149
|
-
msg = msg + (
|
150
|
-
f"Please run: `pip install {package}` to "
|
151
|
-
f"install the {package} package. "
|
152
|
-
)
|
153
|
-
# if msg is not None, none of the above is executed,
|
154
|
-
# so if msg is passed it overrides the default messages
|
155
|
-
|
156
|
-
if severity == "error":
|
157
|
-
raise ModuleNotFoundError(msg) from e
|
158
|
-
elif severity == "warning":
|
159
|
-
warnings.warn(msg, stacklevel=2)
|
160
|
-
return False
|
161
|
-
elif severity == "none":
|
162
|
-
return False
|
163
|
-
else:
|
164
|
-
raise RuntimeError(
|
165
|
-
"Error in calling _check_soft_dependencies, severity "
|
166
|
-
'argument must be "error", "warning", or "none",'
|
167
|
-
f"found {severity!r}."
|
168
|
-
) from e
|
169
|
-
|
170
|
-
# now we check compatibility with the version specifier if non-empty
|
171
|
-
if package_version_req != SpecifierSet(""):
|
172
|
-
pkg_env_version = pkg_ref.__version__
|
173
|
-
|
174
|
-
msg = (
|
175
|
-
f"{class_name} requires package {package!r} to be present "
|
176
|
-
f"in the python environment, with version {package_version_req}, "
|
177
|
-
f"but incompatible version {pkg_env_version} was found. "
|
178
|
-
)
|
179
|
-
if obj is not None:
|
180
|
-
msg = msg + (
|
181
|
-
f"{package!r}, with version {package_version_req},"
|
182
|
-
f"is a dependency of {class_name} and required to construct it. "
|
183
|
-
)
|
184
|
-
|
185
|
-
# raise error/warning or return False if version is incompatible
|
186
|
-
if pkg_env_version not in package_version_req:
|
187
|
-
if severity == "error":
|
188
|
-
raise ModuleNotFoundError(msg)
|
189
|
-
elif severity == "warning":
|
190
|
-
warnings.warn(msg, stacklevel=2)
|
191
|
-
elif severity == "none":
|
192
|
-
return False
|
193
|
-
else:
|
194
|
-
raise RuntimeError(
|
195
|
-
"Error in calling _check_soft_dependencies, severity argument"
|
196
|
-
f' must be "error", "warning", or "none", found {severity!r}.'
|
197
|
-
)
|
198
|
-
|
199
|
-
# if package can be imported and no version issue was caught for any string,
|
200
|
-
# then obj is compatible with the requirements and we should return True
|
201
|
-
return True
|
202
|
-
|
203
|
-
|
204
|
-
def _check_python_version(obj, package=None, msg=None, severity="error"):
|
205
|
-
"""Check if system python version is compatible with requirements of obj.
|
206
|
-
|
207
|
-
Parameters
|
208
|
-
----------
|
209
|
-
obj : BaseObject descendant
|
210
|
-
used to check python version
|
211
|
-
package : str, default = None
|
212
|
-
if given, will be used in error message as package name
|
213
|
-
msg : str, optional, default = default message (msg below)
|
214
|
-
error message to be returned in the `ModuleNotFoundError`, overrides default
|
215
|
-
severity : str, "error" (default), "warning", or "none"
|
216
|
-
whether the check should raise an error, a warning, or nothing
|
217
|
-
|
218
|
-
Returns
|
219
|
-
-------
|
220
|
-
compatible : bool, whether obj is compatible with system python version
|
221
|
-
check is using the python_version tag of obj
|
222
|
-
|
223
|
-
Raises
|
224
|
-
------
|
225
|
-
ModuleNotFoundError
|
226
|
-
User friendly error if obj has python_version tag that is
|
227
|
-
incompatible with the system python version. If package is given,
|
228
|
-
error message gives package as the reason for incompatibility.
|
229
|
-
"""
|
230
|
-
est_specifier_tag = obj.get_class_tag("python_version", tag_value_default="None")
|
231
|
-
if est_specifier_tag in ["None", None]:
|
232
|
-
return True
|
233
|
-
|
234
|
-
try:
|
235
|
-
est_specifier = SpecifierSet(est_specifier_tag)
|
236
|
-
except InvalidSpecifier:
|
237
|
-
msg_version = (
|
238
|
-
f"wrong format for python_version tag, "
|
239
|
-
f'must be PEP 440 compatible specifier string, e.g., "<3.9, >= 3.6.3",'
|
240
|
-
f" but found {est_specifier_tag!r}"
|
241
|
-
)
|
242
|
-
raise InvalidSpecifier(msg_version) from None
|
243
|
-
|
244
|
-
# python sys version, e.g., "3.8.12"
|
245
|
-
sys_version = sys.version.split(" ")[0]
|
246
|
-
|
247
|
-
if sys_version in est_specifier:
|
248
|
-
return True
|
249
|
-
# now we know that est_version is not compatible with sys_version
|
250
|
-
if isclass(obj):
|
251
|
-
class_name = obj.__name__
|
252
|
-
else:
|
253
|
-
class_name = type(obj).__name__
|
254
|
-
|
255
|
-
if not isinstance(msg, str):
|
256
|
-
msg = (
|
257
|
-
f"{class_name} requires python version to be {est_specifier},"
|
258
|
-
f" but system python version is {sys.version}."
|
259
|
-
)
|
260
|
-
|
261
|
-
if package is not None:
|
262
|
-
msg += (
|
263
|
-
f" This is due to python version requirements of the {package} package."
|
264
|
-
)
|
265
|
-
|
266
|
-
if severity == "error":
|
267
|
-
raise ModuleNotFoundError(msg)
|
268
|
-
elif severity == "warning":
|
269
|
-
warnings.warn(msg, stacklevel=2)
|
270
|
-
elif severity == "none":
|
271
|
-
return False
|
272
|
-
else:
|
273
|
-
raise RuntimeError(
|
274
|
-
"Error in calling _check_python_version, severity "
|
275
|
-
f'argument must be "error", "warning", or "none", found {severity!r}.'
|
276
|
-
)
|
277
|
-
return True
|
278
|
-
|
279
|
-
|
280
|
-
def _check_estimator_deps(obj, msg=None, severity="error"):
|
281
|
-
"""Check if object/estimator's package & python requirements are met by python env.
|
282
|
-
|
283
|
-
Convenience wrapper around `_check_python_version` and `_check_soft_dependencies`,
|
284
|
-
checking against estimator tags `"python_version"`, `"python_dependencies"`.
|
285
|
-
|
286
|
-
Checks whether dependency requirements of `BaseObject`-s in `obj`
|
287
|
-
are satisfied by the current python environment.
|
288
|
-
|
289
|
-
Parameters
|
290
|
-
----------
|
291
|
-
obj : `BaseObject` descendant, instance or class, or list/tuple thereof
|
292
|
-
object(s) that this function checks compatibility of, with the python env
|
293
|
-
msg : str, optional, default = default message (msg below)
|
294
|
-
error message to be returned in the `ModuleNotFoundError`, overrides default
|
295
|
-
severity : str, "error" (default), "warning", or "none"
|
296
|
-
behaviour for raising errors or warnings
|
297
|
-
"error" - raises a `ModuleNotFoundError` if environment is incompatible
|
298
|
-
"warning" - raises a warning if environment is incompatible
|
299
|
-
function returns False if environment is incompatible, otherwise True
|
300
|
-
"none" - does not raise exception or warning
|
301
|
-
function returns False if environment is incompatible, otherwise True
|
302
|
-
|
303
|
-
Returns
|
304
|
-
-------
|
305
|
-
compatible : bool, whether `obj` is compatible with python environment
|
306
|
-
False is returned only if no exception is raised by the function
|
307
|
-
checks for python version using the python_version tag of obj
|
308
|
-
checks for soft dependencies present using the python_dependencies tag of obj
|
309
|
-
if `obj` contains multiple `BaseObject`-s, checks whether all are compatible
|
310
|
-
|
311
|
-
Raises
|
312
|
-
------
|
313
|
-
ModuleNotFoundError
|
314
|
-
User friendly error if obj has python_version tag that is
|
315
|
-
incompatible with the system python version.
|
316
|
-
Compatible python versions are determined by the "python_version" tag of obj.
|
317
|
-
User friendly error if obj has package dependencies that are not satisfied.
|
318
|
-
Packages are determined based on the "python_dependencies" tag of obj.
|
319
|
-
"""
|
320
|
-
compatible = True
|
321
|
-
|
322
|
-
# if list or tuple, recurse & iterate over element, and return conjunction
|
323
|
-
if isinstance(obj, (list, tuple)):
|
324
|
-
for x in obj:
|
325
|
-
x_chk = _check_estimator_deps(x, msg=msg, severity=severity)
|
326
|
-
compatible = compatible and x_chk
|
327
|
-
return compatible
|
328
|
-
|
329
|
-
compatible = compatible and _check_python_version(obj, severity=severity)
|
330
|
-
|
331
|
-
pkg_deps = obj.get_class_tag("python_dependencies", None)
|
332
|
-
pck_alias = obj.get_class_tag("python_dependencies_alias", None)
|
333
|
-
if pkg_deps is not None and not isinstance(pkg_deps, list):
|
334
|
-
pkg_deps = [pkg_deps]
|
335
|
-
if pkg_deps is not None:
|
336
|
-
pkg_deps_ok = _check_soft_dependencies(
|
337
|
-
*pkg_deps, severity=severity, obj=obj, package_import_alias=pck_alias
|
338
|
-
)
|
339
|
-
compatible = compatible and pkg_deps_ok
|
340
|
-
|
341
|
-
return compatible
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/utils/dependencies/tests/test_check_dependencies.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{scikit_base-0.8.1 → scikit_base-0.8.2}/skbase/validate/tests/test_iterable_named_objects.py
RENAMED
File without changes
|
File without changes
|