Skip to content

Directed Inputs Class

Directed Inputs Class (directed-inputs-class) provides flexible, transparent input handling for Python applications. Load inputs from environment variables, stdin, or dictionaries and have them automatically injected into your method arguments.

Terminal window
pip install directed-inputs-class

PyPI Python CI


Decorator API

No inheritance required — use @directed_inputs on any class

Type Coercion

Automatic string → bool, int, float, datetime, Path

Multi-Source

Environment variables, stdin JSON, or dictionaries

Per-Method Config

Fine-grained control with @input_config


import os
from directed_inputs_class import directed_inputs, input_config
os.environ["USER_ID"] = "12345"
os.environ["DEBUG"] = "true"
os.environ["PORT"] = "8080"
@directed_inputs(from_env=True)
class UserService:
"""Methods automatically receive inputs from environment."""
def get_user(self, user_id: str, debug: bool = False) -> dict:
# user_id populated from USER_ID environment variable
# debug coerced from "true" string to True boolean
return {"id": user_id, "debug": debug}
@input_config("api_key", source_name="API_KEY", required=True)
def secure_operation(self, api_key: str, data: dict) -> dict:
# api_key MUST be provided (from env var API_KEY)
return {"key": api_key[:4], "data": data}
# Usage
service = UserService()
result = service.get_user() # Loads from environment
print(result) # {"id": "12345", "debug": True}

For each method parameter, inputs are resolved in this order:

  1. Explicit argumentservice.method(domain="example.com") always wins

  2. Stdin JSON — If from_stdin=True and key matches parameter name

  3. Environment variable — If from_env=True (checks DOMAIN or env_prefix + DOMAIN)

  4. Default value — Falls back to parameter default

@directed_inputs(from_env=True, from_stdin=True)
class Config:
def get_setting(self, timeout: int = 30) -> int:
return timeout
config = Config()
# Resolution order:
# 1. config.get_setting(timeout=60) → 60 (explicit)
# 2. stdin: {"timeout": 45} → 45 (stdin)
# 3. TIMEOUT=90 → 90 (env)
# 4. (nothing set) → 30 (default)

Class decorator that enables automatic input handling for all methods:

@directed_inputs(
from_stdin=False, # Read JSON from stdin on first method call
from_env=True, # Load matching environment variables
from_environment=True, # Alias for from_env
env_prefix=None, # Filter env vars by prefix (e.g., "MY_APP_")
strip_env_prefix=False, # Strip prefix from keys (MY_APP_FOO → FOO)
)
class MyService:
...

Method decorator for per-parameter configuration:

@input_config(
"param_name", # Parameter to configure
source_name=None, # Alternative source name (e.g., "API_KEY" → api_key param)
aliases=None, # List of alternative names to check
required=False, # Raise error if not found
default=None, # Override default value
decode_base64=False, # Decode value from base64
decode_json=False, # Parse value as JSON
decode_yaml=False, # Parse value as YAML
decode_from_json=False, # Alias for decode_json
is_bool=False, # Force boolean coercion
is_integer=False, # Force integer coercion
is_float=False, # Force float coercion
)
def method(self, param_name: str):
...

String inputs from environment/stdin are automatically coerced based on type hints:

Type HintCoercion
bool"true"/"1"/"yes"True, "false"/"0"/"no"False
int"42"42
float"3.14"3.14
Path"/tmp/file"Path("/tmp/file")
datetimeISO format string → datetime object
dictJSON string → parsed dict
listJSON string → parsed list
str | NoneHandles Optional types correctly
import os
from directed_inputs_class import directed_inputs
os.environ["DEBUG"] = "true"
os.environ["PORT"] = "8080"
os.environ["TIMEOUT"] = "30.5"
@directed_inputs(from_env=True)
class Config:
def get_settings(
self,
debug: bool = False,
port: int = 3000,
timeout: float = 60.0
) -> dict:
return {"debug": debug, "port": port, "timeout": timeout}
config = Config()
print(config.get_settings())
# {"debug": True, "port": 8080, "timeout": 30.5}

