Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

metacore

⚠️ EARLY ALPHA WARNING: This library is in early development and the API may change significantly between versions. Use at your own risk in production environments.

A sophisticated type system enhancement library for Python metaprogramming and runtime behavior modification.

Overview

metacore bridges the gap between static type hints and runtime behavior by making types first-class citizens in the runtime environment. It provides powerful tools for:

  • Immutable constant namespaces with automatic type coercion
  • Extensible type annotation processing via registry pattern
  • Enhanced exception handling with formatted tracebacks
  • Runtime type validation and conversion

Imports and layout

Import everyday tools directly from metacore:

from metacore import (
    ConstantNamespace,
    TracedException,
    ValidationLevel,
    annotation_registry,
    convert_to_annotation,
    converter_from_annotation,
    default_from_annotation,
    defaulter_from_annotation,
    validate_from_annotation,
    fully_matches_annotation,
    validator_from_annotation,
)

Use feature modules for extension interfaces, type helpers, and errors:

from metacore.annotations import AnnotationsRegistry, AnnotationEntry, HousingAnnotationEntry, CastType
from metacore.constants import ConstantsMetaclass
from metacore.exceptions import ConstantsModificationError, ConvertingToAnnotationTypeError
from metacore.typing import Implement, is_optional, is_union, resolve_annotation_types
src/metacore/
├── __init__.py          # Common public imports
├── constants.py         # Constant namespaces and their metaclass
├── exceptions.py        # Exception hierarchy and traceback formatting
├── typing.py            # Annotation inspection helpers and Implement
├── py.typed             # Type information for package consumers
└── annotations/
    ├── __init__.py      # Public annotation API
    ├── _api.py          # Shared registry and convenience functions
    ├── _registry.py     # Registry implementation
    ├── _entries.py      # Custom processor interfaces
    ├── _builtins.py     # Built-in handlers and explicit registration
    └── _types.py        # Validation levels, CastType, and callable aliases

Modules beginning with _ are implementation details. Each public module declares its exports with __all__. The former meta, abstract, annotations_processors, and typing_utilities import paths have been removed.

annotation_registry() returns the shared default registry. Construct AnnotationsRegistry() for an independent registry with its own built-in handlers and customizations. Neither requires importing a handler module first.

For string annotations or nested forward references, supply the namespaces where the names are defined:

from metacore import convert_to_annotation
from metacore.typing import resolve_annotation_types

types = resolve_annotation_types(
    {"values": "list[Item]"}, nglobals=globals(), nlocals={"Item": int}
)
values = convert_to_annotation("list[Item]", ["1", "2"], nlocals={"Item": int})

The validation, defaulting, and conversion helpers and registry methods accept keyword-only nglobals and nlocals. Registry cache-clearing methods accept them as well. Names are resolved for each call; namespaces are not stored on the shared registry. Constant namespaces supply their declaring module and class namespaces automatically.

Processing contracts

Unsupported parameterized annotations raise AnnotationProcessorError rather than silently ignoring their type arguments. Registered handlers can extend the supported annotations.

Annotated[T, metadata] keeps its metadata during resolution. A processor registered for that exact annotation takes precedence; other operations fall back to T. Metadata without a registered processor does not add validation rules. Unhashable metadata is accepted for this fallback but is not cached.

Plain aliases (type Items = list[int]) and specialized generic aliases (type Items[T] = list[T], used as Items[int]) resolve to their underlying annotations. Generic aliases require all declared type arguments explicitly. An exact alias-specific registration takes precedence over its underlying type. Recursive aliases are rejected with AnnotationProcessorError, including when requesting a default for an empty container or a union with a nonrecursive first branch. Finite nesting, such as Items[Items[int]], is supported.

Both fixed tuples (tuple[int, str]) and variadic tuples (tuple[int, ...]) are supported. Variadic tuples default to (). Lists, sets, and dictionaries default to empty containers without constructing or defaulting their elements.

Looking up a default factory does not construct an instance. Construction occurs when the factory is called, and constructor failures raise DefaultingAnnotationError with the original exception as their cause.

Each registry caches successfully created validators, converters, and default factories. Registration or replacement of any processor clears the entire cache. All cache-clearing methods also clear the entire cache, including processors for containing annotations. Already-returned callables are not rewritten; request new callables after changing the registry. After directly mutating an entry obtained through get_processor(), call clear_cache() explicitly.

Validation returns either a boolean or ValidationLevel. Both FULL and PARTIAL are truthy; PARTIAL means only the outer container matches. For a boolean full match, use fully_matches_annotation(annotation, value), also available on AnnotationsRegistry. It returns False for partial matches and mismatches; unsupported annotations still raise an error.

