multinut
The multitool nobody asked for. Includes stuff and so.
multinut.env
A simple but flexible environment loader.
Supports .env file parsing with optional mode suffixes (e.g. .env.production, .env.testing, etc.), lazy loading, and dynamic access.
Features
-
Mode-based config support (
.env.production,.env.testing, etc.) -
Access via:
env["KEY"]env.get("KEY", ...)env.KEY
-
Optional type casting (
str,bool,list,dict, etc.) -
Sane default handling
-
Does not mutate
os.environ
Use cases
- Loading
.envfiles in mode-aware Python projects - Separating secrets and configs by deployment context
- Dynamically reading values like
env.DB_URL,env.get("DEBUG", default=False, cast=cast_bool) - Avoiding
os.environpollution
Basic Example
from multinut.env import Environment, Modes
env = Environment(env_file_name=".env", mode=Modes.DEVELOPMENT)
print(env.get("DEBUG", default=False))
print(env["API_KEY"])
print(env.DB_URL)
Given a .env.development file:
DEBUG=true
API_KEY=secret
DB_URL=https://example.com
Smart Casts Example
from multinut.env import (
Environment, cast_bool, cast_int, cast_float,
cast_list, cast_tuple, cast_dict, cast_none_or_str
)
env = Environment()
print("INT:", env.get("PORT", cast=cast_int)) # -> int
print("FLOAT:", env.get("PI", cast=cast_float)) # -> float
print("BOOL:", env.get("ENABLED", cast=cast_bool)) # -> bool
print("LIST:", env.get("NUMBERS", cast=cast_list)) # -> list[str]
print("TUPLE:", env.get("WORDS", cast=cast_tuple)) # -> tuple[str]
print("DICT:", env.get("CONFIG", cast=cast_dict)) # -> dict
print("NONE_OR_STR:", env.get("OPTIONAL", cast=cast_none_or_str)) # -> None or str
Example .env:
PORT=8080
PI=3.1415
ENABLED=yes
NUMBERS=1,2,3
WORDS=hello,world,test
CONFIG={"timeout": 30, "debug": true}
OPTIONAL=null
Included Cast Helpers
All built-in cast functions handle common edge cases:
| Cast Function | Description |
|---|---|
cast_str |
Ensures string |
cast_int |
Converts to integer |
cast_float |
Converts to float |
cast_bool |
Accepts 1, true, yes, on, etc. |
cast_list |
Comma-split list |
cast_tuple |
Comma-split, converted to tuple |
cast_dict |
Parses JSON string into dictionary |
cast_none_or_str |
Returns None if value is null or None |
EnviromentKeyMissing
if a key is get from env.get and it has no default given it will raise
EnviromentKeyMissing(f"Environment variable '{key}' not found.")
multinut.funky
A type-aware method overloading system for Python classes.
Provides a decorator-based approach to method overloading that dispatches based on argument types, allowing multiple implementations of the same method with different type signatures.
Features
- Type-based method dispatch using Python type annotations
- Support for both exact type matching (
type(value) is ann) and inheritance-based matching (isinstance(value, ann)) - Automatic signature binding and validation
- Clean decorator syntax with
@overload - Descriptor protocol support for seamless integration with class methods
Use cases
- Creating polymorphic methods that behave differently based on argument types
- Building APIs with type-specific implementations
- Implementing mathematical operations that work with different numeric types
- Creating flexible data processing methods that handle various input formats
Basic Example
from multinut.funky import Dispatcher, overload
class Calculator:
add = Dispatcher("add")
@overload(add)
def add(self, x: int, y: int) -> int:
return x + y
@overload(add)
def add(self, x: str, y: str) -> str:
return x + y
@overload(add)
def add(self, x: list, y: list) -> list:
return x + y
calc = Calculator()
print(calc.add(1, 2)) # -> 3 (int)
print(calc.add("a", "b")) # -> "ab" (str)
print(calc.add([1], [2])) # -> [1, 2] (list)
Complex Type Dispatch Example
from multinut.funky import Dispatcher, overload
from typing import Union
class DataProcessor:
process = Dispatcher("process")
@overload(process)
def process(self, data: str) -> str:
return f"Processing string: {data.upper()}"
@overload(process)
def process(self, data: int) -> str:
return f"Processing number: {data * 2}"
@overload(process)
def process(self, data: list) -> str:
return f"Processing list of {len(data)} items"
@overload(process)
def process(self, data: dict) -> str:
return f"Processing dict with keys: {list(data.keys())}"
processor = DataProcessor()
print(processor.process("hello")) # -> "Processing string: HELLO"
print(processor.process(42)) # -> "Processing number: 84"
print(processor.process([1, 2, 3])) # -> "Processing list of 3 items"
print(processor.process({"a": 1, "b": 2})) # -> "Processing dict with keys: ['a', 'b']"
Error Handling
When no matching overload is found, a TypeError is raised with details about the failed dispatch:
# This will raise: TypeError: No matching overload for add(1.5, 2.5)
calc.add(1.5, 2.5) # No overload for float arguments
multinut.explain
An AI-powered code explanation system that adds .explain() methods to classes and functions.
Provides automatic code documentation by leveraging OpenAI's GPT models to generate human-readable explanations of Python code. Classes can inherit from Explainable to automatically gain explanation capabilities.
Features
- Automatic
.explain()method injection for classes and their methods - AI-powered code analysis using OpenAI's GPT-4
- Environment-based API key management via
.envfiles - Source code introspection and intelligent explanation generation
- Method-level and class-level explanations
- Graceful error handling for missing API keys or source code issues
Use cases
- Automatically generating documentation for complex classes
- Understanding legacy code or third-party implementations
- Creating educational materials and code walkthroughs
- Quick code review and comprehension assistance
- Onboarding new developers with self-documenting code
Basic Example
from multinut.explain import Explainable
# Set up API key from environment
Explainable.use_env(".env") # Looks for OPENAPI_KEY in .env file
class Calculator(Explainable):
def add(self, x: int, y: int) -> int:
return x + y
def multiply(self, x: int, y: int) -> int:
return x * y
# Explain the entire class
print(Calculator.explain())
# Explain individual methods
calc = Calculator()
print(calc.add.explain())
print(calc.multiply.explain())
Environment Setup
Create a .env file with your OpenAI API key:
OPENAPI_KEY=your-openai-api-key-here
Or set the API key directly:
from multinut.explain import Explainable
Explainable.API_KEY = "your-openai-api-key-here"
Complex Example
from multinut.explain import Explainable
class DataAnalyzer(Explainable):
def __init__(self, dataset):
self.dataset = dataset
self.processed_data = []
def clean_data(self, remove_nulls=True):
cleaned = [item for item in self.dataset if item is not None]
if remove_nulls:
cleaned = [item for item in cleaned if item != ""]
return cleaned
def calculate_stats(self, data):
if not data:
return {"mean": 0, "count": 0}
return {
"mean": sum(data) / len(data),
"count": len(data),
"max": max(data),
"min": min(data)
}
# Get AI explanations
analyzer = DataAnalyzer([1, 2, 3, None, 4])
print("Class explanation:")
print(DataAnalyzer.explain())
print("\nMethod explanation:")
print(analyzer.clean_data.explain())
Error Handling
The system handles various error conditions gracefully:
# Missing API key
try:
Calculator.explain()
except ApiKeyMissingError as e:
print(f"Error: {e}")
# Invalid source code or API issues
print(some_method.explain()) # Returns: "<could not get function explanation: ...>"