nthlayer-core 3.0.2__tar.gz → 4.0.0__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 (38) hide show
  1. {nthlayer_core-3.0.2 → nthlayer_core-4.0.0}/PKG-INFO +9 -5
  2. {nthlayer_core-3.0.2 → nthlayer_core-4.0.0}/README.md +7 -3
  3. {nthlayer_core-3.0.2 → nthlayer_core-4.0.0}/pyproject.toml +6 -4
  4. {nthlayer_core-3.0.2 → nthlayer_core-4.0.0}/src/nthlayer_core/cli.py +1 -1
  5. {nthlayer_core-3.0.2 → nthlayer_core-4.0.0}/src/nthlayer_core.egg-info/PKG-INFO +9 -5
  6. {nthlayer_core-3.0.2 → nthlayer_core-4.0.0}/src/nthlayer_core.egg-info/SOURCES.txt +1 -0
  7. nthlayer_core-4.0.0/src/nthlayer_core.egg-info/entry_points.txt +2 -0
  8. {nthlayer_core-3.0.2 → nthlayer_core-4.0.0}/src/nthlayer_core.egg-info/requires.txt +1 -1
  9. nthlayer_core-4.0.0/tests/test_console_script_name.py +126 -0
  10. {nthlayer_core-3.0.2 → nthlayer_core-4.0.0}/tests/test_deploying_doc_parity.py +2 -2
  11. nthlayer_core-3.0.2/src/nthlayer_core.egg-info/entry_points.txt +0 -2
  12. {nthlayer_core-3.0.2 → nthlayer_core-4.0.0}/setup.cfg +0 -0
  13. {nthlayer_core-3.0.2 → nthlayer_core-4.0.0}/src/nthlayer_core/__init__.py +0 -0
  14. {nthlayer_core-3.0.2 → nthlayer_core-4.0.0}/src/nthlayer_core/_openapi/__init__.py +0 -0
  15. {nthlayer_core-3.0.2 → nthlayer_core-4.0.0}/src/nthlayer_core/_openapi/paths_assessments_cases.py +0 -0
  16. {nthlayer_core-3.0.2 → nthlayer_core-4.0.0}/src/nthlayer_core/_openapi/paths_health.py +0 -0
  17. {nthlayer_core-3.0.2 → nthlayer_core-4.0.0}/src/nthlayer_core/_openapi/paths_other.py +0 -0
  18. {nthlayer_core-3.0.2 → nthlayer_core-4.0.0}/src/nthlayer_core/_openapi/paths_verdicts.py +0 -0
  19. {nthlayer_core-3.0.2 → nthlayer_core-4.0.0}/src/nthlayer_core/catalogue.py +0 -0
  20. {nthlayer_core-3.0.2 → nthlayer_core-4.0.0}/src/nthlayer_core/openapi_spec.py +0 -0
  21. {nthlayer_core-3.0.2 → nthlayer_core-4.0.0}/src/nthlayer_core/server.py +0 -0
  22. {nthlayer_core-3.0.2 → nthlayer_core-4.0.0}/src/nthlayer_core/store.py +0 -0
  23. {nthlayer_core-3.0.2 → nthlayer_core-4.0.0}/src/nthlayer_core.egg-info/dependency_links.txt +0 -0
  24. {nthlayer_core-3.0.2 → nthlayer_core-4.0.0}/src/nthlayer_core.egg-info/top_level.txt +0 -0
  25. {nthlayer_core-3.0.2 → nthlayer_core-4.0.0}/tests/test_api.py +0 -0
  26. {nthlayer_core-3.0.2 → nthlayer_core-4.0.0}/tests/test_api_cases.py +0 -0
  27. {nthlayer_core-3.0.2 → nthlayer_core-4.0.0}/tests/test_api_component_state.py +0 -0
  28. {nthlayer_core-3.0.2 → nthlayer_core-4.0.0}/tests/test_api_heartbeats.py +0 -0
  29. {nthlayer_core-3.0.2 → nthlayer_core-4.0.0}/tests/test_api_manifests.py +0 -0
  30. {nthlayer_core-3.0.2 → nthlayer_core-4.0.0}/tests/test_api_overrides.py +0 -0
  31. {nthlayer_core-3.0.2 → nthlayer_core-4.0.0}/tests/test_api_suppressions.py +0 -0
  32. {nthlayer_core-3.0.2 → nthlayer_core-4.0.0}/tests/test_dependency_declarations.py +0 -0
  33. {nthlayer_core-3.0.2 → nthlayer_core-4.0.0}/tests/test_health.py +0 -0
  34. {nthlayer_core-3.0.2 → nthlayer_core-4.0.0}/tests/test_openapi.py +0 -0
  35. {nthlayer_core-3.0.2 → nthlayer_core-4.0.0}/tests/test_retention.py +0 -0
  36. {nthlayer_core-3.0.2 → nthlayer_core-4.0.0}/tests/test_store.py +0 -0
  37. {nthlayer_core-3.0.2 → nthlayer_core-4.0.0}/tests/test_store_verdictstore.py +0 -0
  38. {nthlayer_core-3.0.2 → nthlayer_core-4.0.0}/tests/test_threshold_schema_parity.py +0 -0
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nthlayer-core
3
- Version: 3.0.2
3
+ Version: 4.0.0
4
4
  Summary: NthLayer core — reliability-critical verdict store, case management, and HTTP API
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
7
- Requires-Dist: nthlayer-common<6.0.0,>=5.3.0
7
+ Requires-Dist: nthlayer-common<6.0.0,>=5.3.1
8
8
  Requires-Dist: starlette>=0.40
