From 14d3e21369a8b49d1439680c38551ba817ba9c8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mochol=C3=AD?= Date: Wed, 22 Feb 2023 13:24:12 +0100 Subject: [PATCH 1/4] More resillient `RequirementCache` that checks for module importability (#112) Co-authored-by: Jirka --- CHANGELOG.md | 2 +- src/lightning_utilities/core/imports.py | 40 ++++++++++++++++++------- tests/unittests/core/test_imports.py | 11 +++++++ 3 files changed, 42 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a455e22a..2926d200 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,7 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed -- +- More resilient RequirementCache that checks for module import-ability ([#112](https://github.com/Lightning-AI/utilities/pull/112)) ## [0.7.0] - 2023-02-20 diff --git a/src/lightning_utilities/core/imports.py b/src/lightning_utilities/core/imports.py index 6d93fe23..38456c4d 100644 --- a/src/lightning_utilities/core/imports.py +++ b/src/lightning_utilities/core/imports.py @@ -84,7 +84,11 @@ def compare_version(package: str, op: Callable, version: str, use_base_version: class RequirementCache: - """Boolean-like class for check of requirement with extras and version specifiers. + """Boolean-like class to check for requirement and module availability. + + Args: + requirement: The requirement to check, version specifiers are allowed. + module: The optional module to try to import if the requirement check fails. >>> RequirementCache("torch>=0.1") Requirement 'torch>=0.1' met @@ -92,20 +96,36 @@ class RequirementCache: True >>> bool(RequirementCache("torch>100.0")) False + >>> RequirementCache("torch") + Requirement 'torch' met + >>> bool(RequirementCache("torch")) + True + >>> bool(RequirementCache("unknown_package")) + False """ - def __init__(self, requirement: str) -> None: + def __init__(self, requirement: str, module: Optional[str] = None) -> None: self.requirement = requirement + self.module = module def _check_requirement(self) -> None: - if not hasattr(self, "available"): - try: - pkg_resources.require(self.requirement) - self.available = True - self.message = f"Requirement {self.requirement!r} met" - except Exception as ex: - self.available = False - self.message = f"{ex.__class__.__name__}: {ex}. HINT: Try running `pip install -U {self.requirement!r}`" + if hasattr(self, "available"): + return + try: + # first try the pkg_resources requirement + pkg_resources.require(self.requirement) + self.available = True + self.message = f"Requirement {self.requirement!r} met" + except Exception as ex: + self.available = False + self.message = f"{ex.__class__.__name__}: {ex}. HINT: Try running `pip install -U {self.requirement!r}`" + requirement_contains_version_specifier = any(c in self.requirement for c in "=<>") + if not requirement_contains_version_specifier or self.module is not None: + module = self.requirement if self.module is None else self.module + # sometimes `pkg_resources.require()` fails but the module is importable + self.available = module_available(module) + if self.available: + self.message = f"Module {module!r} available" def __bool__(self) -> bool: """Format as bool.""" diff --git a/tests/unittests/core/test_imports.py b/tests/unittests/core/test_imports.py index 161077f7..059fb9aa 100644 --- a/tests/unittests/core/test_imports.py +++ b/tests/unittests/core/test_imports.py @@ -52,6 +52,17 @@ def test_requirement_cache(): assert not RequirementCache(f"pytest<{pytest.__version__}") assert "pip install -U '-'" in str(RequirementCache("-")) + # invalid requirement is skipped by valid module + assert RequirementCache(f"pytest<{pytest.__version__}", "pytest") + + cache = RequirementCache("this_module_is_not_installed") + assert not cache + assert "pip install -U 'this_module_is_not_installed" in str(cache) + + cache = RequirementCache("this_module_is_not_installed", "this_also_is_not") + assert not cache + assert "pip install -U 'this_module_is_not_installed" in str(cache) + def test_module_available_cache(): assert ModuleAvailableCache("pytest") From 9dfe22de712d5bcb00ac21797b9cbdd89ba9672c Mon Sep 17 00:00:00 2001 From: Jirka Borovec <6035284+Borda@users.noreply.github.com> Date: Thu, 23 Feb 2023 15:59:59 +0100 Subject: [PATCH 2/4] ci: remove duplicate try to publish (#114) --- .github/workflows/release-pypi.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/release-pypi.yml b/.github/workflows/release-pypi.yml index 46e585cb..0e43628d 100644 --- a/.github/workflows/release-pypi.yml +++ b/.github/workflows/release-pypi.yml @@ -56,7 +56,6 @@ jobs: run: ls -lh dist/ - name: Upload to release - if: startsWith(github.event.ref, 'refs/tags') || github.event_name == 'release' uses: AButler/upload-release-assets@v2.0 with: files: 'dist/*' @@ -65,7 +64,7 @@ jobs: delay-publish: needs: build-package - if: startsWith(github.event.ref, 'refs/tags') + if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags') runs-on: ubuntu-latest steps: - name: Delay ⏰ releasing @@ -93,7 +92,6 @@ jobs: # We do this, since failures on test.pypi aren't that bad - name: Publish to Test PyPI - if: startsWith(github.event.ref, 'refs/tags') || github.event_name == 'release' uses: pypa/gh-action-pypi-publish@v1.6.4 with: user: __token__ @@ -102,7 +100,6 @@ jobs: verbose: true - name: Publish distribution 📦 to PyPI - if: startsWith(github.event.ref, 'refs/tags') || github.event_name == 'release' uses: pypa/gh-action-pypi-publish@v1.6.4 with: user: __token__ From 521af065d10b0450b791f030a4a3e88d8d7fa5ed Mon Sep 17 00:00:00 2001 From: Jirka Date: Tue, 28 Feb 2023 00:32:49 +0100 Subject: [PATCH 3/4] update chlog after 0.7.1 --- CHANGELOG.md | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2926d200..4f12d584 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,17 +10,32 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- CI: guardian as parametrization closure ([#111](https://github.com/Lightning-AI/utilities/pull/111)) +- ### Changed -- CI: allow specify typing extra ([#110](https://github.com/Lightning-AI/utilities/pull/110)) +- + + +### Fixed + +- + + +## [0.7.1] - 2023-02-23 +### Added + +- CI: guardian as parametrization closure ([#111](https://github.com/Lightning-AI/utilities/pull/111)) + +### Changed + +- CI: allow specify typing extra ([#110](https://github.com/Lightning-AI/utilities/pull/110)) ### Fixed -- More resilient RequirementCache that checks for module import-ability ([#112](https://github.com/Lightning-AI/utilities/pull/112)) +- More resilient `RequirementCache` that checks for module import-ability ([#112](https://github.com/Lightning-AI/utilities/pull/112)) ## [0.7.0] - 2023-02-20 From 21341e92771d8c7e102e04db37c278614639a992 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 28 Feb 2023 07:01:31 +0100 Subject: [PATCH 4/4] [pre-commit.ci] pre-commit suggestions (#115) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 21254ac3..5bac87fb 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -56,7 +56,7 @@ repos: - "flake8-docstrings" - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: v0.0.249 + rev: v0.0.253 hooks: - id: ruff args: ["--fix"]