Skip to main content

ConfigParser with environment variable and direct assignment overrides

Project description

ConfigParser Override

Version Build Status

ConfigParserOverride enhances the python standard library built-in ConfigParser by allowing you to override or add new options using; environment variables and directly assigned key-value arguments.

NOTE: This package only depends on the Python Standard Library!

Features

  • Override configuration options with environment variables.
  • Override configuration options with directly assigned arguments.
  • Convert configuration object to a dataclass and cast the values to predefined datatypes.
  • Find and collect configuration files in conventional locations based on your operating system.

Install

pip install configparser-override

Usage

Example of how to use ConfigParserOverride:

Example config.ini File

[DEFAULT]
default_key1 = default_value1
default_key2 = default_value2

[section1]
key1 = value1
key2 = value2

[section2]
key3 = value3
key4 = value4

Python Code

import os

from configparser_override import ConfigParserOverride

# Optionally set environment variables for overriding
os.environ["MYAPP_DEFAULT_KEY1"] = "overridden_default_value1"
os.environ["MYAPP_SECTION1__KEY1"] = "overridden_value1"
os.environ["MYAPP_SECTION2__KEY3"] = "overridden_value3"

# Initialize the parser with an optional environment variable prefix and
# overrides from direct assignments.
parser = ConfigParserOverride(
    env_prefix="MYAPP_",
    # Sections & options are case insensitive by default
    SECTION2__KEY4="direct_override_value4",
    section2__key5="direct_override_value5",
)

# Read configuration from a file
parser.read(filenames="config.ini")

# Apply overrides
parser.apply_overrides()

# Access the configuration
print(config.defaults()["default_key1"])  # Output: overridden_default_value1
print(config.defaults()["default_key2"])  # Output: default_value2
print(config["section1"]["key1"])  # Output: overridden_value1
print(config["section1"]["key2"])  # Output: value2
print(config["section2"]["key3"])  # Output: overridden_value3
print(config["section2"]["key4"])  # Output: direct_override_value4
print(config["section2"]["key5"])  # Output: direct_override_value5

Configuration source precedence

Configuration options can be overridden in three ways. This is the order of precedence:

  1. Directly assigned arguments during initialization of the ConfigParserOverride class.
  2. Environment variables.
  3. Configuration files.

Environment variable configuration

To override configuration options, use environment variables with the following format. Separate sections and options using double underscores (__):

  • With Prefix (MYAPP_ as an example):

    • For DEFAULT section: [PREFIX][OPTION]
    • For other sections: [PREFIX][SECTION]__[OPTION]
  • No Prefix:

    • For DEFAULT section: [OPTION]
    • For other sections: [SECTION]__[OPTION]

Example:

  • To override key1 in section1 with prefix MYAPP_, use MYAPP_SECTION1__KEY1.

Find and collect configuration files

The library also contains a helper function config_file_collector that will search for configuration files in conventional locations based on your OS. The collected files can then be used as input to ConfigParserOverride.read()

Searched paths

Linux and MacOS

Unix systems follows XDG base directory specification and used environment variables:

  • XDG_CONFIG_HOME (User config)
    • Default to $HOME/.config
  • XDG_CONFIG_DIRS (System wide config)
    • List of directories separated by semicolon :
    • Default to /etc/xdg

Windows

Windows paths are specific in environment variables:

  • APPDATA (User config)
    • Usually: C:\Users\USERNAME\AppData\Roaming
  • PROGRAMDATA (System wide config)
    • Usually: C:\ProgramData

Example

from configparser_override import ConfigParserOverride, config_file_collector

collected_files = config_file_collector(file_name="config.ini", app_name="myapp")

print(collected_files)
# For Linux and MacOS
# Output: ["/etc/xdg/myapp/config.ini", "/home/USERNAME/.config/myapp/config.ini"]

# For Windows
# Output: ["C:/ProgramData/myapp/config.ini", "C:/Users/USERNAME/AppData/Roaming/myapp/config.ini"]

parser = ConfigParserOverride()
parser.read(filenames=collected_files)
parser.apply_overrides()
config = parser.config

Convert to a Dataclass and Validate Data Types

The library features a ConfigConverter class, which enables the conversion of configuration data into a dataclass instance. This functionality is particularly useful for ensuring that the configuration adheres to the expected format, since it tries to cast the option in the config to the types in the dataclass. Hence, it also allows you to take advantage of various typing frameworks and tools, such as integrations with your text editor, providing enhanced validation and code assistance.

Example

from dataclasses import dataclass
from typing import Optional

