From 8c9cf2acc6bbdf5d9ce2e6786e515af13731d768 Mon Sep 17 00:00:00 2001 From: "Javier G. Sogo" Date: Thu, 11 Mar 2021 00:02:47 +0100 Subject: [PATCH] preserve the explicit value None for attributes with a different default (#8622) --- conans/model/scm.py | 7 +- .../test/functional/scm/test_git_shallow.py | 68 +++++++++++++++++-- conans/test/functional/scm/test_verify_ssl.py | 63 +++++++++++++++-- 3 files changed, 122 insertions(+), 16 deletions(-) diff --git a/conans/model/scm.py b/conans/model/scm.py index 23c8125da24..32dc4432217 100644 --- a/conans/model/scm.py +++ b/conans/model/scm.py @@ -38,7 +38,7 @@ def __init__(self, conanfile): data = getattr(conanfile, "scm") self.type = _get_dict_value(data, "type", string_types) self.url = _get_dict_value(data, "url", string_types) - self.revision = _get_dict_value(data, "revision", string_types + (int, ), + self.revision = _get_dict_value(data, "revision", string_types + (int,), disallowed_type=bool) # bool is subclass of integer self.verify_ssl = _get_dict_value(data, "verify_ssl", bool, SCMData.VERIFY_SSL_DEFAULT) self.username = _get_dict_value(data, "username", string_types) @@ -65,11 +65,12 @@ def as_dict(self): d = {"url": self.url, "revision": self.revision, "username": self.username, "password": self.password, "type": self.type, "subfolder": self.subfolder, "submodule": self.submodule} + d = {k: v for k, v in d.items() if v is not None} + # Preserve the value 'None' for those entries with not falsy default. if self.shallow != self.SHALLOW_DEFAULT: d.update({"shallow": self.shallow}) if self.verify_ssl != self.VERIFY_SSL_DEFAULT: d.update({"verify_ssl": self.verify_ssl}) - d = {k: v for k, v in d.items() if v is not None} return d def __repr__(self): @@ -78,6 +79,8 @@ def __repr__(self): def _kv_to_string(key, value): if isinstance(value, bool): return '"{}": {}'.format(key, value) + elif value is None: + return '"{}": None'.format(key) else: value_str = str(value).replace('"', r'\"') return '"{}": "{}"'.format(key, value_str) diff --git a/conans/test/functional/scm/test_git_shallow.py b/conans/test/functional/scm/test_git_shallow.py index 4ba306f4a91..a6937c83044 100644 --- a/conans/test/functional/scm/test_git_shallow.py +++ b/conans/test/functional/scm/test_git_shallow.py @@ -1,5 +1,5 @@ # coding=utf-8 - +import os import textwrap import unittest @@ -8,13 +8,32 @@ from parameterized.parameterized import parameterized_class from conans.model.ref import ConanFileReference -from conans.test.utils.tools import TestClient +from conans.paths import DATA_YML from conans.test.utils.scm import create_local_git_repo +from conans.test.utils.tools import TestClient from conans.util.files import load +@pytest.mark.parametrize("scm_to_conandata", [True, False]) +def test_shallow_none_string(scm_to_conandata): + client = TestClient() + client.run("config set general.scm_to_conandata={}".format('1' if scm_to_conandata else '0')) + client.save({'conanfile.py': textwrap.dedent(""" + from conans import ConanFile + + class Recipe(ConanFile): + scm = {"type": "git", "url": "https://github.com/repo/library.git", + "revision": "123456", "shallow": 'None' } + """)}) + + client.run('export . name/version@', assert_error=True) + assert "ERROR: SCM value for 'shallow' must be of type 'bool' (found 'str')" in str(client.out) + + @pytest.mark.tool_git -@parameterized_class([{"shallow": True}, {"shallow": False}, {"shallow": None}, {"shallow": "None"}]) +@parameterized_class([{"shallow": True}, {"shallow": False}, + {"shallow": None}, # No value written in the recipe + {"shallow": 'None'}]) # Explicit 'None' written in the recipe class GitShallowTestCase(unittest.TestCase): conanfile = textwrap.dedent(""" from conans import ConanFile @@ -41,7 +60,20 @@ def _shallow_attrib_str(self): shallow_attrib_str = '"shallow": {}'.format(self.shallow) return shallow_attrib_str - def test_export(self): + def _check_info_values(self, client): + client.run("inspect {} -a scm".format(self.ref)) # Check we get a loadable conanfile.py + if self.shallow in [None]: + self.assertNotIn('shallow', str(client.out)) + elif self.shallow in [True]: # This is the default value + not_appears = 'shallow' not in str(client.out) + value_explicit = 'shallow: True' in str(client.out) + self.assertTrue(not_appears or value_explicit) + elif self.shallow in ['None']: + self.assertIn('shallow: None', str(client.out)) + else: + self.assertIn('shallow: False', str(client.out)) + + def test_export_scm_substituted(self): # Check the shallow value is substituted with the proper value client = TestClient() files = {'conanfile.py': self.conanfile.format(shallow_attrib=self._shallow_attrib_str(), @@ -52,12 +84,34 @@ def test_export(self): client.run("export . {}".format(self.ref)) content = load(client.cache.package_layout(self.ref).conanfile()) - if self.shallow in [None, True, "None"]: + if self.shallow in [None, True]: self.assertNotIn("shallow", content) + elif self.shallow in ['None']: + self.assertIn('"shallow": None', content) else: self.assertIn('"shallow": False', content) - client.run("inspect {} -a scm".format(self.ref)) # Check we get a loadable conanfile.py + self._check_info_values(client) + + def test_export_scm_to_conandata(self): + # Check the shallow value is stored and propagated with the proper value + client = TestClient() + client.run("config set general.scm_to_conandata=1") + files = {'conanfile.py': self.conanfile.format(shallow_attrib=self._shallow_attrib_str(), + url='auto', rev='auto')} + url, _ = create_local_git_repo(files=files) + client.run_command('git clone "{}" .'.format(url)) + + client.run("export . {}".format(self.ref)) + content = load(os.path.join(client.cache.package_layout(self.ref).export(), DATA_YML)) + if self.shallow in [None, True]: + self.assertNotIn('shallow', content) + elif self.shallow in ['None']: + self.assertIn('shallow: null', content) + else: + self.assertIn('shallow: false', content) + + self._check_info_values(client) @parameterized.expand([("c6cc15fa2f4b576bd", False), ("0.22.1", True)]) def test_remote_build(self, revision, shallow_works): @@ -70,7 +124,7 @@ def test_remote_build(self, revision, shallow_works): client.run("create . {}".format(self.ref)) - if self.shallow in [None, True, "None"] and shallow_works: + if self.shallow in [None, True] and shallow_works: self.assertIn(">>> describe-fails", client.out) else: self.assertIn(">>> tags: 0.22.1", client.out) diff --git a/conans/test/functional/scm/test_verify_ssl.py b/conans/test/functional/scm/test_verify_ssl.py index 034ed5bc36a..6ee79352b63 100644 --- a/conans/test/functional/scm/test_verify_ssl.py +++ b/conans/test/functional/scm/test_verify_ssl.py @@ -1,5 +1,5 @@ # coding=utf-8 - +import os import textwrap import unittest @@ -7,14 +7,33 @@ from parameterized.parameterized import parameterized_class from conans.model.ref import ConanFileReference -from conans.test.utils.tools import TestClient +from conans.paths import DATA_YML from conans.test.utils.scm import create_local_git_repo +from conans.test.utils.tools import TestClient from conans.util.files import load +@pytest.mark.parametrize("scm_to_conandata", [True, False]) +def test_verify_ssl_none_string(scm_to_conandata): + client = TestClient() + client.run("config set general.scm_to_conandata={}".format('1' if scm_to_conandata else '0')) + client.save({'conanfile.py': textwrap.dedent(""" + from conans import ConanFile + + class Recipe(ConanFile): + scm = {"type": "git", "url": "https://github.com/repo/library.git", + "revision": "123456", "verify_ssl": 'None' } + """)}) + + client.run('export . name/version@', assert_error=True) + assert "ERROR: SCM value for 'verify_ssl' must be of type " \ + "'bool' (found 'str')" in str(client.out) + + @pytest.mark.tool_git @parameterized_class([{"verify_ssl": True}, {"verify_ssl": False}, - {"verify_ssl": None},{"verify_ssl": "None"}, ]) + {"verify_ssl": None}, # No value written in the recipe + {"verify_ssl": 'None'}]) # Explicit 'None' written in the recipe class GitVerifySSLTestCase(unittest.TestCase): conanfile = textwrap.dedent(""" from conans import ConanFile @@ -37,13 +56,43 @@ def setUp(self): url, _ = create_local_git_repo(files=files, commits=4, tags=['v0', ]) self.client.run_command('git clone "{}" .'.format(url)) - def test_export(self): - # Check the shallow value is substituted with the proper value + def _check_info_values(self, client): + client.run("inspect {} -a scm".format(self.ref)) # Check we get a loadable conanfile.py + if self.verify_ssl in [None]: + self.assertNotIn('verify_ssl', str(client.out)) + elif self.verify_ssl in [True]: # This is the default value + not_appears = 'verify_ssl' not in str(client.out) + value_explicit = 'verify_ssl: True' in str(client.out) + self.assertTrue(not_appears or value_explicit) + elif self.verify_ssl in ['None']: + self.assertIn('verify_ssl: None', str(client.out)) + else: + self.assertIn('verify_ssl: False', str(client.out)) + + def test_export_scm_substituted(self): + # Check the verify_ssl value is substituted with the proper value + self.client.run("config set general.scm_to_conandata=0") self.client.run("export . {}".format(self.ref)) content = load(self.client.cache.package_layout(self.ref).conanfile()) - if self.verify_ssl in [None, True, "None"]: + if self.verify_ssl in [None, True]: self.assertNotIn("verify_ssl", content) + elif self.verify_ssl in ['None']: + self.assertIn('"verify_ssl": None', content) else: self.assertIn('"verify_ssl": False', content) - self.client.run("inspect {} -a scm".format(self.ref)) # Check we get a loadable conanfile.py + self._check_info_values(self.client) + + def test_export_scm_to_conandata(self): + # Check the verify_ssl value is stored and propagated with the proper value + self.client.run("config set general.scm_to_conandata=1") + self.client.run("export . {}".format(self.ref)) + content = load(os.path.join(self.client.cache.package_layout(self.ref).export(), DATA_YML)) + if self.verify_ssl in [None, True]: + self.assertNotIn('verify_ssl', content) + elif self.verify_ssl in ['None']: + self.assertIn('verify_ssl: null', content) + else: + self.assertIn('verify_ssl: false', content) + + self._check_info_values(self.client)