Reference¶
Every public module, listed in full. See Usage and How it works for the explanations behind them.
Forms¶
WalkOutcome
module-attribute
WalkOutcome = typing.Union[pydantic_forms.core.shared.NextPage, pydantic_forms.core.shared.Complete, pydantic_forms.core.shared.Rejected]
Complete
dataclass
Every page was accepted: the result of the form.
state
instance-attribute
state: pydantic_forms.types.State
DisplayOnlyFieldType
FormFieldError
Bases: pydantic.BaseModel
One message of the verdict on a rejected page, as data.
loc
class-attribute
instance-attribute
loc: list[typing.Union[int, str]] = pydantic.Field(description='Path of the rejected value on the page: the field name, then an index or a sub-field. ``["__root__"]`` for a page-level error.')
msg
class-attribute
instance-attribute
msg: str = pydantic.Field(description='The translated validation error.')
type
class-attribute
instance-attribute
type: str = pydantic.Field(description='pydantic\'s error type, e.g. "missing", "enum", "value_error".')
FormPage
Bases: pydantic.BaseModel
model_config
class-attribute
instance-attribute
model_config = pydantic.ConfigDict(arbitrary_types_allowed=True, title='unknown', extra='forbid', validate_default=True)
meta__
class-attribute
meta__: pydantic_forms.types.FormMeta | None = None
Data about the page itself, passed to the frontend alongside its JSON schema.
Set it on a subclass to tell the frontend something the schema cannot express, such as whether
another page follows (hasNext). It travels out as the meta key of the FormNotCompleteError
response, see form_meta(). Being a ClassVar it is not a form field, so it stays out of the
schema and the validated result.
Relabel or restyle the previous/next buttons with a ButtonsConfig field, or else with
customButtons here. The two can’t be combined.
form_meta
classmethod
form_meta() -> FormMeta | None
Return meta__ with the page’s ButtonsConfig field added.
NextPage
dataclass
The form asks for another page: its model, and its index (the number of pages accepted before it).
model
instance-attribute
model: pydantic_forms.types.InputForm
index
instance-attribute
index: int
Rejected
dataclass
A page did not validate: which one, its model, and the verdict.
model
instance-attribute
model: pydantic_forms.types.InputForm
index
instance-attribute
index: int
error
instance-attribute
error: pydantic_forms.exceptions.FormValidationError
form_errors
form_errors(error: pydantic_forms.exceptions.FormValidationError) -> list[FormFieldError]
The verdict on a rejected page as data: one FormFieldError per message.
list_forms
list_forms() -> list[str]
page_schema
page_schema(page: type[pydantic.BaseModel], *, inline_refs: bool = False) -> JsonSchemaValue
The JSON schema of a page.
A frozen field of the page is marked readOnly: the page sets its value itself and ignores anything submitted
for it. A frozen field of a nested model is regular input and carries no marker.
With inline_refs every $ref to a $defs entry is replaced by that entry, so a program gets one
self-contained document per page; see pydantic_forms.utils.schema.inline_refs.
register_form
register_form(key: str, form: typing.Callable) -> None
generate_form
generate_form(form_generator: typing.Union[pydantic_forms.types.StateInputFormGenerator, None], state: pydantic_forms.types.State, user_inputs: list[pydantic_forms.types.State], lang: str = 'en_US', extra_translations: typing.Union[dict[str, str], None] = None) -> Union[State, None]
Generate form using form generator as defined by a form definition.
post_form
post_form(form_generator: typing.Union[pydantic_forms.types.StateInputFormGenerator, None], state: pydantic_forms.types.State, user_inputs: list[pydantic_forms.types.State], locale: str = 'en_US', extra_translations: typing.Union[dict[str, str], None] = None) -> State
Post user_input based ond serve a new form if the form wizard logic dictates it.
start_form
start_form(form_key: str, user_inputs: typing.Union[list[pydantic_forms.types.State], None] = None, user: str = 'Just a user', locale: str = 'en_US', extra_translations: typing.Union[dict[str, str], None] = None, **extra_state: dict[str, typing.Any]) -> State
Handle the logic for the endpoint that the frontend uses to render a form with or without prefilled input.
Args:
form_key: name of form in the FORM dict
user_inputs: List of form inputs from frontend
user: User who starts this form
locale: Language of the form
extra_translations: Extra translations to apply to the form
extra_state: Optional initial state variables
Returns:
The data that the user entered into the form
walk_form
walk_form(form_generator: typing.Union[pydantic_forms.types.StateInputFormGenerator, None], state: pydantic_forms.types.State, user_inputs: list[pydantic_forms.types.State], locale: str = 'en_US', extra_translations: typing.Union[dict[str, str], None] = None) -> WalkOutcome
Feed user_inputs to the form page by page and say where that ends: the next page, the result, or a rejected page.
NextPage carries the model of the pending page and its index, Complete the result of the whole form,
and Rejected the page that did not validate, with its verdict. A program that reads a form goes through
this; post_form wraps it for use in an API.
Raises: FormOverflowError: more inputs than the form has pages.
Async variants¶
generate_form
async
generate_form(form_generator: typing.Union[pydantic_forms.types.StateInputFormGeneratorAsync, None], state: pydantic_forms.types.State, user_inputs: list[pydantic_forms.types.State], locale: str = 'en_US', extra_translations: typing.Union[dict[str, str], None] = None) -> Union[State, None]
Generate form using form generator as defined by a form definition.
walk_form
async
walk_form(form_generator: typing.Union[pydantic_forms.types.StateInputFormGeneratorAsync, None], state: pydantic_forms.types.State, user_inputs: list[pydantic_forms.types.State], locale: str = 'en_US', extra_translations: typing.Union[dict[str, str], None] = None) -> WalkOutcome
Feed user_inputs to the form page by page and say where that ends: the next page, the result, or a rejected page.
The async variant of walk_form: the same three endings, through asend. An async generator cannot
return, so a yielded dict is the result and ends the walk as Complete.
Raises: FormOverflowError: more inputs than the form has pages.
post_form
async
post_form(form_generator: typing.Union[pydantic_forms.types.StateInputFormGeneratorAsync, None], state: pydantic_forms.types.State, user_inputs: list[pydantic_forms.types.State], locale: str = 'en_US', extra_translations: typing.Union[dict[str, str], None] = None) -> State
Post user_input based ond serve a new form if the form wizard logic dictates it.
start_form
async
start_form(form_key: str, user_inputs: typing.Union[list[pydantic_forms.types.State], None] = None, user: str = 'Just a user', locale: str = 'en_US', extra_translations: typing.Union[dict[str, str], None] = None, **extra_state: typing.Any) -> State
Handle the logic for the endpoint that the frontend uses to render a form with or without prefilled input.
Args:
form_key: name of form in the FORM dict
user_inputs: List of form inputs from frontend
user: User who starts this form
locale: Language of the form
extra_translations: Extra translations to apply to the form
extra_state: Optional initial state variables
Returns:
The data that the user entered into the form
Field types¶
Callout
module-attribute
Callout = pydantic_forms.validators.components.callout._Callout
A callout box. Use callout() to set its content and styling.
ContactPersonName
module-attribute
ContactPersonName = str
A string tagged so the frontend renders it as a contact person name.
DisplaySubscription
module-attribute
DisplaySubscription = uuid.UUID
Displays a subscription, identified by UUID. Display-only: it collects no input.
Deprecated: use orchestrator.forms.validators.DisplaySubscription instead.
Divider
module-attribute
Divider = typing.Optional[str]
A horizontal separator line. Display-only: it collects no input.
Hidden
module-attribute
Hidden = typing.Optional[str]
A field that is not shown to the user. Display-only: it collects no input.
Label
module-attribute
Label = typing.Optional[str]
A plain string formatted as a label. Display-only: it collects no input.
ListOfOne
module-attribute
ListOfOne = list[pydantic_forms.validators.components.unique_constrained_list.T]
A list constrained to exactly one item.
ListOfTwo
module-attribute
ListOfTwo = list[pydantic_forms.validators.components.unique_constrained_list.T]
A list constrained to exactly two items, which must differ from each other.
LongText
module-attribute
LongText = str
A multi-line text input.
Markdown
module-attribute
Markdown = pydantic_forms.validators.components.markdown._Markdown
A block of rendered Markdown. Use markdown() to set its content.
MigrationSummary
module-attribute
MigrationSummary = pydantic_forms.validators.components.migration_summary._MigrationSummary
A static summary table. Use migration_summary() to supply its data.
OrganisationId
module-attribute
OrganisationId = str
A string tagged for organisation-ID rendering.
Deprecated: use orchestrator.forms.validators.CustomerId instead.
Timestamp
module-attribute
Timestamp = pydantic_forms.validators.components.timestamp.timestamp()
A date/time picker with default settings. Use timestamp() to configure it.
Accept
Bases: str
A checklist the user works through before the form can be submitted.
Validation fails while the value is still "INCOMPLETE". Subclass it and set data to
describe the items to display.
data
class-attribute
data: typing.Optional[pydantic_forms.types.AcceptData] = None
enum_validator
classmethod
enum_validator(v: typing.Any) -> str
must_be_complete
classmethod
must_be_complete(v: str) -> bool
AcceptValues
Bases: pydantic_forms.types.strEnum
ACCEPTED
class-attribute
instance-attribute
ACCEPTED = 'ACCEPTED'
INCOMPLETE
class-attribute
instance-attribute
INCOMPLETE = 'INCOMPLETE'
Button
dataclass
Label and/or color for one button. Left as None, the frontend keeps its default.
text
class-attribute
instance-attribute
text: typing.Optional[str] = None
color
class-attribute
instance-attribute
color: typing.Optional[str] = None
to_meta
to_meta() -> ButtonConfig
ButtonsConfig
dataclass
Overrides for the previous/next buttons, set as the default of a form field.
custom_buttons: ButtonsConfig = ButtonsConfig(next=Button(text="Confirm")) sends the overrides to the
frontend as customButtons in the page’s meta. The field takes no input and stays out of the schema and
the validated result. It can’t be combined with customButtons in the page’s meta__.
previous
class-attribute
instance-attribute
previous: typing.Optional[pydantic_forms.validators.components.buttons.Button] = None
next
class-attribute
instance-attribute
next: typing.Optional[pydantic_forms.validators.components.buttons.Button] = None
to_meta
to_meta() -> CustomButtons
Choice
Bases: pydantic_forms.types.strEnum
Let the user choose from an enum, showing a label that may differ from the stored value.
Each member is a (value, label) pair: the value is what the form submits, the label is what
the frontend displays. A member declared without a label uses its value for both.
As of March 2023 mypy does not yet support functional API on Enum subclasses https://github.com/python/mypy/issues/6037
This means that: MyChoice1 = Choice(“MyChoice1”, {“option1”: “value1”, “option2”: “value2”})
Will result in the (invalid) mypy error error: Argument 2 to “Choice” has incompatible type “Dict[str, str]”; expected “Optional[str]” [arg-type]
Because it maps to Choice.new instead of Enum.call
Workaround is to be explicit: MyChoice1 = Choice.call(“MyChoice1”, {“option1”: “value1”, “option2”: “value2”})
label
class-attribute
label: str
ContactPerson
Bases: pydantic.BaseModel
name
instance-attribute
name: pydantic_forms.validators.components.contact_person.ContactPersonName
email
instance-attribute
email: pydantic.EmailStr
phone
class-attribute
instance-attribute
phone: str = ''
Layout
dataclass
Bases: annotated_types.GroupedMetadata
Position a field on the 12-column form grid, e.g. Annotated[str, Layout(span=6)].
Adds a camelCase layout object to the field’s JSON schema.
Fields without one take the full width.
Args: span: Width in columns (1-12), full width when unset. start: Column (1-12) to start at, leaves the columns before it empty. new_row: Always start on a new row, even when the current row has space left. row_span: Height in rows, one row when unset. The following fields are placed next to it. align: Vertical position of the field within its row.
span
class-attribute
instance-attribute
span: int | None = None
start
class-attribute
instance-attribute
start: int | None = None
new_row
class-attribute
instance-attribute
new_row: bool = False
row_span
class-attribute
instance-attribute
row_span: int | None = None
align
class-attribute
instance-attribute
align: pydantic_forms.validators.components.layout.LayoutAlign | None = None
callout
callout(*, header: typing.Union[str, None] = None, message: typing.Union[str, None] = None, icon_type: typing.Union[str, None] = 'info', message_type: typing.Union[pydantic_forms.validators.components.callout.CalloutMessageType, str] = CalloutMessageType.PRIMARY) -> type[Callout]
Create a callout box.
message_type selects the styling and accepts a CalloutMessageType: primary, success,
warning, danger or accent. icon_type names the icon to show, and defaults to "info".
choice_list
choice_list(item_type: type[pydantic_forms.validators.components.choice.Choice], *, min_items: typing.Optional[int] = None, max_items: typing.Optional[int] = None, unique_items: typing.Optional[bool] = None) -> type[list[Choice]]
Create a multi-select list of Choice values.
contact_person_list
contact_person_list(organisation: typing.Optional[uuid.UUID] = None, organisation_key: typing.Optional[str] = 'organisation', min_items: typing.Optional[int] = None, max_items: typing.Optional[int] = None) -> type[list[T]]
Create a list of ContactPerson entries.
Deprecated: use orchestrator.forms.validators.customer_contact_list instead.
markdown
markdown(*, content: typing.Union[str, None] = None, color: typing.Union[pydantic_forms.validators.components.markdown.MarkdownColor, str] = MarkdownColor.PRIMARY) -> type[Markdown]
Create a block of rendered Markdown.
color accepts a MarkdownColor: primary, success, warning, danger or accent.
migration_summary
migration_summary(data: pydantic_forms.types.SummaryData) -> type[MigrationSummary]
Create a static table from a {headers, labels, columns} mapping.
read_only_field
read_only_field(default: typing.Any, merge_type: typing.Any | None = None) -> Any
Create type with json schema that sets frontend form field to active=false.
Args:
default(Any): value to display as inactive field on form
merge_type(Any | None): merge another pydantic_form type for this field
Returns:
type annotation which will submit json schema with active=false to uniforms
read_only_list
read_only_list(default: list[typing.Any] | None = None) -> Any
Create type with json schema of type array that is ‘read only’.
timestamp
timestamp(show_time_select: typing.Optional[bool] = True, locale: typing.Optional[str] = None, validate: bool = True, min: typing.Optional[int] = None, max: typing.Optional[int] = None, date_format: typing.Optional[str] = None, time_format: typing.Optional[str] = None) -> Any
Create a date/time picker, backed by an int holding a unix timestamp.
Pass validate=False to emit the widget bounds without enforcing min/max on the value.
This can be useful to implement custom validation error messages while still having the same
UI representation.
unique_conlist
unique_conlist(item_type: type[pydantic_forms.validators.components.unique_constrained_list.T], *, min_items: typing.Optional[int] = None, max_items: typing.Optional[int] = None) -> type[list[T]]
Create a list whose items must all be unique.
validate_unique_list
validate_unique_list(values: list[pydantic_forms.validators.components.unique_constrained_list.T]) -> list[T]
Return the list unchanged, raising PydanticCustomError if it holds duplicates.
Exceptions¶
Loc
module-attribute
Loc = tuple[typing.Union[int, str], ...]
FormException
Bases: Exception
FormNotCompleteError
Bases: pydantic_forms.exceptions.FormException
Raised when fewer inputs are provided than the form can process.
This exception is part of the normal forms workflow.
form
instance-attribute
form: pydantic_forms.types.JSON = pydantic_forms.exceptions.FormNotCompleteError(form)
meta
instance-attribute
meta: typing.Optional[pydantic_forms.types.JSON] = pydantic_forms.exceptions.FormNotCompleteError(meta)
FormOverflowError
Bases: pydantic_forms.exceptions.FormException
Raised when more inputs are provided than the form can process.
FormNotFoundError
Bases: pydantic_forms.exceptions.FormException
Raised when the requested form key is not registered.
FormValidationError
Bases: pydantic_forms.exceptions.FormException
validator_name
instance-attribute
validator_name: str = pydantic_forms.exceptions.FormValidationError(validator_name)
errors
instance-attribute
errors: list[pydantic_core.ErrorDetails] = list(pydantic_forms.exceptions.convert_errors(pydantic_forms.exceptions.FormValidationError(error), pydantic_forms.exceptions.FormValidationError(tr), pydantic_forms.exceptions.FormValidationError(locale)))
ErrorDict
Bases: pydantic_forms.exceptions._ErrorDictRequired
ctx
instance-attribute
ctx: dict[str, typing.Any]
convert_errors
convert_errors(validation_error: pydantic.ValidationError, tr: pydantic_i18n.PydanticI18n, locale: str = 'en_US') -> Iterable[ErrorDetails]
Convert Pydantic’s error messages to our needs.
https://docs.pydantic.dev/2.4/errors/errors/#customize-error-messages
display_errors
display_errors(errors: list[pydantic_forms.exceptions.ErrorDict]) -> str
show_ex
show_ex(ex: Exception, stacklimit: typing.Union[int, None] = None) -> str
Show an exception, including its class name, message and (limited) stacktrace.
Examples:
>>> try:
... raise Exception("Something went wrong")
... except Exception as e:
... print(show_ex(e))
Exception: Something went wrong
...
Types¶
union_types
module-attribute
union_types = [typing.Union, types.UnionType]
UUIDstr
module-attribute
UUIDstr = str
State
module-attribute
State = dict[str, typing.Any]
JSON
module-attribute
JSON = typing.Any
InputForm
module-attribute
InputForm = typing.Type[pydantic.main.BaseModel]
AcceptData
module-attribute
AcceptData = list[typing.Union[tuple[str, pydantic_forms.types.AcceptItemType], tuple[str, pydantic_forms.types.AcceptItemType, dict]]]
T
module-attribute
T = typing.TypeVar('T', bound=pydantic.main.BaseModel)
FormGenerator
module-attribute
FormGenerator = typing.Generator[typing.Type[pydantic_forms.types.T], pydantic_forms.types.T, pydantic_forms.types.State]
SimpleInputFormGenerator
module-attribute
SimpleInputFormGenerator = typing.Callable[..., pydantic_forms.types.InputForm]
InputFormGenerator
module-attribute
InputFormGenerator = typing.Callable[..., pydantic_forms.types.FormGenerator]
InputStepFunc
module-attribute
InputStepFunc = typing.Union[pydantic_forms.types.SimpleInputFormGenerator, pydantic_forms.types.InputFormGenerator]
StateSimpleInputFormGenerator
module-attribute
StateSimpleInputFormGenerator = typing.Callable[[pydantic_forms.types.State], pydantic_forms.types.InputForm]
StateInputFormGenerator
module-attribute
StateInputFormGenerator = typing.Callable[[pydantic_forms.types.State], pydantic_forms.types.FormGenerator]
StateInputStepFunc
module-attribute
StateInputStepFunc = typing.Union[pydantic_forms.types.StateSimpleInputFormGenerator, pydantic_forms.types.StateInputFormGenerator]
SubscriptionMapping
module-attribute
SubscriptionMapping = dict[str, list[dict[str, str]]]
FormGeneratorAsync
module-attribute
FormGeneratorAsync = typing.AsyncGenerator[typing.Union[typing.Type[pydantic_forms.types.T], pydantic_forms.types.State], pydantic_forms.types.T]
StateInputFormGeneratorAsync
module-attribute
StateInputFormGeneratorAsync = typing.Callable[[pydantic_forms.types.State], pydantic_forms.types.FormGeneratorAsync]
AcceptItemType
Bases: pydantic_forms.types.strEnum
INFO
class-attribute
instance-attribute
INFO = 'info'
LABEL
class-attribute
instance-attribute
LABEL = 'label'
WARNING
class-attribute
instance-attribute
WARNING = 'warning'
URL
class-attribute
instance-attribute
URL = 'url'
CHECKBOX
class-attribute
instance-attribute
CHECKBOX = 'checkbox'
SUBCHECKBOX
class-attribute
instance-attribute
SUBCHECKBOX = '>checkbox'
OPTIONAL_CHECKBOX
class-attribute
instance-attribute
OPTIONAL_CHECKBOX = 'checkbox?'
OPTIONAL_SUBCHECKBOX
class-attribute
instance-attribute
OPTIONAL_SUBCHECKBOX = '>checkbox?'
SKIP
class-attribute
instance-attribute
SKIP = 'skip'
VALUE
class-attribute
instance-attribute
VALUE = 'value'
MARGIN
class-attribute
instance-attribute
MARGIN = 'margin'
SummaryData
Bases: typing_extensions.TypedDict
headers
instance-attribute
headers: list[str]
labels
instance-attribute
labels: list[str]
columns
instance-attribute
columns: list[list[typing.Union[str, int, bool, float]]]
ButtonConfig
Bases: typing_extensions.TypedDict
text
instance-attribute
text: str
color
instance-attribute
color: str
CustomButtons
Bases: typing_extensions.TypedDict
previous
instance-attribute
previous: pydantic_forms.types.ButtonConfig
next
instance-attribute
next: pydantic_forms.types.ButtonConfig
FormMeta
Bases: typing_extensions.TypedDict
Documented keys for FormPage.meta__, not enforced at runtime; extra keys are allowed.
Attributes:
hasNext: Whether another page follows, so the frontend can label the submit button
accordingly.
customButtons: Per-button overrides (label, color) for “previous” and “next”. Prefer a ButtonsConfig
field on the page, which sets this key; the two cannot be combined.
hasNext
instance-attribute
hasNext: bool
customButtons
instance-attribute
customButtons: pydantic_forms.types.CustomButtons
Schema utilities¶
Helper utilities for dealing with json schema objects (json_schema_extra, etc).
merge_json_schema
merge_json_schema(source_type: typing.Any, target_type: typing.Any) -> Any
Add json_schema from target_type to source_type.
inline_refs
inline_refs(schema: dict[str, typing.Any]) -> dict[str, Any]
Replace each local $ref by the $defs entry it points to, so a reader needs no lookup.
The keys beside a $ref (a field’s own title, default, readOnly) win over the definition’s.
A reference that is part of a cycle, a model that contains itself, is left as it is. $defs keeps the
definitions still referred to, by such a reference or by a discriminator mapping, and is dropped when empty.
inline_refs( … { … “properties”: {“port”: {“$ref”: “#/$defs/Port”, “title”: “Uplink”}}, … “$defs”: {“Port”: {“type”: “string”, “title”: “Port”}}, … } … ) {‘properties’: {‘port’: {‘type’: ‘string’, ‘title’: ‘Uplink’}}}
FastAPI exception handler¶
form_error_handler
async
form_error_handler(request: fastapi.requests.Request, exc: pydantic_forms.exceptions.FormException) -> JSONResponse
FastAPI exception handler that turns a FormException into a HTTP 4xx/5xx response with JSON body.