Skip to content

bagof.magic._fields

Classes

Field

Field(*arg, **kwargs)

Bases: SlotsBase

A single field in a Magic class.

Every annotation in a Magic class body becomes a Field. You rarely create one directly. The annotation family (Factory, KwOnly, ConvertTo, ...) and the field() function are the usual ways in.

Parameters:

Name Type Description Default
name str

The field's name in the class body.

required
type type or type hint

The field's type. Used for conversion, validation and factory defaults when those are turned on.

required
default any

The default value.

required
factory bool or Callable[[], any]

How a fresh default is built per instance, rather than one value shared across instances: False builds nothing, True works the factory out from the type, and a callable is called to build it. The build property reads this as a plain "is a default built?" boolean. The build= keyword is an alias that sets this: build=True/build=False.

`Options().factory`
init bool

Whether this field appears in __init__. Not stored directly: it reads as kw or positional. Setting init=False forbids both ways. Setting init=True changes nothing (a field is a parameter unless something says otherwise). Assigning field.init = value afterwards sets both kw and positional.

required
repr bool

Include this field in the generated __repr__.

True (False for a pseudo-field)
hash bool

Include this field in the generated __hash__. Defaults to following eq, since equal instances must hash equally.

None (follows `eq`)
eq bool

Include this field in the generated __eq__.

True
order bool

Include this field in the generated ordering. A field out of __eq__ is out of ordering too. Setting eq=False with order=True is an error.

follows `eq`
metadata dict

Arbitrary user-defined metadata.

required
kw bool

Allow this field to be passed by keyword. To make it keyword-only, also set positional=False. With both set to False, the field takes its default (same as init=False).

`not Options().positional_only`
positional bool

Allow this field to be passed by position. To make it positional-only, also set kw=False.

`not Options().kw_only`
frozen bool

Forbid assignment after construction.

`Options().frozen`
converter bool or Callable[[any], any]

How the incoming value is converted: False not at all, True using a converter worked out from the type, or a callable that converts it. The convert property reads this as a boolean. The convert= keyword is an alias that sets this: convert=True/convert=False.

`Options().convert`
validator bool or Callable[[any], any]

How the incoming value is validated, in the same three forms as converter (a validator returns the value unchanged when valid and raises when not). The validate property reads this as a boolean; validate= is an alias that sets it.

`Options().validate`
var bool

Mark this as a pseudo-field. An InitVar is passed to the constructor but not stored. A ClassVar is a class attribute, shared by every instance and absent from the constructor. Using the InitVar and ClassVar annotations is usually clearer.

False
doc str

Documentation for this field. Also settable through the Doc annotation.

required
key bool | str

Include this field in the dict-like interface. A string value is used as the key name.

`Options().mapping`
alias str

The name used in generated methods (constructor parameter, repr output, dict key). Useful when the field name is not a good public name, or when matching an external API.

`name.lstrip("_")`

Other Parameters:

Name Type Description
compare bool

Shorthand for setting both eq and order at once.

Attributes

init property writable
init: bool

Whether the generated __init__ takes this field.

True when the field can be passed by keyword, by position, or both. False when it can be passed neither way. Computed from kw and positional.

Setting field.init = True or field.init = False sets both kw and positional to that value. Field(init=False) (or NoInit) forbids both ways. Field(init=True) (or Init) changes nothing, since a field is a parameter by default.

convert property
convert: bool

Whether the incoming value is converted.

Reads converter: True for a converter worked out from the type or a callable given directly, False when conversion is off. Set conversion through converter, or the convert= keyword when building the field.

validate property
validate: bool

Whether the incoming value is validated.

Reads validator, the same way convert reads converter.

build property
build: bool

Whether a fresh default is built for this field per instance.

Reads factory, the same way convert reads converter.

public_name property
public_name: str

The public name of this field, used in generated methods.

public_key property
public_key: str | None

The key to use for this field in the generated dict-like interface.

Methods:

Default

Default(*values, **kwvalues)

Bases: AnnotatedField

Give a field a default value.

How it lowers

>>> Default(10)
Default(default=10)
>>> Default[int, 10]
typing.Annotated[int, Default(default=10)]

In a class

>>> class Point(Magic):
...     x: Default[float, 0.0]
...     y: Default[float, 0.0]
...
>>> Point()
Point(x=0.0, y=0.0)