from metacore import fully_matches_annotation

assert fully_matches_annotation(list[int], [1, 2]) is True
assert fully_matches_annotation(list[int], [1, "2"]) is False

Literal conversion preserves a matching value and otherwise returns the first declared literal. For example, converting "unknown" to Literal["a", "b"] returns "a". Literal matching currently uses Python equality, so True matches Literal[1]. The full-match helper follows that same validator contract; it does not introduce stricter Literal matching.

Features

ConstantNamespace

Create immutable constant namespaces with automatic type coercion at class definition time:

from metacore import ConstantNamespace
import pathlib

class MyConstants(ConstantNamespace):
    # Regular attributes (not annotated) are not constants
    A = 1

    # Annotated attributes become immutable constants
    B: int = 2
    C: int = 3.4  # Coerced to 3

    # Advanced type coercion
    path: pathlib.Path = "documents/test.txt"  # Coerced to Path object
    numbers: list[int] = [1, 1.5, 3]  # Coerced to [1, 1, 3]

# Access constants
print(MyConstants.B)  # 2
print(MyConstants.path)  # PosixPath('documents/test.txt')

# Modification is prevented
MyConstants.C = 10  # Raises ConstantsModificationError

# Dict-like interface
MyConstants.items()  # [('B', 2), ('C', 3), ...]
MyConstants.keys()   # ('B', 'C', 'path', 'numbers')
MyConstants.get('B', default=0)  # 2

Type Annotation Processing

Extensible registry-based type processing system:

from metacore.annotations import (
    annotation_registry,
    HousingAnnotationEntry,
)

registry = annotation_registry()

# Convert values to match type annotations
result = registry.convert_to_annotation(list[int], ("42", 4.2))  # Returns [42, 4]

# Register a custom type handler
class MyCustomType:
    def __init__(self, value):
        self.value = value

registry.register_processor(
    MyCustomType,
    HousingAnnotationEntry(converter=MyCustomType),
)
custom = registry.convert_to_annotation(MyCustomType, "value")

Enhanced Exceptions

All library exceptions inherit from TracedException for better debugging:

from metacore import TracedException

class MyError(TracedException):
    """Custom exception with formatted traceback"""

Installation

Install the sg-metacore package from PyPI:

python -m pip install sg-metacore

Requirements

  • Python 3.12 or higher

Development Status

This library is in active alpha development. Breaking changes may occur between minor versions.

From the metacore project directory, install the development dependencies and run the tests:

python -m pip install -e ".[dev]"
python -m pytest

Tests use the public package imports. Pytest also adds src to the import path for local development without an editable installation.

Roadmap

tbd

Contributing

This is an early-stage project. Contributions, bug reports, and feedback are welcome but please be aware of the alpha status.

License

MIT License - see LICENSE file for details.

Author

Sébastien Gachoud


Note: Some code and tests were developed with assistance from Claude AI.

Metadata

Release files for sg-metacore 0.1.0a1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for sg-metacore 0.1.0a1
File Size Uploaded Provenance Actions
sg_metacore-0.1.0a1.tar.gz 40.7 kB Without provenance Details for sg_metacore-0.1.0a1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for sg-metacore 0.1.0a1
File Interpreter ABI Platform Actions
sg_metacore-0.1.0a1-py3-none-any.whl Python 3 none any Details for sg_metacore-0.1.0a1-py3-none-any.whl

Total release size: 69.4 kB

Release files / sg_metacore-0.1.0a1.tar.gz

Download URL sg_metacore-0.1.0a1.tar.gz
Size 40.7 kB
Tags Source
SHA-256 checksum
How to use checksums
91aab3480546f131db9edffb2eaf2c7b059e6c2c8fe42da361dc84bc23e092a6
BLAKE2b-256 checksum
How to use checksums
d5aef9b38d0fabff59b33c1c50edc9c26c173ca9ba97f817b77cd0feca733417
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.2

Release files / sg_metacore-0.1.0a1-py3-none-any.whl

Download URL sg_metacore-0.1.0a1-py3-none-any.whl
Size 28.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d69a7841d828901abc49b4be1e3d6e23463568667c9a5de7c3089dd11e8cb663
BLAKE2b-256 checksum
How to use checksums
2c708e81dc64442a3bd56f39928a86d520e44e48e6b2b7f9ab89d09d341d83a6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.2

Security for sg-metacore 0.1.0a1

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.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page