from configparser_override import ConfigParserOverride


@dataclass
class Section1:
    key1: int
    key2: list[str]
    key3: Optional[str] = None


@dataclass
class ExampleConfig:
    section1: Section1


# Initialize the parser with overrides
parser = ConfigParserOverride(
    section1__key1="42", section1__key2="['item1', 'item2']"
)

# Read configuration from **optional** file
parser.read(filenames=[])

# Apply overrides
parser.apply_overrides()

# Convert to dataclass
config_as_dataclass = parser.to_dataclass(ExampleConfig)

print(config_as_dataclass.section1.key1)  # Output: 42
print(type(config_as_dataclass.section1.key1))  # Output: <class 'int'>
print(config_as_dataclass.section1.key2)  # Output: ['item1', 'item2']
print(type(config_as_dataclass.section1.key2))  # Output: <class 'list'>
print(config_as_dataclass.section1.key3)  # Output: None

Data Types

Supported data types are:

  • String
  • Integer
  • Bool
  • Float
  • Complex
  • Bytes
  • pathlib.Path

Collections (nesting is supported):

  • List
  • Dict
  • Set
  • Tuple

Others:

  • None
  • Optional | Option does not need to exist in config
  • Union | Tries to cast until successful, in the order the types are specified
  • Any | no type cast

Built-in custom types:

  • SecretType (abstract): Custom abstract type that masks the secret value when converted to a string. Use SecretType.get_secret_value() to retrieve the actual value.
    • SecretStr | Implementation for strings
    • SecretBytes | Implementation for bytes

Experimental Support for Arbitrary Types:

The converter offers an experimental option to accept any object that can be initialized with a single, unnamed string argument. To enable this feature, set allow_custom_types = True when using the converter (the default is False).

For example, a compatible object initialization might look like this: MyCustomType("string value from config").

Platform Dependency

Different operating systems handle environment variables differently. Linux is case sensitive while Windows is not. See os.environ docs. Hence, it is safest to always use capitalized environment variables to avoid any unexpected behavior.

Recommendation

In order to avoid any unanticipated issues and make your code safe to run on any platform, follow these rules:

Element Recommended Case
Environment variables UPPERCASE
Environment variable prefix UPPERCASE
DEFAULT section in config.ini (as per convention in the standard library ConfigParser) UPPERCASE
Sections in config.ini files lowercase
Options in config.ini files lowercase
Directly assigned arguments lowercase

Case Sensitivity Handling

By default, ConfigParserOverride tries to stores everything as lowercase, with the exception of Section headers that are read from configuration files, where the existing casing in the file is honored. However, if you want to override such a section with an environment variable or direct assignment, it will recognize the existing casing of the section and continue to use that even though you use other casing in the override method.

It is highly discouraged, but you can make ConfigParserOverride case-sensitive by initializing it with the argument case_sensitive_overrides=True.

from configparser_override import ConfigParserOverride

parser = ConfigParserOverride(env_prefix="MYAPP_", case_sensitive_overrides=True)

Project details


Download files

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

Source Distribution

configparser_override-0.11.0.tar.gz (27.1 kB view details)

Uploaded Source

Built Distribution

configparser_override-0.11.0-py3-none-any.whl (18.8 kB view details)

Uploaded Python 3

File details

Details for the file configparser_override-0.11.0.tar.gz.

File metadata

File hashes

Hashes for configparser_override-0.11.0.tar.gz
Algorithm Hash digest
SHA256 ae8521170780af5f12589df94db2665cf00277b13b381e13ae01955dea378503
MD5 c55634ec2530d76f139322f21fda391c
BLAKE2b-256 aec04e0f326c717847b8e7156a24fd702427d866d8be69f2fce306a3eb696bd0

See more details on using hashes here.

Provenance

The following attestation bundles were made for configparser_override-0.11.0.tar.gz:

Publisher: publish-pypi.yaml on RicNord/configparser-override

Attestations:

File details

Details for the file configparser_override-0.11.0-py3-none-any.whl.

File metadata

File hashes

Hashes for configparser_override-0.11.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f963503c92613e7dacb810f21d67bf2f567fb32f3d6f41c76541bc37b85bc033
MD5 dfa006990011102f6f325db6c22a24f3
BLAKE2b-256 317e499df998845f7a63d6e6a2aceb35e924b5343b45bf23600d94df7867695d

See more details on using hashes here.

Provenance

The following attestation bundles were made for configparser_override-0.11.0-py3-none-any.whl:

Publisher: publish-pypi.yaml on RicNord/configparser-override

Attestations:

Supported by

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