Factory

Factory(*values, **kwvalues)

Bases: AnnotatedField

Build a field's default by calling something, once per instance.

Use this instead of a plain default for anything mutable: every instance gets its own object. With no argument, the factory is worked out from the field's type.

How it lowers

>>> Factory()
Factory(factory=True)
>>> Factory(list)
Factory(factory=<class 'list'>)
>>> Factory[list]
typing.Annotated[list, Factory(factory=True)]
>>> Factory[list, tuple]
typing.Annotated[list, Factory(factory=<class 'tuple'>)]

In a class

>>> class Basket(Magic):
...     items: Factory[list]
...
>>> Basket().items is Basket().items
False

ConvertTo

ConvertTo(*values, **kwvalues)

Bases: AnnotatedField

Convert whatever is passed in to the field's type.

With no argument the converter is worked out from the type; pass a callable to use your own.

How it lowers

>>> ConvertTo()
ConvertTo(converter=True)
>>> ConvertTo(int)
ConvertTo(converter=<class 'int'>)
>>> ConvertTo[int]
typing.Annotated[int, ConvertTo(converter=True)]

In a class

>>> class Server(Magic):
...     port: ConvertTo[int]
...
>>> Server("8080")
Server(port=8080)

Validate

Validate(*values, **kwvalues)

Bases: AnnotatedField

Reject a value that does not match the field's type.

With no argument the check is worked out from the type; pass a callable to use your own. Unlike ConvertTo, the value is left exactly as it was given.

How it lowers

>>> Validate()
Validate(validator=True)
>>> Validate[str]
typing.Annotated[str, Validate(validator=True)]

In a class

>>> class Server(Magic):
...     host: Validate[str]
...
>>> Server("localhost")
Server(host='localhost')
>>> Server(1234)
Traceback (most recent call last):
TypeValidationError: ...

Init

Init(*values, **kwvalues)

Bases: BoolAnnotatedField

Include a field in the generated __init__, or leave it out.

NoInit lets a field be passed neither by name nor by position: it still exists and takes its default or factory value, it just cannot be passed in.

Init is the other way round and says nothing new -- a field is a parameter unless something says otherwise -- so it changes nothing and is there to say so out loud. How the field may be passed stays with the class, or with Kw and Positional if you want to say.

How it lowers

>>> Init()
Init()
>>> NoInit()
NoInit(kw=False, positional=False)
>>> NoInit[int]
typing.Annotated[int, NoInit(kw=False, positional=False)]

Kw

Kw(*values, **kwvalues)

Bases: BoolAnnotatedField

Allow a field to be passed by keyword, or forbid it.

Pair it with Positional to say exactly how a field may be given. KwOnly and PositionalOnly are the two useful combinations, ready made; forbidding both is NoInit.

How it lowers

>>> Kw()
Kw(kw=True)
>>> NotKw()
NotKw(kw=False)
>>> KwOnly()
KwOnly(kw=True, positional=False)
>>> KwOnly[int]
typing.Annotated[int, KwOnly(kw=True, positional=False)]

Positional

Positional(*values, **kwvalues)

Bases: BoolAnnotatedField

Allow a field to be passed by position, or forbid it.

Pair it with Kw to say exactly how a field may be given. PositionalOnly and KwOnly are the two useful combinations, ready made.

How it lowers

>>> Positional()
Positional(positional=True)
>>> NotPositional()
NotPositional(positional=False)
>>> PositionalOnly()
PositionalOnly(kw=False, positional=True)

Frozen

Frozen(*values, **kwvalues)

Bases: BoolAnnotatedField

Forbid assignment to a field after the object is built.

Useful for freezing part of an otherwise mutable class.

How it lowers

>>> Frozen()
Frozen(frozen=True)
>>> NotFrozen()
NotFrozen(frozen=False)
>>> Frozen[int]
typing.Annotated[int, Frozen(frozen=True)]

In a class

>>> class Account(Magic):
...     id: Frozen[int]
...     balance: float
...
>>> account = Account(1, 0.0)
>>> account.balance = 10.0
>>> account.id = 2
Traceback (most recent call last):
AttributeError: Cannot set frozen field 'id'

Var

Var(*values, **kwvalues)

Bases: BoolAnnotatedField

Declare something that is not stored on each instance.

