Skip to content

bagof.magic._fields

Attributes

REQUIRED module-attribute

REQUIRED = REQUIRED

A default that says the field has none.

Written as the default of a field that inherits, as in x: ... = REQUIRED, it removes the default the field inherited, so the field has to be passed.

PIN module-attribute

PIN: Literal['pin'] = 'pin'

Give the field the value the subclass stands for, as its default.

CLASSVAR module-attribute

CLASSVAR: Literal['classvar'] = 'classvar'

Make the field a class attribute that holds the value.

An argument for the field is still accepted, and dropped.

KEEP module-attribute

KEEP: Literal['keep'] = 'keep'

Leave the field as it is.

NARROW module-attribute

NARROW: Literal['narrow'] = 'narrow'

The same as PIN_NARROW.

PIN_NARROW module-attribute

PIN_NARROW: Literal['pin+narrow'] = 'pin+narrow'

Like PIN, and also refuse any other value for the field.

CLASSVAR_NARROW module-attribute

CLASSVAR_NARROW: Literal['classvar+narrow'] = 'classvar+narrow'

Like CLASSVAR, and also refuse any other value for the field.

KEEP_NARROW module-attribute

KEEP_NARROW: Literal['keep+narrow'] = 'keep+narrow'

Like KEEP, and also refuse any other value for the field.

Classes

Field

Field(*arg, **kwargs)

Bases: SlotsBase

A single field in a Magic class.

Every annotation in a Magic class body becomes a Field. A Field is rarely created directly: the annotation family, such as Factory, KwOnly and ConvertTo, and the field() function are the usual ways to declare one.

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, from which the converter, the validator and the default factory are worked out when those are turned on.

required
default any

The default value.

required
factory bool or Callable[[], any]

How a fresh default is built for each instance, instead of one value being shared between instances. False builds nothing, True works the factory out from the type, and a callable is called to build the default. The build property reads this slot as a boolean that says whether a default is built, and the build= keyword is an alias that sets it.

`Options().factory`
init bool

Whether the field is a parameter of __init__. It is not stored, but read as kw or positional. init=False forbids both ways of passing the field, and init=True changes nothing, because a field is a parameter unless something says otherwise. Assigning field.init = value later sets both kw and positional.

required
repr bool or Callable[[any], any]

Whether the field is shown in the generated __repr__. A function of the value decides how the field is shown: a string that it returns is shown in place of the value, None or False hides the field, and any other result shows repr(value) if it is true and hides the field if it is false. ShowIf, HideIf, HideIfNone and HideIfDefault are ready-made tests. For a pseudo-field the default is False.

True
hash bool

Whether the field counts towards the generated __hash__. By default it follows eq, since equal instances must have equal hashes: a field that __eq__ compares with == is hashed, and one that it leaves out or compares with a function is not, because two values that the function calls equal can still hash differently.

None (follows `eq`)
eq bool or Callable[[any, any], bool]

Whether the field counts towards the generated __eq__, and how. True compares the two values with ==, and False leaves the field out. A function is called with the field's value on each of the two objects, and the objects can be equal only when its answer is true. This suits a value whose == does not answer with a single True or False, such as an array.

True
order bool

Whether the field counts towards the generated ordering methods. A field left out of __eq__ is left out of the ordering too, and eq=False together with order=True is an error. A field compared with a function is left out of the ordering unless order=True is given, and then the values themselves are ordered, since the function says only whether two values are equal.

follows `eq`
metadata dict

Arbitrary user-defined metadata.

required
kw bool

Whether the field may be passed by keyword. Together with positional=False, it makes the field keyword-only. When both are False, the field takes its default, as with init=False.

`not Options().positional_only`
positional bool

Whether the field may be passed by position. Together with kw=False, it makes the field positional-only.

`not Options().kw_only`
frozen bool

Forbid assignment after construction.

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

How an incoming value is converted. False leaves it unconverted, True uses a converter worked out from the type, and a callable converts the value itself. The convert property reads this slot as a boolean, and the convert= keyword is an alias that sets it.

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

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

`Options().validate`
var bool

Whether the field is a pseudo-field. An InitVar is passed to the constructor but not stored, and a ClassVar is a class attribute, shared by every instance and absent from the constructor. The InitVar and ClassVar annotations are usually clearer.

False
doc str

The field's documentation, which can also be given with the Doc annotation.

required
key bool | str | ShowIf | tuple

