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)
| 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)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| 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.