InitVar is passed to __init__, used, and not kept -- it reaches __pre_init__ and __post_init__ like any other argument; ClassVar is a plain class attribute, shared by every instance and absent from __init__.

How it lowers

>>> Var()
Var(var=True)
>>> InitVar()
InitVar(var=True)
>>> ClassVar()
ClassVar(kw=False, positional=False, var=True)
>>> ClassVar[str]
typing.Annotated[str, ClassVar(kw=False, positional=False, var=True)]

In a class

>>> class Counter(Magic):
...     start: int
...     unit: ClassVar[str] = "clicks"
...
>>> Counter(3)
Counter(start=3)
>>> Counter(3).unit
'clicks'

Repr

Repr(*values, **kwvalues)

Bases: BoolAnnotatedField

Show a field in the generated __repr__, or hide it.

Use HIDE_IF_NONE to show it only when it has a value.

How it lowers

>>> Repr()
Repr(repr=True)
>>> NoRepr()
NoRepr(repr=False)
>>> NoRepr[str]
typing.Annotated[str, NoRepr(repr=False)]

In a class

>>> class User(Magic):
...     name: str
...     password: NoRepr[str]
...
>>> User("ada", "hunter2")
User(name='ada')

Eq

Eq(*values, **kwvalues)

Bases: BoolAnnotatedField

Compare a field in the generated __eq__, or ignore it.

An ignored field takes no part in equality, so two objects that differ only there compare equal.

How it lowers

>>> Eq()
Eq(eq=True)
>>> NoEq()
NoEq(eq=False)
>>> NoEq[int]
typing.Annotated[int, NoEq(eq=False)]

In a class

>>> class Sample(Magic):
...     value: int
...     measured_at: NoEq[float] = 0.0
...
>>> Sample(1, 100.0) == Sample(1, 999.0)
True

Order

Order(*values, **kwvalues)

Bases: BoolAnnotatedField

Compare a field in the generated ordering, or ignore it.

Ordering is off unless the class asks for it with order=True.

How it lowers

>>> Order()
Order(order=True)
>>> NoOrder()
NoOrder(order=False)
>>> NoOrder[int]
typing.Annotated[int, NoOrder(order=False)]

Compare

Compare(*values, **kwvalues)

Bases: Eq, Order

Use a field for both equality and ordering, or for neither.

A shorthand for setting Eq and Order together.

How it lowers

>>> Compare()
Compare(eq=True, order=True)
>>> NoCompare()
NoCompare(eq=False, order=False)
>>> NoCompare[int]
typing.Annotated[int, NoCompare(eq=False, order=False)]

Hash

Hash(*values, **kwvalues)

Bases: BoolAnnotatedField

Include a field in the generated __hash__, or leave it out.

A field left out of the comparison is left out of the hash too, so you rarely need this on its own.

How it lowers

>>> Hash()
Hash(hash=True)
>>> NoHash()
NoHash(hash=False)
>>> NoHash[int]
typing.Annotated[int, NoHash(hash=False)]

Key

Key(*values, **kwvalues)

Bases: BoolAnnotatedField

Include a field in the dict-like interface, or leave it out.

Only relevant on a class built with mapping=True. Pass a string to use a different key from the field name.

How it lowers

>>> Key()
Key(key=True)
>>> NotKey()
NotKey(key=False)
>>> Key("id")
Key(key='id')
>>> NotKey[int]
typing.Annotated[int, NotKey(key=False)]

In a class

>>> class Row(Magic, mapping=True):
...     name: str
...     cached: NotKey[int] = 0
...
>>> dict(Row("ada"))
{'name': 'ada'}

Doc

Doc(documentation: str)

Bases: AnnotatedField, Doc

Document a field.

The text appears in the class docstring and in the documentation of the generated __init__.

How it lowers

>>> Doc("how many times to retry")
Doc(doc='how many times to retry')
>>> Doc[int, "how many times to retry"]
typing.Annotated[int, Doc(doc='how many times to retry')]

Functions:

field

field(**kwargs: Any) -> Any

Describe one field, for use as its default value.

class Task(Magic):
    name: str
    tags: list = field(factory=list)
    token: str = field(default="", repr=False)

Takes the same arguments as Field and produces the same object. The difference is for type checkers: field(...) declares its return type as the annotated type, so tags: list = field(...) reads cleanly. Field(...) in that position also works.