@@ -51,25 +51,59 @@ Using ``importlib.metadata`` and ``packaging``
51
51
As a safer alternative that does check whether the optional dependencies are
52
52
installed at the correct versions, :py:mod: `importlib.metadata ` and
53
53
:ref: `packaging ` can be used to iterate through the extra's requirements
54
- recursively and check whether all are installed in the current environment.
55
-
56
- This process is currently quite involved. An implementation can be found in
57
- `packaging-problems #664 <packaging-problems-664 _>`_, which is also made
58
- available in the `hbutils <https://pypi.org/project/hbutils/ >`_ package as
59
- ``hbutils.system.check_reqs ``.
60
- The possibility of offering a similar helper function in ``importlib.metadata ``
61
- or ``packaging `` themselves is still being discussed
62
- (`packaging-problems #317 <packaging-problems-317 _>`_).
63
-
64
- With ``check_reqs `` included in your codebase or imported from ``hbutils ``,
65
- usage is as simple as:
54
+ recursively and check whether all are installed in the current environment
55
+ (based on `code <hbutils-snippet _>`_ from the `hbutils `_ library):
66
56
67
57
.. code-block :: python
68
58
59
+ # TODO Unless we get special permission, this snippet is Apache-2-licensed:
60
+ # https://github.com/HansBug/hbutils/blob/927b0757449a781ce8e30132f26b06089a24cd71/LICENSE
61
+
62
+ from collections.abc import Iterable
63
+ from importlib.metadata import PackageNotFoundError, distribution, metadata
64
+
65
+ from packaging.metadata import Metadata
66
+ from packaging.requirements import Requirement
67
+
68
+ def check_reqs (req_strs : Iterable[str ]) -> bool :
69
+ return all (
70
+ _check_req_recursive(req)
71
+ for req_str in req_strs
72
+ if not (req := Requirement(req_str)).marker or req.marker.evaluate()
73
+ )
74
+
75
+ def _check_req_recursive (req : Requirement) -> bool :
76
+ try :
77
+ version = distribution(req.name).version
78
+ except PackageNotFoundError:
79
+ return False # req not installed
80
+
81
+ if not req.specifier.contains(version):
82
+ return False # req version does not match
83
+
84
+ req_metadata = Metadata.from_raw(metadata(req.name).json, validate = False )
85
+ for child_req in req_metadata.requires_dist or []:
86
+ for extra in req.extras:
87
+ if child_req.marker and child_req.marker.evaluate({" extra" : extra}):
88
+ if not _check_req_recursive(child_req):
89
+ return False
90
+ break
91
+
92
+ return True
93
+
94
+
95
+ # Perform check, e.g.:
69
96
extra_installed = check_reqs([" your-package[your-extra]" ])
70
97
71
- In contrast to the method above, this is typically done in :term: `LBYL ` style
72
- prior to importing the modules in question.
98
+ TODO Either point out that this snippet doesn't actually check everything
99
+ (https://github.com/HansBug/hbutils/issues/109) or fix it.
100
+
101
+ The possibility of offering a helper function similar to ``check_reqs `` in
102
+ ``importlib.metadata `` or ``packaging `` themselves is still being discussed
103
+ (`packaging-problems #317 <packaging-problems-317 _>`_).
104
+
105
+ In contrast to the method above, this check is typically done in :term: `LBYL `
106
+ style prior to importing the modules in question.
73
107
In principle, it could also be done after the imports succeeded just to check
74
108
the version, in which case the imports themselves would have to be wrapped in a
75
109
``try ``-``except `` block to handle the possibility of not being installed at
@@ -262,10 +296,12 @@ TODO mention that you might want to provide a way for users to check
262
296
263
297
------------------
264
298
299
+ .. _hbutils-snippet : https://github.com/HansBug/hbutils/blob/927b0757449a781ce8e30132f26b06089a24cd71/hbutils/system/python/package.py#L171-L242
300
+
301
+ .. _hbutils : https://pypi.org/project/hbutils/
302
+
265
303
.. _pkg_resources : https://setuptools.pypa.io/en/latest/pkg_resources.html
266
304
267
305
.. _packaging-problems-317 : https://github.com/pypa/packaging-problems/issues/317
268
306
269
- .. _packaging-problems-664 : https://github.com/pypa/packaging-problems/issues/664
270
-
271
307
.. _generalimport : https://github.com/ManderaGeneral/generalimport
0 commit comments