terp-cli 0.5.2__tar.gz → 0.5.4__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.
@@ -0,0 +1,16 @@
1
+ Metadata-Version: 2.4
2
+ Name: terp-cli
3
+ Version: 0.5.4
4
+ Summary: Terp command-line tool — inspect, scaffolding, migrations, checks, api-docs.
5
+ License-Expression: Apache-2.0
6
+ Requires-Python: >=3.13
7
+ Requires-Dist: terp-arch==0.5.4
8
+ Requires-Dist: terp-core==0.5.4
9
+ Requires-Dist: terp-migrations==0.5.4
10
+ Provides-Extra: jobs
11
+ Requires-Dist: terp-cap-outbox==0.5.4; extra == 'jobs'
12
+ Requires-Dist: terp-cap-scheduler-apscheduler==0.5.4; extra == 'jobs'
13
+ Provides-Extra: scheduler
14
+ Requires-Dist: terp-cap-scheduler-apscheduler==0.5.4; extra == 'scheduler'
15
+ Provides-Extra: worker
16
+ Requires-Dist: terp-cap-outbox==0.5.4; extra == 'worker'
@@ -4,29 +4,29 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "terp-cli"
7
- version = "0.5.2"
7
+ version = "0.5.4"
8
8
  description = "Terp command-line tool — inspect, scaffolding, migrations, checks, api-docs."
9
9
  requires-python = ">=3.13"
10
10
  license = "Apache-2.0"
11
11
  dependencies = [
12
- "terp-core==0.5.2",
12
+ "terp-core==0.5.4",
13
13
  # `terp migrate` delegates to terp-migrations (lazily imported), keeping Alembic
14
14
  # off the path for `terp inspect` / `terp guide` while making migrations work.
15
- "terp-migrations==0.5.2",
15
+ "terp-migrations==0.5.4",
16
16
  # `terp guide rules` projects the live rule registry; terp-arch is imported lazily
17
17
  # (only when that topic is rendered), so it stays off the common `terp guide` path.
18
- "terp-arch==0.5.2",
18
+ "terp-arch==0.5.4",
19
19
  ]
20
20
 
21
21
  # Job-process adapters stay optional: the base CLI must not install the table-owning outbox
22
22
  # or a scheduler engine into apps that only use inspect/check/migrate. Deployments can select
23
23
  # one process role, while `jobs` is the convenient complete jobs-process bundle.
24
24
  [project.optional-dependencies]
25
- worker = ["terp-cap-outbox==0.5.2"]
26
- scheduler = ["terp-cap-scheduler-apscheduler==0.5.2"]
25
+ worker = ["terp-cap-outbox==0.5.4"]
26
+ scheduler = ["terp-cap-scheduler-apscheduler==0.5.4"]
27
27
  jobs = [
28
- "terp-cap-outbox==0.5.2",
29
- "terp-cap-scheduler-apscheduler==0.5.2",
28
+ "terp-cap-outbox==0.5.4",
29
+ "terp-cap-scheduler-apscheduler==0.5.4",
30
30
  ]
31
31
 
32
32
  [project.scripts]
