polylith-cli 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. polylith_cli-0.1.0/PKG-INFO +88 -0
  2. polylith_cli-0.1.0/README.md +66 -0
  3. polylith_cli-0.1.0/polylith_cli/polylith/alias/__init__.py +2 -0
  4. polylith_cli-0.1.0/polylith_cli/polylith/alias/core.py +23 -0
  5. polylith_cli-0.1.0/polylith_cli/polylith/bricks/__init__.py +3 -0
  6. polylith_cli-0.1.0/polylith_cli/polylith/bricks/base.py +15 -0
  7. polylith_cli-0.1.0/polylith_cli/polylith/bricks/brick.py +19 -0
  8. polylith_cli-0.1.0/polylith_cli/polylith/bricks/component.py +27 -0
  9. polylith_cli-0.1.0/polylith_cli/polylith/check/__init__.py +2 -0
  10. polylith_cli-0.1.0/polylith_cli/polylith/check/collect.py +30 -0
  11. polylith_cli-0.1.0/polylith_cli/polylith/check/grouping.py +36 -0
  12. polylith_cli-0.1.0/polylith_cli/polylith/check/report.py +47 -0
  13. polylith_cli-0.1.0/polylith_cli/polylith/cli/__init__.py +2 -0
  14. polylith_cli-0.1.0/polylith_cli/polylith/cli/__main__.py +2 -0
  15. polylith_cli-0.1.0/polylith_cli/polylith/cli/core.py +58 -0
  16. polylith_cli-0.1.0/polylith_cli/polylith/cli/create.py +42 -0
  17. polylith_cli-0.1.0/polylith_cli/polylith/cli/options.py +17 -0
  18. polylith_cli-0.1.0/polylith_cli/polylith/commands/__init__.py +2 -0
  19. polylith_cli-0.1.0/polylith_cli/polylith/commands/check.py +26 -0
  20. polylith_cli-0.1.0/polylith_cli/polylith/commands/create.py +13 -0
  21. polylith_cli-0.1.0/polylith_cli/polylith/commands/diff.py +30 -0
  22. polylith_cli-0.1.0/polylith_cli/polylith/commands/info.py +16 -0
  23. polylith_cli-0.1.0/polylith_cli/polylith/commands/libs.py +16 -0
  24. polylith_cli-0.1.0/polylith_cli/polylith/commands/sync.py +17 -0
  25. polylith_cli-0.1.0/polylith_cli/polylith/development/__init__.py +2 -0
  26. polylith_cli-0.1.0/polylith_cli/polylith/development/development.py +6 -0
  27. polylith_cli-0.1.0/polylith_cli/polylith/diff/__init__.py +2 -0
  28. polylith_cli-0.1.0/polylith_cli/polylith/diff/collect.py +45 -0
  29. polylith_cli-0.1.0/polylith_cli/polylith/diff/report.py +50 -0
  30. polylith_cli-0.1.0/polylith_cli/polylith/dirs/__init__.py +2 -0
  31. polylith_cli-0.1.0/polylith_cli/polylith/dirs/dirs.py +10 -0
  32. polylith_cli-0.1.0/polylith_cli/polylith/distributions/__init__.py +2 -0
  33. polylith_cli-0.1.0/polylith_cli/polylith/distributions/core.py +31 -0
  34. polylith_cli-0.1.0/polylith_cli/polylith/files/__init__.py +2 -0
  35. polylith_cli-0.1.0/polylith_cli/polylith/files/files.py +9 -0
  36. polylith_cli-0.1.0/polylith_cli/polylith/imports/__init__.py +2 -0
  37. polylith_cli-0.1.0/polylith_cli/polylith/imports/parser.py +67 -0
  38. polylith_cli-0.1.0/polylith_cli/polylith/info/__init__.py +3 -0
  39. polylith_cli-0.1.0/polylith_cli/polylith/info/collect.py +31 -0
  40. polylith_cli-0.1.0/polylith_cli/polylith/info/report.py +69 -0
  41. polylith_cli-0.1.0/polylith_cli/polylith/interface/__init__.py +2 -0
  42. polylith_cli-0.1.0/polylith_cli/polylith/interface/interfaces.py +19 -0
  43. polylith_cli-0.1.0/polylith_cli/polylith/libs/__init__.py +3 -0
  44. polylith_cli-0.1.0/polylith_cli/polylith/libs/grouping.py +35 -0
  45. polylith_cli-0.1.0/polylith_cli/polylith/libs/report.py +80 -0
  46. polylith_cli-0.1.0/polylith_cli/polylith/libs/stdlib.py +271 -0
  47. polylith_cli-0.1.0/polylith_cli/polylith/project/__init__.py +5 -0
  48. polylith_cli-0.1.0/polylith_cli/polylith/project/create.py +18 -0
  49. polylith_cli-0.1.0/polylith_cli/polylith/project/get.py +57 -0
  50. polylith_cli-0.1.0/polylith_cli/polylith/project/parser.py +15 -0
  51. polylith_cli-0.1.0/polylith_cli/polylith/project/templates.py +37 -0
  52. polylith_cli-0.1.0/polylith_cli/polylith/readme/__init__.py +2 -0
  53. polylith_cli-0.1.0/polylith_cli/polylith/readme/readme.py +21 -0
  54. polylith_cli-0.1.0/polylith_cli/polylith/repo/__init__.py +3 -0
  55. polylith_cli-0.1.0/polylith_cli/polylith/repo/get.py +23 -0
  56. polylith_cli-0.1.0/polylith_cli/polylith/repo/repo.py +72 -0
  57. polylith_cli-0.1.0/polylith_cli/polylith/reporting/__init__.py +2 -0
  58. polylith_cli-0.1.0/polylith_cli/polylith/reporting/theme.py +10 -0
  59. polylith_cli-0.1.0/polylith_cli/polylith/sync/__init__.py +4 -0
  60. polylith_cli-0.1.0/polylith_cli/polylith/sync/collect.py +27 -0
  61. polylith_cli-0.1.0/polylith_cli/polylith/sync/report.py +24 -0
  62. polylith_cli-0.1.0/polylith_cli/polylith/sync/update.py +66 -0
  63. polylith_cli-0.1.0/polylith_cli/polylith/test/__init__.py +2 -0
  64. polylith_cli-0.1.0/polylith_cli/polylith/test/tests.py +20 -0
  65. polylith_cli-0.1.0/polylith_cli/polylith/workspace/__init__.py +2 -0
  66. polylith_cli-0.1.0/polylith_cli/polylith/workspace/create.py +21 -0
  67. polylith_cli-0.1.0/polylith_cli/polylith/workspace/parser.py +56 -0
  68. polylith_cli-0.1.0/polylith_cli/polylith/workspace/paths.py +21 -0
  69. polylith_cli-0.1.0/pyproject.toml +26 -0
