diff --git a/.all-contributorsrc b/.all-contributorsrc index 6826ad2c..da919087 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -103,6 +103,15 @@ "contributions": [ "maintenance" ] + }, + { + "login": "Abelarm", + "name": "Luigi Giugliano", + "avatar_url": "https://avatars.githubusercontent.com/u/6976921?v=4", + "profile": "https://github.com/Abelarm", + "contributions": [ + "code" + ] } ], "projectName": "skbase", diff --git a/skbase/utils/dependencies/_dependencies.py b/skbase/utils/dependencies/_dependencies.py index 2e1fda15..cf71888a 100644 --- a/skbase/utils/dependencies/_dependencies.py +++ b/skbase/utils/dependencies/_dependencies.py @@ -236,7 +236,9 @@ def _get_pkg_version(package_name): return pkg_env_version -def _check_python_version(obj, package=None, msg=None, severity="error"): +def _check_python_version( + obj, package=None, msg=None, severity="error", prereleases=True +): """Check if system python version is compatible with requirements of obj. Parameters @@ -259,6 +261,13 @@ def _check_python_version(obj, package=None, msg=None, severity="error"): * "none" - does not raise exception or warning function returns False if one of packages is not installed, otherwise True + prereleases: str, default = True + Whether prerelease versions are considered compatible. + If True, allows prerelease versions to be considered compatible. + If False, always considers prerelease versions as incompatible, i.e., always + raises error, warning, or returns False, if the system python version is a + prerelease. + Returns ------- compatible : bool, whether obj is compatible with system python version @@ -276,7 +285,7 @@ def _check_python_version(obj, package=None, msg=None, severity="error"): return True try: - est_specifier = SpecifierSet(est_specifier_tag) + est_specifier = SpecifierSet(est_specifier_tag, prereleases=prereleases) except InvalidSpecifier: msg_version = ( f"wrong format for python_version tag, " @@ -303,6 +312,9 @@ def _check_python_version(obj, package=None, msg=None, severity="error"): f" but system python version is {sys.version}." ) + if "rc" in sys_version: + msg += " This is due to the release candidate status of your system Python." + if package is not None: msg += ( f" This is due to python version requirements of the {package} package." diff --git a/skbase/utils/dependencies/tests/test_check_dependencies.py b/skbase/utils/dependencies/tests/test_check_dependencies.py index 53f1353f..bbb51737 100644 --- a/skbase/utils/dependencies/tests/test_check_dependencies.py +++ b/skbase/utils/dependencies/tests/test_check_dependencies.py @@ -1,9 +1,11 @@ # -*- coding: utf-8 -*- """Tests for _check_soft_dependencies utility.""" +from unittest.mock import patch + import pytest from packaging.requirements import InvalidRequirement -from skbase.utils.dependencies._dependencies import _check_soft_dependencies +from skbase.utils.dependencies import _check_python_version, _check_soft_dependencies def test_check_soft_deps(): @@ -47,3 +49,53 @@ def test_check_soft_deps(): assert _check_soft_dependencies( ("pytest", "!!numpy<~><>0.1.0"), severity="none" ) + + +@patch("skbase.utils.dependencies._dependencies.sys") +@pytest.mark.parametrize( + "mock_release_version, prereleases, expect_exception", + [ + (True, True, False), + (True, False, True), + (False, False, False), + (False, True, False), + ], +) +def test_check_python_version( + mock_sys, mock_release_version, prereleases, expect_exception +): + from skbase.base import BaseObject + + if mock_release_version: + mock_sys.version = "3.8.1rc" + else: + mock_sys.version = "3.8.1" + + class DummyObjectClass(BaseObject): + _tags = { + "python_version": ">=3.7.1", # PEP 440 version specifier, e.g., ">=3.7" + "python_dependencies": None, # PEP 440 dependency strs, e.g., "pandas>=1.0" + "env_marker": None, # PEP 508 environment marker, e.g., "os_name=='posix'" + } + """Define dummy class to test set_tags.""" + + dummy_object_instance = DummyObjectClass() + + try: + _check_python_version(dummy_object_instance, prereleases=prereleases) + except ModuleNotFoundError as exception: + expected_msg = ( + f"{type(dummy_object_instance).__name__} requires python version " + f"to be {dummy_object_instance.get_tags()['python_version']}, " + f"but system python version is {mock_sys.version}. " + "This is due to the release candidate status of your system Python." + ) + + if not expect_exception or exception.msg != expected_msg: + # Throw Error since exception is not expected or has not the correct message + raise AssertionError( + "ModuleNotFoundError should be NOT raised by:", + f"\n\t - mock_release_version: {mock_release_version},", + f"\n\t - prereleases: {prereleases},", + f"\nERROR MESSAGE: {exception.msg}", + ) from exception