or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

tessl/pypi-pyaml-env

Provides yaml file parsing with environment variable resolution

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
pypipkg:pypi/pyaml-env@1.2.x

To install, run

npx @tessl/cli install tessl/pypi-pyaml-env@1.2.0

index.mddocs/

pyaml-env

A Python library that parses YAML configuration files and resolves environment variables, enabling secure configuration management by keeping sensitive data like passwords, API keys, and database credentials out of version control. The library supports custom YAML tags, default value handling, multiple environment variables per line, and provides a BaseConfig class for attribute-style configuration access.

Package Information

  • Package Name: pyaml-env
  • Package Type: pypi
  • Language: Python
  • Installation: pip install pyaml-env

Core Imports

from pyaml_env import parse_config, BaseConfig

Individual imports:

from pyaml_env import parse_config
from pyaml_env import BaseConfig

Basic Usage

from pyaml_env import parse_config, BaseConfig

# Parse YAML file with environment variable resolution
config = parse_config('path/to/config.yaml')

# Access as dictionary
username = config['database']['username']

# Or use BaseConfig for attribute-style access
config_obj = BaseConfig(config)
username = config_obj.database.username

Example YAML file with environment variables:

database:
  name: test_db
  username: !ENV ${DB_USER}
  password: !ENV ${DB_PASS}
  url: !ENV 'http://${DB_BASE_URL}:${DB_PORT}'

With environment variables set:

export DB_USER=my_user
export DB_PASS=my_password
export DB_BASE_URL=localhost
export DB_PORT=5432

Capabilities

YAML Configuration Parsing

Parse YAML configuration files with automatic environment variable resolution using custom tags.

def parse_config(
    path=None,
    data=None,
    tag='!ENV',
    default_sep=':',
    default_value='N/A',
    raise_if_na=False,
    loader=yaml.SafeLoader,
    encoding='utf-8'
):
    """
    Load yaml configuration from path or from the contents of a file (data)
    and resolve any environment variables. The environment variables
    must have the tag e.g. !ENV *before* them and be in this format to be
    parsed: ${VAR_NAME}

    Args:
        path (str, optional): Path to the yaml file
        data (str, optional): YAML data itself as a stream
        tag (str): Tag to look for environment variables (default: '!ENV'). If None, all env variables will be resolved
        default_sep (str): Separator for default values (default: ':')
        default_value (str): Default value when no environment variable or default is found (default: 'N/A')
        raise_if_na (bool): Raise exception if there is no default value set for the env variable (default: False)
        loader (Type[yaml.loader]): YAML loader to use (default: yaml.SafeLoader)
        encoding (str): Encoding of the data if a path is specified (default: 'utf-8')

    Returns:
        dict: Parsed configuration dictionary with resolved environment variables

    Raises:
        ValueError: If neither path nor data is provided, or if raise_if_na=True and no default value is found for an environment variable
    """

Usage Examples:

File-based parsing:

config = parse_config(path='config.yaml')

String-based parsing:

yaml_data = '''
database:
  host: !ENV ${DB_HOST:localhost}
  port: !ENV ${DB_PORT:5432}
'''
config = parse_config(data=yaml_data)

Custom tag and defaults:

config = parse_config(
    path='config.yaml',
    tag='!CUSTOM',
    default_sep='|',
    default_value='missing',
    raise_if_na=True
)

Environment Variable Syntax:

Basic environment variable:

username: !ENV ${DB_USER}

With default value:

username: !ENV ${DB_USER:default_user}
port: !ENV ${DB_PORT:5432}

Multiple variables in one line:

url: !ENV 'http://${HOST:localhost}:${PORT:8080}/api'

Type conversion with YAML tags:

port: !ENV tag:yaml.org,2002:int ${DB_PORT:5432}
enabled: !ENV tag:yaml.org,2002:bool ${FEATURE_ENABLED:false}
ratio: !ENV tag:yaml.org,2002:float ${RATIO:1.5}

