Skip to main content

Sparse, scoped, multi-level property store with pluggable key mapping

Project description

vcti-properties

Sparse, scoped, multi-level property store with pluggable key mapping.

Overview

vcti-properties stores sparse named properties attached to entities or collections, with optional layered resolution — a per-entity value that falls back to a shared default. It spans the whole range from fixed metadata (units, components, a creator — authoritative, rarely overridden) to overridable properties (a plot's color, opacity, visibility — set as chart-wide defaults and overridden per series). Both are just values addressed by a structured key.

Rather than a tangle of nested dictionaries, every property lives in one sparse store keyed by a flat string, and three concerns are kept cleanly separated:

  • Authority — application-defined (system) properties vs. user-supplied ones, held in separate namespaces so they never collide. System names are drawn from a controlled vocabulary; user names are free-form.
  • Locus — what a property is bound to: a specific entity (one column, element, node), or the whole collection.
  • Resolution — a per-entity lookup can fall back to a shared default, so one value stands in for every entity that has not set its own.

Nothing here is dataset-specific: any set of entities — form fields, scene objects, config sections — each carrying its own properties over shared defaults, with system settings kept apart from user settings, fits the same shape. The levels you declare encode the (authority, locus, name) parts into one flat string key, so the store stays sparse (unset combinations cost nothing) and lookups stay O(1). Pure standard library, zero runtime dependencies.

Why not nested dicts or ChainMap?

Nested dictionaries — one level per authority, per entity, per name — mean a lot of structure for little data, plus hand-rolled lookup repeated at every call site. ChainMap gives you fallthrough for free, but you would need one dict per (authority × locus) combination and must reassemble the chain per entity — the layer count explodes with the number of entities. This package keeps the unbounded axis (locus) in a flat key over one sparse dict, and reserves layered fallthrough for the small, bounded axis where it belongs (see Design & Concepts).

Installation

pip install vcti-properties

In pyproject.toml dependencies

dependencies = [
    "vcti-properties>=1.0.0",
]

Quick Start

A store is defined by an ordered list of levels passed straight to PropertyStore (it builds the key encoder for you). Each level is one part of the key; the last level is the property name, and the value is stored separately against the assembled key.

Here's a chart whose series are styled with chart-wide defaults that any series can override — exactly the overridable kind of value that is a property (unlike fixed metadata such as units or a creator):

from enum import StrEnum

from vcti.properties import PropertyStore, Level

class Style(StrEnum):                     # the plot properties we recognise
    COLOR = "color"
    OPACITY = "opacity"
    VISIBLE = "visible"

chart = PropertyStore(
    Level("series", sentinel="@"),        # a specific series, or chart-wide (@)
    Level("style", vocabulary=Style),     # the property name
)

# Chart-wide defaults — apply to every series unless overridden:
chart.set(style=Style.COLOR, value="steelblue")     # -> "@.color"
chart.set(style=Style.OPACITY, value=1.0)
chart.set(style=Style.VISIBLE, value=True)

# Per-series overrides:
chart.set(series="revenue", style=Style.COLOR, value="crimson")
chart.set(series="forecast", style=Style.OPACITY, value=0.4)

# resolve() = the series' own value, else the chart-wide default:
chart.resolve(series="revenue",  style=Style.COLOR)    # "crimson"    (overridden)
chart.resolve(series="forecast", style=Style.COLOR)    # "steelblue"  (chart-wide default)
chart.resolve(series="forecast", style=Style.OPACITY)  # 0.4          (overridden)
chart.get(series="forecast", style=Style.COLOR)        # None         (exact read, no fallback)

Add a system/user scope as one more level when the application's own defaults must stay separate from a user's edits — system.* and user.* are distinct namespaces that never collide (each resolves within itself):

from vcti.properties import PropertyStore, KeyScope, Level

chart = PropertyStore(
    Level("scope", vocabulary=KeyScope),   # application theme (system) vs. user edits (user)
    Level("series", sentinel="@"),
    Level("name"),
)

chart.set(scope=KeyScope.SYSTEM, name="color", value="steelblue")                 # "system.@.color"
chart.set(scope=KeyScope.USER, series="revenue", name="color", value="crimson")   # "user.revenue.color"

With no levels, the store uses dot-joined paths (DefaultKeyMapper):

from vcti.properties import PropertyStore

store = PropertyStore()                                   # or PropertyStore(separator="/")
store.set("user", "profile", value={"name": "Alice"})     # key "user.profile"
store.get("user", "profile")                              # {"name": "Alice"}

Key mappers

Passing levels to PropertyStore builds a KeyMapper — the codec that encodes the (authority, locus, name) parts into a flat key and back. It's pluggable, so one store serves every layout:

Mapper What it does
DefaultKeyMapper Unstructured positional parts joined by a separator (a.b.c) — the default when no levels are given
LeveledKeyMapper Structured named levels — built from the Levels you pass to PropertyStore

Scenario-specific layouts (a system/user scoped key, a single-level config key) are configurations of levels, not separate classes. For a bespoke scheme, pass key_mapper= any object implementing parts_to_key(...) and key_to_parts(key). See Extending.


Dependencies

None. Standard library only.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

vcti_properties-1.0.0.tar.gz (16.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

vcti_properties-1.0.0-py3-none-any.whl (12.0 kB view details)

Uploaded Python 3

File details

Details for the file vcti_properties-1.0.0.tar.gz.

File metadata

  • Download URL: vcti_properties-1.0.0.tar.gz
  • Upload date:
  • Size: 16.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for vcti_properties-1.0.0.tar.gz
Algorithm Hash digest
SHA256 c4bc2f5bcd7fc7fa101605ba0964d30c8495dd86fe2611f7a254cf3cfbfcd613
MD5 42b82da7b43c9c00cb531c7e29fb4e95
BLAKE2b-256 a5b8eed6a526a9fe0da85f3b1a9f5023fd02cf674a6cbe749db50e3c4cb27593

See more details on using hashes here.

Provenance

The following attestation bundles were made for vcti_properties-1.0.0.tar.gz:

Publisher: release.yml on vcollab/vcti-python-properties

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file vcti_properties-1.0.0-py3-none-any.whl.

File metadata

File hashes

Hashes for vcti_properties-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 a60f394c56896a78bb5bf78d55069f8cc0b30752adcc3b4c4069bf4c3e45a0ab
MD5 18231e2d81d9bfcbe11306c343f1b5bc
BLAKE2b-256 46bcc153849464cfb570ef79cf194bd045619e58ef694a19a370f41ace6faa1f

See more details on using hashes here.

Provenance

The following attestation bundles were made for vcti_properties-1.0.0-py3-none-any.whl:

Publisher: release.yml on vcollab/vcti-python-properties

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page