Skip to main content

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+.

Runtime dependency policy:

  • Published runtime dependencies use tested compatibility ranges, not exact pins.
  • Supported runtime dependency ranges are declared in the package metadata.
  • Exact versions for development are managed separately in the lockfile/tooling.
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.local
  • APP_FEATURE_FLAGS=true,false,true
  • APP_PORTS=http:80,https:443

Core API

  • ParsedEnvSettings: BaseSettings subclass that wires in the custom env source.
  • Parsed[T]: shorthand for Annotated[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 like tuple[str, int]
  • dict[K, V] (uses default kv_delimiter set to :)

Supported element conversion:

  • str, int, float, bool (true / false)
  • Enum / StrEnum
  • Literal[...]

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-settings required-field behavior.

  • None is 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 for list[int] and similar non-string item types.
    • "k1:v1,broken,k2:v2" is invalid for dict[K, V].
  • Empty segments are allowed for str items:

    • 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] or dict[str, bool | None]) are intentionally out of scope for simple parsing. Use standard JSON-based pydantic-settings parsing 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.

Versioning and releases

  • Package versions are derived from Git tags via hatch-vcs.
  • Release tags must use the vX.Y.Z form, for example v0.3.1.
  • The published package version strips the v prefix, so v0.3.1 becomes 0.3.1 on PyPI.
  • This project follows semantic versioning for its public API. While the package is still 0.x, breaking changes may still land in minor releases.

License

Apache-2.0.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pydantic_parsed_env-0.1.0.tar.gz (45.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pydantic_parsed_env-0.1.0-py3-none-any.whl (14.1 kB view details)

Uploaded Python 3

File details

Details for the file pydantic_parsed_env-0.1.0.tar.gz.

File metadata

  • Download URL: pydantic_parsed_env-0.1.0.tar.gz
  • Upload date:
  • Size: 45.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for pydantic_parsed_env-0.1.0.tar.gz
Algorithm Hash digest
SHA256 1eba464e1a1edeaa39e228a16d0d24de9583d908b0dfd9350b962244ff69671b
MD5 8831e18cc84304e773a4c58e18baf69f
BLAKE2b-256 6f4c777a842b0aed69f767ec3605e86448ecb4d551f7ca23928f14ee99ef011c

See more details on using hashes here.

Provenance

The following attestation bundles were made for pydantic_parsed_env-0.1.0.tar.gz:

Publisher: release.yml on adamcik/pydantic-parsed-env

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pydantic_parsed_env-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for pydantic_parsed_env-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 4148bdd2890d4bec6b4077e529eda07c812b08eeb54c536f5421fad3f74be829
MD5 398f41ee746a1aed74b5f8ff46cad08d
BLAKE2b-256 9db54aac8bac72cc02c1f6e1e7eca95903691df70bf106828817e88c07398c00

See more details on using hashes here.

Provenance

The following attestation bundles were made for pydantic_parsed_env-0.1.0-py3-none-any.whl:

Publisher: release.yml on adamcik/pydantic-parsed-env

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page