A Python tool to enforce type hints as runtime assertions.
Project description
DieCast
A Python tool to enforce type hints as runtime assertions. Shape your code or watch it die.
Description
DieCast is a type checking utility that transforms Python's optional type hints into runtime assertions. It helps catch type errors early and makes your code more robust without changing how you write type annotations or littering your code.
DieCast embodies Assertion-Driven Development - the philosophy that if your program doesn't satisfy expectations, it should crash. Code that silently invalidates expectations is a liability. Your code will adjust to the expected (asserted) shape or die trying.
Features
- Type checking decorator (
@diecast) - Apply to functions, methods, or classes to enforce type checks. - Automatic Module Decoration (
mold()) - Apply type checking to all eligible functions/classes in a module usingmold(). - Support for complex types - Works with
typingconstructs likeList,Dict,Union,Optional,TypeVar, etc. - Generic Type Support - Handles
typing.Genericclasses and resolvesTypeVars based on instantiation. - Nested type validation - Validates nested structures like
List[Dict[str, int]]. - Special case handling - Properly handles generators, async functions/generators, forward references, etc.
- Exclusion Decorator (
@diecast.ignore) - Use@diecast.ignoreto skip specific functions/classes during automatic decoration. - Clean error messages - Reports detailed information about type errors via
YouDiedError.
Why Assertion-Driven Development?
DieCast treats assertions as a blueprint, a mold, a scaffold, and a gauntlet. Your type hints (and inline assertions) form a contract that your code must satisfy. This architecture-first approach works even without extensive planning - add your type assumptions and state assertions as you go, and let the sum of these expectations form the mold to which your code must conform.
Installation
pip install py-diecast
Or install from source:
# Replace with the actual URL if different
git clone https://github.com/GWUDCAP/diecast.git
cd diecast
pip install -e .
Usage
Basic Decorator
Apply the @diecast decorator directly to functions or methods:
from diecast import diecast
from typing import List
@diecast
def greet(name: str) -> str:
return f"Hello, {name}!"
@diecast
def process_numbers(numbers: List[int]) -> int:
return sum(numbers)
# Works fine
greet("World")
process_numbers([1, 2, 3])
# Raises YouDiedError (subclass of TypeError) - wrong argument type
greet(123)
process_numbers([1, "a", 3])
# Note: For @diecast to work reliably, especially when combined with other decorators,
# it should generally be placed as the *innermost* decorator (closest to the `def` line).
Automatic Module Decoration (mold())
Enable type checking for an entire module by importing and calling the mold() function at the end of the module file:
# In your module (e.g., my_module.py)
from typing import List, Dict, Any
from diecast import mold
# Define your functions and classes with type hints
def process_data(items: List[int]) -> Dict[str, Any]:
return {"processed": sum(items)}
class MyClass:
def method(self, value: str) -> bool:
return bool(value)
# Call mold() at the end of the module
# This applies @diecast to eligible, annotated functions/classes defined above
mold()
# ---
# Now, elsewhere in your project:
# import my_module
#
# my_module.process_data([1, 2]) # OK
# # my_module.process_data(["a"]) # Raises YouDiedError
#
# instance = my_module.MyClass()
# instance.method("hello") # OK
# # instance.method(123) # Raises YouDiedError
Excluding Functions/Classes (@diecast.ignore)
Use the @diecast.ignore decorator to exclude specific functions or classes when using mold():
from diecast import diecast, mold
from typing import List, Dict, Any
@diecast.ignore
def function_to_skip(a: int, b: str) -> int:
# This won't be type checked by mold()
return a + int(b)
@diecast.ignore
class ClassToSkip:
# This class and its methods won't be type checked by mold()
# Note: @diecast.ignore applies to the whole class here.
# If you want to ignore only specific methods, apply @diecast to the
# class and @diecast.ignore to the specific methods.
def method(self, x: int) -> str:
return str(x * 2)
# ... other functions ...
mold() # Apply to the rest of the module
Working with Generics
@diecast correctly handles generic classes defined using typing.Generic and resolves TypeVars based on how the generic class is specialized.
from typing import TypeVar, Generic, List
from diecast import diecast
T = TypeVar('T')
@diecast
class Box(Generic[T]):
def __init__(self, item: T):
self.item: T = item
def get_item(self) -> T:
return self.item
def process_list(self, items: List[T]) -> List[T]:
# This will check that elements in 'items' match the T of the Box instance
return items[:]
# Create instances with specific types
int_box = Box[int](10)
str_box = Box[str]("hello")
# Type checks are specific to the instance's type
int_box.get_item() # OK, returns int
# int_box.process_list(["a"]) # Raises YouDiedError (expects List[int])
int_box.process_list([1, 2]) # OK
str_box.get_item() # OK, returns str
# str_box.process_list([1]) # Raises YouDiedError (expects List[str])
str_box.process_list(["a", "b"]) # OK
Error Handling
When a type mismatch occurs at runtime, DieCast raises a YouDiedError, which is a subclass of Python's built-in TypeError.
YouDiedError provides a detailed error message indicating:
- The function or method where the error occurred.
- The parameter name or return value involved.
- The expected type.
- The actual type received.
- The value that caused the mismatch (truncated if large).
- The location in the user's code that called the failing function.
from diecast import diecast
@diecast
def add(x: int, y: int) -> int:
return x + y
try:
add(5, "oops")
except Exception as e:
print(f"Caught: {type(e).__name__}")
print(e)
# Output will show YouDiedError and a detailed message
# about 'y' expecting 'int' but receiving 'str'.
Logging
DieCast uses the standard Python logging module. It logs information under the logger name 'diecast'.
Important: DieCast does not configure any logging handlers or set logging levels by default. If you want to see DieCast's internal logging messages (e.g., for debugging decorator application or type resolution), you must configure the 'diecast' logger using standard Python logging techniques:
import logging
# Example: Configure basic logging to the console
logging.basicConfig(level=logging.DEBUG) # Or use logging.INFO
diecast_logger = logging.getLogger('diecast')
# You can add specific handlers, formatters, etc. to diecast_logger if needed
# e.g., handler = logging.FileHandler('diecast.log')
# e.g., diecast_logger.addHandler(handler)
# Now run your code using @diecast or mold()
# ...
License
MIT License. See the LICENSE file for full details.
Contributing
Contributions are welcome! Please see the CONTRIBUTING.md file for guidelines.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file py_diecast-0.2.1.tar.gz.
File metadata
- Download URL: py_diecast-0.2.1.tar.gz
- Upload date:
- Size: 102.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.6.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
561859e3433f070f45f58361521298d3114cb9c091fa6b13c4333b2ca68830eb
|
|
| MD5 |
bf7dad900324ce9c4e08a12917d1e660
|
|
| BLAKE2b-256 |
567eb4ff17b5c611b224a6ca66ae40308397557a08d64d14bc708ff01d886605
|
File details
Details for the file py_diecast-0.2.1-py3-none-any.whl.
File metadata
- Download URL: py_diecast-0.2.1-py3-none-any.whl
- Upload date:
- Size: 59.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.6.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b4cae5ebb5e9e3d39fc225ee86876aabf099220ff029be012ff256041e644709
|
|
| MD5 |
97d53869f496363f5096c896550f43d4
|
|
| BLAKE2b-256 |
b0337e98d0fe8456725ebf3d29696d5957aea57e1f297a5cb264dcbd1c799761
|