| name | appsettings2 |
|---|---|
| description | Unifies configuration sources into a Configuration object that can be bound to complex Python types or accessed directly. Covers ConfigurationBuilder, providers, object binding, helpers, and ConfigurationException. Use as a reference document for appsettings2 API and concepts. |
| user-invocable | true |
| disable-model-invocation | false |
| type | reference |
Unifies configuration sources into a Configuration object that can be bound to complex Python types or accessed directly. Requires Python 3.11+.
from appsettings2 import ConfigurationBuilder
from appsettings2.providers import JsonConfigurationProvider, EnvironmentConfigurationProvider
config = ConfigurationBuilder()\
.add_provider(JsonConfigurationProvider('appsettings.json'))\
.add_provider(EnvironmentConfigurationProvider())\
.build()- ConfigurationBuilder
- Configuration Access Patterns
- Object Binding
- Built-in Providers
- helpers.get_configuration
- CustomAttributes
- Dictionary Conversion
- ConfigurationException
Fluent builder that assembles one or more ConfigurationProvider objects into a single Configuration.
ConfigurationBuilder(normalize: bool = False, scrubkeys: bool = False) -> ConfigurationBuilder| Parameter | Effect |
|---|---|
normalize=True |
Normalises all attribute names to UPPER-CASE on the resulting Configuration |
scrubkeys=True |
Scrubs attribute names to be lexically valid Python identifiers (replaces non-identifier chars with _) |
All return ConfigurationBuilder for fluent chaining.
builder = ConfigurationBuilder()
builder.add_provider(provider) # custom provider (most generic)
builder.add_json(filepath='s.json') # from file or string
builder.add_toml(filepath='s.toml') # from file or string
builder.add_yaml(filepath='s.yaml') # from file or string
builder.add_command_line(argv=['a=1']) # from sys.argv or custom list
builder.add_environment() # from os.environ
builder.add_environment(required_prefix='APP_') # only APP_* vars, prefix stripped.add_json('appsettings.Production.json', required=False) # silently missing
.add_toml('secrets.toml', required=True) # raises ConfigurationExceptionWhen required_prefix='PREFIX_', only environment variables starting with PREFIX_ are loaded, and that prefix is stripped from the resulting config key.
export MYAPP_DB_HOST=postgres://... # loaded as "DB_HOST"
export OTHER_KEY=value # ignoredconfig = ConfigurationBuilder()\
.add_json('base.json')\
.add_json('appsettings.Development.json', required=False)\
.add_environment()\
.build()Precedence rule: providers added later take precedence. Later providers override values from earlier providers for matching keys only.
The Configuration object exposes four equivalent ways to read hierarchical config data, using __ or : as path delimiters.
# Example hierarchy produces key "ConnectionStrings__SampleDb" or "ConnectionStrings:SampleDb"
config.ConnectionStrings.SampleDb # dynamic attribute (case-sensitive)
config.get('ConnectionStrings__SampleDb') # get() API (case-insensitive)
config.get('ConnectionStrings:SampleDb') # colon-delimiter (case-insensitive)
config.get('ConnectionStrings').get('SampleDb') # chained get() (case-insensitive)
config['ConnectionStrings__SampleDb'] # indexer (case-insensitive)
config['ConnectionStrings']['SampleDb'] # chained indexer (case-insensitive)__ and : are equivalent and both case-insensitive. Attribute access is case-sensitive.
config.set('Section__Key', 'value') # hierarchical key
config['Section__Key'] = 'value' # same via setter| Member | Signature | Description |
|---|---|---|
get(key, default=None) |
Any |
Get value with fallback |
set(key, value) |
None |
Set a (possibly nested) key |
has_key(key) |
bool |
Check existence (case-insensitive) |
keys() |
list[str] |
All top-level keys |
items() |
list[tuple[str, Any]] |
Key-value pairs |
values() |
list[Any] |
All values |
pop(key) |
Any |
Retrieve and delete |
clear() |
None |
Remove all keys |
__delitem__(key) |
None |
Delete by key |
__iter__() |
Iterator[str] |
Iterate keys |
__len__() |
int |
Number of keys |
Map configuration data onto strongly-typed Python classes with type hints. The binding is case-insensitive and performs primitive coercion.
from appsettings2 import ConfigurationBuilder
from appsettings2.providers import JsonConfigurationProvider
config = ConfigurationBuilder()\
.add_json(json='''{
"ConnectionStrings": {"SampleDb": "conn_str_here"},
"EnableSwagger": true,
"MaxBatchSize": 100
}''')\
.build()
class ConnStrs:
SampleDB: str
class AppSettings:
ConnectionStrings: ConnStrs
EnableSwagger: bool
MaxBatchSize: int
settings = AppSettings()
config.bind(settings)
print(settings.ConnectionStrings.SampleDB) # "conn_str_here"Bind only a slice of the configuration using the key parameter:
conn_strs = config.bind(ConnStrs(), 'ConnectionStrings')
print(conn_strs.SampleDB)Returns the target object (for method chaining).
from typing import List
class Node:
host: str
port: int
class ClusterSettings:
servers: dict[str, Node]
nodes: list[Node]
ports: set[int]
config.bind(ClusterSettings())dict[str, SomeClass]— values are rehydrated into instances ofSomeClasslist[SomeClass]— each element is rehydrated into an instance ofSomeClassset[SomeClass]— each element is rehydrated into an instance ofSomeClassdict[str, str]— remains a plain dict (no rehydration)list[int]/set[str]— elements coerced to the element type
from typing import Optional
class Settings:
maybe_servers: Optional[dict[str, InnerNode]] # None if absent in configBinding converts config values to match type hints:
| Target type | Coercion |
|---|---|
int |
int(value) |
float |
float(value) |
str |
str(value) |
bool |
bool(value) |
When both the ConfigurationBuilder and the Configuration use scrubkeys=True or normalize=True, binding matches scrubbed/normalised names:
config = ConfigurationBuilder(normalize=True, scrubkeys=True).build()
config.set('X-foo.1', 'hello') # stored as 'X_FOO_1'
class Target:
x_foo_1: str
config.bind(Target()) # Target.x_foo_1 == 'hello'All providers subclass the abstract ConfigurationProvider:
class ConfigurationProvider(ABC):
@abstractmethod
def populate_configuration(self, configuration: Configuration) -> None:
...from appsettings2.providers import JsonConfigurationProvider
# From file path (required by default)
JsonConfigurationProvider(filepath='appsettings.json')
# From JSON string
JsonConfigurationProvider(json='{"key": "value"}')
# From file descriptor (int)
JsonConfigurationProvider(fd=io.open('appsettings.json').fd)
# Optional / environment-override pattern
JsonConfigurationProvider(filepath='appsettings.Production.json', required=False)Hierarchical JSON flattens to __-delimited keys. Nested dicts produce nested Configuration objects.
from appsettings2.providers import YamlConfigurationProvider
YamlConfigurationProvider(filepath='appsettings.yaml')
YamlConfigurationProvider(yaml='key: value')Same interface and behavior as JSON provider, using yaml.safe_load. Requires pyyaml.
from appsettings2.providers import TomlConfigurationProvider
TomlConfigurationProvider(filepath='appsettings.toml')
TomlConfigurationProvider(toml='key = "value"')Same interface as JSON provider, using tomllib. No extra dependency — stdlib in Python 3.11+.
from appsettings2.providers import EnvironmentConfigurationProvider
# All env vars become config keys
EnvironmentConfigurationProvider()
# Only vars with prefix (prefix stripped in output)
EnvironmentConfigurationProvider(required_prefix='APP_')from appsettings2.providers import CommandLineConfigurationProvider
# Uses sys.argv
CommandLineConfigurationProvider()
# Custom argv
CommandLineConfigurationProvider(argv=['--key=value', 'flag'])Parses --key=value pairs and --flag switches (set to True). Keys may be key__nested for hierarchy.
from appsettings2 import Configuration
from appsettings2.providers import ConfigurationProvider
class RedisConfigurationProvider(ConfigurationProvider):
def populate_configuration(self, configuration: Configuration) -> None:
import redis
client = redis.Redis()
configuration.set('Redis__Host', client.config_get('host')['host'])Most providers are under 20 lines of logic.
Convenience function for the common case of loading from well-known file variations, CLI args, and environment variables in a single call.
import appsettings2
config = appsettings2.get_configuration()| Parameter | Default | Description |
|---|---|---|
basename |
'appsettings' |
Base filename for file-based providers |
json |
True |
Load JSON variations |
toml |
True |
Load TOML variations |
yaml |
True |
Load YAML variations |
cli |
True |
Load from command-line args |
environment |
True |
Load from environment variables |
variations |
['', 'prod', 'production', 'stage', 'staging', 'qa', 'dev', 'development', 'local'] |
Filename suffixes (e.g. appsettings.Development.json) |
env_prefix |
None |
If set, only load env vars with this prefix (stripped from key) |
# Default: loads appsettings.json, appsettings.dev.json, etc.
config = appsettings2.get_configuration()
# Custom base name and skip TOML
config = appsettings2.get_configuration('myapp', toml=False)
# Custom variation list (dev only, nothing else)
config = appsettings2.get_configuration('appsettings', variations=['dev', 'staging'])
# Only load env vars with prefix, skip all file-based providers
config = appsettings2.get_configuration(
json=False, toml=False, yaml=False, cli=False,
environment=True, env_prefix='MYAPP_'
)
# pathlib.Path support
from pathlib import Path
config = appsettings2.get_configuration(Path('configs') / 'settings')Each variation file is loaded with required=False so missing files are silently skipped.
When normalize=True on the builder, all keys are stored in UPPER-CASE on the resulting Configuration object. Attribute access becomes case-insensitive too.
config = ConfigurationBuilder(normalize=True).build()
config.set('Section__Key', 'value')
print(config.SECTION__KEY) # 'value'When scrubkeys=True, keys are rewritten to be valid Python identifiers — non-identifier characters are replaced with _.
config = ConfigurationBuilder(scrubkeys=True).build()
config.set('X-foo.bar', 'hello')
print(config.X_FOO_BAR) # 'hello'd = config.to_dict()Returns a recursive copy. Nested Configuration objects become nested dicts.
from appsettings2 import Configuration
config = Configuration.from_dict({
'Section': {
'Key1': 'value1',
'Key2': 42
}
})
# With custom options
config = Configuration.from_dict(data, normalize=True, scrubkeys=True)from appsettings2 import ConfigurationException
try:
config = ConfigurationBuilder()\
.add_json('missing_required.json', required=True)\
.build()
except ConfigurationException as e:
print(e.reason) # "Missing required file: missing_required.json"Raised when a required configuration source (file) is not found.
| Symbol | Module | Description |
|---|---|---|
Configuration |
appsettings2 |
Main config object |
ConfigurationBuilder |
appsettings2 |
Builder with fluent API |
ConfigurationException |
appsettings2 |
Error type |
get_configuration() |
appsettings2 |
Quick-start helper |
helpers |
appsettings2 |
Namespace for helpers module |
providers |
appsettings2 |
Namespace for provider modules |
ConfigurationProvider |
appsettings2.providers |
Abstract base for custom providers |
JsonConfigurationProvider |
appsettings2.providers |
JSON file/string |
YamlConfigurationProvider |
appsettings2.providers |
YAML file/string |
TomlConfigurationProvider |
appsettings2.providers |
TOML file/string |
EnvironmentConfigurationProvider |
appsettings2.providers |
os.environ |
CommandLineConfigurationProvider |
appsettings2.providers |
CLI args |