diff --git a/.github/scripts/build-contract-shell.sh b/.github/scripts/build-contract-shell.sh index 3845a1d..9fbc79c 100755 --- a/.github/scripts/build-contract-shell.sh +++ b/.github/scripts/build-contract-shell.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Wraps the Dhall-generated package (from tests/Exhaustive.dhall) in a minimal +# Wraps the Dhall-generated package (from demos/Exhaustive.dhall) in a minimal # consumer shell, mirroring the hand-written tests/golden/ shell (pyproject.toml # + py.typed) so basedpyright strict runs against the same layout a real # consumer would import, per the full_package pattern in tests/conftest.py. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7664a04..b5d526f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -121,7 +121,7 @@ jobs: with: ref: ${{ inputs.ref || github.sha }} - # The `as Source` import mode on gen/Deps only changes how pgn loads the + # The `as Source` import mode on src/Deps only changes how pgn loads the # remote packages (unnormalized, to save RAM), not what they evaluate to. # The pinned action below bundles a dhall fork that predates the mode, so # strip it here; the evaluation is semantically identical either way. The @@ -132,16 +132,16 @@ jobs: shell: bash run: | set -euo pipefail - sed -i -e 's/^[[:space:]]*as Source$//' -e 's/^[[:space:]]*sha256:[0-9a-f]\{64\}$//' gen/Deps/*.dhall + sed -i -e 's/^[[:space:]]*as Source$//' -e 's/^[[:space:]]*sha256:[0-9a-f]\{64\}$//' src/Deps/*.dhall - # A plain, standard-Dhall evaluator cannot run this: gen/Interpreters/Project.dhall's + # A plain, standard-Dhall evaluator cannot run this: src/Interpreters/Project.dhall's # buildLookup and gen-sdk's own Fixtures.Exhaustive both use Text/equal, a builtin # from pgn's forked Dhall, absent from the upstream dhall-lang Prelude. This action # bundles that same fork. - name: Generate output from Dhall uses: nikita-volkov/dhall-directory-tree.github-action@60a18dc647d6daea805263ea0fed7bb8011f3bcd # v2 with: - dhall_file: tests/Exhaustive.dhall + dhall_file: demos/Exhaustive.dhall output_dir: contract-output - name: Assert the compile did not fail diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 25e92c7..91764bc 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -86,7 +86,7 @@ jobs: id: resolve uses: nikita-volkov/dhall-resolve.github-action@7caaf1fdb40ac864bc02e37575f577ee084713a8 # v3 with: - file: gen/Gen.dhall + file: src/package.dhall minify: true - name: Prepare changelog for release diff --git a/AGENTS.md b/AGENTS.md index f6d2533..7da3b7b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -33,6 +33,6 @@ already says. ## Dhall -`gen/` pins its remote imports by sha256 (`gen/Deps/*.dhall`). Bump those +`src/` pins its remote imports by sha256 (`src/Deps/*.dhall`). Bump those deliberately, one at a time, and re-run the harness before committing a pin change. diff --git a/CHANGELOG.md b/CHANGELOG.md index 8cc7b2c..f98fe04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Upcoming +- Migrated the generator's internal dependencies to `gen-contract` v4.0.1 + and `gen-sdk` v2.0.0, adopting `Sdk.Sigs` in place of the local + `Algebras/` module, and restructured the repository layout to match the + pGenie generator architecture: implementation moved from `gen/` to + `src/`, the public entry point renamed from `gen/Gen.dhall` to + `src/package.dhall`, and the fixture driver moved from + `tests/Exhaustive.dhall` to `demos/Exhaustive.dhall`. No change to + generated output or the public Dhall interface (`artifacts..gen` + URLs pointing at a previously-released `resolved.dhall` are unaffected; + only the next release's URL path changes, from `.../gen/Gen.dhall` — the + unresolved source path some projects may reference directly instead of a + frozen release — to `.../src/package.dhall`). - The test harness now runs every pgn subprocess in its own process group under an RSS watchdog: a thread polls `ps -o rss=` every 2 s and, on breach of `PGN_MAX_RSS_GB` (default 40 GB), kills the whole group and fails the test with diff --git a/DESIGN.md b/DESIGN.md index bff5a2c..79e8a7c 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -1,7 +1,7 @@ # python.gen DESIGN Status: current. This describes the generator as shipped, not a plan. The code -is the source of truth: the Dhall generator under `gen/`, the fixture project +is the source of truth: the Dhall generator under `src/`, the fixture project and golden output under `tests/`. When this doc and the tree disagree, the tree wins. @@ -378,34 +378,37 @@ overwritten on every run. Do not hand-edit it. ## 10. Generator decomposition -`gen/Gen.dhall` is the entry point handed to gen-sdk: +`src/package.dhall` is the entry point handed to gen-sdk: ```dhall let Sdk = ./Deps/Sdk.dhall -in Sdk ./Config.dhall ./compile.dhall +let Config = ./Config.dhall + +let interpret = ./Interpret.dhall + +in Sdk.Sigs.generator Config Config/default interpret ``` -The Sdk `module` function has signature `\(Config : Type) -> \(compile) -> -{ contractVersion, Config, compile, compileToFileMap }`, and -`compile : Optional Config -> Project -> Lude.Compiled.Type Lude.Files.Type`, -where `Files.Type = List { path : Text, content : Text }`. `compile.dhall` -folds the optional user config into the internal interpreter config and -calls `Interpreters/Project.dhall`, which traverses queries and custom types -and assembles the file list. +`Sdk.Sigs.generator` has signature `\(Config : Type) -> \(defaultConfig : Config) -> +\(interpret : Config -> Contract.Project -> Contract.Output) -> ...`; it curries +`interpret` against `defaultConfig` whenever the user config is absent and hands +the result to gen-contract's `Contract.module`. `Interpret.dhall` folds the +optional user config into the internal interpreter config and calls +`Interpreters/Project.dhall`, which traverses queries and custom types and +assembles the file list (`Contract.Output`). -`gen/` mirrors a typical pgn gen-sdk generator, Python-flavored. The -algebra/interpreter/template split keeps assembly separate from rendering. +`src/` mirrors a typical pgn gen-sdk generator, Python-flavored: `Interpreters/` +assembles data, `Templates/` renders it to Python text. The interpreter/template +algebra signatures themselves live in gen-sdk's `Sdk.Sigs` (`interpreter.dhall`/ +`template.dhall`), not a local `Algebras/` dir. ```text -gen/ - Gen.dhall # Sdk Config compile (entry handed to gen-sdk) +src/ + package.dhall # Sdk.Sigs.generator Config Config/default interpret (entry handed to gen-sdk) Config.dhall # user config TYPE: { packageName, emitSync, onUnsupported } - compile.dhall # derive interpreter Config from user Config, call Project.run - Deps/ # pinned remote imports (gen-sdk module + Project, lude, Prelude) - Algebras/ - Interpreter.dhall # Config + `module Input Output run` ; Run = Config -> Input -> Compiled Output - Template.dhall # `module Params run` ; Run = Params -> Text + Interpret.dhall # derive interpreter Config from user Config, call Project.run + Deps/ # pinned remote imports: gen-sdk, gen-contract, lude, dhall Prelude Structures/ Surface.dhall # async/sync token table (section 4) CustomKind.dhall # Lookup : Name -> < Enum | Composite | Absent > + composite fields @@ -426,6 +429,7 @@ gen/ Project.dhall # traverse queries+customTypes, assemble all files + facade + header, # apply the Skip filter (section 11) Templates/ + CoreModule.dhall # shared _core.py: JsonValue, NoRowError/DecodeError, require_array RuntimeModule.dhall # async + sync _runtime.py bodies RowsModule.dhall # shared _rows.py (Row dataclasses + decode fns) StatementModule.dhall # one per-surface statement wrapper @@ -629,11 +633,11 @@ SQL rendering are largely driver-agnostic. CI runs two independent jobs (`.github/workflows/ci.yml`): `harness` (the pytest suite against a live Postgres) and `contract` (compiles gen-sdk's -`Fixtures.Exhaustive` via `tests/Exhaustive.dhall` and runs basedpyright +`Fixtures.Exhaustive` via `demos/Exhaustive.dhall` and runs basedpyright strict on the result). The `contract` job needs `nikita-volkov/dhall-directory-tree.github-action`, a Docker action bundling a forked Dhall evaluator; the local `dhall` CLI most people have installed is the standard dhall-lang build and does not -understand `Text/equal`, so it cannot run `tests/Exhaustive.dhall` directly. +understand `Text/equal`, so it cannot run `demos/Exhaustive.dhall` directly. Reproduce the `contract` job locally with [`act`](https://github.com/nektos/act) (not installed in this environment; `act -j contract` pulls the same pinned Docker action and runs the job as GitHub would). diff --git a/README.md b/README.md index db3226f..9e9a17f 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ name: ```yaml artifacts: python: - gen: https://raw.githubusercontent.com/slavashvets/python.gen/master/gen/Gen.dhall + gen: https://raw.githubusercontent.com/slavashvets/python.gen/master/src/package.dhall config: packageName: my-db-client emitSync: true @@ -61,10 +61,10 @@ database. | form | example | works? | | --------------- | --------------------------------------- | ------------------------------------------ | -| plain http(s) | `https://.../python.gen/gen/Gen.dhall` | yes; pgn fetches it and every relative import over HTTP | -| relative path | `../path/to/python.gen/gen/Gen.dhall` | yes, if you keep a local checkout next to your project | -| absolute path | `/abs/path/to/python.gen/gen/Gen.dhall` | yes, but the resulting freeze key is machine-specific | -| `file://` URL | `file:///abs/.../Gen.dhall` | rejected; pgn's project schema does not accept `file://` | +| plain http(s) | `https://.../python.gen/src/package.dhall` | yes; pgn fetches it and every relative import over HTTP | +| relative path | `../path/to/python.gen/src/package.dhall` | yes, if you keep a local checkout next to your project | +| absolute path | `/abs/path/to/python.gen/src/package.dhall` | yes, but the resulting freeze key is machine-specific | +| `file://` URL | `file:///abs/.../package.dhall` | rejected; pgn's project schema does not accept `file://` | Whichever form you use, the freeze file that caches the resolved generator (section "Freeze lifecycle" below) keys on the literal `gen:` value and diff --git a/bench/as-source.sh b/bench/as-source.sh index 203b973..c16945b 100755 --- a/bench/as-source.sh +++ b/bench/as-source.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash # Cold-cache benchmark of `pgn generate` on the fixture project, comparing the -# current gen (Deps imported `as Source`) against the same tree with the +# current src (Deps imported `as Source`) against the same tree with the # pre-as-Source Deps (plain pinned imports). Both variants run the same pgn # binary by default, so the measurement isolates the import mode itself. # @@ -24,7 +24,7 @@ trap 'rm -rf "$work"' EXIT prepare() { # $1 = variant root; mirrors the repo layout the fixture expects rm -rf "$1" && mkdir -p "$1/tests" - cp -R "$root/gen" "$1/gen" + cp -R "$root/src" "$1/src" cp -R "$root/tests/fixture-project" "$1/tests/fixture-project" rm -f "$1/tests/fixture-project/freeze1.pgn.yaml" rm -rf "$1/tests/fixture-project/artifacts" @@ -34,9 +34,23 @@ prepare() { # $1 = variant root; mirrors the repo layout the fixture expects # normalized-expression pins (an `as Source` pin hashes the import's source, # so the two modes need different sha256 values for the same version). strip_as_source() { # $1 = variant root - perl -i -ne 'print unless /^\s*as Source$/' "$1"/gen/Deps/*.dhall - perl -i -pe 's/8d43544ecb0e612406af3133bdbca51138c704a77a5a29ef62fe034d0e77a3a6/b9f7bb842345f3864c71e877fda4200306ba5c044a43e6f7713a23bc4769b91a/' "$1/gen/Deps/Sdk.dhall" - perl -i -pe 's/46b527b071eba96a17e76b4bc5774645714dd5b4355974d221e705aa7c126e77/14c43eec97972ae27afe3386ff937d04db66f84273d5551476361db12d2c4b50/' "$1/gen/Deps/Lude.dhall" + perl -i -ne 'print unless /^\s*as Source$/' "$1"/src/Deps/*.dhall + + # gen-sdk v2.0.0's src/package.dhall: `mise x -- dhall hash` against the + # live GitHub-hosted package (both `... as Source` and the plain import) + # returns the SAME sha256 (b9def6ab1179bc4aaae7fc6e91977f094f75934cd5755175c294a9e97ca71b15), + # matching the value already committed in src/Deps/Sdk.dhall -- so, unlike + # the old v0.11.0 pin this pair used to target (where the two genuinely + # differed: 8d43544e...->b9f7bb84...), no character swap is needed here + # after the strip above; the committed pin already equals the plain-import + # target. See docs/superpowers/plans/2026-07-11-gen-sdk-v2-migration.md + # Task 6 for how this was verified (dhall's local import cache made the + # lookup instant; a cold, uncached fetch of a *different* URL hung in this + # sandbox, so treat network reachability here as best-effort, not given). + + # lude v5.1.0 is unchanged by this migration (see src/Deps/Lude.dhall), so + # its existing as-Source -> plain-import hash swap below is still correct. + perl -i -pe 's/46b527b071eba96a17e76b4bc5774645714dd5b4355974d221e705aa7c126e77/14c43eec97972ae27afe3386ff937d04db66f84273d5551476361db12d2c4b50/' "$1/src/Deps/Lude.dhall" } measure() { # $1 = label, $2 = variant root, $3 = pgn binary diff --git a/bench/generate.sh b/bench/generate.sh index a2acd50..2769621 100755 --- a/bench/generate.sh +++ b/bench/generate.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Build the fixture client from the working-tree gen, in one of two variants: -# with - gen/Deps as committed (imports `as Source`) +# Build the fixture client from the working-tree src, in one of two variants: +# with - src/Deps as committed (imports `as Source`) # without - the pre-as-Source Deps (mode stripped, normalized-expression # pins restored; byte-identical to commit fb78869's Deps) # Output lands in ./demo-with-as-source or ./demo-without-as-source. @@ -15,15 +15,29 @@ url="${PGN_TEST_DATABASE_URL:-postgresql://postgres:postgres@localhost:5432/post out="$root/demo-$variant-as-source" rm -rf "$out" && mkdir -p "$out/tests" -cp -R "$root/gen" "$out/gen" +cp -R "$root/src" "$out/src" cp -R "$root/tests/fixture-project" "$out/tests/fixture-project" rm -f "$out/tests/fixture-project/freeze1.pgn.yaml" rm -rf "$out/tests/fixture-project/artifacts" if [ "$variant" = without ]; then - perl -i -ne 'print unless /^\s*as Source$/' "$out"/gen/Deps/*.dhall - perl -i -pe 's/8d43544ecb0e612406af3133bdbca51138c704a77a5a29ef62fe034d0e77a3a6/b9f7bb842345f3864c71e877fda4200306ba5c044a43e6f7713a23bc4769b91a/' "$out/gen/Deps/Sdk.dhall" - perl -i -pe 's/46b527b071eba96a17e76b4bc5774645714dd5b4355974d221e705aa7c126e77/14c43eec97972ae27afe3386ff937d04db66f84273d5551476361db12d2c4b50/' "$out/gen/Deps/Lude.dhall" + perl -i -ne 'print unless /^\s*as Source$/' "$out"/src/Deps/*.dhall + + # gen-sdk v2.0.0's src/package.dhall: `mise x -- dhall hash` against the + # live GitHub-hosted package (both `... as Source` and the plain import) + # returns the SAME sha256 (b9def6ab1179bc4aaae7fc6e91977f094f75934cd5755175c294a9e97ca71b15), + # matching the value already committed in src/Deps/Sdk.dhall -- so, unlike + # the old v0.11.0 pin this pair used to target (where the two genuinely + # differed: 8d43544e...->b9f7bb84...), no character swap is needed here + # after the strip above; the committed pin already equals the plain-import + # target. See docs/superpowers/plans/2026-07-11-gen-sdk-v2-migration.md + # Task 6 for how this was verified (dhall's local import cache made the + # lookup instant; a cold, uncached fetch of a *different* URL hung in this + # sandbox, so treat network reachability here as best-effort, not given). + + # lude v5.1.0 is unchanged by this migration (see src/Deps/Lude.dhall), so + # its existing as-Source -> plain-import hash swap below is still correct. + perl -i -pe 's/46b527b071eba96a17e76b4bc5774645714dd5b4355974d221e705aa7c126e77/14c43eec97972ae27afe3386ff937d04db66f84273d5551476361db12d2c4b50/' "$out/src/Deps/Lude.dhall" fi cd "$out/tests/fixture-project" diff --git a/tests/Exhaustive.dhall b/demos/Exhaustive.dhall similarity index 63% rename from tests/Exhaustive.dhall rename to demos/Exhaustive.dhall index bfe8b84..35e8f28 100644 --- a/tests/Exhaustive.dhall +++ b/demos/Exhaustive.dhall @@ -1,9 +1,9 @@ -- Applies this generator to gen-sdk's shared cross-backend fixture project --- (the same "music_catalogue" project java.gen's own tests/Exhaustive.dhall +-- (the same "music_catalogue" project java.gen's own demos/Exhaustive.dhall -- exercises), so a Python client compiles from it and passes basedpyright -- strict. Pinned directly at gen-sdk's package.dhall, separately from --- gen/Deps/Sdk.dhall: that file only imports gen-sdk's `module.dhall` (the --- generator-construction function), which has no `Fixtures` field. +-- src/Deps/Sdk.dhall: that file only imports gen-sdk's `package.dhall` `as +-- Source` for RAM, and this fixture load doesn't need that mode. -- -- The fixture project deliberately covers PG types this generator does not -- support (box, inet, money, ranges, ...), so onUnsupported is set to Skip: @@ -13,13 +13,13 @@ -- Intended to be executed with: -- -- ```bash --- dhall to-directory-tree --file tests/Exhaustive.dhall --output --allow-path-separators +-- dhall to-directory-tree --file demos/Exhaustive.dhall --output --allow-path-separators -- ``` -let Sdk = ../gen/Deps/Sdk.dhall +let Sdk = ../src/Deps/Sdk.dhall -let Gen = ../gen/Gen.dhall +let Gen = ../src/package.dhall -let OnUnsupported = ../gen/Structures/OnUnsupported.dhall +let OnUnsupported = ../src/Structures/OnUnsupported.dhall let project = Sdk.Fixtures.Exhaustive @@ -30,4 +30,4 @@ let config = , onUnsupported = Some OnUnsupported.Mode.Skip } -in Gen.compileToFileMap config project +in Sdk.Output.toFileMap (Gen.compile config project) diff --git a/gen/Algebras/Interpreter.dhall b/gen/Algebras/Interpreter.dhall deleted file mode 100644 index 7532153..0000000 --- a/gen/Algebras/Interpreter.dhall +++ /dev/null @@ -1,21 +0,0 @@ -let Deps = ../Deps/package.dhall - -let OnUnsupported = ../Structures/OnUnsupported.dhall - -let Config = - { packageName : Text - , importName : Text - , emitSync : Bool - , onUnsupported : OnUnsupported.Mode - } - -let module = - \(Input : Type) -> - \(Output : Type) -> - let Result = Deps.Lude.Compiled.Type Output - - let Run = Config -> Input -> Result - - in \(run : Run) -> { Input, Output, Result, Run, run } - -in { Config, module } diff --git a/gen/Algebras/Template.dhall b/gen/Algebras/Template.dhall deleted file mode 100644 index 15f2624..0000000 --- a/gen/Algebras/Template.dhall +++ /dev/null @@ -1,5 +0,0 @@ -let module = - \(Params : Type) -> - let Run = Params -> Text in \(run : Run) -> { Params, Run, run } - -in { module } diff --git a/gen/Algebras/package.dhall b/gen/Algebras/package.dhall deleted file mode 100644 index f0f64bd..0000000 --- a/gen/Algebras/package.dhall +++ /dev/null @@ -1 +0,0 @@ -{ Interpreter = ./Interpreter.dhall, Template = ./Template.dhall } diff --git a/gen/Deps/Sdk.dhall b/gen/Deps/Sdk.dhall deleted file mode 100644 index 6cf6014..0000000 --- a/gen/Deps/Sdk.dhall +++ /dev/null @@ -1,3 +0,0 @@ -https://raw.githubusercontent.com/pgenie-io/gen-sdk/v0.11.0/dhall/package.dhall - sha256:8d43544ecb0e612406af3133bdbca51138c704a77a5a29ef62fe034d0e77a3a6 - as Source diff --git a/gen/Deps/package.dhall b/gen/Deps/package.dhall deleted file mode 100644 index b395cd9..0000000 --- a/gen/Deps/package.dhall +++ /dev/null @@ -1,4 +0,0 @@ -{ Sdk = ./Sdk.dhall -, Lude = ./Lude.dhall -, Prelude = ./Prelude.dhall -} diff --git a/gen/Gen.dhall b/gen/Gen.dhall deleted file mode 100644 index 9cb87fb..0000000 --- a/gen/Gen.dhall +++ /dev/null @@ -1,3 +0,0 @@ -let Sdk = ./Deps/Sdk.dhall - -in Sdk.module ./Config.dhall ./compile.dhall diff --git a/gen/compile.dhall b/gen/compile.dhall deleted file mode 100644 index cd12642..0000000 --- a/gen/compile.dhall +++ /dev/null @@ -1,67 +0,0 @@ -let Deps = ./Deps/package.dhall - -let Model = Deps.Sdk.Project - -let Prelude = Deps.Prelude - -let Config = ./Config.dhall - -let OnUnsupported = ./Structures/OnUnsupported.dhall - -let ProjectInterpreter = ./Interpreters/Project.dhall - --- Entry point handed to gen-sdk's module. `config` and each of its fields are --- Optional, so a project may omit the whole config block or any subset of its --- keys; `defaults` collects every fallback in one place (packageName from the --- project name in kebab case, emitSync off, onUnsupported Fail), so a future --- knob's default is added here alongside the others. The async surface is --- always emitted; emitSync adds the sync mirror. -in \(config : Optional Config) -> - \(project : Model.Project) -> - let defaults = - { packageName = project.name.inKebabCase - , emitSync = False - , onUnsupported = OnUnsupported.Mode.Fail - } - - let packageName = - Prelude.Optional.fold - Config - config - Text - (\(c : Config) -> - Prelude.Optional.fold Text c.packageName Text (\(t : Text) -> t) defaults.packageName - ) - defaults.packageName - - let emitSync = - Prelude.Optional.fold - Config - config - Bool - (\(c : Config) -> - Prelude.Optional.fold Bool c.emitSync Bool (\(b : Bool) -> b) defaults.emitSync - ) - defaults.emitSync - - let onUnsupported = - Prelude.Optional.fold - Config - config - OnUnsupported.Mode - ( \(c : Config) -> - Prelude.Optional.fold - OnUnsupported.Mode - c.onUnsupported - OnUnsupported.Mode - (\(m : OnUnsupported.Mode) -> m) - defaults.onUnsupported - ) - defaults.onUnsupported - - let importName = Prelude.Text.replace "-" "_" packageName - - let interpreterConfig = - { packageName, importName, emitSync, onUnsupported } - - in ProjectInterpreter.run interpreterConfig project diff --git a/mise.toml b/mise.toml index 040920b..68903bf 100644 --- a/mise.toml +++ b/mise.toml @@ -35,10 +35,10 @@ run = "bench/generate.sh without" # Regenerates only the "python" artifact: a full 7-artifact generate peaks at # ~31 GB RSS (memory goes to normalizing the generator closure per artifact, # see ci.yml), a single-artifact one at ~10 GB. The temp copy guarantees a -# fresh resolve of the working-tree gen/ (no stale freeze) and keeps pgn's +# fresh resolve of the working-tree src/ (no stale freeze) and keeps pgn's # scratch files out of the repo. [tasks.golden] -description = "Refresh tests/golden from the working-tree gen/ (single-artifact run)" +description = "Refresh tests/golden from the working-tree src/ (single-artifact run)" run = ''' #!/usr/bin/env bash set -euo pipefail @@ -48,14 +48,14 @@ trap 'rm -rf "$tmp"' EXIT cp -R "$root/tests/fixture-project/." "$tmp/fixture" rm -f "$tmp/fixture/freeze1.pgn.yaml" rm -rf "$tmp/fixture/artifacts" -python3 - "$tmp/fixture/project1.pgn.yaml" "$root/gen/Gen.dhall" <<'EOF' +python3 - "$tmp/fixture/project1.pgn.yaml" "$root/src/package.dhall" <<'EOF' import sys from pathlib import Path p = Path(sys.argv[1]) head, sep, _ = p.read_text().partition(" # The variants below") assert sep, "variants marker not found in project1.pgn.yaml" -_ = p.write_text(head.rstrip().replace("../../gen/Gen.dhall", sys.argv[2]) + "\n") +_ = p.write_text(head.rstrip().replace("../../src/package.dhall", sys.argv[2]) + "\n") EOF cd "$tmp/fixture" pgn --database-url "${PGN_TEST_DATABASE_URL:-postgresql://postgres:postgres@localhost:5432/postgres?sslmode=disable}" generate diff --git a/gen/Config.dhall b/src/Config.dhall similarity index 89% rename from gen/Config.dhall rename to src/Config.dhall index 7ec1fe6..1377127 100644 --- a/gen/Config.dhall +++ b/src/Config.dhall @@ -5,7 +5,7 @@ -- unsupported statement/type and its dependents, with a warning) when a query -- or custom type hits a PG shape the generator cannot render; see -- Structures/OnUnsupported.dhall. All fields are Optional so a project may omit --- the whole config block or any subset of its keys; compile.dhall supplies the +-- the whole config block or any subset of its keys; Interpret.dhall supplies the -- defaults. let OnUnsupported = ./Structures/OnUnsupported.dhall diff --git a/src/Deps/Contract.dhall b/src/Deps/Contract.dhall new file mode 100644 index 0000000..ea9e805 --- /dev/null +++ b/src/Deps/Contract.dhall @@ -0,0 +1,2 @@ +https://raw.githubusercontent.com/pgenie-io/gen-contract/v4.0.1/src/package.dhall + sha256:4a130ba7fbaa152a776babbb1bf2994a4833931ca76bde9bf6930d354225651e diff --git a/gen/Deps/Lude.dhall b/src/Deps/Lude.dhall similarity index 100% rename from gen/Deps/Lude.dhall rename to src/Deps/Lude.dhall diff --git a/gen/Deps/Prelude.dhall b/src/Deps/Prelude.dhall similarity index 100% rename from gen/Deps/Prelude.dhall rename to src/Deps/Prelude.dhall diff --git a/src/Deps/Sdk.dhall b/src/Deps/Sdk.dhall new file mode 100644 index 0000000..5602c43 --- /dev/null +++ b/src/Deps/Sdk.dhall @@ -0,0 +1,3 @@ +https://raw.githubusercontent.com/pgenie-io/gen-sdk/v2.0.0/src/package.dhall + sha256:b9def6ab1179bc4aaae7fc6e91977f094f75934cd5755175c294a9e97ca71b15 + as Source diff --git a/src/Interpret.dhall b/src/Interpret.dhall new file mode 100644 index 0000000..de4642f --- /dev/null +++ b/src/Interpret.dhall @@ -0,0 +1,54 @@ +let Contract = ./Deps/Contract.dhall + +let Prelude = ./Deps/Prelude.dhall + +let Config = ./Config.dhall + +let OnUnsupported = ./Structures/OnUnsupported.dhall + +let ProjectInterpreter = ./Interpreters/Project.dhall + +-- Entry point handed to gen-sdk's Sdk.Sigs.generator as `interpret`. Each +-- field of Config is independently Optional, so a project may omit the +-- whole config block (Sdk.Sigs.generator substitutes an all-None +-- defaultConfig, see package.dhall) or any subset of its keys; `defaults` +-- collects every fallback in one place (packageName from the project name in +-- kebab case, emitSync off, onUnsupported Fail). The async surface is always +-- emitted; emitSync adds the sync mirror. +in \(config : Config) -> + \(project : Contract.Project) -> + let defaults = + { packageName = project.name.inKebabCase + , emitSync = False + , onUnsupported = OnUnsupported.Mode.Fail + } + + let packageName = + Prelude.Optional.fold + Text + config.packageName + Text + (\(t : Text) -> t) + defaults.packageName + + let emitSync = + Prelude.Optional.fold + Bool + config.emitSync + Bool + (\(b : Bool) -> b) + defaults.emitSync + + let onUnsupported = + Prelude.Optional.fold + OnUnsupported.Mode + config.onUnsupported + OnUnsupported.Mode + (\(m : OnUnsupported.Mode) -> m) + defaults.onUnsupported + + let importName = Prelude.Text.replace "-" "_" packageName + + let interpreterConfig = { packageName, importName, emitSync, onUnsupported } + + in ProjectInterpreter.run interpreterConfig project diff --git a/gen/Interpreters/CustomType.dhall b/src/Interpreters/CustomType.dhall similarity index 94% rename from gen/Interpreters/CustomType.dhall rename to src/Interpreters/CustomType.dhall index 7927f0a..676f9ab 100644 --- a/gen/Interpreters/CustomType.dhall +++ b/src/Interpreters/CustomType.dhall @@ -1,16 +1,16 @@ -let Deps = ../Deps/package.dhall +let Lude = ../Deps/Lude.dhall -let ImportSet = ../Structures/ImportSet.dhall +let Prelude = ../Deps/Prelude.dhall -let CustomKind = ../Structures/CustomKind.dhall +let Model = ../Deps/Contract.dhall -let Algebra = ../Algebras/Interpreter.dhall +let Sdk = ../Deps/Sdk.dhall -let Lude = Deps.Lude +let ImportSet = ../Structures/ImportSet.dhall -let Prelude = Deps.Prelude +let CustomKind = ../Structures/CustomKind.dhall -let Model = Deps.Sdk.Project +let OnUnsupported = ../Structures/OnUnsupported.dhall let MemberGen = ./Member.dhall @@ -18,6 +18,13 @@ let EnumModule = ../Templates/EnumModule.dhall let CompositeModule = ../Templates/CompositeModule.dhall +let Config = + { packageName : Text + , importName : Text + , emitSync : Bool + , onUnsupported : OnUnsupported.Mode + } + let Input = Model.CustomType let TypeKind = < Enum | Composite > @@ -75,7 +82,7 @@ let renderExtraImports = ) let run = - \(config : Algebra.Config) -> + \(config : Config) -> \(input : Input) -> let typeName = input.name.inPascalCase @@ -174,4 +181,4 @@ let run = } input.definition -in { Input, Output, TypeKind, run } +in Sdk.Sigs.interpreter Config Input Output run diff --git a/gen/Interpreters/Member.dhall b/src/Interpreters/Member.dhall similarity index 95% rename from gen/Interpreters/Member.dhall rename to src/Interpreters/Member.dhall index 672dd18..8d2d27c 100644 --- a/gen/Interpreters/Member.dhall +++ b/src/Interpreters/Member.dhall @@ -1,4 +1,8 @@ -let Deps = ../Deps/package.dhall +let Lude = ../Deps/Lude.dhall + +let Prelude = ../Deps/Prelude.dhall + +let Model = ../Deps/Contract.dhall let ImportSet = ../Structures/ImportSet.dhall @@ -6,14 +10,17 @@ let CustomKind = ../Structures/CustomKind.dhall let PyIdent = ../Structures/PyIdent.dhall -let Algebra = ../Algebras/Interpreter.dhall - -let Lude = Deps.Lude - -let Model = Deps.Sdk.Project +let OnUnsupported = ../Structures/OnUnsupported.dhall let Value = ./Value.dhall +let Config = + { packageName : Text + , importName : Text + , emitSync : Bool + , onUnsupported : OnUnsupported.Mode + } + let Input = Model.Member -- decodeExpr is a Dhall function: given the source expression (e.g. row["x"] or @@ -29,7 +36,7 @@ let Output = } let run = - \(config : Algebra.Config) -> + \(config : Config) -> \(lookup : CustomKind.Lookup) -> \(input : Input) -> -- Result-column / composite-field name becomes a dataclass field and decode @@ -95,7 +102,7 @@ let run = \(fields : List CustomKind.CompositeField) -> \(src : Text) -> let fieldTypes = - Deps.Prelude.Text.concatMapSep + Prelude.Text.concatMapSep ", " CustomKind.CompositeField (\(f : CustomKind.CompositeField) -> f.pyType) @@ -120,7 +127,7 @@ let run = , decodeExpr = passthroughDecode } , Custom = - Deps.Prelude.Optional.fold + Prelude.Optional.fold Model.Name value.scalar.customRef (Lude.Compiled.Type Output) @@ -235,6 +242,6 @@ let run = in Lude.Compiled.flatMap Value.Output Output buildOutput compiledValue -let Run = Algebra.Config -> CustomKind.Lookup -> Input -> Lude.Compiled.Type Output +let Run = Config -> CustomKind.Lookup -> Input -> Lude.Compiled.Type Output in { Input, Output, Run, run } diff --git a/gen/Interpreters/ParamsMember.dhall b/src/Interpreters/ParamsMember.dhall similarity index 93% rename from gen/Interpreters/ParamsMember.dhall rename to src/Interpreters/ParamsMember.dhall index 47bef3f..2a4b469 100644 --- a/gen/Interpreters/ParamsMember.dhall +++ b/src/Interpreters/ParamsMember.dhall @@ -1,17 +1,24 @@ -let Deps = ../Deps/package.dhall +let Lude = ../Deps/Lude.dhall -let ImportSet = ../Structures/ImportSet.dhall +let Prelude = ../Deps/Prelude.dhall -let CustomKind = ../Structures/CustomKind.dhall +let Model = ../Deps/Contract.dhall -let Algebra = ../Algebras/Interpreter.dhall +let ImportSet = ../Structures/ImportSet.dhall -let Lude = Deps.Lude +let CustomKind = ../Structures/CustomKind.dhall -let Model = Deps.Sdk.Project +let OnUnsupported = ../Structures/OnUnsupported.dhall let Value = ./Value.dhall +let Config = + { packageName : Text + , importName : Text + , emitSync : Bool + , onUnsupported : OnUnsupported.Mode + } + let Input = Model.Member let PyIdent = ../Structures/PyIdent.dhall @@ -192,7 +199,7 @@ let scalarIsJsonb = let valueIsArray = \(value : Model.Value) -> - Deps.Prelude.Optional.fold + Prelude.Optional.fold Model.ArraySettings value.arraySettings Bool @@ -204,15 +211,15 @@ let valueIsArray = -- the two must not be collapsed. let isJsonbScalar = \(value : Model.Value) -> - Deps.Prelude.Bool.and - [ scalarIsJsonb value, Deps.Prelude.Bool.not (valueIsArray value) ] + Prelude.Bool.and + [ scalarIsJsonb value, Prelude.Bool.not (valueIsArray value) ] let isJsonScalar = \(value : Model.Value) -> - Deps.Prelude.Bool.and + Prelude.Bool.and [ scalarIsJson value - , Deps.Prelude.Bool.not (scalarIsJsonb value) - , Deps.Prelude.Bool.not (valueIsArray value) + , Prelude.Bool.not (scalarIsJsonb value) + , Prelude.Bool.not (valueIsArray value) ] -- A json/jsonb ARRAY param has no faithful psycopg bind (Jsonb wraps a scalar, @@ -221,10 +228,10 @@ let isJsonScalar = -- in SQL is the supported route (the param then types as text[], not json[]). let isJsonArray = \(value : Model.Value) -> - Deps.Prelude.Bool.and [ scalarIsJson value, valueIsArray value ] + Prelude.Bool.and [ scalarIsJson value, valueIsArray value ] let run = - \(config : Algebra.Config) -> + \(config : Config) -> \(lookup : CustomKind.Lookup) -> \(input : Input) -> let fieldName = pySafeName input.name.inSnakeCase @@ -251,7 +258,7 @@ let run = let compositeBind = \(fields : List CustomKind.CompositeField) -> let joinedFields = - Deps.Prelude.Text.concatMapSep + Prelude.Text.concatMapSep ", " CustomKind.CompositeField ( \(f : CustomKind.CompositeField) -> @@ -265,8 +272,8 @@ let run = -- as a 1-tuple, so force it for exactly one field; concatMapSep -- already inserts the internal comma for two or more. let trailingComma = - if Deps.Prelude.Natural.equal - ( Deps.Prelude.List.length + if Prelude.Natural.equal + ( Prelude.List.length CustomKind.CompositeField fields ) @@ -301,7 +308,7 @@ let run = , needsJsonbImport } - in Deps.Prelude.Optional.fold + in Prelude.Optional.fold Model.Name value.scalar.customRef (Lude.Compiled.Type Output) @@ -377,6 +384,6 @@ let run = in Lude.Compiled.flatMap Value.Output Output buildOutput compiledValue -let Run = Algebra.Config -> CustomKind.Lookup -> Input -> Lude.Compiled.Type Output +let Run = Config -> CustomKind.Lookup -> Input -> Lude.Compiled.Type Output in { Input, Output, Run, run } diff --git a/gen/Interpreters/Primitive.dhall b/src/Interpreters/Primitive.dhall similarity index 86% rename from gen/Interpreters/Primitive.dhall rename to src/Interpreters/Primitive.dhall index 0e1cb70..052ef1b 100644 --- a/gen/Interpreters/Primitive.dhall +++ b/src/Interpreters/Primitive.dhall @@ -1,10 +1,19 @@ -let Deps = ../Deps/package.dhall +let Lude = ../Deps/Lude.dhall + +let Model = ../Deps/Contract.dhall + +let Sdk = ../Deps/Sdk.dhall let ImportSet = ../Structures/ImportSet.dhall -let Algebra = ../Algebras/Interpreter.dhall +let OnUnsupported = ../Structures/OnUnsupported.dhall -let Model = Deps.Sdk.Project +let Config = + { packageName : Text + , importName : Text + , emitSync : Bool + , onUnsupported : OnUnsupported.Mode + } let Input = Model.Primitive @@ -13,16 +22,16 @@ let Output = { pyType : Text, imports : ImportSet.Type } let supported = \(pyType : Text) -> \(imports : ImportSet.Type) -> - Deps.Lude.Compiled.ok Output { pyType, imports } + Lude.Compiled.ok Output { pyType, imports } let unsupported = \(pgType : Text) -> - Deps.Lude.Compiled.report Output [ pgType ] "Unsupported type" + Lude.Compiled.report Output [ pgType ] "Unsupported type" let plain = \(pyType : Text) -> supported pyType ImportSet.empty let run = - \(config : Algebra.Config) -> + \(config : Config) -> \(input : Input) -> merge { Bit = unsupported "bit" @@ -89,4 +98,4 @@ let run = } input -in Algebra.module Input Output run +in Sdk.Sigs.interpreter Config Input Output run diff --git a/gen/Interpreters/Project.dhall b/src/Interpreters/Project.dhall similarity index 98% rename from gen/Interpreters/Project.dhall rename to src/Interpreters/Project.dhall index d51438f..d4fa6d6 100644 --- a/gen/Interpreters/Project.dhall +++ b/src/Interpreters/Project.dhall @@ -1,12 +1,10 @@ -let Deps = ../Deps/package.dhall +let Lude = ../Deps/Lude.dhall -let Algebra = ../Algebras/Interpreter.dhall +let Prelude = ../Deps/Prelude.dhall -let Lude = Deps.Lude +let Model = ../Deps/Contract.dhall -let Prelude = Deps.Prelude - -let Model = Deps.Sdk.Project +let Sdk = ../Deps/Sdk.dhall let CustomKind = ../Structures/CustomKind.dhall @@ -40,6 +38,13 @@ let OnUnsupported = ../Structures/OnUnsupported.dhall let Report = { path : List Text, message : Text } +let Config = + { packageName : Text + , importName : Text + , emitSync : Bool + , onUnsupported : OnUnsupported.Mode + } + let Input = Model.Project let Output = Lude.Files.Type @@ -67,7 +72,7 @@ let withHeader = -- Internal interpreter config (importName etc.) is only needed to satisfy -- Value.run's signature; rendering a composite field's pyType does not read it. let lookupConfig - : Algebra.Config + : Config = { packageName = "" , importName = "" , emitSync = False @@ -175,7 +180,7 @@ let buildLookup = (\(_ : Model.Name) -> CustomKind.TypeKind.Absent) let combineOutputs = - \(config : Algebra.Config) -> + \(config : Config) -> \(input : Input) -> \(queries : List QueryGen.Output) -> -- Already the post-Skip-filter surviving set (see `run`); equal to @@ -430,7 +435,7 @@ let combineOutputs = let QueryCheck = { query : Model.Query, keep : Bool, warning : Optional Report } let run = - \(config : Algebra.Config) -> + \(config : Config) -> \(input : Input) -> let skip = merge { Fail = False, Skip = True } config.onUnsupported @@ -542,4 +547,4 @@ let run = in Lude.Compiled.appendWarnings Output skipWarnings combined -in Algebra.module Input Output run +in Sdk.Sigs.interpreter Config Input Output run diff --git a/gen/Interpreters/Query.dhall b/src/Interpreters/Query.dhall similarity index 94% rename from gen/Interpreters/Query.dhall rename to src/Interpreters/Query.dhall index b0e688d..cce3fcd 100644 --- a/gen/Interpreters/Query.dhall +++ b/src/Interpreters/Query.dhall @@ -1,6 +1,6 @@ -let Deps = ../Deps/package.dhall +let Prelude = ../Deps/Prelude.dhall -let Algebra = ../Algebras/Interpreter.dhall +let Lude = ../Deps/Lude.dhall let ImportSet = ../Structures/ImportSet.dhall @@ -10,6 +10,8 @@ let PyIdent = ../Structures/PyIdent.dhall let Surface = ../Structures/Surface.dhall +let OnUnsupported = ../Structures/OnUnsupported.dhall + let RowsModule = ../Templates/RowsModule.dhall let ResultModule = ./Result.dhall @@ -20,13 +22,16 @@ let ParamsMember = ./ParamsMember.dhall let StatementModule = ../Templates/StatementModule.dhall -let Prelude = Deps.Prelude - -let Lude = Deps.Lude +let Config = + { packageName : Text + , importName : Text + , emitSync : Bool + , onUnsupported : OnUnsupported.Mode + } let Compiled = Lude.Compiled -let Model = Deps.Sdk.Project +let Model = ../Deps/Contract.dhall let Input = Model.Query @@ -46,7 +51,7 @@ let Output = } let render = - \(config : Algebra.Config) -> + \(config : Config) -> \(input : Input) -> \(result : ResultModule.Output) -> \(fragments : QueryFragmentsModule.Output) -> @@ -131,7 +136,7 @@ let render = } let run = - \(config : Algebra.Config) -> + \(config : Config) -> \(lookup : CustomKind.Lookup) -> \(input : Input) -> let rowClassName = input.name.inPascalCase ++ "Row" diff --git a/gen/Interpreters/QueryFragments.dhall b/src/Interpreters/QueryFragments.dhall similarity index 75% rename from gen/Interpreters/QueryFragments.dhall rename to src/Interpreters/QueryFragments.dhall index 066bc75..7813900 100644 --- a/gen/Interpreters/QueryFragments.dhall +++ b/src/Interpreters/QueryFragments.dhall @@ -1,16 +1,21 @@ -let Deps = ../Deps/package.dhall +let Prelude = ../Deps/Prelude.dhall -let Algebra = ../Algebras/Interpreter.dhall +let Lude = ../Deps/Lude.dhall -let Prelude = Deps.Prelude +let Sdk = ../Deps/Sdk.dhall -let Sdk = Deps.Sdk +let Compiled = Lude.Compiled -let Lude = Deps.Lude +let Model = ../Deps/Contract.dhall -let Compiled = Lude.Compiled +let OnUnsupported = ../Structures/OnUnsupported.dhall -let Model = Deps.Sdk.Project +let Config = + { packageName : Text + , importName : Text + , emitSync : Bool + , onUnsupported : OnUnsupported.Mode + } let Input = Model.QueryFragments @@ -47,8 +52,8 @@ let renderSql Prelude.Text.concatMap Model.QueryFragment renderFragment fragments let run = - \(config : Algebra.Config) -> + \(config : Config) -> \(input : Input) -> Compiled.ok Output { sqlLiteral = renderSql input } -in Algebra.module Input Output run +in Sdk.Sigs.interpreter Config Input Output run diff --git a/gen/Interpreters/Result.dhall b/src/Interpreters/Result.dhall similarity index 88% rename from gen/Interpreters/Result.dhall rename to src/Interpreters/Result.dhall index a6638e3..5d652a0 100644 --- a/gen/Interpreters/Result.dhall +++ b/src/Interpreters/Result.dhall @@ -1,20 +1,25 @@ -let Deps = ../Deps/package.dhall +let Prelude = ../Deps/Prelude.dhall -let Algebra = ../Algebras/Interpreter.dhall +let Lude = ../Deps/Lude.dhall + +let Model = ../Deps/Contract.dhall let ImportSet = ../Structures/ImportSet.dhall let CustomKind = ../Structures/CustomKind.dhall -let ResultColumns = ./ResultColumns.dhall - -let Prelude = Deps.Prelude +let OnUnsupported = ../Structures/OnUnsupported.dhall -let Lude = Deps.Lude +let ResultColumns = ./ResultColumns.dhall let Compiled = Lude.Compiled -let Model = Deps.Sdk.Project +let Config = + { packageName : Text + , importName : Text + , emitSync : Bool + , onUnsupported : OnUnsupported.Mode + } let Input = Model.Result @@ -57,7 +62,7 @@ let cardinalityShape cardinality let rowsOutput = - \(config : Algebra.Config) -> + \(config : Config) -> \(lookup : CustomKind.Lookup) -> \(rowClassName : Text) -> \(rows : Model.ResultRows) -> @@ -84,7 +89,7 @@ let rowsOutput = (ResultColumns.run config lookup rowClassName columns) let run = - \(config : Algebra.Config) -> + \(config : Config) -> \(lookup : CustomKind.Lookup) -> \(rowClassName : Text) -> \(input : Input) -> diff --git a/gen/Interpreters/ResultColumns.dhall b/src/Interpreters/ResultColumns.dhall similarity index 84% rename from gen/Interpreters/ResultColumns.dhall rename to src/Interpreters/ResultColumns.dhall index ef550ec..e0bbfaa 100644 --- a/gen/Interpreters/ResultColumns.dhall +++ b/src/Interpreters/ResultColumns.dhall @@ -1,20 +1,25 @@ -let Deps = ../Deps/package.dhall +let Prelude = ../Deps/Prelude.dhall -let Algebra = ../Algebras/Interpreter.dhall +let Lude = ../Deps/Lude.dhall + +let Model = ../Deps/Contract.dhall let ImportSet = ../Structures/ImportSet.dhall let CustomKind = ../Structures/CustomKind.dhall -let Member = ./Member.dhall - -let Prelude = Deps.Prelude +let OnUnsupported = ../Structures/OnUnsupported.dhall -let Lude = Deps.Lude +let Member = ./Member.dhall let Compiled = Lude.Compiled -let Model = Deps.Sdk.Project +let Config = + { packageName : Text + , importName : Text + , emitSync : Bool + , onUnsupported : OnUnsupported.Mode + } let Input = List Model.Member @@ -53,7 +58,7 @@ let assemble } let run = - \(config : Algebra.Config) -> + \(config : Config) -> \(lookup : CustomKind.Lookup) -> \(rowClassName : Text) -> \(input : Input) -> diff --git a/gen/Interpreters/Scalar.dhall b/src/Interpreters/Scalar.dhall similarity index 77% rename from gen/Interpreters/Scalar.dhall rename to src/Interpreters/Scalar.dhall index b0eff70..63ec788 100644 --- a/gen/Interpreters/Scalar.dhall +++ b/src/Interpreters/Scalar.dhall @@ -1,15 +1,22 @@ -let Deps = ../Deps/package.dhall +let Lude = ../Deps/Lude.dhall -let ImportSet = ../Structures/ImportSet.dhall +let Model = ../Deps/Contract.dhall -let Algebra = ../Algebras/Interpreter.dhall +let Sdk = ../Deps/Sdk.dhall -let Lude = Deps.Lude +let ImportSet = ../Structures/ImportSet.dhall -let Model = Deps.Sdk.Project +let OnUnsupported = ../Structures/OnUnsupported.dhall let Primitive = ./Primitive.dhall +let Config = + { packageName : Text + , importName : Text + , emitSync : Bool + , onUnsupported : OnUnsupported.Mode + } + let Input = Model.Scalar -- Passthrough for primitives; Custom is opaque here. The enum-vs-composite @@ -24,7 +31,7 @@ let Output = } let run = - \(config : Algebra.Config) -> + \(config : Config) -> \(input : Input) -> merge { Primitive = @@ -52,4 +59,4 @@ let run = } input -in Algebra.module Input Output run /\ { ScalarDecode } +in Sdk.Sigs.interpreter Config Input Output run /\ { ScalarDecode } diff --git a/gen/Interpreters/Value.dhall b/src/Interpreters/Value.dhall similarity index 80% rename from gen/Interpreters/Value.dhall rename to src/Interpreters/Value.dhall index de69fd0..e1c8d6a 100644 --- a/gen/Interpreters/Value.dhall +++ b/src/Interpreters/Value.dhall @@ -1,17 +1,24 @@ -let Deps = ../Deps/package.dhall +let Lude = ../Deps/Lude.dhall -let ImportSet = ../Structures/ImportSet.dhall +let Prelude = ../Deps/Prelude.dhall -let Algebra = ../Algebras/Interpreter.dhall +let Model = ../Deps/Contract.dhall -let Lude = Deps.Lude +let Sdk = ../Deps/Sdk.dhall -let Prelude = Deps.Prelude +let ImportSet = ../Structures/ImportSet.dhall -let Model = Deps.Sdk.Project +let OnUnsupported = ../Structures/OnUnsupported.dhall let Scalar = ./Scalar.dhall +let Config = + { packageName : Text + , importName : Text + , emitSync : Bool + , onUnsupported : OnUnsupported.Mode + } + let Input = Model.Value let Output = @@ -23,7 +30,7 @@ let Output = } let run = - \(config : Algebra.Config) -> + \(config : Config) -> \(input : Input) -> Lude.Compiled.map Scalar.Output @@ -62,4 +69,4 @@ let run = ) (Scalar.run config input.scalar) -in Algebra.module Input Output run +in Sdk.Sigs.interpreter Config Input Output run diff --git a/gen/Structures/CustomKind.dhall b/src/Structures/CustomKind.dhall similarity index 95% rename from gen/Structures/CustomKind.dhall rename to src/Structures/CustomKind.dhall index 22d3031..f2c4a1b 100644 --- a/gen/Structures/CustomKind.dhall +++ b/src/Structures/CustomKind.dhall @@ -1,6 +1,4 @@ -let Deps = ../Deps/package.dhall - -let Model = Deps.Sdk.Project +let Model = ../Deps/Contract.dhall -- A composite field as the decode/encode sites need it: the Python attribute -- name and the rendered Python type (already nullability-applied). Threaded so diff --git a/gen/Structures/ImportSet.dhall b/src/Structures/ImportSet.dhall similarity index 100% rename from gen/Structures/ImportSet.dhall rename to src/Structures/ImportSet.dhall diff --git a/gen/Structures/OnUnsupported.dhall b/src/Structures/OnUnsupported.dhall similarity index 100% rename from gen/Structures/OnUnsupported.dhall rename to src/Structures/OnUnsupported.dhall diff --git a/gen/Structures/PyIdent.dhall b/src/Structures/PyIdent.dhall similarity index 100% rename from gen/Structures/PyIdent.dhall rename to src/Structures/PyIdent.dhall diff --git a/gen/Structures/Surface.dhall b/src/Structures/Surface.dhall similarity index 100% rename from gen/Structures/Surface.dhall rename to src/Structures/Surface.dhall diff --git a/gen/Templates/CompositeModule.dhall b/src/Templates/CompositeModule.dhall similarity index 80% rename from gen/Templates/CompositeModule.dhall rename to src/Templates/CompositeModule.dhall index 198c77d..b1ab3e0 100644 --- a/gen/Templates/CompositeModule.dhall +++ b/src/Templates/CompositeModule.dhall @@ -1,6 +1,6 @@ -let Deps = ../Deps/package.dhall +let Prelude = ../Deps/Prelude.dhall -let Algebra = ../Algebras/Template.dhall +let Sdk = ../Deps/Sdk.dhall let Field = { fieldName : Text, fieldType : Text } @@ -12,7 +12,7 @@ let Params = { typeName : Text, extraImports : List Text, fields : List Field } let run = \(params : Params) -> let fieldLines = - Deps.Prelude.Text.concatMapSep + Prelude.Text.concatMapSep "\n" Field ( \(field : Field) -> @@ -21,12 +21,12 @@ let run = params.fields let imports = - if Deps.Prelude.List.null Text params.extraImports + if Prelude.List.null Text params.extraImports then "from dataclasses import dataclass" else '' from dataclasses import dataclass - ${Deps.Prelude.Text.concatSep "\n" params.extraImports}'' + ${Prelude.Text.concatSep "\n" params.extraImports}'' in '' ${imports} @@ -43,4 +43,4 @@ let run = ${fieldLines} '' -in Algebra.module Params run /\ { Field } +in Sdk.Sigs.template Params run /\ { Field } diff --git a/gen/Templates/CoreModule.dhall b/src/Templates/CoreModule.dhall similarity index 95% rename from gen/Templates/CoreModule.dhall rename to src/Templates/CoreModule.dhall index d3938c2..34a4943 100644 --- a/gen/Templates/CoreModule.dhall +++ b/src/Templates/CoreModule.dhall @@ -1,4 +1,4 @@ -let Algebra = ../Algebras/Template.dhall +let Sdk = ../Deps/Sdk.dhall -- The surface-agnostic core of a generated package, emitted once at -- _generated/_core.py. It owns the names shared by every module and by both the @@ -43,4 +43,4 @@ let content = ) '' -in Algebra.module {} (\(_ : {}) -> content) +in Sdk.Sigs.template {} (\(_ : {}) -> content) diff --git a/gen/Templates/EnumModule.dhall b/src/Templates/EnumModule.dhall similarity index 68% rename from gen/Templates/EnumModule.dhall rename to src/Templates/EnumModule.dhall index 7f82ebf..1ebf486 100644 --- a/gen/Templates/EnumModule.dhall +++ b/src/Templates/EnumModule.dhall @@ -1,6 +1,6 @@ -let Deps = ../Deps/package.dhall +let Prelude = ../Deps/Prelude.dhall -let Algebra = ../Algebras/Template.dhall +let Sdk = ../Deps/Sdk.dhall let Variant = { memberName : Text, pgValue : Text } @@ -16,18 +16,18 @@ let run = let escapeLabel : Text -> Text = \(raw : Text) -> - Deps.Prelude.Function.composeList + Prelude.Function.composeList Text - [ Deps.Prelude.Text.replace "\\" "\\\\" - , Deps.Prelude.Text.replace "\r" "\\r" - , Deps.Prelude.Text.replace "\n" "\\n" - , Deps.Prelude.Text.replace "\t" "\\t" - , Deps.Prelude.Text.replace "\"" "\\\"" + [ Prelude.Text.replace "\\" "\\\\" + , Prelude.Text.replace "\r" "\\r" + , Prelude.Text.replace "\n" "\\n" + , Prelude.Text.replace "\t" "\\t" + , Prelude.Text.replace "\"" "\\\"" ] raw let memberLines = - Deps.Prelude.Text.concatMapSep + Prelude.Text.concatMapSep "\n" Variant ( \(variant : Variant) -> @@ -43,4 +43,4 @@ let run = ${memberLines} '' -in Algebra.module Params run /\ { Variant } +in Sdk.Sigs.template Params run /\ { Variant } diff --git a/gen/Templates/FacadeModule.dhall b/src/Templates/FacadeModule.dhall similarity index 96% rename from gen/Templates/FacadeModule.dhall rename to src/Templates/FacadeModule.dhall index 2beb74c..18d6f2e 100644 --- a/gen/Templates/FacadeModule.dhall +++ b/src/Templates/FacadeModule.dhall @@ -1,8 +1,6 @@ -let Deps = ../Deps/package.dhall +let Prelude = ../Deps/Prelude.dhall -let Algebra = ../Algebras/Template.dhall - -let Prelude = Deps.Prelude +let Sdk = ../Deps/Sdk.dhall -- A statement's public surface: the function and, when the query returns rows, -- its frozen Row dataclass. functionName doubles as the leaf module name. @@ -126,4 +124,4 @@ let run = ${allEntries}] '' -in Algebra.module Params run /\ { StatementExport, TypeExport } +in Sdk.Sigs.template Params run /\ { StatementExport, TypeExport } diff --git a/gen/Templates/InitModule.dhall b/src/Templates/InitModule.dhall similarity index 87% rename from gen/Templates/InitModule.dhall rename to src/Templates/InitModule.dhall index f609ed6..cf5fc9f 100644 --- a/gen/Templates/InitModule.dhall +++ b/src/Templates/InitModule.dhall @@ -1,4 +1,4 @@ -let Algebra = ../Algebras/Template.dhall +let Sdk = ../Deps/Sdk.dhall -- A minimal package __init__.py: a module docstring plus an empty __all__. -- Used for the _generated subpackage root and the statements subpackage. The @@ -15,4 +15,4 @@ let render = __all__: list[str] = [] '' -in Algebra.module Params render +in Sdk.Sigs.template Params render diff --git a/gen/Templates/RegisterModule.dhall b/src/Templates/RegisterModule.dhall similarity index 96% rename from gen/Templates/RegisterModule.dhall rename to src/Templates/RegisterModule.dhall index ec6886b..cc94bfb 100644 --- a/gen/Templates/RegisterModule.dhall +++ b/src/Templates/RegisterModule.dhall @@ -1,11 +1,9 @@ -let Deps = ../Deps/package.dhall +let Prelude = ../Deps/Prelude.dhall -let Algebra = ../Algebras/Template.dhall +let Sdk = ../Deps/Sdk.dhall let Surface = ../Structures/Surface.dhall -let Prelude = Deps.Prelude - -- Per-connection type registration, emitted once per surface. psycopg decodes an -- unregistered composite as a text string; registering its CompositeInfo makes -- it decode to a namedtuple, which the generated decode then splats into the @@ -96,4 +94,4 @@ let run = in Prelude.Text.concatSep "\n" allLines ++ "\n" -in Algebra.module Params run +in Sdk.Sigs.template Params run diff --git a/gen/Templates/RowsModule.dhall b/src/Templates/RowsModule.dhall similarity index 94% rename from gen/Templates/RowsModule.dhall rename to src/Templates/RowsModule.dhall index 0107fa2..18da31b 100644 --- a/gen/Templates/RowsModule.dhall +++ b/src/Templates/RowsModule.dhall @@ -1,10 +1,10 @@ -let Deps = ../Deps/package.dhall +let Prelude = ../Deps/Prelude.dhall -let Algebra = ../Algebras/Template.dhall +let Lude = ../Deps/Lude.dhall -let ImportSet = ../Structures/ImportSet.dhall +let Sdk = ../Deps/Sdk.dhall -let Prelude = Deps.Prelude +let ImportSet = ../Structures/ImportSet.dhall -- The shared row module: every query's frozen Row dataclass and its decode -- function live here once, so the async and sync statement modules import the @@ -25,7 +25,7 @@ let indentAll \(text : Text) -> let pad = Prelude.Text.replicate n " " - in pad ++ Deps.Lude.Text.indentNonEmpty n text + in pad ++ Lude.Text.indentNonEmpty n text let importLineIf : Bool -> Text -> List Text @@ -118,4 +118,4 @@ let run = ++ Prelude.Text.concatMapSep "\n\n\n" RowDef renderRow params.rows ++ "\n" -in Algebra.module Params run /\ { RowDef } +in Sdk.Sigs.template Params run /\ { RowDef } diff --git a/gen/Templates/RuntimeModule.dhall b/src/Templates/RuntimeModule.dhall similarity index 98% rename from gen/Templates/RuntimeModule.dhall rename to src/Templates/RuntimeModule.dhall index ddaf53a..baa56bc 100644 --- a/gen/Templates/RuntimeModule.dhall +++ b/src/Templates/RuntimeModule.dhall @@ -1,4 +1,4 @@ -let Algebra = ../Algebras/Template.dhall +let Sdk = ../Deps/Sdk.dhall -- The fixed _runtime.py body, emitted once per generated package. No per-query -- customization. Mirrors DESIGN section 3 with two strict-clean adjustments the @@ -160,5 +160,5 @@ let syncContent = _ = cur.execute(sql, params) '' -in Algebra.module {} (\(_ : {}) -> content) +in Sdk.Sigs.template {} (\(_ : {}) -> content) /\ { runSync = \(_ : {}) -> syncContent } diff --git a/gen/Templates/StatementModule.dhall b/src/Templates/StatementModule.dhall similarity index 97% rename from gen/Templates/StatementModule.dhall rename to src/Templates/StatementModule.dhall index f717aa7..f2a6f61 100644 --- a/gen/Templates/StatementModule.dhall +++ b/src/Templates/StatementModule.dhall @@ -1,13 +1,13 @@ -let Algebra = ../Algebras/Template.dhall +let Prelude = ../Deps/Prelude.dhall -let Deps = ../Deps/package.dhall +let Lude = ../Deps/Lude.dhall + +let Sdk = ../Deps/Sdk.dhall let ImportSet = ../Structures/ImportSet.dhall let Surface = ../Structures/Surface.dhall -let Prelude = Deps.Prelude - -- Prefix every line (including the first) with `n` spaces, leaving blank lines -- untouched so trailing whitespace never appears. let indentAll @@ -16,7 +16,7 @@ let indentAll \(text : Text) -> let pad = Prelude.Text.replicate n " " - in pad ++ Deps.Lude.Text.indentNonEmpty n text + in pad ++ Lude.Text.indentNonEmpty n text -- A statement module is the thin per-surface I/O wrapper: it imports its Row -- dataclass and decode function from the shared `_rows` module and renders one @@ -187,7 +187,7 @@ let renderCall then "return ${await}${params.helperName}(conn, _SQL, params, ${params.decodeName})" else "return ${await}${params.helperName}(conn, _SQL, params)" -in Algebra.module +in Sdk.Sigs.template Params ( \(params : Params) -> renderImports params diff --git a/gen/Templates/TypesInit.dhall b/src/Templates/TypesInit.dhall similarity index 73% rename from gen/Templates/TypesInit.dhall rename to src/Templates/TypesInit.dhall index 7583526..3182085 100644 --- a/gen/Templates/TypesInit.dhall +++ b/src/Templates/TypesInit.dhall @@ -1,6 +1,6 @@ -let Deps = ../Deps/package.dhall +let Prelude = ../Deps/Prelude.dhall -let Algebra = ../Algebras/Template.dhall +let Sdk = ../Deps/Sdk.dhall let Export = { moduleName : Text, typeName : Text } @@ -9,7 +9,7 @@ let Params = { exports : List Export } let run = \(params : Params) -> let exportLines = - Deps.Prelude.Text.concatMapSep + Prelude.Text.concatMapSep "\n" Export ( \(export : Export) -> @@ -21,4 +21,4 @@ let run = ${exportLines} '' -in Algebra.module Params run /\ { Export } +in Sdk.Sigs.template Params run /\ { Export } diff --git a/src/package.dhall b/src/package.dhall new file mode 100644 index 0000000..3d07cf7 --- /dev/null +++ b/src/package.dhall @@ -0,0 +1,16 @@ +let Sdk = ./Deps/Sdk.dhall + +let OnUnsupported = ./Structures/OnUnsupported.dhall + +let Config = ./Config.dhall + +let Config/default + : Config + = { packageName = None Text + , emitSync = None Bool + , onUnsupported = None OnUnsupported.Mode + } + +let interpret = ./Interpret.dhall + +in Sdk.Sigs.generator Config Config/default interpret diff --git a/tests/_harness.py b/tests/_harness.py index 037ac41..9e5ffbd 100644 --- a/tests/_harness.py +++ b/tests/_harness.py @@ -23,7 +23,7 @@ DEFAULT_MAX_RSS_GB = 40.0 HERE = Path(__file__).resolve().parent -GEN_DIR = HERE.parent / "gen" +SRC_DIR = HERE.parent / "src" FIXTURE_PROJECT = HERE / "fixture-project" GOLDEN_DIR = HERE / "golden" diff --git a/tests/conftest.py b/tests/conftest.py index d0ccc23..5bac571 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -15,9 +15,9 @@ from tests._harness import ( FIXTURE_PROJECT, - GEN_DIR, GOLDEN_DIR, HERE, + SRC_DIR, admin_database_url, effective_database_name, run_pgn, @@ -67,15 +67,15 @@ def fixture_copy(tmp_path: Path) -> Path: def generated_tree(pgn_bin: str, pgn_admin_url: str, tmp_path_factory: pytest.TempPathFactory) -> Path: """Generate the fixture client once and return its artifacts/python dir. - The generator path in project1.pgn.yaml is `../../gen/Gen.dhall`, + The generator path in project1.pgn.yaml is `../../src/package.dhall`, relative to the fixture project. To keep it resolvable the copy mirrors the - real layout: `/gen` and `/tests/fixture-project`. The freeze + real layout: `/src` and `/tests/fixture-project`. The freeze file is dropped so pgn re-resolves the working-tree generator instead of a - cached hash (a stale freeze makes pgn ignore gen/ edits and silently + cached hash (a stale freeze makes pgn ignore src/ edits and silently emit the old output). """ root = tmp_path_factory.mktemp("pygen") - _ = shutil.copytree(GEN_DIR, root / "gen") + _ = shutil.copytree(SRC_DIR, root / "src") project = shutil.copytree(FIXTURE_PROJECT, root / "tests" / "fixture-project") (project / "freeze1.pgn.yaml").unlink(missing_ok=True) shutil.rmtree(project / "artifacts", ignore_errors=True) diff --git a/tests/fixture-project/project1.pgn.yaml b/tests/fixture-project/project1.pgn.yaml index f6bec82..ff75999 100644 --- a/tests/fixture-project/project1.pgn.yaml +++ b/tests/fixture-project/project1.pgn.yaml @@ -4,7 +4,7 @@ version: 0.0.0 postgres: 18 artifacts: python: - gen: ../../gen/Gen.dhall + gen: ../../src/package.dhall config: packageName: specimen-client emitSync: true @@ -12,25 +12,25 @@ artifacts: # Optional Config knobs (undocumented by pgn itself); see # tests/test_config_variants.py for the assertions. python-name-only: - gen: ../../gen/Gen.dhall + gen: ../../src/package.dhall config: packageName: name-only-client python-sync-only: - gen: ../../gen/Gen.dhall + gen: ../../src/package.dhall config: emitSync: true python-empty: - gen: ../../gen/Gen.dhall + gen: ../../src/package.dhall config: {} python-bare: - gen: ../../gen/Gen.dhall + gen: ../../src/package.dhall python-unknown-key: - gen: ../../gen/Gen.dhall + gen: ../../src/package.dhall config: packageName: unknown-key-client bogusField: 1 python-null: - gen: ../../gen/Gen.dhall + gen: ../../src/package.dhall config: packageName: null-client emitSync: null diff --git a/tests/test_unsupported_types.py b/tests/test_unsupported_types.py index 9d99eab..642db87 100644 --- a/tests/test_unsupported_types.py +++ b/tests/test_unsupported_types.py @@ -24,14 +24,14 @@ import pytest -from tests._harness import FIXTURE_PROJECT, GEN_DIR, HERE, run_pgn +from tests._harness import FIXTURE_PROJECT, HERE, SRC_DIR, run_pgn HARNESS_ROOT = HERE.parent def test_unsupported_pg_type_fails_loudly(pgn_bin: str, pgn_admin_url: str, tmp_path: Path) -> None: root = tmp_path / "pygen" - _ = shutil.copytree(GEN_DIR, root / "gen") + _ = shutil.copytree(SRC_DIR, root / "src") project = shutil.copytree(FIXTURE_PROJECT, root / "tests" / "fixture-project") (project / "freeze1.pgn.yaml").unlink(missing_ok=True) shutil.rmtree(project / "artifacts", ignore_errors=True) @@ -51,7 +51,7 @@ def test_unsupported_pg_type_fails_loudly(pgn_bin: str, pgn_admin_url: str, tmp_ def test_json_array_param_fails_loudly(pgn_bin: str, pgn_admin_url: str, tmp_path: Path) -> None: root = tmp_path / "pygen" - _ = shutil.copytree(GEN_DIR, root / "gen") + _ = shutil.copytree(SRC_DIR, root / "src") project = shutil.copytree(FIXTURE_PROJECT, root / "tests" / "fixture-project") (project / "freeze1.pgn.yaml").unlink(missing_ok=True) shutil.rmtree(project / "artifacts", ignore_errors=True) @@ -66,7 +66,7 @@ def test_json_array_param_fails_loudly(pgn_bin: str, pgn_admin_url: str, tmp_pat "postgres: 18\n" "artifacts:\n" " python:\n" - " gen: ../../gen/Gen.dhall\n" + " gen: ../../src/package.dhall\n" " config:\n" " onUnsupported: Fail\n" ) @@ -104,7 +104,7 @@ def test_skip_unsupported_drops_offending_units_and_cascades( golden package is held to. """ root = tmp_path / "pygen" - _ = shutil.copytree(GEN_DIR, root / "gen") + _ = shutil.copytree(SRC_DIR, root / "src") project = shutil.copytree(FIXTURE_PROJECT, root / "tests" / "fixture-project") (project / "freeze1.pgn.yaml").unlink(missing_ok=True) shutil.rmtree(project / "artifacts", ignore_errors=True) @@ -129,7 +129,7 @@ def test_skip_unsupported_drops_offending_units_and_cascades( "postgres: 18\n" "artifacts:\n" " python:\n" - " gen: ../../gen/Gen.dhall\n" + " gen: ../../src/package.dhall\n" " config:\n" " onUnsupported: Skip\n" )