Skip to content

Commit f323b5c

Browse files
authored
feat(poly sync): sort the bricks to add by brick type and alphabetically (#457)
* fix(poly sync): sorting the bricks to update in the pyproject.toml * bump Poetry plugin to 1.53.0 * bump CLI to 1.49.0
1 parent 1441411 commit f323b5c

4 files changed

Lines changed: 122 additions & 40 deletions

File tree

components/polylith/sync/update.py

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import itertools
2+
import operator
13
from functools import reduce
24
from pathlib import Path
35
from typing import List, Union
@@ -28,6 +30,10 @@ def to_key_value_include(acc: dict, package: dict) -> dict:
2830
return {**acc, **{include: brick}}
2931

3032

33+
def sort_bricks(bricks: dict) -> List[tuple]:
34+
return sorted(bricks.items(), key=lambda i: i[0])
35+
36+
3137
def generate_updated_pep_621_project(data: TOMLDocument, bricks_to_add: dict) -> str:
3238
copy = copy_toml_data(data)
3339

@@ -40,7 +46,7 @@ def generate_updated_pep_621_project(data: TOMLDocument, bricks_to_add: dict) ->
4046
if not copy["tool"]["polylith"].get("bricks"):
4147
copy["tool"]["polylith"]["bricks"] = {}
4248

43-
for k, v in bricks_to_add.items():
49+
for k, v in sort_bricks(bricks_to_add):
4450
copy["tool"]["polylith"]["bricks"][k] = v
4551

4652
return tomlkit.dumps(copy)
@@ -55,21 +61,39 @@ def generate_updated_hatch_project(data: TOMLDocument, bricks_to_add: dict) -> s
5561
if not has_polylith and has_hatch:
5662
copy = copy_toml_data(data)
5763

58-
for k, v in bricks_to_add.items():
64+
for k, v in sort_bricks(bricks_to_add):
5965
copy["tool"]["hatch"]["build"]["force-include"][k] = v
6066

6167
return tomlkit.dumps(copy)
6268

6369
return generate_updated_pep_621_project(data, bricks_to_add)
6470

6571

72+
def sort_fn_by_from(data: dict) -> str:
73+
return data["from"]
74+
75+
76+
def sort_fn_by_include(data: dict) -> str:
77+
return data["include"]
78+
79+
80+
def to_sorted_packages(packages: List[dict]) -> List[dict]:
81+
sorted_by_brick_type = sorted(packages, key=sort_fn_by_from)
82+
grouped = itertools.groupby(sorted_by_brick_type, key=sort_fn_by_from)
83+
84+
groups = [list(g) for _k, g in grouped]
85+
flattened: List[dict] = reduce(operator.iadd, groups, [])
86+
87+
return sorted(flattened, key=sort_fn_by_include)
88+
89+
6690
def generate_updated_poetry_project(data: TOMLDocument, packages: List[dict]) -> str:
6791
copy = copy_toml_data(data)
6892

6993
if copy["tool"]["poetry"].get("packages") is None:
7094
copy["tool"]["poetry"].add("packages", [])
7195

72-
for package in packages:
96+
for package in to_sorted_packages(packages):
7397
copy["tool"]["poetry"]["packages"].append(package)
7498

7599
copy["tool"]["poetry"]["packages"].multiline(True)

projects/poetry_polylith_plugin/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "poetry-polylith-plugin"
3-
version = "1.52.2"
3+
version = "1.53.0"
44
description = "A Poetry plugin that adds tooling support for the Polylith Architecture"
55
authors = ["David Vujic"]
66
homepage = "https://davidvujic.github.io/python-polylith-docs/"

projects/polylith_cli/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "polylith-cli"
3-
version = "1.48.3"
3+
version = "1.49.0"
44
description = "Python tooling support for the Polylith Architecture"
55
authors = ['David Vujic']
66
homepage = "https://davidvujic.github.io/python-polylith-docs/"

test/components/polylith/sync/test_update.py

Lines changed: 93 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11
from pathlib import Path
2+
from typing import Union
23

34
import tomlkit
45
from polylith.sync import update
56

67

8+
def parse(data: Union[str, None]) -> dict:
9+
parsed = tomlkit.parse(data) if data else {}
10+
11+
return parsed or {}
12+
13+
714
def test_brick_to_pyproject_package():
815
ns = "unit_test"
916
brick = "greet"
@@ -70,75 +77,126 @@ def test_bricks_to_pyproject_packages():
7077
"components/hello/third": "hello/third",
7178
}
7279

80+
unsorted_packages = [
81+
{"include": "hello/c", "from": "components"},
82+
{"include": "hello/a", "from": "bases"},
83+
{"include": "hello/b", "from": "components"},
84+
]
85+
86+
expected_sorted_pep621_bricks = {
87+
"bases/hello/first": "hello/first",
88+
"bases/hello/a": "hello/a",
89+
"components/hello/b": "hello/b",
90+
"components/hello/c": "hello/c",
91+
}
7392

