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.
- polylith_cli-0.1.0/PKG-INFO +88 -0
- polylith_cli-0.1.0/README.md +66 -0
- polylith_cli-0.1.0/polylith_cli/polylith/alias/__init__.py +2 -0
- polylith_cli-0.1.0/polylith_cli/polylith/alias/core.py +23 -0
- polylith_cli-0.1.0/polylith_cli/polylith/bricks/__init__.py +3 -0
- polylith_cli-0.1.0/polylith_cli/polylith/bricks/base.py +15 -0
- polylith_cli-0.1.0/polylith_cli/polylith/bricks/brick.py +19 -0
- polylith_cli-0.1.0/polylith_cli/polylith/bricks/component.py +27 -0
- polylith_cli-0.1.0/polylith_cli/polylith/check/__init__.py +2 -0
- polylith_cli-0.1.0/polylith_cli/polylith/check/collect.py +30 -0
- polylith_cli-0.1.0/polylith_cli/polylith/check/grouping.py +36 -0
- polylith_cli-0.1.0/polylith_cli/polylith/check/report.py +47 -0
- polylith_cli-0.1.0/polylith_cli/polylith/cli/__init__.py +2 -0
- polylith_cli-0.1.0/polylith_cli/polylith/cli/__main__.py +2 -0
- polylith_cli-0.1.0/polylith_cli/polylith/cli/core.py +58 -0
- polylith_cli-0.1.0/polylith_cli/polylith/cli/create.py +42 -0
- polylith_cli-0.1.0/polylith_cli/polylith/cli/options.py +17 -0
- polylith_cli-0.1.0/polylith_cli/polylith/commands/__init__.py +2 -0
- polylith_cli-0.1.0/polylith_cli/polylith/commands/check.py +26 -0
- polylith_cli-0.1.0/polylith_cli/polylith/commands/create.py +13 -0
- polylith_cli-0.1.0/polylith_cli/polylith/commands/diff.py +30 -0
- polylith_cli-0.1.0/polylith_cli/polylith/commands/info.py +16 -0
- polylith_cli-0.1.0/polylith_cli/polylith/commands/libs.py +16 -0
- polylith_cli-0.1.0/polylith_cli/polylith/commands/sync.py +17 -0
- polylith_cli-0.1.0/polylith_cli/polylith/development/__init__.py +2 -0
- polylith_cli-0.1.0/polylith_cli/polylith/development/development.py +6 -0
- polylith_cli-0.1.0/polylith_cli/polylith/diff/__init__.py +2 -0
- polylith_cli-0.1.0/polylith_cli/polylith/diff/collect.py +45 -0
- polylith_cli-0.1.0/polylith_cli/polylith/diff/report.py +50 -0
- polylith_cli-0.1.0/polylith_cli/polylith/dirs/__init__.py +2 -0
- polylith_cli-0.1.0/polylith_cli/polylith/dirs/dirs.py +10 -0
- polylith_cli-0.1.0/polylith_cli/polylith/distributions/__init__.py +2 -0
- polylith_cli-0.1.0/polylith_cli/polylith/distributions/core.py +31 -0
- polylith_cli-0.1.0/polylith_cli/polylith/files/__init__.py +2 -0
- polylith_cli-0.1.0/polylith_cli/polylith/files/files.py +9 -0
- polylith_cli-0.1.0/polylith_cli/polylith/imports/__init__.py +2 -0
- polylith_cli-0.1.0/polylith_cli/polylith/imports/parser.py +67 -0
- polylith_cli-0.1.0/polylith_cli/polylith/info/__init__.py +3 -0
- polylith_cli-0.1.0/polylith_cli/polylith/info/collect.py +31 -0
- polylith_cli-0.1.0/polylith_cli/polylith/info/report.py +69 -0
- polylith_cli-0.1.0/polylith_cli/polylith/interface/__init__.py +2 -0
- polylith_cli-0.1.0/polylith_cli/polylith/interface/interfaces.py +19 -0
- polylith_cli-0.1.0/polylith_cli/polylith/libs/__init__.py +3 -0
- polylith_cli-0.1.0/polylith_cli/polylith/libs/grouping.py +35 -0
- polylith_cli-0.1.0/polylith_cli/polylith/libs/report.py +80 -0
- polylith_cli-0.1.0/polylith_cli/polylith/libs/stdlib.py +271 -0
- polylith_cli-0.1.0/polylith_cli/polylith/project/__init__.py +5 -0
- polylith_cli-0.1.0/polylith_cli/polylith/project/create.py +18 -0
- polylith_cli-0.1.0/polylith_cli/polylith/project/get.py +57 -0
- polylith_cli-0.1.0/polylith_cli/polylith/project/parser.py +15 -0
- polylith_cli-0.1.0/polylith_cli/polylith/project/templates.py +37 -0
- polylith_cli-0.1.0/polylith_cli/polylith/readme/__init__.py +2 -0
- polylith_cli-0.1.0/polylith_cli/polylith/readme/readme.py +21 -0
- polylith_cli-0.1.0/polylith_cli/polylith/repo/__init__.py +3 -0
- polylith_cli-0.1.0/polylith_cli/polylith/repo/get.py +23 -0
- polylith_cli-0.1.0/polylith_cli/polylith/repo/repo.py +72 -0
- polylith_cli-0.1.0/polylith_cli/polylith/reporting/__init__.py +2 -0
- polylith_cli-0.1.0/polylith_cli/polylith/reporting/theme.py +10 -0
- polylith_cli-0.1.0/polylith_cli/polylith/sync/__init__.py +4 -0
- polylith_cli-0.1.0/polylith_cli/polylith/sync/collect.py +27 -0
- polylith_cli-0.1.0/polylith_cli/polylith/sync/report.py +24 -0
- polylith_cli-0.1.0/polylith_cli/polylith/sync/update.py +66 -0
- polylith_cli-0.1.0/polylith_cli/polylith/test/__init__.py +2 -0
- polylith_cli-0.1.0/polylith_cli/polylith/test/tests.py +20 -0
- polylith_cli-0.1.0/polylith_cli/polylith/workspace/__init__.py +2 -0
- polylith_cli-0.1.0/polylith_cli/polylith/workspace/create.py +21 -0
- polylith_cli-0.1.0/polylith_cli/polylith/workspace/parser.py +56 -0
- polylith_cli-0.1.0/polylith_cli/polylith/workspace/paths.py +21 -0
- 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,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,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,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,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,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)
|