unitysvc-data 0.1.2__tar.gz → 0.1.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.
- {unitysvc_data-0.1.2/src/unitysvc_data.egg-info → unitysvc_data-0.1.4}/PKG-INFO +1 -1
- {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/pyproject.toml +1 -1
- {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/src/unitysvc_data/__init__.py +9 -0
- {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/src/unitysvc_data/_manifest.json +10 -2
- unitysvc_data-0.1.4/src/unitysvc_data/_registry.py +67 -0
- unitysvc_data-0.1.4/src/unitysvc_data/_version.py +17 -0
- {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/src/unitysvc_data/examples/s3/code-example/README.md +1 -0
- {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/src/unitysvc_data/examples/s3/connectivity/README.md +1 -1
- {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/src/unitysvc_data/presets.py +4 -0
- {unitysvc_data-0.1.2 → unitysvc_data-0.1.4/src/unitysvc_data.egg-info}/PKG-INFO +1 -1
- {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/src/unitysvc_data.egg-info/SOURCES.txt +1 -0
- {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/tests/test_presets.py +81 -1
- unitysvc_data-0.1.2/src/unitysvc_data/_version.py +0 -1
- {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/LICENSE +0 -0
- {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/README.md +0 -0
- {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/setup.cfg +0 -0
- {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/src/unitysvc_data/cli.py +0 -0
- {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/src/unitysvc_data/examples/api/connectivity/README.md +0 -0
- {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/src/unitysvc_data/examples/api/connectivity/connectivity-v1.sh.j2 +0 -0
- {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/src/unitysvc_data/examples/llm/request-template/README.md +0 -0
- {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/src/unitysvc_data/examples/llm/request-template/request-template-v1.json +0 -0
- {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/src/unitysvc_data/examples/s3/code-example/code-example-v1.py.j2 +0 -0
- {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/src/unitysvc_data/examples/s3/connectivity/connectivity-v1.py.j2 +0 -0
- {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/src/unitysvc_data/examples/s3/description/README.md +0 -0
- {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/src/unitysvc_data/examples/s3/description/description-v1.md +0 -0
- {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/src/unitysvc_data/examples/smtp/connectivity/README.md +0 -0
- {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/src/unitysvc_data/examples/smtp/connectivity/connectivity-v1.sh.j2 +0 -0
- {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/src/unitysvc_data/py.typed +0 -0
- {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/src/unitysvc_data.egg-info/dependency_links.txt +0 -0
- {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/src/unitysvc_data.egg-info/entry_points.txt +0 -0
- {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/src/unitysvc_data.egg-info/requires.txt +0 -0
- {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/src/unitysvc_data.egg-info/top_level.txt +0 -0
- {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/tests/test_build.py +0 -0
- {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/tests/test_cli.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "unitysvc-data"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.4"
|
|
8
8
|
description = "Standard examples and presets for UnitySVC data packages"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [{ name = "Bo Peng", email = "bo.peng@unitysvc.com" }]
|
|
@@ -90,6 +90,11 @@ def list_examples() -> list[str]:
|
|
|
90
90
|
|
|
91
91
|
# Imported last so presets.py can compute its own _EXAMPLES_ROOT without
|
|
92
92
|
# reaching back into this module while __init__ is still loading.
|
|
93
|
+
# The ``from .presets import ...`` line also runs the ``@preset``
|
|
94
|
+
# decorators that populate :data:`PRESET_FNS` — so downstream consumers
|
|
95
|
+
# (notably ``unitysvc_core.load_data_file``) can enumerate every
|
|
96
|
+
# decorated preset without knowing their names ahead of time.
|
|
97
|
+
from ._registry import PRESET_FNS, preset # noqa: E402 (placement is deliberate)
|
|
93
98
|
from .presets import ( # noqa: E402 (placement is deliberate)
|
|
94
99
|
ALIASES,
|
|
95
100
|
MANIFEST,
|
|
@@ -112,6 +117,10 @@ __all__ = [
|
|
|
112
117
|
"MANIFEST",
|
|
113
118
|
"OVERRIDABLE",
|
|
114
119
|
"register_jinja_globals",
|
|
120
|
+
# Decorator-driven registry — downstream tools enumerate these to
|
|
121
|
+
# discover every preset type without hard-coding function names.
|
|
122
|
+
"preset",
|
|
123
|
+
"PRESET_FNS",
|
|
115
124
|
# Low-level path-based API.
|
|
116
125
|
"example_path",
|
|
117
126
|
"read_example",
|
|
@@ -40,7 +40,12 @@
|
|
|
40
40
|
"example_file": "s3/code-example/code-example-v1.py.j2",
|
|
41
41
|
"is_active": true,
|
|
42
42
|
"is_public": true,
|
|
43
|
-
"meta": {
|
|
43
|
+
"meta": {
|
|
44
|
+
"output_contains": "connectivity ok",
|
|
45
|
+
"requirements": [
|
|
46
|
+
"boto3"
|
|
47
|
+
]
|
|
48
|
+
},
|
|
44
49
|
"mime_type": "python",
|
|
45
50
|
"preset_name": "s3_code_example",
|
|
46
51
|
"source_readme": "s3/code-example/README.md",
|
|
@@ -53,7 +58,10 @@
|
|
|
53
58
|
"is_active": true,
|
|
54
59
|
"is_public": false,
|
|
55
60
|
"meta": {
|
|
56
|
-
"output_contains": "connectivity ok"
|
|
61
|
+
"output_contains": "connectivity ok",
|
|
62
|
+
"requirements": [
|
|
63
|
+
"boto3"
|
|
64
|
+
]
|
|
57
65
|
},
|
|
58
66
|
"mime_type": "python",
|
|
59
67
|
"preset_name": "s3_connectivity",
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""Decorator-driven registry for preset sentinel callables.
|
|
2
|
+
|
|
3
|
+
Any function decorated with :func:`preset` is registered under its
|
|
4
|
+
``__name__`` in :data:`PRESET_FNS`, where downstream consumers
|
|
5
|
+
(notably ``unitysvc-core``'s ``load_data_file``) discover them. Adding
|
|
6
|
+
a new preset type — for example a ``schema_preset`` or
|
|
7
|
+
``policy_preset`` — is then purely a matter of decorating the
|
|
8
|
+
function and releasing a new ``unitysvc-data``; every consumer picks
|
|
9
|
+
it up automatically on the next install.
|
|
10
|
+
|
|
11
|
+
The decorator wraps each function with the **sentinel calling
|
|
12
|
+
convention**: the value in a ``{"$<name>": value}`` sentinel is
|
|
13
|
+
passed to the wrapped function, with the seller-facing flat form
|
|
14
|
+
|
|
15
|
+
{"name": "<preset>", "<override>": ...}
|
|
16
|
+
|
|
17
|
+
transparently unpacked into ``fn(name, **overrides)``. Any other
|
|
18
|
+
shape (bare string, the internal ``{"$preset": ..., "$with": {...}}``
|
|
19
|
+
form that ``doc_preset`` understands, etc.) is forwarded verbatim.
|
|
20
|
+
|
|
21
|
+
The original (undecorated) function is returned unchanged so callers
|
|
22
|
+
can keep using it programmatically:
|
|
23
|
+
|
|
24
|
+
>>> from unitysvc_data import doc_preset
|
|
25
|
+
>>> doc_preset("s3_connectivity", is_public=False)
|
|
26
|
+
{...}
|
|
27
|
+
|
|
28
|
+
The registry lookup uses the wrapped, sentinel-aware version.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
from __future__ import annotations
|
|
32
|
+
|
|
33
|
+
import functools
|
|
34
|
+
from collections.abc import Callable
|
|
35
|
+
from typing import Any
|
|
36
|
+
|
|
37
|
+
#: Registered preset callables keyed by sentinel name (without the
|
|
38
|
+
#: leading ``$``). Populated by :func:`preset` at import time.
|
|
39
|
+
PRESET_FNS: dict[str, Callable[[Any], Any]] = {}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def preset(fn: Callable[..., Any]) -> Callable[..., Any]:
|
|
43
|
+
"""Register *fn* in :data:`PRESET_FNS` under its ``__name__``.
|
|
44
|
+
|
|
45
|
+
The registered entry is a thin wrapper that unpacks the
|
|
46
|
+
seller-facing flat sentinel form before delegating::
|
|
47
|
+
|
|
48
|
+
{"name": "<preset>", "<override>": ...} -> fn("<preset>", **overrides)
|
|
49
|
+
|
|
50
|
+
Any other value (bare string, dict without a string ``name`` key,
|
|
51
|
+
etc.) is forwarded to ``fn`` unchanged — the underlying function
|
|
52
|
+
is responsible for validating its own input.
|
|
53
|
+
|
|
54
|
+
Returns the undecorated function so programmatic callers keep the
|
|
55
|
+
original signature.
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
@functools.wraps(fn)
|
|
59
|
+
def _sentinel_entrypoint(source: Any) -> Any:
|
|
60
|
+
if isinstance(source, dict) and isinstance(source.get("name"), str):
|
|
61
|
+
name = source["name"]
|
|
62
|
+
overrides = {k: v for k, v in source.items() if k != "name"}
|
|
63
|
+
return fn(name, **overrides)
|
|
64
|
+
return fn(source)
|
|
65
|
+
|
|
66
|
+
PRESET_FNS[fn.__name__] = _sentinel_entrypoint
|
|
67
|
+
return fn
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Single source of truth for the package version.
|
|
2
|
+
|
|
3
|
+
The canonical version lives in ``pyproject.toml``'s ``[project]`` table
|
|
4
|
+
— that's what PyPI, ``pip install``, and ``pip show`` read. This
|
|
5
|
+
module asks ``importlib.metadata`` for the installed distribution's
|
|
6
|
+
version so downstream code can ``from unitysvc_data import __version__``
|
|
7
|
+
without the two values ever drifting apart.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
11
|
+
|
|
12
|
+
try:
|
|
13
|
+
__version__ = version("unitysvc-data")
|
|
14
|
+
except PackageNotFoundError: # pragma: no cover
|
|
15
|
+
# Running from a checkout that hasn't been ``pip install``-ed.
|
|
16
|
+
__version__ = "0.0.0+unknown"
|
|
17
|
+
|
{unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/src/unitysvc_data/examples/s3/code-example/README.md
RENAMED
|
@@ -6,6 +6,7 @@ file = "code-example.py.j2"
|
|
|
6
6
|
description = "Python example: list objects in an S3 bucket via boto3"
|
|
7
7
|
is_active = true
|
|
8
8
|
is_public = true
|
|
9
|
+
meta = { output_contains = "connectivity ok", requirements = ["boto3"] }
|
|
9
10
|
+++
|
|
10
11
|
|
|
11
12
|
# s3 / code-example — list objects via boto3
|
{unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/src/unitysvc_data/examples/s3/connectivity/README.md
RENAMED
|
@@ -6,7 +6,7 @@ file = "connectivity.py.j2"
|
|
|
6
6
|
description = "Verify S3 endpoint accepts the configured credentials"
|
|
7
7
|
is_active = true
|
|
8
8
|
is_public = false
|
|
9
|
-
meta = { output_contains = "connectivity ok" }
|
|
9
|
+
meta = { output_contains = "connectivity ok", requirements = ["boto3"] }
|
|
10
10
|
+++
|
|
11
11
|
|
|
12
12
|
# s3 / connectivity — S3 credential smoke test
|
|
@@ -36,6 +36,8 @@ from importlib.resources import files as _files
|
|
|
36
36
|
from pathlib import Path
|
|
37
37
|
from typing import Any
|
|
38
38
|
|
|
39
|
+
from ._registry import preset
|
|
40
|
+
|
|
39
41
|
# Resolved once at import time. Duplicated with __init__ rather than
|
|
40
42
|
# imported to avoid a circular import during package load. Use
|
|
41
43
|
# ``__package__`` (the parent package, "unitysvc_data") rather than
|
|
@@ -177,6 +179,7 @@ def _lookup(name: str, pool: dict[str, Any], what: str) -> Any:
|
|
|
177
179
|
# --- Public API ------------------------------------------------------------
|
|
178
180
|
|
|
179
181
|
|
|
182
|
+
@preset
|
|
180
183
|
def doc_preset(source: Any, **overrides: Any) -> dict[str, Any]:
|
|
181
184
|
"""Return a fully-populated document record for the named preset.
|
|
182
185
|
|
|
@@ -205,6 +208,7 @@ def doc_preset(source: Any, **overrides: Any) -> dict[str, Any]:
|
|
|
205
208
|
return factory(**(sentinel_overrides or overrides))
|
|
206
209
|
|
|
207
210
|
|
|
211
|
+
@preset
|
|
208
212
|
def file_preset(source: Any) -> str:
|
|
209
213
|
"""Return the raw UTF-8 content of the preset's bundled file.
|
|
210
214
|
|
|
@@ -116,7 +116,7 @@ def test_doc_preset_sentinel_versioned():
|
|
|
116
116
|
record = doc_preset({"$preset": "s3_connectivity_v1"})
|
|
117
117
|
assert record["category"] == "connectivity_test"
|
|
118
118
|
assert record["mime_type"] == "python"
|
|
119
|
-
assert record["meta"] == {"output_contains": "connectivity ok"}
|
|
119
|
+
assert record["meta"] == {"output_contains": "connectivity ok", "requirements": ["boto3"]}
|
|
120
120
|
assert Path(record["file_path"]).is_file()
|
|
121
121
|
|
|
122
122
|
|
|
@@ -308,3 +308,83 @@ def test_manifest_is_up_to_date():
|
|
|
308
308
|
f"stdout:\n{result.stdout}\n"
|
|
309
309
|
f"stderr:\n{result.stderr}"
|
|
310
310
|
)
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
# ---------------------------------------------------------------------------
|
|
314
|
+
# @preset decorator + PRESET_FNS registry
|
|
315
|
+
# ---------------------------------------------------------------------------
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
def test_preset_fns_registry_contains_doc_and_file():
|
|
319
|
+
from unitysvc_data import PRESET_FNS
|
|
320
|
+
|
|
321
|
+
assert "doc_preset" in PRESET_FNS
|
|
322
|
+
assert "file_preset" in PRESET_FNS
|
|
323
|
+
# Each registered entry is callable.
|
|
324
|
+
assert callable(PRESET_FNS["doc_preset"])
|
|
325
|
+
assert callable(PRESET_FNS["file_preset"])
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
def test_preset_decorator_unpacks_flat_form():
|
|
329
|
+
"""The wrapper auto-unpacks {"name": "<x>", ...} into fn("<x>", **rest)."""
|
|
330
|
+
from unitysvc_data import PRESET_FNS
|
|
331
|
+
|
|
332
|
+
# Flat form via the registered wrapper should equal direct call.
|
|
333
|
+
via_registry = PRESET_FNS["doc_preset"](
|
|
334
|
+
{"name": "s3_connectivity_v1", "is_public": True}
|
|
335
|
+
)
|
|
336
|
+
via_direct = doc_preset("s3_connectivity_v1", is_public=True)
|
|
337
|
+
assert via_registry == via_direct
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
def test_preset_decorator_forwards_bare_string():
|
|
341
|
+
from unitysvc_data import PRESET_FNS
|
|
342
|
+
|
|
343
|
+
via_registry = PRESET_FNS["doc_preset"]("s3_connectivity_v1")
|
|
344
|
+
via_direct = doc_preset("s3_connectivity_v1")
|
|
345
|
+
assert via_registry == via_direct
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
def test_preset_decorator_preserves_original_function_signature():
|
|
349
|
+
"""@preset returns the undecorated function so programmatic callers
|
|
350
|
+
keep the original signature (`**overrides`, etc.)."""
|
|
351
|
+
import inspect
|
|
352
|
+
|
|
353
|
+
sig = inspect.signature(doc_preset)
|
|
354
|
+
# If @preset had replaced doc_preset with the single-arg wrapper,
|
|
355
|
+
# we would see just `source` here.
|
|
356
|
+
assert "overrides" in sig.parameters or any(
|
|
357
|
+
p.kind is inspect.Parameter.VAR_KEYWORD for p in sig.parameters.values()
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
def test_preset_decorator_does_not_duplicate_across_reimports():
|
|
362
|
+
"""Re-importing the module must not double-register any preset."""
|
|
363
|
+
import importlib
|
|
364
|
+
|
|
365
|
+
import unitysvc_data._registry as reg
|
|
366
|
+
|
|
367
|
+
before = dict(reg.PRESET_FNS)
|
|
368
|
+
importlib.reload(reg)
|
|
369
|
+
# After reload, each preset appears at most once in the new registry.
|
|
370
|
+
# (We can't easily re-decorate here without re-running presets.py, but
|
|
371
|
+
# we can at least confirm the registry isn't "leaking" entries.)
|
|
372
|
+
assert set(reg.PRESET_FNS) <= set(before) | {"doc_preset", "file_preset"}
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
def test_preset_decorator_register_custom_function():
|
|
376
|
+
"""Third-party code can register a new sentinel type with @preset."""
|
|
377
|
+
from unitysvc_data._registry import PRESET_FNS, preset
|
|
378
|
+
|
|
379
|
+
try:
|
|
380
|
+
@preset
|
|
381
|
+
def _custom_preset(source, **kwargs):
|
|
382
|
+
return {"source": source, "kwargs": kwargs}
|
|
383
|
+
|
|
384
|
+
assert "_custom_preset" in PRESET_FNS
|
|
385
|
+
assert PRESET_FNS["_custom_preset"]({"name": "x", "flag": True}) == {
|
|
386
|
+
"source": "x",
|
|
387
|
+
"kwargs": {"flag": True},
|
|
388
|
+
}
|
|
389
|
+
finally:
|
|
390
|
+
PRESET_FNS.pop("_custom_preset", None)
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.1.2"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/src/unitysvc_data/examples/api/connectivity/README.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/src/unitysvc_data/examples/s3/description/README.md
RENAMED
|
File without changes
|
|
File without changes
|
{unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/src/unitysvc_data/examples/smtp/connectivity/README.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|