Skip to main content
Yanked

This release has been yanked by its maintainers, and will be ignored by installers, except when explicitly specified.
Consider using release 3000.7.1 instead.
Reason given by maintainers: Logs the 1Password service-account token when authentication is retried. Upgrade to 3000.7.1. See GHSA-3rmr-35mv-wrrp.

Configator logo depicting a cool gator

A convenient way to load your app configuration from 1Password.

Ruff CI status Quality Gate Status Test Coverage Lines of Code

This project is licensed under the terms of the MIT license.

Quick Start

import asyncio
import os
from configator import load_config
from pydantic import BaseModel


class DatabaseConfig(BaseModel):
    host: str
    port: int
    username: str
    password: str


class AppConfig(BaseModel):
    api_key: str
    debug: bool
    timeout: int


class Config(BaseModel):
    db: DatabaseConfig
    app: AppConfig
    debug: bool = False


async def main():
    token = os.getenv("OP_SERVICE_ACCOUNT_TOKEN")
    cfg: Config = await load_config(
        schema=Config,
        token=token,
        vault="REPO whatever",
        item="whatever-develop",
    )
    assert cfg.db.port == 5432


asyncio.run(main())

Developer Mode

For local development, you can override configuration values using a .env file by setting the CONFIGATOR_DEV_MODE environment variable:

export CONFIGATOR_DEV_MODE=1

When developer mode is enabled, values are loaded with the following priority (highest to lowest):

  1. .env file
  2. Environment variables
  3. 1Password values (via initialization parameters)

Without developer mode, the standard priority applies (1Password values take precedence over environment variables and .env files).

⚠️ Never enable developer mode in production. A stray .env file would silently override vetted secrets. Configator guards against this: if CONFIGATOR_DEV_MODE is set while ENVIRONMENT (or, as a fallback, APP_ENV) resolves to production — any value case-insensitively starting with product, e.g. product or production — instantiating a config model raises a RuntimeError. Production deployments must ensure CONFIGATOR_DEV_MODE is unset and that no .env file ships in production images.

This feature works with the provided common configuration models (PostgresConfig, SentryConfig). For your own config schemas, you can simply extend ConfigatorSettings to get this behavior.

Installation

uv add "git+https://github.com/Utiligize/configator@v3000.0.0"

or, if you like the bleeding edge:

uv add "git+https://github.com/Utiligize/configator"

For information on how to authenticate uv with GitHub, see https://docs.astral.sh/uv/concepts/authentication/git/.

For information on how to use private repos in GitHub Actions, see https://docs.astral.sh/uv/guides/integration/github/#private-repos. If you create a fine-grained access token, it simply needs the "Content" read permission.

Writing Config Classes

Define your app's config as a class deriving from Pydantic's BaseModel. The field names will be matched against the 1Password item field titles, and the values loaded from them. The field names are treated as lower snake case, and item field names in 1Password are converted accordingly when matching. For example, a Python model with a field called sentry_key will match a 1Password item with a field title of SENTRY_KEY or sentry-key. It is therefore important to ensure that field names are unique, at least within sections.

Nested models are loaded from separate sections in the 1Password item. Fields in these nested models can have the same name as fields in other sections. Fields in the base config class are found by name, no matter their section (but the intention is for them to be added without one), so the names of these must be unique in the full model.

Supported Features

  • Basic types (str, int, float, even complex) are simply parsed from the string in 1Password.
  • decimal.Decimal is also supported and should usually be preferred over float.
  • Booleans are special: since any string is truthy in Python, a bool must have one of 8 (case-insensitive) values:
    • "true", "1", "yes", and "on" are interpreted as True.
    • "false", "0", "no", and "off" are interpreted as False.
    • any other value for a field defined as bool will raise a ValueError.
  • Collections (dict, list, set) are loaded by interpreting the string value in 1Password as JSON and passing that object to the constructor. This means that a set can be constructed from what looks like a list, for example.
  • Any string starting with op:// will be resolved recursively (up to a depth of 10 links). All references in the config item are resolved together, one batched request per level of nesting, so a schema with many referencing fields costs a handful of 1Password requests rather than one per field. The request count for each load is emitted as an info log line.
  • Every 1Password call is retried individually on transient failures (3 attempts, with no further attempt scheduled more than 10 seconds after the first), so a retry costs one request rather than a full reload. The cap bounds retrying, not a single hung request — the SDK exposes no request timeout. Rate-limit errors are never retried: they are logged and raised immediately, because the hourly read budget can be up to an hour from resetting and each retry would spend a request against a budget that is already empty.

Planned Features

Unsupported Features

  • Typed collections are sadly not supported, because it confuses the issubclass matching of fields. This means that fields in your config model must be defined as e.g. plain dict, not dict[str, str].

  • Optional and Union fields are not supported, i.e. you cannot do either of

    foo: str | None = None
    bar: Optional[str]
    baz: int | float
    

    because it confuses the hydrator, who won't know which constructor to call or will try to initialize None.

  • While default values are supported, default_factory is not.

  • Basic Python types bytes and bytearray may work but are not officially supported.

Development

Setup

uv sync

Lint and Format

just lint

Run Tests

just test

Run Failed Tests

just test-failed

◼️◼️◼️

Release files for configator-op 3000.6.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for configator-op 3000.6.0
File Size Uploaded
configator_op-3000.6.0.tar.gz 27.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for configator-op 3000.6.0
File Interpreter ABI Platform
configator_op-3000.6.0-py3-none-any.whl Python 3 none any Details

Total release size: 40.2 kB

Release files / configator_op-3000.6.0.tar.gz

Download URL configator_op-3000.6.0.tar.gz
Size 27.3 kB
Tags Source
SHA-256 checksum
How to use checksums
9d9c41f9c66e073a8bc0dbcc21345e53429c4041140abd807be60a40cec79a32
BLAKE2b-256 checksum
How to use checksums
7ef40943475b4e054f0d0e8ec18ec38dc42eabe26b3de069330283a4293cae4e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.2

Release files / configator_op-3000.6.0-py3-none-any.whl

Download URL configator_op-3000.6.0-py3-none-any.whl
Size 13.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
0df3649278aa56e0c6645a5e15122877d6cce14111331951319fd340b9ee228b
BLAKE2b-256 checksum
How to use checksums
c946ab05c72f0757135008afbd2e979bfa7087c3c6baaa352f1798ad718f6903
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.2

Release history Release notifications | RSS feed

This release

3000.6.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page