Decorator API
No inheritance required — use @directed_inputs on any 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.
pip install directed-inputs-classDecorator 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 osfrom 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}
# Usageservice = UserService()result = service.get_user() # Loads from environmentprint(result) # {"id": "12345", "debug": True}For each method parameter, inputs are resolved in this order:
Explicit argument — service.method(domain="example.com") always wins
Stdin JSON — If from_stdin=True and key matches parameter name
Environment variable — If from_env=True (checks DOMAIN or env_prefix + DOMAIN)
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)@directed_inputsClass 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: ...@input_configMethod 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 Hint | Coercion |
|---|---|
bool | "true"/"1"/"yes" → True, "false"/"0"/"no" → False |
int | "42" → 42 |
float | "3.14" → 3.14 |
Path | "/tmp/file" → Path("/tmp/file") |
datetime | ISO format string → datetime object |
dict | JSON string → parsed dict |
list | JSON string → parsed list |
str | None | Handles Optional types correctly |
import osfrom 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 osfrom 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 providedimport osimport base64from directed_inputs_class import directed_inputs, input_config
# Set encoded valuecert_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 osfrom 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:
echo '{"user": "alice", "count": 5}' | python script.pyfrom 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 valueSave 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,)| Field | Type | Default | Description |
|---|---|---|---|
name | str | required | Parameter name |
source_name | str | None | None | Alternative env var name |
aliases | list[str] | [] | Alternative names to check |
required | bool | False | Raise if not found |
default | Any | None | Override default |
decode_base64 | bool | False | Decode from base64 |
decode_json | bool | False | Parse as JSON |
decode_yaml | bool | False | Parse as YAML |
is_bool | bool | False | Force boolean coercion |
is_integer | bool | False | Force integer coercion |
is_float | bool | False | Force float coercion |