From 643a9c84fe6dc0cb2f9fcbfc9dc5bd2e789c690e Mon Sep 17 00:00:00 2001 From: James Date: Thu, 3 Jan 2019 20:29:16 +0100 Subject: [PATCH] import conanfile just once (#4095) * import conanfile just once * reset cache * fixing tests * review --- conans/client/loader.py | 13 ++++--- conans/test/optimize_conanfile_load_test.py | 36 +++++++++++++++++++ .../unittests/model/transitive_reqs_test.py | 3 +- .../unittests/model/version_ranges_test.py | 1 + 4 files changed, 48 insertions(+), 5 deletions(-) create mode 100644 conans/test/optimize_conanfile_load_test.py diff --git a/conans/client/loader.py b/conans/client/loader.py index 79611675ac3..7f9bbf4b315 100644 --- a/conans/client/loader.py +++ b/conans/client/loader.py @@ -33,12 +33,17 @@ def __init__(self, runner, output, python_requires): self._runner = runner self._output = output self._python_requires = python_requires - sys.modules["conans"].python_requires = self._python_requires + sys.modules["conans"].python_requires = python_requires + self.cached_conanfiles = {} def load_class(self, conanfile_path): - self._python_requires.valid = True - _, conanfile = parse_conanfile(conanfile_path, self._python_requires) - self._python_requires.valid = False + try: + return self.cached_conanfiles[conanfile_path] + except KeyError: + self._python_requires.valid = True + _, conanfile = parse_conanfile(conanfile_path, self._python_requires) + self._python_requires.valid = False + self.cached_conanfiles[conanfile_path] = conanfile return conanfile def load_export(self, conanfile_path, name, version, user, channel): diff --git a/conans/test/optimize_conanfile_load_test.py b/conans/test/optimize_conanfile_load_test.py new file mode 100644 index 00000000000..89bb0c3c872 --- /dev/null +++ b/conans/test/optimize_conanfile_load_test.py @@ -0,0 +1,36 @@ +import unittest + +from conans.test.utils.tools import TestClient + + +class OptimizeConanFileLoadTest(unittest.TestCase): + + def test_multiple_load(self): + """ when a conanfile is used more than once in a dependency graph, the python file + should be read and interpreted just once, then instance 2 different ConanFile + objects. The module global value "mycounter" is global to all instances, this + should be discouraged to use as if it was an instance value. + In this test there are 2 nodes "Build/0.1" as it is a build-requires of both the + conanfile.py and the test_package/conanfile.py + """ + client = TestClient() + conanfile = """from conans import ConanFile +mycounter = 0 +class Pkg(ConanFile): + mycounter2 = 0 + def configure(self): + global mycounter + mycounter += 1 + self.mycounter2 += 1 + self.output.info("MyCounter1 %s, MyCounter2 %s" % (mycounter, self.mycounter2)) +""" + client.save({"conanfile.py": conanfile}) + + client.run("create . Build/0.1@user/testing") + + client.save({"conanfile.py": conanfile, + "test_package/conanfile.py": conanfile + " def test(self): pass", + "myprofile": "[build_requires]\nBuild/0.1@user/testing"}) + + client.run("create . Pkg/0.1@user/testing -pr=myprofile") + self.assertIn("Build/0.1@user/testing: MyCounter1 2, MyCounter2 1", client.out) diff --git a/conans/test/unittests/model/transitive_reqs_test.py b/conans/test/unittests/model/transitive_reqs_test.py index 46aac37615f..1990afb9cc9 100644 --- a/conans/test/unittests/model/transitive_reqs_test.py +++ b/conans/test/unittests/model/transitive_reqs_test.py @@ -115,6 +115,7 @@ def setUp(self): MockRequireResolver(), None, None) def root(self, content): + self.loader.cached_conanfiles = {} processed_profile = test_processed_profile() root_conan = self.retriever.root(content, processed_profile) deps_graph = self.builder.load_graph(root_conan, False, False, None, @@ -430,7 +431,7 @@ class ChatConan(ConanFile): self.retriever.conan(bye_ref, bye_content2) with self.assertRaisesRegexp(ConanException, "Conflict in Bye/0.2@user/testing"): - _ = self.root(chat_content) + self.root(chat_content) def test_diamond_conflict_solved(self): chat_content = """ diff --git a/conans/test/unittests/model/version_ranges_test.py b/conans/test/unittests/model/version_ranges_test.py index 8500e45613d..6790896b78f 100644 --- a/conans/test/unittests/model/version_ranges_test.py +++ b/conans/test/unittests/model/version_ranges_test.py @@ -182,6 +182,7 @@ class SayConan(ConanFile): self.retriever.conan(say_ref, say_content) def root(self, content, update=False): + self.loader.cached_conanfiles = {} processed_profile = test_processed_profile() root_conan = self.retriever.root(content, processed_profile) deps_graph = self.builder.load_graph(root_conan, update, update, None, processed_profile)