openapi-client-codegen 0.1.0__tar.gz → 0.1.2__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 (42) hide show
  1. openapi_client_codegen-0.1.2/.gitattributes +1 -0
  2. {openapi_client_codegen-0.1.0 → openapi_client_codegen-0.1.2}/.github/workflows/ci.yml +4 -1
  3. {openapi_client_codegen-0.1.0 → openapi_client_codegen-0.1.2}/AGENTS.md +8 -5
  4. {openapi_client_codegen-0.1.0 → openapi_client_codegen-0.1.2}/PKG-INFO +1 -1
  5. openapi_client_codegen-0.1.2/README.md +74 -0
  6. {openapi_client_codegen-0.1.0 → openapi_client_codegen-0.1.2}/pyproject.toml +1 -1
  7. openapi_client_codegen-0.1.2/ruff.base.toml +204 -0
  8. openapi_client_codegen-0.1.2/ruff.toml +1 -0
  9. {openapi_client_codegen-0.1.0 → openapi_client_codegen-0.1.2}/src/openapi_client_codegen/cli.py +38 -0
  10. {openapi_client_codegen-0.1.0 → openapi_client_codegen-0.1.2}/src/openapi_client_codegen/client.py +1 -1
  11. openapi_client_codegen-0.1.2/src/openapi_client_codegen/orchestrator.py +95 -0
  12. openapi_client_codegen-0.1.2/src/openapi_client_codegen/py.typed +0 -0
  13. {openapi_client_codegen-0.1.0 → openapi_client_codegen-0.1.2}/src/openapi_client_codegen/templates.py +1 -1
  14. {openapi_client_codegen-0.1.0 → openapi_client_codegen-0.1.2}/tests/test_downgrade.py +1 -1
  15. {openapi_client_codegen-0.1.0 → openapi_client_codegen-0.1.2}/tests/test_naming.py +1 -1
  16. openapi_client_codegen-0.1.2/tests/test_orchestrate.py +157 -0
  17. {openapi_client_codegen-0.1.0 → openapi_client_codegen-0.1.2}/tests/test_unity.py +1 -1
  18. openapi_client_codegen-0.1.0/README.md +0 -73
  19. openapi_client_codegen-0.1.0/ruff.toml +0 -71
  20. openapi_client_codegen-0.1.0/src/openapi_client_codegen/__init__.py +0 -15
  21. {openapi_client_codegen-0.1.0 → openapi_client_codegen-0.1.2}/.gitignore +0 -0
  22. {openapi_client_codegen-0.1.0 → openapi_client_codegen-0.1.2}/.python-version +0 -0
  23. {openapi_client_codegen-0.1.0 → openapi_client_codegen-0.1.2}/CLAUDE.md +0 -0
  24. {openapi_client_codegen-0.1.0 → openapi_client_codegen-0.1.2}/LICENSE +0 -0
  25. {openapi_client_codegen-0.1.0 → openapi_client_codegen-0.1.2}/NOTICE +0 -0
  26. {openapi_client_codegen-0.1.0 → openapi_client_codegen-0.1.2}/publish-config.json +0 -0
  27. /openapi_client_codegen-0.1.0/src/openapi_client_codegen/py.typed → /openapi_client_codegen-0.1.2/src/openapi_client_codegen/__init__.py +0 -0
  28. {openapi_client_codegen-0.1.0 → openapi_client_codegen-0.1.2}/src/openapi_client_codegen/_data/configs/csharp.json +0 -0
  29. {openapi_client_codegen-0.1.0 → openapi_client_codegen-0.1.2}/src/openapi_client_codegen/_data/configs/python.json +0 -0
  30. {openapi_client_codegen-0.1.0 → openapi_client_codegen-0.1.2}/src/openapi_client_codegen/_data/openapi-generator-ignore +0 -0
  31. {openapi_client_codegen-0.1.0 → openapi_client_codegen-0.1.2}/src/openapi_client_codegen/_data/templates-patches/csharp/0001-csharp-remove-errant-comma.patch +0 -0
  32. {openapi_client_codegen-0.1.0 → openapi_client_codegen-0.1.2}/src/openapi_client_codegen/_data/templates-patches/csharp/0002-csharp-models-required-only-constructors.patch +0 -0
  33. {openapi_client_codegen-0.1.0 → openapi_client_codegen-0.1.2}/src/openapi_client_codegen/_data/templates-patches/csharp/0003-csharp-httpclient-use-defaultT.patch +0 -0
  34. {openapi_client_codegen-0.1.0 → openapi_client_codegen-0.1.2}/src/openapi_client_codegen/_data/templates-patches/csharp/0004-csharp-support-hybrid-multipart-payloads.patch +0 -0
  35. {openapi_client_codegen-0.1.0 → openapi_client_codegen-0.1.2}/src/openapi_client_codegen/_data/templates-patches/csharp/0006-csharp-streaming-download-response.patch +0 -0
  36. {openapi_client_codegen-0.1.0 → openapi_client_codegen-0.1.2}/src/openapi_client_codegen/_data/templates-patches/csharp/0007-csharp-model-default-initializers.patch +0 -0
  37. {openapi_client_codegen-0.1.0 → openapi_client_codegen-0.1.2}/src/openapi_client_codegen/_data/templates-patches/csharp/0008-csharp-redirect-documentation-file.patch +0 -0
  38. {openapi_client_codegen-0.1.0 → openapi_client_codegen-0.1.2}/src/openapi_client_codegen/downgrade.py +0 -0
  39. {openapi_client_codegen-0.1.0 → openapi_client_codegen-0.1.2}/src/openapi_client_codegen/naming.py +0 -0
  40. {openapi_client_codegen-0.1.0 → openapi_client_codegen-0.1.2}/src/openapi_client_codegen/resources.py +0 -0
  41. {openapi_client_codegen-0.1.0 → openapi_client_codegen-0.1.2}/src/openapi_client_codegen/unity.py +0 -0
  42. {openapi_client_codegen-0.1.0 → openapi_client_codegen-0.1.2}/uv.lock +0 -0
@@ -0,0 +1 @@
1
+ ruff.base.toml -text
@@ -32,6 +32,9 @@ jobs:
32
32
  - name: Test
33
33
  run: uv run pytest
34
34
 
35
+ - name: Ruff config drift
36
+ run: uvx --from python-devkit==0.1.3 sync-ruff --check
37
+
35
38
  publish:
36
39
  needs: check
37
40
  if: github.event_name == 'push' && github.ref == 'refs/heads/main'
@@ -48,4 +51,4 @@ jobs:
48
51
  - uses: astral-sh/setup-uv@v7
49
52
 
50
53
  - name: Publish