import os
from directed_inputs_class import directed_inputs
os.environ["MY_APP_DATABASE_URL"] = "postgres://..."
os.environ["MY_APP_DEBUG"] = "true"
os.environ["OTHER_VAR"] = "ignored"
@directed_inputs(
from_env=True,
env_prefix="MY_APP_",
strip_env_prefix=True, # Strip prefix from parameter names
)
class AppConfig:
def get_db(self, database_url: str | None = None) -> str:
# Only MY_APP_* variables loaded
# Prefix stripped: MY_APP_DATABASE_URL → DATABASE_URL
return database_url
config = AppConfig()
print(config.get_db()) # "postgres://..."
from directed_inputs_class import directed_inputs, input_config
@directed_inputs(from_env=True)
class SecureService:
@input_config("api_key", required=True)
def call_api(self, api_key: str, endpoint: str = "/api") -> dict:
# Raises ValueError if api_key not in env/stdin
return {"key": api_key[:4], "endpoint": endpoint}
service = SecureService()
# If API_KEY not set: ValueError: Required input 'api_key' not provided
import os
import base64
from directed_inputs_class import directed_inputs, input_config
# Set encoded value
cert_data = "certificate-content-here"
os.environ["CERT"] = base64.b64encode(cert_data.encode()).decode()
@directed_inputs(from_env=True)
class TLSClient:
@input_config("cert", decode_base64=True)
def connect(self, cert: str) -> None:
# cert is automatically decoded from base64
print(cert) # "certificate-content-here"
client = TLSClient()
client.connect()
import os
from directed_inputs_class import directed_inputs, input_config
os.environ["CONFIG"] = '{"host": "localhost", "port": 5432}'
@directed_inputs(from_env=True)
class Database:
@input_config("config", decode_json=True)
def connect(self, config: dict) -> str:
return f"Connecting to {config['host']}:{config['port']}"
db = Database()
print(db.connect()) # "Connecting to localhost:5432"

Process JSON piped to your script:

Terminal window
echo '{"user": "alice", "count": 5}' | python script.py
from directed_inputs_class import directed_inputs
@directed_inputs(from_stdin=True)
class Processor:
def process(self, user: str, count: int = 10) -> str:
return f"Processing {count} items for {user}"
p = Processor()
print(p.process()) # "Processing 5 items for alice"

Access the input context directly for advanced use cases:

@directed_inputs(from_env=True)
class MyService:
def debug_inputs(self):
# Access raw inputs
ctx = self._input_context
print(f"All inputs: {ctx.inputs}")
print(f"Frozen: {ctx.frozen}")
# Manual input retrieval
value = ctx.get("key", default="fallback")
return value

Save and restore input state:

from directed_inputs_class import directed_inputs
@directed_inputs(from_env=True)
class StatefulService:
def operation(self):
# Freeze current inputs (returns copy, clears internal state)
frozen = self._input_context.freeze()
# ... do something that might modify inputs ...
# Restore frozen state
self._input_context.thaw(frozen)

The original inheritance-based API is still supported:

from directed_inputs_class import DirectedInputsClass
class MyService(DirectedInputsClass):
def __init__(self):
super().__init__(from_environment=True, from_stdin=True)
def get_user(self, user_id: str | None = None) -> dict:
user_id = self.get_input("user_id", user_id)
return {"id": user_id}
# Before (inheritance + manual get_input)
class OldService(DirectedInputsClass):
def method(self, domain: str | None = None):
domain = self.get_input("domain", domain)
return self._process(domain)
# After (decorator + automatic injection)
@directed_inputs(from_env=True)
class NewService:
def method(self, domain: str | None = None):
# domain is automatically populated!
return self._process(domain)

from directed_inputs_class import (
# Decorator API (recommended)
directed_inputs, # Class decorator
input_config, # Method decorator
InputConfig, # Configuration dataclass
InputContext, # Runtime context
# Legacy API
DirectedInputsClass,
)
FieldTypeDefaultDescription
namestrrequiredParameter name
source_namestr | NoneNoneAlternative env var name
aliaseslist[str][]Alternative names to check
requiredboolFalseRaise if not found
defaultAnyNoneOverride default
decode_base64boolFalseDecode from base64
decode_jsonboolFalseParse as JSON
decode_yamlboolFalseParse as YAML
is_boolboolFalseForce boolean coercion
is_integerboolFalseForce integer coercion
is_floatboolFalseForce float coercion