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=Trueonce, 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")storesport=9000because the hint saysint. 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 inheritedxwithout losing its default or its aliases, and_x: ... = Inherit("x")stores the inheritedxunder a private name while it is still read and passed asx. See Field annotations. - The Extras page covers the rest: dict-like instances,
replace,asdictandastuple, 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)
| File | Size | Uploaded | |
|---|---|---|---|
| bagof_magic-0.3.dev4.tar.gz | 246.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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
|