Whether the field is a key of the dict-like interface. A string is used as the key name. A test such as ShowIf(bool) or HideIfNone() keeps the key only while the test passes, and a (name, test) pair gives both a name and a test.

`Options().mapping`
alias str | sequence[str] | bool

The input name, or the input names in order of preference. The first name is used in the signature, in the repr and as the mapping key. By default the field is known by its own name without any leading underscore. True adds the names of the enabled properties after that default public name, and False keeps the stored name, leading underscore included.

required
property str | sequence[str] | mapping | bool

Forwarding attributes. A name or a sequence of names creates properties that can be read and written. A mapping chooses True (or "readwrite"), "readonly" or False for each name. True or "readonly" on its own exposes the preferred public name with that access. "all" exposes every input alias that the field accepts, other than the stored attribute and any name already in use, for reading and writing.

False
pin str or bool

What a subclass that matches on this field does with it. When it is left out, each such subclass uses its own pin_discriminant setting. It takes the same values as pin_discriminant, which are "pin", "classvar", "keep", "narrow", "pin+narrow", "classvar+narrow" and "keep+narrow", and also True for "pin" and False for "keep". It wins over pin_discriminant on every class that matches on the field, including subclasses that inherit the field. It can also be given with the Pin, Narrow and NoPin annotations.

required
inherit bool or str

How the field relates to a field the class inherits. False removes the inherited field of this name instead of declaring one: it is no longer a parameter, part of the repr, the comparisons or the dict-like view, and its public name is free for another field. True changes the inherited field of this name: the field keeps everything the base class declared for it, except what this annotation says again. A string names an inherited field for this field to take over: that field leaves the class, and this one starts from its declaration, its public name and its place in the constructor. These are usually written as x: Forget, x: Frozen[...] and _x: Inherit[..., "x"].

required
position bool

Read only when the field inherits, that is when inherit is True or a name. True puts the field where the inherited field was, so that positional arguments keep their meaning. False puts it where it is written in the class body, as a new field would be.

True

Other Parameters:

Name Type Description
compare bool

A shorthand that sets both eq and order. It takes only True or False: a function to compare with goes on eq.

kw_only bool

The dataclasses spelling. kw_only=True makes the field keyword-only, as the KwOnly annotation does. kw_only=False lets it be passed by position or by keyword, whatever the class says.

default_factory Callable[[], any]

The dataclasses spelling of factory: called to build a fresh default for each instance.

Raises:

Type Description
TypeError

If a keyword that is not listed here is given, or if both factory and default_factory are given.

Attributes

init property writable
init: bool

Whether the generated __init__ takes this field.

The property is computed from kw and positional. It is True when the field can be passed by keyword, by position or both, and False when it can be passed neither way.

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

convert property
convert: bool

Whether an incoming value is converted.

The property reads converter. It is True for a converter worked out from the type and for a callable given directly, and False when conversion is off. Conversion is set through converter, or through the convert= keyword when the field is built.

validate property
validate: bool

Whether an incoming value is validated.

The property reads validator, in the same way that convert reads converter.

build property
build: bool

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

The property reads factory, in the same way that convert reads converter.

aliases property
aliases: tuple[str, ...]

The accepted input names, with the preferred public name first.

properties property
properties: Mapping[str, bool | str]

A copy of the forwarding attribute names and their access modes.

public_name property
public_name: str

The preferred public name, which the generated methods use.

public_key property
public_key: str | None

The key of this field in the generated dict-like interface.

Methods:

Alias

Alias(*values, **kwvalues)

Bases: AnnotatedField

Accept one or more input names for a field, in order of preference.

Alias[str, ("label", "name")] accepts either keyword, and label is preferred. Alias[str] adds the names of the field's enabled properties.

Property

Property(*values, **kwvalues)

Bases: AnnotatedField

Expose forwarding attributes for a stored field.

Property[str, "label"] makes label an attribute that reads and writes the field, and Property[str, {"label": "readonly"}] makes it read-only. A sequence of names gives every name read and write access. Without names, the preferred public name is exposed, and Property[str, "all"] exposes every input alias that the field accepts.

ReadOnlyProperty

ReadOnlyProperty(*values: Any, **kwvalues: Any)

Bases: Property

Expose read-only forwarding attributes for a stored field.

ReadOnlyProperty is the read-only counterpart of Property: ReadOnlyProperty[str, names] exposes the same attributes as Property[str, names], but forbids assignment through them. ReadOnlyProperty[str, "label"] reads the field through label. Without names, the preferred public name is exposed, and ReadOnlyProperty[str, "all"] exposes every input alias.

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 a factory, once for each instance.

