Skip to main content
Pre-release

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

bagof-magic

Classes that build themselves from your type hints.

Write your fields as annotations. Magic generates __init__, __repr__, __eq__, and everything else.

from bagof.magic import Magic

class Point(Magic, frozen=True):
    x: float
    y: float
>>> Point(1.0, 2.0)
Point(x=1.0, y=2.0)
>>> Point(1.0, 2.0) == Point(1.0, 2.0)
True
>>> Point(1.0, 2.0).x = 3.0
Traceback (most recent call last):
AttributeError: Cannot set frozen field 'x'

If you prefer a decorator:

=== "Base class"

```python
from bagof.magic import Magic

class Point(Magic, frozen=True):
    x: float
    y: float
```

=== "Decorator"

```python
from bagof.magic import magic

@magic(frozen=True)
class Point:
    x: float
    y: float
```

What sets it apart

  • Settings are class keyword arguments, and subclasses inherit them. Set frozen=True on a base once; every subclass keeps it, and can override just the settings it needs to change. See Settings.
  • Per-field behaviour lives in the annotation, not in a field() call: tags: Factory[list], token: NoRepr[str] = "". See Field annotations.
  • Conversion and validation come from the type hint, opt in per class or per field. Config("localhost", "9000") becomes port=9000 because the hint says int. See Conversion and validation.
  • A class can build one of its own subclasses, chosen from the arguments it's called with. See Building the right subclass.
  • A field can accept several input names, and expose forwarding properties under other names. See Input aliases and forwarding properties.
  • A required field can follow one with a default, so a subclass can add one to a base whose fields all have defaults. See Field annotations.
  • Also included: dict-like instances, replace/asdict/astuple, mutable defaults that aren't shared, hooks around construction, generic classes, and docstrings generated from the fields. See Extras.

How it compares

Close to attrs in spirit, with pydantic's habit of doing real work from your type hints, and inheritance where the others use decorators.

dataclasses attrs pydantic magic
settings inherited by subclasses no no yes yes
per-field behaviour in the annotation no no partly yes
conversion from the type hint no partly yes yes
validation from the type hint no partly yes yes
dict-like instances no no partly yes
no methods added unless asked yes yes no yes

There is a fuller side-by-side in the comparison page.

Settings

class Thing(Magic, frozen=True, kw_only=True, slots=True):
    ...
Setting Default What it does
init True generate __init__
repr True generate __repr__
eq True generate __eq__
order False generate the comparisons
hash None generate __hash__; decides for itself by default
unsafe_hash False generate one even when the class is mutable
frozen False refuse assignment after construction
match_args False support structural pattern matching
kw_only False every field must be passed by name
positional_only False every field must be passed by position
slots False use __slots__, and drop __dict__
weakref_slot False allow weak references under slots
convert False convert every field from its type
validate False check every field against its type
convert_defaults True convert a value that came from a default, too
validate_defaults True check a value that came from a default, too
unresolved_hints "warn" what to do when a type hint still names something undefined the first time a field needs it; or "raise", or "ignore"
factory False build every missing default from its type
mutable_default "factory" give each instance its own copy of x: list = []; or "raise", or "allow"
alias default public name input names; True includes enabled property names, False keeps underscores
property False forwarding attributes; True or "readonly" exposes the public name, "all" every input alias
mapping False behave like a dictionary; a subclass inherits the methods and cannot turn them off
override False apply this class's settings to inherited fields too
polymorphic False build one of this class's subclasses, chosen from the arguments; or "strict", which refuses to build this class when none of them matches
pin_discriminant "pin" what a subclass does with the field it matches on; also "classvar", "keep", or any of those with +narrow (and "narrow" = "pin+narrow") to narrow its type and reject other values; a field's own Pin[T, mode] wins over it
reverse False list a subclass's own fields before inherited ones
doc True add the field table to the class docstring

Most of them also accept a string instead of True, which binds the generated method under that name. This is useful when you want to call the generated method from your own.

See Settings for how they're inherited and how to override them.

Install

pip install git+https://github.com/bagofseeds/bagof-magic.git

Python 3.8 and later.

Status

Early. The API is settling, and things may still move. Issues and ideas are welcome at bagofseeds/bagof-magic.

Metadata

Release files for bagof-magic 0.3.dev0

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

Source distribution (sdist)

Source distribution for bagof-magic 0.3.dev0
File Size Uploaded
bagof_magic-0.3.dev0.tar.gz 199.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for bagof-magic 0.3.dev0
File Interpreter ABI Platform
bagof_magic-0.3.dev0-py3-none-any.whl Python 3 none any Details

Total release size: 312.7 kB

Release files / bagof_magic-0.3.dev0.tar.gz

Download URL bagof_magic-0.3.dev0.tar.gz
Size 199.7 kB
Tags Source
SHA-256 checksum
How to use checksums
26f32ccdde2f6b721d8fbd61e46be1f2914b5acecd41ea52c281da1a4ed8b075
BLAKE2b-256 checksum
How to use checksums
12cba47acc5b66505433799df22bacf975372e318c948cdc6e4287e65dc8f649
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / bagof_magic-0.3.dev0-py3-none-any.whl

Download URL bagof_magic-0.3.dev0-py3-none-any.whl
Size 112.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6f5e9ec5e642f05fe0613b013267491bfb1f1587db84d925d3185a9451532403
BLAKE2b-256 checksum
How to use checksums
6fb7ae6923794d71908d61e51d6afa38eb40bc87697000dcd95b8cb1e9a81bab
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14
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