Mixit
A lightweight and flexible mixin system for Python that allows dynamic composition of functionality through mixins.
Features
- Dynamic mixin composition at runtime
- Method export system with conflict detection
- Optional initialization through
mix_init - Clean mixin coordination through mixer access
- Simple and intuitive API
Installation
pip install git+https://github.com/joshms123/mixit.git
Quick Start
from mixit import Mixer, Mixin, export
# Define a mixin
class LoggerMixin(Mixin):
def __init__(self):
super().__init__()
self.logs = []
self.prefix = ""
def mix_init(self, prefix: str = "", **kwargs):
self.prefix = prefix
@export
def log(self, message: str):
self.logs.append(f"{self.prefix}{message}")
return len(self.logs)
# Create a mixer and add the mixin
mixer = Mixer()
mixer.add_mixin("logger", LoggerMixin, prefix="[INFO] ")
# Use the exported method
mixer.log("Hello world!") # Returns: 1
print(mixer.logger.logs) # Prints: ['[INFO] Hello world!']
Auto-derived names (v0.7+)
You can omit the explicit name. The mixer derives one by converting the class name to snake_case:
mixer = Mixer()
mixer.add_mixin(LoggerMixin, prefix="[INFO] ") # registered as "logger_mixin"
mixer.log("Hello!")
print(mixer.logger_mixin.logs)
For batches of plain mixins (no mix_init kwargs), add_mixins registers them in order:
mixer.add_mixins(CounterMixin, MathMixin, LoggerMixin)
mixer.add_mixin(DatabaseMixin, dsn="...") # use add_mixin for kwarg cases
mixer.add_mixins(CacheMixin, MetricsMixin)
Core Concepts
Mixins
Mixins are classes that inherit from Mixin and provide functionality that can be mixed into a Mixer instance. Methods can be marked for export using the @export decorator, making them directly accessible from the mixer instance.
class CounterMixin(Mixin):
def __init__(self):
super().__init__()
self.value = 0
@export
def increment(self):
self.value += 1
return self.value
Initialization
Mixins can define an optional mix_init method that will be called when the mixin is added to a mixer. Any additional keyword arguments passed to add_mixin will be forwarded to mix_init.
class MathMixin(Mixin):
def __init__(self):
super().__init__()
self.precision = 2
def mix_init(self, precision: int = 2, **kwargs):
self.precision = precision
@export
def add(self, a: float, b: float) -> float:
return round(a + b, self.precision)
mixer.add_mixin("math", MathMixin, precision=3)
Method Export
Methods marked with @export are made available directly on the mixer instance. If multiple mixins try to export methods with the same name, only the first one is exported and conflicts are tracked.
# Method available directly on mixer
result = mixer.add(1.23, 4.56)
# Access through mixin instance
result = mixer.math.add(1.23, 4.56)
# Check for conflicts
conflicts = mixer.get_conflicts()
Mixin Coordination
Mixins can coordinate with each other through the mixer instance. By default, the mixer is accessible via the mixer property, but you can customize this name to avoid conflicts or improve readability:
# Using default 'mixer' attribute
class WorkerMixin(Mixin):
@export
def do_work(self):
self.mixer.log("Starting work") # Use another mixin's exported method
result = self.perform_work()
self.mixer.logger.log("Done!") # Access mixin instance directly
return result
# Using custom mixer attribute name
class DatabaseMixin(Mixin, mixer_attr='app'):
@export
def query(self, sql: str):
# Access mixer through custom name
logger = self.app.logger
logger.log(f"Executing: {sql}")
return self.execute_query(sql)
API Reference
Mixer
add_mixin(mixin_class: Type[Mixin], **kwargs) -> Mixin— auto-derives the registration name from the class name.add_mixin(name: str, mixin_class: Type[Mixin], **kwargs) -> Mixin— explicit name.add_mixin_instance(instance: Mixin) -> Mixin— auto-derives the name; for pre-built instances.add_mixin_instance(name: str, instance: Mixin) -> Mixin— explicit name.add_mixins(*mixin_classes: Type[Mixin]) -> List[Mixin]— bulk-register plain mixins in order, no kwargs.remove_mixin(name: str) -> Noneget_mixin(name: str) -> Mixinget_mixins() -> Dict[str, Mixin]get_conflicts() -> Dict[str, List[str]]call_all_mixins(func_name: str, *args, **kwargs) -> Dict[str, Any]
Helpers
derive_mixin_name(cls: Type[Mixin]) -> str— exported helper that converts a class name to its snake_case registration key.
Mixin
mix_init(**kwargs) -> None- Optional initialization methodcleanup() -> None- Clean up resources when removedmixerproperty - Default access to mixer instancemixer_attrclass parameter - Customize mixer attribute name (e.g.class MyMixin(Mixin, mixer_attr='app'))
Decorators
@export- Mark a method for export to the mixer namespace
License
MIT License
Metadata
Release files for mixit 0.7.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| mixit-0.7.0.tar.gz | 10.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| mixit-0.7.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 18.9 kB
Release files / mixit-0.7.0.tar.gz
| Download URL | mixit-0.7.0.tar.gz |
|---|---|
| Size | 10.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e389ba3bd81a9565e7ed57eaf255a0e4881a177122d28f3326dad67e8ac6e237
|
|
BLAKE2b-256 checksum How to use checksums |
152490a0547efe05b6d4da542c7dcd605d8b112b9286759eb0b6b1db20b64277
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Apr 26, 2026.
Transparency logRelease files / mixit-0.7.0-py3-none-any.whl
| Download URL | mixit-0.7.0-py3-none-any.whl |
|---|---|
| Size | 9.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0b8352c1d2dc83645193685c5b02652432413ab0ce05bb7e991a2ac90fae6415
|
|
BLAKE2b-256 checksum How to use checksums |
011b250fc376f5ccab4a98489ee32f9c2417676a854f5aebcd040d42b7e0ad04
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Apr 26, 2026.
Transparency log