51
- run: uvx --from git+https://github.com/outernet-foundation/pubpkg.git@ea2ecf35a93a3c6405b9430a651d2a7b3d2faa6c publish-packages --config publish-config.json
54
+ run: uvx --from release-kit==0.1.0 --with bashrun==0.1.0 publish-packages --config publish-config.json
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## What this is
4
4
 
5
- `openapi-client-codegen` wraps [OpenAPI Generator](https://openapi-generator.tech/) to turn an OpenAPI schema into typed client packages — a Python (`httpx`) client and a Unity-consumable C# (`httpclient`) client. It owns every step that is generic across consumers: downgrading 3.1→3.0, authoring and patching the C# templates, running the generator, and writing the Unity package metadata. The caller owns what is inherently project-specific: **producing the OpenAPI spec** (however its app exposes one), which projects map to which clients, the package-name policy, and the output paths.
5
+ `openapi-client-codegen` wraps [OpenAPI Generator](https://openapi-generator.tech/) to turn an OpenAPI schema into typed client packages — a Python (`httpx`) client and a Unity-consumable C# (`httpclient`) client. It owns every step that is generic across consumers: the project orchestration loop, downgrading 3.1→3.0, the committed-spec unchanged skip, authoring and patching the C# templates, running the generator, and writing the Unity package metadata. The caller owns what is inherently project-specific: **producing the OpenAPI spec** (injected as a required strategy — any callable from project directory to raw spec JSON; the org's uv convention ships as `dump_openapi_spec`), the project→client mapping, the naming root, the npm scope, license, repository URL, and the generated output root.
6
6
 
7
7
  The package is `openapi_client_codegen` (src-layout under `src/openapi_client_codegen/`), renamed from `openapi-clientgen` before the first publish (operator, 2026-09-20 — no artifact carries the old name). Its Python dependencies are `bashrun` (the guardrailed subprocess wrapper) and `typer` (the thin CLI), both from PyPI — git-source pins only in scratch branches testing unreleased changes. At **runtime** it also needs **Java (JDK 11+)** and **`uvx`** on PATH — the generator itself runs as `uvx --from 'openapi-generator-cli[jdk4py]==<pin>' ...`.
8
8
 
@@ -12,17 +12,20 @@ Publishing rides `ci.yml`'s `publish` job on every push to `main` (gated on the
12
12
 
13
13
  ## Public API
14
14
 
15
- `__init__` re-exports the surface; import from the package root.
15
+ The package init is empty (RUF067 strict mode — the canonical ruff config bans any content in `__init__.py`); import each symbol from its defining module: `client` (`generate_client`), `downgrade` (`downgrade_openapi_3_1_to_3_0`, `JsonDict`), `naming` (`ClientNaming`, `DefaultNamingPolicy`, `NamingPolicy`), `orchestrator` (`generate_projects`, `dump_openapi_spec`, `SpecProducer`), `templates` (`regenerate_templates`), `unity` (`write_unity_package_metadata`).
16
16
 
17
17
  | Symbol | Role |
18
18
  |---|---|
19
19
  | `downgrade_openapi_3_1_to_3_0(schema)` | In-place 3.1.0→3.0.3 fixup (openapi-generator rejects 3.1). Pure function. |
20
+ | `generate_projects(projects, root_name, generated_root, dump_spec, npm_scope=None, license_spdx=None, repository_url=None, project=None, client=None, no_cache=False, root=Path())` | The project orchestrator: per mapping entry (`{project path: [generators]}`), produce the spec via the injected `dump_spec` strategy, downgrade it, write `<project>/openapi.json` unless byte-identical to the committed spec (`no_cache` forces through), and `generate_client` per listed generator into `<generated_root>/<generator>/<names.base>`. `project` / `client` restrict the run to one entry; `root` is what project paths resolve against. |
21
+ | `SpecProducer(command)` | Callable class wrapping any shell command that prints the spec to stdout, run with the project directory as cwd; the CLI `--spec-command` bridge. The strategy contract itself is untyped vocabulary — any `Callable[[Path], str]` (project directory in, raw OpenAPI JSON out) satisfies `dump_spec`. |
22
+ | `dump_openapi_spec(project)` | The org strategy: `uv run --project . python -m src.dump_openapi` with `CODEGEN=1` gating heavy imports (per-call env overlay). Shipped as the exemplar — `dump_spec` is required, so every consumer names its strategy explicitly. |
20
23
  | `regenerate_templates(target_dir, extra_patches_dir=None)` | Author the C# templates into `target_dir/csharp` and apply the shipped (then optional extra) patches. |
21
24
  | `generate_client(spec, generator, output_dir, names, templates_dir=None, extra_references=None, npm_scope=None, license_spdx=None, repository_url=None)` | Run the generator for one `(spec, generator)` and sync the result into `output_dir`. `templates_dir` is required for `csharp`. For csharp, `npm_scope` composes the UPM identity `{scope}.{base-minus-dashes}` (e.g. `org.outernet.placeframe.apiclient`); unset falls back to the legacy `org.nuget.{camel-lower}` identity. `license_spdx` / `repository_url` populate the manifest's `license` / `repository` fields when given. |
22
25
  | `write_unity_package_metadata(package_dir, package_name, extra_references=None, npm_name=None, license_spdx=None, repository_url=None)` | Write `package.json` / `.asmdef` / `csc.rsp` / `Directory.Build.props`. Called by `generate_client` for csharp. |
23
26
  | `DefaultNamingPolicy(root_name)` / `ClientNaming` / `NamingPolicy` | Package-name derivation. `DefaultNamingPolicy("placeframe")("docker/api")` → base `api-client`, dashed `placeframe-api-client`, underscored `placeframe_api_client`, camel `PlaceframeApiClient`. |
24
27
 
25
- A consumer orchestrates: `regenerate_templates(tmp)` once, then per `(project, client)` produce the spec, `downgrade` it, and call `generate_client`. The generator version pin and the shipped configs/patches/ignore-file live in `src/openapi_client_codegen/_data/` (paths in `_data.py`).
28
+ `generate_projects` orchestrates the whole run — `regenerate_templates` once, then per project: produce (via the injected strategy) → downgrade → committed-spec skip → per-client generate. Consumers hand it their parsed mapping, their spec-production strategy, and their identity parameters. The generator version pin and the shipped configs/patches/ignore-file live in `src/openapi_client_codegen/_data/` (paths in `_data.py`).
26
29
 
27
30
  ## Constraints
28
31
 
@@ -34,7 +37,7 @@ To author a new patch: author the raw templates (`openapi-generator-cli author t
34
37
 
35
38
  ### Generator env vars are passed per-call, never set at module level
36
39
 
37
- `JAVA_OPTS=-Dlog.level=warn` (quiets the generator) is passed as `bash(..., env={"JAVA_OPTS": "-Dlog.level=warn"})` on the two `openapi-generator-cli` calls that need it, so it reaches only those child processes and never touches this process's `os.environ`. `bashrun`'s `env=` overlays the inherited environment for that one child (see its AGENTS.md), which is the whole point — the alternative, mutating the global `os.environ`, leaks the var into every later subprocess. This covers only the generator this package runs itself; a consumer's spec-production step owns its own environment (e.g. any import-gating flag its services read).
40
+ `JAVA_OPTS=-Dlog.level=warn` (quiets the generator) is passed as `bash(..., env={"JAVA_OPTS": "-Dlog.level=warn"})` on the two `openapi-generator-cli` calls that need it, so it reaches only those child processes and never touches this process's `os.environ`. `bashrun`'s `env=` overlays the inherited environment for that one child (see its AGENTS.md), which is the whole point — the alternative, mutating the global `os.environ`, leaks the var into every later subprocess. This covers only the generator this package runs itself. The shipped `dump_openapi_spec` strategy follows the same rule: `CODEGEN=1` (which gates a service's heavy imports so the app imports cleanly for the dump) rides bashrun's per-call env overlay and never touches this process's `os.environ`.
38
41
 
39
42
  ### The Unity reference set is coupled to the patched templates
40
43
 
@@ -48,7 +51,7 @@ The generated `.csproj` stays inside the UPM package because it is the `dotnet p
48
51
 
49
52
  ### What the consumer owns
50
53
 
51
- The project→clients mapping, the naming policy, and the output paths are the consumer's. This package ships `DefaultNamingPolicy` as a convenience but defines no schema for the mapping file — the consumer iterates its own config and calls the API per entry. `NamingPolicy` is a `Protocol`; a consumer can supply any callable `(project: str) -> ClientNaming`.
54
+ The spec-production strategy (`dump_spec`, required — the package is consumable by projects that use no Python, no uv, or no dump entry point at all; the org's `dump_openapi_spec` ships beside it, and a project whose spec is a committed file needs only a file read), the project→clients mapping (in whatever config shape it likes — the verb takes the parsed mapping, not a file path), the naming root, the npm scope, license, repository URL, and the generated output root. The orchestrator iterates the mapping itself and fixes `DefaultNamingPolicy`; a consumer needing a different policy calls the per-client API directly — `NamingPolicy` is a `Protocol`, any callable `(project: str) -> ClientNaming` works there.
52
55
 
53
56
  ## See also
54
57
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: openapi-client-codegen
3
- Version: 0.1.0
3
+ Version: 0.1.2
4
4
  Summary: OpenAPI client-generation wrapper: Litestar spec dump, 3.1->3.0 downgrade, patched openapi-generator templates, Unity package metadata
5
5
  License-File: LICENSE
6
6
  License-File: NOTICE
@@ -0,0 +1,74 @@
1
+ # openapi-client-codegen
2
+
3
+ Generate typed API clients from an OpenAPI schema. `openapi-client-codegen` wraps [OpenAPI Generator](https://openapi-generator.tech/): it downgrades OpenAPI 3.1→3.0 (the generator only accepts 3.0), authors and patches the C# templates, runs the generator, and writes Unity package metadata. It produces a Python (`httpx`) client and a Unity-consumable C# (`httpclient`) client.
4
+
5
+ The generic steps live here; the caller supplies the spec-production strategy (any callable from project directory to raw spec JSON — the org's uv convention ships as `dump_openapi_spec`), the project→client mapping, the naming root, the npm scope, license, repository URL, and the output root. See [`AGENTS.md`](./AGENTS.md) for the API surface, the lib-owns/consumer-owns boundary, and the patch mechanism.
6
+
7
+ ## Requirements
8
+
9
+ - Python 3.13+ and [uv](https://docs.astral.sh/uv/)
10
+ - **Java (JDK 11+)** and **`uvx`** on PATH at runtime — the generator runs as `uvx --from 'openapi-generator-cli[jdk4py]==<pin>' ...`
11
+
12
+ ## CLI
13
+
14
+ A thin CLI exposes the OpenAPI 3.1→3.0 downgrade, single-client generation, and the multi-project orchestrator:
15
+
16
+ ```bash
17
+ uv run openapi-client-codegen downgrade path/to/openapi.json # 3.1→3.0 in place
18
+ uv run openapi-client-codegen downgrade path/to/openapi.json out.json # write elsewhere
19
+
20
+ uv run openapi-client-codegen generate path/to/openapi.json generated/csharp/api-client \
21
+ --root-name myproject --project services/api \
22
+ --npm-scope org.example.myproject --license-spdx Apache-2.0 \
23
+ --repository-url https://github.com/org/repo.git
24
+
25
+ uv run openapi-client-codegen generate-projects clients.json \
26
+ --spec-command 'uv run --project . python -m src.dump_openapi' \
27
+ --root-name myproject --generated-root generated \
28
+ --npm-scope org.example.myproject --license-spdx Apache-2.0 \
29
+ --repository-url https://github.com/org/repo.git
30
+ ```
31
+
32
+ `clients.json` maps each project path to its client generators (`{"services/api": ["python", "csharp"]}`). `--spec-command` names the strategy: a shell command that prints the project's raw OpenAPI JSON to stdout, run with the project directory as cwd (env prefixes like `CODEGEN=1 …` inline directly). The orchestrator downgrades each produced spec, skips generation when the committed `<project>/openapi.json` is byte-identical (`--no-cache` forces through), and syncs each client under `<generated-root>/<generator>/<base-name>`.
33
+
34
+ The same surface is the library API, for in-process consumption from a consumer's own command — `dump_spec` is required, so every consumer names its strategy explicitly:
35
+
36
+ ```python
37
+ import json
38
+ from pathlib import Path
39
+
40
+ from openapi_client_codegen.orchestrator import dump_openapi_spec, generate_projects
41
+
42
+ projects = json.loads(Path("clients.json").read_text(encoding="utf-8"))
43
+ generate_projects(
44
+ projects,
45
+ root_name="myproject",
46
+ generated_root=Path("generated"),
47
+ dump_spec=dump_openapi_spec,
48
+ npm_scope="org.example.myproject",
49
+ license_spdx="Apache-2.0",
50
+ repository_url="https://github.com/org/repo.git",
51
+ )
52
+ ```
53
+
54
+ A non-uv project passes its own callable instead — e.g. `dump_spec=lambda project: bash_output("dotnet run -- dump-openapi", cwd=project)`, or a file read for a project whose spec is committed rather than dumped.
55
+
56
+ ## Consuming from another repo
57
+
58
+ Install from PyPI:
59
+
60
+ ```toml
61
+ [project]
62
+ dependencies = ["openapi-client-codegen>=0.1.0"]
63
+ ```
64
+
65
+ `bashrun` resolves transitively. At **runtime** the generator also needs Java (JDK 11+) and `uvx` on PATH. To test an unreleased change, pin the repo at a git ref in a scratch branch instead (`openapi-client-codegen = { git = "…", rev = "<sha>" }` under `[tool.uv.sources]`) and drop the pin when the release lands.
66
+
67
+ ## Development
68
+
69
+ ```bash
70
+ uv run ruff check .
71
+ uv run ruff format --check .
72
+ uv run basedpyright
73
+ uv run pytest
74
+ ```
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "openapi-client-codegen"
3
- version = "0.1.0"
3
+ version = "0.1.2"
4
4
  description = "OpenAPI client-generation wrapper: Litestar spec dump, 3.1->3.0 downgrade, patched openapi-generator templates, Unity package metadata"
5
5
  requires-python = ">=3.13"
6
6
  dependencies = [
@@ -0,0 +1,204 @@
1
+ # Org-canonical ruff configuration, hosted in python-devkit.
2
+ # Consuming repos carry this file verbatim as ruff.base.toml, written by the
3
+ # sync-ruff verb (uvx --from python-devkit sync-ruff) and drift-gated in CI.
4
+ # Never edit a synced copy: change this canonical, release python-devkit, and
5
+ # re-run sync-ruff in each consuming repo.
6
+ target-version = "py313"
7
+ line-length = 120
8
+ preview = true
9
+ required-version = ">=0.14.11"
10
+
11
+ [lint]
12
+ select = ["ALL"]
13
+ ignore = [
14
+ # ── Temporarily ignored (inline noqas exist for these rules but are redundant
15
+ # while the rules are globally ignored — RUF100 suppresses the "unused noqa" warning
16
+ # until we un-ignore each ticket's rules)
17
+ "RUF100",
18
+
19
+ # ── Permanently ignored ──────────────────────────────────────────────
20
+ # Type annotations — basedpyright strict mode handles this
21
+ "ANN",
22
+ # EM101 (string literal in exception) — the msg = ... indirection adds verbosity without benefit
23
+ "EM101",
24
+ # TRY003 (raise vanilla args) — requires custom exception class for every raise site;
25
+ # the real problem (interpolation) is caught by EM102/EM103
26
+ "TRY003",
27
+ # A003 (class attribute shadowing) — class attributes via self.x never conflict with builtins
28
+ "A003",
29
+ # Function complexity counting — C901 already covers structural complexity;
30
+ # these counting rules are largely redundant
31
+ "PLR0911",
32
+ "PLR0912",
33
+ "PLR0913",
34
+ "PLR0914",
35
+ "PLR0915",
36
+ "PLR0917",
37
+
38
+ # ── Temporarily ignored (will enable when conventions are established) ─
39
+ # Docstrings — project policy is "no docstrings" for now
40
+ "D",
41
+ # Copyright notices — not yet established
42
+ "CPY",
43
+ # Docstring formatting — blocked on D
44
+ "DOC",
45
+ # TODO/FIXME comment formatting
46
+ "TD",
47
+ "FIX",
48
+
49
+ # The "Ticket N" blocks below are the transitional cleanup slices tracked
50
+ # by PLE-248 (Enforce the deferred ruff rules):
51
+ # https://linear.app/plerionplatforms/issue/PLE-248
52
+ # Ticket 3 (T201, print → logging) is tracked by PLE-336:
53
+ # https://linear.app/plerionplatforms/issue/PLE-336
54
+ # Lifting a rule = remove it here in python-devkit's canonical config,
55
+ # release, and run sync-ruff in every consuming repo.
56
+
57
+ # ── Ticket 1: Auto-fix formatting & type modernization ───────────────
58
+ # Zero risk. Purely mechanical, all auto-fixable via ruff --fix.
59
+ "COM812", # missing trailing commas
60
+ "I001", # import sorting
61
+ "ISC003", # explicit string concatenation
62
+ "RUF031", # parenthesized tuple in subscript
63
+ "TC001", # move import into TYPE_CHECKING block
64
+ "TC002", # move third-party import into TYPE_CHECKING block
65
+ "TC003", # move stdlib import into TYPE_CHECKING block
66
+ "TC006", # move typing import into TYPE_CHECKING block (PEP 695)
67
+ "TC008", # move import into TYPE_CHECKING block (quoted annotation)
68
+ "UP006", # use X instead of typing.X
69
+ "UP007", # use X | Y instead of Union
70
+ "UP011", # unnecessary parentheses to functools.lru_cache
71
+ "UP015", # unnecessary open mode parameters
72
+ "UP017", # use datetime.UTC alias
73
+ "UP034", # extraneous parentheses
74
+ "UP035", # deprecated import (use collections.abc, etc.)
75
+ "UP040", # use PEP 695 type alias
76
+ "UP042", # use PEP 695 type parameters
77
+ "UP045", # use X | None instead of Optional
78
+
79
+ # ── Ticket 2: Auto-fix code simplifications ──────────────────────────
80
+ # Very low risk. Auto-fixable with trivial semantic changes.
81
+ "C401", # unnecessary generator — use set/dict comprehension
82
+ "C416", # unnecessary comprehension — use list/dict
83
+ "ERA001", # commented-out code
84
+ "FURB101", # read-text can replace open+read
85
+ "FURB103", # write-text can replace open+write
86
+ "FURB110", # use ternary instead of try/except for default
87
+ "FURB113", # use list.extend instead of repeated append
88
+ "FURB118", # use operator module instead of lambda
89
+ "LOG004", # use __name__ as logger name
90
+ "PERF401", # use list comprehension instead of manual append
91
+ "PIE810", # unnecessary spread of single-element starred
92
+ "PLC0207", # use a single __all__ definition
93
+ "PLC1901", # compare to empty string using falsy check
94
+ "PLR1711", # useless return at end of function
95
+ "PLR6104", # use augmented assignment
96
+ "PLR6201", # use a set literal for membership test
97
+ "PLW1510", # subprocess.run without explicit check= argument
98
+ "PLW1514", # open without explicit encoding
99
+ "PT001", # use @pytest.fixture over @pytest.fixture()
100
+ "PT018", # composite assertion — split into multiple asserts
101
+ "PTH101", # os.chmod → Path.chmod
102
+ "PTH111", # os.path.expanduser → Path.expanduser
103
+ "PTH112", # os.path.isdir → Path.is_dir
104
+ "PTH118", # os.path.join → Path /
105
+ "PTH123", # open() → Path.open()
106
+ "PTH201", # pathlib.Path(".")
107
+ "RET501", # unnecessary explicit return None
108
+ "RET504", # unnecessary assignment before return
109
+ "RET505", # unnecessary else after return
110
+ "RET506", # unnecessary else after raise
111
+ "RET507", # unnecessary else after continue
112
+ "RSE102", # unnecessary parentheses on raised exception
113
+ "RUF005", # consider spread operator instead of concatenation
114
+ "RUF010", # use explicit conversion flag in f-string
115
+ "RUF015", # prefer next() over single-element slice
116
+ "RUF029", # function is declared async but has no await
117
+ "RUF052", # local variable is assigned but never used (underscore prefix)
118
+ "RUF056", # unnecessary default value for keyword argument
119
+ "SIM102", # collapsible if statements
120
+ "SIM105", # use contextlib.suppress instead of try/except/pass
121
+ "SIM115", # use context manager for opening files
122
+ "SIM117", # use single with statement for multiple context managers
123
+ "SIM118", # use key in dict instead of key in dict.keys()
124
+ "TID252", # prefer absolute imports over relative
125
+
126
+ # ── Ticket 3: print → logging ────────────────────────────────────────
127
+ # All code should use logging, including CLI scripts.
128
+ "T201", # print found
129
+
130
+ # ── Ticket 4: Exception message interpolation ────────────────────────
131
+ # Remove f-strings from exception messages — use string literals.
132
+ "EM102", # f-string in exception message
133
+
134
+ # ── Ticket 5: Missing __init__.py + shebangs ─────────────────────────
135
+ # Add __init__.py to installable packages; remove vestigial shebangs.
136
+ "INP001", # implicit namespace package — add __init__.py
137
+ "EXE001", # shebang present but file not executable
138
+
139
+ # ── Ticket 6: Type ignore specificity ────────────────────────────────
140
+ # Replace blanket type: ignore with specific error codes.
141
+ "PGH003", # blanket type: ignore
142
+
143
+ # ── Ticket 7: Boolean trap refactoring ───────────────────────────────
144
+ # Change function signatures — callers must update too.
145
+ "FBT001", # boolean positional argument
146
+ "FBT002", # boolean default value in function definition
147
+ "FBT003", # boolean positional value in call
148
+
149
+ # ── Ticket 8: Exception handling patterns ────────────────────────────
150
+ # Behavioral changes to error handling.
151
+ "B020", # loop variable overwritten by except
152
+ "B903", # generator should use yield from
153
+ "B904", # raise without from inside except
154
+ "B905", # zip() without strict= parameter
155
+ "BLE001", # blind except Exception
156
+ "S101", # assert outside tests
157
+ "TRY002", # create custom exception class
158
+ "TRY300", # try/except/return — move return into else
159
+
160
+ # ── Ticket 9: Complexity, naming & refactoring ───────────────────────
161
+ # Structural changes requiring judgment.
162
+ "A001", # builtin shadowing (dir, bytes)
163
+ "ARG001", # unused function argument
164
+ "ARG002", # unused method argument
165
+ "C901", # function too complex
166
+ "E501", # line too long
167
+ "N802", # function name should be lowercase
168
+ "N806", # variable in function should be lowercase
169
+ "N815", # mixed-case variable in class scope
170
+ "N818", # exception name should end in Error
171
+ "PLC0415", # import not at top level
172
+ "PLE0704", # bare raise not inside except handler
173
+ "PLR1702", # too many nested blocks
174
+ "PLR2004", # magic value used in comparison
175
+ "PLR6301", # method could be function or static method
176
+ "PLW0603", # global statement
177
+ "PLW2901", # loop variable overwritten
178
+ "RUF001", # ambiguous unicode — replace with ASCII
179
+ "RUF003", # ambiguous unicode in comment
180
+ "S106", # possible hardcoded password
181
+ "S108", # probable insecure temp file/dir usage
182
+ "S110", # try/except/pass on broad exception
183
+ "S202", # use of tarfile.extractall
184
+ # S404/S603: prerequisite — bash.py needs env, stderr=STDOUT merge, and no-raise-with-capture
185
+ # before context_sha.py, deptry_check.py, test_context_sha.py, list_debug_targets.py can migrate
186
+ "S404", # suspicious subprocess import
187
+ "S603", # subprocess call with non-literal
188
+ "S606", # starting process without shell
189
+ "S607", # starting process with partial path
190
+ ]
191
+
192
+ [lint.per-file-ignores]
193
+ # Tests use assert as the standard pytest mechanism; fixtures take arguments
194
+ # that pytest consumes implicitly; pytest-style test classes need no self use
195
+ "**/tests/**" = ["S101", "ARG001", "ARG002", "PLR6301"]
196
+
197
+ [lint.ruff]
198
+ # RUF067 in strict mode: __init__.py files must be empty — no re-exports, no
199
+ # side effects. Consumers import submodule paths (from pkg.mod import thing);
200
+ # the package layout is the public surface.
201
+ strictly-empty-init-modules = true
202
+
203
+ [lint.flake8-builtins]
204
+ ignorelist = ["id", "map"]
@@ -0,0 +1 @@
1
+ extend = "ruff.base.toml"
@@ -8,6 +8,7 @@ from typer import Argument, Option, Typer
8
8
  from .client import generate_client
9
9
  from .downgrade import downgrade_openapi_3_1_to_3_0
10
10
  from .naming import DefaultNamingPolicy
11
+ from .orchestrator import SpecProducer, generate_projects
11
12
  from .templates import regenerate_templates
12
13
 
13
14
  app = Typer(pretty_exceptions_show_locals=False)
@@ -51,3 +52,40 @@ def generate(
51
52
  license_spdx=license_spdx,
52
53
  repository_url=repository_url,
53
54
  )
55
+
56
+
57
+ @app.command("generate-projects")
58
+ def generate_projects_command(
59
+ config: Annotated[Path, Option(help="JSON file mapping project paths to client generator lists")],
60
+ root_name: Annotated[str, Option(help="Root name handed to DefaultNamingPolicy")],
61
+ generated_root: Annotated[Path, Option(help="Root directory the generated clients sync into")],
62
+ spec_command: Annotated[
63
+ str,
64
+ Option(
65
+ help="Shell command printing the project's raw OpenAPI JSON to stdout; runs with the project directory as cwd"
66
+ ),
67
+ ],
68
+ npm_scope: Annotated[str | None, Option(help="npm scope composing the UPM package identity")] = None,
69
+ license_spdx: Annotated[str | None, Option(help="SPDX license id written to package.json")] = None,
70
+ repository_url: Annotated[str | None, Option(help="git repository URL written to package.json")] = None,
71
+ project: Annotated[str | None, Option(help="Generate only this project, as keyed in the config")] = None,
72
+ client: Annotated[str | None, Option(help="Generate only this client generator")] = None,
73
+ no_cache: Annotated[
74
+ bool, Option("--no-cache", help="Regenerate even when the committed spec is unchanged")
75
+ ] = False,
76
+ root: Annotated[Path, Option(help="Repository root the project paths resolve against")] = Path(),
77
+ ) -> None:
78
+ projects: dict[str, list[str]] = json.loads(config.read_text(encoding="utf-8"))
79
+ generate_projects(
80
+ projects,
81
+ root_name=root_name,
82
+ generated_root=generated_root,
83
+ dump_spec=SpecProducer(spec_command),
84
+ npm_scope=npm_scope,
85
+ license_spdx=license_spdx,
86
+ repository_url=repository_url,
87
+ project=project,
88
+ client=client,
89
+ no_cache=no_cache,
90
+ root=root,
91
+ )
@@ -4,7 +4,7 @@ from pathlib import Path
4
4
  from shutil import copytree
5
5
  from tempfile import NamedTemporaryFile, TemporaryDirectory
6
6
 
7
- from bashrun import bash
7
+ from bashrun.bash import bash
8
8
 
9
9
  from .resources import CONFIGS_DIR, IGNORE_FILE, OPENAPI_GENERATOR_CLI_VERSION
10
10
  from .naming import ClientNaming
@@ -0,0 +1,95 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from collections.abc import Callable
5
+ from pathlib import Path
6
+ from tempfile import TemporaryDirectory
7
+
8
+ from bashrun.bash import bash_output
9
+
10
+ from .client import generate_client
11
+ from .downgrade import downgrade_openapi_3_1_to_3_0
12
+ from .naming import DefaultNamingPolicy
13
+ from .templates import regenerate_templates
14
+
15
+
16
+ class SpecProducer:
17
+ def __init__(self, command: str) -> None:
18
+ self.command = command
19
+
20
+ def __call__(self, project: Path) -> str:
21
+ return bash_output(self.command, cwd=project)
22
+
23
+
24
+ def generate_projects(
25
+ projects: dict[str, list[str]],
26
+ root_name: str,
27
+ generated_root: Path,
28
+ dump_spec: Callable[[Path], str],
29
+ npm_scope: str | None = None,
30
+ license_spdx: str | None = None,
31
+ repository_url: str | None = None,
32
+ project: str | None = None,
33
+ client: str | None = None,
34
+ no_cache: bool = False,
35
+ root: Path = Path(),
36
+ ) -> None:
37
+ root = root.resolve()
38
+ naming = DefaultNamingPolicy(root_name)
39
+
40
+ with TemporaryDirectory() as templates_directory_string:
41
+ templates_directory = Path(templates_directory_string)
42
+ regenerate_templates(templates_directory)
43
+
44
+ for project_name, clients in projects.items():
45
+ if project is not None and project != project_name:
46
+ continue
47
+
48
+ openapi_spec = _produce_and_cache_spec(root / project_name, dump_spec, no_cache)
49
+
50
+ if openapi_spec is None:
51
+ continue
52
+
53
+ names = naming(project_name)
54
+
55
+ for client_name in clients:
56
+ if client is not None and client_name != client:
57
+ continue
58
+
59
+ generate_client(
60
+ openapi_spec,
61
+ client_name,
62
+ generated_root / client_name / names.base,
63
+ names,
64
+ templates_dir=templates_directory,
65
+ npm_scope=npm_scope,
66
+ license_spdx=license_spdx,
67
+ repository_url=repository_url,
68
+ )
69
+
70
+
71
+ def dump_openapi_spec(project: Path) -> str:
72
+ # CODEGEN=1 gates a service package's heavy imports so the app imports cleanly for the spec
73
+ # dump; it reaches the child through bashrun's per-call env overlay.
74
+ return bash_output("uv run --project . python -m src.dump_openapi", cwd=project, env={"CODEGEN": "1"})
75
+
76
+
77
+ def _produce_and_cache_spec(project: Path, dump_spec: Callable[[Path], str], no_cache: bool) -> str | None:
78
+ print(f"Producing OpenAPI spec for project: {project}")
79
+
80
+ openapi_json = json.loads(dump_spec(project))
81
+ downgrade_openapi_3_1_to_3_0(openapi_json)
82
+ openapi_spec = json.dumps(openapi_json, indent=2)
83
+
84
+ spec_path = project / "openapi.json"
85
+
86
+ if spec_path.exists():
87
+ old_spec = spec_path.read_text(encoding="utf-8")
88
+
89
+ if openapi_spec == old_spec and not no_cache:
90
+ print("OpenAPI spec unchanged, skipping client generation")
91
+ return None
92
+
93
+ spec_path.write_text(openapi_spec, encoding="utf-8")
94
+
95
+ return openapi_spec
@@ -1,6 +1,6 @@
1
1
  from pathlib import Path
2
2
 
3
- from bashrun import bash
3
+ from bashrun.bash import bash
4
4
 
5
5
  from .resources import OPENAPI_GENERATOR_CLI_VERSION, SHIPPED_PATCHES_DIR
6
6
 
@@ -1,6 +1,6 @@
1
1
  from typing import Any
2
2
 
3
- from openapi_client_codegen import downgrade_openapi_3_1_to_3_0
3
+ from openapi_client_codegen.downgrade import downgrade_openapi_3_1_to_3_0
4
4
 
5
5
 
6
6
  def test_bumps_version():
@@ -1,4 +1,4 @@
1
- from openapi_client_codegen import DefaultNamingPolicy
1
+ from openapi_client_codegen.naming import DefaultNamingPolicy
2
2
 
3
3
 
4
4
  def test_default_naming_derives_all_variants():
@@ -0,0 +1,157 @@
1
+ import copy
2
+ import json
3
+ from collections.abc import Callable
4
+ from pathlib import Path
5
+
6
+ import pytest
7
+
8
+ from openapi_client_codegen import orchestrator
9
+ from openapi_client_codegen.downgrade import JsonDict, downgrade_openapi_3_1_to_3_0
10
+ from openapi_client_codegen.naming import ClientNaming
11
+ from openapi_client_codegen.orchestrator import SpecProducer, dump_openapi_spec, generate_projects
12
+
13
+ RAW_SCHEMA: JsonDict = {"openapi": "3.1.0", "info": {"title": "demo", "version": "0.0.0"}, "paths": {}}
14
+
15
+
16
+ class Recorder:
17
+ def __init__(self) -> None:
18
+ self.generated: list[tuple[str, Path, ClientNaming]] = []
19
+ self.dumps: list[Path | None] = []
20
+ self.commands: list[tuple[str, Path | None, dict[str, str] | None]] = []
21
+
22
+ def fake_dump_spec(self, project: Path) -> str:
23
+ self.dumps.append(project)
24
+ return json.dumps(RAW_SCHEMA)
25
+
26
+ def fake_bash_output(self, command: str, *, cwd: Path | None = None, env: dict[str, str] | None = None) -> str:
27
+ self.commands.append((command, cwd, env))
28
+ return json.dumps(RAW_SCHEMA)
29
+
30
+ def fake_regenerate_templates(self, target_dir: Path, extra_patches_dir: Path | None = None) -> None:
31
+ pass
32
+
33
+ def fake_generate_client(
34
+ self,
35
+ spec: str,
36
+ generator: str,
37
+ output_dir: Path,
38
+ names: ClientNaming,
39
+ templates_dir: Path | None = None,
40
+ extra_references: list[str] | None = None,
41
+ npm_scope: str | None = None,
42
+ license_spdx: str | None = None,
43
+ repository_url: str | None = None,
44
+ ) -> None:
45
+ self.generated.append((generator, output_dir, names))
46
+
47
+
48
+ @pytest.fixture
49
+ def recorder(monkeypatch: pytest.MonkeyPatch) -> Recorder:
50
+ recorder = Recorder()
51
+ monkeypatch.setattr(orchestrator, "bash_output", recorder.fake_bash_output)
52
+ monkeypatch.setattr(orchestrator, "regenerate_templates", recorder.fake_regenerate_templates)
53
+ monkeypatch.setattr(orchestrator, "generate_client", recorder.fake_generate_client)
54
+ return recorder
55
+
56
+
57
+ def downgraded_spec() -> str:
58
+ schema = copy.deepcopy(RAW_SCHEMA)
59
+ downgrade_openapi_3_1_to_3_0(schema)
60
+ return json.dumps(schema, indent=2)
61
+
62
+
63
+ def make_project(root: Path, name: str) -> Path:
64
+ project = root / name
65
+ project.mkdir(parents=True)
66
+ return project
67
+
68
+
69
+ def test_generates_all_clients_and_writes_downgraded_spec(tmp_path: Path, recorder: Recorder) -> None:
70
+ make_project(tmp_path, "docker/api")
71
+
72
+ generate_projects(
73
+ {"docker/api": ["python", "csharp"]},
74
+ "placeframe",
75
+ tmp_path / "generated",
76
+ recorder.fake_dump_spec,
77
+ root=tmp_path,
78
+ )
79
+
80
+ assert [(generator, output_dir.name) for generator, output_dir, _ in recorder.generated] == [
81
+ ("python", "api-client"),
82
+ ("csharp", "api-client"),
83
+ ]
84
+ assert all(output_dir.parent.parent == tmp_path / "generated" for _, output_dir, _ in recorder.generated)
85
+
86
+ written = (tmp_path / "docker" / "api" / "openapi.json").read_text(encoding="utf-8")
87
+ assert written == downgraded_spec()
88
+ assert json.loads(written)["openapi"] == "3.0.3"
89
+
90
+ assert recorder.dumps == [tmp_path.resolve() / "docker" / "api"]
91
+
92
+
93
+ def test_skips_generation_when_committed_spec_unchanged(tmp_path: Path, recorder: Recorder) -> None:
94
+ project = make_project(tmp_path, "docker/api")
95
+ (project / "openapi.json").write_text(downgraded_spec(), encoding="utf-8")
96
+
97
+ generate_projects(
98
+ {"docker/api": ["python"]}, "placeframe", tmp_path / "generated", recorder.fake_dump_spec, root=tmp_path
99
+ )
100
+
101
+ assert recorder.generated == []
102
+
103
+
104
+ def test_no_cache_forces_generation_despite_unchanged_spec(tmp_path: Path, recorder: Recorder) -> None:
105
+ project = make_project(tmp_path, "docker/api")
106
+ (project / "openapi.json").write_text(downgraded_spec(), encoding="utf-8")
107
+
108
+ generate_projects(
109
+ {"docker/api": ["python"]},
110
+ "placeframe",
111
+ tmp_path / "generated",
112
+ recorder.fake_dump_spec,
113
+ no_cache=True,
114
+ root=tmp_path,
115
+ )
116
+
117
+ assert len(recorder.generated) == 1
118
+
119
+
120
+ def test_project_and_client_filters(tmp_path: Path, recorder: Recorder) -> None:
121
+ make_project(tmp_path, "docker/api")
122
+ make_project(tmp_path, "docker/localizer")
123
+
124
+ generate_projects(
125
+ {"docker/api": ["python", "csharp"], "docker/localizer": ["python"]},
126
+ "placeframe",
127
+ tmp_path / "generated",
128
+ recorder.fake_dump_spec,
129
+ project="docker/api",
130
+ client="csharp",
131
+ root=tmp_path,
132
+ )
133
+
134
+ assert len(recorder.generated) == 1
135
+ generator, output_dir, names = recorder.generated[0]
136
+ assert generator == "csharp"
137
+ assert output_dir == tmp_path / "generated" / "csharp" / "api-client"
138
+ assert names.dashed == "placeframe-api-client"
139
+
140
+
141
+ def test_dump_openapi_spec_pins_the_org_convention(recorder: Recorder) -> None:
142
+ dump_openapi_spec(Path("docker/api"))
143
+
144
+ assert len(recorder.commands) == 1
145
+ command, cwd, env = recorder.commands[0]
146
+ assert command == "uv run --project . python -m src.dump_openapi"
147
+ assert cwd == Path("docker/api")
148
+ assert env == {"CODEGEN": "1"}
149
+
150
+
151
+ def test_command_spec_producer_runs_with_project_cwd(recorder: Recorder) -> None:
152
+ producer: Callable[[Path], str] = SpecProducer("dotnet run -- dump-openapi")
153
+
154
+ produced = producer(Path("services/api"))
155
+
156
+ assert json.loads(produced) == RAW_SCHEMA
157
+ assert recorder.commands == [("dotnet run -- dump-openapi", Path("services/api"), None)]
@@ -1,7 +1,7 @@
1
1
  import json
2
2
  from pathlib import Path
3
3
 
4
- from openapi_client_codegen import write_unity_package_metadata
4
+ from openapi_client_codegen.unity import write_unity_package_metadata
5
5
 
6
6
 
7
7
  def write_package(
@@ -1,73 +0,0 @@
1
- # openapi-client-codegen
2
-
3
- Generate typed API clients from an OpenAPI schema. `openapi-client-codegen` wraps [OpenAPI Generator](https://openapi-generator.tech/): it downgrades OpenAPI 3.1→3.0 (the generator only accepts 3.0), authors and patches the C# templates, runs the generator, and writes Unity package metadata. It produces a Python (`httpx`) client and a Unity-consumable C# (`httpclient`) client.
4
-
5
- The generic steps live here; the caller supplies the OpenAPI spec, the project→client mapping, the package-name policy, and the output paths. See [`AGENTS.md`](./AGENTS.md) for the API surface, the lib-owns/consumer-owns boundary, and the patch mechanism.
6
-
7
- ## Requirements
8
-
9
- - Python 3.13+ and [uv](https://docs.astral.sh/uv/)
10
- - **Java (JDK 11+)** and **`uvx`** on PATH at runtime — the generator runs as `uvx --from 'openapi-generator-cli[jdk4py]==<pin>' ...`
11
-
12
- ## CLI
13
-
14
- A thin CLI exposes the OpenAPI 3.1→3.0 downgrade and single-client generation:
15
-
16
- ```bash
17
- uv run openapi-client-codegen downgrade path/to/openapi.json # 3.1→3.0 in place
18
- uv run openapi-client-codegen downgrade path/to/openapi.json out.json # write elsewhere
19
-
20
- uv run openapi-client-codegen generate path/to/openapi.json generated/csharp/api-client \
21
- --root-name myproject --project services/api \
22
- --npm-scope org.example.myproject --license-spdx Apache-2.0 \
23
- --repository-url https://github.com/org/repo.git
24
- ```
25
-
26
- Client generation itself is driven from Python (it needs the consumer's project map, naming policy, and output paths):
27
-
28
- ```python
29
- import json
30
- import tempfile
31
- from pathlib import Path
32
-
33
- from openapi_client_codegen import (
34
- DefaultNamingPolicy,
35
- downgrade_openapi_3_1_to_3_0,
36
- generate_client,
37
- regenerate_templates,
38
- )
39
-
40
- naming = DefaultNamingPolicy("myproject")
41
- templates_dir = Path(tempfile.mkdtemp())
42
- regenerate_templates(templates_dir)
43
-
44
- # Produce the raw OpenAPI JSON however your app exposes it, then downgrade it.
45
- schema = json.loads(my_openapi_spec_json())
46
- downgrade_openapi_3_1_to_3_0(schema)
47
- spec = json.dumps(schema, indent=2)
48
-
49
- generate_client(spec, "python", Path("generated/python/api-client"), naming("services/api"))
50
- generate_client(
51
- spec, "csharp", Path("generated/csharp/api-client"), naming("services/api"), templates_dir=templates_dir
52
- )
53
- ```
54
-
55
- ## Consuming from another repo
56
-
57
- Install from PyPI:
58
-
59
- ```toml
60
- [project]
61
- dependencies = ["openapi-client-codegen>=0.1.0"]
62
- ```
63
-
64
- `bashrun` resolves transitively. At **runtime** the generator also needs Java (JDK 11+) and `uvx` on PATH. To test an unreleased change, pin the repo at a git ref in a scratch branch instead (`openapi-client-codegen = { git = "…", rev = "<sha>" }` under `[tool.uv.sources]`) and drop the pin when the release lands.
65
-
66
- ## Development
67
-
68
- ```bash
69
- uv run ruff check .
70
- uv run ruff format --check .
71
- uv run basedpyright
72
- uv run pytest
73
- ```
@@ -1,71 +0,0 @@
1
- target-version = "py313"
2
- line-length = 120
3
- preview = true
4
-
5
- [lint]
6
- select = ["ALL"]
7
- ignore = [
8
- # ── Permanent ─────────────────────────────────────────────────────────
9
- # Type annotations — basedpyright strict mode handles this
10
- "ANN",
11
- # No docstrings, no copyright headers
12
- "D",
13
- "DOC",
14
- "CPY",
15
- # TODO/FIXME comment formatting
16
- "TD",
17
- "FIX",
18
- # Exception message ergonomics — literal-indirection and custom-class churn without benefit
19
- "EM101",
20
- "EM102",
21
- "TRY003",
22
- # Class attribute names never conflict with builtins via self.x
23
- "A003",
24
- # Function-size counters — C901 already covers structural complexity
25
- "PLR0911",
26
- "PLR0912",
27
- "PLR0913",
28
- "PLR0914",
29
- "PLR0915",
30
- "PLR0917",
31
- # Typer's Option/Argument in argument defaults is its standard API pattern
32
- "B008",
33
- # Boolean parameters read fine as keyword-only flags here
34
- "FBT001",
35
- "FBT002",
36
- "FBT003",
37
- # Relative imports are the intra-package convention
38
- "TID252",
39
- # Formatter-owned; enforced by `ruff format`
40
- "COM812",
41
- "I001",
42
- # Magic values in comparisons are readable in this CLI
43
- "PLR2004",
44
- # The formatter owns wrapping; long string literals and URLs stay on one line
45
- "E501",
46
- # src-layout tests need no __init__.py
47
- "INP001",
48
- # Runtime-used typing imports stay at module level
49
- "TC001",
50
- "TC002",
51
- "TC003",
52
- # Small literal tuples read fine in membership tests
53
- "PLR6201",
54
-
55
- # ── Deferred to PLE-248 — Enforce the deferred ruff rules
56
- # https://linear.app/plerionplatforms/issue/PLE-248
57
- # Mirrors placeframe's root ruff.toml transitional Ticket-N blocks; lifting
58
- # a rule here should happen alongside lifting it from placeframe's ruff.toml.
59
- "C901", # function too complex — Ticket 9
60
- "PLR1702", # too many nested blocks — Ticket 9
61
-
62
- # ── Deferred to PLE-336 — Replace print() with structured logging
63
- # https://linear.app/plerionplatforms/issue/PLE-336
64
- "T201", # print found
65
- ]
66
-
67
- [lint.per-file-ignores]
68
- "tests/**" = ["S101", "S404", "S607", "PLR6301"]
69
-
70
- [lint.flake8-builtins]
71
- ignorelist = ["id", "map"]
@@ -1,15 +0,0 @@
1
- from .client import generate_client
2
- from .downgrade import downgrade_openapi_3_1_to_3_0
3
- from .naming import ClientNaming, DefaultNamingPolicy, NamingPolicy
4
- from .templates import regenerate_templates
5
- from .unity import write_unity_package_metadata
6
-
7
- __all__ = [
8
- "ClientNaming",
9
- "DefaultNamingPolicy",
10
- "NamingPolicy",
11
- "downgrade_openapi_3_1_to_3_0",
12
- "generate_client",
13
- "regenerate_templates",
14
- "write_unity_package_metadata",
15
- ]