A factory suits any mutable default, because every instance receives its own object. Without an 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.

Without an argument, the converter is worked out from the type. A callable given as the argument is used as the converter instead.

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.

Without an argument, the check is worked out from the type. A callable given as the argument is used as the validator instead. Unlike ConvertTo, Validate leaves the value 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. The field still exists and takes its default or factory value, but it cannot be passed to the constructor.

Init says the opposite, which is already true of every field unless something says otherwise. It therefore changes nothing, and it serves to state the intent explicitly. How the field may be passed is still decided by the class, or by Kw and Positional.

How it lowers

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

NoInit

NoInit(*values, **kwvalues)

Bases: Init, InversedBoolAnnotatedField

Leave a field out of the generated __init__.

x: NoInit[int] can be passed neither by name nor by position, so the field takes its default or factory value, or holds nothing until something sets it. It is the opposite of Init.

Kw

Kw(*values, **kwvalues)

Bases: BoolAnnotatedField

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

Together with Positional, Kw says exactly how a field may be passed. KwOnly and PositionalOnly are the two useful combinations, ready-made, and forbidding both ways 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)]

NotKw

NotKw(*values, **kwvalues)

Bases: Kw, InversedBoolAnnotatedField

Forbid passing a field by keyword.

x: NotKw[int] can still be passed by position, unless something forbids that too. It is the opposite of Kw.

Positional

Positional(*values, **kwvalues)

Bases: BoolAnnotatedField

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

Together with Kw, Positional says exactly how a field may be passed. 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)

NotPositional

NotPositional(*values, **kwvalues)

Bases: Positional, InversedBoolAnnotatedField

Forbid passing a field by position.

x: NotPositional[int] can still be passed by keyword, unless something forbids that too. It is the opposite of Positional.

KwOnly

KwOnly(*values, **kwvalues)

Bases: Kw, NotPositional

Make a field keyword-only.

x: KwOnly[int] can be passed by name but not by position, as with field(kw_only=True). It combines Kw with NotPositional.

NotKwOnly

NotKwOnly(*values, **kwvalues)

Bases: Positional

Let a field be passed by position as well as by keyword.

x: NotKwOnly[int] undoes a keyword-only setting, such as kw_only=True on the class, and leaves passing by keyword as it was. It is the same as Positional.

PositionalOnly

PositionalOnly(*values, **kwvalues)

Bases: Positional, NotKw

Make a field positional-only.

x: PositionalOnly[int] can be passed by position but not by name. It combines Positional with NotKw.

NotPositionalOnly

NotPositionalOnly(*values, **kwvalues)

Bases: Kw

Let a field be passed by keyword as well as by position.

x: NotPositionalOnly[int] undoes a positional-only setting, such as positional_only=True on the class, and leaves passing by position as it was. It is the same as Kw.

Frozen

Frozen(*values, **kwvalues)

Bases: BoolAnnotatedField

Forbid assignment to a field after the object is built.

Frozen freezes 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'

NotFrozen

NotFrozen(*values, **kwvalues)

Bases: Frozen, InversedBoolAnnotatedField

Allow assignment to a field after the object is built.

x: NotFrozen[int] stays assignable on a class built with frozen=True. It is the opposite of Frozen.

Var

Var(*values, **kwvalues)

Bases: BoolAnnotatedField

Declare a pseudo-field, which is not stored on each instance.

An InitVar is passed to __init__ and used, but not kept. It reaches __pre_init__ and __post_init__ like any other argument. A 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'

InitVar

InitVar(*values, **kwvalues)

Bases: Var

Declare a value that is passed to __init__ but not stored.

x: InitVar[float] reaches __pre_init__ and __post_init__ like any other argument, and is not kept on the instance. Var describes both kinds of pseudo-field.

ClassVar

ClassVar(*values, **kwvalues)

Bases: Var, NoInit

Declare a class attribute that is not a field.

x: ClassVar[str] = "clicks" is shared by every instance and is not a parameter of __init__. Var describes both kinds of pseudo-field.

Repr

Repr(*values, **kwvalues)

Bases: BoolAnnotatedField

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

Besides True and False, a field's repr setting can be a function of the value, and the function's result decides what is shown:

  • a string is shown as the value's text, in place of repr(value);
  • None or False hides the field;
  • True, or anything else that is true, shows repr(value);
  • anything else that is false hides the field.