9
9
  Requires-Dist: uvicorn>=0.30
10
10
  Requires-Dist: httpx>=0.27
@@ -23,9 +23,13 @@ Requires-Dist: ruff>=0.8; extra == "dev"
23
23
 
24
24
  ```bash
25
25
  pip install nthlayer-core
26
- nthlayer serve --host 0.0.0.0 --port 8000
26
+ nthlayer-core serve --host 0.0.0.0 --port 8000
27
27
  ```
28
28
 
29
+ The console script was `nthlayer` before 4.0.0; it was renamed to `nthlayer-core` because nthlayer-generate owns `nthlayer` (opensrm-dww5).
30
+
31
+ **Upgrading from 3.x:** if nthlayer-generate is installed in the same environment, reinstall it after upgrading core, because the 3.x core package owned the `nthlayer` script file and the upgrade removes it: `pip install --force-reinstall --no-deps nthlayer-generate` (or `uv pip install --reinstall-package nthlayer-generate nthlayer-generate`).
32
+
29
33
  ## What it is
30
34
 
31
35
  `nthlayer-core` is the single source of truth for the NthLayer runtime. It owns the SQLite store, exposes an HTTP API, and is the only component that touches the database. Tier 2 workers ([`nthlayer-workers`](https://github.com/rsionnach/nthlayer-workers)) and the Tier 3 operator TUI ([`nthlayer-bench`](https://github.com/rsionnach/nthlayer-bench)) talk to core exclusively over HTTP — never directly to SQLite.
@@ -101,8 +105,8 @@ WAL mode + `PRAGMA synchronous=NORMAL` + `busy_timeout=5000`. Thread-local conne
101
105
  ## CLI
102
106
 
103
107
  ```bash
104
- nthlayer serve [--host 0.0.0.0] [--port 8000] # start the HTTP server
105
- nthlayer -V # print version
108
+ nthlayer-core serve [--host 0.0.0.0] [--port 8000] # start the HTTP server
109
+ nthlayer-core -V # print version
106
110
  ```
107
111
 
108
112
  ## NthLayer ecosystem
@@ -4,9 +4,13 @@
4
4
 
5
5
  ```bash
6
6
  pip install nthlayer-core
7
- nthlayer serve --host 0.0.0.0 --port 8000
7
+ nthlayer-core serve --host 0.0.0.0 --port 8000
8
8
  ```
9
9
 
10
+ The console script was `nthlayer` before 4.0.0; it was renamed to `nthlayer-core` because nthlayer-generate owns `nthlayer` (opensrm-dww5).
11
+
12
+ **Upgrading from 3.x:** if nthlayer-generate is installed in the same environment, reinstall it after upgrading core, because the 3.x core package owned the `nthlayer` script file and the upgrade removes it: `pip install --force-reinstall --no-deps nthlayer-generate` (or `uv pip install --reinstall-package nthlayer-generate nthlayer-generate`).
13
+
10
14
  ## What it is
11
15
 
12
16
  `nthlayer-core` is the single source of truth for the NthLayer runtime. It owns the SQLite store, exposes an HTTP API, and is the only component that touches the database. Tier 2 workers ([`nthlayer-workers`](https://github.com/rsionnach/nthlayer-workers)) and the Tier 3 operator TUI ([`nthlayer-bench`](https://github.com/rsionnach/nthlayer-bench)) talk to core exclusively over HTTP — never directly to SQLite.
@@ -82,8 +86,8 @@ WAL mode + `PRAGMA synchronous=NORMAL` + `busy_timeout=5000`. Thread-local conne
82
86
  ## CLI
83
87
 
84
88
  ```bash
85
- nthlayer serve [--host 0.0.0.0] [--port 8000] # start the HTTP server
86
- nthlayer -V # print version
89
+ nthlayer-core serve [--host 0.0.0.0] [--port 8000] # start the HTTP server
90
+ nthlayer-core -V # print version
87
91
  ```
88
92
 
89
93
  ## NthLayer ecosystem
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "nthlayer-core"
7
- version = "3.0.2"
7
+ version = "4.0.0"
8
8
  description = "NthLayer core — reliability-critical verdict store, case management, and HTTP API"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -20,17 +20,19 @@ dependencies = [
20
20
  # every threshold entry [opensrm-7zpz]; catalogue.manifest_to_dict
21
21
  # serialises each threshold via threshold_to_v2_entry, common's validated
22
22
  # emitter of the v2 spec.judgment_threshold entry, never a hand-copy.
23
- # WHY 5.3.0 (opensrm-pnmv): the version this repo is now built and tested
23
+ # WHY 5.3.1 (opensrm-dww5): the version this repo is now built and tested
24
24
  # against (rule 10: the floor is the installed sibling). Bump it on
25
25
  # every nthlayer-common minor release: the floor guard fails until you do.
26
- "nthlayer-common>=5.3.0,<6.0.0",
26
+ # Raised to 5.3.1 (a patch) because its opensrm/v1 rejection names a
27
+ # command that runs in a co-install.
28
+ "nthlayer-common>=5.3.1,<6.0.0",
27
29
  "starlette>=0.40",
28
30
  "uvicorn>=0.30",
29
31
  "httpx>=0.27",
30
32
  ]
31
33
 
32
34
  [project.scripts]
33
- nthlayer = "nthlayer_core.cli:main"
35
+ nthlayer-core = "nthlayer_core.cli:main"
34
36
 
35
37
  [project.optional-dependencies]
36
38
  dev = [
@@ -9,7 +9,7 @@ def build_parser() -> argparse.ArgumentParser:
9
9
  """Build the CLI parser. Exposed so tests can introspect the surface
10
10
  without running ``main``'s side effects (see
11
11
  ``tests/test_deploying_doc_parity.py``)."""
12
- parser = argparse.ArgumentParser(description="NthLayer core")
12
+ parser = argparse.ArgumentParser(prog="nthlayer-core", description="NthLayer core")
13
13
  parser.add_argument("-V", "--version", action="version", version=f"%(prog)s {__version__}")
14
14
  sub = parser.add_subparsers(dest="command")
15
15
 
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nthlayer-core
3
- Version: 3.0.2
3
+ Version: 4.0.0
4
4
  Summary: NthLayer core — reliability-critical verdict store, case management, and HTTP API
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
7
- Requires-Dist: nthlayer-common<6.0.0,>=5.3.0
7
+ Requires-Dist: nthlayer-common<6.0.0,>=5.3.1
8
8
  Requires-Dist: starlette>=0.40
9
9
  Requires-Dist: uvicorn>=0.30
10
10
  Requires-Dist: httpx>=0.27
@@ -23,9 +23,13 @@ Requires-Dist: ruff>=0.8; extra == "dev"
23
23
 
24
24
  ```bash
25
25
  pip install nthlayer-core
26
- nthlayer serve --host 0.0.0.0 --port 8000
26
+ nthlayer-core serve --host 0.0.0.0 --port 8000
27
27
  ```
28
28
 
29
+ The console script was `nthlayer` before 4.0.0; it was renamed to `nthlayer-core` because nthlayer-generate owns `nthlayer` (opensrm-dww5).
30
+
31
+ **Upgrading from 3.x:** if nthlayer-generate is installed in the same environment, reinstall it after upgrading core, because the 3.x core package owned the `nthlayer` script file and the upgrade removes it: `pip install --force-reinstall --no-deps nthlayer-generate` (or `uv pip install --reinstall-package nthlayer-generate nthlayer-generate`).
32
+
29
33
  ## What it is
30
34
 
31
35
  `nthlayer-core` is the single source of truth for the NthLayer runtime. It owns the SQLite store, exposes an HTTP API, and is the only component that touches the database. Tier 2 workers ([`nthlayer-workers`](https://github.com/rsionnach/nthlayer-workers)) and the Tier 3 operator TUI ([`nthlayer-bench`](https://github.com/rsionnach/nthlayer-bench)) talk to core exclusively over HTTP — never directly to SQLite.
@@ -101,8 +105,8 @@ WAL mode + `PRAGMA synchronous=NORMAL` + `busy_timeout=5000`. Thread-local conne
101
105
  ## CLI
102
106
 
103
107
  ```bash
104
- nthlayer serve [--host 0.0.0.0] [--port 8000] # start the HTTP server
105
- nthlayer -V # print version
108
+ nthlayer-core serve [--host 0.0.0.0] [--port 8000] # start the HTTP server
109
+ nthlayer-core -V # print version
106
110
  ```
107
111
 
108
112
  ## NthLayer ecosystem
@@ -24,6 +24,7 @@ tests/test_api_heartbeats.py
24
24
  tests/test_api_manifests.py
25
25
  tests/test_api_overrides.py
26
26
  tests/test_api_suppressions.py
27
+ tests/test_console_script_name.py
27
28
  tests/test_dependency_declarations.py
28
29
  tests/test_deploying_doc_parity.py
29
30
  tests/test_health.py
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ nthlayer-core = nthlayer_core.cli:main
@@ -1,4 +1,4 @@
1
- nthlayer-common<6.0.0,>=5.3.0
1
+ nthlayer-common<6.0.0,>=5.3.1
2
2
  starlette>=0.40
3
3
  uvicorn>=0.30
4
4
  httpx>=0.27
@@ -0,0 +1,126 @@
1
+ """The console script is ``nthlayer-core``, never ``nthlayer``.
2
+
3
+ nthlayer-generate owns ``nthlayer``. Both packages registering it made the
4
+ installed one depend on install order, so core declares only ``nthlayer-core``
5
+ (symmetric naming: PyPI name = repo = script). Bead: opensrm-dww5.
6
+ """
7
+ from __future__ import annotations
8
+
9
+ import pathlib
10
+ import re
11
+ import tomllib
12
+
13
+ import pytest
14
+
15
+ from nthlayer_core.cli import build_parser
16
+
17
+ REPO_ROOT = pathlib.Path(__file__).resolve().parent.parent
18
+
19
+ # Lines containing any of these are exempt from the guard: they name the old
20
+ # script on purpose (the rename note and the 3.x upgrade note). Keep in sync
21
+ # with those notes in README.md and docs/deploying.md.
22
+ RENAME_NOTE_MARKERS = ("before 4.0.0", "Upgrading from 3.x")
23
+
24
+ # Legitimate mentions of the bare name that are NOT the console script: the
25
+ # `nthlayer` meta-package on PyPI. Matched by line substring, not by weakening
26
+ # the regex.
27
+ ALLOWED_LINE_SUBSTRINGS = ("front door + meta-package",)
28
+
29
+ # The bare word `nthlayer` (not preceded by a word char, `/`, `.` or `-`) in a
30
+ # stale-invocation position: followed by a subcommand/flag (`serve`,
31
+ # `--version`, `-V`, `--help`), a version number (`nthlayer 1.7.0` output), a
32
+ # backtick, `:`, `→`, or end of line. So it ignores nthlayer-core,
33
+ # nthlayer-generate, nthlayer_core, NTHLAYER_*, /var/lib/nthlayer/, nthlayer.io.
34
+ STALE_SCRIPT_RE = re.compile(
35
+ r"(?<![\w/.-])nthlayer"
36
+ r"(?=\s+(?:serve\b|--version|-V\b|--help|\d+\.\d+)|\s*$|`|:|\s*→)",
37
+ re.MULTILINE,
38
+ )
39
+
40
+
41
+ def _scripts() -> dict[str, str]:
42
+ data = tomllib.loads((REPO_ROOT / "pyproject.toml").read_text(encoding="utf-8"))
43
+ return data["project"]["scripts"]
44
+
45
+
46
+ def test_pyproject_declares_nthlayer_core_script() -> None:
47
+ assert _scripts().get("nthlayer-core") == "nthlayer_core.cli:main"
48
+
49
+
50
+ def test_pyproject_does_not_declare_nthlayer_script() -> None:
51
+ assert "nthlayer" not in _scripts(), (
52
+ "core must not register `nthlayer`; nthlayer-generate owns it"
53
+ )
54
+
55
+
56
+ def test_parser_prog_is_nthlayer_core() -> None:
57
+ assert build_parser().prog == "nthlayer-core"
58
+
59
+
60
+ @pytest.mark.parametrize(
61
+ "text",
62
+ [
63
+ "nthlayer serve --port 8000",
64
+ "nthlayer --version",
65
+ "nthlayer -V",
66
+ "nthlayer --help",
67
+ "nthlayer 1.7.0",
68
+ "run nthlayer",
69
+ "see nthlayer:",
70
+ "the `nthlayer` script",
71
+ "nthlayer \u2192 nthlayer_core.cli:main",
72
+ ],
73
+ )
74
+ def test_stale_script_regex_matches(text: str) -> None:
75
+ assert STALE_SCRIPT_RE.search(text), text
76
+
77
+
78
+ @pytest.mark.parametrize(
79
+ "text",
80
+ [
81
+ "nthlayer-core serve",
82
+ "nthlayer-generate build",
83
+ "import nthlayer_core",
84
+ "nthlayer-common>=1.5.0",
85
+ "NTHLAYER_STORE_PATH=/x",
86
+ "/var/lib/nthlayer/data",
87
+ "https://nthlayer.io/docs",
88
+ "nthlayer.db-wal",
89
+ "pip install nthlayer-core",
90
+ "nthlayer-core --version",
91
+ # `-` in the lookbehind: a hyphenated prefix is another name, not
92
+ # the bare script (e.g. a `foo-nthlayer` binary or path segment).
93
+ "foo-nthlayer serve",
94
+ ],
95
+ )
96
+ def test_stale_script_regex_ignores(text: str) -> None:
97
+ assert not STALE_SCRIPT_RE.search(text), text
98
+
99
+
100
+ # Top-level docs that tell a reader how to run core. CHANGELOG.md is excluded:
101
+ # it is a historical record and names the old script on purpose.
102
+ _ROOT_DOCS = ("README.md", "AGENTS.md", "CONTRIBUTING.md", "CLAUDE.md")
103
+
104
+
105
+ def _doc_paths() -> list[pathlib.Path]:
106
+ return [
107
+ *(REPO_ROOT / name for name in _ROOT_DOCS),
108
+ *sorted((REPO_ROOT / "docs").glob("*.md")),
109
+ ]
110
+
111
+
112
+ def test_docs_exist_so_guard_is_not_vacuous() -> None:
113
+ names = {p.name for p in _doc_paths()}
114
+ assert {*_ROOT_DOCS, "deploying.md", "architecture.md"} <= names
115
+
116
+
117
+ @pytest.mark.parametrize("path", _doc_paths(), ids=lambda p: p.name)
118
+ def test_docs_invoke_nthlayer_core_not_nthlayer(path: pathlib.Path) -> None:
119
+ lines = [
120
+ ln
121
+ for ln in path.read_text(encoding="utf-8").splitlines()
122
+ if not any(m in ln for m in RENAME_NOTE_MARKERS)
123
+ and not any(a in ln for a in ALLOWED_LINE_SUBSTRINGS)
124
+ ]
125
+ bare = STALE_SCRIPT_RE.findall("\n".join(lines))
126
+ assert not bare, f"{path.name} still names the old script: {bare}"
@@ -109,7 +109,7 @@ def test_cli_subsection_matches_argparse() -> None:
109
109
  (plus any subparsers) so the test sees exactly what users see.
110
110
 
111
111
  Pins both *flag* parity and *subcommand-name* parity — a new top-level
112
- subcommand (e.g. ``nthlayer migrate``) added to cli.py without a
112
+ subcommand (e.g. ``nthlayer-core migrate``) added to cli.py without a
113
113
  matching mention in the deploying.md CLI section fails this test.
114
114
  """
115
115
  section = _doc_section("### CLI")
@@ -144,7 +144,7 @@ def test_cli_subsection_matches_argparse() -> None:
144
144
 
145
145
  # Subcommand-name parity: each subcommand registered with the parser
146
146
  # must appear verbatim in the CLI section (typically inside a code
147
- # block like ``nthlayer serve``). Catches "new subcommand added,
147
+ # block like ``nthlayer-core serve``). Catches "new subcommand added,
148
148
  # nobody updated the docs."
149
149
  missing_subcommands = {sc for sc in src_subcommands if sc not in section}
150
150
  assert not missing_subcommands, (
@@ -1,2 +0,0 @@
1
- [console_scripts]
2
- nthlayer = nthlayer_core.cli:main
File without changes