@@ -372,25 +372,70 @@ Domain events (eventbus capability)
372
372
  "testing": """\
373
373
  Testing a Terp app (process-global runtime isolation)
374
374
 
375
- - create_app installs SIX process globals per app: the audit policy+sink, the event
376
- catalog+dispatcher, the job catalog+queue, the schedule catalog, the password policy,
377
- and the decrypt call site. In a test process they outlive the app that installed them.
378
- - You get isolation for free. terp-core ships a pytest plugin (terp.core.testing,
379
- registered under pytest11) whose autouse fixture SNAPSHOTS every runtime before a
380
- test and RESTORES it after. No conftest.py line, no opt-in. Without it a suite goes
381
- order-dependent: green together, red alone - the sharpest failure mode there is,
382
- because the green is the wrong answer.
383
- - Isolation does not INSTALL a runtime your test needs - that is your decision:
375
+ WHAT YOU MUST STILL DO YOURSELF. The platform UNDOES a runtime; it never INSTALLS the
376
+ one your test needs. That distinction is the whole of testing on Terp:
384
377
  * whole runtime -> compose the app in a fixture (see apps/example/tests/conftest.py,
385
378
  where db_engine + app_db are function-scoped and build() runs per test)
386
379
  * only the event bus -> the `terp_events` fixture, which installs a catalog
387
380
  (and dispatcher) for the duration of one test:
388
381
  terp_events(event_catalog, dispatcher=dispatch_in_process)
382
+ Annotate it `InstallEvents` (exported from terp.core.testing) - it carries
383
+ configure_events' real signature, so a wrong catalog is a type error.
389
384
  * asserting on what was written to a FAKE/bare session -> `terp_default_runtime`,
390
385
  which states the baseline instead of assuming it. Without it a composed app's
391
386
  durable audit sink quietly adds audit rows to the session under assertion.
392
- - If a test passes in the suite and fails alone (or vice versa), it is reading a
393
- runtime it never installed. Install it explicitly; never re-order the suite.
387
+ Deleting a conftest that INSTALLS something is not part of adopting the plugin.
388
+
389
+ WHAT YOU GET FOR FREE. terp-core ships a pytest plugin (terp.core.testing, registered
390
+ under pytest11) whose autouse fixture SNAPSHOTS every runtime before a test and
391
+ RESTORES it after. No conftest.py line, no opt-in. Without it a suite goes
392
+ order-dependent: green together, red alone - the sharpest failure mode there is,
393
+ because the green is the wrong answer.
394
+
395
+ THE SIX SEAMS. create_app installs six process globals per app; in a test process they
396
+ outlive the app that installed them. Restored automatically -- but installed by you:
397
+
398
+ seam what create_app installs who installs it in a test
399
+ audit audit policy + durable sink compose the app, else the log-only
400
+ default (terp_default_runtime)
401
+ events event catalog + dispatcher compose the app, or `terp_events`
402
+ jobs job catalog + queue compose the app
403
+ scheduling schedule catalog compose the app
404
+ passwords password policy compose the app, else the default
405
+ secrets the decrypt call site compose the app (a secrets capability)
406
+
407
+ STRICT MODE. Restoring is faithful, which is also its blind spot: a runtime installed
408
+ BEFORE the first test (a stray `import app.main` at collection time, a module-scope
409
+ create_app()) is part of the snapshot, so it is restored before every test and covers
410
+ every test equally - green together, red alone, with nothing having leaked. Strict mode
411
+ follows the snapshot with a RESET, so every test starts from the platform baseline and
412
+ a test that only ever passed on ambient state fails where it stands:
413
+ [tool.pytest.ini_options]
414
+ terp_strict_isolation = true # or: pytest --terp-strict-isolation
415
+ New projects start with it on, and the framework runs its own suites that way. Switching
416
+ it on in an existing suite can turn a green red - that red is the finding, not the
417
+ regression: the test was reading a runtime it never installed. Install it explicitly;
418
+ never re-order the suite.
419
+
420
+ AND READ THE GREEN HONESTLY. Strict mode resets BEFORE fixtures run, so it can only see
421
+ installs that happen before the suite. An autouse fixture that installs a runtime for a
422
+ whole package is invisible to it - every test gets a bus it never asked for, and strict
423
+ mode agrees with you every time. So: a green strict run does NOT mean your installs are
424
+ precise, only that none of them happen before the suite. Ask the other half instead:
425
+ uv run pytest --terp-report-runtime-installs
426
+ which reports, per seam, the tests that installed it. Read the SHAPE of the answer: one or
427
+ two ids under a seam is a test installing what it needs; every id in a package under one
428
+ seam is a fixture installing it for them. Then make that installer NAMED and non-autouse,
429
+ and let the tests that emit request it. The ones that then fail were the ones being carried.
430
+
431
+ TWO FIXTURES, ONE SEAM. A seam holds one runtime, so the last install wins - and pytest
432
+ decides "last" from the FIXTURE GRAPH, not from the order of your test's parameters. A tap
433
+ that installs a recording dispatcher after the catalog fixture must DEPEND on it:
434
+ @pytest.fixture
435
+ def emitted(events_runtime: None, terp_events: InstallEvents) -> list[object]:
436
+ ...
437
+ Getting it the wrong way round is silent: the tap installs first, the catalog fixture
438
+ overwrites it, and the recorder simply records nothing.
394
439
  """,
