mixinforge package#

Tools for working with mixinforge classes.

This package provides reusable mixins, context managers, and utility functions that help you build well-structured Python classes. It offers tools for parameter management, cache management, initialization control, thread safety, pickle prevention, JSON serialization, nested collection processing, dictionary utilities, output capturing, and runtime package management.

Public API: - ParameterizableMixin: Base class for parameterizable objects with JSON serialization. - ImmutableMixin: Base class for immutable objects with customizable identity keys. - ImmutableParameterizableMixin: Immutable objects with params-based identity. - CacheablePropertiesMixin: Automatic discovery and invalidation of cached_property attributes. - NotPicklableMixin: Mixin that prevents pickling/unpickling. - SingleThreadEnforcerMixin: Enforces single-threaded execution with multi-process support. - GuardedInitMeta: Metaclass for strict initialization control and lifecycle hooks. - SingletonMixin: Ensures each subclass maintains exactly one instance. - OutputCapturer: Context manager that captures stdout, stderr, and logging output. - OutputSuppressor: Context manager that suppresses stdout and stderr output. - sort_dict_by_keys: Sort a dictionary by its keys alphabetically. - dumpjs: Serialize an object (or parameters) into a JSON string. - loadjs: Deserialize a JSON string produced by dumpjs back into a Python object. - update_jsparams: Update parameters in a JSON-serialized string. - access_jsparams: Access parameters in a JSON-serialized string. - JsonSerializedObject: NewType alias for JSON strings produced by dumpjs. - flatten_nested_collection: Find all atomic objects in nested collections (handles cycles). - find_instances_inside_composite_object: Find instances of type(s) in composite structures (handles cycles). Supports deep or shallow search. - transform_instances_inside_composite_object: Transform instances of type(s) in composite structures. Supports deep (handles cycles) or shallow search. - is_executed_in_notebook: Detect if running in Jupyter/IPython notebook. - reset_notebook_detection: Clear cached notebook detection result. - is_valid_env_name: Validate environment variable names using a strict, portable rule. - install_package: Install a Python package from PyPI into the current environment. - is_package_installed: Check if a Python package is currently installed. - uninstall_package: Remove a Python package from the current environment.

class mixinforge.CacheablePropertiesMixin[source]#

Bases: object

Mixin class for automatic management of cached properties.

Provides methods to discover all functools.cached_property attributes in the class hierarchy and to inspect, set, and invalidate their cached values. This enables efficient cache management without manual tracking of individual cached properties.

Note

This class is not thread-safe and should not be used with dynamically modified classes.

Subclasses using __slots__ MUST include ‘__dict__’ to support functools.cached_property, as enforced by _ensure_cache_storage_supported().

class mixinforge.GuardedInitMeta(name, bases, namespace, /, **kwargs)[source]#

Bases: ABCMeta

Metaclass for strict initialization control and lifecycle hooks.

Enforces a contract where _init_finished is False during initialization and only becomes True after all initialization code completes. This ensures that properties and methods can reliably check initialization state.

The metaclass automatically: - Injects _init_finished = False before __init__ runs - Sets _init_finished = True after __init__ completes - Wraps __setstate__ to maintain the same contract during unpickling - Invokes __post_init__ and __post_setstate__ hooks when defined

Contract:
  • The metaclass sets _init_finished = False before __init__ runs.

  • The metaclass sets _init_finished = True after __init__ returns (but before __post_init__, if defined).

  • Subclasses must NOT set _init_finished = True in __init__.

  • __setstate__ is wrapped to ensure _init_finished becomes True after full state restoration (but before __post_setstate__, if defined).

Note

If a class uses __slots__ without __dict__, it must include ‘_init_finished’ in its __slots__ declaration.

__init__(name, bases, dct)[source]#

Initialize the class and inject lifecycle enforcement.

Wraps __setstate__ to ensure proper initialization state after unpickling and validates that the class is compatible with the GuardedInitMeta contract.

Parameters:
  • name – The class name.

  • bases – Base classes.

  • dct – Class dictionary.

Raises:

