Plugin API¶
The contracts for backend and source authors: the vocabulary shared by the extension points of both packages. It covers the contracts themselves, the base classes that fill them with defaults, the bindings that declare a plugin, and the registration that opens a family's seats for plugins.
The serializer side¶
advexp.settings.plugin_api.ISettingsSerializer
¶
Mandatory storage operations.
The four operations every storage backend provides — load, save,
delete, contains — plus synchronize at the end of a pass. A
backend implementing this contract alone is valid: the pipeline
asks for nothing more, and the optional parts — the name wishes,
the context injection, the notification bracket — live in their own
contracts, which SettingsBaseSerializer fills with defaults.
The attr argument of the operations is the setting's attribute
instance, or None for a dynamic-settings entry: dynamic settings
have no attribute instance, only the plugin's pinned
attribute_class. Backends read their own parameters off the attribute
through
:meth:~advexp.settings_base.settings_base_serializer.SettingsBaseSerializer.attribute_of.
load(setting_name, attr)
abstractmethod
¶
Return the stored value for the setting, or :data:NOT_FOUND.
save(setting_name, attr, value)
abstractmethod
¶
Store the value; None is a legitimate value.
A storage that can represent it (a document storage, say) keeps it
as its null form; a string-only storage that has none removes the
entry instead — for such a storage None and absence coincide.
Removing a setting outright is :meth:delete's job.
value is the caller's current object, handed over by reference.
Backends that keep it in memory (rather than serializing right away)
must copy mutable values themselves — the class storage must not share
mutable state with the backend's.
delete(setting_name, attr)
abstractmethod
¶
Remove the setting from storage (missing setting is not an error).
contains(setting_name, attr)
abstractmethod
¶
Whether the storage currently holds a value for the setting.
synchronize()
abstractmethod
¶
Flush pending changes.
Runs after the save/delete member loop and before the end
notifications — inside the on_start/end_serializer_action bracket.
Conditions that must reach the user follow the storage operations'
rule: wrap the original error in
:class:~advexp.settings_base.user_facing_error.UserFacingError.
advexp.settings.plugin_api.ISettingsSerializerWishes
¶
Optional wishes: storage-name formatting and context injection.
delimiter()
abstractmethod
¶
Name-part separator wish: '.' or '_'.
Any other value is replaced with '.' and reported through a
library log warning (once per serializer class).
remove_inappropriate_symbols(text)
abstractmethod
¶
Sanitize a name fragment before it is used in storage names.
set_serializer_params(context)
abstractmethod
¶
Called once after construction with the settings context.
advexp.settings.plugin_api.ISerializerNotificationSink
¶
Pass notifications.
A separate contract that
:class:~advexp.settings_base.settings_base_serializer.SettingsBaseSerializer
fills with no-op defaults.
contains_setting fires the same notification bracket as the other
passes.
on_start_serializer_action()
abstractmethod
¶
Notification: a load/save/delete pass is about to use this serializer.
on_end_serializer_action()
abstractmethod
¶
Notification: the pass using this serializer has finished.
on_start_load_settings()
abstractmethod
¶
Notification: start of a load pass.
on_end_load_settings()
abstractmethod
¶
Notification: end of a load pass.
on_start_save_settings()
abstractmethod
¶
Notification: start of a save pass.
on_end_save_settings()
abstractmethod
¶
Notification: end of a save pass.
on_start_delete_settings()
abstractmethod
¶
Notification: start of a delete pass.
on_end_delete_settings()
abstractmethod
¶
Notification: end of a delete pass.
on_start_contains_settings()
abstractmethod
¶
Notification: start of a contains probe.
on_end_contains_settings()
abstractmethod
¶
Notification: end of a contains probe.
advexp.settings.plugin_api.ISettingNameDescriber
¶
Spell the storage address of a setting for messages that a human reads.
This is a serializer capability that the load diagnostics quote. By
default, a pass names a setting by its pattern-built storage name
(namespace.Class.member, or the explicit name= of the member). The
storage that a backend reads may address the setting differently, however.
For example, the environment family builds each variable name from the
naming options of the class, so the pattern name is a string that the
storage never sees.
A backend of that kind spells its own address instead. The word for the
member is the own term of the distribution (here, member). The
designation of the source of a file backend is added next to the address,
and it names the file that the address alone cannot name:
member 'host' (environment variable 'DEMO_APP_HOST') of 'AppConfig'
member 'port' (key 'port' in the TOML file /etc/app.toml) of ...
:class:~advexp.settings_base.settings_base_serializer.SettingsBaseSerializer
implements this interface with a None default, which means the
pattern-built name. A backend that derives from it overrides only this one
method. The interface itself is the contract for a backend that is built on
:class:~advexp.settings_base.settings_serializer_interface.ISettingsSerializer
alone, and it is probed through isinstance.
describe_setting_name(setting_name, attr)
abstractmethod
¶
Return the storage address of the setting, in the own spelling of this backend.
A pass calls this method when it builds a message about the setting, for
example the abort of a required member, or a fallback warning.
setting_name is the pattern-built storage name (the explicit name=
if the member declares one). attr is the attribute of the member, or
None for a dynamic-settings entry. These are the inputs from which a
backend spells its address: name=, the bound member name, and the class
metadata through the context.
The result is a short phrase that names the entry of the storage. It is the
address alone (environment variable 'APP_MODE', key 'port'). The
caller adds the designation of the source next to it if that designation
adds the file, so the message names the exact file only once. None says
that the backend has no spelling of its own, and the caller prints
setting_name instead.
advexp.settings.plugin_api.ISourceDescriber
¶
Name the backend itself, for messages that a human reads.
This is a serializer capability that the load diagnostics quote in their
causes. Instead of the faceless its source provided no value, the
reader learns which storage came up empty: the process environment
provided no value, the JSON file raised ....
Together with
:class:~advexp.settings_base.setting_name_describer_interface.ISettingNameDescriber,
this is the side of the backend in the diagnostics. That interface spells
the storage address of the setting, and this one names the storage that
holds it.
The designation reports only the facts: the phrase for the storage and its exact file. The caller lays them out. Next to an address that already names the file, the phrase stands alone, so a message never reads the path twice.
:class:~advexp.settings_base.settings_base_serializer.SettingsBaseSerializer
implements this interface with a None default, which means the unnamed
spelling its source. A backend that derives from it overrides this one
method. The interface itself is the contract for a backend that is built on
:class:~advexp.settings_base.settings_serializer_interface.ISettingsSerializer
alone, and it is probed through isinstance.
describe_source(attr)
abstractmethod
¶
Return the own designation of the backend, which the load causes quote.
attr is the attribute of the member. A backend whose place depends on
the class (for example, a file binding) resolves it through the context. It
is None for an entry of dynamic settings.
The result is a pair:
- the phrase for the storage: a short noun phrase with its article (
the process environment,the JSON file); - its exact file, as the full path on disk (a relative binding addresses a
place in the working directory, and the message must name the file
itself). The path is
Nonefor a storage that is not a file.
The caller composes the two. Next to the address of a setting that already
names the file, the phrase stands alone. When there is no address (attr
is None, a dynamic entry), the phrase carries the path. If the whole
result is None, the backend has no designation, and the caller says
its source instead.
The naming is a best effort of the caller: a describe call that itself breaks degrades to the unnamed spelling, instead of disturbing the load that it reports on.
advexp.settings.plugin_api.SettingsBaseSerializer()
¶
Base class for storage backends: the class from which plugin serializers derive.
Everything except the four storage operations has a default:
synchronizeand the notifications are no-ops.- The delimiter wish is
'.'. - Name fragments pass through unsanitized.
- The name of a setting in messages keeps the pattern-built spelling.
- The delivered context is kept for the
contextproperty.
The base declares every optional interface, so the pipeline always delivers parameters, wishes, name spellings, and notifications to subclasses. It is enough to override only the ones that a backend cares about.
The pipeline creates instances per serializer class, and an instance may be
reused across the settings of one pass. The context of the pass arrives
through set_serializer_params and is read back through the context
property.
The value-form helpers to_json_value, to_toml_value, and
to_storage_string render a value into the storage forms that the
round-trip types of the library support. Override them to extend the set of
storable types. The narrowing helper attribute_of turns the attr of
the interface methods into the own attribute class of the backend, for
reading backend-specific parameters.
context
property
¶
The read side of the context injection of ISettingsSerializerWishes:
the settings context of the pass that is being run.
The pipeline delivers the context through :meth:set_serializer_params
before the first storage call. Reading it on an instance outside a pass
(before any delivery) is an error. The value-form helpers
(to_json_value, to_storage_string) and the name wishes do not need
the context, so an instance that is constructed directly stays usable for
them. This property mirrors the context property of the plugin side.
load(setting_name, attr)
abstractmethod
¶
ISettingsSerializer storage operation: return the stored
value for setting_name, or :data:NOT_FOUND.
save(setting_name, attr, value)
abstractmethod
¶
ISettingsSerializer storage operation: store value. This includes
None, which is stored in the null form of the storage if it has one (a
storage without a null form removes the entry).
delete(setting_name, attr)
abstractmethod
¶
ISettingsSerializer storage operation: remove the setting
from storage (a missing setting is not an error).
contains(setting_name, attr)
abstractmethod
¶
ISettingsSerializer storage operation: whether the storage
currently holds a value for the setting.
synchronize()
¶
ISettingsSerializer storage operation: flush pending
changes. The default is nothing to flush.
describe_setting_name(setting_name, attr)
¶
Default: None. Messages name the setting by its pattern-built storage
name.
Override this method when the storage addresses the setting differently from the pattern (for example, the environment family builds each variable name from the naming options of the class). The returned phrase is the address alone: the caller adds the designation of the source next to it, so the message names the exact file only once.
describe_source(attr)
¶
Default: None. The load diagnostics say just its source.
Override this method to name the backend in them. The designation that it
returns replaces its source in the causes, for example the process
environment provided no value, or the JSON file /etc/app.json raised
....
attribute_of(attr, attribute_class)
staticmethod
¶
The own helper of the base: return attr narrowed to
attribute_class, or None if the narrowing does not apply.
The serializer contracts type attr as Optional[ISettingAttribute],
because dynamic entries call with attr=None. This helper is the only way
to turn attr into the typed view from which a backend reads its own
parameters (path= and similar ones). None covers the case in which
the view does not apply:
to_storage_string(value)
staticmethod
¶
The own helper of the base: render value for a string-oriented storage
(environment variables and similar storages).
Booleans are rendered as True/False, dates and times in ISO format,
enums by member name, and collections and dataclasses in JSON-encoded form.
The result round-trips with the conversion on the load side. Override the
method to support further types.
to_json_value(value)
staticmethod
¶
The own helper of the base: render value for a JSON document (the
storage form of a JSON backend).
Enums are rendered by member name, dates and times in ISO format, and collections and dataclasses in a JSON-friendly form. The result round-trips with the conversion on the load side. Override the method to support further types.
to_toml_value(value)
staticmethod
¶
The own helper of the base: render value for a TOML document (the
storage form of a TOML backend).
Enums are rendered by member name, timedeltas in their str() form,
collections and dataclasses as arrays and tables, and datetimes in native
form. None and text that is not UTF-8 raise an error, because TOML has
neither a null nor a string that is not UTF-8. The result round-trips with
the conversion on the load side. Override the method to support further
types.
to_yaml_value(value)
staticmethod
¶
The own helper of the base: render value for a YAML document (the
storage form of a YAML backend).
Enums are rendered by member name, timedeltas in their str() form,
collections and dataclasses as sequences and mappings, and datetimes and
dates in native form. None is kept as the null of the document. Text
that is not UTF-8 raises an error, because every YAML string is UTF-8. The
result round-trips with the conversion on the load side. Override the method
to support further types.
delimiter()
¶
ISettingsSerializerWishes wish: the separator the machinery
uses between the name parts of an automatic storage name. The
default is '.'.
remove_inappropriate_symbols(text)
¶
ISettingsSerializerWishes wish: sanitize a name fragment
before it is used in a storage name. The default leaves text
unchanged.
set_serializer_params(context)
¶
ISettingsSerializerWishes delivery: the pipeline calls this
once after construction with the settings context of the pass;
the base stores it, and context reads it back.
on_start_serializer_action()
¶
ISerializerNotificationSink hook: a load, save or delete pass
is about to use this serializer. The default is a no-op.
on_end_serializer_action()
¶
ISerializerNotificationSink hook: the pass using this
serializer has finished. The default is a no-op.
on_start_load_settings()
¶
ISerializerNotificationSink hook: a load pass starts. The
default is a no-op.
on_end_load_settings()
¶
ISerializerNotificationSink hook: a load pass ends. The
default is a no-op.
on_start_save_settings()
¶
ISerializerNotificationSink hook: a save pass starts. The
default is a no-op.
on_end_save_settings()
¶
ISerializerNotificationSink hook: a save pass ends. The
default is a no-op.
on_start_delete_settings()
¶
ISerializerNotificationSink hook: a delete pass starts. The
default is a no-op.
on_end_delete_settings()
¶
ISerializerNotificationSink hook: a delete pass ends. The
default is a no-op.
on_start_contains_settings()
¶
ISerializerNotificationSink hook: a contains probe starts.
The default is a no-op.
on_end_contains_settings()
¶
ISerializerNotificationSink hook: a contains probe ends. The
default is a no-op.
The plugin side¶
advexp.settings.plugin_api.ISettingsPlugin
¶
The plugin contract: a name and the context of the settings class.
A plugin bundles a storage backend behind this contract. It registers its
setting attributes and serializers (conventionally from a setup() class
method that is called at registration), and it exposes plugin-specific
operations that user code reaches through Settings.get_plugin(...).
The binding consists of two members. :meth:bind_context receives the
context of the settings class once, right after construction, and
context returns it afterward. Where the context is kept is up to the
implementation: the contract says nothing about it.
plugin_name
abstractmethod
property
¶
Human-readable plugin name.
context
abstractmethod
property
¶
The settings class context this plugin instance is bound to.
bind_context(context)
abstractmethod
¶
Receive the context of the settings class this instance serves.
Called once by Settings.get_plugin, right after construction;
an implementation stores the context where its context
property reads it.
advexp.settings.plugin_api.SettingsBasePlugin()
¶
Base class for settings plugins: the contract plus context binding.
Everything but plugin_name is ready: the bound context is kept in
the name-mangled __context attribute (a subclass's own same-named
attribute cannot clobber the context binding), and reaching for the
context before it is bound raises a clear error. A plugin declaring a
:attr:binding needs no setup() of its own — the base one
registers it.
binding = None
class-attribute
¶
The declarative attribute-to-serializer binding.
Registered by the base :meth:setup when the plugin is registered. A
plugin serving several attribute classes, or registering its pieces
differently, overrides setup() instead of declaring a binding;
None (the default) registers nothing. On a
dynamic-settings plugin base the binding is mandatory and its attribute
class is the collection's pin.
context
property
¶
The settings class context this plugin instance is bound to.
plugin_name
abstractmethod
property
¶
Human-readable plugin name.
setup()
classmethod
¶
Register the declared attribute-to-serializer pair, if there is one.
It runs when the plugin is registered (when its module is imported). If nothing is declared, there is nothing to register.
The settings context¶
advexp.settings.plugin_api.SettingsContext(settings_class)
¶
One instance per settings class, created by the settings metaclass.
Every storage holds the own entries of the class: the declarations of the class body, plus the runtime writes into this class. The declarations of a plain mixin are absorbed here too, because a mixin has no runtime channel of its own. The reading operations resolve the inheritance chain live, key by key, and the most derived entry wins.
The chain of a member ends at the class that declared it: the classes above it never held the member, and a redeclared member reads only the metadata of the redeclaration.
logger
property
¶
The logging channel of the distribution that drives the settings class.
There is one logger per distribution, not per settings class: the contexts of all classes under the same root declaration hold the same logger object.
get_family_options(family_key, declaring_class)
¶
Return the options entry of the family that governs a member of
declaring_class.
This is the resolution rule of the per-class entries of a family: the
nearest entry at or above the class that declared the member. It is the
own entry of the declaring class if that class has one, and otherwise the
entry of the closest ancestor. The lookup runs over the live storages of the
chain. An override that a reading subclass declares or retunes at runtime
therefore applies, and so does a runtime retune of the own entry of an
ancestor; the classes above the declaring class never see either of them.
The result is None if no class of the chain declared the options of the
family.
set_class_metadata(metadata_name, metadata_value)
¶
Store a class-level metadata entry on this class; None is a value.
A key with a registered validator is checked before it is stored. This is the same check that the declaration of a class body goes through, so data that the backends rely on cannot break after startup.
A flat key of a family stores the own entry of the class (the per-class
key). The entry of an ancestor is addressed by its per-class key, which the
helpers of the family build from the class itself. The addressee must be the
class itself or one of its bases, which is the same chain rule that the
class statement enforces.
The value is deep-copied, so the stored metadata never shares mutable state
with the object of the caller. The entry is stored in the own storage of
this class, and the readers of the chain (this class and its subclasses)
resolve it live, so the write reaches every subclass at once. Removing an
entry is the job of :meth:delete_class_metadata.
get_class_metadata(metadata_name, default=None)
¶
Read a class-level metadata entry; the nearest entry wins.
The rule is the same as for class attribute lookup: the own storage of this class first, then the storages of the bases, with the most derived entry for each key. Declarations and runtime writes are resolved alike, and live. A flat key of a family reads the nearest entry, which is the one that governs the own members of this class.
get_class_all_metadata()
¶
Return every class-level metadata entry, keyed by metadata name.
This is the enumeration that the single-key getter cannot give: the entries that are visible to the class (its own entries and those of the chain) in one dict of deep copies. Changing the snapshot never affects the stored metadata. The per-class entries of a family keep their distinct composite keys, so the snapshot of a subclass lists its own entry next to the entry of the base. A class with nothing stored anywhere in its chain returns an empty dict.
contains_class_metadata(metadata_name)
¶
Return whether the chain holds the name, counting the nearest own entry.
This is the unambiguous form of :meth:get_class_metadata. get returns
its default both for a missing entry and for a stored None, while
contains tells the two cases apart. It checks the whole chain, with the
same resolution that get applies.
delete_class_metadata(metadata_name)
¶
Remove the own class-level metadata entry of the class; a missing entry is not an error.
Only what this class itself declared or stored is removed. An entry of an ancestor stays visible to the readers of the chain, in the same way that deleting an attribute of a subclass makes the lookup fall back to the attribute of the base class. A flat key of a family removes the own entry of the family of the class. The method returns whether an own entry existed and was removed. An entry that was seeded by the declaration in the class body is removed in the same way: the declaration seeds the storage only once, and the entry does not come back.
delete_class_all_metadata()
¶
Remove every own class-level metadata entry of the class.
Only the own storage of the class is cleared. The entries of the ancestors stay visible to the readers of the chain: clearing a subclass does not clear its bases. The iteration order of a dynamic plugin and the entries that were seeded by the declaration in the class body are own entries, and they are removed with the rest.
anchored_file_path(anchor_key, configured)
¶
The absolute path that the class has anchored for one file configuration.
A file backend passes in the configured spelling (the binding of its class or the module default, resolved by its own chain) and receives the absolute path that the class anchors for it. The first access anchors the file, and every pass during the life of the class (reads, diagnostics, and writes) keeps using that file, even if the working directory changes.
Reconfiguration re-anchors the file: a spelling that differs from the
anchored one is anchored anew, against the working directory of that moment,
so a changed file_path takes effect on the next access. anchor_key
tells the anchors apart inside one class: a backend with two files anchors
each of them under its own key.
open_parameter_pass(initial=None)
¶
Open the scope of one pass for resolved path-parameter values.
The load pass opens a scope before it walks the members and closes it afterwards. Every resolution that the pass makes, whichever link of the chain it serves, therefore shares one reading of each variable.
Scopes nest: a pass that is opened inside another one resolves its own
values, because the values of the outer pass belong to the outer pass.
initial seeds the scope with values that the pass must not read again.
The write directions pass the remembered values of the last load here, so a
save expands its template with the environment that the class actually
loaded from.
close_parameter_pass()
¶
Close the innermost open scope of resolved path-parameter values.
remembered_parameter_values()
¶
The path-parameter values that the passes have resolved so far.
The values are keyed by variable name. The result is a copy, so the mapping of the caller never shares state with the memory. A load refreshes an entry each time it reads the variable again. A write pass that finds no entry resolves the variable live, and its answer is stored here in the same way. As a result, a process that saves before its first load keeps one environment for every pass that follows.
pass_parameter_value(variable, resolve)
¶
Return the value of one variable for the innermost open pass.
The first request for a variable runs the resolve call and remembers its
answer for the rest of the pass. Later requests read the remembered value. A
fresh resolution that is made inside a pass is also stored in the remembered
values of the class, which is the memory from which a write pass seeds its
scope. Outside any open scope (a probe outside a pass), every request
resolves anew, because nothing is cached where no pass owns the value.
set_setting_metadata(setting_name, metadata_name, metadata_value)
¶
Store metadata for one setting on this class; None is a value.
The entry is stored in the own storage of this class, and the readers of the chain resolve it live. The readers are this class and its subclasses, but never a class above the declarer of the member.
setting_name is the member name or a getter lambda for it. A member that
the settings class does not declare raises KeyError; this is the same
check that every setting-metadata operation here applies. The value is
deep-copied, so the stored metadata never shares mutable state with the
object of the caller. Removing an entry is the job of
:meth:delete_setting_metadata.
get_setting_metadata(setting_name, metadata_name, default=None)
¶
Read one metadata entry of a setting; the nearest entry wins.
The ChainMap rule applies along the chain of the member (from this class down to the declaring class): the most derived entry that holds the key wins, whichever side declared or wrote it.
get_setting_all_metadata(setting_name)
¶
Return every metadata entry of one setting, keyed by metadata name.
This is the enumeration that the single-key getter cannot give: the entries that are visible to the class along the chain of the member. For each key, the most derived entry wins. A runtime write adds keys on top of the inherited ones without removing them. The dict and its values are deep copies, so changing the snapshot never affects the stored metadata. A setting with nothing stored anywhere in its chain returns an empty dict. A member that a subclass redeclares reads only the metadata of the redeclaration, because its chain ends at the subclass.
contains_setting_metadata(setting_name, metadata_name)
¶
Return whether the chain of the member holds the key, counting the nearest entry.
This is the unambiguous form of :meth:get_setting_metadata. get
returns its default both for a missing entry and for a stored None,
while contains tells the two cases apart. It checks the whole chain,
with the same resolution that get applies.
delete_setting_metadata(setting_name, metadata_name)
¶
Remove the own entry of the class for one setting; a missing entry is not an error.
Only what this class itself declared or stored is removed. An entry of the
declaring class, or of another reader of the chain, stays visible, in the
same way that deleting an attribute of a subclass makes the lookup fall back
to the attribute of the base class. The method returns whether an own entry
existed and was removed. An entry that was seeded by the metadata=
declaration of the member is removed in the same way: the declaration seeds
the storage only once, and the entry does not come back.
get_dynamic_setting_full_name(setting_name, serializer=None)
¶
Return the full storage name of one dynamic entry of this settings class.
This is the counterpart of :meth:get_dynamic_setting_name. It takes the
plain name by which the settings know the entry, and expands it to the name
that the storage sees: the dynamic marker and the class part of the pattern
are put in front. Backends that lay out dynamic entries in structures of
their own use it to build the names that they later enumerate. The
serializer argument carries the delimiter wish of the serializer. The
serializer in which the call runs usually passes itself.
get_dynamic_setting_name(full_name, serializer=None)
¶
Return the plain setting name for a dynamic full name of this settings class.
This is the inverse of :meth:get_dynamic_setting_full_name. It removes the
dynamic marker and the class part of the pattern, and leaves the name by
which the settings know the entry. The result is None if the name is not
a dynamic entry of this class: a static name, a dynamic entry of another
class, or foreign data.
The serializer argument carries the delimiter wish of the serializer.
The serializer in which the call runs usually passes itself. A backend that
serves dynamic entries reaches the metadata of their plugin in this way,
because that metadata is keyed by the plain name.
get_plugin(plugin_class)
¶
Return the cached plugin instance for this settings class.
The attribute side¶
advexp.settings.plugin_api.ISettingAttribute
¶
The setting attribute contract: it marks a class member as a setting.
The settings metaclass detects (by this interface) an instance that is assigned in the body of a settings class, and replaces it with a value descriptor. The serializer that serves the setting is the one that is registered for the class of the attribute.
The contract consists of five data fields with fixed semantics:
name: the override of the storage name (Noneexpands the name pattern).default: the declared default, or the :data:~advexp.settings_base.no_default.NO_DEFAULTsentinel when there is none.Noneis a legitimate default value, so absence has its own marker.aliases: additional full storage addresses that a load tries after the primary one.bound_name: the member to which the instance is bound. The library writes it when it binds the instance.declaring_class: the settings class whose body declared the member. The library writes it at the same moment.
Everything beyond the contract belongs to the attribute class itself. These
are optional parameters that backends read through
:meth:~advexp.settings_base.settings_base_serializer.SettingsBaseSerializer.attribute_of
(path, and so on).
The fields are declared as abstract properties: an attribute class that is
missing any of them fails at instantiation with a :class:TypeError. An
implementation satisfies them with properties over per-instance storage, as
:class:~advexp.settings_base.base_attribute.BaseAttribute does.
An ABCMeta in the MRO of an attribute can confuse the matching of
constructor arguments in PyCharm (weak warnings on name= and
default=). The enforced contract is worth that noise.
name
abstractmethod
property
¶
Storage name override from the attribute constructor.
None expands the configured name pattern, with the member name
in the {FieldName} slot.
default
abstractmethod
property
¶
The declared default value.
If no default was given, the value is the
:data:~advexp.settings_base.no_default.NO_DEFAULT sentinel. None is a
legitimate default, so absence has its own marker. The
:data:~advexp.settings_base.required.REQUIRED sentinel declares a member
that must get its value from a source.
aliases
abstractmethod
property
¶
Additional storage addresses of the setting, tried after the primary one.
The value is an empty tuple for an attribute that is declared without
aliases=. Each alias is a full address in the storage and has the same
status as an explicit name=: the naming options of the family never
change it. A load looks up the primary address first, then the aliases in
their declared order, and the first address found wins. The write direction
keeps serving the primary address.
bound_name
abstractmethod
property
writable
¶
Name of the class member this instance is assigned to.
Not set by user code: the library writes it at class creation, and
backends read it as the member-name fallback when name is unset
(env vars and similar backends). Stays None for attributes
bound to no member (dynamic settings).
declaring_class
abstractmethod
property
writable
¶
The settings class whose body declared this member.
It is not set by user code: the library writes it at class creation,
together with bound_name. Only the creation of the declaring class
writes it; a subclass that inherits the member does not. Backends read it as
the anchor for the per-class options of the family: the storage name of a
member is built with the entry of the class that declared it. It stays
None for attributes that are bound to no member (dynamic settings).
advexp.settings.plugin_api.SettingsAttribute(*, name=None, default=NO_DEFAULT, metadata=None, aliases=None, gt=None, ge=None, lt=None, le=None, eq=None, ne=None, min_length=None, max_length=None, regex=None, validator=None, when=None)
¶
The setting attribute base of the sources of the settings package.
This is the family base with the defaults of this package spelled out: if
default= is omitted (or is
:data:~advexp.settings_base.no_default.NO_DEFAULT), the member is
optional, and None stands in when the storage has nothing. Source
authors derive their attribute classes from this one, through
advexp.settings.plugin_api. The family base itself carries no default
values for parameters, so the base of each package is the one place where
the defaults of that package live.
FAMILY_NAME = PACKAGE_NAME
class-attribute
instance-attribute
¶
The distribution every attribute derived from this base belongs to; the settings machinery words its distribution boundary with it.
advexp.settings.plugin_api.AttributeSerializerBinding
¶
The declarative binding of a settings plugin.
attribute_class is the setting attribute class that the
serializer_class of the plugin serves. The :meth:setup
<advexp.settings_base.settings_base_plugin.SettingsBasePlugin.setup> of the
plugin base registers the binding when the plugin is registered (when its
module is imported). On a dynamic-settings plugin base, the binding is
mandatory, and attribute_class is the pin of the dynamic collection.
advexp.configuration.plugin_api.ConfigurationAttribute(*, name=None, default=REQUIRED, metadata=None, aliases=None, gt=None, ge=None, lt=None, le=None, eq=None, ne=None, min_length=None, max_length=None, regex=None, validator=None, when=None)
¶
The setting attribute base of the configuration package's sources.
This is the family base with one difference, which reflects the philosophy
of the package: if default= is omitted (or is REQUIRED), the member
is required, so its value must come from a source. For an optional member,
declare :data:~advexp.settings_base.no_default.NO_DEFAULT or a value.
Source authors derive their attribute classes from this one, so their
members behave like the members of the bundled sources out of the box:
required by default, with no explicit sentinel to remember.
FAMILY_NAME = PACKAGE_NAME
class-attribute
instance-attribute
¶
The distribution every attribute derived from this base belongs to; the settings machinery words its distribution boundary with it.
The dynamic side¶
advexp.settings.plugin_api.IDynamicSettingsInfo
¶
Optional capability: the storage can enumerate its dynamic entries.
get_dynamic_settings_names()
abstractmethod
¶
Return the full storage names of the dynamic entries that the storage holds.
The names serve the settings class to which the pass of the serializer is
bound. They are built through
:meth:SettingsContext.get_dynamic_setting_full_name, and they are returned
in the own order of the storage. The plugin filters them down to its class,
so the names of other classes, where a storage returns them, have no effect.
advexp.settings.plugin_api.DynamicSettingsBasePlugin()
¶
Base class for plugins that serve dynamic settings.
A dynamic settings plugin reuses a static backend. The mandatory
:attr:binding pins the setting attribute whose serializer also serves the
dynamic entries. The values then live in the same storage as the static
settings of the class, under dynamic names (the D prefix keeps the two
apart).
The recommended pin is the namespaced form DynamicSettingAttribute[Attr].
It has its own registry slot, so the setup() of the base, which registers
the binding, never touches the registration of Attr by the static
plugin. The serializer must tolerate None in its attr argument. For
:meth:load_settings, it must also implement
:class:~advexp.settings_base.dynamic_settings_info_interface.IDynamicSettingsInfo.
The values are held in the plugin instance (one per settings class), keyed by
the plain setting name. Nothing touches the storage until one of the storage
methods runs. The conversion follows the value contract of the static side:
saving renders the values into the storage form of the backend, and
:meth:get_setting converts them back to the type of the given default.
The iteration order of the collection comes from the class metadata key
:attr:ITERATION_ORDER_METADATA. The names that it lists come first, in
that order, and the names that it does not list follow in the order in which
they were added.
ITERATION_ORDER_METADATA = 'dynamic_settings_order'
class-attribute
instance-attribute
¶
Class metadata key holding the canonical iteration order of the names.
binding
class-attribute
¶
Mandatory on every dynamic plugin (validated at class definition).
The binding the base :meth:setup registers; the attribute class is
the collection's pin. The namespaced form
DynamicSettingAttribute[Attr]
(:class:~advexp.settings_base.dynamic_setting_attribute.DynamicSettingAttribute) is the
recommended pin: it is its own slot in the serializer registry — the
serializer bound to it serves the dynamic entries alone and never
touches the registration of the attribute class the pin namespaces. A
bare attribute class pins that attribute directly, sharing the slot
with the static side.
attribute_class
property
¶
The collection's pin: the binding's attribute class.
load_settings()
¶
Rebuild the collection from the storage.
Every dynamic entry of this settings class that is found in the storage replaces the current collection. Ordinary backend errors are logged, and the entry is skipped. A user-facing error propagates and aborts the pass (the entries before the failing one stay loaded). The metadata is cleared with the rebuild: a load brings values, and the storage holds no metadata to bring.
save_settings()
¶
Save every value, drop the storage entries that the collection no longer has, and flush.
Ordinary backend errors are logged, and the value is skipped. A user-facing
error propagates and aborts the pass. Saving None follows the storage
contract: a backend with a null form keeps the entry, and a backend without
one drops it.
delete_settings()
¶
Delete every dynamic entry of this class from the storage, and clear the collection.
Ordinary backend errors are logged, and the entry is skipped. A user-facing error propagates and aborts the pass. The metadata of the collection is cleared with it.
load_setting(setting_name)
¶
Load one entry from the storage into the collection.
If the storage no longer holds the entry, it is dropped from the collection (the storage is the source of truth). A name that never held a value is dropped in the same way. In both cases, the metadata of the name is dropped too: a load brings the value, and the storage holds no metadata to bring.
save_setting(setting_name)
¶
Save one value to the storage, and flush.
The name must be in the collection (call :meth:set_setting first),
because there is nothing to save otherwise. Saving None stores it like
any other value, as the storage contract says.
delete_setting(setting_name)
¶
Delete one entry from the storage, and drop it from the collection.
Deleting a name that the storage does not hold is not an error. The
metadata of the name is removed with it, while
:meth:delete_setting_metadata removes only the metadata.
get_setting(setting_name, default=NO_DEFAULT)
¶
Return the value of a dynamic setting.
Without default, the raw storage form is returned, and a missing name
raises KeyError. With default, the stored value is converted to the
type of the default (like the annotated conversion of declared settings). If
the value fails to convert, or if the name is missing, a deep copy of the
default is returned.
set_setting(setting_name, value)
¶
Set a value in the collection (a plain assignment; nothing is stored).
The value is kept as given, with the usual reference semantics of an
assignment. The storage is written by :meth:save_setting and
:meth:save_settings. Deleting a stored entry is the job of
:meth:delete_setting.
set_setting_metadata(setting_name, metadata_name, metadata_value)
¶
Store metadata for one dynamic setting; None is a value like any other.
The metadata lives with the collection of this plugin, keyed by the plain
dynamic name. The two storages therefore stay apart, even when a declared
member has the same name (that metadata belongs to the own channel of the
declared side, SettingsContext.set_setting_metadata).
A dynamic setting exists exactly while it holds a value. Every metadata
operation therefore requires the name to be one that the collection holds. A
miss raises KeyError that names the collection.
The metadata is a runtime state of the collection, not a stored one: the
deletes remove it too, and the loads clear it (see :meth:delete_setting
and :meth:load_settings). Removing entries alone is the job of
:meth:delete_setting_metadata and :meth:delete_setting_all_metadata. The
value is deep-copied, so the stored metadata never shares mutable state with
the object of the caller.
get_setting_all_metadata(setting_name)
¶
Return every metadata entry of one dynamic setting, keyed by metadata name.
This is the snapshot form of :meth:get_setting_metadata, and the way to
enumerate everything that the name carries. The dict and its values are
deep-copied. A setting with nothing stored returns an empty dict. As with
every metadata operation, the name must be one that the collection holds.
contains_setting_metadata(setting_name, metadata_name)
¶
Return whether a metadata entry of the dynamic setting is stored under the name.
This is the unambiguous form of :meth:get_setting_metadata. get
returns its default both for a missing entry and for a stored None,
while contains tells the two cases apart. As with every metadata
operation, the name must be one that the collection holds.
delete_setting_metadata(setting_name, metadata_name)
¶
Remove one metadata entry of a dynamic setting; a missing entry is not an error.
As with every metadata operation, the name must be one that the collection
holds. This method removes only the metadata, and the setting stays as it
is. Deleting the setting itself (:meth:delete_setting) removes its
metadata together with it.
delete_setting_all_metadata(setting_name)
¶
Remove every metadata entry of one dynamic setting at once.
The per-entry form is :meth:delete_setting_metadata. As with every
metadata operation, the name must be one that the collection holds. A
setting with no stored metadata is not an error.
to_dict()
¶
Return a snapshot of the collection, keyed by setting name.
The values are deep-copied, and the keys follow the effective iteration order (first the order that is declared in the metadata, then the order in which the names were added).
update_from_dict(data)
¶
Set many values at once; unknown names become new settings.
The point of a dynamic collection is its open set of names. Unlike the bulk update of the declared side, this method therefore rejects no key. The values are deep-copied before anything is applied, and the storage is not written: saving stays explicit.
advexp.settings.plugin_api.DocumentDynamicSettingsPlugin()
¶
Base class for dynamic-settings plugins over document backends.
The document holds the dynamic entries in their own section, with one nested
object per settings class. The section is placed by two parameters that
mirror the name=/path= of the setting attribute of a document
family:
- :attr:
dynamic_settings_section_nameis the key of the section. Assign another key to relocate the section, orNonefor the flat layout (D-prefixed full-name keys at the document root). - The optional :attr:
dynamic_settings_section_pathlocates the section deeper in the document tree, as a JSON Pointer.
The layout that is built from these parameters is the shared storage form of the family: a file that is written by one distribution of the family can be read back through another. The parameters and the layout that they spell are therefore a frozen contract, not a private choice of a backend.
Both parameters follow the MRO of the settings class, each independently. The most derived class that has an explicit binding of a parameter wins, and a class without one uses the default of the parameter, which puts the section at the document root. A backend without sections derives from the root base instead: the parameters here belong to document backends only.
DEFAULT_SECTION = 'dynamic'
class-attribute
instance-attribute
¶
Document key holding the dynamic entries (absent in the flat layout).
dynamic_settings_section_name
property
writable
¶
The document key that holds the dynamic entries of this class.
A non-empty string names the section object in which the entries are stored
(grouped per settings class). None switches to the flat layout:
D-prefixed full-name keys at the document root, next to the static keys.
The path parameter does not apply to the flat layout.
dynamic_settings_section_path
property
writable
¶
The location of the section in the document tree, as a JSON Pointer.
"/config" places the section inside the config object of the
document, in the same way that the path= of a setting of a document
family locates a static key. None (the default) keeps the section at the
document root. The path must start with /; ~1 and ~0 escape
/ and ~.
advexp.settings.plugin_api.DynamicSettingAttribute
¶
The namespaced form of a setting attribute: DynamicSettingAttribute[X].
Subscripting the class with a setting attribute class produces a distinct
subclass of that class, once per base class, so the pin is a stable identity
(DynamicSettingAttribute[X] is DynamicSettingAttribute[X]). The pin is
used as the attribute class of the mandatory binding of a
:class:~advexp.settings_base.dynamic_settings_base_plugin.DynamicSettingsBasePlugin.
It gives the dynamic entries their own slot in the attribute-to-serializer
registry: the serializer that the binding names serves only the dynamic
entries, and the two slots never clash. Bindings of the pin leave the
registration of the base attribute untouched, and vice versa. An
unregistered pin resolves like any unregistered attribute class: it raises
:class:PluginNotEnabledError.
The class itself carries the contract only abstractly. The three fields of the interface stay abstract on it, so it cannot be instantiated. Only its parameterized form is a concrete setting attribute. It inherits from the base class of the subscription, and the constructor conventions of that base apply unchanged. Declaring a class member with a pin instance is not supported: the pin names the dynamic side of a backend, and it does not declare settings.
Generic serves the type checkers only: it lets them read
DynamicSettingAttribute[X] as a type application. At runtime, the
subscription is the __class_getitem__ below, which overrides the one that
Generic would provide.
advexp.settings.plugin_api.get_dynamic_settings_class_prefix(settings_class, serializer=None, pattern=DEFAULT_SETTINGS_NAME_PATTERN)
¶
Return the name part with which every dynamic entry of settings_class
starts.
The function expands the pattern up to the {FieldName} slot with the
namespace and the class name of the class, so a full dynamic name is this
prefix followed by the setting name. Dynamic-settings plugins use it to pick
the entries of their class out of the names that a storage enumerates: the
pattern-expanded names of two settings classes never share a prefix.
A name pattern without a {FieldName} slot cannot hold per-setting names
at all. This is a configuration error, and it raises here, at the first pass
that needs the prefix.
The configuration package's read-only facade built on the same base classes:
advexp.configuration.plugin_api.ReadOnlyDocumentDynamicSettingsPlugin()
¶
Base class for the dynamic plugins of this package: a read-only collection.
The collection reads the group of dynamic entries that a writing class
stored. The writer's group key, which is declared next to the reading
class's source binding (json_source(dynamic_of=...) and its TOML
counterpart), names the group in the document. The file levels and the
section parameters place the group exactly as the writer laid it out.
The reading interface is the runtime half of the collection.
:meth:load_configuration re-reads the group (it must be called
explicitly: the class's load pass never touches the dynamic side).
:meth:get_setting converts the value to the type of the default.
:meth:to_dict returns a snapshot. Iteration follows the order in the
class metadata, like the writer's own collection. The writing half and the
metadata API are refused with a TypeError, because the entries belong
to the writer, and the sources of this package are read-only.
WRITER_GROUP_METADATA
class-attribute
¶
The class metadata key holding the writing class's group key this collection reads; each document family's plugin names its own key.
load_configuration()
¶
Rebuild the collection from the writer's group in the storage.
This is the package's own name for the collection's load, so that one verb serves both the class and the plugin handle. It must be called explicitly: the class's load pass never touches the dynamic side.
save_settings()
¶
Refused with an error: this package never writes to its sources.
The dynamic entries belong to the writing distribution. A reader that needs them stored must write them through its own settings class.
save_setting(setting_name)
¶
Refused with an error: this package never writes to its sources.
The dynamic entries belong to the writing distribution. A reader that needs them stored must write them through its own settings class.
delete_settings()
¶
Refused with an error: this package never deletes from its sources.
delete_setting(setting_name)
¶
Refused with an error: this package never deletes from its sources.
set_setting(setting_name, value)
¶
Refused with an error: values come from the storage only.
A read-only collection holds what its writer stored. To refresh it, call
:meth:load_configuration.
update_from_dict(data)
¶
Refused with an error: values come from the storage only.
A read-only collection holds what its writer stored. To refresh it, call
:meth:load_configuration.
load_setting(setting_name)
¶
Refused: the only reading operation is the load of the whole group.
Refreshing a single entry is a live-update feature, which this reader does
not provide. :meth:load_configuration re-reads every entry of the group.
set_setting_metadata(setting_name, metadata_name, metadata_value)
¶
Refused with an error: a read-only collection carries no metadata.
The storage holds none (the writer's metadata is its runtime state and is never stored), so the reader has nothing to restore it from.
get_setting_metadata(setting_name, metadata_name, default=None)
¶
Refused with an error: a read-only collection carries no metadata.
The storage holds none (the writer's metadata is its runtime state and is never stored), so the reader has nothing to restore it from.
get_setting_all_metadata(setting_name)
¶
Refused with an error: a read-only collection carries no metadata.
The storage holds none (the writer's metadata is its runtime state and is never stored), so the reader has nothing to restore it from.
contains_setting_metadata(setting_name, metadata_name)
¶
Refused with an error: a read-only collection carries no metadata.
The storage holds none (the writer's metadata is its runtime state and is never stored), so the reader has nothing to restore it from.
delete_setting_metadata(setting_name, metadata_name)
¶
Refused with an error: a read-only collection carries no metadata.
The storage holds none (the writer's metadata is its runtime state and is never stored), so the reader has nothing to restore it from.
delete_setting_all_metadata(setting_name)
¶
Refused with an error: a read-only collection carries no metadata.
The storage holds none (the writer's metadata is its runtime state and is never stored), so the reader has nothing to restore it from.
The layered side¶
The additional contracts that the configuration package defines for its source chains.
advexp.configuration.plugin_api.ISourceLinkSink
¶
Receive the link that a serializer serves in a layered class.
This is a serializer capability that the chain machinery delivers. A backend
that runs as a link of a
:class:~advexp.settings_base.sources.Sources chain reads its file, its
subtree, and its naming from the link, not from the own bindings of the
class, which a layered class does not have.
The delivery happens right after the delivery of the context and before the first storage call, so the link is in place by the time a load asks for a value. A backend that never runs as a link never sees this call, and the bindings of its class keep governing it as before. The interface is a seat for the link, not an obligation.
set_source_link(link)
abstractmethod
¶
Take delivery of the link that this serializer serves.
The :meth:~advexp.settings_base.source_link.SourceLink.params property of
the link already serves a fresh copy on every read. An implementation
therefore keeps the link itself and reads its parameters on every pass, so a
link that was reconfigured reaches the next resolution without a new
delivery.
advexp.configuration.plugin_api.SourceBinding(link, extras=None, family=None)
¶
The result of a source builder: one link, its side entries, and its stamp.
The builders of the packages create it, and an application never constructs one.
linkis the link of the chain proper.extrasare the metadata entries that the link declares next to the chain (for example, the environment naming dictionary, when an environment link is the one that declares it). They are stored under their own family keys, in the same way as the helpers of the families store them.familyis the stamp: the name of the distribution whose builder created the binding. It is checked against the family of the class at theclassstatement.
advexp.configuration.plugin_api.SourceLink(attribute_class=None, serializer_class=None, params=None)
¶
One link of a source chain: a family, a serializer, and the own parameters of the link.
The source builders of a package construct links, and an application never builds one by hand.
attribute_classnames the family that the link serves. It is the class that the member factory of the family builds, so a member that is declared through that factory reads this link and no other.Nonemeans that the link serves no family: only the neutral members read through it.serializer_class, when given, is the serializer that the link runs. Without it, the machinery resolves the serializer that is registered forattribute_class, in the same registry that the non-layered pass uses.paramscarries everything that the serializer reads from the link. The builders of the family fill it, the serializer of the family reads it, and no other code touches it.
attribute_class
property
¶
The attribute class naming the family this link serves.
None for a family-less link: a member factory never matches
it, and the neutral members read through it.
params
property
¶
The link's own parameters, as the family's builder spelled them.
param(name, default=None)
¶
Read one link parameter; default when the link has none.
serves(attribute)
¶
Return whether a member that is declared through attribute reads this
link.
A family link serves the attributes of its family: the members that its
attribute_class accepts, including a registered subclass. A link without
a family serves none of them. Only the neutral members read through such a
link, and the machinery routes them by its own rule.
serializer(context, attribute_class=None)
¶
Build the serializer of this link, bound to the context of the pass.
attribute_class names the own attribute class of the member when it
differs from the family of the link. A registered subclass is served by its
own registered serializer, in the same way as the non-layered pass resolves
it. Without it (or with no registration for it), the family class of the
link decides. The serializer class that was given at construction always
wins: a link without a family carries its own.
The context is delivered in the same way as every pass delivers it, and the
link follows it. A serializer that implements
:class:~advexp.settings_base.source_link_sink_interface.ISourceLinkSink
also receives this link. This is how the serializer of a family reads the
file, the subtree, and the naming of the link.
advexp.configuration.plugin_api.SOURCE_CHAIN_METADATA = '__SourceChain'
module-attribute
¶
The class metadata key of the source chain — one key for every
layered class of the family. The value is the :class:Sources object
itself; the machinery stores it per declaring class, so inheritance
resolves the nearest chain above a subclass.
advexp.configuration.plugin_api.LINK_PARAMETERS_PARAM = 'parameters'
module-attribute
¶
The link parameter carrying the path parameters of a templated
file — the :class:~advexp.settings_base.env_parameter.EnvParameter
results the builder's parameters= spelled. An untemplated link
carries None; a templated one carries the tuple the builder's
checks left standing, and its serializer unfolds the file through them
on every pass.
advexp.configuration.plugin_api.resolve_templated_path(context, configured, parameters, source)
¶
Return the configured spelling with its slots resolved for this pass.
A spelling without parameters is returned as given: this is the plain file path that every unparameterized link spells. A templated spelling resolves each of its slots through the parameter that declares it. The pass resolves each variable once and reuses the value for the rest of the pass, and it reads the environment anew on every pass.
A variable that is not set breaks the link: a :class:UserFacingError names
the variable, the template, and the source, unless the parameter declared a
default=. A value outside the whitelist breaks the link in the same way.
The resolved value of every freshly resolved variable is written to the log
as a debug record that names the file that it produced.
Registration and module defaults¶
advexp.settings.plugin_api.register_source_link_key(metadata_name)
¶
Register the source seat of a family: its metadata key and its validator.
The key of the seat is the place where the *_source builder of the
family stores its link, and where the passes look the link up. Registering
twice hands the registry the same validator, which is a no-op. This is the
registration rule for family keys that the metadata registry follows.
A custom backend calls this function from its plugin module. The import alone seats the family, which is the pattern of the bundled families.
advexp.settings.plugin_api.register_file_seat(file_seat, source_seat)
¶
Register the file seat of a family, and the source seat that it cannot be declared next to.
The class statement rejects a declaration that holds both seats of one
family: the pair would name one storage twice, and the file of a bound
family belongs on its source builder. This rejection is not a special case
of the bundled families. It comes from the registry that this function
fills.
Registering the same pair again is a no-op, which is the registration rule for family keys that the metadata registry follows. The registry refuses a conflict in two cases: a file seat that is claimed for another source seat, and a pair that names a source seat that no family registered.
A custom backend calls this function from its plugin module, next to
:func:register_source_link_key, which is the pattern of the bundled
families.
advexp.settings.plugin_api.register_module_default(holder, parameter, validator)
¶
Open a parameter of a configuration holder for module declarations.
A backend calls this function next to its holder, from the module that
declares the holder itself. The parameter is one attribute read of the holder
(lambda holder: holder.file_path). The registration checks that the
attribute named by the lambda is one that the holder declares: a renamed
attribute fails here, at the import of the module of the holder, instead of
leaving a dead name that no declaration ever finds.
The validator is the own validator of the parameter: the same one that the metadata registry holds for the file binding of the backend. Every declared value passes it at the assignment, so an invalid value fails at the line of the declaration itself.
Registering a parameter again with the same validator is a no-op, which is the same rule that the metadata registry follows for family keys. A different validator that claims a registered parameter is a conflict, and the registry refuses it.
advexp.settings.plugin_api.module_default_for(holder, parameter, module)
¶
Return the value that a module declared for the parameter; None if it
declared none.
The parameter is the same attribute read that the registration spelled. This is the lookup of the backends: a settings class asks with the name of the module in which it is declared, before it reads the attribute of the holder. The declaration of the module therefore wins over the process-wide value, and it never reaches the classes of other modules.
advexp.settings.plugin_api.family_entry_key(family_key, declaring_class)
¶
Return the metadata key of the own entry of one class in the options of a family.
The options of a family are stored per declaring class, so a subclass with its own options coexists with the base class instead of replacing it. The key is the family key joined with the module and the name of the class. The module keeps classes with the same name from different modules distinct. User code never spells this string: the metadata channel names the class itself, and the machinery builds the key.
advexp.settings.plugin_api.ModuleDefaultMeta
¶
The metaclass of the configuration holders of the backends: the classes whose class-level attributes carry the process-wide defaults.
Assigning a
:class:~advexp.settings_base.module_default.ModuleDefault to one of the
parameters of a holder declares the value for the module in which the
assignment is written. The metaclass validates the value and stores the
declaration in the registry of module declarations. The attribute of the
holder keeps the process-wide value that it held: reading the parameter after
a declaration still shows the fallback that every other module resolves
through.
Any other assignment reaches the class untouched, and it is the plain process-wide channel, exactly as before.
The supporting names¶
advexp.settings.plugin_api.UserFacingError(inner)
¶
Wraps an error that must reach the user.
Serializers may raise it (with the original exception as the argument) to signal conditions that must not be silently logged and skipped.
advexp.settings.plugin_api.NOT_FOUND = _NotFound()
module-attribute
¶
Returned by ISettingsSerializer.load when the storage holds no value.
A None return is a valid stored value; absence of a value is always
expressed with this sentinel.
advexp.settings.plugin_api.NO_DEFAULT = _NoDefault()
module-attribute
¶
Sentinel for absent default values (allows default=False/default=None).
A member declared with it serves None while no value has arrived, and
that stand-in faces the member's own constraints at class creation: a
member whose constraints reject None — eq=, a validator
returning false — fails the class statement, for without an assignment
it could never hold a valid value; :data:~advexp.settings_base.required.REQUIRED
is the honest spelling for such a member.
advexp.settings.plugin_api.REQUIRED = _Required()
module-attribute
¶
The default that makes a member required: its value must come from a source.
Declared as the default (default=REQUIRED). Every boundary that would
serve the default — a load that found nothing usable in the source, a read
of a never-loaded member — raises
:class:~advexp.settings_base.member_load_error.MemberLoadError
instead: the load-time reasons are entries of the load pass's aggregate
abort. The family-wide spelling of an optional member is
:data:~advexp.settings_base.no_default.NO_DEFAULT: None stands in,
and its load problems land in the report the loading method returns.
advexp.settings.plugin_api.PACKAGE_NAME = 'advexp.settings'
module-attribute
¶
The import name of the distribution — the one spelling of it: the machinery's logging channel is declared with it, and the family's diagnostics prefix their messages with it.
advexp.settings.plugin_api.SETTINGS_CONTEXT_ATTR = '__settings_context__'
module-attribute
¶
Attribute the settings metaclass stores each class's context under. Every settings class holds its own; the chain-resolving reads of the class metadata reach the ancestors' storages through it.
advexp.settings.plugin_api.SettingRef = Union[str, _Getter]
module-attribute
¶
A setting reference: the member name or a getter lambda for it.
advexp.settings.plugin_api.EnvParameter(variable, choices, default=None, slot_name=None)
¶
One environment variable from which a templated path reads a slot.
It is built by :func:env_parameter; an application never constructs one
directly.
variablenames the environment variable.slot_nameis the slot that the template spells for the variable. It is the name of the variable unlessslot=renamed it, and it is the name by which every check between slots and parameters goes.choicesis the whitelist of the variable.defaultis the value to which an unset variable resolves. It is already known to be one of the choices, or it isNoneif none was declared.
variable
property
¶
The environment variable the parameter reads.
choices
property
¶
The whitelist of values the variable may hold.
default
property
¶
The value an unset variable resolves to; None when the
parameter declares no default.
slot_name
property
¶
The slot name the template spells for this parameter.
advexp.settings.plugin_api.env_parameter(variable, choices, default=None, slot=None)
¶
Declare one path parameter that is read from an environment variable.
Pass the result to the parameters= of a builder of a source that carries
a file, either alone or in a tuple:
app_env = env_parameter("APP_ENV", choices=("dev", "test", "prod"))
class AppConfig(Configuration, options=Sources(
json_source(file="app.json"),
json_source(file="settings-{APP_ENV}.json", parameters=app_env),
)):
variable names the environment variable, and the template spells the
slot by that name: settings-{APP_ENV}.json.
slot= renames the slot if the template spells another name. The value
still comes from variable, and the template spells the renamed slot, so
slot="env" reads APP_ENV into settings-{env}.json. This is
useful for a template that dictates its own slot names (a file shared with
another system, or a path that reads better with a short slot). The name
must be a valid identifier: otherwise, the braces would stay as literal text
instead of becoming a slot.
choices is the obligatory whitelist of which the value of the variable
must be one. It is checked before any file is touched.
default is the value to which an unset variable resolves. It must be one
of the choices. Without it, an unset variable breaks the link at load time.
advexp.settings.plugin_api.setting_name_candidates(primary_name, attribute)
¶
Return the addresses that a load tries for one setting, with the primary address first.
The primary address is the explicit name= or the pattern-built one,
whatever the caller resolved. The declared aliases follow, in their order. A
load walks the list, and the first address found wins, so the primary address
outranks every alias, and an alias outranks the aliases that are declared
after it. A dynamic entry carries no attribute instance, so its delivered
name is its only candidate.
advexp.settings.plugin_api.spelled_with_aliases(address, aliases)
¶
Return an address phrase of a message, with the declared aliases listed next to it.
For a member without aliases, the phrase is environment variable
'APP_TTL' alone. With aliases, it is environment variable 'APP_TTL'
(aliases: 'APP_TTL_OLD'). This way, the reader of a miss record sees every
address that the pass checked, not only the primary one. The phrase keeps
whatever spelling the caller built, and the aliases are joined in their
declared order.