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.

Fields are written as annotations, and Magic generates __init__, __repr__, __eq__ and the other methods from them.

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'

The same class can also be written with the magic 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. A base class sets frozen=True once, every subclass keeps it, and a subclass overrides only 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, and are turned on per class or per field. Config("localhost", "9000") stores 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 is called with. See Building the right subclass.
  • A field can accept several input names, and can be read through 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 a required field to a base whose fields all have defaults. See Field annotations.
  • A subclass can forget a field it inherits by writing x: Forget, which frees the name for a field of its own. See Field annotations.
  • A subclass can change part of an inherited field and keep the rest: x: ... = Inherit(frozen=True) freezes the inherited x without losing its default or its aliases, and _x: ... = Inherit("x") stores the inherited x under a private name while it is still read and passed as x. See Field annotations.
  • The Extras page covers the rest: dict-like instances, replace, asdict and astuple, mutable defaults that are not shared, hooks around construction, generic classes, and docstrings generated from the fields.

How it compares

magic is close to attrs in spirit. Like pydantic, it does real work with the type hints, and like pydantic it uses inheritance where dataclasses and attrs 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

The comparison page sets the four side by side in more detail.

Settings

class Thing(Magic, frozen=True, kw_only=True, slots=True):
    ...
Setting Default What it does
init True generate __init__
repr True generate __repr__; a test such as HideIfNone() applies to every field
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 the settings that generate a method also accept a string in place of True. The generated method is then bound under that name, so that a hand-written method can call it.

The Settings page explains how settings are inherited and how a subclass overrides them.

Install

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

Python 3.8 and later.

Status

The package is at an early stage. The API is still settling, and parts of it may change. Issues and ideas are welcome at bagofseeds/bagof-magic.

Metadata

Release files for bagof-magic 0.3.dev4

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.dev4
File Size Uploaded
bagof_magic-0.3.dev4.tar.gz 246.2 kB Details

Built distribution (wheel)

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

Total release size: 382.5 kB

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

Download URL bagof_magic-0.3.dev4.tar.gz
Size 246.2 kB
Tags Source
SHA-256 checksum
How to use checksums
f4d82c7e06d9b019966c5871c99a5e3dcf3368e563db6eb5ffc72e1c172f3aba
BLAKE2b-256 checksum
How to use checksums
eb7fd960a7e6adf02ce55f2cb05806d545163983b40ad410960d7abe79041f6d
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.dev4-py3-none-any.whl

Download URL bagof_magic-0.3.dev4-py3-none-any.whl
Size 136.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
cf4f321cc6d51b65f4021f5d96683a75a380b2ad3b09102567d1d491531d0cd0
BLAKE2b-256 checksum
How to use checksums
fe1e1dc07ca6f74746503b405983a0571f1234713d8a3e652fb3633d344f1040
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