TypeError – If class is a dataclass, has multiple GuardedInitMeta bases, or uses __slots__ without declaring _init_finished.

class mixinforge.ImmutableMixin(*args: Any, **kwargs: Any)[source]#

Bases: object

Base mixin for objects that never change after initialization.

Provides value-based identity semantics with optimized hashing and equality comparisons. Instead of using object identity (id), instances are compared based on a customizable identity key that represents their immutable state. This enables efficient use in sets and dictionaries while supporting value equality semantics.

The mixin caches the hash value for O(1) lookups and uses hash-based short-circuiting in equality checks to avoid expensive comparisons. This is particularly beneficial for complex objects with many fields.

Note that this mixin does not enforce immutability; subclasses are responsible for ensuring their instances truly never change after initialization.

Subclasses must override get_identity_key() to return a hashable value that uniquely defines the object’s identity based on its immutable state.

__init__(*args, **kwargs)[source]#

Initialize the mixin.

get_identity_key()[source]#

Return a hashable value defining this object’s identity.

Subclasses must override this method to specify what makes an instance unique. The returned value is used for hashing and equality comparisons, enabling value-based semantics. Common implementations return a tuple of the object’s immutable fields.

The returned value must be hashable and must remain constant for the object’s lifetime to maintain hash consistency.

Return type:

Any

Returns:

A hashable value uniquely identifying this object based on its immutable state.

Raises:

NotImplementedError – If not overridden by subclass.

property identity_key: Any#

Cached identity key for consistent hashing and equality checks.

Caches the result of get_identity_key() to ensure the same value is used throughout the object’s lifetime. This guarantees hash stability and enables efficient repeated comparisons without recomputing the identity key.

Returns:

The cached identity key.

Raises:

RuntimeError – If called before initialization completes.

class mixinforge.ImmutableParameterizableMixin(*args: Any, **kwargs: Any)[source]#

Bases: ParameterizableMixin, ImmutableMixin

Immutable objects with parameter-based identity and hashing.

Combines ParameterizableMixin’s parameter management with ImmutableMixin’s immutability support. Objects use JSON-serialized essential parameters as their identity key, enabling parameter-based equality comparisons and hashing.

This design ensures that two instances with identical essential parameters are considered equal and have the same hash, regardless of when or where they were created. This makes it possible to use parameterizable objects as dictionary keys and set members.

Note that the mixin does not enforce immutability; subclasses are responsible for ensuring their instances truly never change after initialization.

get_identity_key()[source]#

Return JSON-serialized essential parameters as an identity key.

Uses JSON-serialized essential parameters to ensure consistent, deterministic identity across instances. This approach guarantees that objects with identical essential parameters produce the same hash and compare as equal, enabling reliable use in hash-based collections.

Return type:

Any

Returns:

JSON string representation of essential parameters used for hashing and equality comparisons.

property essential_jsparams: JsonSerializedObject#

Cached JSON-serialized essential parameters for identity operations.

Provides a cached JSON representation of the object’s core parameters, used as the basis for hashing and equality comparisons. Caching ensures that serialization happens only once, improving performance for repeated operations.

Returns:

JSON string representation of the object’s essential parameters.

class mixinforge.NotPicklableMixin[source]#

Bases: object

A mixin class that prevents objects from being pickled or unpickled.

This class provides a mechanism to explicitly prevent instances from being serialized using Python’s pickle module. Classes that inherit from this mixin will raise TypeError exceptions when pickle attempts to serialize or deserialize them.

This is useful for objects that contain non-serializable resources or should not be persisted for security or architectural reasons.

class mixinforge.OutputCapturer[source]#

Bases: object

Context manager that captures stdout, stderr, and logging output.

Uses a dual-stream “tee” approach: output is sent to both the original streams (preserving normal display) and to an internal buffer (enabling capture). This ensures users see output in real-time while also storing it for later retrieval via get_output().

Example

>>> with OutputCapturer() as capturer:
...     print("Hello")  # Prints normally AND is captured
...     logging.info("Test")  # Logged normally AND is captured
>>> output = capturer.get_output()
>>> assert "Hello" in output
>>> assert "Test" in output
__init__()[source]#

