diff --git a/components/polylith/configuration/core.py b/components/polylith/configuration/core.py index 0e6d95e..1f79c86 100644 --- a/components/polylith/configuration/core.py +++ b/components/polylith/configuration/core.py @@ -1,25 +1,11 @@ -from functools import lru_cache from pathlib import Path from typing import List, Union -import tomlkit from polylith import repo -@lru_cache -def _load_workspace_config(path: Path) -> tomlkit.TOMLDocument: - fullpath = path / repo.workspace_file - - if not fullpath.exists(): - fullpath = path / repo.default_toml - - content = fullpath.read_text() - - return tomlkit.loads(content) - - def get_namespace_from_config(path: Path) -> str: - toml: dict = _load_workspace_config(path) + toml: dict = repo.load_workspace_config(path) return toml["tool"]["polylith"]["namespace"] @@ -30,7 +16,7 @@ def get_git_tag_pattern(toml: dict) -> str: def get_tag_pattern_from_config(path: Path, key: Union[str, None]) -> Union[str, None]: - toml: dict = _load_workspace_config(path) + toml: dict = repo.load_workspace_config(path) patterns = toml["tool"]["polylith"].get("tag", {}).get("patterns") @@ -41,7 +27,7 @@ def get_tag_pattern_from_config(path: Path, key: Union[str, None]) -> Union[str, def get_tag_sort_options_from_config(path: Path) -> List[str]: - toml: dict = _load_workspace_config(path) + toml: dict = repo.load_workspace_config(path) options = toml["tool"]["polylith"].get("tag", {}).get("sorting") # Default sorting option @@ -51,21 +37,21 @@ def get_tag_sort_options_from_config(path: Path) -> List[str]: def is_test_generation_enabled(path: Path) -> bool: - toml: dict = _load_workspace_config(path) + toml: dict = repo.load_workspace_config(path) enabled = toml["tool"]["polylith"]["test"]["enabled"] return bool(enabled) def is_readme_generation_enabled(path: Path) -> bool: - toml: dict = _load_workspace_config(path) + toml: dict = repo.load_workspace_config(path) enabled = toml["tool"]["polylith"].get("resources", {}).get("brick_docs_enabled") return bool(enabled) def get_theme_from_config(path: Path) -> str: - toml: dict = _load_workspace_config(path) + toml: dict = repo.load_workspace_config(path) return toml["tool"]["polylith"]["structure"].get("theme") or "tdd" diff --git a/components/polylith/repo/__init__.py b/components/polylith/repo/__init__.py index c05041e..99195cc 100644 --- a/components/polylith/repo/__init__.py +++ b/components/polylith/repo/__init__.py @@ -9,6 +9,7 @@ is_pdm, is_pep_621_ready, is_poetry, + load_workspace_config, projects_dir, readme_file, workspace_file, @@ -26,6 +27,7 @@ "is_pdm", "is_pep_621_ready", "is_poetry", + "load_workspace_config", "projects_dir", "readme_file", "workspace_file", diff --git a/components/polylith/repo/repo.py b/components/polylith/repo/repo.py index 5752143..e43b44e 100644 --- a/components/polylith/repo/repo.py +++ b/components/polylith/repo/repo.py @@ -1,6 +1,9 @@ +from functools import lru_cache from pathlib import Path from typing import Union +import tomlkit + workspace_file = "workspace.toml" root_file = ".git" default_toml = "pyproject.toml" @@ -12,6 +15,38 @@ development_dir = "development" +def load_content(fullpath: Path) -> tomlkit.TOMLDocument: + content = fullpath.read_text() + + return tomlkit.loads(content) + + +@lru_cache +def load_root_project_config(path: Path) -> tomlkit.TOMLDocument: + fullpath = path / default_toml + + return load_content(fullpath) + + +def has_workspace_config(data: tomlkit.TOMLDocument) -> bool: + ns = data.get("tool", {}).get("polylith", {}).get("namespace") + + return True if ns else False + + +@lru_cache +def load_workspace_config(path: Path) -> tomlkit.TOMLDocument: + fullpath = path / workspace_file + + if fullpath.exists(): + content = load_content(fullpath) + + if has_workspace_config(content): + return content + + return load_root_project_config(path) + + def is_drive_root(cwd: Path) -> bool: return cwd == Path(cwd.root) or cwd == cwd.parent @@ -22,6 +57,12 @@ def is_repo_root(cwd: Path) -> bool: return fullpath.exists() +def is_python_workspace_root(path: Path) -> bool: + data = load_root_project_config(path) + + return has_workspace_config(data) + + def find_upwards(cwd: Path, name: str) -> Union[Path, None]: if is_drive_root(cwd): return None @@ -29,7 +70,10 @@ def find_upwards(cwd: Path, name: str) -> Union[Path, None]: fullpath = cwd / name if fullpath.exists(): - return fullpath + if name == workspace_file: + return fullpath + + return fullpath if is_python_workspace_root(cwd) else None if is_repo_root(cwd): return None @@ -45,9 +89,13 @@ def find_upwards_dir(cwd: Path, name: str) -> Union[Path, None]: def find_workspace_root(cwd: Path) -> Union[Path, None]: workspace_root = find_upwards_dir(cwd, workspace_file) + if workspace_root: return workspace_root - return find_upwards_dir(cwd, root_file) + + repo_root = find_upwards_dir(cwd, root_file) + + return repo_root or find_upwards_dir(cwd, default_toml) def get_workspace_root(cwd: Path) -> Path: @@ -55,7 +103,7 @@ def get_workspace_root(cwd: Path) -> Path: if not root: raise ValueError( - "Didn't find the workspace root. Expected to find a workspace.toml or .git file." + "Didn't find the workspace root. Expected to find a workspace.toml or pyproject.toml with Workspace config." ) return root diff --git a/projects/hatch_polylith_bricks/pyproject.toml b/projects/hatch_polylith_bricks/pyproject.toml index d2e1271..e66166d 100644 --- a/projects/hatch_polylith_bricks/pyproject.toml +++ b/projects/hatch_polylith_bricks/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "hatch-polylith-bricks" -version = "1.2.8" +version = "1.2.9" description = "Hatch build hook plugin for Polylith" authors = ['David Vujic'] homepage = "https://davidvujic.github.io/python-polylith-docs/" diff --git a/projects/pdm_polylith_bricks/pyproject.toml b/projects/pdm_polylith_bricks/pyproject.toml index 029151e..be910e7 100644 --- a/projects/pdm_polylith_bricks/pyproject.toml +++ b/projects/pdm_polylith_bricks/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pdm-polylith-bricks" -version = "1.0.9" +version = "1.1.0" description = "a PDM build hook for Polylith" authors = ["David Vujic"] homepage = "https://davidvujic.github.io/python-polylith-docs/" diff --git a/projects/pdm_polylith_workspace/pyproject.toml b/projects/pdm_polylith_workspace/pyproject.toml index 1742342..588f76e 100644 --- a/projects/pdm_polylith_workspace/pyproject.toml +++ b/projects/pdm_polylith_workspace/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pdm-polylith-workspace" -version = "1.0.9" +version = "1.1.0" description = "a PDM build hook for a Polylith workspace" homepage = "https://davidvujic.github.io/python-polylith-docs/" repository = "https://github.com/davidvujic/python-polylith" diff --git a/projects/poetry_polylith_plugin/pyproject.toml b/projects/poetry_polylith_plugin/pyproject.toml index c43100e..0cccda4 100644 --- a/projects/poetry_polylith_plugin/pyproject.toml +++ b/projects/poetry_polylith_plugin/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "poetry-polylith-plugin" -version = "1.33.0" +version = "1.34.0" description = "A Poetry plugin that adds tooling support for the Polylith Architecture" authors = ["David Vujic"] homepage = "https://davidvujic.github.io/python-polylith-docs/" diff --git a/projects/polylith_cli/pyproject.toml b/projects/polylith_cli/pyproject.toml index bfdf967..d89ba24 100644 --- a/projects/polylith_cli/pyproject.toml +++ b/projects/polylith_cli/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "polylith-cli" -version = "1.22.0" +version = "1.23.0" description = "Python tooling support for the Polylith Architecture" authors = ['David Vujic'] homepage = "https://davidvujic.github.io/python-polylith-docs/" diff --git a/test/components/polylith/configuration/test_core.py b/test/components/polylith/configuration/test_core.py index 05295ca..842ac84 100644 --- a/test/components/polylith/configuration/test_core.py +++ b/test/components/polylith/configuration/test_core.py @@ -41,9 +41,9 @@ def patch(theme: str, tag_sorting: Optional[List[str]] = None): config = config_template.format( theme=theme, tag_sorting=_tag_sorting(tag_sorting) ) - name = "_load_workspace_config" + name = "load_workspace_config" - monkeypatch.setattr(core, name, lambda *args: tomlkit.loads(config)) + monkeypatch.setattr(core.repo, name, lambda *args: tomlkit.loads(config)) return patch diff --git a/test/components/polylith/libs/test_report.py b/test/components/polylith/libs/test_report.py index 737dc70..5c72c7d 100644 --- a/test/components/polylith/libs/test_report.py +++ b/test/components/polylith/libs/test_report.py @@ -1,5 +1,15 @@ +import pytest from polylith.libs import report +third_party_libs = { + "cleo", + "mypy-extensions", + "poetry", + "tomlkit", + "requests", + "rich", +} + def test_calculate_diff_reports_no_diff(): brick_imports = { @@ -11,13 +21,6 @@ def test_calculate_diff_reports_no_diff(): }, } - third_party_libs = { - "tomlkit", - "cleo", - "requests", - "rich", - } - res = report.calculate_diff(brick_imports, third_party_libs) assert len(res) == 0 @@ -35,67 +38,33 @@ def test_calculate_diff_should_report_missing_dependency(): }, } - third_party_libs = { - "tomlkit", - "poetry", - "mypy-extensions", - "rich", - } - res = report.calculate_diff(brick_imports, third_party_libs) assert res == {expected_missing} -def test_calculate_diff_should_identify_close_match(): +@pytest.mark.parametrize( + "imports, is_strict", + [ + ({"aws_lambda_powertools", "PIL", "pyyoutube"}, False), + ({"typing_extensions"}, True), + ], +) +def test_calculate_diff_should_identify_close_match(imports: set, is_strict: bool): brick_imports = { - "bases": {"my_base": {"poetry"}}, - "components": { - "one": {"tomlkit"}, - "two": {"tomlkit", "aws_lambda_powertools", "rich"}, - "three": {"rich", "pyyoutube"}, - }, + "bases": {"thebase": {"typer"}}, + "components": {"one": imports}, } - third_party_libs = { - "tomlkit", - "python-youtube", - "poetry", + libs = { "aws-lambda-powertools", - "rich", - } - - res = report.calculate_diff(brick_imports, third_party_libs) - - assert len(res) == 0 - - -def test_calculate_diff_should_identify_close_match_case_insensitive(): - brick_imports = { - "bases": {"my_base": {}}, - "components": { - "one": {"PIL"}, - }, - } - - third_party_libs = {"pillow"} - - res = report.calculate_diff(brick_imports, third_party_libs) - - assert len(res) == 0 - - -def test_calculate_diff_strict_should_identify_close_match_for_dash_and_low_dash(): - brick_imports = { - "bases": {"thebase": {"typer"}}, - "components": { - "one": {"typing_extensions"}, - }, + "pillow", + "python-youtube", + "typer", + "typing-extensions", } - third_party_libs = {"typer", "typing-extensions"} - - res = report.calculate_diff(brick_imports, third_party_libs, True) + res = report.calculate_diff(brick_imports, libs, is_strict) assert len(res) == 0