From 72b12e74c7371cbcc862f76cdf66e9cff6fe28f1 Mon Sep 17 00:00:00 2001 From: James Date: Wed, 22 Jul 2020 11:21:43 +0200 Subject: [PATCH] Feature/min conan version (#7360) * min_conan_version proposal * removed wrong test * fix test * made min_conan_version global * required_conan_version is now a range * renamed tests * review --- conans/client/conf/required_version.py | 24 ++++++------- conans/client/loader.py | 13 +++++-- conans/client/tools/version.py | 3 ++ .../conanfile/required_conan_version_test.py | 34 +++++++++++++++++++ .../configuration/required_version_test.py | 13 +++---- 5 files changed, 67 insertions(+), 20 deletions(-) create mode 100644 conans/test/functional/conanfile/required_conan_version_test.py diff --git a/conans/client/conf/required_version.py b/conans/client/conf/required_version.py index bb6b1bf6bfe..85926245fd0 100644 --- a/conans/client/conf/required_version.py +++ b/conans/client/conf/required_version.py @@ -1,9 +1,16 @@ from conans.client.cache.cache import ClientCache -from semver import satisfies, Range +from semver import satisfies from conans import __version__ as client_version from conans.errors import ConanException +def validate_conan_version(required_range): + result = satisfies(client_version, required_range, loose=True) + if not result: + raise ConanException("Current Conan version ({}) does not satisfy " + "the defined one ({}).".format(client_version, required_range)) + + def check_required_conan_version(cache_folder, out): """ Check if the required Conan version in config file matches to the current Conan version @@ -17,14 +24,7 @@ def check_required_conan_version(cache_folder, out): :return: None """ cache = ClientCache(cache_folder, out) - required_version = cache.config.required_conan_version - if required_version: - try: - Range(required_version, False) - except ValueError: - raise ConanException("The required version expression '{}' is not valid." - .format(required_version)) - result = satisfies(client_version, required_version) - if not result: - raise ConanException("The current Conan version ({}) does not match to the required" - " version ({}).".format(client_version, required_version)) + required_range = cache.config.required_conan_version + if required_range: + validate_conan_version(required_range) + diff --git a/conans/client/loader.py b/conans/client/loader.py index ae872cedfdd..71dd1818029 100644 --- a/conans/client/loader.py +++ b/conans/client/loader.py @@ -7,6 +7,7 @@ import yaml +from conans.client.conf.required_version import validate_conan_version from conans.client.generators import registered_generators from conans.client.loader_txt import ConanFileTextLoader from conans.client.tools.files import chdir @@ -202,8 +203,12 @@ def load_conanfile(self, conanfile_path, profile, ref, lock_python_requires=None """ load a conanfile with a full reference, name, version, user and channel are obtained from the reference, not evaluated. Main way to load from the cache """ - conanfile, _ = self.load_basic_module(conanfile_path, lock_python_requires, - ref.user, ref.channel, str(ref)) + try: + conanfile, _ = self.load_basic_module(conanfile_path, lock_python_requires, + ref.user, ref.channel, str(ref)) + except Exception as e: + raise ConanException("%s: Cannot load recipe.\n%s" % (str(ref), str(e))) + conanfile.name = ref.name conanfile.version = str(ref.version) \ if os.environ.get(CONAN_V2_MODE_ENVVAR, False) else ref.version @@ -356,6 +361,10 @@ def _parse_conanfile(conan_file_path): loaded = imp.load_source(module_id, conan_file_path) sys.dont_write_bytecode = False + required_conan_version = getattr(loaded, "required_conan_version", None) + if required_conan_version: + validate_conan_version(required_conan_version) + # These lines are necessary, otherwise local conanfile imports with same name # collide, but no error, and overwrite other packages imports!! added_modules = set(sys.modules).difference(old_modules) diff --git a/conans/client/tools/version.py b/conans/client/tools/version.py index 7822658781b..c6518302c4f 100644 --- a/conans/client/tools/version.py +++ b/conans/client/tools/version.py @@ -19,6 +19,9 @@ def __init__(self, value): except ValueError: raise ConanException("Invalid version '{}'".format(value)) + def __str__(self): + return str(self._semver) + @property def major(self): return str(self._semver.major) diff --git a/conans/test/functional/conanfile/required_conan_version_test.py b/conans/test/functional/conanfile/required_conan_version_test.py new file mode 100644 index 00000000000..1471fda1208 --- /dev/null +++ b/conans/test/functional/conanfile/required_conan_version_test.py @@ -0,0 +1,34 @@ +import textwrap +import unittest + +import mock + +from conans import __version__ +from conans.test.utils.tools import TestClient + + +class RequiredConanVersionTest(unittest.TestCase): + + def required_conan_version_test(self): + client = TestClient() + conanfile = textwrap.dedent(""" + from conans import ConanFile + + required_conan_version = ">=100.0" + + class Lib(ConanFile): + pass + """) + client.save({"conanfile.py": conanfile}) + client.run("export . pkg/1.0@", assert_error=True) + self.assertIn("Current Conan version (%s) does not satisfy the defined one (>=100.0)" + % __version__, client.out) + client.run("inspect . ", assert_error=True) + self.assertIn("Current Conan version (%s) does not satisfy the defined one (>=100.0)" + % __version__, client.out) + with mock.patch("conans.client.conf.required_version.client_version", "101.0"): + client.run("export . pkg/1.0@") + + client.run("install pkg/1.0@", assert_error=True) + self.assertIn("Current Conan version (%s) does not satisfy the defined one (>=100.0)" + % __version__, client.out) diff --git a/conans/test/functional/configuration/required_version_test.py b/conans/test/functional/configuration/required_version_test.py index a00189e9a6f..ebd1ab0f812 100644 --- a/conans/test/functional/configuration/required_version_test.py +++ b/conans/test/functional/configuration/required_version_test.py @@ -1,7 +1,9 @@ import unittest import mock -from conans.test.utils.tools import TestClient + +from conans import __version__ from conans.errors import ConanException +from conans.test.utils.tools import TestClient class RequiredVersionTest(unittest.TestCase): @@ -13,9 +15,8 @@ def test_wrong_version(self): client.run("config set general.required_conan_version={}".format(required_version)) with self.assertRaises(ConanException) as error: client.run("help") - self.assertIn("The current Conan version ({}) " - "does not match to the required version ({})." - .format("1.26.0", required_version), str(error.exception)) + self.assertIn("Current Conan version (1.26.0) does not satisfy the defined " + "one ({})".format(required_version), str(error.exception)) @mock.patch("conans.client.conf.required_version.client_version", "1.22.0") def test_exact_version(self): @@ -44,5 +45,5 @@ def test_bad_format(self): client.run("config set general.required_conan_version={}".format(required_version)) with self.assertRaises(ConanException) as error: client.run("help", assert_error=True) - self.assertIn("The required version expression '{}' is not valid.".format(required_version), - str(error.exception)) + self.assertIn("Current Conan version ({}) does not satisfy the defined one ({})" + .format(__version__, required_version), str(error.exception))