granular_configuration_language

class granular_configuration_language.LazyLoadConfiguration(
*load_order_location: Path | str | PathLike,
base_path: str | Sequence[str] | None = None,
use_env_location: bool = False,
env_location_var_name: str = 'G_CONFIG_LOCATION',
inject_before: Configuration | None = None,
inject_after: Configuration | None = None,
disable_caching: bool = False,
**kwargs: Any,
)[source]

Bases: Mapping

The entry point for defining an immutable Configuration from file paths that lazily loads on first access.

Options:
  • Using env_location_var_name, you can enable pulling locations from an environment variable.

  • See LazyLoadConfiguration.as_typed() for type annotated usage.

  • inject_before and inject_after allow you to inject Python-created settings into you configuration without use a file.

as_typed() Example
class SubConfig(Configuration):
    c: str


class Config(Configuration):
    a: int
    b: SubConfig


typed = LazyLoadConfiguration("config.yaml").as_typed(Config)

assert typed.a == 101
assert typed.b.c == "test me"
assert typed["a"] == 101
Injection Rules and Example
  • Injections must use Configuration for all mappings.

    • Otherwise, they will be treated as a normal value and not merged.

  • Injection occurs before base_path is applied.

    • I.e. You must include the base_path in the injected Configuration.

  • Using injections disables “identical immutable configurations” caching.

  • This is only available for LazyLoadConfiguration

  • Examples:

    • You might want to have a setting the is the current date.

    • You want to provide substitution options via !Sub.

# "config.yaml"
app:
    data:
        key1: !Sub ${$.LOOKUP_KEY}
        key2: !Sub ${$.LOOKUP_KEY}
CONFIG = LazyLoadConfiguration(
    "config.yaml",
    base_path="app",
    inject_after=Configuration(
        app=Configuration(
            today=date.today().isoformat(),
        ),
        LOOKUP_KEY="value made available to `!Sub`",
    ),
)

CONFIG.today  # Today's date as a constant string.
CONFIG.data.as_dict()  # Data defined with a reusable library defined value.

Attention

Parameters:
  • *load_order_location (Path | str | os.PathLike) – File path to configuration file

  • base_path (str | Sequence[str], optional) – Defines the subsection of the configuration file to use. See Examples for usage options.

  • use_env_location (bool, optional) –

    • Enabled to use the default environment variable location.

    • Setting to True is only required if you don’t change env_location_var_name from its default value.

  • env_location_var_name (str, optional) –

    • Specify what environment variable to check for additional file paths.

    • The Environment Variable is read as a comma-delimited list of configuration path that will be appended to load_order_location list.

    • Setting the Environment Variable is always optional.

    • Default: G_CONFIG_LOCATION

      • Setting use_env_location=True is required to use the default value.

  • inject_before (Configuration, optional) – Inject a runtime Configuration instance, as if it were the first loaded file.

  • inject_after (Configuration, optional) – Inject a runtime Configuration instance, as if it were the last loaded file.

  • disable_caching (bool, optional) – When True, this instance will not participate in the caching of “identical immutable configurations”.

  • **kwargs (Any) – There are no public-facing supported extra parameters.

Examples:
# Base Path - Single Key
LazyLoadConfiguration(..., base_path="base_path")
# Base Path - JSON Pointer (strings only)
LazyLoadConfiguration(..., base_path="/base/path")
# Base Path - List of keys
LazyLoadConfiguration(..., base_path=("base", "path"))

# Use Environment Variable: "CONFIG_LOC"
LazyLoadConfiguration(..., env_location_var_name="CONFIG_LOC")

# Use default Environment Variable: "G_CONFIG_LOCATION"
LazyLoadConfiguration(..., use_env_location=True)

# With a typed `Configuration`
LazyLoadConfiguration(...).as_typed(TypedConfig)
__getattr__(
name: str,
) Any[source]

Loads (if not loaded) and fetches from the underlying Configuration object

This also exposes the methods of Configuration (except dunders).

Parameters:

name (str) – Attribute name

Returns:

Result

Return type:

Any

as_typed(
typed_base: type[C],
) C[source]

Create a proxy that is cast to provide Configuration subclass with typed annotated attributes.

This proxy ensures laziness is preserved and is fully compatible with Configuration.