Initialize the OutputCapturer.

Creates the capture buffer. The actual stream redirection happens in __enter__ to support proper nesting of multiple OutputCapturer contexts.

get_output()[source]#

Retrieve all captured output as a single string.

Return type:

str

Returns:

Combined stdout, stderr, and logging output captured during the context manager’s lifetime.

class mixinforge.OutputSuppressor[source]#

Bases: object

Context manager to suppress stdout and stderr.

Example

with OutputSuppressor():

noisy_function() # Output is discarded

Notes

Internally uses contextlib.ExitStack to manage all redirections and to ensure restoration even when exceptions are raised.

class mixinforge.ParameterizableMixin[source]#

Bases: object

Base class for parameterizable classes.

Classes deriving from this base expose a stable set of configuration parameters that define their behavior and identity. Subclasses implement get_params to return these parameters, which can then be serialized to and from a portable JSON representation.

Note

This class is intended to be subclassed. The default implementation of get_params returns an empty mapping.

get_params()[source]#

Return this instance’s configuration parameters.

Parameters define the object’s configuration but not its internal contents or data. They are typically passed to __init__ at creation time.

Return type:

dict[str, Any]

Returns:

A mapping of parameter names to values.

Note

Subclasses should override this method to return their specific parameters. The default implementation returns an empty dictionary.

clone(**kwargs)[source]#

Create a new instance with the same parameters, optionally overriding some.

Parameters:

**kwargs (Any) – Parameter overrides to apply to the clone.

Return type:

Self

Returns:

A new instance with parameters from this instance, updated with kwargs.

get_jsparams()[source]#

Return this instance’s parameters encoded as JSON.

Return type:

NewType(JsonSerializedObject, str)

Returns:

JSON string produced by dumpjs.

classmethod get_default_params()[source]#

Get the default parameters of the class as a dictionary.

Default values are taken from keyword parameters of __init__ and returned as a key-sorted dictionary. Subclasses may override if default computation requires custom logic.

Return type:

dict[str, Any]

Returns:

The class’s default parameters sorted by key.

classmethod get_default_jsparams()[source]#

Return default constructor parameters encoded as JSON.

Return type:

NewType(JsonSerializedObject, str)

Returns:

JSON string with default parameters.

property essential_param_names: set[str]#

Names of parameters that define the object’s core identity and behavior.

Essential parameters are those that fundamentally define an object’s behavior or identity - for example, the maximum number of trees in a random forest or the maximum depth of a decision tree.

These parameters are oftentimes immutable throughout the object’s lifetime. They are guaranteed to be preserved during the copying/deepcopying, serialization/deserialization processes, and similar operations.

Note

Subclasses should override this property to specify which parameters are essential. The default implementation considers all parameters essential.

Returns:

Names of essential parameters.

property auxiliary_param_names: set[str]#

Names of the object’s auxiliary parameters.

Auxiliary parameters are parameters that do not fundamentally impact the object’s behavior or identity. These parameters might include settings like logging verbosity, debug flags, or probability thresholds for consistency checks.

They are considered “disregardable” in the sense that they are not guaranteed to be preserved during serialization/deserialization processes, or even during simple copying/deepcopying operations.

Returns:

Set of auxiliary parameter names.

get_essential_params()[source]#

Return only the essential parameters.

Return type:

dict[str, Any]

Returns:

Mapping of essential parameter names to values.

get_essential_jsparams()[source]#

Return essential parameters encoded as JSON.

Return type:

NewType(JsonSerializedObject, str)

Returns:

JSON string with essential parameters.

get_auxiliary_params()[source]#

Return only the auxiliary parameters.

Return type:

dict[str, Any]

Returns:

Mapping of auxiliary parameter names to values.

get_auxiliary_jsparams()[source]#

Return auxiliary parameters encoded as JSON.

Return type:

NewType(JsonSerializedObject, str)

Returns:

JSON string with auxiliary parameters.

