facetkit 0.1.0__tar.gz → 0.2.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.
- {facetkit-0.1.0 → facetkit-0.2.0}/PKG-INFO +11 -6
- {facetkit-0.1.0 → facetkit-0.2.0}/README.md +10 -5
- {facetkit-0.1.0 → facetkit-0.2.0}/facetkit/__init__.py +1 -1
- {facetkit-0.1.0 → facetkit-0.2.0}/facetkit/container.py +25 -17
- {facetkit-0.1.0 → facetkit-0.2.0}/tests/test_container.py +22 -2
- {facetkit-0.1.0 → facetkit-0.2.0}/.gitignore +0 -0
- {facetkit-0.1.0 → facetkit-0.2.0}/CHANGELOG.md +0 -0
- {facetkit-0.1.0 → facetkit-0.2.0}/LICENSE +0 -0
- {facetkit-0.1.0 → facetkit-0.2.0}/facetkit/facets/__init__.py +0 -0
- {facetkit-0.1.0 → facetkit-0.2.0}/facetkit/facets/cli.py +0 -0
- {facetkit-0.1.0 → facetkit-0.2.0}/facetkit/facets/gui.py +0 -0
- {facetkit-0.1.0 → facetkit-0.2.0}/facetkit/facets/service.py +0 -0
- {facetkit-0.1.0 → facetkit-0.2.0}/facetkit/facets/tui.py +0 -0
- {facetkit-0.1.0 → facetkit-0.2.0}/facetkit/facets/web.py +0 -0
- {facetkit-0.1.0 → facetkit-0.2.0}/facetkit/py.typed +0 -0
- {facetkit-0.1.0 → facetkit-0.2.0}/facetkit/types.py +0 -0
- {facetkit-0.1.0 → facetkit-0.2.0}/pyproject.toml +0 -0
- {facetkit-0.1.0 → facetkit-0.2.0}/requirements.txt +0 -0
- {facetkit-0.1.0 → facetkit-0.2.0}/tests/conftest.py +0 -0
- {facetkit-0.1.0 → facetkit-0.2.0}/tests/test_container_facets.py +0 -0
- {facetkit-0.1.0 → facetkit-0.2.0}/tests/test_facet.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: facetkit
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: A composable dependency container with passive UI and service facets.
|
|
5
5
|
Author-email: "Daniel R. Vásquez Montes" <dev.DanielR@gmail.composable>
|
|
6
6
|
License: MIT
|
|
@@ -143,7 +143,7 @@ app.unmount_facet("cli")
|
|
|
143
143
|
- **Duplicate keys** — registering the same name again overwrites the previous entry (last wins).
|
|
144
144
|
- **Unmount** — `unmount_facet` clears that facet's registries. Attached components are not automatically detached; call `remove_component` first if you need a full teardown.
|
|
145
145
|
- **Facet names** — the mount name is arbitrary (`app.mount_facet("cli", CliFacet())`). The library does not enforce that the name matches the facet type.
|
|
146
|
-
- **Missing facets** — `app.facets["cli"]` raises `KeyError`; `app.get("facets.cli")` returns `None`.
|
|
146
|
+
- **Missing facets** — `app.facets["cli"]` raises `KeyError`; `app.get("facets.cli")` raises `PathAccessError`; `app.get("facets.cli", default=None)` returns `None`.
|
|
147
147
|
|
|
148
148
|
## Introspection with `get()`
|
|
149
149
|
|
|
@@ -158,14 +158,19 @@ app.get("facets.cli.commands") # command registry
|
|
|
158
158
|
app.get("facets.web.routes.users") # a single route entry
|
|
159
159
|
```
|
|
160
160
|
|
|
161
|
-
|
|
161
|
+
Without `default`, resolution errors propagate (missing paths, glom failures, etc.):
|
|
162
162
|
|
|
163
163
|
```python
|
|
164
|
-
app.get("facets.
|
|
165
|
-
app.get("facets.missing"
|
|
164
|
+
app.get("facets.cli.commands") # works when the cli facet is mounted
|
|
165
|
+
app.get("facets.missing") # raises glom.PathAccessError
|
|
166
166
|
```
|
|
167
167
|
|
|
168
|
-
|
|
168
|
+
Pass `default` for optional lookups — any resolution error returns that value:
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
app.get("facets.missing", default=None) # None
|
|
172
|
+
app.get("facets.missing", default={}) # {}
|
|
173
|
+
```
|
|
169
174
|
|
|
170
175
|
Direct dict access also works when you know a facet is mounted:
|
|
171
176
|
|
|
@@ -117,7 +117,7 @@ app.unmount_facet("cli")
|
|
|
117
117
|
- **Duplicate keys** — registering the same name again overwrites the previous entry (last wins).
|
|
118
118
|
- **Unmount** — `unmount_facet` clears that facet's registries. Attached components are not automatically detached; call `remove_component` first if you need a full teardown.
|
|
119
119
|
- **Facet names** — the mount name is arbitrary (`app.mount_facet("cli", CliFacet())`). The library does not enforce that the name matches the facet type.
|
|
120
|
-
- **Missing facets** — `app.facets["cli"]` raises `KeyError`; `app.get("facets.cli")` returns `None`.
|
|
120
|
+
- **Missing facets** — `app.facets["cli"]` raises `KeyError`; `app.get("facets.cli")` raises `PathAccessError`; `app.get("facets.cli", default=None)` returns `None`.
|
|
121
121
|
|
|
122
122
|
## Introspection with `get()`
|
|
123
123
|
|
|
@@ -132,14 +132,19 @@ app.get("facets.cli.commands") # command registry
|
|
|
132
132
|
app.get("facets.web.routes.users") # a single route entry
|
|
133
133
|
```
|
|
134
134
|
|
|
135
|
-
|
|
135
|
+
Without `default`, resolution errors propagate (missing paths, glom failures, etc.):
|
|
136
136
|
|
|
137
137
|
```python
|
|
138
|
-
app.get("facets.
|
|
139
|
-
app.get("facets.missing"
|
|
138
|
+
app.get("facets.cli.commands") # works when the cli facet is mounted
|
|
139
|
+
app.get("facets.missing") # raises glom.PathAccessError
|
|
140
140
|
```
|
|
141
141
|
|
|
142
|
-
|
|
142
|
+
Pass `default` for optional lookups — any resolution error returns that value:
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
app.get("facets.missing", default=None) # None
|
|
146
|
+
app.get("facets.missing", default={}) # {}
|
|
147
|
+
```
|
|
143
148
|
|
|
144
149
|
Direct dict access also works when you know a facet is mounted:
|
|
145
150
|
|
|
@@ -3,13 +3,15 @@
|
|
|
3
3
|
|
|
4
4
|
import glom
|
|
5
5
|
|
|
6
|
-
from typing import Dict, Any
|
|
7
|
-
from facetkit.types import Component, Facet
|
|
8
|
-
|
|
9
|
-
#===============================================================================
|
|
10
|
-
# DEFINITIONS
|
|
11
|
-
|
|
12
|
-
|
|
6
|
+
from typing import Dict, Any
|
|
7
|
+
from facetkit.types import Component, Facet
|
|
8
|
+
|
|
9
|
+
#===============================================================================
|
|
10
|
+
# DEFINITIONS
|
|
11
|
+
|
|
12
|
+
_UNSET = object()
|
|
13
|
+
|
|
14
|
+
class Container:
|
|
13
15
|
|
|
14
16
|
def __init__(self, config: Dict[str, Any]):
|
|
15
17
|
self.config : Dict[str, Any] = config
|
|
@@ -18,16 +20,22 @@ class Container:
|
|
|
18
20
|
|
|
19
21
|
# Public API ===============================================================
|
|
20
22
|
|
|
21
|
-
def get(self, path: str, default: Any =
|
|
22
|
-
"""Retrieve from container using glom.
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
return
|
|
23
|
+
def get(self, path: str, default: Any = _UNSET) -> Any:
|
|
24
|
+
"""Retrieve from container using glom.
|
|
25
|
+
|
|
26
|
+
Raises glom errors when *default* is omitted. Pass *default* to
|
|
27
|
+
return that value instead when the path cannot be resolved.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
if not path: return self
|
|
31
|
+
try:
|
|
32
|
+
return glom.glom(self.__dict__, path)
|
|
33
|
+
except (glom.PathAccessError, glom.GlomError):
|
|
34
|
+
if default is _UNSET: raise
|
|
35
|
+
return default
|
|
36
|
+
except Exception:
|
|
37
|
+
if default is _UNSET: raise
|
|
38
|
+
return default
|
|
31
39
|
|
|
32
40
|
def mount_facet(self, name: str, facet: Facet) -> None:
|
|
33
41
|
if name in self.facets: self.unmount_facet(name)
|
|
@@ -44,8 +44,28 @@ class TestContainerGet:
|
|
|
44
44
|
def test_missing_path_returns_default(self, container):
|
|
45
45
|
assert container.get("config.missing", default="fallback") == "fallback"
|
|
46
46
|
|
|
47
|
-
def
|
|
48
|
-
assert container.get("config.missing") is None
|
|
47
|
+
def test_missing_path_returns_none_when_default_is_none(self, container):
|
|
48
|
+
assert container.get("config.missing", default=None) is None
|
|
49
|
+
|
|
50
|
+
def test_missing_path_raises_without_default(self, container):
|
|
51
|
+
with pytest.raises(glom.PathAccessError):
|
|
52
|
+
container.get("config.missing")
|
|
53
|
+
|
|
54
|
+
def test_glom_error_raises_without_default(self, container, monkeypatch):
|
|
55
|
+
def raise_glom_error(*_args, **_kwargs):
|
|
56
|
+
raise glom.GlomError("boom")
|
|
57
|
+
|
|
58
|
+
monkeypatch.setattr(container_module.glom, "glom", raise_glom_error)
|
|
59
|
+
with pytest.raises(glom.GlomError):
|
|
60
|
+
container.get("config.app.name")
|
|
61
|
+
|
|
62
|
+
def test_unexpected_exception_raises_without_default(self, container, monkeypatch):
|
|
63
|
+
def raise_runtime_error(*_args, **_kwargs):
|
|
64
|
+
raise RuntimeError("unexpected")
|
|
65
|
+
|
|
66
|
+
monkeypatch.setattr(container_module.glom, "glom", raise_runtime_error)
|
|
67
|
+
with pytest.raises(RuntimeError):
|
|
68
|
+
container.get("config.app.name")
|
|
49
69
|
|
|
50
70
|
def test_glom_error_returns_default(self, container, monkeypatch):
|
|
51
71
|
def raise_glom_error(*_args, **_kwargs):
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|