74-
def test_generate_updated_poetry_project():
75-
data = tomlkit.parse(
76-
"""\
93+
poetry_project_data = """\
7794
[tool.poetry]
7895
packages = [{include = "hello/first", from = "bases"}]
7996
8097
[build-system]
8198
requires = ["poetry-core>=1.0.0"]
8299
build-backend = "poetry.core.masonry.api"
83100
"""
84-
)
85101

86-
updated = update.generate_updated_project(data, packages[1:])
87-
88-
res = tomlkit.parse(updated)["tool"]["poetry"]["packages"]
89-
90-
assert res == packages
91102

92-
93-
def test_generate_updated_hatch_project_with_existing_polylith_sections():
94-
data = tomlkit.parse(
95-
"""\
103+
hatchling_build_system = """\
96104
[build-system]
97105
requires = ["hatchling"]
98106
build-backend = "hatchling.build"
107+
"""
108+
109+
hatchling_project_data = """\
110+
{build_system}
99111
100112
[tool.polylith.bricks]
101113
"bases/hello/first" = "hello/first"
102-
"""
103-
)
114+
""".format(
115+
build_system=hatchling_build_system
116+
)
117+
118+
hatch_specific_project_data = """\
119+
{build_system}
120+
121+
[tool.hatch.build.force-include]
122+
"bases/hello/first" = "hello/first"
123+
""".format(
124+
build_system=hatchling_build_system
125+
)
126+
127+
128+
def test_generate_updated_poetry_project():
129+
data = tomlkit.parse(poetry_project_data)
130+
131+
updated = update.generate_updated_project(data, packages[1:])
132+
133+
res = parse(updated)["tool"]["poetry"]["packages"]
134+
135+
assert res == packages
136+
137+
138+
def test_generate_updated_poetry_project_with_the_bricks_to_update_sorted():
139+
data = tomlkit.parse(poetry_project_data)
140+
141+
expected = [
142+
{"include": "hello/first", "from": "bases"},
143+
{"include": "hello/a", "from": "bases"},
144+
{"include": "hello/b", "from": "components"},
145+
{"include": "hello/c", "from": "components"},
146+
]
147+
148+
updated = update.generate_updated_project(data, unsorted_packages)
149+
150+
res = parse(updated)["tool"]["poetry"]["packages"]
151+
152+
assert res == expected
153+
154+
155+
def test_generate_updated_hatch_project_with_existing_polylith_sections():
156+
data = tomlkit.parse(hatchling_project_data)
104157

105158
updated = update.generate_updated_project(data, packages[1:])
106159

107-
res = tomlkit.parse(updated)["tool"]["polylith"]["bricks"]
160+
res = parse(updated)["tool"]["polylith"]["bricks"]
108161

109162
assert res == expected_hatch_packages
110163

111164

165+
def test_generate_updated_pep621_project_with_the_bricks_to_update_sorted():
166+
data = tomlkit.parse(hatchling_project_data)
167+
168+
updated = update.generate_updated_project(data, unsorted_packages)
169+
170+
res = parse(updated)["tool"]["polylith"]["bricks"]
171+
172+
assert list(res.keys()) == list(expected_sorted_pep621_bricks.keys())
173+
174+
112175
def test_generate_updated_hatch_project_with_missing_brick_config():
113-
data = tomlkit.parse(
114-
"""\
115-
[build-system]
116-
requires = ["hatchling"]
117-
build-backend = "hatchling.build"
118-
"""
119-
)
176+
data = tomlkit.parse(hatchling_build_system)
120177

121178
updated = update.generate_updated_project(data, packages)
122179

123-
res = tomlkit.parse(updated)["tool"]["polylith"]["bricks"]
180+
res = parse(updated)["tool"]["polylith"]["bricks"]
124181

125182
assert res == expected_hatch_packages
126183

127184

128185
def test_generate_updated_hatch_project_with_existing_force_include():
129-
data = tomlkit.parse(
130-
"""\
131-
[build-system]
132-
requires = ["hatchling"]
133-
build-backend = "hatchling.build"
134-
135-
[tool.hatch.build.force-include]
136-
"bases/hello/first" = "hello/first"
137-
"""
138-
)
186+
data = tomlkit.parse(hatch_specific_project_data)
139187

140188
updated = update.generate_updated_project(data, packages[1:])
141189

142-
res = tomlkit.parse(updated)["tool"]["hatch"]["build"]["force-include"]
190+
res = parse(updated)["tool"]["hatch"]["build"]["force-include"]
143191

144192
assert res == expected_hatch_packages
193+
194+
195+
def test_generate_updated_hatch_project_force_include_with_sorted_bricks():
196+
data = tomlkit.parse(hatch_specific_project_data)
197+
198+
updated = update.generate_updated_project(data, unsorted_packages)
199+
200+
res = parse(updated)["tool"]["hatch"]["build"]["force-include"]
201+
202+
assert list(res.keys()) == list(expected_sorted_pep621_bricks.keys())

0 commit comments

Comments
 (0)