Examples
The 26 programs in examples are complete and runnable from a checkout with
the project installed in .venv. Ten user-preference scenarios and nine
fundamental recipes use memory or automatically cleaned temporary directories.
The seven persistent examples below use distinct per-user configuration
locations; migration reads an existing historical file and creates a separate
v2 file.
Run an example on Windows with:
.\.venv\Scripts\python.exe examples\basic_store.py
User preference scenarios
Start with User preference scenarios for everyday application behavior. All ten scenarios use temporary files, and only the first two require terminal input.
Script |
User scenario |
|---|---|
|
Initial setup and reuse on the next startup. |
|
Edit a draft, then confirm, discard, or cancel. |
|
Remove one override, a section, or the whole configuration. |
|
Keep a bounded history of recently opened documents. |
|
Restore visible window geometry after changing displays. |
|
Keep separate preferences for accounts within one OS user. |
|
Override preferences through CLI options with opt-in persistence. |
|
Transfer validated portable preferences without local history. |
|
Restore a previous preference snapshot. |
|
Preview legacy conversion and preserve ambiguous raw data. |
Progressive recipes
These examples are explained with full source in Practical recipes. Use the same
command above with the desired filename. On Linux or macOS, use
./.venv/bin/python examples/portable_store.py.
Level |
Script |
Input and file effects |
|---|---|---|
Simple |
|
No input; temporary store with defaults and updates. |
Simple |
|
Terminal; boolean and optional text, no file. |
Intermediate |
|
Terminal; retain values or edit JSON, no file. |
Intermediate |
|
GUI extra required; formatted prefill, no file. |
Intermediate |
|
No input; malformed YAML in a temporary directory. |
Advanced |
|
Terminal; edit a subsection and save a temporary file. |
Advanced |
|
Scripted answers with retries, no file. |
Advanced |
|
No input; dataclass validation and a temporary store. |
Advanced |
|
No input; idempotent transformation of a temporary file. |
The remaining examples below progress from simple storage to project setup, environment profiles, and migration. Persistent examples print their paths.
Basic storage
This example saves a mapping, loads a fresh copy, and prints the exact path. Running it again replaces the same example configuration.
1"""Save and reload a small per-user configuration."""
2
3from upref import ConfigStore
4
5store = ConfigStore("upref-basic-example")
6
7store.save(
8 {
9 "theme": "dark",
10 "notifications": True,
11 }
12)
13
14config = store.load()
15print(config)
16print(f"Configuration saved to: {store.path}")
Defaults, update, and delete
Defaults are merged recursively in memory. The resolved mapping is then saved, selected changes are merged and persisted, and the example removes its file.
1"""Combine defaults, persist selected changes, then clean up the example."""
2
3from upref import Config, ConfigStore
4
5store = ConfigStore("upref-update-example")
6
7defaults: Config = {
8 "network": {
9 "host": "localhost",
10 "port": 8080,
11 },
12 "notifications": True,
13}
14
15config = store.load(defaults=defaults)
16print("With defaults:", config)
17
18# Loading defaults does not write them. Save the resolved configuration when
19# the application wants those values to become persistent.
20store.save(config)
21
22updated = store.update(
23 {
24 "network": {"port": 9000},
25 "notifications": False,
26 }
27)
28print("Saved changes:", updated)
29
30print("Configuration removed:", store.delete())
Required project variables
This terminal-based setup asks only for project variables that have not been
saved yet: directories, the expected Python version, and whether tests should
run before a build. Boolean input accepts common forms such as yes, no,
on, and off.
1"""Collect and persist the variables required to work on one project."""
2
3from upref import ConfigStore, ConfigValue, Field, PromptCancelled, collect, parse_bool
4
5
6def is_non_empty_string(value: ConfigValue) -> bool:
7 """Return whether a parsed configuration value is a non-empty string."""
8 return isinstance(value, str) and bool(value.strip())
9
10
11store = ConfigStore("upref-project-example", filename="variables.yaml")
12schema = {
13 "project_name": Field(
14 "Project name",
15 description="A human-readable name, for example customer-portal",
16 validator=is_non_empty_string,
17 ),
18 "source_directory": Field(
19 "Source directory",
20 description="Path relative to the project root, for example src",
21 validator=is_non_empty_string,
22 ),
23 "build_directory": Field(
24 "Build directory",
25 description="Path relative to the project root, for example dist",
26 validator=is_non_empty_string,
27 ),
28 "python_version": Field(
29 "Python version",
30 description="Version expected by the project, for example 3.12",
31 validator=is_non_empty_string,
32 ),
33 "run_tests_before_build": Field(
34 "Run tests before building",
35 parser=parse_bool,
36 ),
37}
38
39try:
40 variables = collect(
41 schema,
42 initial=store.load(),
43 interface="tty",
44 mode="missing",
45 )
46except PromptCancelled:
47 print("Setup cancelled; the existing project variables were not changed.")
48else:
49 store.save(variables)
50 print("Project variables:")
51 for variable_name in schema:
52 print(f" {variable_name}: {variables[variable_name]}")
53 print(f"Saved to: {store.path}")
Environment profiles and runtime overrides
This example stores development, testing, and production profiles. The
UPREF_PROFILE, UPREF_SERVICE_URL, and UPREF_TIMEOUT environment
variables can temporarily override the selected values without modifying the
saved YAML file.
For example, in PowerShell:
$env:UPREF_PROFILE = "production"
$env:UPREF_TIMEOUT = "45"
.\.venv\Scripts\python.exe examples\environment_profiles.py
1"""Select a saved environment profile and apply temporary runtime overrides."""
2
3import os
4
5from upref import Config, ConfigStore, ConfigValue
6
7
8def require_mapping(value: ConfigValue, *, name: str) -> Config:
9 """Return a configuration mapping or report an invalid saved shape."""
10 if not isinstance(value, dict):
11 raise TypeError(f"{name} must be a mapping")
12 return value
13
14
15def require_string(value: ConfigValue, *, name: str) -> str:
16 """Return a string setting or report an invalid saved type."""
17 if not isinstance(value, str):
18 raise TypeError(f"{name} must be a string")
19 return value
20
21
22def require_integer(value: ConfigValue, *, name: str) -> int:
23 """Return an integer setting while rejecting booleans."""
24 if isinstance(value, bool) or not isinstance(value, int):
25 raise TypeError(f"{name} must be an integer")
26 return value
27
28
29def require_boolean(value: ConfigValue, *, name: str) -> bool:
30 """Return a boolean setting or report an invalid saved type."""
31 if not isinstance(value, bool):
32 raise TypeError(f"{name} must be a boolean")
33 return value
34
35
36DEFAULTS: Config = {
37 "active_profile": "development",
38 "profiles": {
39 "development": {
40 "service_url": "http://localhost:8000",
41 "timeout": 10,
42 "debug": True,
43 },
44 "testing": {
45 "service_url": "http://localhost:8001",
46 "timeout": 5,
47 "debug": False,
48 },
49 "production": {
50 "service_url": "https://api.example.org",
51 "timeout": 30,
52 "debug": False,
53 },
54 },
55}
56
57store = ConfigStore("upref-profiles-example")
58settings = store.load(defaults=DEFAULTS)
59if not store.exists():
60 store.save(settings)
61
62profiles = require_mapping(settings["profiles"], name="profiles")
63saved_profile = require_string(settings["active_profile"], name="active_profile")
64profile_name = os.environ.get("UPREF_PROFILE", saved_profile)
65if profile_name not in profiles:
66 choices = ", ".join(sorted(profiles))
67 raise ValueError(f"Unknown profile {profile_name!r}; choose one of: {choices}")
68
69profile = require_mapping(profiles[profile_name], name=f"profiles.{profile_name}")
70saved_service_url = require_string(profile["service_url"], name="service_url")
71service_url = os.environ.get("UPREF_SERVICE_URL", saved_service_url)
72
73saved_timeout = require_integer(profile["timeout"], name="timeout")
74timeout_text = os.environ.get("UPREF_TIMEOUT")
75timeout = saved_timeout if timeout_text is None else int(timeout_text)
76if timeout <= 0:
77 raise ValueError("UPREF_TIMEOUT must be greater than zero")
78debug = require_boolean(profile["debug"], name="debug")
79
80print(f"Profile: {profile_name}")
81print(f"Service URL: {service_url}")
82print(f"Timeout: {timeout} seconds")
83print(f"Debug logging: {debug}")
84print(f"Persistent configuration: {store.path}")
85print("Environment-variable overrides were not persisted.")
Multiple projects
Related projects can share one application configuration directory while using a distinct YAML filename for each project. This is useful for a workspace containing a frontend, backend, worker, or other independently configured component.
1"""Keep independent per-user settings for several related projects."""
2
3from upref import Config, ConfigStore
4
5PROJECT_DEFAULTS: dict[str, Config] = {
6 "frontend": {
7 "working_directory": "apps/frontend",
8 "test_command": "npm test",
9 "build_command": "npm run build",
10 "enabled": True,
11 },
12 "backend": {
13 "working_directory": "apps/backend",
14 "test_command": "python -m pytest",
15 "build_command": "python -m build",
16 "enabled": True,
17 },
18}
19
20for project_name, defaults in PROJECT_DEFAULTS.items():
21 # Each project receives a separate file below the same application-specific
22 # per-user configuration directory.
23 store = ConfigStore(
24 "upref-workspace-example",
25 filename=f"{project_name}.yaml",
26 )
27 settings = store.load(defaults=defaults)
28 if not store.exists():
29 store.save(settings)
30
31 print(f"{project_name}: {settings}")
32 print(f" file: {store.path}")
Terminal collection
On the first run, missing values are requested in the terminal. Later runs
reuse saved values. Cancelling before save leaves the existing file
unchanged.
1"""Ask only for missing values in a terminal, then save them explicitly."""
2
3from upref import ConfigStore, Field, PromptCancelled, collect
4
5store = ConfigStore("upref-tty-example")
6schema = {
7 "service_url": Field(
8 "Service URL",
9 description="For example: https://api.example.org",
10 ),
11 "timeout": Field(
12 "Timeout in seconds",
13 parser=int,
14 validator=lambda value: isinstance(value, int) and value > 0,
15 ),
16}
17
18try:
19 values = collect(
20 schema,
21 initial=store.load(),
22 interface="tty",
23 mode="missing",
24 )
25except PromptCancelled:
26 print("Input cancelled; the configuration was not changed.")
27else:
28 store.save(values)
29 print(f"Configuration saved to: {store.path}")
Migrating from v1
This program imports an existing historical file. Change LEGACY_NAME to
the old configuration name. Migration leaves its source untouched and checks
for an existing v2 target before saving. Concurrent migrations require an
external lock.
1"""Import values from an existing Upref v1 configuration."""
2
3from upref import ConfigStore, UprefError
4
5LEGACY_NAME = "my_personnal_data"
6store = ConfigStore("upref-migration-example")
7
8try:
9 preview = store.import_legacy(LEGACY_NAME, source_format="auto", dry_run=True)
10 print("Proposed keys:", ", ".join(sorted(preview)))
11 # Use source_format="raw" when keys such as "value" are application data.
12 # The import below reads the source again and repeats the target checks.
13 migrated = store.import_legacy(LEGACY_NAME)
14except UprefError as error:
15 print(f"Migration was not performed: {error}")
16else:
17 print("Migrated keys:", ", ".join(sorted(migrated)))
18 print(f"New configuration: {store.path}")
Warning
Upref configuration files contain plain YAML. Masked interactive input is not encrypted storage; use a system keyring or secrets manager for passwords and tokens.