Example
class SubConfig(Configuration):
    c: str


class Config(Configuration):
    a: int
    b: SubConfig


typed = LazyLoadConfiguration("config.yaml").as_typed(Config)

assert typed.a == 101
assert typed.b.c == "test me"
assert typed["a"] == 101
No runtime type checking

This method uses typing.cast() to return a SafeConfigurationProxy of this instance as the requested Configuration subclass.

This enables typing checking and typed attributes with minimal a runtime cost, but it is limited to just improving developer experience.

Use Pydantic, or some like it, if you require runtime type checking.

Parameters:

typed_base (type[C]) – Subclass of Configuration to assume

Returns:

SafeConfigurationProxy instance that has been cast to the provided type.

Return type:

C

eager_load(
typed_base: type[C],
) C[source]

Added in version 2.3.0.

This will eagerly load this instance, so that there is minimum IO load on future.

This is intended to play well with asyncio, by avoiding blocking the main thread/event loop on IO calls, without introducing an await paradigm just for a few one-time calls.

Part of the EagerIO feature set

Using eager_load() causes immediate loading of this instance in a background thread, so that future calls are non-/minimally blocking.

Behaves like as_typed() otherwise.

Parameters:

typed_base (type[C]) – Subclass of Configuration to assume

Returns:

EagerIOConfigurationProxy instance that has been cast to the provided type.

Return type:

C

get(
k[,
d,]
) D[k] if k in D, else d.  d defaults to None.
items() a set-like object providing a view on D's items
keys() a set-like object providing a view on D's keys
load_configuration() None[source]

Loads the configuration.

values() an object providing a view on D's values
property config: Configuration

Load and fetch the configuration. Configuration is cached for subsequent calls.

Thread-safe

Loading the configuration is thread-safe and locks while the configuration is loaded to prevent duplicative processing and data

class granular_configuration_language.Configuration[source]
class granular_configuration_language.Configuration(
mapping: Mapping[KT, VT],
/,
)
class granular_configuration_language.Configuration(
iterable: Iterable[tuple[KT, VT]],
/,
)
class granular_configuration_language.Configuration(
**kwargs: VT,
)

Bases: Generic[KT, VT], Mapping[KT, VT]

This class represents an immutable Mapping of configuration.

You can create type annotated subclasses of Configuration to enable type checking and code completion, as if your subclass was a dataclass [1].

With you typed class, you can cast a general Configuration to your subclass via Configuration.as_typed().

as_typed() Example
class SubConfig(Configuration):
    c: str


class Config(Configuration):
    a: int
    b: SubConfig


config = ...  # A Configuration instance
typed = config.as_typed(Config)

assert typed.a == 101
assert typed.b.c == "test me"
assert typed["a"] == 101

# Or loading with LazyLoadConfiguration

typed = LazyLoadConfiguration("config.yaml").as_typed(Config)

Advisement

Consider using LazyLoadConfiguration.as_typed() to load your entire configuration as a typed Configuration.

Footnotes
Changes

Changed in version 2.3.0: Added Generic Type Parameters, which default to Any.

Parameters:
__getattr__(
name: str,
) VT[source]

Provides a potentially cleaner path as an alternative to __getitem__().

Comparing to __getitem__()

Example:
config.a.b.c          # Using `__getattr__`
config["a"]["b"]["c"] # Using `__getitem__`
Parameters:

name (str) – Attribute name

Returns:

Fetched value

Return type:

VT

Raises:

AttributeError – When an attribute is not present.

as_dict() dict[KT, VT][source]

Returns this Configuration as standard Python dict. Nested Configuration objects will also be converted.

Evaluation Notice

This will evaluate all lazy tag functions and throw an exception on Placeholder objects.

Returns:

A shallow dict copy

Return type:

dict

as_json_string(
*,
default: Callable[[Any], Any] | None = None,
**kwds: Any,
) str[source]

Returns this Configuration as a JSON string, using standard json library and (as default) the default factory provided by this library (granular_configuration_language.json_default()).

Evaluation Notice

This will evaluate all lazy tag functions and throw an exception on Placeholder objects.

Parameters:
Returns:

JSON-format string

Return type:

str

as_typed(
typed_base: type[C],
) C[source]