@@ -0,0 +1,88 @@
1
+ Metadata-Version: 2.1
2
+ Name: polylith-cli
3
+ Version: 0.1.0
4
+ Summary: Python tooling support for the Polylith Architecture
5
+ Home-page: https://davidvujic.github.io/python-polylith-docs/
6
+ License: MIT
7
+ Author: David Vujic
8
+ Requires-Python: >=3.8,<4.0
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.8
12
+ Classifier: Programming Language :: Python :: 3.9
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Requires-Dist: rich (>=13.6.0,<14.0.0)
17
+ Requires-Dist: tomlkit (>=0.11.5,<0.12.0)
18
+ Requires-Dist: typer[all] (>=0.9.0,<0.10.0)
19
+ Project-URL: Repository, https://github.com/davidvujic/python-polylith
20
+ Description-Content-Type: text/markdown
21
+
22
+ # Python tooling for Polylith
23
+
24
+ A command line interface that adds tooling support for the Polylith Architecture in Python.
25
+
26
+ ## Documentation
27
+ Have a look at the [documentation](https://davidvujic.github.io/python-polylith-docs/).
28
+ You will find installation, setup, usage guides and more.
29
+
30
+ ## Quick start
31
+
32
+ `Poetry` user? For Poetry, the recommended setup is to install the `poetry-polylith-plugin`.
33
+ Read more about Poetry in the [documentation](https://davidvujic.github.io/python-polylith-docs/installation/).
34
+
35
+ ### Setup: Hatch
36
+ Create a directory for your code, initialize it with __git__ and setup the basics with `hatch`:
37
+
38
+ ``` shell
39
+ git init
40
+
41
+ hatch new --init
42
+ ```
43
+
44
+ Add the Polylith CLI as a dev dependency in `pyproject.toml`:
45
+
46
+ ``` toml
47
+ [tool.hatch.envs.default]
48
+ dependencies = ["polylith-cli"]
49
+ ```
50
+
51
+ Add configuration for a local virtual environment in the `pyproject.toml`:
52
+ ``` toml
53
+ [tool.hatch.envs.default]
54
+ type = "virtual"
55
+ path = ".venv"
56
+ python = "3.12" # your preferred version here
57
+ ```
58
+
59
+ Make `Hatch` aware of the Polylith structure, by adding this to the `pyproject.toml`:
60
+ ``` toml
61
+ [tool.hatch.build]
62
+ dev-mode-dirs = ["components", "bases", "development", "."]
63
+
64
+ ```
65
+
66
+ Next: create a Polylith workspace, with a basic Polylith folder structure.
67
+ The `poly` command is now available in the local virtual environment.
68
+ You can run commands in the context of `hatch run` to make Polylith aware of the development environment.
69
+
70
+ ``` shell
71
+ hatch run poly create workspace --name my_namespace --theme loose
72
+ ```
73
+
74
+ ### Ready for coding!
75
+
76
+ Add components, bases and projects:
77
+
78
+ ``` shell
79
+ hatch run poly create component --name my_component
80
+
81
+ hatch run poly create base --name my_example_endpoint
82
+
83
+ hatch run poly create project --name my_example_project
84
+ ```
85
+
86
+ For details, have a look at the [documentation](https://davidvujic.github.io/python-polylith-docs/).
87
+ There, you will find guides for setup, migration, packaging, available commands, code examples and more.
88
+
@@ -0,0 +1,66 @@
1
+ # Python tooling for Polylith
2
+
3
+ A command line interface that adds tooling support for the Polylith Architecture in Python.
4
+
5
+ ## Documentation
6
+ Have a look at the [documentation](https://davidvujic.github.io/python-polylith-docs/).
7
+ You will find installation, setup, usage guides and more.
8
+
9
+ ## Quick start
10
+
11
+ `Poetry` user? For Poetry, the recommended setup is to install the `poetry-polylith-plugin`.
12
+ Read more about Poetry in the [documentation](https://davidvujic.github.io/python-polylith-docs/installation/).
13
+
14
+ ### Setup: Hatch
15
+ Create a directory for your code, initialize it with __git__ and setup the basics with `hatch`:
16
+
17
+ ``` shell
18
+ git init
19
+
20
+ hatch new --init
21
+ ```
22
+
23
+ Add the Polylith CLI as a dev dependency in `pyproject.toml`:
24
+
25
+ ``` toml
26
+ [tool.hatch.envs.default]
27
+ dependencies = ["polylith-cli"]
28
+ ```
29
+
30
+ Add configuration for a local virtual environment in the `pyproject.toml`:
31
+ ``` toml
32
+ [tool.hatch.envs.default]
33
+ type = "virtual"
34
+ path = ".venv"
35
+ python = "3.12" # your preferred version here
36
+ ```
37
+
38
+ Make `Hatch` aware of the Polylith structure, by adding this to the `pyproject.toml`:
39
+ ``` toml
40
+ [tool.hatch.build]
41
+ dev-mode-dirs = ["components", "bases", "development", "."]
42
+
43
+ ```
44
+
45
+ Next: create a Polylith workspace, with a basic Polylith folder structure.
46
+ The `poly` command is now available in the local virtual environment.
47
+ You can run commands in the context of `hatch run` to make Polylith aware of the development environment.
48
+
49
+ ``` shell
50
+ hatch run poly create workspace --name my_namespace --theme loose
51
+ ```
52
+
53
+ ### Ready for coding!
54
+
55
+ Add components, bases and projects:
56
+
57
+ ``` shell
58
+ hatch run poly create component --name my_component
59
+
60
+ hatch run poly create base --name my_example_endpoint
61
+
62
+ hatch run poly create project --name my_example_project
63
+ ```
64
+
65
+ For details, have a look at the [documentation](https://davidvujic.github.io/python-polylith-docs/).
66
+ There, you will find guides for setup, migration, packaging, available commands, code examples and more.
@@ -0,0 +1,2 @@
1
+ from polylith_cli.polylith.alias.core import parse, pick
2
+ __all__ = ['parse', 'pick']
@@ -0,0 +1,23 @@
1
+ from functools import reduce
2
+ from typing import Dict, List, Set
3
+
4
+
5
+ def _to_key_with_values(acc: Dict, alias: str) -> Dict:
6
+ k, v = str.split(alias, "=")
7
+
8
+ values = [str.strip(val) for val in str.split(v, ",")]
9
+
10
+ return {**acc, **{k: values}}
11
+
12
+
13
+ def parse(aliases: List[str]) -> Dict[str, List[str]]:
14
+ """Parse a list of aliases defined as key=value(s) into a dictionary"""
15
+ return reduce(_to_key_with_values, aliases, {})
16
+
17
+
18
+ def pick(aliases: Dict[str, List[str]], keys: Set) -> Set:
19
+ matrix = [v for k, v in aliases.items() if k in keys]
20
+
21
+ flattened: List = sum(matrix, [])
22
+
23
+ return set(flattened)
@@ -0,0 +1,3 @@
1
+ from polylith_cli.polylith.bricks.base import create_base, get_bases_data
2
+ from polylith_cli.polylith.bricks.component import create_component, get_components_data
3
+ __all__ = ['create_base', 'create_component', 'get_bases_data', 'get_components_data']
@@ -0,0 +1,15 @@
1
+ from pathlib import Path
2
+ from typing import List
3
+ from polylith_cli.polylith.bricks import component
4
+ from polylith_cli.polylith.bricks.brick import create_brick
5
+ from polylith_cli.polylith.repo import bases_dir
6
+ from polylith_cli.polylith.test import create_test
7
+
8
+ def create_base(path: Path, options: dict) -> None:
9
+ extra = {'brick': bases_dir}
10
+ base_options = {**options, **extra}
11
+ create_brick(path, base_options)
12
+ create_test(path, base_options)
13
+
14
+ def get_bases_data(path: Path, ns: str) -> List[dict]:
15
+ return component.get_components_data(path, ns, bases_dir)
@@ -0,0 +1,19 @@
1
+ from pathlib import Path
2
+ from polylith_cli.polylith.dirs import create_dir
3
+ from polylith_cli.polylith.files import create_file
4
+ from polylith_cli.polylith.interface import create_interface
5
+ from polylith_cli.polylith.readme import create_brick_readme
6
+ from polylith_cli.polylith.workspace import parser
7
+
8
+ def create_brick(root: Path, options: dict) -> None:
9
+ modulename = options['modulename']
10
+ path_kwargs = {k: v for k, v in options.items() if k in {'brick', 'namespace', 'package'}}
11
+ brick_structure = parser.get_brick_structure_from_config(root)
12
+ resources_structure = parser.get_resources_structure_from_config(root)
13
+ brick_path = brick_structure.format(**path_kwargs)
14
+ resources_path = resources_structure.format(**path_kwargs)
15
+ d = create_dir(root, brick_path)
16
+ create_file(d, f'{modulename}.py')
17
+ create_interface(d, options)
18
+ if parser.is_readme_generation_enabled(root):
19
+ create_brick_readme(root / resources_path, options)
@@ -0,0 +1,27 @@
1
+ from pathlib import Path
2
+ from typing import List
3
+ from polylith_cli.polylith import workspace
4
+ from polylith_cli.polylith.bricks.brick import create_brick
5
+ from polylith_cli.polylith.repo import components_dir
6
+ from polylith_cli.polylith.test import create_test
7
+
8
+ def create_component(path: Path, options: dict) -> None:
9
+ extra = {'brick': components_dir}
10
+ component_options = {**options, **extra}
11
+ create_brick(path, component_options)
12
+ create_test(path, component_options)
13
+
14
+ def is_brick_dir(p: Path) -> bool:
15
+ return p.is_dir() and p.name not in {'__pycache__', '.venv', '.mypy_cache'}
16
+
17
+ def get_component_dirs(root: Path, top_dir, ns) -> list:
18
+ theme = workspace.parser.get_theme_from_config(root)
19
+ dirs = top_dir if theme == 'tdd' else f'{top_dir}/{ns}'
20
+ component_dir = root / dirs
21
+ if not component_dir.exists():
22
+ return []
23
+ return [f for f in component_dir.iterdir() if is_brick_dir(f)]
24
+
25
+ def get_components_data(root: Path, ns: str, top_dir: str=components_dir) -> List[dict]:
26
+ dirs = get_component_dirs(root, top_dir, ns)
27
+ return [{'name': d.name} for d in dirs]
@@ -0,0 +1,2 @@
1
+ from polylith_cli.polylith.check import collect, grouping, report
2
+ __all__ = ['collect', 'grouping', 'report']
@@ -0,0 +1,30 @@
1
+ from pathlib import Path
2
+ from typing import List, Set
3
+ from polylith_cli.polylith import check, imports, workspace
4
+
5
+ def extract_bricks(paths: Set[Path], ns: str) -> dict:
6
+ all_imports = imports.fetch_all_imports(paths)
7
+ return check.grouping.extract_brick_imports(all_imports, ns)
8
+
9
+ def with_unknown_components(root: Path, ns: str, brick_imports: dict) -> dict:
10
+ keys = set(brick_imports.keys())
11
+ values = set().union(*brick_imports.values())
12
+ unknowns = values.difference(keys)
13
+ if not unknowns:
14
+ return brick_imports
15
+ paths = workspace.paths.collect_components_paths(root, ns, unknowns)
16
+ extracted = extract_bricks(paths, ns)
17
+ if not extracted:
18
+ return brick_imports
19
+ collected = {**brick_imports, **extracted}
20
+ return with_unknown_components(root, ns, collected)
21
+
22
+ def diff(known_bricks: Set[str], bases: List[str], components: List[str]) -> Set[str]:
23
+ bricks = set().union(bases, components)
24
+ return known_bricks.difference(bricks)
25
+
26
+ def imports_diff(brick_imports: dict, bases: List, components: List) -> Set[str]:
27
+ flattened_bases = set().union(*brick_imports['bases'].values())
28
+ flattened_components = set().union(*brick_imports['components'].values())
29
+ flattened_imports = set().union(flattened_bases, flattened_components)
30
+ return diff(flattened_imports, bases, components)
@@ -0,0 +1,36 @@
1
+ from typing import Set, Union
2
+
3
+
4
+ def only_brick_imports(imports: Set[str], top_ns: str) -> Set[str]:
5
+ return {i for i in imports if i.startswith(top_ns)}
6
+
7
+
8
+ def only_bricks(import_data: dict, top_ns: str) -> dict:
9
+ return {k: only_brick_imports(v, top_ns) for k, v in import_data.items()}
10
+
11
+
12
+ def brick_import_to_name(brick_import: str) -> Union[str, None]:
13
+ parts = brick_import.split(".")
14
+
15
+ return parts[1] if len(parts) > 1 else None
16
+
17
+
18
+ def only_brick_name(brick_imports: Set[str]) -> Set:
19
+ res = {brick_import_to_name(i) for i in brick_imports}
20
+
21
+ return {i for i in res if i}
22
+
23
+
24
+ def only_brick_names(import_data: dict) -> dict:
25
+ return {k: only_brick_name(v) for k, v in import_data.items() if v}
26
+
27
+
28
+ def exclude_empty(import_data: dict) -> dict:
29
+ return {k: v for k, v in import_data.items() if v}
30
+
31
+
32
+ def extract_brick_imports(all_imports: dict, top_ns) -> dict:
33
+ with_only_bricks = only_bricks(all_imports, top_ns)
34
+ with_only_brick_names = only_brick_names(with_only_bricks)
35
+
36
+ return exclude_empty(with_only_brick_names)
@@ -0,0 +1,47 @@
1
+ from pathlib import Path
2
+ from typing import Set
3
+ from polylith_cli.polylith import imports, libs, workspace
4
+ from polylith_cli.polylith.check import collect, grouping
5
+ from polylith_cli.polylith.reporting import theme
6
+ from rich.console import Console
7
+
8
+ def print_brick_imports(brick_imports: dict) -> None:
9
+ console = Console(theme=theme.poly_theme)
10
+ bases = brick_imports['bases']
11
+ components = brick_imports['components']
12
+ bricks = {**bases, **components}
13
+ for key, values in bricks.items():
14
+ imports_in_brick = values.difference({key})
15
+ if imports_in_brick:
16
+ console.print(f':information: [data]{key}[/] is importing [data]{', '.join(imports_in_brick)}[/]')
17
+
18
+ def print_missing_deps(diff: Set[str], project_name: str) -> None:
19
+ if not diff:
20
+ return
21
+ console = Console(theme=theme.poly_theme)
22
+ missing = ', '.join(sorted(diff))
23
+ console.print(f':thinking_face: Cannot locate {missing} in {project_name}')
24
+
25
+ def fetch_brick_imports(root: Path, ns: str, all_imports: dict) -> dict:
26
+ extracted = grouping.extract_brick_imports(all_imports, ns)
27
+ return collect.with_unknown_components(root, ns, extracted)
28
+
29
+ def collect_all_imports(root: Path, ns: str, project_data: dict) -> dict:
30
+ bases = {b for b in project_data.get('bases', [])}
31
+ components = {c for c in project_data.get('components', [])}
32
+ bases_paths = workspace.paths.collect_bases_paths(root, ns, bases)
33
+ components_paths = workspace.paths.collect_components_paths(root, ns, components)
34
+ all_imports_in_bases = imports.fetch_all_imports(bases_paths)
35
+ all_imports_in_components = imports.fetch_all_imports(components_paths)
36
+ brick_imports = {'bases': fetch_brick_imports(root, ns, all_imports_in_bases), 'components': fetch_brick_imports(root, ns, all_imports_in_components)}
37
+ third_party_imports = {'bases': libs.extract_third_party_imports(all_imports_in_bases, ns), 'components': libs.extract_third_party_imports(all_imports_in_components, ns)}
38
+ return {'brick_imports': brick_imports, 'third_party_imports': third_party_imports}
39
+
40
+ def create_report(project_data: dict, collected_imports: dict, third_party_libs: Set, is_strict: bool=False) -> dict:
41
+ bases = {b for b in project_data.get('bases', [])}
42
+ components = {c for c in project_data.get('components', [])}
43
+ brick_imports = collected_imports['brick_imports']
44
+ third_party_imports = collected_imports['third_party_imports']
45
+ brick_diff = collect.imports_diff(brick_imports, list(bases), list(components))
46
+ libs_diff = libs.report.calculate_diff(third_party_imports, third_party_libs, is_strict)
47
+ return {'brick_imports': brick_imports, 'third_party_imports': third_party_imports, 'brick_diff': brick_diff, 'libs_diff': libs_diff}
@@ -0,0 +1,2 @@
1
+ from polylith_cli.polylith.cli import core
2
+ __all__ = ['core']
@@ -0,0 +1,2 @@
1
+ from polylith_cli.polylith.cli.core import app
2
+ app(prog_name='poly')
@@ -0,0 +1,58 @@
1
+ from pathlib import Path
2
+ from polylith_cli.polylith import commands, info, repo, workspace
3
+ from polylith_cli.polylith.cli import create, options
4
+ from typer import Exit, Option, Typer
5
+ from typing_extensions import Annotated
6
+ app = Typer()
7
+ app.add_typer(create.app, name='create', help='Commands for creating a workspace, bases, components and projects.')
8
+
9
+ @app.command('info')
10
+ def info_command(short: Annotated[bool, options.short_workspace]=False):
11
+ """Info about the Polylith workspace."""
12
+ commands.info.run(short)
13
+
14
+ @app.command('check')
15
+ def check_command(strict: Annotated[bool, options.strict]=False, verbose: Annotated[bool, options.verbose]=False, quiet: Annotated[bool, options.quiet]=False, directory: Annotated[str, options.directory]='', alias: Annotated[str, options.alias]=''):
16
+ """Validates the Polylith workspace."""
17
+ root = repo.get_workspace_root(Path.cwd())
18
+ ns = workspace.parser.get_namespace_from_config(root)
19
+ all_projects_data = info.get_projects_data(root, ns)
20
+ only_projects_data = [p for p in all_projects_data if info.is_project(p)]
21
+ cli_options = {'verbose': verbose, 'quiet': quiet, 'strict': strict, 'alias': str.split(alias, ',') if alias else []}
22
+ dir_path = Path(directory).as_posix() if directory else Path.cwd().name
23
+ projects_data = [p for p in only_projects_data if dir_path in p['path'].as_posix()]
24
+ results = {commands.check.run(root, ns, p, cli_options) for p in projects_data}
25
+ if not all(results):
26
+ raise Exit(code=1)
27
+
28
+ @app.command('diff')
29
+ def diff_command(since: Annotated[str, Option(help='Changed since a specific tag.')]='', short: Annotated[bool, options.short]=False, bricks: Annotated[bool, Option(help='Print changed bricks.')]=False):
30
+ """Shows changed bricks compared to the latest git tag."""
31
+ commands.diff.run(since, short, bricks)
32
+
33
+ @app.command('libs')
34
+ def libs_command(strict: Annotated[bool, options.strict]=False, directory: Annotated[str, options.directory]='', alias: Annotated[str, options.alias]=''):
35
+ """Show third-party libraries used in the workspace."""
36
+ root = repo.get_workspace_root(Path.cwd())
37
+ ns = workspace.parser.get_namespace_from_config(root)
38
+ projects_data = info.get_projects_data(root, ns)
39
+ cli_options = {'strict': strict, 'alias': str.split(alias, ',') if alias else []}
40
+ dir_path = Path(directory).as_posix() if directory else Path.cwd().name
41
+ projects_data = [p for p in projects_data if dir_path in p['path'].as_posix()]
42
+ results = {commands.libs.run(root, ns, p, cli_options) for p in projects_data}
43
+ if not all(results):
44
+ raise Exit(code=1)
45
+
46
+ @app.command('sync')
47
+ def sync_command(strict: Annotated[bool, options.strict]=False, quiet: Annotated[bool, options.quiet]=False, directory: Annotated[str, options.directory]='', verbose: Annotated[str, options.verbose]=''):
48
+ """Update pyproject.toml with missing bricks."""
49
+ root = repo.get_workspace_root(Path.cwd())
50
+ ns = workspace.parser.get_namespace_from_config(root)
51
+ projects_data = info.get_projects_data(root, ns)
52
+ cli_options = {'strict': strict, 'quiet': quiet, 'verbose': verbose}
53
+ dir_path = Path(directory).as_posix() if directory else Path.cwd().name
54
+ projects_data = [p for p in projects_data if dir_path in p['path'].as_posix()]
55
+ for p in projects_data:
56
+ commands.sync.run(root, ns, p, cli_options)
57
+ if __name__ == '__main__':
58
+ app()
@@ -0,0 +1,42 @@
1
+ from pathlib import Path
2
+ from typing import Union
3
+ from polylith_cli.polylith import project, repo
4
+ from polylith_cli.polylith.bricks import base, component
5
+ from polylith_cli.polylith.commands.create import create
6
+ from polylith_cli.polylith.workspace.create import create_workspace
7
+ from typer import Exit, Option, Typer
8
+ from typing_extensions import Annotated
9
+ app = Typer()
10
+
11
+ @app.command('base')
12
+ def base_command(name: Annotated[str, Option(help='Name of the base.')], description: Annotated[str, Option(help='Description of the base.')]=''):
13
+ """Creates a Polylith base."""
14
+ create(name, description, base.create_base)
15
+
16
+ @app.command('component')
17
+ def component_command(name: Annotated[str, Option(help='Name of the component.')], description: Annotated[str, Option(help='Description of the component.')]=''):
18
+ """Creates a Polylith component."""
19
+ create(name, description, component.create_component)
20
+
21
+ def _create_project(root: Path, _ns: str, name: str, description: Union[str, None]):
22
+ root_pyproject: dict = project.get_toml(root / repo.default_toml)
23
+ if repo.is_poetry(root_pyproject):
24
+ template = project.templates.poetry_pyproject
25
+ elif repo.is_hatch(root_pyproject):
26
+ template = project.templates.hatch_pyproject
27
+ if not template:
28
+ print('Failed to guess the used Package & Dependency Management')
29
+ print('Is the root pyproject.toml missing, or are you using a tool not supported by Polylith?')
30
+ raise Exit(code=1)
31
+ project.create_project(root, template, name, description or '')
32
+
33
+ @app.command('project')
34
+ def project_command(name: Annotated[str, Option(help='Name of the project.')], description: Annotated[str, Option(help='Description of the project.')]=''):
35
+ """Creates a Polylith project."""
36
+ create(name, description, _create_project)
37
+
38
+ @app.command('workspace')
39
+ def workspace_command(name: Annotated[str, Option(help='Name of the workspace.')], theme: Annotated[str, Option(help='Workspace theme.')]='tdd'):
40
+ """Creates a Polylith workspace in the current directory."""
41
+ path = Path.cwd()
42
+ create_workspace(path, name, theme)
@@ -0,0 +1,17 @@
1
+ from typer import Option
2
+
3
+ alias = Option(
4
+ help="alias for third-party libraries, useful when an import differ from the library name"
5
+ )
6
+ directory = Option(
7
+ help="The working directory for the command (defaults to the current working directory)."
8
+ )
9
+
10
+ short = Option(help="Print short view.")
11
+ short_workspace = Option(help="Display Workspace Info adjusted for many projects.")
12
+ strict = Option(
13
+ help="More strict checks when matching name of third-party libraries and imports"
14
+ )
15
+
16
+ verbose = Option(help="More verbose output.")
17
+ quiet = Option(help="Do not output any messages.")
@@ -0,0 +1,2 @@
1
+ from polylith_cli.polylith.commands import check, create, diff, info, libs, sync
2
+ __all__ = ['check', 'create', 'diff', 'info', 'libs', 'sync']
@@ -0,0 +1,26 @@
1
+ import importlib.metadata
2
+ from pathlib import Path
3
+ from polylith_cli.polylith import alias, check, distributions
4
+
5
+ def run(root: Path, ns: str, project_data: dict, options: dict) -> bool:
6
+ is_verbose = options['verbose']
7
+ is_quiet = options['quiet']
8
+ is_strict = options['strict']
9
+ library_alias = options['alias']
10
+ third_party_libs = project_data['deps']
11
+ name = project_data['name']
12
+ collected_imports = check.report.collect_all_imports(root, ns, project_data)
13
+ dists = importlib.metadata.distributions()
14
+ known_aliases = distributions.distributions_packages(dists)
15
+ known_aliases.update(alias.parse(library_alias))
16
+ extra = alias.pick(known_aliases, third_party_libs)
17
+ libs = third_party_libs.union(extra)
18
+ details = check.report.create_report(project_data, collected_imports, libs, is_strict)
19
+ res = all([not details['brick_diff'], not details['libs_diff']])
20
+ if not is_quiet:
21
+ check.report.print_missing_deps(details['brick_diff'], name)
22
+ check.report.print_missing_deps(details['libs_diff'], name)
23
+ if is_verbose:
24
+ check.report.print_brick_imports(details['brick_imports'])
25
+ check.report.print_brick_imports(details['third_party_imports'])
26
+ return res
@@ -0,0 +1,13 @@
1
+ from pathlib import Path
2
+ from typing import Union
3
+ from polylith_cli.polylith import repo, workspace
4
+
5
+ def create(name: Union[str, None], description: Union[str, None], fn):
6
+ root = repo.get_workspace_root(Path.cwd())
7
+ namespace = workspace.parser.get_namespace_from_config(root)
8
+ if not name:
9
+ raise ValueError('Please add a name by using --name')
10
+ if not namespace:
11
+ raise ValueError("Didn't find a namespace. Expected to find it in workspace.toml.")
12
+ options = {'namespace': namespace, 'package': name, 'description': description, 'modulename': 'core'}
13
+ fn(root, options)
@@ -0,0 +1,30 @@
1
+ from pathlib import Path
2
+ from typing import Union
3
+ from polylith_cli.polylith import diff, info, repo, workspace
4
+
5
+ def print_views(root: Path, tag: str, short: bool, only_bricks: bool) -> None:
6
+ ns = workspace.parser.get_namespace_from_config(root)
7
+ files = diff.collect.get_files(tag)
8
+ bases = diff.collect.get_changed_bases(files, ns)
9
+ components = diff.collect.get_changed_components(files, ns)
10
+ projects = diff.collect.get_changed_projects(files)
11
+ all_projects_data = info.get_bricks_in_projects(root, components, bases, ns)
12
+ projects_data = [p for p in all_projects_data if info.is_project(p)]
13
+ affected_projects = diff.collect.get_projects_affected_by_changes(projects_data, projects, bases, components)
14
+ if not short and (not only_bricks):
15
+ diff.report.print_diff_summary(tag, bases, components)
16
+ diff.report.print_detected_changes_in_projects(projects, short)
17
+ diff.report.print_diff_details(projects_data, bases, components)
18
+ return
19
+ if short and (not only_bricks):
20
+ diff.report.print_projects_affected_by_changes(affected_projects, short)
21
+ return
22
+ diff.report.print_detected_changes_in_bricks(bases, components, short)
23
+
24
+ def run(tag_name: Union[str, None], short: bool, only_bricks: bool):
25
+ root = repo.get_workspace_root(Path.cwd())
26
+ tag = diff.collect.get_latest_tag(root, tag_name)
27
+ if not tag:
28
+ print('No tags found in repository.')
29
+ return
30
+ print_views(root, tag, short, only_bricks)
@@ -0,0 +1,16 @@
1
+ from pathlib import Path
2
+ from polylith_cli.polylith import info, repo, workspace
3
+
4
+ def run(short: bool):
5
+ root = repo.get_workspace_root(Path.cwd())
6
+ ns = workspace.parser.get_namespace_from_config(root)
7
+ bases = info.get_bases(root, ns)
8
+ components = info.get_components(root, ns)
9
+ projects_data = info.get_bricks_in_projects(root, components, bases, ns)
10
+ info.print_workspace_summary(projects_data, bases, components)
11
+ if not components and (not bases):
12
+ return
13
+ if short:
14
+ info.print_compressed_view_for_bricks_in_projects(projects_data, bases, components)
15
+ else:
16
+ info.print_bricks_in_projects(projects_data, bases, components)
@@ -0,0 +1,16 @@
1
+ from pathlib import Path
2
+ from polylith_cli.polylith import alias
3
+ from polylith_cli.polylith.libs import report
4
+
5
+ def run(root: Path, ns: str, project_data: dict, options: dict) -> bool:
6
+ is_strict = options['strict']
7
+ library_alias = options['alias']
8
+ name = project_data['name']
9
+ third_party_libs = project_data['deps']
10
+ brick_imports = report.get_third_party_imports(root, ns, project_data)
11
+ report.print_libs_summary(brick_imports, project_data)
12
+ report.print_libs_in_bricks(brick_imports)
13
+ library_aliases = alias.parse(library_alias)
14
+ extra = alias.pick(library_aliases, third_party_libs)
15
+ libs = third_party_libs.union(extra)
16
+ return report.print_missing_installed_libs(brick_imports, libs, name, is_strict)
@@ -0,0 +1,17 @@
1
+ from pathlib import Path
2
+ from polylith_cli.polylith import info
3
+ from polylith_cli.polylith import sync
4
+
5
+ def run(root: Path, ns: str, project_data: dict, options: dict):
6
+ is_quiet = options['quiet']
7
+ is_verbose = options['verbose']
8
+ bases = info.get_bases(root, ns)
9
+ components = info.get_components(root, ns)
10
+ workspace_data = {'bases': bases, 'components': components}
11
+ diff = sync.calculate_diff(root, ns, project_data, workspace_data)
12
+ sync.update_project(root, ns, diff)
13
+ if is_quiet:
14
+ return
15
+ sync.report.print_summary(diff)
16
+ if is_verbose:
17
+ sync.report.print_brick_imports(diff)
@@ -0,0 +1,2 @@
1
+ from polylith_cli.polylith.development.development import create_development
2
+ __all__ = ['create_development']
@@ -0,0 +1,6 @@
1
+ from pathlib import Path
2
+ from polylith_cli.polylith.dirs import create_dir
3
+ from polylith_cli.polylith.repo import development_dir
4
+
5
+ def create_development(path: Path, keep=True) -> None:
6
+ create_dir(path, development_dir, keep=keep)
@@ -0,0 +1,2 @@
1
+ from polylith_cli.polylith.diff import collect, report
2
+ __all__ = ['collect', 'report']