A function can therefore either format the value or decide whether the field is shown. ShowIf, HideIf, HideIfNone and HideIfDefault are ready-made functions of the second kind.

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')

Formatting the value

>>> one_place = "{:.1f}".format
>>> class Reading(Magic):
...     celsius: Repr[float, one_place]
...
>>> Reading(21.456)
Reading(celsius=21.5)

NoRepr

NoRepr(*values, **kwvalues)

Bases: Repr, InversedBoolAnnotatedField

Hide a field from the generated __repr__.

x: NoRepr[str] is never shown, which suits a password or a token. It is the opposite of Repr.

ShowIf

ShowIf(predicate: Callable[[Any], Any], /, **kwargs: Any)

Bases: Repr

Show a field in __repr__ only while a test of its value passes.

ShowIf(test) calls test(value) and shows the field when the result is true. On a dict-like class it also works as a key setting, as in Key(ShowIf(test)), where it leaves the key out while the test fails.

After the test, ShowIf takes the same keywords as field(), so it can be written as a field's value: notes: str = ShowIf(bool, default=""). A test given such keywords also declares the field, and so it cannot be used as a repr or key setting on its own.

How it lowers

>>> ShowIf(bool)
ShowIf(<class 'bool'>)
>>> ShowIf[int, bool]
typing.Annotated[int, ShowIf(<class 'bool'>)]
>>> Repr(ShowIf(bool))
Repr(repr=ShowIf(<class 'bool'>))

In a class

>>> class Order(Magic):
...     item: str
...     notes: ShowIf[str, bool] = ""
...
>>> Order("tea")
Order(item='tea')
>>> Order("tea", "no sugar")
Order(item='tea', notes='no sugar')

HideIf

HideIf(predicate: Callable[[Any], Any], /, **kwargs: Any)

Bases: ShowIf

Hide a field from __repr__ while a test of its value passes.

HideIf is the opposite of ShowIf: HideIf(test) hides the field when test(value) is true. Like ShowIf, it takes the keywords of field() after the test.

In a class

>>> class Retry(Magic):
...     attempts: HideIf[int, lambda n: n < 0] = -1
...
>>> Retry()
Retry()
>>> Retry(3)
Retry(attempts=3)

HideIfNone

HideIfNone(**kwargs: Any)

Bases: HideIf

Hide a field from __repr__ while it holds None.

The class can be written wherever an instance can, so Field(repr=HideIfNone) is the same as Field(repr=HideIfNone()). Given as a class setting, as in class C(Magic, repr=HideIfNone()), it applies to every field.

It takes the keywords of field(), so it can be written as a field's value: nickname: Optional[str] = HideIfNone(default=None).

In a class

>>> class Person(Magic):
...     name: str
...     nickname: HideIfNone[tx.Optional[str]] = None
...
>>> Person("Margaret")
Person(name='Margaret')
>>> Person("Margaret", "Peggy")
Person(name='Margaret', nickname='Peggy')

HideIfDefault

HideIfDefault(**kwargs: Any)

Bases: HideIf

Hide a field from __repr__ while it holds its default.

A value counts as the default when it is the default object or compares equal to it, and a value that cannot be compared with the default is shown. The field needs a plain default to compare with, so a default built by a factory is refused, because each instance has its own.

Given as a class setting, as in class C(Magic, repr=HideIfDefault()), it applies to every field that has a plain default, and the other fields are always shown.

It takes the keywords of field(), so it can be written as a field's value together with the default it compares with: method: str = HideIfDefault(default="GET").

In a class

>>> class Request(Magic):
...     url: str
...     method: HideIfDefault[str] = "GET"
...
>>> Request("/home")
Request(url='/home')
>>> Request("/home", "POST")
Request(url='/home', method='POST')

Eq

Eq(*values, **kwvalues)

Bases: BoolAnnotatedField

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

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

Besides True and False, a field's eq setting can be a function of two values, which is called with the field's value on each of the two objects. The objects can be equal only when its answer is true. A function suits a value whose == does not answer with a single True or False: numpy.array_equal compares two arrays as wholes, where == compares them element by element. A field compared with a function is left out of the hash, since two values that the function calls equal can still hash differently, and it is left out of the ordering unless the field says order=True.

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

Comparing with a function

>>> def same_text(a, b):
...     return a.casefold() == b.casefold()
...
>>> class Tag(Magic):
...     label: Eq[str, same_text]
...
>>> Tag("Python") == Tag("PYTHON")
True
>>> Tag("Python") == Tag("Rust")
False

NoEq