class mixinforge.SingleThreadEnforcerMixin(*args, **kwargs)[source]#

Bases: object

Mixin to enforce single-threaded execution with multi-process support.

Add this mixin to any class to ensure its methods are called only from the thread that first instantiated it. Automatically resets ownership after process forks to support multi-process parallelism while preventing concurrent threading issues.

The enforcement happens at instantiation and can be manually triggered via the _restrict_to_single_thread method.

Raises:

RuntimeError – If instantiated or if _restrict_to_single_thread is called from a different thread than the owner thread.

Example

>>> class MyClass(SingleThreadEnforcerMixin):
...     def process(self):
...         self._restrict_to_single_thread()
...         # Process safely on owner thread
__init__(*args, **kwargs)[source]#

Initialize and register the current thread as the owner.

class mixinforge.SingletonMixin(*args, **kwargs)[source]#

Bases: ParameterizableMixin

Mixin for creating singleton classes.

Ensures each subclass maintains exactly one instance that is returned on every instantiation. The singleton instance is stored per class type, so each subclass has its own singleton instance.

_instances#

Dictionary storing the singleton instance for each class.

_counters#

Dictionary tracking the number of instantiation requests per class.

Note

This implementation is not thread-safe. For multi-threaded applications, additional synchronization mechanisms should be added.

mixinforge.access_jsparams(jsparams, *args)[source]#

Access selected constructor parameters from a serialized JSON blob.

Args:

jsparams: The JSON string produced by dumpjs. *args: Parameter names to extract from the internal PARAMS -> DICT

mapping.

Return type:

dict[str, Any]

Returns:

A mapping of parameter names to their deserialized values.

Raises:
  • TypeError – If jsparams is not a string.

  • KeyError – If a key is missing or structure is invalid.

Parameters:
mixinforge.dumpjs(obj, **kwargs)[source]#

Dump an object to a JSON string using custom serialization.

Parameters:
  • obj (Any) – The object to serialize.

  • **kwargs – Additional keyword arguments forwarded to json.dumps (e.g., indent=2, sort_keys=True).

Return type:

NewType(JsonSerializedObject, str)

Returns:

The JSON string.

mixinforge.flatten_nested_collection(obj)[source]#

Yield leaf elements from nested collections with weak deduplication.

Atomic elements are indivisible values such as numbers, strings, matrices, or paths. The function traverses nested iterables, yielding leaf values, which includes both atomics and non-iterable objects. Their exact order and complete deduplication are not guaranteed.

Handles cycles gracefully by visiting each object only once.

Mapping keys and values are both traversed.

Parameters:

obj (Iterable[Any]) – The root collection.

Yields:

Leaf elements in depth-first order, deduplicated by identity.

Raises:

TypeError – If obj is not an iterable.

Return type:

Iterator[Any]

mixinforge.find_instances_inside_composite_object(obj, classinfo, *, deep_search=True)[source]#

Find all instances of a target type within any object.

Performs traversal of iterables, mappings, and custom objects (via __dict__ and __slots__). Yields all instances matching classinfo, continuing to search inside matched objects for nested instances. Exact return order and complete deduplication are not guaranteed.

Handles cycles gracefully by visiting each object only once.

Mapping keys and values are both traversed.

Parameters:
  • obj (Any) – The object to search within.

  • classinfo (type | Union | tuple[type | Union | tuple[ClassInfo, ...], ...]) – Type or tuple of types to search for. Accepts the same values as the second argument to isinstance(): a single type, a tuple of types (recursively), or a union type (e.g., int | str).

  • deep_search (bool) – If True (default), after finding an instance, continue recursively searching inside it for more matching instances. If False, stop traversal at matched instances.

Yields:

Instances matching classinfo in depth-first order, deduplicated by identity.

Raises:

TypeError – If classinfo is invalid.

Return type:

Iterator[Any]

mixinforge.install_package(package_name, *, upgrade=False, version=None, use_uv=True, import_name=None, verify_import=True)[source]#

Install a Python package from PyPI into the current environment.

