compose2pod 0.4.2__tar.gz → 0.4.3__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.
@@ -1,8 +1,8 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: compose2pod
3
- Version: 0.4.2
3
+ Version: 0.4.3
4
4
  Summary: Convert a Docker Compose file into a script that runs its services as a single Podman pod
5
- Keywords: podman,docker-compose,compose,pod,ci,testing,containers
5
+ Keywords: podman,docker-compose,docker,containers,cli,python
6
6
  Author: Artur Shiriev
7
7
  Author-email: Artur Shiriev <me@shiriev.ru>
8
8
  License-Expression: MIT
@@ -13,11 +13,13 @@ Classifier: Programming Language :: Python :: 3.11
13
13
  Classifier: Programming Language :: Python :: 3.12
14
14
  Classifier: Programming Language :: Python :: 3.13
15
15
  Classifier: Programming Language :: Python :: 3.14
16
+ Classifier: Programming Language :: Python :: Free Threading :: 2 - Beta
16
17
  Classifier: Typing :: Typed
17
18
  Classifier: Topic :: Software Development :: Libraries
18
19
  Classifier: Topic :: Software Development :: Testing
19
20
  Requires-Dist: pyyaml>=6 ; extra == 'yaml'
20
21
  Requires-Python: >=3.10, <4
22
+ Project-URL: Homepage, https://modern-python.org
21
23
  Project-URL: Repository, https://github.com/modern-python/compose2pod
22
24
  Project-URL: Issues, https://github.com/modern-python/compose2pod/issues
23
25
  Project-URL: Changelog, https://github.com/modern-python/compose2pod/releases
@@ -55,9 +57,8 @@ Built for CI and test environments where you can't use `docker compose` or `podm
55
57
  ## Requirements
56
58
 
57
59
  compose2pod's generated scripts own `/etc/hosts`: they write it to a temp
58
- file and bind-mount it read-only into every container under `--no-hosts`
59
- (see `architecture/supported-subset.md`), so pod-internal name resolution
60
- works on any Podman version. `host.containers.internal` /
60
+ file and bind-mount it read-only into every container under `--no-hosts`, so
61
+ pod-internal name resolution works on any Podman version. `host.containers.internal` /
61
62
  `host.docker.internal` are not provided — add an explicit `extra_hosts`
62
63
  entry if you need them.
63
64
 
@@ -109,8 +110,8 @@ Compose extension fields (any `x-`-prefixed key) and YAML anchors are accepted
109
110
  as-is, so a top-level `x-*` anchor block for shared config just works.
110
111
  `${VAR}`-style variable interpolation is left live in the generated script,