NoEq(*values, **kwvalues)

Bases: Eq, InversedBoolAnnotatedField

Leave a field out of the generated __eq__.

Two objects that differ only in x: NoEq[float] compare equal, and a field left out of equality is left out of the ordering and the hash too. It is the opposite of Eq.

Order

Order(*values, **kwvalues)

Bases: BoolAnnotatedField

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

The ordering methods are generated only when the class asks for them with order=True.

How it lowers

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

NoOrder

NoOrder(*values, **kwvalues)

Bases: Order, InversedBoolAnnotatedField

Leave a field out of the generated ordering methods.

x: NoOrder[int] still counts towards ==. It is the opposite of Order.

Compare

Compare(*values, **kwvalues)

Bases: Eq, Order

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

Compare is a shorthand for 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)]

NoCompare

NoCompare(*values, **kwvalues)

Bases: Compare, InversedBoolAnnotatedField

Leave a field out of both equality and ordering.

x: NoCompare[int] is NoEq and NoOrder together. It is the opposite of Compare.

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 Hash is rarely needed on its own.

How it lowers

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

NoHash

NoHash(*values, **kwvalues)

Bases: Hash, InversedBoolAnnotatedField

Leave a field out of the generated __hash__.

x: NoHash[int] still counts towards ==. It is the opposite of Hash.

Key

Key(*values, **kwvalues)

Bases: BoolAnnotatedField

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

Key matters only on a class built with mapping=True. A string given as the argument is used as the key in place of the field's 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'}

A test such as ShowIf(bool) or HideIfNone() keeps the key only while the test passes. A name given before the test renames the key as well.

With a test

>>> class Post(Magic, mapping=True):
...     title: str
...     tags: tx.Annotated[tuple, Key("labels", ShowIf(bool))] = ()
...
>>> dict(Post("hi"))
{'title': 'hi'}
>>> dict(Post("hi", ("news",)))
{'title': 'hi', 'labels': ('news',)}

NotKey

NotKey(*values, **kwvalues)

Bases: Key, InversedBoolAnnotatedField

Leave a field out of the dict-like interface.

On a class built with mapping=True, x: NotKey[int] is not a key, although asdict still includes it. It is the opposite of Key.

Doc

Doc(documentation: str, /, **kwargs: Any)

Bases: AnnotatedField, Doc

Document a field.

The text appears in the class docstring and in the documentation of the generated __init__. After the text, Doc takes the same keywords as field(), so it can be written as a field's value with a default: retries: int = Doc("how many times to retry", default=3).

How it lowers

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

Pin

Pin(*values, **kwvalues)

Bases: AnnotatedField

Decide what a subclass that matches on this field does with it.

A subclass written with on={"mode": "minor"} gives mode that value. Pin decides how the value is given to this field, whatever the subclass's pin_discriminant setting says. It takes the same values as that setting, which are "pin" (the default), "classvar", "keep", "narrow", "pin+narrow", "classvar+narrow" and "keep+narrow", and also True for "pin" and False for "keep". Narrow[T] is Pin[T, "narrow"], and NoPin[T] is Pin[T, False].

Each mode is also available as a constant: PIN, CLASSVAR, KEEP, NARROW, PIN_NARROW, CLASSVAR_NARROW and KEEP_NARROW. A linter reads a constant as a name that it knows, whereas it reads "classvar" inside the brackets as an undefined type.

The field keeps its Pin in every subclass, so every class that matches on the field treats it in the same way.

How it lowers

>>> Pin()
Pin(pin='pin')
>>> Pin[str]
typing.Annotated[str, Pin(pin='pin')]
>>> Pin[str, "classvar"]
typing.Annotated[str, Pin(pin='classvar')]
>>> Pin[str, CLASSVAR]
typing.Annotated[str, Pin(pin='classvar')]
>>> Narrow[str]
typing.Annotated[str, Narrow(pin='narrow')]
>>> NoPin[str]
typing.Annotated[str, NoPin(pin=False)]

In a class

>>> class Shape(Magic, polymorphic=True):
...     kind: Pin[str, "classvar"] = ""
...     size: float = 1.0
...
>>> class Circle(Shape, on={"kind": "circle"}):
...     pass
...
>>> Circle.kind
'circle'
>>> Shape(kind="circle", size=2.0)
Circle(size=2.0)

Narrow

Narrow(*values, **kwvalues)

Bases: Pin, BoolAnnotatedField

Pin a field, and narrow it to what the subclass stands for.

