multinut

PyPI - Version Downloads - Monthly Python Versions Wheel

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 .env files 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.environ pollution

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 .env files
  • 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: ...>"

Description
a multitool in python... oy i never said it would be usefull
Readme 107 KiB
Languages
Python 100%