# Atoms
Use frozen atoms inside Annotated. Limits validate values; notation is stored
for consumers and does not change runtime validation.
from typing import Annotated
from pytypehint import Label, Max, Min
Quantity = Annotated[int, Min(1), Max(100), Label("Quantity")]
| Shape | Accepted atoms |
|---|---|
Int |
Min, Max, Choices, MultipleOf, Step, Slider, Placeholder, Extra |
Float |
Min, Max, Choices, Step, Slider, Placeholder, Extra |
Str |
Min, Max, Choices, Pattern, FileHint, IsPassword, Rows, Placeholder, Extra |
Date, Time |
Min, Max, Choices, Placeholder, Extra |
List, Tuple |
Min, Max, Extra |
Bool, NoneShape, EnumShape |
Extra |
Nested dataclass (Struct) |
No shape atoms |
| Any field | Label, Description |
Field allowing None |
OptionalToggle |
| Atom | Rule |
|---|---|
Min(value, exclusive=False), Max(...) |
Bounds; strings and sequences use nonnegative integer lengths, inclusive only |
Choices(values=(...)) |
Nonempty tuple of unique, hashable, exact-type values satisfying all other limits |
MultipleOf(value) |
Positive integer divisor; Int only |
Pattern(regex, message=None) |
Python re.fullmatch; optional custom mismatch message |
Step(value) |
Positive finite numeric notation |
Slider(show_value=True) |
Numeric notation; requires both bounds |
Label(text), Description(text) |
Nonempty field text |
Placeholder(text) |
Nonempty scalar input notation |
IsPassword(), Rows(n) |
String notation; rows must be positive |
OptionalToggle(enabled) |
Boolean field notation; absent leaves the consumer's choice |
Extra(key, value) |
Namespaced key containing a dot; string value, including empty |
exclusive and message are keyword-only. Numeric atoms must match their shape's
rules; Float allows finite integer bounds, but choices remain exact floats.
Compilation rejects unsupported atoms and contradictions it can determine:
empty ranges, impossible integer multiples, invalid choices, missing slider
bounds and OptionalToggle without a None option. It does not prove general
satisfiability of combined regex and length constraints.
# File names
FileHint(extensions=(), min_size=None, max_size=None) marks a string as a file
name. Extensions must be unique lowercase dotted suffixes. Sizes are nonnegative
integer byte counts, with min_size <= max_size when both are present.
The core checks suffixes case-insensitively, after string lengths and pattern and before choices. Empty extensions accept any suffix. Defaults and choices receive the same check. File existence, file kind and sizes are checked by the consumer; pytypehint never reads the filesystem or changes the string into a path object.
# Layering and extras
For repeated atom classes, the outer/rightmost atom wins:
Percent = Annotated[int, Min(0), Max(100)]
Narrow = Annotated[Percent, Max(50)]
Extra merges per key. Shapes store sorted unique pairs and return a fresh
extras dictionary on each access; modifying it does not change the shape.
Manually supplied _extras must be a tuple of unique string pairs.
Put type atoms inside individual union options. Field atoms from different options must agree unless an outer field atom overrides them. Field atoms are not allowed on list items or tuple positions.