Skip to content

Commit

Permalink
fix: add recursion checks to prevent dependency loops (#10)
Browse files Browse the repository at this point in the history
* fix: add recursion checks to prevent dependency loops

- the dependency resolver is recursive which allow for detecting
transitive dependencies
- the recursive dependency search now keeps an accumulator of all
seen dependencies, to prevent duplicate entries in case multiple
packages depend on the same local package
- a check is also added to prevent a package from listing itself
as a dependency, which would cause an infinite recursive loop

* Update src/monas/project.py

---------

Co-authored-by: Frost Ming <mianghong@gmail.com>
  • Loading branch information
AGiantSquid and frostming committed Oct 16, 2023
1 parent b15052e commit 1b9b0f6
Showing 1 changed file with 22 additions and 7 deletions.
29 changes: 22 additions & 7 deletions src/monas/project.py
Expand Up @@ -3,7 +3,7 @@
import textwrap
from pathlib import Path
from shlex import join as sh_join
from typing import Type, cast
from typing import Optional, Type, cast

import tomlkit
from packaging.utils import canonicalize_name
Expand Down Expand Up @@ -155,18 +155,33 @@ def remove_dependency(self, dependency: str) -> None:

def install(self) -> None:
"""Bootstrap the package and link depending packages in the monorepo"""
local_dependencies = [*self.get_local_dependencies(), self]
local_dependencies = self.get_local_dependencies([self])
requirements = [
sh_join(["-e", pkg.path.as_posix()]) for pkg in local_dependencies
]
pip_install(self.path / ".venv", requirements)

def get_local_dependencies(self) -> list[PyPackage]:
"""Return list of local dependencies."""
def get_local_dependencies(
self,
local_dependencies: list[PyPackage] | None = None,
) -> list[PyPackage]:
"""Return list of local dependencies.
Args:
local_dependencies: Accumulated list of local depencies to install
"""
if not local_dependencies:
local_dependencies = []
dependency_names = self.metadata.get_dependency_names()
local_dependencies = []
local_packages = list(self.config.iter_packages())
for pkg in local_packages:
if pkg.canonical_name in dependency_names:
local_dependencies += [*pkg.get_local_dependencies(), pkg]
pkg_name = pkg.canonical_name
if pkg_name not in dependency_names:
continue
if pkg_name == self.canonical_name:
raise ValueError(f'{self.name} cannot have a dependency on itself')
if pkg_name in [ld.canonical_name for ld in local_dependencies]:
continue
local_dependencies = [pkg, *local_dependencies]
local_dependencies = pkg.get_local_dependencies(local_dependencies)
return local_dependencies

0 comments on commit 1b9b0f6

Please sign in to comment.