Attribute-Style Configuration Access

Provides attribute-style access to configuration dictionaries, allowing dot notation for nested configuration access.

class BaseConfig:
    """
    A base Config class providing attribute-style access to configuration dictionaries.
    Recursively converts nested dictionaries to BaseConfig objects.
    """
    
    def __init__(self, config_dict):
        """
        Initialize BaseConfig with a configuration dictionary.
        Copies existing class attributes, updates with config_dict, 
        and recursively converts nested dictionaries to BaseConfig objects.
        
        Args:
            config_dict (dict): Configuration dictionary to wrap
        """

    def __getattr__(self, field_name: str) -> Any:
        """
        Provides attribute-style access to configuration values.
        
        Args:
            field_name (str): Name of the configuration field
            
        Returns:
            Any: The configuration value
        """

    @property
    def errors(self):
        """
        Returns:
            list: List of validation errors
        """
        
    @property
    def _is_validated(self):
        """
        Returns:
            bool: Whether validation has been performed
        """
        
    @property
    def _is_valid(self):
        """
        Returns:
            bool: Whether configuration passed validation
        """
        
    @property
    def _errors(self):
        """
        Returns:
            list: Internal list of validation errors
        """

    def validate(self):
        """
        Abstract method for configuration validation (must be implemented by subclasses).
        
        Raises:
            NotImplementedError: This method must be implemented in subclasses
        """

Usage Examples:

Basic attribute access:

from pyaml_env import parse_config, BaseConfig

config = BaseConfig(parse_config('config.yaml'))

# Dot notation access instead of dictionary syntax
host = config.database.host
port = config.database.port
username = config.database.username

Nested configuration access:

# YAML:
# app:
#   database:
#     primary:
#       host: !ENV ${PRIMARY_DB_HOST}
#       port: !ENV ${PRIMARY_DB_PORT:5432}
#     cache:
#       redis_url: !ENV ${REDIS_URL}

config = BaseConfig(parse_config('config.yaml'))
primary_host = config.app.database.primary.host
redis_url = config.app.database.cache.redis_url

Custom validation (subclass implementation):

class DatabaseConfig(BaseConfig):
    def validate(self):
        if not hasattr(self, 'host'):
            self._errors.append("Missing required field: host")
        if hasattr(self, 'port') and not isinstance(self.port, int):
            self._errors.append("Port must be an integer")
        self._is_validated = True
        self._is_valid = len(self._errors) == 0

db_config = DatabaseConfig(parse_config('db_config.yaml'))
db_config.validate()
if not db_config._is_valid:
    print("Configuration errors:", db_config.errors)

Advanced Features

Custom YAML Loaders

Support for different YAML loaders including UnsafeLoader for serialized Python objects:

import yaml
from pyaml_env import parse_config

# Using UnsafeLoader for Python objects
config = parse_config(path='config.yaml', loader=yaml.UnsafeLoader)

# Using FullLoader
config = parse_config(path='config.yaml', loader=yaml.FullLoader)

Error Handling Modes

Control behavior when environment variables are missing:

# Strict mode - raise exception if no default provided
try:
    config = parse_config('config.yaml', raise_if_na=True)
except ValueError as e:
    print(f"Missing environment variable: {e}")

# Permissive mode - use default_value for missing variables
config = parse_config('config.yaml', default_value='MISSING')

Environment Variable Resolution Rules

  1. Tag Resolution: Only values with the specified tag (default !ENV) are processed
  2. Variable Extraction: Variables must use ${VARIABLE_NAME} syntax
  3. Default Handling: Use separator (default :) for defaults: ${VAR:default}
  4. Fallback Chain: Environment variable → default value → global default_value
  5. Type Conversion: Support for YAML type tags like tag:yaml.org,2002:int

Types

from typing import Any, Type
import yaml

# Type aliases for function parameters
LoaderType = Type[yaml.loader]
ConfigDict = dict