stores¶
CompositeStore ¶
Bases: StoreBase
Routes each kind to a backend by record name, falling back to a default store.
This is what composition buys over subclassing: a deployment keeping rigs and tasks on a network share but trainer state in Dataverse is a routing table, not a class.
Example
from clabe.stores.dataverse import DataverseStore
store = CompositeStore(
default=LocalFileStore(root=VR_LIB),
routes={"trainer_state": DataverseStore()},
)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
default
|
Store | None
|
Serves any kind not named in |
None
|
routes
|
Mapping[str, Store] | None
|
Maps a :attr: |
None
|
Source code in src/clabe/stores/_base.py
318 319 320 321 322 323 324 325 326 327 | |
resolve ¶
resolve(
kind: KindLike[T],
*,
scope: Scope | None = None,
pick_kwargs: PickRequestKwargs | None = None,
) -> T
Delegates to the routed backend, so its own presentation is used.
Source code in src/clabe/stores/_base.py
345 346 347 348 349 350 351 352 353 354 | |
list ¶
list(
kind: KindLike[T], *, scope: Scope | None = None
) -> Sequence[T]
Delegates to the routed backend.
Source code in src/clabe/stores/_base.py
356 357 358 359 | |
write ¶
write(
kind: KindLike[T],
value: T,
*,
scope: Scope | None = None,
) -> None
Delegates to the routed backend.
Source code in src/clabe/stores/_base.py
361 362 363 364 | |
scoped ¶
scoped(**scope: str) -> CompositeStore
Returns a composite whose every backend is narrowed by the given scope.
Source code in src/clabe/stores/_base.py
366 367 368 369 370 371 | |
Kind ¶
Kind(
model: type[T],
name: str | None = None,
*,
validators: Callable[[T], T]
| Iterable[Callable[[T], T]]
| None = None,
)
Bases: Generic[T]
A record name bound to the pydantic model that parses it.
The name routes the record to a backend and determines its storage layout; the model types the return value of every store call made with this kind.
Canonical kinds should be built through :meth:from_rig, :meth:from_task,
:meth:from_session or :meth:from_trainer_state, which fix the framework
name. The bare constructor derives the name from the model class instead,
so Kind(AindVrForagingRig) is named "aind_vr_foraging_rig", not
"rig" -- it is for custom records only.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
model
|
type[T]
|
The pydantic model records of this kind deserialize to. |
required |
name
|
str | None
|
The record name. Defaults to the snake-cased model class name. |
None
|
validators
|
Callable[[T], T] | Iterable[Callable[[T], T]] | None
|
One callable, an iterable of callables, or |
None
|
Source code in src/clabe/stores/_base.py
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | |
adapter
cached
property
¶
adapter: TypeAdapter[T]
Serializes and parses records of this kind. Also covers models that are not BaseModel.
from_rig
staticmethod
¶
from_rig(
model: type[TRig],
*,
validators: Callable[[TRig], TRig]
| Iterable[Callable[[TRig], TRig]]
| None = validators,
) -> Kind[TRig]
Builds the canonical "rig" kind for a rig model.
Source code in src/clabe/stores/_base.py
118 119 120 121 122 123 124 125 | |
from_task
staticmethod
¶
Builds the canonical "task" kind for a task model.
Source code in src/clabe/stores/_base.py
127 128 129 130 | |
from_session
staticmethod
¶
from_session() -> Kind[Session]
Builds the canonical "session" kind for a session model.
Source code in src/clabe/stores/_base.py
132 133 134 135 | |
from_trainer_state
staticmethod
¶
from_trainer_state() -> Kind[TrainerState]
Builds the canonical "trainer_state" kind.
Source code in src/clabe/stores/_base.py
137 138 139 140 141 | |
Store ¶
Bases: Protocol
Reads, resolves and writes records of a given :class:Kind.
Which kinds a store actually serves is a deployment fact, not a type-level one: an unsupported kind raises when the call is made.
resolve ¶
resolve(
kind: KindLike[T],
*,
scope: Scope | None = None,
pick_kwargs: PickRequestKwargs | None = None,
) -> T
Selects a single record, prompting the user when the choice is ambiguous.
Source code in src/clabe/stores/_base.py
170 171 172 173 174 175 176 177 178 | |
list ¶
list(
kind: KindLike[T], *, scope: Scope | None = None
) -> Sequence[T]
Returns every record of this kind in scope, without prompting.
Source code in src/clabe/stores/_base.py
180 181 182 | |
write ¶
write(
kind: KindLike[T],
value: T,
*,
scope: Scope | None = None,
) -> None
Persists a record. Whether that overwrites, versions or appends is the store's contract.
Source code in src/clabe/stores/_base.py
184 185 186 | |
StoreBase ¶
StoreBase(*, scope: Scope | None = None)
Bases: ABC
Base for stores, supplying scope narrowing and the default resolution policy.
Subclasses implement :meth:_candidates and :meth:write, and may override
:meth:resolve when their backend affords a better presentation than a flat
pick list.
Source code in src/clabe/stores/_base.py
210 211 | |
scoped ¶
Returns a view sharing this store's data, narrowed by the given scope.
The view is the same concrete store type, so narrowing never loses backend-specific typing.
Source code in src/clabe/stores/_base.py
218 219 220 221 222 223 224 225 226 | |
write
abstractmethod
¶
write(
kind: KindLike[T],
value: T,
*,
scope: Scope | None = None,
) -> None
Persists a record. See :meth:Store.write.
Source code in src/clabe/stores/_base.py
236 237 238 | |
list ¶
list(
kind: KindLike[T], *, scope: Scope | None = None
) -> Sequence[T]
Returns every record of this kind in scope, validated but unprompted.
Source code in src/clabe/stores/_base.py
240 241 242 243 | |
resolve ¶
resolve(
kind: KindLike[T],
*,
scope: Scope | None = None,
pick_kwargs: PickRequestKwargs | None = None,
) -> T
Selects a single record: raises on none, auto-selects a lone candidate, prompts otherwise.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
kind
|
KindLike[T]
|
The kind of record to resolve. |
required |
scope
|
Scope | None
|
Narrows which records are considered. Layered over the store's own scope. |
None
|
pick_kwargs
|
PickRequestKwargs | None
|
Optional overrides for the :class: |
None
|
Raises:
| Type | Description |
|---|---|
LookupError
|
If no record of this kind exists in scope, or the user declined to pick one. |
NoFrontendError
|
If a choice must be made and no frontend is registered. |
Source code in src/clabe/stores/_base.py
245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 | |
DefaultLayout ¶
The config library tree as it exists on the shared drive today.
Rig/<computer_name>/*.json, Task/*.json and
Subjects/<subject>/<kind>.json. Tasks read from the subject folder first
and fall back to the shared library, which is the ordered scope chain that
replaces the nested lookup the picker used to do inline.
Kinds other than rig and task are per-subject state, living beside
the subject's task; with no subject in scope they sit at the library root,
which is what makes a one-off store pointed at a session directory work.
read ¶
Returns the glob patterns for this kind, subject folder before shared library.
Source code in src/clabe/stores/_local.py
48 49 50 51 52 53 54 55 56 | |
write ¶
Returns the single path this kind is written to.
Source code in src/clabe/stores/_local.py
58 59 60 61 62 | |
Layout ¶
Bases: Protocol
Maps a record kind and scope onto paths within a config library.
read ¶
Returns library-relative glob patterns to read from, highest priority first.
Source code in src/clabe/stores/_local.py
25 26 27 | |
LocalFileStore ¶
LocalFileStore(
root: PathLike | str,
*,
layout: Layout | None = None,
scope: Scope | None = None,
)
Bases: StoreBase
A store over a directory of JSON files, i.e. the config library.
Writes overwrite, and create the directories they need. Files that fail to parse are skipped with a warning rather than failing the whole listing.
Example
store = LocalFileStore(root=r"\\allen\aind\scratch\AindBehavior.db\AindVrForaging")
rig = store.resolve(Kind.from_rig(AindVrForagingRig))
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
root
|
PathLike | str
|
The config library directory. |
required |
layout
|
Layout | None
|
Maps kinds onto paths within |
None
|
scope
|
Scope | None
|
Initial scope. |
None
|
Source code in src/clabe/stores/_local.py
84 85 86 87 88 89 90 91 92 93 | |
scoped ¶
Returns a view sharing this store's data, narrowed by the given scope.
The view is the same concrete store type, so narrowing never loses backend-specific typing.
Source code in src/clabe/stores/_base.py
218 219 220 221 222 223 224 225 226 | |
list ¶
list(
kind: KindLike[T], *, scope: Scope | None = None
) -> Sequence[T]
Returns every record of this kind in scope, validated but unprompted.
Source code in src/clabe/stores/_base.py
240 241 242 243 | |
resolve ¶
resolve(
kind: KindLike[T],
*,
scope: Scope | None = None,
pick_kwargs: PickRequestKwargs | None = None,
) -> T
Selects a single record: raises on none, auto-selects a lone candidate, prompts otherwise.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
kind
|
KindLike[T]
|
The kind of record to resolve. |
required |
scope
|
Scope | None
|
Narrows which records are considered. Layered over the store's own scope. |
None
|
pick_kwargs
|
PickRequestKwargs | None
|
Optional overrides for the :class: |
None
|
Raises:
| Type | Description |
|---|---|
LookupError
|
If no record of this kind exists in scope, or the user declined to pick one. |
NoFrontendError
|
If a choice must be made and no frontend is registered. |
Source code in src/clabe/stores/_base.py
245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 | |
write ¶
write(
kind: KindLike[T],
value: T,
*,
scope: Scope | None = None,
) -> None
Overwrites the file this kind maps to, creating parent directories.
Source code in src/clabe/stores/_local.py
117 118 119 120 121 122 123 | |
MemoryStore ¶
MemoryStore(*, scope: Scope | None = None)
Bases: StoreBase
A store holding records in memory, for tests and for assembling records in-process.
Writes append, as Dataverse does, so several records of one kind can be offered as candidates. A record is visible to a read whose scope agrees with every narrowing the record was written under, so a record written unscoped is library-wide.
Source code in src/clabe/stores/_memory.py
19 20 21 | |
scoped ¶
Returns a view sharing this store's data, narrowed by the given scope.
The view is the same concrete store type, so narrowing never loses backend-specific typing.
Source code in src/clabe/stores/_base.py
218 219 220 221 222 223 224 225 226 | |
list ¶
list(
kind: KindLike[T], *, scope: Scope | None = None
) -> Sequence[T]
Returns every record of this kind in scope, validated but unprompted.
Source code in src/clabe/stores/_base.py
240 241 242 243 | |
resolve ¶
resolve(
kind: KindLike[T],
*,
scope: Scope | None = None,
pick_kwargs: PickRequestKwargs | None = None,
) -> T
Selects a single record: raises on none, auto-selects a lone candidate, prompts otherwise.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
kind
|
KindLike[T]
|
The kind of record to resolve. |
required |
scope
|
Scope | None
|
Narrows which records are considered. Layered over the store's own scope. |
None
|
pick_kwargs
|
PickRequestKwargs | None
|
Optional overrides for the :class: |
None
|
Raises:
| Type | Description |
|---|---|
LookupError
|
If no record of this kind exists in scope, or the user declined to pick one. |
NoFrontendError
|
If a choice must be made and no frontend is registered. |
Source code in src/clabe/stores/_base.py
245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 | |
write ¶
write(
kind: KindLike[T],
value: T,
*,
scope: Scope | None = None,
) -> None
Appends a record, so repeated writes accumulate as candidates.
Source code in src/clabe/stores/_memory.py
34 35 36 | |
FicusClient ¶
FicusClient(
base_url: str = DEFAULT_BASE_URL,
*,
default_extension: str = ".json",
timeout: float = 10.0,
session: Session | None = None,
)
An HTTP client for ficus, independent of clabe's store vocabulary.
Example
client = FicusClient("http://eng-tools/ficus-dev")
result = client.get_merged("aind-behavior-vr-foraging", scopes=[COMPUTERS("DT201256")])
print(result.sources) # contributing documents, lowest precedence first
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
base_url
|
str
|
Ficus' root URL. |
DEFAULT_BASE_URL
|
default_extension
|
str
|
Names a document ficus does not have yet, and is tried first when
reading. Reads fall back across :data: |
'.json'
|
timeout
|
float
|
Seconds to wait on any single request. |
10.0
|
session
|
Session | None
|
A |
None
|
Source code in src/clabe/stores/ficus.py
391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 | |
get_layer ¶
get_layer(
namespace: str,
*,
filename: str = DEFAULT_FILENAME,
scope: ScopeRef | None = None,
) -> Layer | None
Fetches exactly one document, without any server-side merging.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
namespace
|
str
|
The ficus namespace. |
required |
filename
|
str
|
The document to fetch. |
DEFAULT_FILENAME
|
scope
|
ScopeRef | None
|
The scope to read from. |
None
|
Returns:
| Type | Description |
|---|---|
Layer | None
|
Layer | None: The document, or |
Raises:
| Type | Description |
|---|---|
HTTPError
|
For any failure other than the document being absent. |
Source code in src/clabe/stores/ficus.py
463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 | |
locate ¶
Finds which extension a document is stored under, and fetches it.
A read matches the extension exactly, so a document stored as rig.yml is absent to a
request for rig.json. :attr:default_extension is tried first, then the rest of
:data:SUPPORTED_EXTENSIONS. Ficus allows a stem only one extension at a time, so at most
one can exist.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
namespace
|
str
|
The ficus namespace. |
required |
stem
|
str
|
The document name without its extension, e.g. |
required |
scope
|
ScopeRef | None
|
The scope to look in. |
None
|
Returns:
| Type | Description |
|---|---|
tuple[str, Layer | None]
|
tuple[str, Layer | None]: The filename to address -- the extension found, or |
Raises:
| Type | Description |
|---|---|
HTTPError
|
For any failure other than a document being absent. |
Source code in src/clabe/stores/ficus.py
501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 | |
get_merged ¶
get_merged(
namespace: str,
*,
filename: str | None = None,
scopes: Iterable[ScopeRef] = (),
policy: MergePolicy | None = None,
) -> MergeResult
Fetches every layer in the chain and merges them.
The chain runs defaults first, then each scope in the order given, and within each
layer the default document before any extra filename. This is ficus' documented
precedence.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
namespace
|
str
|
The ficus namespace. |
required |
filename
|
str | None
|
An extra document to layer on top of |
None
|
scopes
|
Iterable[ScopeRef]
|
Scopes to layer, lowest precedence first. |
()
|
policy
|
MergePolicy | None
|
How to combine layers, and what to do about missing ones. |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
MergeResult |
MergeResult
|
The merged document and the paths that produced it. |
Raises:
| Type | Description |
|---|---|
LookupError
|
If |
Source code in src/clabe/stores/ficus.py
528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 | |
write ¶
write(
namespace: str,
data: dict[str, Any],
*,
filename: str = DEFAULT_FILENAME,
scope: ScopeRef | None = None,
create_only: bool = False,
) -> None
Writes one document, to exactly one layer.
POST is tried first and falls back to PATCH on a 409. Ficus' PATCH deep-merges
the payload into the existing document server-side, so a partial write needs no
read-modify-write here; it cannot remove a key, which takes a delete and a rewrite.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
namespace
|
str
|
The ficus namespace. |
required |
data
|
dict[str, Any]
|
The document, or the part of it being changed. |
required |
filename
|
str
|
The document to write. |
DEFAULT_FILENAME
|
scope
|
ScopeRef | None
|
The layer to write to. |
None
|
create_only
|
bool
|
Whether to fail rather than update an existing document. |
False
|
Raises:
| Type | Description |
|---|---|
HTTPError
|
If the write fails. |
Source code in src/clabe/stores/ficus.py
611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 | |
plan_write_back
staticmethod
¶
plan_write_back(
current: MergeResult,
new_data: dict[str, Any],
*,
target: LayerKey | None = None,
policy: WritePolicy | None = None,
) -> LayerWrite | None
Works out what a write-back would do, without doing it.
Only the leaves that differ are written, to a single layer. The default target is the top
of the merge chain, which no layer can shadow. A lower target is allowed, and
policy.on_shadowed_write then governs any leaf whose current value comes from above it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
current
|
MergeResult
|
The config as it resolves now, from :meth: |
required |
new_data
|
dict[str, Any]
|
The config as it should resolve. |
required |
target
|
LayerKey | None
|
The layer to write to. |
None
|
policy
|
WritePolicy | None
|
How to handle removals and shadowed writes. |
None
|
Returns:
| Type | Description |
|---|---|
LayerWrite | None
|
LayerWrite | None: The write to perform, or |
Raises:
| Type | Description |
|---|---|
ValueError
|
If there is no chain to write to. |
LookupError
|
If a key would have to be removed and |
PermissionError
|
If the write would be shadowed and |
Source code in src/clabe/stores/ficus.py
646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 | |
write_back ¶
write_back(
namespace: str,
current: MergeResult,
new_data: dict[str, Any],
*,
target: LayerKey | None = None,
policy: WritePolicy | None = None,
) -> LayerWrite | None
Writes the difference between a resolved config and a new one back to a single layer.
See :meth:plan_write_back for how the layer and the payload are chosen; this runs that
plan.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
namespace
|
str
|
The ficus namespace. |
required |
current
|
MergeResult
|
The config as it resolves now, from :meth: |
required |
new_data
|
dict[str, Any]
|
The config as it should resolve. |
required |
target
|
LayerKey | None
|
The layer to write to. |
None
|
policy
|
WritePolicy | None
|
How to handle removals and shadowed writes. |
None
|
Returns:
| Type | Description |
|---|---|
LayerWrite | None
|
LayerWrite | None: What was written, or |
Raises:
| Type | Description |
|---|---|
HTTPError
|
If the write fails. |
Source code in src/clabe/stores/ficus.py
743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 | |
delete ¶
delete(
namespace: str,
*,
filename: str = DEFAULT_FILENAME,
scope: ScopeRef | None = None,
) -> bool
Deletes one document.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
namespace
|
str
|
The ficus namespace. |
required |
filename
|
str
|
The document to delete. |
DEFAULT_FILENAME
|
scope
|
ScopeRef | None
|
The layer to delete from. |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
bool |
bool
|
True if a document was deleted, False if there was nothing there. |
Raises:
| Type | Description |
|---|---|
HTTPError
|
If the delete fails for any reason other than absence. |
Source code in src/clabe/stores/ficus.py
777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 | |
list_files ¶
Lists the documents visible for a namespace, optionally within one scope.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
namespace
|
str
|
The ficus namespace. |
required |
scope
|
ScopeRef | None
|
Restrict to this scope. |
None
|
Returns:
| Type | Description |
|---|---|
list[str]
|
list[str]: The document paths, or an empty list if the scope holds nothing. |
Raises:
| Type | Description |
|---|---|
HTTPError
|
For any failure other than the scope being absent. |
Source code in src/clabe/stores/ficus.py
799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 | |
FicusSettings ¶
Bases: ServiceSettings
Settings for :class:FicusStore, read from the ficus section of clabe.yml.
settings_customise_sources
classmethod
¶
settings_customise_sources(
settings_cls: type[BaseSettings],
init_settings: PydanticBaseSettingsSource,
env_settings: PydanticBaseSettingsSource,
dotenv_settings: PydanticBaseSettingsSource,
file_secret_settings: PydanticBaseSettingsSource,
) -> tuple[PydanticBaseSettingsSource, ...]
Customizes the settings sources to include the safe YAML settings source.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
settings_cls
|
type[BaseSettings]
|
The settings class |
required |
init_settings
|
PydanticBaseSettingsSource
|
The initial settings source |
required |
env_settings
|
PydanticBaseSettingsSource
|
The environment settings source |
required |
dotenv_settings
|
PydanticBaseSettingsSource
|
The dotenv settings source |
required |
file_secret_settings
|
PydanticBaseSettingsSource
|
The file secret settings source |
required |
Returns:
| Type | Description |
|---|---|
tuple[PydanticBaseSettingsSource, ...]
|
Tuple[PydanticBaseSettingsSource, ...]: A tuple of settings sources |
Source code in src/clabe/services.py
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 | |
FicusStore ¶
FicusStore(
settings: FicusSettings | None = None,
*,
client: FicusClient | None = None,
serves: str | set[str] | None = None,
scope: Scope | None = None,
)
Bases: StoreBase
A store over a single ficus namespace.
Serves two shapes of document, told apart by kind name:
- A kind in
config_kinds("rig"by default) is layered config: merged acrossdefaultsand the configured scopes, from ficus'defaultdocument. The merge yields exactly one document, so :meth:resolvenever prompts. - Any other kind is a flat record: a standalone document named after the kind, living in one
scope, read and written whole. :class:
~clabe.modifiers.ByAnimalModifieruses this shape.
Example
store = FicusStore(FicusSettings(namespace="aind-behavior-vr-foraging"))
rig = store.resolve(Kind.from_rig(AindVrForagingRig))
position = store.scoped(subject="789907").resolve(Kind(ManipulatorPosition))
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
settings
|
FicusSettings | None
|
Settings for this store. Defaults to |
None
|
client
|
FicusClient | None
|
The ficus client. Defaults to |
None
|
serves
|
str | set[str] | None
|
Kind name(s) this store will answer for, or |
None
|
scope
|
Scope | None
|
Initial scope. |
None
|
Source code in src/clabe/stores/ficus.py
894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 | |
scoped ¶
Returns a view sharing this store's data, narrowed by the given scope.
The view is the same concrete store type, so narrowing never loses backend-specific typing.
Source code in src/clabe/stores/_base.py
218 219 220 221 222 223 224 225 226 | |
list ¶
list(
kind: KindLike[T], *, scope: Scope | None = None
) -> Sequence[T]
Returns every record of this kind in scope, validated but unprompted.
Source code in src/clabe/stores/_base.py
240 241 242 243 | |
resolve ¶
resolve(
kind: KindLike[T],
*,
scope: Scope | None = None,
pick_kwargs: PickRequestKwargs | None = None,
) -> T
Selects a single record: raises on none, auto-selects a lone candidate, prompts otherwise.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
kind
|
KindLike[T]
|
The kind of record to resolve. |
required |
scope
|
Scope | None
|
Narrows which records are considered. Layered over the store's own scope. |
None
|
pick_kwargs
|
PickRequestKwargs | None
|
Optional overrides for the :class: |
None
|
Raises:
| Type | Description |
|---|---|
LookupError
|
If no record of this kind exists in scope, or the user declined to pick one. |
NoFrontendError
|
If a choice must be made and no frontend is registered. |
Source code in src/clabe/stores/_base.py
245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 | |
write ¶
write(
kind: KindLike[T],
value: T,
*,
scope: Scope | None = None,
) -> None
Persists a value.
A flat record is written whole, to the subject's scope when one is in scope and the rig's
otherwise. Layered config is written as a diff against its current resolution: only the
leaves that differ are sent, to the top of the merge chain. See
:meth:FicusClient.plan_write_back.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
kind
|
KindLike[T]
|
The kind being written. |
required |
value
|
T
|
The value. |
required |
scope
|
Scope | None
|
Extra scope for this call, layered over the store's own. |
None
|
Raises:
| Type | Description |
|---|---|
LookupError
|
If this store does not serve |
PermissionError
|
If a layered write would land below a layer that overrides it. |
HTTPError
|
If the write fails. |
Source code in src/clabe/stores/ficus.py
1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 | |
LayerKey
dataclass
¶
MergePolicy
dataclass
¶
MergePolicy(
null_means: Literal["value", "delete"] = "value",
lists: Literal["replace", "concat"] = "replace",
on_type_conflict: Literal[
"override", "warn", "raise"
] = "warn",
on_missing_layer: Literal["skip", "raise"] = "skip",
)
How layers combine. The defaults reproduce ficus' _deep_update.
Attributes:
| Name | Type | Description |
|---|---|---|
null_means |
Literal['value', 'delete']
|
|
lists |
Literal['replace', 'concat']
|
|
on_type_conflict |
Literal['override', 'warn', 'raise']
|
What to do when a layer replaces a dict with a non-dict or vice versa.
|
on_missing_layer |
Literal['skip', 'raise']
|
|
WritePolicy
dataclass
¶
WritePolicy(
on_key_removal: Literal["raise", "skip"] = "raise",
on_shadowed_write: Literal[
"raise", "warn", "allow"
] = "raise",
)
How a layered-config write-back behaves.
Attributes:
| Name | Type | Description |
|---|---|---|
on_key_removal |
Literal['raise', 'skip']
|
What to do when a key present in the resolved config is absent from the
value being written back. Ficus' |
on_shadowed_write |
Literal['raise', 'warn', 'allow']
|
What to do when a changed leaf's current value comes from a layer above
the one being written to, so the next read would return the old value. |
as_kind ¶
as_kind(kind: KindLike[T]) -> Kind[T]
Normalizes a kind-or-model argument into a :class:Kind.
Source code in src/clabe/stores/_base.py
156 157 158 | |