Skip to content

Commit 10bf175

Browse files
authored
Return flag tokens instead of mutating a list in place (#51)
* refactor(emit): return flag tokens instead of mutating a list in place The three _add_*_flags helpers were the only flag sources in the package that mutated a list passed in rather than returning list[Token] -- the convention KeySpec.emit, stores.flags, deploy_resource_flags, and pod.py's own helpers all follow. They sat beside three return-style calls in the same ten-line run_flags body. Behavior-preserving: the generated script is byte-identical. * docs(planning): correct the naming rationale in the flag-composition change file pod.py's _add_host_flags returns tokens while keeping the _add_ prefix, so the package is not uniform on this; say so rather than implying the prefix always tracks mutation.
1 parent be69334 commit 10bf175

2 files changed

Lines changed: 103 additions & 9 deletions

File tree

‎compose2pod/emit.py‎

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,9 @@ def entrypoint_tokens(svc: dict[str, Any]) -> list[Token]:
4747
return [Expand(value=str(token)) for token in entrypoint]
4848

4949

50-
def _add_health_flags(flags: list[Token], healthcheck: dict[str, Any]) -> None:
51-
"""Add healthcheck flags to the flags list."""
50+
def _health_flags(healthcheck: dict[str, Any]) -> list[Token]:
51+
"""Healthcheck flag tokens."""
52+
flags: list[Token] = []
5253
cmd = health_cmd(healthcheck.get("test"))
5354
if cmd is not None:
5455
flags += ["--health-cmd", Expand(value=cmd)]
@@ -58,10 +59,12 @@ def _add_health_flags(flags: list[Token], healthcheck: dict[str, Any]) -> None:
5859
flags += ["--health-start-period", str(healthcheck["start_period"])]
5960
if "retries" in healthcheck:
6061
flags += ["--health-retries", str(healthcheck["retries"])]
62+
return flags
6163

6264

63-
def _add_env_flags(flags: list[Token], svc: dict[str, Any], project_dir: str) -> None:
64-
"""Add -e and --env-file flags to the flags list."""
65+
def _env_flags(svc: dict[str, Any], project_dir: str) -> list[Token]:
66+
"""-e and --env-file flag tokens."""
67+
flags: list[Token] = []
6568
# A null environment value means "pass KEY through from the host" (bare `-e KEY`).
6669
for pair in key_value_pairs(svc.get("environment") or {}):
6770
flags += ["-e", Expand(value=str(pair))]
@@ -70,10 +73,12 @@ def _add_env_flags(flags: list[Token], svc: dict[str, Any], project_dir: str) ->
7073
env_files = [env_files]
7174
for env_file in env_files:
7275
flags += ["--env-file", Expand(value=str(Path(project_dir, env_file)))]
76+
return flags
7377

7478

75-
def _add_volume_flags(flags: list[Token], svc: dict[str, Any], project_dir: str) -> None:
76-
"""Add -v and --tmpfs flags to the flags list."""
79+
def _volume_flags(svc: dict[str, Any], project_dir: str) -> list[Token]:
80+
"""-v and --tmpfs flag tokens."""
81+
flags: list[Token] = []
7782
for volume in svc.get("volumes") or []:
7883
if ":" not in volume:
7984
# Anonymous volume: a bare container path, no host source to translate.
@@ -91,14 +96,15 @@ def _add_volume_flags(flags: list[Token], svc: dict[str, Any], project_dir: str)
9196
tmpfs = [tmpfs]
9297
for mount in tmpfs:
9398
flags += ["--tmpfs", Expand(value=mount)]
99+
return flags
94100

95101

96102
def run_flags(name: str, svc: dict[str, Any], pod: str, project_dir: str) -> list[Token]:
97103
"""Flag tokens (unquoted) for `podman run` of one service."""
98104
flags: list[Token] = ["--pod", pod, "--name", f"{pod}-{name}"]
99-
_add_env_flags(flags, svc, project_dir)
100-
_add_volume_flags(flags, svc, project_dir)
101-
_add_health_flags(flags, svc.get("healthcheck") or {})
105+
flags += _env_flags(svc, project_dir)
106+
flags += _volume_flags(svc, project_dir)
107+
flags += _health_flags(svc.get("healthcheck") or {})
102108
for key, spec in SERVICE_KEYS.items():
103109
if key in svc:
104110
flags += spec.emit(svc[key])
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
---
2+
summary: Convert emit.py's three mutate-in-place flag helpers (_add_env_flags, _add_volume_flags, _add_health_flags) to return list[Token], so all six flag sources composed by run_flags share the one convention every other flag source in the package already follows.
3+
---
4+
5+
# Change: Uniform flag composition in run_flags
6+
7+
**Lane:** lightweight — ≲30 LOC net, 1 file, no new file, no public-API
8+
change, no new test (behavior-preserving; existing tests are the safety net).
9+
10+
## Goal
11+
12+
`run_flags` (`emit.py`) composes six flag sources in ten lines using two
13+
different conventions: three helpers mutate a list passed in
14+
(`_add_env_flags`, `_add_volume_flags`, `_add_health_flags`, all `-> None`),
15+
while three return a list that gets concatenated (`spec.emit(...)`,
16+
`stores.flags(...)`, `deploy_resource_flags(...)`). Make all six uniform by
17+
converting the three mutators to return `list[Token]`.
18+
19+
The return convention is the established one everywhere else in the package —
20+
`KeySpec.emit`, `stores.flags`, `resources.deploy_resource_flags`, and all
21+
three of `pod.py`'s helpers (`_dns_flags`, `_sysctl_flags`, `_add_host_flags`,
22+
composed by `pod_create_flags` as `a + b + c`). These three `emit.py` helpers
23+
are the only exceptions in the package, and they sit directly beside three
24+
return-style calls in the same function, making the inconsistency maximally
25+
visible.
26+
27+
Behavior-preserving: no bug is closed and no risk is removed. This is a
28+
consistency fix, done because it is cheap and leaves one convention rather
29+
than two.
30+
31+
## Approach
32+
33+
Rename and re-shape the three helpers, keeping every body unchanged except for
34+
accumulating into a local list and returning it:
35+
36+
```python
37+
def _health_flags(healthcheck: dict[str, Any]) -> list[Token]:
38+
flags: list[Token] = []
39+
... # body unchanged
40+
return flags
41+
```
42+
43+
`run_flags` then reads uniformly:
44+
45+
```python
46+
def run_flags(name: str, svc: dict[str, Any], pod: str, project_dir: str) -> list[Token]:
47+
flags: list[Token] = ["--pod", pod, "--name", f"{pod}-{name}"]
48+
flags += _env_flags(svc, project_dir)
49+
flags += _volume_flags(svc, project_dir)
50+
flags += _health_flags(svc.get("healthcheck") or {})
51+
for key, spec in SERVICE_KEYS.items():
52+
if key in svc:
53+
flags += spec.emit(svc[key])
54+
flags += stores.flags(svc, pod)
55+
flags += deploy_resource_flags(svc)
56+
return flags
57+
```
58+
59+
Emitted flag order is unchanged — each helper's tokens still land in the same
60+
position, since `+=` at the same call site appends the same tokens the
61+
in-place mutation did.
62+
63+
The helpers are renamed to `_env_flags`/`_volume_flags`/`_health_flags`, since
64+
the `_add_*` prefix described the side effect that is going away. This matches
65+
`pod.py`'s `_dns_flags`/`_sysctl_flags`; note `pod.py` also has an
66+
`_add_host_flags` that returns tokens while keeping the prefix, so the package
67+
is not uniform on this — the naming here follows the majority, and no rename
68+
outside this file is in scope.
69+
70+
## Files
71+
72+
- `compose2pod/emit.py` — three helpers converted from
73+
`(flags, ...) -> None` to `(...) -> list[Token]` and renamed; `run_flags`
74+
updated to concatenate them.
75+
76+
No test changes: all three helpers are private with one caller each, reached
77+
only through `run_flags`, which existing tests cover thoroughly
78+
(`TestRunFlags`, plus the integration suite executing real generated scripts).
79+
80+
## Verification
81+
82+
- [ ] No failing-test-first step — this is a behavior-preserving refactor with
83+
no new behavior to assert. The existing suite is the safety net: it must
84+
stay green, unchanged, at 100% coverage.
85+
- [ ] Apply the change.
86+
- [ ] `just test-ci` — 407 tests green (unchanged count), 100% line coverage.
87+
- [ ] `just lint-ci` — clean.
88+
- [ ] `just check-planning` — OK.

0 commit comments

Comments
 (0)