Skip to content

Automated Settings Management🔖

A great use-case for type-coercion is the automatic resolution of environment variables. Typical has your back with the typic.settings decorator:

typic.environ🔖

A proxy for os.environ which facilitates geting/setting of strongly-typed data.

typic.environ.getenv(...)🔖

Fetch the value assigned to the given variable.

Parameters🔖

var: str

The environment variable to lookup.

t: Type = Any

An optional type to coerce the value at var.

ci: bool = True

Whether the environment variable should be considered case-insensitive.

typic.environ.setenv(...)🔖

Set a value at the target variable.

Parameters🔖

var: str

The environment variable to set.

value: Any

The value to set at the given variable.

typic.register(...)🔖

Registers a handler for resolving variables with type t.

Parameters🔖

t: Type

The target type to create and register a handler for.

name: str = None

An optional name to register the handler with.

Usage🔖

Fetching & Setting Values
import typic

typic.environ.setenv("USE_FOO", True)

use_foo = typic.environ.getenv("use_foo", t=bool)
print(use_foo)
#> True
Using a Type Handler

typic.environ ships with handlers for all native types and typical’s own extended types.

import typic

typic.environ.setenv("USE_FOO", True)

print(typic.environ.bool("use_foo"))
#> True

print(typic.environ.str("use_foo"))
#> true

print(typic.environ.int("use_foo"))
#> 1

typic.environ.setenv("DATABASE_URL", "postgres://localhost:5432/db")

dsn = typic.environ.DSN("DATABASE_URL")

print(dsn)
#> postgres://localhost:5432/db

print(dsn.info)
#> DSNInfo(driver='postgres', username='', password=, host='localhost', port=5432, name='/db', qs='', is_ip=False)
Register a Custom Handler
import dataclasses

import typic


@dataclasses.dataclass
class Foo:
    bar: str = None


typic.register(Foo)
typic.environ.setenv("THIS_FOO", Foo())

print(typic.environ.Foo("THIS_FOO"))
#> Foo(bar=None)

@typic.settings(...)🔖

Create a typed class which sets its defaults from env vars.

The resolution order of values is default(s) -> env value(s) -> passed value(s).

Settings instances are indistinguishable from other typical dataclasses at run-time and are frozen by default. If you really want your settings to be mutable, you may pass in frozen=False manually.

Parameters🔖

prefix: str = ‘’

A string which all the target variables with begin with, i.e., ‘APP_’

case_sensitive: bool = False

Whether to respect the case of environment variables.

frozen: bool = True

Whether the resulting dataclass should be immutable.

aliases: Mapping = None

A mapping of full-name aliases for the defined attributes. {‘other_foo’: ‘foo’} will locate the env var OTHER_FOO and place it on the Bar.foo attribute.

Environment Variables for settings classes are resolved via typic.environ getters, which are set as default factories.

If your class has defaults assigned, we will not try to resolve via the environment.

If you pass in a value for a given attribute, that will override any default.

Using Settings
import os
import typic

os.environ['FOO'] = "1"

@typic.settings
class Bar:
    foo: int


print(Bar())
#> Bar(foo=1)

print(Bar("3"))
#> Bar(foo=3)

bar = Bar()
bar.foo = 2
#> Traceback (most recent call last):
#>   ...
#> dataclasses.FrozenInstanceError: cannot assign to field 'foo'

When the final dataclass is generated, all matching environment variables will be resolved as default values for the matching attribute (or a default factory in the case of a mutable default).

When the class itself is initialized, values passed in will override variables provided in your environment.