Installs packages using uv (default) or pip, automatically bootstrapping missing package managers. Handles packages where the PyPI name differs from the import name, and verifies successful installation by default.

Parameters:
  • package_name (str) – PyPI package name.

  • upgrade (bool) – Whether to upgrade.

  • version (str | None) – Pinned version.

  • use_uv (bool) – Use uv (default) or pip.

  • import_name (str | None) – Module name for verification.

  • verify_import (bool) – Check importability.

Raises:
Return type:

None

Example

>>> install_package("requests")
>>> install_package("Pillow", import_name="PIL")
mixinforge.is_package_installed(package_name)[source]#

Check if a Python package is installed and importable.

Parameters:

package_name (str)

Return type:

bool

mixinforge.is_valid_env_name(name)[source]#

Validate a portable environment variable name.

Enforces a strict, shell-safe subset (POSIX identifiers) so names are portable across macOS, Windows, and Ubuntu. Names must start with an ASCII letter or underscore and contain only ASCII letters, digits, and underscores.

Parameters:

name (str) – Candidate environment variable name.

Return type:

bool

Returns:

True if name is a valid portable identifier, False otherwise.

mixinforge.transform_instances_inside_composite_object(obj, classinfo, transform_fn, *, deep_transformation=True)[source]#

Transform all instances of a target type within any object.

Traverses collections and custom objects. Transforms matching instances and reconstructs the composite object. Handles cycles.

Parameters:
  • obj (Any) – The object to transform.

  • classinfo (type | Union | tuple[type | Union | tuple[ClassInfo, ...], ...]) – Type(s) to search for and transform.

  • transform_fn (Callable[[Any], Any]) – Function to apply to matching instances.

  • deep_transformation (bool) – If True, recursively transform inside result.

Return type:

Any

Returns:

The transformed object (or original if unchanged).

Raises:

TypeError – If classinfo is invalid.

mixinforge.is_executed_in_notebook()[source]#

Return whether code is running inside a Jupyter/IPython notebook.

Uses a lightweight heuristic checking for IPython presence and specific attributes. Cached to avoid repeated imports.

Return type:

bool

Returns:

True if running inside a notebook.

mixinforge.loadjs(s, **kwargs)[source]#

Load an object from a JSON string produced by dumpjs.

Parameters:
  • s (NewType(JsonSerializedObject, str)) – The JSON string to parse.

  • **kwargs – Arguments forwarded to json.loads (no object_hook).

Return type:

Any

Returns:

The reconstructed Python object.

Raises:
mixinforge.reset_notebook_detection()[source]#

Clear the cached result of is_executed_in_notebook().

Forces re-detection on next call (useful for testing).

Return type:

None

mixinforge.sort_dict_by_keys(d)[source]#

Return a new dictionary with keys sorted alphabetically.

Parameters:

d (dict[str, Any]) – The input dictionary.

Return type:

dict[str, Any]

Returns:

A new dictionary with sorted keys.

Raises:

TypeError – If d is not a dictionary.

mixinforge.uninstall_package(package_name, *, use_uv=True, import_name=None, verify_uninstall=True)[source]#

Remove a Python package from the current environment.

Protects critical packages (pip, uv). Verifies removal.

Parameters:
  • package_name (str) – Package to uninstall.

  • use_uv (bool) – Use uv (default) or pip.

  • import_name (str | None) – Module name for verification.

  • verify_uninstall (bool) – Check removal.

Raises:
Return type:

None

mixinforge.update_jsparams(jsparams, **kwargs)[source]#

Update constructor parameters inside a serialized JSON blob.

This helper takes a JSON string produced by dumpjs for an object that was serialized via its get_params method and returns a new JSON string with the provided parameters updated or added under the internal PARAMS -> DICT mapping.

Parameters:
  • jsparams (NewType(JsonSerializedObject, str)) – The JSON string from dumpjs.

  • **kwargs – Parameters to update or add.

Return type:

NewType(JsonSerializedObject, str)

Returns:

A new JSON string with updated parameters.

Raises:
  • TypeError – If jsparams is not a string.

  • KeyError – If the structure is invalid.