Cast this Configuration instance into subclass of Configuration with typed annotated attributes

Advisement

Consider using LazyLoadConfiguration.as_typed() to load your entire configuration as a typed Configuration, instead of just a section with this version.

No runtime type checking

This method uses typing.cast() to return this instance, unmodified, as the requested Configuration subclass. This enables typing checking and typed attributes with minimal a runtime cost. It is limited to just improving developer experience.

Use Pydantic, or some like it, if you require runtime type checking.

Parameters:

typed_base (type[C]) – Subclass of Configuration to assume

Returns:

This instance

Return type:

C

copy() Configuration[KT, VT]

Returns a shallow copy of this instance. (Matches dict.copy() interface.)

Caution

LazyEval do not make copies. If you have not evaluated all tags, you should called evaluate_all() before calling this method.

Tip

Configuration is immutable, so you do not need to make a copy to protect it.

evaluate_all() None[source]

Evaluates all lazy tag functions and throws an exception on Placeholder instances

exists(key: Any) bool[source]

Checks that a key exists and is not a Placeholder

Parameters:

key (Any) – key to be checked

Returns:

Returns True if the key exists and is not a Placeholder

Return type:

bool

get(
key: KT,
/,
) VT | None[source]
get(
key: KT,
/,
default: VT | T,
) VT | T

Return the value for key if key is in the Configuration, else default.

Changed in version 2.3.0: Added typing overload. key is typed as positional.

Parameters:
  • key ((KT)) – Key being fetched

  • default ((VT | T)) – Default value. Defaults to None.

Returns:

Fetched value or default

Return type:

VT | T | None

items() a set-like object providing a view on D's items
keys() a set-like object providing a view on D's keys
typed_get(
type: type[T],
key: Any,
) T[source]
typed_get(
type: type[T],
key: Any,
*,
default: T,
) T
typed_get(
type: type[T],
key: Any,
*,
predicate: Callable[[Any], TypeGuard[T]],
) T
typed_get(
type: type[T],
key: Any,
*,
default: T,
predicate: Callable[[Any], TypeGuard[T]],
) T

Provides a typed-checked get() option

Parameters:
  • type (type[T]) – Wanted typed

  • key (Any) – Key for wanted value

  • default (T, optional) – Provides a default value like dict.get()

  • predicate (Callable[[Any], TypeGuard[T]], optional) – Replaces the isinstance(value, type) check with a custom method predicate(value: Any) -> bool

Returns:

Value stored under the key

Return type:

T

Raises:

TypeError – If the real type is not an instance of the expected type

values() an object providing a view on D's values
class granular_configuration_language.MutableLazyLoadConfiguration(
*load_order_location: Path | str | PathLike,
base_path: str | Sequence[str] | None = None,
use_env_location: bool = False,
env_location_var_name: str = 'G_CONFIG_LOCATION',
)[source]

Bases: LazyLoadConfiguration, MutableMapping

Used to define a mutable Configuration from file paths that lazily loads on first access.

Options:
  • Using env_location_var_name, you can enable pulling locations from an environment variable.

Tip

Consider using an immutable configuration with LazyLoadConfiguration in you code to reduce unexpected side-effects.

Classes used for mutability
Parameters:
  • *load_order_location (Path | str | os.PathLike) – File path to configuration file

  • base_path (str | Sequence[str], optional) – Defines the subsection of the configuration file to use. See Examples for usage options.

  • use_env_location (bool, optional) –

    • Enabled to use the default environment variable location.

    • Setting to True is only required if you don’t change env_location_var_name from its default value.

  • env_location_var_name (str, optional) –

    • Specify what environment variable to check for additional file paths.

    • The Environment Variable is read as a comma-delimited list of configuration path that will be appended to load_order_location list.

    • Setting the Environment Variable is always optional.

    • Default: G_CONFIG_LOCATION

      • Setting use_env_location=True is required to use the default value.

Examples:
# Base Path - Single Key
MutableLazyLoadConfiguration(..., base_path="base_path")
# Base Path - JSON Pointer (strings only)
MutableLazyLoadConfiguration(..., base_path="/base/path")
# Base Path - List of keys
MutableLazyLoadConfiguration(..., base_path=("base", "path"))

