Skip to main content

Manage your configuration data by stacking grouped edits.

Project description

tgzr.contextual_settigs

Manage settings values in layers of operations.

Install

pip install tgzr.contextual_settigs

If you want to use the demo GUI: pip install tgzr.contextual_settigs[demo] See Running the Demo

Usage

There will be more store types in the future (FileStore, RESTStore, SqlStore, ...) But there's only MemoryStore for now.

Also, you'll be able to provide your own store implementations.

MemoryStore

The MemoryStore stores the config in memory. You need to populate it yourself.

from tgzr.contextual_settigs.stores.memory_store import MemoryStore

# Create the store
store = MemoryStore()

# Populate values to some contexts
store.set("BASE", "app.name", "my_app")
store.set("BASE", "app.title", "My App")
store.set("BASE", "app.version", "1.0")
store.set("BASE", "team", ["Alice", "Bob"])

store.set("DEV", "app.title", "My App (Dev)")
store.set("DEV", "app.version", "1.0+dev1")
store.append("DEV", "team", "Carol")

store.set("RC", "app.title", "My App (Release Candidat)")
store.set("RC", "app.version", "2.0rc1")
store.remove("RC", "team", "Bob")
store.append("RC", "team", "Dee")

Resolving Config

As Nested Dicts

Once the store is populated, you can retrieve the config for a given context:

base = store.get_context_dict(["BASE"])
assert base["app"]["name"] == "my_app"
assert base["app"]["title"] == "My App"
assert base["team"] == ["Alice", "Bob"]

dev = store.get_context_dict(["DEV"])
try:
    base["app"]["name"]
except KeyError as err:
    assert str(err) == "name"
assert dev["app"]["title"] == "My App (Dev)"
assert dev["team"] == ["Carol"]

Or for a list of stacked contexts:

conf_dev = store.get_context_dict(["BASE", "DEV"])
assert conf_dev["app"]["name"] == "my_app"
assert conf_dev["app"]["title"] == "My App (Dev)"
assert conf_dev["team"] == ["Alice", "Bob", "Carol"]

conf_rc = store.get_context_dict(["BASE", "RC"])
assert conf_rc["app"]["name"] == "my_app"
assert conf_rc["app"]["title"] == "My App (Release Candidat)"
assert conf_rc["team"] == ["Alice", "Dee"]

With History

if you want to inspect how the value was built, you can set the with_history arg to True. The returned dict will contain a __history__ key containing a dict with the same keys as previously, but each value for these key is a list of dict depicting the operation affecting the value:

config = store.get_context_dict(["BASE", "DEV"], with_history=True)
assert config["app"]["name"] == "my_app"
assert config["app"]["title"] == "My App (Dev)"
assert config["team"] == ["Alice", "Bob", "Carol"]
assert config["__history__"]["app"].get("name") is None
assert config["__history__"]["app"]["title"][0]["context_name"] == "DEV"
assert config["__history__"]["app"]["title"][0]["op_name"] == "Set"

The history list contain dicts with these keys:

  • context_name: the name of the context affecting the value
  • op_name: the name of the operation affecting the value
  • op: a string representation of the operation affecting the value

As Flat Dict

If you prefer to access your value without nested dict, you can use get_context_flat() (with or without history):

config = store.get_context_flat(["BASE", "DEV"])
assert config["app.name"] == "my_app"
assert config["app.title"] == "My App (Dev)"
assert config["team"] == ["Alice", "Bob", "Carol"]
assert config["__history__"].get("app.name") is None
assert config["__history__"]["app.title"][0]["context_name"] == "DEV"
assert config["__history__"]["app.title"][0]["op_name"] == "Set"

As Pydantic Models

You can get a config as an instance of a pydantic.BaseModel. This is usefull to coerce / validate the schema of the generated config, and also provide you with ✨code completion✨:

import pydantic

class AppSettings(pydantic.BaseModel):
    name: str | None = None
    title: str | None = None
    version: str | None = None

class Config(pydantic.BaseModel):
    app: AppSettings = AppSettings()
    team: list[str] = []

config = store.get_context(["BASE", "DEV"], Config)
assert config.app.name == "my_app"
assert config.app.title == "My App (Dev)"
assert config.team == ["Alice", "Bob", "Carol"]
Note: 
You cannot request the history when getting the context as a pydandic model.

Context name expansion

Environment Variables

You can use environment variable in the context names:

import os

os.environ['STAGE'] = 'PROD'