395
440
  "jobs": """\
396
441
  Background jobs (terp.core.enqueue + JobCatalog)
@@ -729,14 +774,26 @@ Data collections (DataView)
729
774
  refused by the boundary lint. It gives search, sorting, pagination, column management,
730
775
  selection + batch actions, row actions, expandable rows, and persisted view
731
776
  preferences, driven by a repository port.
732
- - Client-side (small collections — rows already in memory):
733
- const repo = useMemo(() => new InMemoryDataViewRepository(rows), [rows]);
734
- <DataView repository={repo} columns={columns} keyField="id" />
777
+ - Client-side (small collections — rows already in memory). The repository owns row
778
+ identity and value access; the component never reads a field itself:
779
+ const repo = useMemo(
780
+ () =>
781
+ new InMemoryDataViewRepository(rows, {
782
+ getRowId: (r) => r.id,
783
+ getValue: (r, col) => r[col as keyof Row],
784
+ searchFields: ["title", "status"],
785
+ }),
786
+ [rows],
787
+ );
788
+ <DataView repository={repo} columns={columns} viewId="notes.list" />
735
789
  - Server-side (large collections — let the backend paginate/sort/filter):
736
790
  const repo = useMemo(() => new HttpDataViewRepository({...}), [client]);
737
791
  and keep query state in the URL with useServerDataView.
738
- - Columns declare {key, header, render?}; header text is UiText. Row actions and batch
739
- actions are declared as data (the component renders the token-styled controls).
792
+ - Columns declare {id, header, accessor?, cell?, enableSorting?, meta?} — `id` is the
793
+ column key the repository's getValue receives, `accessor` reads the value and `cell`
794
+ renders it; header text is UiText. There is no `keyField` prop: row identity is
795
+ `getRowId` on the repository. Row actions and batch actions are declared as data
796
+ (the component renders the token-styled controls).
740
797
  - Persist per-user view preferences via the ViewStateRepository seam
741
798
  (LocalStorageViewStateRepository for the browser; InMemoryViewStateRepository in tests).
742
799
  - For a simple titled CRUD list (no tables), ResourceList over useResource is the
terp_cli-0.5.2/PKG-INFO DELETED
@@ -1,16 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: terp-cli
3
- Version: 0.5.2
4
- Summary: Terp command-line tool — inspect, scaffolding, migrations, checks, api-docs.
5
- License-Expression: Apache-2.0
6
- Requires-Python: >=3.13
7
- Requires-Dist: terp-arch==0.5.2
8
- Requires-Dist: terp-core==0.5.2
9
- Requires-Dist: terp-migrations==0.5.2
10
- Provides-Extra: jobs
11
- Requires-Dist: terp-cap-outbox==0.5.2; extra == 'jobs'
12
- Requires-Dist: terp-cap-scheduler-apscheduler==0.5.2; extra == 'jobs'
13
- Provides-Extra: scheduler
14
- Requires-Dist: terp-cap-scheduler-apscheduler==0.5.2; extra == 'scheduler'
15
- Provides-Extra: worker
16
- Requires-Dist: terp-cap-outbox==0.5.2; extra == 'worker'
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes