gpdatacached 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.
- {gpdatacached-0.1.0 → gpdatacached-0.2.0}/.gitignore +4 -0
- gpdatacached-0.2.0/CHANGELOG.md +116 -0
- {gpdatacached-0.1.0 → gpdatacached-0.2.0}/PKG-INFO +12 -11
- {gpdatacached-0.1.0 → gpdatacached-0.2.0}/README.md +11 -10
- {gpdatacached-0.1.0 → gpdatacached-0.2.0}/README.zh-CN.md +11 -10
- {gpdatacached-0.1.0 → gpdatacached-0.2.0}/docs/en/api.md +189 -12
- {gpdatacached-0.1.0 → gpdatacached-0.2.0}/docs/en/extensibility.md +17 -10
- {gpdatacached-0.1.0 → gpdatacached-0.2.0}/docs/en/index.md +2 -1
- {gpdatacached-0.1.0 → gpdatacached-0.2.0}/docs/en/lifecycle.md +34 -23
- {gpdatacached-0.1.0 → gpdatacached-0.2.0}/docs/en/locking.md +11 -11
- {gpdatacached-0.1.0 → gpdatacached-0.2.0}/docs/en/overview.md +22 -4
- {gpdatacached-0.1.0 → gpdatacached-0.2.0}/docs/en/pandas-support.md +16 -4
- {gpdatacached-0.1.0 → gpdatacached-0.2.0}/docs/en/performance-guide.md +20 -16
- {gpdatacached-0.1.0 → gpdatacached-0.2.0}/docs/en/pydantic-support.md +15 -3
- {gpdatacached-0.1.0 → gpdatacached-0.2.0}/docs/zh/api.md +147 -12
- {gpdatacached-0.1.0 → gpdatacached-0.2.0}/docs/zh/extensibility.md +17 -10
- {gpdatacached-0.1.0 → gpdatacached-0.2.0}/docs/zh/index.md +2 -1
- {gpdatacached-0.1.0 → gpdatacached-0.2.0}/docs/zh/lifecycle.md +35 -24
- {gpdatacached-0.1.0 → gpdatacached-0.2.0}/docs/zh/locking.md +11 -11
- {gpdatacached-0.1.0 → gpdatacached-0.2.0}/docs/zh/overview.md +21 -3
- {gpdatacached-0.1.0 → gpdatacached-0.2.0}/docs/zh/pandas-support.md +16 -4
- {gpdatacached-0.1.0 → gpdatacached-0.2.0}/docs/zh/performance-guide.md +20 -16
- {gpdatacached-0.1.0 → gpdatacached-0.2.0}/docs/zh/pydantic-support.md +15 -3
- {gpdatacached-0.1.0 → gpdatacached-0.2.0}/pyproject.toml +1 -0
- {gpdatacached-0.1.0 → gpdatacached-0.2.0}/src/gpdatacached/__init__.py +27 -11
- {gpdatacached-0.1.0 → gpdatacached-0.2.0}/src/gpdatacached/builtins/dict_object.py +23 -19
- {gpdatacached-0.1.0 → gpdatacached-0.2.0}/src/gpdatacached/builtins/list_object.py +18 -13
- {gpdatacached-0.1.0 → gpdatacached-0.2.0}/src/gpdatacached/builtins/set_object.py +9 -0
- {gpdatacached-0.1.0 → gpdatacached-0.2.0}/src/gpdatacached/config.py +25 -0
- {gpdatacached-0.1.0 → gpdatacached-0.2.0}/src/gpdatacached/container.py +29 -2
- gpdatacached-0.2.0/src/gpdatacached/data_cache.py +214 -0
- {gpdatacached-0.1.0 → gpdatacached-0.2.0}/src/gpdatacached/domain.py +40 -4
- {gpdatacached-0.1.0 → gpdatacached-0.2.0}/src/gpdatacached/exceptions.py +18 -0
- {gpdatacached-0.1.0 → gpdatacached-0.2.0}/src/gpdatacached/extensions/pandas_support.py +60 -25
- {gpdatacached-0.1.0 → gpdatacached-0.2.0}/src/gpdatacached/extensions/pydantic_support.py +27 -4
- {gpdatacached-0.1.0 → gpdatacached-0.2.0}/src/gpdatacached/namespace.py +16 -7
- {gpdatacached-0.1.0 → gpdatacached-0.2.0}/src/gpdatacached/object.py +37 -10
- {gpdatacached-0.1.0 → gpdatacached-0.2.0}/src/gpdatacached/types.py +40 -5
- {gpdatacached-0.1.0 → gpdatacached-0.2.0}/LICENSE +0 -0
- {gpdatacached-0.1.0 → gpdatacached-0.2.0}/src/gpdatacached/builtins/__init__.py +0 -0
- {gpdatacached-0.1.0 → gpdatacached-0.2.0}/src/gpdatacached/builtins/bool_object.py +0 -0
- {gpdatacached-0.1.0 → gpdatacached-0.2.0}/src/gpdatacached/builtins/datetime_object.py +0 -0
- {gpdatacached-0.1.0 → gpdatacached-0.2.0}/src/gpdatacached/builtins/float_object.py +0 -0
- {gpdatacached-0.1.0 → gpdatacached-0.2.0}/src/gpdatacached/builtins/int_object.py +0 -0
- {gpdatacached-0.1.0 → gpdatacached-0.2.0}/src/gpdatacached/builtins/none_object.py +0 -0
- {gpdatacached-0.1.0 → gpdatacached-0.2.0}/src/gpdatacached/builtins/registry.py +0 -0
- {gpdatacached-0.1.0 → gpdatacached-0.2.0}/src/gpdatacached/builtins/str_object.py +0 -0
- {gpdatacached-0.1.0 → gpdatacached-0.2.0}/src/gpdatacached/builtins/tuple_object.py +0 -0
- {gpdatacached-0.1.0 → gpdatacached-0.2.0}/src/gpdatacached/codec.py +0 -0
- {gpdatacached-0.1.0 → gpdatacached-0.2.0}/src/gpdatacached/extensions/__init__.py +0 -0
- {gpdatacached-0.1.0 → gpdatacached-0.2.0}/src/gpdatacached/gc.py +0 -0
- {gpdatacached-0.1.0 → gpdatacached-0.2.0}/src/gpdatacached/keys.py +0 -0
- {gpdatacached-0.1.0 → gpdatacached-0.2.0}/src/gpdatacached/lifecycle.py +0 -0
- {gpdatacached-0.1.0 → gpdatacached-0.2.0}/src/gpdatacached/locking.py +0 -0
- {gpdatacached-0.1.0 → gpdatacached-0.2.0}/src/gpdatacached/meta.py +0 -0
- {gpdatacached-0.1.0 → gpdatacached-0.2.0}/src/gpdatacached/refset.py +0 -0
- {gpdatacached-0.1.0 → gpdatacached-0.2.0}/src/gpdatacached/scalar.py +0 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
GitHub Releases (with download artifacts) are published at
|
|
9
|
+
<https://github.com/LinnetCodes/gpdatacached/releases>.
|
|
10
|
+
|
|
11
|
+
## [0.2.0] — 2026-07-26
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- **`GPDataCache` facade** — a config-driven entry point that discovers
|
|
16
|
+
`GPDCDomain` instances from a `gpconfig` `GPConfigFolder`, lazily constructs
|
|
17
|
+
them on first access, and resolves GPDC URIs to live `GPDCDataObject`
|
|
18
|
+
instances. Includes `__contains__` (config-snapshot membership, usable after
|
|
19
|
+
`close()`), `close()` (closes all cached domains), and the URI accessors below.
|
|
20
|
+
- **GPDC URI access** — `GPDataCache.get_object_by_uri(uri)` and
|
|
21
|
+
`has_uri(uri)`, with multi-segment path traversal into nested containers
|
|
22
|
+
(e.g. `gpdc://domain/ns/order_obj/items/0`). Terminals on a scalar field
|
|
23
|
+
raise `ObjectNotFoundByURIError`. The utilities `make_uri` /
|
|
24
|
+
`uri_to_redis_key` / `validate_object_name` are part of the public surface.
|
|
25
|
+
- **`expire_now()`** on `GPDCDataObject` — an explicit, mode-independent
|
|
26
|
+
"delete now" entry point (full teardown incl. anonymous subtrees; raises
|
|
27
|
+
`CanonicalObjectReferencedError` if still referenced). Use this instead of
|
|
28
|
+
`expire_in(0)` / `expire_at(past)` when you want unambiguous immediate
|
|
29
|
+
deletion.
|
|
30
|
+
- **Public exception bases** — `GPDCError` (root of the whole hierarchy) and
|
|
31
|
+
`GPDCLockError` (base for lock-acquisition/ownership errors) are now
|
|
32
|
+
exported, so callers can catch by category: `except GPDCError` catches every
|
|
33
|
+
library error; `except GPDCLockError` catches every lock failure.
|
|
34
|
+
- **`ObjectNotFoundByURIError`** — raised by URI resolution on any
|
|
35
|
+
unresolvable URI (missing domain/namespace/object, broken traversal,
|
|
36
|
+
scalar terminal, malformed syntax).
|
|
37
|
+
- **Pydantic container-mode field validation** —
|
|
38
|
+
`BaseModelContainerObject.__setitem__` now validates the value against the
|
|
39
|
+
field's declared type *before* any Redis write (single-field via pydantic's
|
|
40
|
+
`TypeAdapter`); a type mismatch raises `pydantic.ValidationError` at the
|
|
41
|
+
call site and leaves the object untouched. Cross-field validators still
|
|
42
|
+
require the scalar codec.
|
|
43
|
+
- gpconfig integration: `GPDCDomain` / `GPDCDomainConfig` are registered with
|
|
44
|
+
`gpconfig`'s object factory at import time, so a domain can be constructed
|
|
45
|
+
directly from a config file via `folder.get_object(name)`.
|
|
46
|
+
|
|
47
|
+
### Changed
|
|
48
|
+
|
|
49
|
+
- **BREAKING:** `GPDCDomain.__init__` signature changed from
|
|
50
|
+
`(name, config)` to `(config, name=None)`, conforming to gpconfig's
|
|
51
|
+
`cls(config)` configurable convention. The old call order raises
|
|
52
|
+
`TypeError` with migration guidance before any Redis connection is made.
|
|
53
|
+
`config.name` (the filename stem when loaded from a file) is preferred as
|
|
54
|
+
the domain name; the explicit `name` argument is the fallback.
|
|
55
|
+
- `expire_in(seconds)` now requires `seconds > 0` (raises `ValueError`
|
|
56
|
+
otherwise). Use `expire_now()` for immediate deletion.
|
|
57
|
+
- `GC interval` validation: `gc_interval_seconds` must now be `>= 30`
|
|
58
|
+
(default `300` unchanged). Smaller values busy-looped the background GC
|
|
59
|
+
thread.
|
|
60
|
+
|
|
61
|
+
### Fixed
|
|
62
|
+
|
|
63
|
+
- **Pandas non-unit `RangeIndex`** — `DataFrameObject.__len__` and
|
|
64
|
+
`SeriesObject`/`DataFrameObject` `extend` now honour the stored
|
|
65
|
+
`range_step` instead of assuming `step == 1`. Extending a non-unit-step
|
|
66
|
+
DataFrame now stays in the compact range representation (previously
|
|
67
|
+
promoted to labelled levels).
|
|
68
|
+
- **`SeriesObject.name` identity** — `.name` now returns the namespace
|
|
69
|
+
binding (inherited from `GPDCDataObject`, matching `.canonical_name` /
|
|
70
|
+
`.uri`); the pandas label is exposed as `.series_name`. Restores the
|
|
71
|
+
base-class identity contract that `SeriesObject` had been overriding.
|
|
72
|
+
- **Failed multi-element encodes** — `encode_session` now also reverses
|
|
73
|
+
ref-count increments it made on pre-existing containers, and a failed
|
|
74
|
+
first-time store cleans up the just-written binding `obj:`/`raw:`/`ref:`
|
|
75
|
+
keys, so a failed encode no longer leaves phantom referrers or orphan
|
|
76
|
+
metadata.
|
|
77
|
+
- **Stale-handle mutations** — every container mutator now runs a
|
|
78
|
+
`_check_alive()` liveness guard *before* its first Redis write, so a
|
|
79
|
+
handle whose object has been deleted/expired raises `ObjectGoneError`
|
|
80
|
+
without first corrupting the `raw:` key.
|
|
81
|
+
- **Sliding-TTL performance characterization** — sliding refresh batches the
|
|
82
|
+
TTL re-apply into one pipeline per tree level (the load-bearing cost is the
|
|
83
|
+
per-child `HGETALL`), corrected in the Performance Guide.
|
|
84
|
+
|
|
85
|
+
### Documentation
|
|
86
|
+
|
|
87
|
+
- New MkDocs site (Material theme) with full EN + Simplified-Chinese (ZH)
|
|
88
|
+
documentation: overview, API reference, lifecycle, locking, performance
|
|
89
|
+
guide, extensibility, pydantic/pandas support.
|
|
90
|
+
- Durable explicit-anchor scheme (`{ #id }` attributes) + a `mkdocs build
|
|
91
|
+
--strict` validation gate, so editing a heading never silently breaks an
|
|
92
|
+
in-page cross-reference.
|
|
93
|
+
|
|
94
|
+
[0.2.0]: https://github.com/LinnetCodes/gpdatacached/releases/tag/version-0.2.0
|
|
95
|
+
|
|
96
|
+
## [0.1.0] — 2026-07-04
|
|
97
|
+
|
|
98
|
+
Initial public release.
|
|
99
|
+
|
|
100
|
+
Core typed cross-process data sharing and caching over Redis:
|
|
101
|
+
|
|
102
|
+
- `GPDCDomain` / `GPDCNamespace` / `GPDCDataObject` three-tier model.
|
|
103
|
+
- Typed scalars (int/float/str/bool/datetime/date/time/none/tuple) and live
|
|
104
|
+
containers (list/dict/set) with reference semantics — mutate in place, no
|
|
105
|
+
read-modify-write.
|
|
106
|
+
- Per-object lifecycle: `permanent` / `ttl` / `sliding-window` cache modes
|
|
107
|
+
with cascade propagation.
|
|
108
|
+
- Anonymous nested objects with reference counting and cascade delete.
|
|
109
|
+
- Opt-in distributed locking (`obj.lock()` / `try_lock()`) via
|
|
110
|
+
`SET NX EX` + `BLPOP` with Lua-atomic release.
|
|
111
|
+
- Background GC for orphaned/expired keys.
|
|
112
|
+
- Extensibility: `GPDCTypeRegistry`, codec/object ABCs, pandas
|
|
113
|
+
(`Series`/`DataFrame`) and pydantic support extensions.
|
|
114
|
+
- Strict config validation; `reset_domain_config` utility.
|
|
115
|
+
|
|
116
|
+
[0.1.0]: https://github.com/LinnetCodes/gpdatacached/releases/tag/version-0.1.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gpdatacached
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Redis-based cross-process data sharing and caching library
|
|
5
5
|
Project-URL: Homepage, https://github.com/linnetcodes/gpdatacached
|
|
6
6
|
Project-URL: Documentation, https://linnetcodes.github.io/gpdatacached
|
|
@@ -59,6 +59,7 @@ Sharing mutable state across processes is painful. `multiprocessing.Manager` is
|
|
|
59
59
|
- **Lifecycle control** — per-object `permanent` / `ttl` / `sliding-window` cache modes, with cascade propagation to owned subtrees.
|
|
60
60
|
- **Background GC** — reclaims expired and orphaned anonymous objects.
|
|
61
61
|
- **Opt-in distributed locking** — serialize writes across processes with `obj.lock()`.
|
|
62
|
+
- **URI-based access** — manage multiple domains from config and resolve `gpdc://{domain}/{namespace}/{name}` URIs to data objects via `GPDataCache`, including traversal into anonymous nested containers.
|
|
62
63
|
- **Optional extensions** — cache [pydantic](https://docs.pydantic.dev/) `BaseModel` records and [pandas](https://pandas.pydata.org/) `Series` / `DataFrame` (see [Extensions](#extensions)).
|
|
63
64
|
|
|
64
65
|
---
|
|
@@ -89,7 +90,7 @@ config = GPDCDomainConfig(
|
|
|
89
90
|
redis_port=6379,
|
|
90
91
|
redis_password="secret",
|
|
91
92
|
)
|
|
92
|
-
domain = GPDCDomain("mydomain"
|
|
93
|
+
domain = GPDCDomain(config, name="mydomain")
|
|
93
94
|
ns = domain.ensure_namespace("analytics")
|
|
94
95
|
|
|
95
96
|
# Scalars read back as native Python values.
|
|
@@ -129,21 +130,21 @@ GPDCDomain ── GPDCNamespace ── GPDCDataObject (scalar | container)
|
|
|
129
130
|
|
|
130
131
|
## Documentation
|
|
131
132
|
|
|
132
|
-
📖 **[Full documentation index](./docs/index.md)** — the entry point to all guides and references below.
|
|
133
|
+
📖 **[Full documentation index](./docs/en/index.md)** — the entry point to all guides and references below.
|
|
133
134
|
|
|
134
|
-
- **[Overview](./docs/overview.md)** — design purpose, philosophy, and end-to-end architecture (start here to understand the library as a whole).
|
|
135
|
-
- **[API Reference](./docs/api.md)** — complete public API reference.
|
|
136
|
-
- **[Lifecycle Management](./docs/lifecycle.md)** — how object creation, expiry, refresh, and deletion work, including anonymous and reference objects (read this to set correct expectations).
|
|
137
|
-
- **[Extensibility](./docs/extensibility.md)** — the type system explained, with worked examples for adding your own scalar and container types (codecs, object classes, registration).
|
|
138
|
-
- **[Performance Guide](./docs/performance-guide.md)** — usage patterns that degrade performance (sliding TTL overuse, deep nesting, unnecessary GC/locking) and their recommended alternatives. Read before putting GPDC on a hot path.
|
|
139
|
-
- **[Multi-Process Locking](./docs/locking.md)** — when to lock, performance impact, recommended usage, deadlock avoidance, and GC interaction.
|
|
135
|
+
- **[Overview](./docs/en/overview.md)** — design purpose, philosophy, and end-to-end architecture (start here to understand the library as a whole).
|
|
136
|
+
- **[API Reference](./docs/en/api.md)** — complete public API reference.
|
|
137
|
+
- **[Lifecycle Management](./docs/en/lifecycle.md)** — how object creation, expiry, refresh, and deletion work, including anonymous and reference objects (read this to set correct expectations).
|
|
138
|
+
- **[Extensibility](./docs/en/extensibility.md)** — the type system explained, with worked examples for adding your own scalar and container types (codecs, object classes, registration).
|
|
139
|
+
- **[Performance Guide](./docs/en/performance-guide.md)** — usage patterns that degrade performance (sliding TTL overuse, deep nesting, unnecessary GC/locking) and their recommended alternatives. Read before putting GPDC on a hot path.
|
|
140
|
+
- **[Multi-Process Locking](./docs/en/locking.md)** — when to lock, performance impact, recommended usage, deadlock avoidance, and GC interaction.
|
|
140
141
|
|
|
141
142
|
## Extensions
|
|
142
143
|
|
|
143
144
|
Optional built-in extensions under `gpdatacached.extensions`:
|
|
144
145
|
|
|
145
|
-
- **[Pydantic Support](./docs/pydantic-support.md)** — cache pydantic `BaseModel` records. Two storage modes: scalar (atomic JSON) for scalar-only models, container (Redis hash) for models needing field-level access or nested `list`/`dict`/`set` fields. Pydantic is a core dependency; no extra install needed.
|
|
146
|
-
- **[Pandas Support](./docs/pandas-support.md)** — cache pandas `Series` / `DataFrame` as live objects. Online selective accessors (`.iloc`, `.loc`, `df[col]`) for one-shot subset reads; `.value` for full restore; `extend` for appends. Requires `pip install "gpdatacached[pandas]"`.
|
|
146
|
+
- **[Pydantic Support](./docs/en/pydantic-support.md)** — cache pydantic `BaseModel` records. Two storage modes: scalar (atomic JSON) for scalar-only models, container (Redis hash) for models needing field-level access or nested `list`/`dict`/`set` fields. Pydantic is a core dependency; no extra install needed.
|
|
147
|
+
- **[Pandas Support](./docs/en/pandas-support.md)** — cache pandas `Series` / `DataFrame` as live objects. Online selective accessors (`.iloc`, `.loc`, `df[col]`) for one-shot subset reads; `.value` for full restore; `extend` for appends. Requires `pip install "gpdatacached[pandas]"`.
|
|
147
148
|
|
|
148
149
|
---
|
|
149
150
|
|
|
@@ -22,6 +22,7 @@ Sharing mutable state across processes is painful. `multiprocessing.Manager` is
|
|
|
22
22
|
- **Lifecycle control** — per-object `permanent` / `ttl` / `sliding-window` cache modes, with cascade propagation to owned subtrees.
|
|
23
23
|
- **Background GC** — reclaims expired and orphaned anonymous objects.
|
|
24
24
|
- **Opt-in distributed locking** — serialize writes across processes with `obj.lock()`.
|
|
25
|
+
- **URI-based access** — manage multiple domains from config and resolve `gpdc://{domain}/{namespace}/{name}` URIs to data objects via `GPDataCache`, including traversal into anonymous nested containers.
|
|
25
26
|
- **Optional extensions** — cache [pydantic](https://docs.pydantic.dev/) `BaseModel` records and [pandas](https://pandas.pydata.org/) `Series` / `DataFrame` (see [Extensions](#extensions)).
|
|
26
27
|
|
|
27
28
|
---
|
|
@@ -52,7 +53,7 @@ config = GPDCDomainConfig(
|
|
|
52
53
|
redis_port=6379,
|
|
53
54
|
redis_password="secret",
|
|
54
55
|
)
|
|
55
|
-
domain = GPDCDomain("mydomain"
|
|
56
|
+
domain = GPDCDomain(config, name="mydomain")
|
|
56
57
|
ns = domain.ensure_namespace("analytics")
|
|
57
58
|
|
|
58
59
|
# Scalars read back as native Python values.
|
|
@@ -92,21 +93,21 @@ GPDCDomain ── GPDCNamespace ── GPDCDataObject (scalar | container)
|
|
|
92
93
|
|
|
93
94
|
## Documentation
|
|
94
95
|
|
|
95
|
-
📖 **[Full documentation index](./docs/index.md)** — the entry point to all guides and references below.
|
|
96
|
+
📖 **[Full documentation index](./docs/en/index.md)** — the entry point to all guides and references below.
|
|
96
97
|
|
|
97
|
-
- **[Overview](./docs/overview.md)** — design purpose, philosophy, and end-to-end architecture (start here to understand the library as a whole).
|
|
98
|
-
- **[API Reference](./docs/api.md)** — complete public API reference.
|
|
99
|
-
- **[Lifecycle Management](./docs/lifecycle.md)** — how object creation, expiry, refresh, and deletion work, including anonymous and reference objects (read this to set correct expectations).
|
|
100
|
-
- **[Extensibility](./docs/extensibility.md)** — the type system explained, with worked examples for adding your own scalar and container types (codecs, object classes, registration).
|
|
101
|
-
- **[Performance Guide](./docs/performance-guide.md)** — usage patterns that degrade performance (sliding TTL overuse, deep nesting, unnecessary GC/locking) and their recommended alternatives. Read before putting GPDC on a hot path.
|
|
102
|
-
- **[Multi-Process Locking](./docs/locking.md)** — when to lock, performance impact, recommended usage, deadlock avoidance, and GC interaction.
|
|
98
|
+
- **[Overview](./docs/en/overview.md)** — design purpose, philosophy, and end-to-end architecture (start here to understand the library as a whole).
|
|
99
|
+
- **[API Reference](./docs/en/api.md)** — complete public API reference.
|
|
100
|
+
- **[Lifecycle Management](./docs/en/lifecycle.md)** — how object creation, expiry, refresh, and deletion work, including anonymous and reference objects (read this to set correct expectations).
|
|
101
|
+
- **[Extensibility](./docs/en/extensibility.md)** — the type system explained, with worked examples for adding your own scalar and container types (codecs, object classes, registration).
|
|
102
|
+
- **[Performance Guide](./docs/en/performance-guide.md)** — usage patterns that degrade performance (sliding TTL overuse, deep nesting, unnecessary GC/locking) and their recommended alternatives. Read before putting GPDC on a hot path.
|
|
103
|
+
- **[Multi-Process Locking](./docs/en/locking.md)** — when to lock, performance impact, recommended usage, deadlock avoidance, and GC interaction.
|
|
103
104
|
|
|
104
105
|
## Extensions
|
|
105
106
|
|
|
106
107
|
Optional built-in extensions under `gpdatacached.extensions`:
|
|
107
108
|
|
|
108
|
-
- **[Pydantic Support](./docs/pydantic-support.md)** — cache pydantic `BaseModel` records. Two storage modes: scalar (atomic JSON) for scalar-only models, container (Redis hash) for models needing field-level access or nested `list`/`dict`/`set` fields. Pydantic is a core dependency; no extra install needed.
|
|
109
|
-
- **[Pandas Support](./docs/pandas-support.md)** — cache pandas `Series` / `DataFrame` as live objects. Online selective accessors (`.iloc`, `.loc`, `df[col]`) for one-shot subset reads; `.value` for full restore; `extend` for appends. Requires `pip install "gpdatacached[pandas]"`.
|
|
109
|
+
- **[Pydantic Support](./docs/en/pydantic-support.md)** — cache pydantic `BaseModel` records. Two storage modes: scalar (atomic JSON) for scalar-only models, container (Redis hash) for models needing field-level access or nested `list`/`dict`/`set` fields. Pydantic is a core dependency; no extra install needed.
|
|
110
|
+
- **[Pandas Support](./docs/en/pandas-support.md)** — cache pandas `Series` / `DataFrame` as live objects. Online selective accessors (`.iloc`, `.loc`, `df[col]`) for one-shot subset reads; `.value` for full restore; `extend` for appends. Requires `pip install "gpdatacached[pandas]"`.
|
|
110
111
|
|
|
111
112
|
---
|
|
112
113
|
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
- **生命周期控制** —— 每个对象可设置 `permanent` / `ttl` / `sliding`(滑动窗口)缓存模式,并将策略级联传播到所拥有的子树。
|
|
23
23
|
- **后台 GC** —— 自动回收过期或成为孤儿的匿名对象。
|
|
24
24
|
- **可选的分布式锁** —— 通过 `obj.lock()` 在多进程间串行化写入。
|
|
25
|
+
- **基于 URI 的访问** —— 通过 `GPDataCache` 从配置管理多个 domain,并将 `gpdc://{domain}/{namespace}/{name}` 形式的 URI 解析为数据对象,包括对匿名嵌套容器的遍历。
|
|
25
26
|
- **可选扩展** —— 可缓存 [pydantic](https://docs.pydantic.dev/) `BaseModel` 记录与 [pandas](https://pandas.pydata.org/) `Series` / `DataFrame`(见[扩展](#扩展))。
|
|
26
27
|
|
|
27
28
|
---
|
|
@@ -52,7 +53,7 @@ config = GPDCDomainConfig(
|
|
|
52
53
|
redis_port=6379,
|
|
53
54
|
redis_password="secret",
|
|
54
55
|
)
|
|
55
|
-
domain = GPDCDomain("mydomain"
|
|
56
|
+
domain = GPDCDomain(config, name="mydomain")
|
|
56
57
|
ns = domain.ensure_namespace("analytics")
|
|
57
58
|
|
|
58
59
|
# 标量读取出来是原生 Python 值。
|
|
@@ -92,21 +93,21 @@ GPDCDomain ── GPDCNamespace ── GPDCDataObject (标量 | 容器)
|
|
|
92
93
|
|
|
93
94
|
## 文档
|
|
94
95
|
|
|
95
|
-
📖 **[完整文档索引](./docs/zh
|
|
96
|
+
📖 **[完整文档索引](./docs/zh/index.md)** —— 以下所有指南与参考的入口。
|
|
96
97
|
|
|
97
|
-
- **[总体概览](./docs/zh
|
|
98
|
-
- **[API 参考](./docs/zh
|
|
99
|
-
- **[生命周期管理](./docs/zh
|
|
100
|
-
- **[可扩展性](./docs/zh
|
|
101
|
-
- **[性能指南](./docs/zh
|
|
102
|
-
- **[多进程加锁](./docs/zh
|
|
98
|
+
- **[总体概览](./docs/zh/overview.md)** —— 设计目的、设计理念与端到端整体架构(从这里开始,从整体上理解本库)。
|
|
99
|
+
- **[API 参考](./docs/zh/api.md)** —— 完整的公共 API 参考。
|
|
100
|
+
- **[生命周期管理](./docs/zh/lifecycle.md)** —— 对象的创建、过期、刷新与删除如何运作,含匿名对象与引用对象(阅读本文以建立合理预期)。
|
|
101
|
+
- **[可扩展性](./docs/zh/extensibility.md)** —— 类型系统详解,含添加自定义标量与容器类型的完整示例(codec、对象类、注册)。
|
|
102
|
+
- **[性能指南](./docs/zh/performance-guide.md)** —— 会拖累性能的用法(滥用 sliding TTL、过深嵌套、不必要地启用 GC/加锁)及其推荐替代。把 GPDC 放上热路径前必读。
|
|
103
|
+
- **[多进程加锁](./docs/zh/locking.md)** —— 何时需要加锁、性能影响、推荐用法、避免死锁的顺序以及与 GC 的交互。
|
|
103
104
|
|
|
104
105
|
## 扩展
|
|
105
106
|
|
|
106
107
|
`gpdatacached.extensions` 下的可选内建扩展:
|
|
107
108
|
|
|
108
|
-
- **[Pydantic 支持](./docs/zh
|
|
109
|
-
- **[Pandas 支持](./docs/zh
|
|
109
|
+
- **[Pydantic 支持](./docs/zh/pydantic-support.md)** —— 缓存 pydantic `BaseModel` 记录。两种存储模式:标量模式(原子 JSON,适用于纯标量模型)和容器模式(Redis hash,适用于需要字段级访问或嵌套 `list`/`dict`/`set` 字段的模型)。pydantic 是核心依赖,无需额外安装。
|
|
110
|
+
- **[Pandas 支持](./docs/zh/pandas-support.md)** —— 将 pandas `Series` / `DataFrame` 缓存为活跃对象。在线选择性访问器(`.iloc`、`.loc`、`df[col]`)用于一次性子集读取;`.value` 用于完整还原;`extend` 用于追加。需要 `pip install "gpdatacached[pandas]"`。
|
|
110
111
|
|
|
111
112
|
---
|
|
112
113
|
|
|
@@ -44,7 +44,7 @@ GPDCDomain ──┬── GPDCNamespace ──┬── GPDCDataObject (scalar
|
|
|
44
44
|
|
|
45
45
|
---
|
|
46
46
|
|
|
47
|
-
## Quick Start
|
|
47
|
+
## Quick Start { #api-quick-start }
|
|
48
48
|
|
|
49
49
|
```python
|
|
50
50
|
import gpdatacached
|
|
@@ -57,7 +57,7 @@ config = GPDCDomainConfig(
|
|
|
57
57
|
redis_db=0,
|
|
58
58
|
redis_password="secret",
|
|
59
59
|
)
|
|
60
|
-
domain = GPDCDomain("mydomain"
|
|
60
|
+
domain = GPDCDomain(config, name="mydomain")
|
|
61
61
|
|
|
62
62
|
# 2. Get or create a namespace.
|
|
63
63
|
ns = domain.ensure_namespace("analytics")
|
|
@@ -90,12 +90,49 @@ domain.close()
|
|
|
90
90
|
|
|
91
91
|
---
|
|
92
92
|
|
|
93
|
+
## Managing Domains via Configuration: `GPDataCache`
|
|
94
|
+
|
|
95
|
+
When your application drives many domains from configuration files managed by
|
|
96
|
+
[`gpconfig`](https://github.com/linnetcodes/gpconfig), `GPDataCache` is the
|
|
97
|
+
higher-level entry point. It holds a `GPConfigFolder`, lazily constructs and
|
|
98
|
+
caches `GPDCDomain` instances from it, and resolves GPDC URIs to data objects.
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
from gpconfig import GPConfigManager
|
|
102
|
+
import gpdatacached # registers GPDCDomain with gpconfig
|
|
103
|
+
from gpdatacached import GPDataCache
|
|
104
|
+
|
|
105
|
+
# 1. Build a GPConfigFolder for the "gpdc" subfolder of your config folder.
|
|
106
|
+
mgr = GPConfigManager("myproject", cfg_folder="/path/to/configs")
|
|
107
|
+
folder = mgr.get_config("gpdc") # -> GPConfigFolder; sample layouts in tests/configs/gpdc
|
|
108
|
+
|
|
109
|
+
# 2. GPDataCache manages the domains found in that folder.
|
|
110
|
+
with GPDataCache(folder) as cache:
|
|
111
|
+
"core_domain" in cache # True iff a config exists (no Redis)
|
|
112
|
+
domain = cache.get_domain("core_domain") # lazy construct + cache
|
|
113
|
+
|
|
114
|
+
# 3. Resolve objects by GPDC URI — including anonymous nested objects.
|
|
115
|
+
ns = domain.get_namespace("core_app")
|
|
116
|
+
ns["config"] = {"server": {"host": "localhost", "port": 8080}, "tags": ["a", "b"]}
|
|
117
|
+
|
|
118
|
+
cfg = cache.get_object_by_uri("gpdc://core_domain/core_app/config")
|
|
119
|
+
server = cache.get_object_by_uri("gpdc://core_domain/core_app/config/server")
|
|
120
|
+
cache.has_uri("gpdc://core_domain/core_app/missing") # False
|
|
121
|
+
# close() on exit closes every cached domain (idempotent).
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
See [`GPDataCache`](#gpdatacache) below for the full API and the [URI resolution
|
|
125
|
+
rules](#get_object_by_uri) (path traversal, scalar terminals, `ListObject`
|
|
126
|
+
index coercion).
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
93
130
|
## Core Types
|
|
94
131
|
|
|
95
132
|
### `GPDCDomain`
|
|
96
133
|
|
|
97
134
|
```python
|
|
98
|
-
class GPDCDomain(
|
|
135
|
+
class GPDCDomain(config: GPDCDomainConfig, name: str | None = None)
|
|
99
136
|
```
|
|
100
137
|
|
|
101
138
|
The configuration root and Redis connection pool. On construction it:
|
|
@@ -158,7 +195,7 @@ Configuration for a `GPDCDomain`. All fields are validated; lock fields must be
|
|
|
158
195
|
| `default_cache_mode` | `str` | `"permanent"` | One of `"permanent"`, `"ttl"`, `"sliding"`. |
|
|
159
196
|
| `default_ttl_seconds` | `int` | `0` | Default TTL; must be `> 0` when mode is `"ttl"`/`"sliding"`. |
|
|
160
197
|
| `gc_enabled` | `bool` | `False` | Start the background GC thread. |
|
|
161
|
-
| `gc_interval_seconds` | `int` | `300` | Background GC cycle interval. |
|
|
198
|
+
| `gc_interval_seconds` | `int` | `300` | Background GC cycle interval (must be `≥ 30`; smaller values busy-loop the GC thread and flood Redis). |
|
|
162
199
|
| `namespaces` | `dict[str, GPDCNamespaceConfig]` | `{}` | Predefined namespaces to create eagerly. |
|
|
163
200
|
| `default_lock_ttl_seconds` | `int` | `30` | TTL on lock keys (must be `≥ 1`). |
|
|
164
201
|
| `default_lock_acquire_timeout_seconds` | `int` | `30` | Blocking timeout for `obj.lock()` (must be `≥ 0`). |
|
|
@@ -239,13 +276,133 @@ Delete the four lock-config fields (`default_lock_ttl_seconds`, `default_lock_ac
|
|
|
239
276
|
|
|
240
277
|
```python
|
|
241
278
|
reset_domain_config("mydomain", old_config) # must be called BEFORE construction
|
|
242
|
-
domain = GPDCDomain("mydomain"
|
|
279
|
+
domain = GPDCDomain(new_config, name="mydomain") # writes the new lock config
|
|
243
280
|
```
|
|
244
281
|
|
|
245
282
|
Only the Redis connection fields on `config` are read; lock fields on the passed config are ignored.
|
|
246
283
|
|
|
247
284
|
---
|
|
248
285
|
|
|
286
|
+
### `GPDataCache`
|
|
287
|
+
|
|
288
|
+
```python
|
|
289
|
+
class GPDataCache(folder: gpconfig.GPConfigFolder) # context manager
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
A facade that manages `GPDCDomain` instances discovered in a
|
|
293
|
+
[`gpconfig`](https://github.com/linnetcodes/gpconfig) `GPConfigFolder`, and
|
|
294
|
+
resolves GPDC URIs to [`GPDCDataObject`](#gpdcdataobject) instances. It is the
|
|
295
|
+
recommended entry point when your domains are driven by config files rather
|
|
296
|
+
than constructed ad-hoc in code.
|
|
297
|
+
|
|
298
|
+
**Construction.** Built from a `GPConfigFolder`. On construction it stores the
|
|
299
|
+
folder reference only — **no domain is created**. Domains are constructed
|
|
300
|
+
lazily (via gpconfig's object factory `folder.get_object(name)`) on first
|
|
301
|
+
[`get_domain`](#gpdatacache) access, and cached in an internal dict. The cache
|
|
302
|
+
is a snapshot: config files edited after construction are **not** re-read;
|
|
303
|
+
rebuild the `GPDataCache` to pick up changes.
|
|
304
|
+
|
|
305
|
+
`GPDataCache` does **not** create domains lacking a config file — for ad-hoc
|
|
306
|
+
domains use [`GPDCDomain`](#gpdcdomain) directly.
|
|
307
|
+
|
|
308
|
+
> **Registration side effect.** `import gpdatacached` registers `GPDCDomain`
|
|
309
|
+
> and `GPDCDomainConfig` with gpconfig's object factory (idempotent), which is
|
|
310
|
+
> what makes `folder.get_object(name)` able to construct a `GPDCDomain`. No
|
|
311
|
+
> extra setup is needed.
|
|
312
|
+
|
|
313
|
+
**Dunder protocols**
|
|
314
|
+
|
|
315
|
+
- `name in cache` → `True` iff a config named `name` exists in the folder. This is a pure config-snapshot check: it does **not** touch Redis and does not construct (or require) a `GPDCDomain` instance — the answer depends only on whether the config file is present, not on whether `get_domain(name)` has been called. The domain instance is created lazily on first `get_domain(name)` access.
|
|
316
|
+
- `with cache as c` / `__exit__` → closes every cached domain on exit; `close()` is idempotent.
|
|
317
|
+
|
|
318
|
+
> **`close()` behaviour — the membership exception.** After `cache.close()`, the resource-touching methods (`get_domain`, `get_object_by_uri`, `has_uri`) raise `RuntimeError("GPDataCache is closed")`. The `name in cache` membership check is the **deliberate exception**: because it only reads the config snapshot and touches no resources, it keeps working after `close()`. This is intentional — "does a config for this domain exist?" is meaningful regardless of whether the cache is open.
|
|
319
|
+
|
|
320
|
+
**Methods**
|
|
321
|
+
|
|
322
|
+
| Method | Description |
|
|
323
|
+
|---|---|
|
|
324
|
+
| `get_domain(name)` | Return the cached `GPDCDomain` for `name`, constructing it lazily on first access. Propagates gpconfig's `ConfigNotFoundError` if no config exists for `name`. Raises `RuntimeError` after `close()`. |
|
|
325
|
+
| [`get_object_by_uri(uri)`](#get_object_by_uri) | Resolve a GPDC URI to a `GPDCDataObject`. Raises `RuntimeError` after `close()`. |
|
|
326
|
+
| `has_uri(uri)` | Return `True` iff `get_object_by_uri(uri)` would succeed. Raises `RuntimeError` after `close()`. |
|
|
327
|
+
| `close()` | Close every cached domain. Idempotent. |
|
|
328
|
+
|
|
329
|
+
---
|
|
330
|
+
|
|
331
|
+
#### `get_object_by_uri`
|
|
332
|
+
|
|
333
|
+
```python
|
|
334
|
+
def get_object_by_uri(self, uri: str) -> GPDCDataObject
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
Resolve a GPDC URI to a [`GPDCDataObject`](#gpdcdataobject). The URI must begin
|
|
338
|
+
with `gpdc://` and has the form:
|
|
339
|
+
|
|
340
|
+
```
|
|
341
|
+
gpdc://{domain}/{namespace}/{name}[/{path_segment}...]
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
- The first three segments (`domain` / `namespace` / `name`) locate the **root
|
|
345
|
+
object**, fetched via `namespace.get_object(name)` — so the root is always
|
|
346
|
+
returned as a `GPDCDataObject`, **even when it is a scalar** (unlike
|
|
347
|
+
`ns[name]`, which unwraps scalars to native values).
|
|
348
|
+
- Any further segments form a **path** that descends into the object via
|
|
349
|
+
`__getitem__`, one segment at a time. This is how anonymous nested containers
|
|
350
|
+
become reachable: `gpdc://d/ns/obj/key/0` walks `obj["key"][0]`.
|
|
351
|
+
|
|
352
|
+
**Returns objects, never values.** `get_object_by_uri` always returns a
|
|
353
|
+
`GPDCDataObject`. It cannot be used to read a scalar value or an element value
|
|
354
|
+
directly.
|
|
355
|
+
|
|
356
|
+
**Path-segment rules**
|
|
357
|
+
|
|
358
|
+
| Current object | Segment handling |
|
|
359
|
+
|---|---|
|
|
360
|
+
| `ListObject` | The segment must be a **non-negative integer** (a pure-digit string); it is converted to `int` before indexing. Negative indices, decimals, and slices are rejected. |
|
|
361
|
+
| `DictObject` / pydantic container-mode model / other subscriptable container | The segment is used as-is (a string key). |
|
|
362
|
+
| `SetObject`, scalar objects | Have no `__getitem__` — any further path segment raises `ObjectNotFoundByURIError`. |
|
|
363
|
+
|
|
364
|
+
**Scalar terminals are rejected.** Scalars nested inside a container are stored
|
|
365
|
+
as inlined values (not as referenced objects), so descending into a scalar
|
|
366
|
+
element yields a native Python value. Because `get_object_by_uri` returns
|
|
367
|
+
objects only, a path that terminates on a scalar raises `ObjectNotFoundByURIError`.
|
|
368
|
+
For example, given `ns["cfg"] = {"port": 8080}`, `gpdc://d/ns/cfg/port` raises
|
|
369
|
+
(the value `8080` is a scalar, not a data object).
|
|
370
|
+
|
|
371
|
+
**Errors.** Every failure mode raises [`ObjectNotFoundByURIError`](#objectnotfoundbyurierror)
|
|
372
|
+
(carries `.uri` and `.reason`): malformed URI (missing `gpdc://`, fewer than
|
|
373
|
+
three segments, empty segment), unknown domain, missing namespace, missing root
|
|
374
|
+
object, a path segment that fails (`KeyError`/`IndexError`/`TypeError`), and a
|
|
375
|
+
scalar terminal.
|
|
376
|
+
|
|
377
|
+
**Example.** Given `ns["cfg"] = {"server": {"host": "x"}, "tags": ["a","b"]}`:
|
|
378
|
+
|
|
379
|
+
| URI | Result |
|
|
380
|
+
|---|---|
|
|
381
|
+
| `gpdc://d/ns/cfg` | the `DictObject` for `cfg` |
|
|
382
|
+
| `gpdc://d/ns/cfg/server` | the nested `DictObject` for `server` |
|
|
383
|
+
| `gpdc://d/ns/cfg/server/host` | raises — `host` is the scalar `"x"` |
|
|
384
|
+
| `gpdc://d/ns/cfg/tags/0` | raises — `tags[0]` is the scalar `"a"` |
|
|
385
|
+
| `gpdc://d/ns/cfg/missing` | raises — `KeyError` on the path segment |
|
|
386
|
+
|
|
387
|
+
---
|
|
388
|
+
|
|
389
|
+
### `ObjectNotFoundByURIError`
|
|
390
|
+
|
|
391
|
+
```python
|
|
392
|
+
class ObjectNotFoundByURIError(GPDCError)
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
Raised by [`GPDataCache.get_object_by_uri`](#get_object_by_uri) when a GPDC URI
|
|
396
|
+
cannot be resolved to a `GPDCDataObject`. Covers every failure mode: malformed
|
|
397
|
+
URI syntax, unknown domain (no config), missing namespace, missing root object,
|
|
398
|
+
a path segment that fails, and a scalar terminal. Carries `.uri` (the URI
|
|
399
|
+
string) and `.reason` (a short explanation).
|
|
400
|
+
|
|
401
|
+
`has_uri` swallows only this exception; infrastructure errors (e.g. Redis
|
|
402
|
+
connection failures) propagate.
|
|
403
|
+
|
|
404
|
+
---
|
|
405
|
+
|
|
249
406
|
## Data Objects
|
|
250
407
|
|
|
251
408
|
### `GPDCDataObject`
|
|
@@ -286,12 +443,13 @@ Abstract base class for every GPDC data object. Subclasses are split into [`GPDC
|
|
|
286
443
|
|
|
287
444
|
| Method | Description |
|
|
288
445
|
|---|---|
|
|
289
|
-
| `expire_in(seconds)` | Set a new TTL. Object must be in `cache_mode="ttl"`. Requires ownership. |
|
|
446
|
+
| `expire_in(seconds)` | Set a new TTL. Object must be in `cache_mode="ttl"`. `seconds` must be `> 0` — to delete now, use `expire_now()`. Requires ownership. |
|
|
290
447
|
| `expire_at(when)` | Set absolute expiry time. Object must be in `cache_mode="ttl"`. Requires ownership. Deletes the object immediately if `when` is in the past (equivalent to `del ns[name]`: cleans `obj:`/`raw:`/`ref:` and cascade-deletes anonymous subtrees; raises `CanonicalObjectReferencedError` if the owner is still referenced — check `has_referrers()` first). |
|
|
448
|
+
| `expire_now()` | Delete this object immediately. Equivalent to `del ns[canonical_name]`: full teardown (obj:/raw:/ref: + cascade-delete of anonymous subtrees). Works in any `cache_mode` (unlike `expire_in`/`expire_at`, which require `cache_mode="ttl"`). Requires ownership; raises `CanonicalObjectReferencedError` if the owner is still referenced by aliases — check `has_referrers()` first. |
|
|
291
449
|
| `persist()` | Switch to `cache_mode="permanent"` with `ttl=0`. Requires ownership. |
|
|
292
450
|
| `update_cache_mode_and_ttl(cache_mode, ttl)` | Replace the full cache policy. Requires ownership. |
|
|
293
451
|
| `refresh_ttl()` | Re-apply this object's own TTL to its keys and cascade-refresh its owned subtree (used by the sliding-window path). No-op for permanent objects. |
|
|
294
|
-
| `take_ownership()` | Promote a reference object to
|
|
452
|
+
| `take_ownership()` | Promote **the referenced object** to own itself (primary use case: a named reference to an anonymous nested object). On a reference to a named (already self-owned) object it is a correct no-op. The call itself must be made via a named reference handle. See [Lifecycle §7](./lifecycle.md#7-the-ownership-model). |
|
|
295
453
|
|
|
296
454
|
**Locking methods** (opt-in, never called automatically by mutations)
|
|
297
455
|
|
|
@@ -467,7 +625,7 @@ Members may be any hashable scalar (no containers, no empty tuple). Implements t
|
|
|
467
625
|
|
|
468
626
|
## Codecs
|
|
469
627
|
|
|
470
|
-
Codecs convert between Python values and their Redis storage representation. Every type registered with [`GPDCTypeRegistry`](#type-registry) must supply a codec.
|
|
628
|
+
Codecs convert between Python values and their Redis storage representation. Every type registered with [`GPDCTypeRegistry`](#api-type-registry) must supply a codec.
|
|
471
629
|
|
|
472
630
|
> This section is the API summary. For a full walkthrough of writing codecs — scalar and container, with worked examples (`Color`, `Counter`) and the mutation hooks every container must call — see the dedicated [Extensibility](./extensibility.md) document.
|
|
473
631
|
|
|
@@ -516,7 +674,7 @@ Abstract base for container codecs. Containers store their elements in a dedicat
|
|
|
516
674
|
|
|
517
675
|
---
|
|
518
676
|
|
|
519
|
-
## Type Registry
|
|
677
|
+
## Type Registry { #api-type-registry }
|
|
520
678
|
|
|
521
679
|
### `GPDCTypeRegistry`
|
|
522
680
|
|
|
@@ -562,7 +720,10 @@ GPDCTypeRegistry.register(
|
|
|
562
720
|
|
|
563
721
|
## Key & URI Utilities
|
|
564
722
|
|
|
565
|
-
Low-level helpers for working with GPDC's key/URI scheme.
|
|
723
|
+
Low-level helpers for working with GPDC's key/URI scheme. For actually
|
|
724
|
+
*resolving* a URI to a data object, use
|
|
725
|
+
[`GPDataCache.get_object_by_uri`](#get_object_by_uri) — these helpers only build
|
|
726
|
+
or convert URI/key strings and are rarely needed directly.
|
|
566
727
|
|
|
567
728
|
### `make_uri`
|
|
568
729
|
|
|
@@ -592,7 +753,23 @@ Validate that `name` contains only letters, digits, `.` or `_` and is not a rese
|
|
|
592
753
|
|
|
593
754
|
## Exceptions
|
|
594
755
|
|
|
595
|
-
All public exceptions descend from
|
|
756
|
+
All public exceptions descend from the public `GPDCError(Exception)` root. Use `except GPDCError` to catch every gpdatacached error, or catch them individually for precise handling. The lock-acquisition errors additionally share a public `GPDCLockError` base — `except GPDCLockError` catches every lock-acquisition/ownership failure (currently `LockAcquisitionTimeout`).
|
|
757
|
+
|
|
758
|
+
### `GPDCError`
|
|
759
|
+
|
|
760
|
+
```python
|
|
761
|
+
class GPDCError(Exception)
|
|
762
|
+
```
|
|
763
|
+
|
|
764
|
+
The root of the gpdatacached exception hierarchy. Every exception raised by this library is a subclass of `GPDCError`, so `except GPDCError` is the broad catch-all. Prefer catching the specific subclass when you need to distinguish failure modes.
|
|
765
|
+
|
|
766
|
+
### `GPDCLockError`
|
|
767
|
+
|
|
768
|
+
```python
|
|
769
|
+
class GPDCLockError(GPDCError)
|
|
770
|
+
```
|
|
771
|
+
|
|
772
|
+
Base class for lock-acquisition/ownership errors. `except GPDCLockError` catches every lock-related failure (currently `LockAcquisitionTimeout`; `LockOwnershipError` also descends from it but is swallowed internally by `HeldLock.release()`).
|
|
596
773
|
|
|
597
774
|
### `ObjectGoneError`
|
|
598
775
|
|
|
@@ -624,7 +801,7 @@ Raised during encoding when a container references itself (directly or transitiv
|
|
|
624
801
|
class LockAcquisitionTimeout(GPDCLockError)
|
|
625
802
|
```
|
|
626
803
|
|
|
627
|
-
Raised when `obj.lock()` cannot acquire the lock within the configured `timeout`. Carries `.lock_key` and `.timeout`. See [Multi-Process Locking](./locking.md).
|
|
804
|
+
Raised when `obj.lock()` cannot acquire the lock within the configured `timeout`. Carries `.lock_key` and `.timeout`. It extends the public `GPDCLockError` base, so `except GPDCLockError` catches it. See [Multi-Process Locking](./locking.md).
|
|
628
805
|
|
|
629
806
|
### `GPDCLockConfigInconsistentError`
|
|
630
807
|
|