# Use Environment Variable: "CONFIG_LOC"
MutableLazyLoadConfiguration(..., env_location_var_name="CONFIG_LOC")

# Use default Environment Variable: "G_CONFIG_LOCATION"
MutableLazyLoadConfiguration(..., use_env_location=True)
__getattr__(
name: str,
) Any

Loads (if not loaded) and fetches from the underlying Configuration object

This also exposes the methods of Configuration (except dunders).

Parameters:

name (str) – Attribute name

Returns:

Result

Return type:

Any

as_typed(
typed_base: type[C],
) NoReturn[source]

Not supported for MutableLazyLoadConfiguration. Use LazyLoadConfiguration.

clear() None.  Remove all items from D.
eager_load(
typed_base: type[C],
) NoReturn[source]

Not supported for MutableLazyLoadConfiguration. Use LazyLoadConfiguration.

get(
k[,
d,]
) D[k] if k in D, else d.  d defaults to None.
items() a set-like object providing a view on D's items
keys() a set-like object providing a view on D's keys
load_configuration() None

Loads the configuration.

pop(
k[,
d,]
) v, remove specified key and return the corresponding value.

If key is not found, d is returned if given, otherwise KeyError is raised.

popitem() (k, v), remove and return some (key, value) pair

as a 2-tuple; but raise KeyError if D is empty.

setdefault(
k[,
d,]
) D.get(k,d), also set D[k]=d if k not in D
update(
[E,]
**F,
) None.  Update D from mapping/iterable E and F.

If E present and has a .keys() method, does: for k in E.keys(): D[k] = E[k] If E present and lacks .keys() method, does: for (k, v) in E: D[k] = v In either case, this is followed by: for k, v in F.items(): D[k] = v

values() an object providing a view on D's values
property config: MutableConfiguration

Load and fetch the configuration. Configuration is cached for subsequent calls.

Thread-safe

Loading the configuration is thread-safe and locks while the configuration is loaded to prevent duplicative processing and data

class granular_configuration_language.MutableConfiguration[source]
class granular_configuration_language.MutableConfiguration(
mapping: Mapping[KT, VT],
/,
)
class granular_configuration_language.MutableConfiguration(
iterable: Iterable[tuple[KT, VT]],
/,
)
class granular_configuration_language.MutableConfiguration(
**kwargs: VT,
)

Bases: Generic[KT, VT], MutableMapping[KT, VT], Configuration[KT, VT]

This class represents an MutableMapping of the configuration. Inherits from Configuration

Tip

Consider using Configuration in you code to reduce unexpected side-effects.

Parameters:
__getattr__(
name: str,
) VT

Provides a potentially cleaner path as an alternative to __getitem__().

Comparing to __getitem__()

Example:
config.a.b.c          # Using `__getattr__`
config["a"]["b"]["c"] # Using `__getitem__`
Parameters:

name (str) – Attribute name

Returns:

Fetched value

Return type:

VT

Raises:

AttributeError – When an attribute is not present.

as_dict() dict[KT, VT]

Returns this Configuration as standard Python dict. Nested Configuration objects will also be converted.

Evaluation Notice

This will evaluate all lazy tag functions and throw an exception on Placeholder objects.

Returns:

A shallow dict copy

Return type:

dict

as_json_string(
*,
default: Callable[[Any], Any] | None = None,
**kwds: Any,
) str

Returns this Configuration as a JSON string, using standard json library and (as default) the default factory provided by this library (granular_configuration_language.json_default()).

Evaluation Notice

This will evaluate all lazy tag functions and throw an exception on Placeholder objects.

Parameters:
Returns:

JSON-format string

Return type:

str

as_typed(
typed_base: type[C],
) C

Cast this Configuration instance into subclass of Configuration with typed annotated attributes

Advisement

Consider using LazyLoadConfiguration.as_typed() to load your entire configuration as a typed Configuration, instead of just a section with this version.

No runtime type checking

This method uses typing.cast() to return this instance, unmodified, as the requested Configuration subclass. This enables typing checking and typed attributes with minimal a runtime cost. It is limited to just improving developer experience.

Use Pydantic, or some like it, if you require runtime type checking.

Parameters:

typed_base (type[C]) – Subclass of Configuration to assume

Returns:

This instance

Return type:

C

clear() None.  Remove all items from D.
copy() MutableConfiguration

Returns a shallow copy of this instance. (Matches dict.copy() interface.)

Caution

LazyEval do not make copies. If you have not evaluated all tags, you should called evaluate_all() before calling this method.

evaluate_all() None

Evaluates all lazy tag functions and throws an exception on Placeholder instances

exists(key: Any) bool

Checks that a key exists and is not a Placeholder

Parameters:

key (Any) – key to be checked

Returns:

Returns True if the key exists and is not a Placeholder

Return type:

bool

get(
key: KT,
default: VT | T | None = None,
) VT | T | None

Return the value for key if key is in the Configuration, else default.

Changed in version 2.3.0: Added typing overload. key is typed as positional.

Parameters:
  • key ((KT)) – Key being fetched

  • default ((VT | T)) – Default value. Defaults to None.

Returns:

Fetched value or default

Return type:

VT | T | None

items() a set-like object providing a view on D's items
keys() a set-like object providing a view on D's keys
pop(
k[,
d,]
) v, remove specified key and return the corresponding value.

If key is not found, d is returned if given, otherwise KeyError is raised.

popitem() (k, v), remove and return some (key, value) pair

as a 2-tuple; but raise KeyError if D is empty.

setdefault(
k[,
d,]
) D.get(k,d), also set D[k]=d if k not in D
typed_get(
type: type[T],
key: Any,
) T[source]
typed_get(
type: type[T],
key: Any,
*,
default: T,
) T
typed_get(
type: type[T],
key: Any,
*,
predicate: Callable[[Any], TypeGuard[T]],
) T
typed_get(
type: type[T],
key: Any,
*,
default: T,
predicate: Callable[[Any], TypeGuard[T]],
) T

Provides a typed-checked get() option

Parameters:
  • type (type[T]) – Wanted typed

  • key (Any) – Key for wanted value

  • default (T, optional) – Provides a default value like dict.get()

  • predicate (Callable[[Any], TypeGuard[T]], optional) – Replaces the isinstance(value, type) check with a custom method predicate(value: Any) -> bool

Returns:

Value stored under the key

Return type:

T

Raises:

TypeError – If the real type is not an instance of the expected type

update(
[E,]
**F,
) None.  Update D from mapping/iterable E and F.

If E present and has a .keys() method, does: for k in E.keys(): D[k] = E[k] If E present and lacks .keys() method, does: for (k, v) in E: D[k] = v In either case, this is followed by: for k, v in F.items(): D[k] = v

values() an object providing a view on D's values
granular_configuration_language.LLC

alias of LazyLoadConfiguration

granular_configuration_language.json_default(value: Any) Any[source]

A factory function to be used by the json.dump() family of functions.

Provides serialization for types produced by this library’s Tags.

Explicitly:

Parameters:

value (Any) – Value being converted

Returns:

json.dump() compatible object

Return type:

Any

Raises:

TypeError – When an incompatible is provided, as required by JSONEncoder

granular_configuration_language.merge(
configs: Iterable[Configuration | LazyLoadConfiguration | LazyEval | PathLike | Any],
*,
mutable: bool = False,
) Configuration[source]

Merges the provided configurations into a single configuration.

Why does this exist?

  • To enable merging a framework configuration with a library-specific configuration.

    • The explicit case was for a pytest sub-plugin that was a part of a framework plugin.

    • Using merge() allows users to set settings in the framework configuration without requiring the framework configuration needing to know about the sub-plugin.

      • base_path is required to ensure safety.

Caution

Don’t use merge() as a replacement for LazyLoadConfiguration. It is less efficient and creates Load Boundaries on each configuration.

Attention

Parameters:
Returns:

Merged configuration. Empty if nothing was mergeable.

Return type:

Configuration

Made available from .yaml.classes

class granular_configuration_language.Masked[source]

Bases: str

Used to keep secrets from printing to screen when running tests.

  • Inherits from str.

  • Replaces the standard __repr__() result with the constant literal '<****>'.

  • Used by !Mask tag

Note

Does not alter text or prevent print() from display the string value.

class granular_configuration_language.Placeholder(message: str)[source]

Bases: object

Representation of !Placeholder tag.

Holds the !Placeholder message.