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.
Files changed (34) hide show
  1. {unitysvc_data-0.1.2/src/unitysvc_data.egg-info → unitysvc_data-0.1.4}/PKG-INFO +1 -1
  2. {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/pyproject.toml +1 -1
  3. {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/src/unitysvc_data/__init__.py +9 -0
  4. {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/src/unitysvc_data/_manifest.json +10 -2
  5. unitysvc_data-0.1.4/src/unitysvc_data/_registry.py +67 -0
  6. unitysvc_data-0.1.4/src/unitysvc_data/_version.py +17 -0
  7. {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/src/unitysvc_data/examples/s3/code-example/README.md +1 -0
  8. {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/src/unitysvc_data/examples/s3/connectivity/README.md +1 -1
  9. {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/src/unitysvc_data/presets.py +4 -0
  10. {unitysvc_data-0.1.2 → unitysvc_data-0.1.4/src/unitysvc_data.egg-info}/PKG-INFO +1 -1
  11. {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/src/unitysvc_data.egg-info/SOURCES.txt +1 -0
  12. {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/tests/test_presets.py +81 -1
  13. unitysvc_data-0.1.2/src/unitysvc_data/_version.py +0 -1
  14. {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/LICENSE +0 -0
  15. {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/README.md +0 -0
  16. {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/setup.cfg +0 -0
  17. {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/src/unitysvc_data/cli.py +0 -0
  18. {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/src/unitysvc_data/examples/api/connectivity/README.md +0 -0
  19. {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/src/unitysvc_data/examples/api/connectivity/connectivity-v1.sh.j2 +0 -0
  20. {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/src/unitysvc_data/examples/llm/request-template/README.md +0 -0
  21. {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/src/unitysvc_data/examples/llm/request-template/request-template-v1.json +0 -0
  22. {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/src/unitysvc_data/examples/s3/code-example/code-example-v1.py.j2 +0 -0
  23. {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/src/unitysvc_data/examples/s3/connectivity/connectivity-v1.py.j2 +0 -0
  24. {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/src/unitysvc_data/examples/s3/description/README.md +0 -0
  25. {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/src/unitysvc_data/examples/s3/description/description-v1.md +0 -0
  26. {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/src/unitysvc_data/examples/smtp/connectivity/README.md +0 -0
  27. {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/src/unitysvc_data/examples/smtp/connectivity/connectivity-v1.sh.j2 +0 -0
  28. {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/src/unitysvc_data/py.typed +0 -0
  29. {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/src/unitysvc_data.egg-info/dependency_links.txt +0 -0
  30. {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/src/unitysvc_data.egg-info/entry_points.txt +0 -0
  31. {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/src/unitysvc_data.egg-info/requires.txt +0 -0
  32. {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/src/unitysvc_data.egg-info/top_level.txt +0 -0
  33. {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/tests/test_build.py +0 -0
  34. {unitysvc_data-0.1.2 → unitysvc_data-0.1.4}/tests/test_cli.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: unitysvc-data
3
- Version: 0.1.2
3
+ Version: 0.1.4
4
4
  Summary: Standard examples and presets for UnitySVC data packages
5
5
  Author-email: Bo Peng <bo.peng@unitysvc.com>
6
6
  Maintainer-email: Bo Peng <bo.peng@unitysvc.com>
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "unitysvc-data"
7
- version = "0.1.2"
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
+
@@ -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
@@ -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
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: unitysvc-data
3
- Version: 0.1.2
3
+ Version: 0.1.4
4
4
  Summary: Standard examples and presets for UnitySVC data packages
5
5
  Author-email: Bo Peng <bo.peng@unitysvc.com>
6
6
  Maintainer-email: Bo Peng <bo.peng@unitysvc.com>
@@ -3,6 +3,7 @@ README.md
3
3
  pyproject.toml
4
4
  src/unitysvc_data/__init__.py
5
5
  src/unitysvc_data/_manifest.json
6
+ src/unitysvc_data/_registry.py
6
7
  src/unitysvc_data/_version.py
7
8
  src/unitysvc_data/cli.py
8
9
  src/unitysvc_data/presets.py
@@ -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