API reference
The objects on this page form the supported Upref v2 API. Start with
ConfigStore for persistence and use collect() only when the
application needs interactive input.
Public API summary
|
Read and persist one application's YAML configuration. |
|
Describe the presentation and conversion rules for one value. |
|
Specify the minimal interface for an interactive front end. |
|
Collect configuration values without mutating or persisting inputs. |
Base class for Upref's domain-specific exceptions. |
|
Indicate that a configuration name or path is invalid. |
|
Indicate data that cannot be represented by Upref's data model. |
|
Indicate that a configuration file could not be read. |
|
Indicate that a filesystem mutation could not be completed safely. |
|
Indicate that a legacy configuration could not be migrated. |
|
Indicate that the user cancelled interactive value collection. |
|
Indicate that the requested interactive interface is unavailable. |
Configuration storage
- class upref.ConfigStore(app_name, *, filename='config.yaml', app_author=None, directory=None, roaming=False)
Read and persist one application’s YAML configuration.
A store is a lightweight, uncached handle to one configuration file. Its constructor resolves the path, including existing symbolic links, but does not read or write the configuration and creates no directory. Every
load()reads the file again, and everysave()validates and atomically replaces it.The optional
directoryoverride is intended for tests and portable applications. Without it, Upref selects the platform-specific user configuration directory.Every persisted value is readable plain-text YAML. A store provides file integrity, not encryption or credential-vault semantics.
Example
>>> store = ConfigStore("my-application") >>> store.save({"theme": "dark", "retries": 3}) >>> store.load()["theme"] 'dark'
- Parameters:
app_name (str)
filename (str)
app_author (str | None)
directory (str | PathLike[str] | None)
roaming (bool)
- __init__(app_name, *, filename='config.yaml', app_author=None, directory=None, roaming=False)
Resolve a store without reading or writing its configuration.
- Parameters:
app_name (str) – Portable application identifier used as the default configuration-directory name. It must be a safe, non-empty path component.
filename (str) – Configuration filename inside the selected directory. Directory separators, absolute paths, and unsafe Windows names are rejected.
app_author (str | None) – Optional publisher directory used by
platformdirs. When omitted, the application directory is not nested below a duplicate author directory.directory (str | PathLike[str] | None) – Absolute directory overriding the platform-specific location. The final file is
directory / filename.roaming (bool) – Whether Windows should prefer the roaming configuration directory. Other platforms pass this flag through to
platformdirs.
- Raises:
ConfigPathError – If an identifier, filename, author, or explicit directory is invalid or the resulting path escapes its base.
- Return type:
None
- property app_name: str
Return the validated application identifier used by the store.
- Returns:
The original validated
app_nameconstructor argument.
- property filename: str
Return the configuration filename without directory components.
- Returns:
The original validated
filenameconstructor argument.
- property path: Path
Return the resolved absolute configuration-file path.
- Returns:
The confined path determined during construction.
- exists()
Return whether the resolved path currently identifies a file.
- Returns:
Truefor a regular file, including a symbolic link resolved at construction, otherwiseFalse.- Raises:
ConfigReadError – If the operating system cannot inspect the path.
- Return type:
bool
- load(*, defaults=None)
Load a fresh configuration mapping.
Missing, whitespace-only, comment-only, and explicit YAML
nulldocuments behave like an empty mapping. When defaults are supplied, nested mappings are merged recursively and stored values override defaults. Lists and scalar values are replaced rather than combined.Neither
defaultsnor any mutable value inside it is shared with the returned configuration. Calling this method never writes defaults to disk.- Parameters:
defaults (Mapping[str, bool | int | float | str | None | list[bool | int | float | str | None | list[ConfigValue] | dict[str, ConfigValue]] | dict[str, bool | int | float | str | None | list[ConfigValue] | dict[str, ConfigValue]]] | None) – Optional fallback configuration applied in memory below the stored values.
- Returns:
A validated, detached dictionary containing the resolved values.
- Raises:
ConfigReadError – If the file exists but cannot be read.
ConfigFormatError – If YAML is malformed, is not UTF-8, has a non-mapping root, or contains unsupported values or key types.
- Return type:
dict[str, bool | int | float | str | None | list[bool | int | float | str | None | list[ConfigValue] | dict[str, ConfigValue]] | dict[str, bool | int | float | str | None | list[ConfigValue] | dict[str, ConfigValue]]]
- save(data)
Validate and atomically replace the stored configuration.
The complete document is serialized as safe UTF-8 YAML before any filesystem change. A temporary file in the destination directory is flushed and then installed with an atomic replacement. YAML comments, anchors, and custom formatting are not preserved.
Values are written as readable plain text. This method provides no encryption or secret-store semantics.
- Parameters:
data (Mapping[str, bool | int | float | str | None | list[bool | int | float | str | None | list[ConfigValue] | dict[str, ConfigValue]] | dict[str, bool | int | float | str | None | list[ConfigValue] | dict[str, ConfigValue]]]) – Complete mapping to persist. Only
ConfigValuevalues and string mapping keys are supported.- Raises:
ConfigFormatError – If
datacannot be represented by the Upref configuration model or serialized as safe YAML.ConfigWriteError – If a directory cannot be created or the atomic write cannot be completed.
- Return type:
None
- update(changes)
Recursively merge
changesinto stored data and save the result.Mappings merge recursively. Lists and scalars, including falsey values such as
False,0, andNone, replace existing values.This method is a convenience read/merge/write sequence, not a locked transaction. Atomic replacement prevents partial YAML files, but concurrent writers remain last-writer-wins. Changes are persisted as readable plain-text YAML.
- Parameters:
changes (Mapping[str, bool | int | float | str | None | list[bool | int | float | str | None | list[ConfigValue] | dict[str, ConfigValue]] | dict[str, bool | int | float | str | None | list[ConfigValue] | dict[str, ConfigValue]]]) – Partial configuration whose values take precedence over the currently stored mapping.
- Returns:
The detached merged configuration that was persisted.
- Raises:
ConfigReadError – If the existing file cannot be read.
ConfigFormatError – If existing or replacement data is invalid.
ConfigWriteError – If the merged document cannot be written.
- Return type:
dict[str, bool | int | float | str | None | list[bool | int | float | str | None | list[ConfigValue] | dict[str, ConfigValue]] | dict[str, bool | int | float | str | None | list[ConfigValue] | dict[str, ConfigValue]]]
- delete()
Delete only this store’s configuration file.
- Returns:
Truewhen a file was removed, orFalsewhen it was already absent. Parent directories are never removed.- Raises:
ConfigWriteError – If the path exists but cannot be deleted.
- Return type:
bool
- import_legacy(name, *, overwrite=False, legacy_directory=None)
Import one historical v1
.conffile into this store.Mappings recognized by v1’s structural descriptor heuristic are converted by extracting
valueentries; other mappings retain their shape. A raw mapping that resembles descriptors can be ambiguous. The source file is never modified or deleted, and using it as the target is rejected.Migrated values, including historical password fields, are persisted as readable plain-text YAML.
Target existence is checked before saving but is not protected by a lock. Concurrent migration writers must coordinate externally.
- Parameters:
name (str) – Legacy preference name without the
.confsuffix.overwrite (bool) – Permit replacement when the v2 target already exists.
legacy_directory (str | PathLike[str] | None) – Absolute override for the legacy source directory, primarily for tests and controlled migrations.
- Returns:
A detached dictionary containing the imported raw values.
- Raises:
ConfigPathError – If the legacy name or directory is unsafe.
ConfigReadError – If the legacy source cannot be read or the target path cannot be inspected.
MigrationError – If source and target are the same file, the source is missing, the target already exists without
overwrite, or persistence fails.ConfigFormatError – If the legacy YAML cannot be parsed or contains unsupported configuration data.
- Return type:
dict[str, bool | int | float | str | None | list[bool | int | float | str | None | list[ConfigValue] | dict[str, ConfigValue]] | dict[str, bool | int | float | str | None | list[ConfigValue] | dict[str, ConfigValue]]]
Configuration types
- upref.Config
A configuration mapping with string keys and
ConfigValuevalues.
- upref.ConfigValue
A recursive type alias accepting
None,bool,int,float,str, lists of supported values, and dictionaries with string keys.
- prompt.PromptMode
The collection mode literal:
"missing"or"all".
Interactive schema and protocol
- class upref.Field(label, description='', required=True, secret=False, parser=<class 'str'>, validator=None)
Describe the presentation and conversion rules for one value.
Fields are immutable and can therefore be reused safely across collection calls. The parser receives the raw string returned by the prompter. The validator then receives the parsed value and may either return
Falseor raiseValueErrorto reject it. A return value ofTrueorNoneaccepts the value.secretdoes not make a value safe to persist. It asks a compatible prompter to mask input and suppress the current value; custom prompters are responsible for honoring that hint. Parsed secret values remain plaintext.- Parameters:
label (str)
description (str)
required (bool)
secret (bool)
parser (Callable[[str], bool | int | float | str | None | list[bool | int | float | str | None | list[ConfigValue] | dict[str, ConfigValue]] | dict[str, bool | int | float | str | None | list[ConfigValue] | dict[str, ConfigValue]]])
validator (Callable[[bool | int | float | str | None | list[bool | int | float | str | None | list[ConfigValue] | dict[str, ConfigValue]] | dict[str, bool | int | float | str | None | list[ConfigValue] | dict[str, ConfigValue]]], bool | None] | None)
- label
Human-readable field name. Bundled prompters fall back to the schema key when this string is empty.
- Type:
str
- description
Optional explanatory text displayed before entry.
- Type:
str
- required
Whether an empty raw answer is rejected. In
"missing"mode, this also makes an existing empty string count as missing.- Type:
bool
- secret
Whether compatible prompters should mask entry and hide the current value. This is a display-only safeguard, not encryption.
- Type:
bool
- parser
Callable converting raw input to a supported configuration value. Raising
ValueErrordisplays the error and retries; secret fields use a generic message instead of the exception text.- Type:
collections.abc.Callable[[str], bool | int | float | str | None | list[bool | int | float | str | None | list[ConfigValue] | dict[str, ConfigValue]] | dict[str, bool | int | float | str | None | list[ConfigValue] | dict[str, ConfigValue]]]
- validator
Optional callable checking the parsed value. Returning
Falseor raisingValueErrorrejects the value and retries the prompt.- Type:
collections.abc.Callable[[bool | int | float | str | None | list[bool | int | float | str | None | list[ConfigValue] | dict[str, ConfigValue]] | dict[str, bool | int | float | str | None | list[ConfigValue] | dict[str, ConfigValue]]], bool | None] | None
- class upref.Prompter(*args, **kwargs)
Specify the minimal interface for an interactive front end.
Runtime-checkable implementations supply raw input and display recoverable parsing or validation errors. Returning
Nonefromask()signals cancellation; an empty string is an entered value and is handled according toField.required.A custom implementation may expose a
close()method, butcollect()does not call it because the caller owns custom prompters.- ask(name, field, current)
Request one raw value from the user.
- Parameters:
name (str) – Configuration key being collected.
field (Field) – Presentation and conversion metadata for the key.
current (bool | int | float | str | None | list[ConfigValue] | dict[str, ConfigValue]) – Existing value, or
Nonewhen no value is available. Implementations should not reveal it whenfield.secretis true.
- Returns:
The unparsed text entered by the user, including an empty string, or
Noneto signal cancellation.- Return type:
str | None
- show_error(message)
Display a recoverable conversion or validation error.
- Parameters:
message (str) – Human-readable explanation of why the value was rejected.
- Returns:
None.
- Return type:
None
- upref.collect(schema, initial=None, interface='tty', mode='missing')
Collect configuration values without mutating or persisting inputs.
In
"missing"mode, a field is requested when its key is absent, its value isNone, or a required string is empty. Values such asFalseand0are therefore already populated."all"requests every field. Keys ininitialthat are not part ofschemaare preserved. Schema iteration order determines prompt order.initialis normalized into a detached tree before collection, and parser results are normalized before insertion. Consequently, neither the input mapping nor nested mutable objects returned by a parser are shared with the result. Collection has no persistence side effects: callers must explicitly pass the returned mapping to a store.String selectors create bundled prompters lazily. If a created prompter exposes
close(), it is called in afinallyblock, including after cancellation or another error. A caller-provided prompter is never closed by this function. If no fields require input, a selected bundled interface is not instantiated.- Parameters:
schema (Mapping[str, Field]) – Mapping from configuration keys to immutable field definitions.
initial (Mapping[str, bool | int | float | str | None | list[bool | int | float | str | None | list[ConfigValue] | dict[str, ConfigValue]] | dict[str, bool | int | float | str | None | list[ConfigValue] | dict[str, ConfigValue]]] | None) – Existing configuration values. Unsupported values, non-string keys, and cyclic containers are rejected. Defaults to an empty mapping.
interface (str | Prompter) –
"tty"for dependency-free terminal input,"gui"for optional wxPython dialogs, or a caller-ownedPrompter.mode (Literal['missing', 'all']) –
"missing"to request only absent or incomplete fields, or"all"to request every schema field.
- Returns:
A new, detached configuration dictionary containing preserved initial keys and all successfully collected values.
- Raises:
TypeError – If schema keys are not strings, schema values are not
Fieldinstances, or a non-stringinterfacedoes not implementPrompter.ValueError – If
modeor a stringinterfaceselector is invalid.PromptCancelled – If the user cancels any requested field. Partial results are discarded and
initialremains unchanged.upref.errors.ConfigFormatError – If
initialor a parser result lies outside the supported configuration data model.upref.errors.PromptUnavailableError – If the GUI interface is requested but wxPython cannot be imported or initialized.
Exception – Unexpected exceptions from a prompter, parser, validator, or owned prompter’s
close()method propagate. A close failure during exception handling can replace the original exception.
- Return type:
dict[str, bool | int | float | str | None | list[bool | int | float | str | None | list[ConfigValue] | dict[str, ConfigValue]] | dict[str, bool | int | float | str | None | list[ConfigValue] | dict[str, ConfigValue]]]
Built-in interfaces
- class upref.tty.TTYPrompter(input_func=None, getpass_func=None, print_func=None)
Ask for fields using standard terminal input.
The three functions are injectable so applications and tests can control input and presentation without replacing process-wide builtins. This prompter owns no external resource and requires no explicit close step.
- Parameters:
input_func (ReadFunction | None)
getpass_func (ReadFunction | None)
print_func (PrintFunction | None)
- __init__(input_func=None, getpass_func=None, print_func=None)
Initialize a terminal prompter.
- Parameters:
input_func (Callable[[str], str] | None) – Reader for ordinary fields. It receives the prompt text and returns one raw line. Defaults to
input().getpass_func (Callable[[str], str] | None) – Reader for secret fields. It receives the prompt text and should avoid echoing entered characters. Defaults to
getpass.getpass().print_func (Callable[[str], object] | None) – Function used to display labels, descriptions, current values, and errors. Defaults to
print().
- Returns:
None.
- Return type:
None
- ask(name, field, current)
Display field context and read one raw line.
The field label and optional description are displayed first. A non-
Nonecurrent value is also displayed for ordinary fields. Secret fields instead use the injected password reader and never show the current value. The returned secret is nevertheless plaintext.- Parameters:
name (str) – Configuration key, used as the label when
field.labelis empty.field (Field) – Presentation metadata, including whether entry is secret.
current (bool | int | float | str | None | list[ConfigValue] | dict[str, ConfigValue]) – Existing value to display for an ordinary field, or
Nonewhen unavailable.
- Returns:
The raw line returned by the selected reader, including an empty string, or
Nonewhen reading raisesEOFErrororKeyboardInterrupt.- Raises:
Exception – Display-function errors and reader errors other than
EOFErrorandKeyboardInterruptpropagate. The two cancellation exceptions are converted only when the selected reader raises them.- Return type:
str | None
- show_error(message)
Display a conversion or validation error with a clear prefix.
- Parameters:
message (str) – Human-readable error supplied by the collection layer.
- Returns:
None.
- Raises:
Exception – Any exception from the injected output function propagates unchanged.
- Return type:
None
- class upref.gui.GuiPrompter(title='Preferences', parent=None)
Collect one value at a time with wxPython modal dialogs.
Instances can be closed explicitly or used as context managers. Closing is idempotent and destroys a
wx.Apponly when this instance created it. An application that already existed at construction remains owned by its original caller. Once closed, the prompter cannot display new dialogs.- Parameters:
title (str)
parent (Any)
- __init__(title='Preferences', parent=None)
Initialize wxPython and prepare a modal-dialog prompter.
- Parameters:
title (str) – Window title used by text-entry and error dialogs.
parent (Any) – Optional wx window that owns the dialogs.
Nonecreates top-level dialogs according to wxPython’s normal behavior.
- Returns:
None.
- Raises:
PromptUnavailableError – If wxPython is not installed or a wx application cannot be initialized.
- Return type:
None
- ask(name, field, current)
Show a modal text-entry dialog for one field.
Ordinary fields are prefilled with a non-
Nonecurrent value. Secret fields use wxPython’s password style and deliberately start empty, so the current secret is neither displayed nor copied into the widget. Any entered secret is still returned as plaintext. The dialog is destroyed after confirmation or cancellation and when displaying or reading the constructed dialog raises an error.- Parameters:
name (str) – Configuration key, used as the label when
field.labelis empty.field (Field) – Presentation metadata, including description and secret handling.
current (ConfigValue) – Existing value used as the default for an ordinary field, or
Nonewhen unavailable.
- Returns:
The raw dialog text when the user confirms, including an empty string, or
Nonewhen the dialog is cancelled or dismissed.- Raises:
RuntimeError – If the prompter has already been closed.
Exception – Errors raised by wxPython while constructing, showing, reading, or destroying the dialog propagate unchanged.
- Return type:
str | None
- show_error(message)
Display a modal conversion or validation error.
- Parameters:
message (str) – Human-readable error supplied by the collection layer.
- Returns:
None.
- Raises:
RuntimeError – If the prompter has already been closed.
Exception – Errors raised by
wx.MessageBoxpropagate unchanged.
- Return type:
None
- close()
Release GUI resources owned by this prompter.
Closing is idempotent. If construction created a private
wx.App, itsDestroymethod is called when available. An application that predated this prompter is never destroyed.- Returns:
None.
- Raises:
Exception – An error raised by the owned
wx.App.Destroymethod propagates. The prompter remains marked as closed.- Return type:
None
- __enter__()
Enter a context manager without changing GUI ownership.
Entering does not initialize another application or reopen a prompter that was already closed.
- Returns:
This prompter instance.
- Return type:
- __exit__(*exc_info)
Close the prompter when leaving a context manager.
- Parameters:
*exc_info (object) – Exception details supplied by the context-management protocol. They are not inspected or suppressed.
- Returns:
None. An exception from the managed block normally propagates.
- Raises:
Exception – An exception from
close()propagates and can replace an active exception from the managed block.- Return type:
None
Exceptions
- exception upref.UprefError
Bases:
ExceptionBase class for Upref’s domain-specific exceptions.
Unexpected programming errors and third-party exceptions outside Upref’s documented boundaries are not necessarily wrapped in this hierarchy.
- exception upref.ConfigPathError
Bases:
UprefError,ValueErrorIndicate that a configuration name or path is invalid.
This exception also subclasses
ValueError, allowing callers that treat invalid path arguments as ordinary value errors to catch either API.
- exception upref.ConfigFormatError
Bases:
UprefErrorIndicate data that cannot be represented by Upref’s data model.
Typical causes include malformed YAML, a non-mapping document root, non-string keys, unsupported Python values, and recursive container cycles.
- exception upref.ConfigReadError
Bases:
UprefErrorIndicate that a configuration file could not be read.
Format and encoding failures use
ConfigFormatError; this exception represents path conversion and filesystem read failures.
- exception upref.ConfigWriteError
Bases:
UprefErrorIndicate that a filesystem mutation could not be completed safely.
The exception covers atomic saves and deletions. Invalid configuration data is reported separately through
ConfigFormatError.
- exception upref.MigrationError
Bases:
UprefErrorIndicate that a legacy configuration could not be migrated.
The original legacy file is intended to remain untouched when this error is raised.
- exception upref.PromptCancelled
Bases:
UprefErrorIndicate that the user cancelled interactive value collection.
Cancellation is an expected control-flow outcome and does not persist a partially collected configuration.
Bases:
UprefErrorIndicate that the requested interactive interface is unavailable.
This commonly means an optional GUI dependency is not installed or cannot be initialized in the current environment.
Package metadata
upref.__version__ contains the installed distribution version.
upref.__author__, upref.__license__, and upref.__copyright__
describe the package’s authorship and license.