Simple delimiter-based environment parsing for Pydantic Settings collections
Project description
pydantic-parsed-env
Parse common collection settings from simple env strings instead of JSON.
If you want ALLOWED_HOSTS=api.local,worker.local (not
ALLOWED_HOSTS=["api.local","worker.local"]), this package is for you.
Quickstart
pip install pydantic-parsed-env
Requires Python 3.12+.
from typing import Annotated
from pydantic import Field
from pydantic_settings import SettingsConfigDict
from pydantic_parsed_env import ParseOptions, Parsed, ParsedEnvSettings
class Settings(ParsedEnvSettings):
model_config = SettingsConfigDict(env_prefix="APP_")
hosts: Parsed[list[str]] = Field(default_factory=list[str])
ports: Annotated[
dict[str, int],
ParseOptions(kv_delimiter="="),
] = Field(default_factory=dict[str, int])
# export APP_HOSTS="api.local, worker.local"
# export APP_PORTS="http=80,https=443"
settings = Settings()
assert settings.hosts == ["api.local", "worker.local"]
assert settings.ports == {"http": 80, "https": 443}
In the simple case, you can use Parsed[dict[str, int]] and the default
kv_delimiter=":". The example uses ParseOptions(kv_delimiter="=") only to
show delimiter override.
When to use this
Use this package when you want readable delimiter-based env values for collections.
Use plain pydantic-settings JSON parsing when you need nested objects,
nullable collection items, or other complex shapes.
Why
pydantic-settings is excellent, but structured env values commonly use JSON.
That can be verbose and brittle in shell scripts, Docker env files, and ops
tooling.
pydantic-parsed-env keeps common collection config short and readable:
APP_HOSTS=api.local,worker.localAPP_FEATURE_FLAGS=true,false,trueAPP_PORTS=http:80,https:443
Core API
ParsedEnvSettings:BaseSettingssubclass that wires in the custom env source.Parsed[T]: shorthand forAnnotated[T, ParseOptions()].ParseOptions(...): annotation metadata factory for delimiter-based parsing.
ParseOptions(...) metadata alone is not enough. The custom parser is
installed via settings_customise_sources, so your settings class must inherit
from ParsedEnvSettings.
Supported parsing
ParseOptions(...) applies to:
list[T]set[T]tuple[T, ...]and fixed tuples liketuple[str, int]dict[K, V](uses defaultkv_delimiterset to:)
Supported element conversion:
str,int,float,bool(true/false)Enum/StrEnumLiteral[...]
Fields without ParseOptions(...) keep normal pydantic-settings behavior,
including JSON parsing for complex values.
Behavior matrix
| Field type | Example input | Parsed result |
|---|---|---|
list[int] |
"1,2,3" |
[1, 2, 3] |
set[str] |
"a,a,b" |
{"a", "b"} |
tuple[float, ...] |
"1.2,3.4" |
(1.2, 3.4) |
tuple[str, int] |
"host,80" |
("host", 80) |
dict[str, int] + kv_delimiter=":" |
"http:80,https:443" |
{"http": 80, "https": 443} |
Empty and malformed input semantics
-
For collection fields, unset and
""map to empty collections:list[T] -> []set[T] -> set()tuple[T, ...] -> ()dict[K, V] -> {}
-
For required fields without defaults, unset values still follow normal
pydantic-settingsrequired-field behavior. -
Noneis not inferred from empty input by default. If you need nullable collection values, use an explicit sentinel convention. -
Parsing is strict for malformed segments:
"a,,b"is invalid forlist[int]and similar non-string item types."k1:v1,broken,k2:v2"is invalid fordict[K, V].
-
Empty segments are allowed for
stritems:list[str]:"a,,b" -> ["a", "", "b"]
Dict parsing rules
- Dict fields require a key-value delimiter, for example
ParseOptions(kv_delimiter=":"). - Each pair must match
key<kv_delimiter>value, for example"k:v". - Whitespace around keys and values is trimmed before conversion.
- Duplicate keys use the last value encountered:
"a:1,a:2" -> {"a": 2}
Error behavior
At the settings integration layer, parsing errors are raised as
pydantic_settings.SettingsError (matching upstream source behavior).
Detailed parser failure context is preserved in SettingsError.__cause__.
Non-goals and limits
- Complex nested model elements (for example
list[MyModel]) are not supported by simple string parsing. - Nullable item types inside collections (for example
list[int | None]ordict[str, bool | None]) are intentionally out of scope for simple parsing. Use standard JSON-basedpydantic-settingsparsing for those shapes. - Complex item-level unions (including nullable item unions) are not supported for simple parsing.
- Applying
ParseOptions(...)to non-collection fields is a type error.
Development
Default (no Nix required):
uv sync
uv run ruff check .
uv run ruff format .
uv run pyright .
uv run pytest -q
If you use Nix, a dev shell plus formatting/check wiring is already provided:
nix develop
nix fmt
nix flake check
CI runs the same Nix commands (nix fmt and nix flake check) using
Determinate Nix + Magic Nix Cache.
License
Apache-2.0.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pydantic_parsed_env-0.1.0a1.tar.gz.
File metadata
- Download URL: pydantic_parsed_env-0.1.0a1.tar.gz
- Upload date:
- Size: 27.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4264e1ee70dd63b78e0a20305f8009900838284908f808d1146f2cea84dbc591
|
|
| MD5 |
5ed6b512182d3f996c59e7a70045a780
|
|
| BLAKE2b-256 |
c52a59992906fbbd260bb798c9426bdb940e8d4ffa9a172fa74eeaac3a9d6f43
|
Provenance
The following attestation bundles were made for pydantic_parsed_env-0.1.0a1.tar.gz:
Publisher:
release.yml on adamcik/pydantic-parsed-env
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pydantic_parsed_env-0.1.0a1.tar.gz -
Subject digest:
4264e1ee70dd63b78e0a20305f8009900838284908f808d1146f2cea84dbc591 - Sigstore transparency entry: 1281028190
- Sigstore integration time:
-
Permalink:
adamcik/pydantic-parsed-env@2a527ebe33906327beaf344a1da0217f9ba8ee95 -
Branch / Tag:
refs/tags/v0.1.0a1 - Owner: https://github.com/adamcik
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@2a527ebe33906327beaf344a1da0217f9ba8ee95 -
Trigger Event:
release
-
Statement type:
File details
Details for the file pydantic_parsed_env-0.1.0a1-py3-none-any.whl.
File metadata
- Download URL: pydantic_parsed_env-0.1.0a1-py3-none-any.whl
- Upload date:
- Size: 13.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ed7e2ba71ecdf516246d900f5537be000bea5fa43f1b508c1b299962cf2c6643
|
|
| MD5 |
1dd3e4c16ca65c80e7b9d8061ae0a692
|
|
| BLAKE2b-256 |
e2734338df95fa9d311eacffd9cd46718f3e2d5bd5377871ff6395eed9ecb545
|
Provenance
The following attestation bundles were made for pydantic_parsed_env-0.1.0a1-py3-none-any.whl:
Publisher:
release.yml on adamcik/pydantic-parsed-env
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pydantic_parsed_env-0.1.0a1-py3-none-any.whl -
Subject digest:
ed7e2ba71ecdf516246d900f5537be000bea5fa43f1b508c1b299962cf2c6643 - Sigstore transparency entry: 1281028193
- Sigstore integration time:
-
Permalink:
adamcik/pydantic-parsed-env@2a527ebe33906327beaf344a1da0217f9ba8ee95 -
Branch / Tag:
refs/tags/v0.1.0a1 - Owner: https://github.com/adamcik
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@2a527ebe33906327beaf344a1da0217f9ba8ee95 -
Trigger Event:
release
-
Statement type: