valbridge 0.1.1__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,25 @@
1
+ # Temporary compliance test harness files
2
+ xschema-harness-*.py
3
+
4
+ # Python cache
5
+ __pycache__/
6
+ *.pyc
7
+ *.pyo
8
+ *.pyd
9
+ .Python
10
+
11
+ # Pytest cache
12
+ .pytest_cache/
13
+
14
+ # mypy
15
+ .mypy_cache/
16
+ .dmypy.json
17
+ dmypy.json
18
+
19
+ # uv
20
+ .venv/
21
+
22
+ # Build artifacts
23
+ dist/
24
+ build/
25
+ *.egg-info/
@@ -0,0 +1,7 @@
1
+ # Changelog
2
+
3
+ ## [0.1.1](https://github.com/vectorfy-co/valbridge/compare/py-client-v0.1.0...py-client-v0.1.1) (2026-04-07)
4
+
5
+ ### Features
6
+
7
+ * import valbridge monorepo ([2a7ed24](https://github.com/vectorfy-co/valbridge/commit/2a7ed246c8cb87206d13860fe01155de24de1ae8))
@@ -0,0 +1,54 @@
1
+ Metadata-Version: 2.4
2
+ Name: valbridge
3
+ Version: 0.1.1
4
+ Summary: Runtime client for valbridge-generated validators with type-safe schema lookup
5
+ Project-URL: Homepage, https://github.com/vectorfy-co/valbridge
6
+ Project-URL: Documentation, https://github.com/vectorfy-co/valbridge
7
+ Project-URL: Repository, https://github.com/vectorfy-co/valbridge
8
+ Project-URL: Issues, https://github.com/vectorfy-co/valbridge/issues
9
+ Project-URL: Changelog, https://github.com/vectorfy-co/valbridge/blob/main/CHANGELOG.md
10
+ Author: vectorfyco
11
+ Maintainer: vectorfyco
12
+ License: MIT
13
+ Keywords: code-generation,json-schema,pydantic,runtime-client,schema,type-safety,validation
14
+ Classifier: Development Status :: 4 - Beta
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.9
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Programming Language :: Python :: 3.13
24
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
25
+ Classifier: Typing :: Typed
26
+ Requires-Python: >=3.9
27
+ Provides-Extra: dev
28
+ Requires-Dist: pytest>=8.0; extra == 'dev'
29
+ Description-Content-Type: text/markdown
30
+
31
+ # valbridge
32
+
33
+ Runtime client for valbridge-generated validators with type-safe schema lookup.
34
+
35
+ ## Installation
36
+
37
+ ```bash
38
+ pip install valbridge
39
+ ```
40
+
41
+ ## Usage
42
+
43
+ ```python
44
+ from valbridge import create_valbridge
45
+ from _valbridge import schemas
46
+
47
+ valbridge = create_valbridge(schemas)
48
+
49
+ # Full key lookup
50
+ user_validator = valbridge("user:Profile")
51
+
52
+ # Validate data
53
+ user_validator.validate_python({"name": "Alice", "email": "alice@example.com"})
54
+ ```
@@ -0,0 +1,24 @@
1
+ # valbridge
2
+
3
+ Runtime client for valbridge-generated validators with type-safe schema lookup.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install valbridge
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```python
14
+ from valbridge import create_valbridge
15
+ from _valbridge import schemas
16
+
17
+ valbridge = create_valbridge(schemas)
18
+
19
+ # Full key lookup
20
+ user_validator = valbridge("user:Profile")
21
+
22
+ # Validate data
23
+ user_validator.validate_python({"name": "Alice", "email": "alice@example.com"})
24
+ ```
@@ -0,0 +1,55 @@
1
+ [project]
2
+ name = "valbridge"
3
+ version = "0.1.1"
4
+ description = "Runtime client for valbridge-generated validators with type-safe schema lookup"
5
+ readme = "README.md"
6
+ requires-python = ">=3.9"
7
+ license = { text = "MIT" }
8
+ authors = [{ name = "vectorfyco" }]
9
+ maintainers = [{ name = "vectorfyco" }]
10
+ keywords = [
11
+ "json-schema",
12
+ "validation",
13
+ "pydantic",
14
+ "code-generation",
15
+ "runtime-client",
16
+ "schema",
17
+ "type-safety",
18
+ ]
19
+ classifiers = [
20
+ "Development Status :: 4 - Beta",
21
+ "Intended Audience :: Developers",
22
+ "License :: OSI Approved :: MIT License",
23
+ "Operating System :: OS Independent",
24
+ "Programming Language :: Python :: 3",
25
+ "Programming Language :: Python :: 3.9",
26
+ "Programming Language :: Python :: 3.10",
27
+ "Programming Language :: Python :: 3.11",
28
+ "Programming Language :: Python :: 3.12",
29
+ "Programming Language :: Python :: 3.13",
30
+ "Topic :: Software Development :: Libraries :: Python Modules",
31
+ "Typing :: Typed",
32
+ ]
33
+ dependencies = []
34
+
35
+ [project.urls]
36
+ Homepage = "https://github.com/vectorfy-co/valbridge"
37
+ Documentation = "https://github.com/vectorfy-co/valbridge"
38
+ Repository = "https://github.com/vectorfy-co/valbridge"
39
+ Issues = "https://github.com/vectorfy-co/valbridge/issues"
40
+ Changelog = "https://github.com/vectorfy-co/valbridge/blob/main/CHANGELOG.md"
41
+
42
+ [project.optional-dependencies]
43
+ dev = [
44
+ "pytest>=8.0",
45
+ ]
46
+
47
+ [build-system]
48
+ requires = ["hatchling"]
49
+ build-backend = "hatchling.build"
50
+
51
+ [tool.hatch.build.targets.wheel]
52
+ packages = ["src/valbridge"]
53
+
54
+ [tool.pytest.ini_options]
55
+ testpaths = ["tests"]
@@ -0,0 +1,135 @@
1
+ """
2
+ Runtime client for valbridge-generated validators.
3
+
4
+ This module provides runtime schema lookup for validators generated by valbridge.
5
+ The client wraps a dictionary of validators and provides a callable interface.
6
+
7
+ Design decisions:
8
+
9
+ 1. NAMESPACE:ID FORMAT - Keys use "namespace:id" format (e.g., "user:Profile")
10
+ matching the Go CLI output. This provides:
11
+ - Namespacing to avoid collisions when multiple config files define schemas
12
+ - Clear provenance: namespace often maps to the config filename
13
+ - Consistency across TypeScript and Python (same format in both)
14
+
15
+ 2. NO DEFAULT NAMESPACE - Unlike the TypeScript client, Python doesn't support
16
+ declaration merging for type safety on shorthand lookups. Without type
17
+ benefits, shorthand adds API complexity without value. Full keys are clearer.
18
+
19
+ 3. DICTIONARY-BASED STORAGE - Schemas stored in a dict keyed by "namespace:id".
20
+ Generated code creates this dict statically. Runtime lookup is O(1).
21
+
22
+ 4. CALLABLE INTERFACE - Client is callable: valbridge("namespace:id")
23
+ This mimics function call semantics while allowing stateful configuration.
24
+
25
+ 5. USER-FRIENDLY ERRORS - ValbridgeError includes "Run `valbridge generate`" hint
26
+ because the most common error is forgetting to regenerate after schema changes.
27
+
28
+ Usage:
29
+ from _valbridge import valbridge # Generated by valbridge CLI
30
+
31
+ User = valbridge("user:User") # Returns Pydantic model class
32
+ user = User.model_validate({"name": "Alice"})
33
+ """
34
+
35
+ from typing import Any, Dict, Generic, Protocol, TypeVar
36
+
37
+ __all__ = ["ValbridgeClient", "ValbridgeError", "create_valbridge"]
38
+
39
+ T = TypeVar("T")
40
+ T_co = TypeVar("T_co", covariant=True)
41
+
42
+
43
+ class _SchemaLookup(Protocol[T_co]):
44
+ def __call__(self, key: str) -> T_co: ...
45
+
46
+
47
+ class ValbridgeError(Exception):
48
+ """
49
+ Raised when a schema cannot be found.
50
+
51
+ Includes a hint to run `valbridge generate` since the most common cause
52
+ is forgetting to regenerate after adding/renaming schemas.
53
+ """
54
+
55
+ pass
56
+
57
+
58
+ class ValbridgeClient(Generic[T]):
59
+ """
60
+ Type-safe client for looking up schemas by namespace:id.
61
+
62
+ Generic over T to allow typing the return value. In generated code,
63
+ T is typically a Union of all generated model types, providing IDE
64
+ autocompletion for the returned validator.
65
+
66
+ The class wraps a dictionary but provides a callable interface for
67
+ ergonomic usage: valbridge("namespace:id") instead of valbridge.get("namespace:id")
68
+ """
69
+
70
+ def __init__(self, schemas: Dict[str, T]) -> None:
71
+ """
72
+ Initialize the valbridge client.
73
+
74
+ Args:
75
+ schemas: Dictionary of schemas keyed by "namespace:id".
76
+ Keys use colon separator matching CLI output format.
77
+ """
78
+ self._schemas = schemas
79
+
80
+ def __call__(self, key: str) -> T:
81
+ """
82
+ Look up a schema by key.
83
+
84
+ Args:
85
+ key: Schema key in "namespace:id" format (e.g., "user:Profile")
86
+ The namespace is typically the config filename without extension.
87
+ The id is the schema's declared identifier.
88
+
89
+ Returns:
90
+ The schema validator (e.g., Pydantic model class)
91
+
92
+ Raises:
93
+ ValbridgeError: If schema not found. Error message includes hint
94
+ to run `valbridge generate` since stale code is common.
95
+ """
96
+ if key not in self._schemas:
97
+ msg = f"Unknown schema: {key}. Run `valbridge generate`."
98
+ raise ValbridgeError(msg)
99
+
100
+ return self._schemas[key]
101
+
102
+
103
+ def create_valbridge(schemas: Dict[str, T]) -> _SchemaLookup[T]:
104
+ """
105
+ Create an valbridge client for looking up schemas by namespace:id.
106
+
107
+ This is the public factory function used by generated code. It returns
108
+ a callable that provides runtime schema lookup.
109
+
110
+ Why a factory function instead of direct ValbridgeClient instantiation?
111
+ - Cleaner generated code: `valbridge = create_valbridge(schemas)`
112
+ - Hides implementation detail (class vs function doesn't matter to user)
113
+ - Allows future changes to internal representation without breaking API
114
+
115
+ Args:
116
+ schemas: Dictionary of schemas keyed by "namespace:id".
117
+ Generated code provides this dict with all validators.
118
+
119
+ Returns:
120
+ A ValbridgeClient instance. It's callable and takes a "namespace:id"
121
+ key, returning the validator. Raises ValbridgeError if the key is not found.
122
+
123
+ Example:
124
+ >>> from valbridge import create_valbridge
125
+ >>> schemas = {"user:Profile": ProfileModel, "config:TSConfig": TSConfigModel}
126
+ >>> valbridge = create_valbridge(schemas)
127
+ >>>
128
+ >>> # Look up by full namespace:id key
129
+ >>> Profile = valbridge("user:Profile")
130
+ >>>
131
+ >>> # Use the validator
132
+ >>> user = Profile.model_validate({"name": "Alice"})
133
+ """
134
+ client = ValbridgeClient(schemas)
135
+ return client
File without changes
@@ -0,0 +1,101 @@
1
+ """Tests for valbridge."""
2
+
3
+ import pytest
4
+ from valbridge import ValbridgeError, create_valbridge
5
+
6
+
7
+ class MockValidator:
8
+ """Mock validator for testing."""
9
+
10
+ def __init__(self, name: str):
11
+ self.name = name
12
+
13
+ def model_validate(self, data):
14
+ return data
15
+
16
+
17
+ def test_full_key_lookup():
18
+ """Test looking up schemas with full namespace:id keys."""
19
+ schemas = {
20
+ "user:Profile": MockValidator("Profile"),
21
+ "another:TSConfig": MockValidator("TSConfig"),
22
+ }
23
+ valbridge = create_valbridge(schemas)
24
+
25
+ # Should find by full key
26
+ assert valbridge("user:Profile").name == "Profile"
27
+ assert valbridge("another:TSConfig").name == "TSConfig"
28
+
29
+
30
+ def test_schema_not_found():
31
+ """Test error when schema doesn't exist."""
32
+ schemas = {
33
+ "user:Profile": MockValidator("Profile"),
34
+ }
35
+ valbridge = create_valbridge(schemas)
36
+
37
+ # Non-existent key
38
+ with pytest.raises(ValbridgeError) as exc_info:
39
+ valbridge("user:NonExistent")
40
+ assert "Unknown schema: user:NonExistent" in str(exc_info.value)
41
+
42
+
43
+ def test_empty_schemas():
44
+ """Test client with empty schemas dict."""
45
+ valbridge = create_valbridge({})
46
+
47
+ with pytest.raises(ValbridgeError):
48
+ valbridge("anything")
49
+
50
+
51
+ def test_type_inference():
52
+ """Test that client preserves type information."""
53
+ schemas = {
54
+ "user:Profile": MockValidator("Profile"),
55
+ }
56
+ valbridge = create_valbridge(schemas)
57
+
58
+ # Should return the exact validator instance
59
+ validator = valbridge("user:Profile")
60
+ assert isinstance(validator, MockValidator)
61
+ assert validator.name == "Profile"
62
+
63
+
64
+ def test_nonexistent_namespace():
65
+ """Test error when namespace doesn't exist."""
66
+ schemas = {
67
+ "user:Profile": MockValidator("Profile"),
68
+ "another:Config": MockValidator("Config"),
69
+ }
70
+ valbridge = create_valbridge(schemas)
71
+
72
+ # Non-existent namespace should fail
73
+ with pytest.raises(ValbridgeError) as exc_info:
74
+ valbridge("nonexistent:Schema")
75
+ assert "Unknown schema: nonexistent:Schema" in str(exc_info.value)
76
+
77
+
78
+ def test_client_is_callable():
79
+ """Test that returned client is callable."""
80
+ schemas = {"user:Profile": MockValidator("Profile")}
81
+ valbridge = create_valbridge(schemas)
82
+
83
+ # Should be callable
84
+ assert callable(valbridge)
85
+
86
+ # Should work like a function
87
+ result = valbridge("user:Profile")
88
+ assert result.name == "Profile"
89
+
90
+
91
+ def test_multiple_clients():
92
+ """Test creating multiple independent clients."""
93
+ schemas1 = {"user:Profile": MockValidator("Profile1")}
94
+ schemas2 = {"user:Profile": MockValidator("Profile2")}
95
+
96
+ valbridge1 = create_valbridge(schemas1)
97
+ valbridge2 = create_valbridge(schemas2)
98
+
99
+ # Should be independent
100
+ assert valbridge1("user:Profile").name == "Profile1"
101
+ assert valbridge2("user:Profile").name == "Profile2"