x: Narrow[str] is Pin[str, "narrow"]: a subclass that matches on the field gives it the matched value as its default, narrows its type, and refuses any other value. Pin describes every mode.

NoPin

NoPin(*values, **kwvalues)

Bases: Pin, BoolAnnotatedField

Leave a field as the subclass that matches on it wrote it.

x: NoPin[str] is Pin[str, False], the same as the "keep" mode, whatever the subclass's pin_discriminant setting says. Pin describes every mode.

Forget

Forget(*values, **kwvalues)

Bases: AnnotatedField

Remove a field the class inherits.

Forget is written on its own as the annotation, as in y: Forget. The field is gone from this class and from every class that inherits from it: the constructor no longer takes it, and the repr, the comparisons and the dict-like view leave it out. Its public name is free again, so another field may take it. A subclass further down that declares the field again creates a new field.

A value written beside Forget becomes an ordinary class attribute. When no value is written, the name is looked up as on any class, so reading it can still find what a base class holds under that name, such as the field's old default.

A field by which a base class chooses its subclasses can be forgotten too. Passing that field to the class is then an error that names the class that forgot it, and a base class that is choosing a subclass passes over the forgetting class whenever the call passes that field.

How it lowers

>>> Forget()
Forget(inherit=False)

In a class

>>> class Point(Magic):
...     x: int
...     y: int = 0
...
>>> class Tick(Point):
...     y: Forget
...
>>> Tick(3)
Tick(x=3)

Inherit

Inherit(*values: Any, **kwvalues: Any)

Bases: AnnotatedField

Build a field from a field the class inherits.

A field that a subclass declares again normally replaces the inherited field completely, so the default, the aliases and every other setting written in the base class are lost. Inherit keeps them instead. Written as the value of a field whose type is ..., as in x: ... = Inherit(frozen=True), it starts the field from what the base class declared and changes only the settings it is given. Inherit accepts every keyword that Field accepts. A plain value after ..., as in x: ... = 5, changes nothing but the default, and a default of REQUIRED removes the inherited default.

Given the name of another inherited field, as in _x: ... = Inherit("x"), Inherit takes that field over. The inherited field leaves the class, and the new field starts from the inherited field's declaration, its public name and its place in the constructor. The class also receives a property under the public name that reads and assigns the new field, unless the class body defines something under that name itself. The public name can be changed with alias=, in which case no property is made, and position=False places the field where it is written in the class body rather than where the inherited field was.

Inherit can also be written in the brackets of an annotation, where the first argument is the type, as for every annotation in the family. x: Inherit[float] keeps the inherited field but gives it a new type, and a converter, validator or factory that was worked out from the old type is worked out again from the new one. Inherit["Node"] is read as a forward reference to a class named Node. Inherit[..., "x"] keeps the inherited type and takes over x. Linters such as Ruff and Pyflakes read the string "x" in that annotation as a forward reference and report it as an undefined name (F821), so code that a linter checks either adds # noqa: F821 to the line or writes Inherit as the value instead.

Every other annotation in the family accepts ... as its type in the same way, so x: Frozen[...] is another spelling of x: ... = Inherit(frozen=True). These spellings work on every supported version of Python, whereas Annotated[..., Frozen()] written by hand works only from Python 3.12, because older versions of Annotated refuse ... as a type.

How it lowers

>>> Inherit()
Inherit(inherit=True)
>>> Inherit("x", position=False)
Inherit(inherit='x', position=False)
>>> Inherit[float]
typing.Annotated[float, Inherit(inherit=True)]
>>> Inherit[..., "x"]
typing.Annotated[..., Inherit(inherit='x')]

Changing an inherited field

>>> class Point(Magic):
...     x: int = 0
...     y: int = 0
...
>>> class Pinned(Point):
...     x: ... = Inherit(frozen=True)
...     y: ... = 1
...
>>> Pinned()
Pinned(x=0, y=1)

Taking a field over

>>> class Account(Magic):
...     balance: int = 0
...
>>> class Audited(Account):
...     _balance: ... = Inherit("balance")
...
>>> Audited(10)
Audited(balance=10)
>>> Audited(10).balance
10
>>> [f.name for f in fields(Audited)]
['_balance']

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)
    retries: int = field(default=0, kw_only=True)

field() takes the same arguments as Field and returns the same object, and it refuses a keyword that it does not recognise. The two differ only for type checkers: field(...) declares its return type as the annotated type, so a checker accepts tags: list = field(...). Field(...) in that position also works when the program runs.