config_1 = store.get_context_dict(['defaults', '$STAGE'])
config_2 = store.get_context_dict(['defaults', 'PROD'])
assert config_1 == config_2

You can even nest environment variables:

import os

os.environ['FirstName'] = 'Guido'
os.environ['LastName'] = 'van Rossum'
os.environ['FullName'] = '$FirstName $LastName'

config_1 = store.get_context_dict(['$FullName'])
config_2 = store.get_context_dict(['Guido van Rossum'])
assert config_1 == config_2

Expand Path

When you manage context for a hierarchy of things, you often need to specify a context per level of that hierarchy. For example, with a hierarchy like:

'$project_name/teams/$team_name'

You'll probably want to use context names like:

['@project_name', '$project_name/teams', '$project_name/teams/$team_name']

to cumulate all the value set at each level of the hierarchy.

To generate this list, you can use the Expand Path notation. When a context name is enclosed with square brackets, the name will be treated as the path you want to expand:

config_1 = store.get_context_dict(['Base', '[$PROJ/Teams/$Team]', 'Last'])
config_2 = store.get_context_dict(
    ['Base', '$PROJ', '$PROJ/Teams', '$PROJ/Teams/$Team', 'Last']
)
assert config_1 == config_2

(Of course, environement variable will be reduced too)

Running the Demo

To run the Demo GUI, you must install with the demo extra:

pip install tgzr.contextual_settigs[demo]

And then, you can launch the demo with:

uv run tgzr_contextual_settings_demo

or, if you want to pass arguments to faspapi:

uv run -p 3.13 --extra demo fastapi dev ./src/tgzr/contextual_settigs/demo/app.py

This will open a browser window with the Demo GUI.

With docker

If you fancy it, you can build a docker image and run it.

From the root of the repository:

docker build -t tgzr-contextual_settigs-demo .
docker run -d --name contextual_settigs_demo -p 8083:8080 tgzr-contextual_settigs-demo

Implementation Checklist

  • MemoryStore
  • Basic set of operators
  • Get context as dict
  • Get context as flat dict
  • Get context as pydantic model
  • Support env var in context names
  • Support path expansion in context names
  • Get context with history
  • Rename *store -> *Conf ?
  • Support context info (color, type, ...)
  • [~] web UI
  • Update context with dict
  • Update context with flat dict
  • Update context with pydantic model
  • [~] Base models for collection of items
  • Validate context with pydantic model, report all error (for gui/inspection)
  • Refactor tests wiht pytest
  • test store.get_context* with path
  • Rest Service
  • desktop UI
  • Fancy operators
  • FileStore
  • SqlStore
  • RestStore
  • Support string templating in context name
  • Support env var in values
  • Support string templating in values

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

tgzr_contextual_settings-0.0.10.tar.gz (661.9 kB view details)

Uploaded Source

Built Distribution

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

tgzr_contextual_settings-0.0.10-py3-none-any.whl (674.6 kB view details)

Uploaded Python 3

File details

Details for the file tgzr_contextual_settings-0.0.10.tar.gz.

File metadata

  • Download URL: tgzr_contextual_settings-0.0.10.tar.gz
  • Upload date:
  • Size: 661.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.25 {"installer":{"name":"uv","version":"0.9.25","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"KDE neon","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for tgzr_contextual_settings-0.0.10.tar.gz
Algorithm Hash digest
SHA256 98115e9831260a378e2beda99c005b1b24828bcb3b8ed75225753250bd6c486b
MD5 b8db5de722448fa45e13911220264ef3
BLAKE2b-256 b9b9bb34c72c4384a834683f380a103751ebc0c97c99535996ec70f8c29eb57d

See more details on using hashes here.

File details

Details for the file tgzr_contextual_settings-0.0.10-py3-none-any.whl.

File metadata

  • Download URL: tgzr_contextual_settings-0.0.10-py3-none-any.whl
  • Upload date:
  • Size: 674.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.25 {"installer":{"name":"uv","version":"0.9.25","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"KDE neon","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for tgzr_contextual_settings-0.0.10-py3-none-any.whl
Algorithm Hash digest
SHA256 6135c1d55d5885783100ffbfcbd51a4bbfab571ab43599c12fc0c5fe64dff19a
MD5 66d242bf566ba95919d8b92a2461b96c
BLAKE2b-256 e50b2dfb93982d37dfc08b5a04157a00b17067145d10e1bc91861b6a15576d20

See more details on using hashes here.

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