From eaff7f307ccabd2c81c1aece9729009a0a097495 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Fri, 15 Apr 2022 14:17:21 +0300 Subject: [PATCH] Avoid RecursionError for circular_dependencies // Resolve #4228 --- platformio/package/manager/_install.py | 8 +++- tests/package/test_manager.py | 53 ++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/platformio/package/manager/_install.py b/platformio/package/manager/_install.py index cc2d1713..c9863a65 100644 --- a/platformio/package/manager/_install.py +++ b/platformio/package/manager/_install.py @@ -76,6 +76,9 @@ class PackageManagerInstallMixin(object): pkg = None if pkg: + # avoid RecursionError for circular_dependencies + self._INSTALL_HISTORY[spec] = pkg + self.log.debug( click.style( "{name}@{version} is already installed".format( @@ -112,9 +115,12 @@ class PackageManagerInstallMixin(object): ) self.memcache_reset() + # avoid RecursionError for circular_dependencies + self._INSTALL_HISTORY[spec] = pkg + if not skip_dependencies: self.install_dependencies(pkg) - self._INSTALL_HISTORY[spec] = pkg + return pkg def install_dependencies(self, pkg, print_header=True): diff --git a/tests/package/test_manager.py b/tests/package/test_manager.py index 9b92b0b8..896a7231 100644 --- a/tests/package/test_manager.py +++ b/tests/package/test_manager.py @@ -386,6 +386,59 @@ if action == "preuninstall": (storage_dir / "preuninstall.flag").is_file() +def test_install_circular_dependencies(tmp_path: Path): + storage_dir = tmp_path / "storage" + # Foo + pkg_dir = storage_dir / "foo" + pkg_dir.mkdir(parents=True) + (pkg_dir / "library.json").write_text( + """ +{ + "name": "Foo", + "version": "1.0.0", + "dependencies": { + "Bar": "*" + } +} +""" + ) + # Bar + pkg_dir = storage_dir / "bar" + pkg_dir.mkdir(parents=True) + (pkg_dir / "library.json").write_text( + """ +{ + "name": "Bar", + "version": "1.0.0", + "dependencies": { + "Foo": "*" + } +} +""" + ) + + lm = LibraryPackageManager(str(storage_dir)) + lm.set_log_level(logging.ERROR) + assert len(lm.get_installed()) == 2 + + # root library + pkg_dir = tmp_path / "root" + pkg_dir.mkdir(parents=True) + (pkg_dir / "library.json").write_text( + """ +{ + "name": "Root", + "version": "1.0.0", + "dependencies": { + "Foo": "^1.0.0", + "Bar": "^1.0.0" + } +} +""" + ) + lm.install("file://%s" % str(pkg_dir)) + + def test_get_installed(isolated_pio_core, tmpdir_factory): storage_dir = tmpdir_factory.mktemp("storage") pm = ToolPackageManager(str(storage_dir))