111
112
  resolved by its shell against the environment present when the script runs (no
112
- `.env` file support). See `architecture/supported-subset.md` for the full
113
- accept/ignore/reject matrix and `planning/decisions/` for the boundary rulings.
113
+ `.env` file support). The boundary rulings — which forms are refused, and why —
114
+ are recorded in [`docs/adr/`](https://github.com/modern-python/compose2pod/tree/main/docs/adr/).
114
115
 
115
116
  ## Status
116
117
 
@@ -29,9 +29,8 @@ Built for CI and test environments where you can't use `docker compose` or `podm
29
29
  ## Requirements
30
30
 
31
31
  compose2pod's generated scripts own `/etc/hosts`: they write it to a temp
32
- file and bind-mount it read-only into every container under `--no-hosts`
33
- (see `architecture/supported-subset.md`), so pod-internal name resolution
34
- works on any Podman version. `host.containers.internal` /
32
+ file and bind-mount it read-only into every container under `--no-hosts`, so
33
+ pod-internal name resolution works on any Podman version. `host.containers.internal` /
35
34
  `host.docker.internal` are not provided — add an explicit `extra_hosts`
36
35
  entry if you need them.
37
36
 
@@ -83,8 +82,8 @@ Compose extension fields (any `x-`-prefixed key) and YAML anchors are accepted
83
82
  as-is, so a top-level `x-*` anchor block for shared config just works.
84
83
  `${VAR}`-style variable interpolation is left live in the generated script,
85
84
  resolved by its shell against the environment present when the script runs (no
86
- `.env` file support). See `architecture/supported-subset.md` for the full
87
- accept/ignore/reject matrix and `planning/decisions/` for the boundary rulings.
85
+ `.env` file support). The boundary rulings — which forms are refused, and why —
86
+ are recorded in [`docs/adr/`](https://github.com/modern-python/compose2pod/tree/main/docs/adr/).
88
87
 
89
88
  ## Status
90
89
 
@@ -58,7 +58,7 @@ def _depends_on_entry_condition(dep: str, spec: dict[str, Any]) -> str:
58
58
  # defaults to service_started -- that default is Docker's own and
59
59
  # stays. This one was compose2pod's own invention (`spec.get(...,
60
60
  # "service_started")`) and was a false green against the hard rule
61
- # in `planning/decisions/2026-07-14-docker-rejection-parity.md`.
61
+ # in `docs/adr/0006-docker-rejection-parity.md`.
62
62
  msg = f"depends_on entry {dep!r}: missing required key 'condition'"
63
63
  raise UnsupportedComposeError(msg)
64
64
  condition = spec["condition"]
@@ -34,9 +34,8 @@ def _validate_string_list(name: str, key: str, value: Any) -> None: # noqa: ANN
34
34
  raise UnsupportedComposeError(msg)
35
35
 
36
36
 
37
- # Accepted but never emitted -- meaningless inside a single shared-namespace pod
38
- # (see architecture/supported-subset.md). Ignored at *emit* is not unchecked at
39
- # the *gate*: a key compose2pod does not use is still one Docker validates, and a
37
+ # Accepted but never emitted -- meaningless inside a single shared-namespace pod.
38
+ # Ignored at *emit* is not unchecked at the *gate*: a key compose2pod does not use is still one Docker validates, and a
40
39
  # document carrying a malformed one is a document Docker will not run. Each value
41
40
  # is the shape validator; the content rules are Docker's own, measured -- it
42
41
  # accepts `restart: banana` and any `stop_signal` string, so neither is enumerated
@@ -866,8 +865,7 @@ def _sweep_service(name: str, svc: dict[str, Any]) -> None:
866
865
 
867
866
  Two of the service's own top-level keys are skipped rather than swept,
868
867
  because compose2pod never reads their contents: `build` (accepted, but
869
- `image_for` never reads its contents -- see
870
- architecture/supported-subset.md) and any `x-`-prefixed key (an
868
+ `image_for` never reads its contents) and any `x-`-prefixed key (an
871
869
  extension field whose value is arbitrary user payload, by design, same
872
870
  as everywhere else `x-` is skipped). `depends_on`/`networks`/`ulimits`
873
871
  are identifier-keyed and go through `_sweep_identifier_map` instead of
@@ -1053,7 +1051,7 @@ def _validate_network_driver_opts(name: str, key: str, value: Any) -> None: # n
1053
1051
  Measured against `docker compose config` v5.1.2: a bool/null/list entry
1054
1052
  value is refused ('must be a number or string') even though the key
1055
1053
  itself may be any string. compose2pod never reads a network entry's
1056
- `driver_opts` contents (see architecture/supported-subset.md), but a
1054
+ `driver_opts` contents, but a
1057
1055
  malformed value here is still a document Docker refuses.
1058
1056
  """
1059
1057
  if not isinstance(value, dict):
@@ -1,7 +1,7 @@
1
1
  """Value grammars: the shapes `docker compose config` accepts for a scalar key.
2
2
 
3
3
  compose2pod refuses every document Docker refuses
4
- (`planning/decisions/2026-07-14-docker-rejection-parity.md`), which means
4
+ (`docs/adr/0006-docker-rejection-parity.md`), which means
5
5
  matching Docker's *value* grammars, not just its types: `mem_limit: ""` and
6
6
  `cpus: somevalue` are documents Docker will not run.
7
7
 
@@ -0,0 +1,113 @@
1
+ [project]
2
+ name = "compose2pod"
3
+ description = "Convert a Docker Compose file into a script that runs its services as a single Podman pod"
4
+ requires-python = ">=3.10,<4"
5
+ license = "MIT"
6
+ readme = "README.md"
7
+ keywords = [
8
+ "podman",
9
+ "docker-compose",
10
+ "docker",
11
+ "containers",
12
+ "cli",
13
+ "python",
14
+ ]
15
+ classifiers = [
16
+ "Development Status :: 4 - Beta",
17
+ "Intended Audience :: Developers",
18
+ "Programming Language :: Python :: 3.10",
19
+ "Programming Language :: Python :: 3.11",
20
+ "Programming Language :: Python :: 3.12",
21
+ "Programming Language :: Python :: 3.13",
22
+ "Programming Language :: Python :: 3.14",
23
+ "Programming Language :: Python :: Free Threading :: 2 - Beta",
24
+ "Typing :: Typed",
25
+ "Topic :: Software Development :: Libraries",
26
+ "Topic :: Software Development :: Testing",
27
+ ]
28
+ version = "0.4.3"
29
+
30
+ [[project.authors]]
31
+ name = "Artur Shiriev"
32
+ email = "me@shiriev.ru"
33
+
34
+ [project.optional-dependencies]
35
+ yaml = ["PyYAML>=6"]
36
+
37
+ [project.scripts]
38
+ compose2pod = "compose2pod.cli:main"
39
+
40
+ [project.urls]
41
+ Homepage = "https://modern-python.org"
42
+ Repository = "https://github.com/modern-python/compose2pod"
43
+ Issues = "https://github.com/modern-python/compose2pod/issues"
44
+ Changelog = "https://github.com/modern-python/compose2pod/releases"
45
+
46
+ [build-system]
47
+ requires = ["uv_build>=0.11,<1.0"]
48
+ build-backend = "uv_build"
49
+
50
+ [tool.uv.build-backend]
51
+ module-name = "compose2pod"
52
+ module-root = ""
53
+
54
+ [tool.ruff]
55
+ fix = true
56
+ unsafe-fixes = true
57
+ line-length = 120
58
+
59
+ [tool.ruff.lint]
60
+ select = ["ALL"]
61
+ ignore = [
62
+ "D1",
63
+ "D203",
64
+ "D213",
65
+ "COM812",
66
+ "ISC001",
67
+ "CPY001",
68
+ "FBT",
69
+ "TCH",
70
+ ]
71
+
72
+ [tool.ruff.lint.isort]
73
+ lines-after-imports = 2
74
+ no-lines-before = [
75
+ "standard-library",
76
+ "local-folder",
77
+ ]
78
+
79
+ [tool.ruff.lint.per-file-ignores]
80
+ "tests/**" = ["S101"]
81
+
82
+ [tool.pytest.ini_options]
83
+ addopts = "-m 'not integration and not conformance'"
84
+ testpaths = ["tests"]
85
+ pythonpath = ["."]
86
+ markers = [
87
+ "integration: end-to-end tests that execute generated scripts against real podman (CI-only)",
88
+ "conformance: differential tests asserting compose2pod refuses whatever `docker compose config` refuses (CI-only; needs the docker CLI, no daemon)",
89
+ ]
90
+
91
+ [tool.coverage.report]
92
+ exclude_also = ["if typing.TYPE_CHECKING:"]
93
+ fail_under = 100
94
+
95
+ [tool.coverage.run]
96
+ omit = [
97
+ "compose2pod/__main__.py",
98
+ "tests/integration/*",
99
+ "tests/conformance/*",
100
+ ]
101
+
102
+ [dependency-groups]
103
+ dev = [
104
+ "pytest",
105
+ "pytest-cov",
106
+ ]
107
+ lint = [
108
+ "ruff",
109
+ "ty",
110
+ "eof-fixer",
111
+ "typing-extensions",
112
+ "types-PyYAML",
113
+ ]
@@ -5,7 +5,7 @@ authors = [{ name = "Artur Shiriev", email = "me@shiriev.ru" }]
5
5
  requires-python = ">=3.10,<4"
6
6
  license = "MIT"
7
7
  readme = "README.md"
8
- keywords = ["podman", "docker-compose", "compose", "pod", "ci", "testing", "containers"]
8
+ keywords = ["podman", "docker-compose", "docker", "containers", "cli", "python"]
9
9
  classifiers = [
10
10
  "Development Status :: 4 - Beta",
11
11
  "Intended Audience :: Developers",
@@ -14,11 +14,12 @@ classifiers = [
14
14
  "Programming Language :: Python :: 3.12",
15
15
  "Programming Language :: Python :: 3.13",
16
16
  "Programming Language :: Python :: 3.14",
17
+ "Programming Language :: Python :: Free Threading :: 2 - Beta",
17
18
  "Typing :: Typed",
18
19
  "Topic :: Software Development :: Libraries",
19
20
  "Topic :: Software Development :: Testing",
20
21
  ]
21
- version = "0.4.2"
22
+ version = "0.4.3"
22
23
 
23
24
  [project.optional-dependencies]
24
25
  yaml = ["PyYAML>=6"]
@@ -27,6 +28,7 @@ yaml = ["PyYAML>=6"]
27
28
  compose2pod = "compose2pod.cli:main"
28
29
 
29
30
  [project.urls]
31
+ Homepage = "https://modern-python.org"
30
32
  Repository = "https://github.com/modern-python/compose2pod"
31
33
  Issues = "https://github.com/modern-python/compose2pod/issues"
32
34
  Changelog = "https://github.com/modern-python/compose2pod/releases"
@@ -56,23 +58,25 @@ lint = [
56
58
  fix = true
57
59
  unsafe-fixes = true
58
60
  line-length = 120
59
- target-version = "py310"
60
61
 
61
62
  [tool.ruff.lint]
62
63
  select = ["ALL"]
63
64
  ignore = [
64
- "D1", # allow missing docstrings
65
- "S101", # allow asserts
66
- "TCH", # ignore flake8-type-checking
67
- "FBT", # allow boolean args
68
- "D203", # "one-blank-line-before-class" conflicting with D211
69
- "D213", # "multi-line-summary-second-line" conflicting with D212
70
- "COM812", # flake8-commas "Trailing comma missing"
71
- "ISC001", # flake8-implicit-str-concat
65
+ "D1", # docstrings are not forced; a docstring exists when it says something
66
+ "D203", # conflicts with D211
67
+ "D213", # conflicts with D212
68
+ "COM812", # conflicts with the formatter
69
+ "ISC001", # conflicts with the formatter
70
+ "CPY001", # no per-file copyright header
71
+ "FBT", # boolean positional arguments are fine
72
+ "TCH", # imports stay real; TYPE_CHECKING-only imports break runtime introspection
72
73
  ]
73
74
  isort.lines-after-imports = 2
74
75
  isort.no-lines-before = ["standard-library", "local-folder"]
75
76
 
77
+ [tool.ruff.lint.per-file-ignores]
78
+ "tests/**" = ["S101"]
79
+
76
80
  [tool.pytest.ini_options]
77
81
  addopts = "-m 'not integration and not conformance'"
78
82
  testpaths = ["tests"]
@@ -84,6 +88,7 @@ markers = [
84
88
 
85
89
  [tool.coverage]
86
90
  report.exclude_also = ["if typing.TYPE_CHECKING:"]
91
+ report.fail_under = 100
87
92
  # The __main__.py shim is a pure `sys.exit(main())` entrypoint exercised only via
88
93
  # subprocess (tests/test_cli.py::test_python_m_runs), which the parent coverage process
89
94
  # can't measure; omit it rather than enable subprocess/branch coverage machinery.