Skip to main content

PyTypeInput 1.0.2

PyPI version Python Tests License

Define parameters with Python type hints. Get complete UI metadata automatically.

PyTypeInput analyzes standard Python type annotations and extracts everything a frontend needs to render forms: types, constraints, choices, labels, defaults, and validation — all from a single source of truth.

For interactive documentation, live examples, and full integration, see FuncToWeb — which uses PyTypeInput as its core engine.


Quick Example

from typing import Annotated, Literal
from pydantic import Field
from pytypeinput import analyze_function
from pytypeinput.types import Label, Slider, Email, Description, Rows

def create_user(
    name: Annotated[str, Label("Full Name"), Field(min_length=2, max_length=50)],
    email: Email,
    age: Annotated[int, Slider(), Field(ge=0, le=120)] = 25,
    role: Literal["admin", "user", "viewer"] = "user",
    bio: Annotated[str, Description("Short biography"), Rows(4)] | None = None,
):
    ...

params = analyze_function(create_user)

for p in params:
    print(p.name, "→", p.param_type.__name__)
# name  → str
# email → str
# age   → int
# role  → str
# bio   → str

Each ParamMetadata object carries the full picture: type, default, constraints, choices, labels, and a precompiled validator. The frontend resolves the appropriate widget from param_type, item_ui, choices, and special_widget.


Installation

pip install pytypeinput

Dependencies

  • Python 3.10+
  • Pydantic 2.x

What You Can Analyze

PyTypeInput works with any of these sources — same output format regardless:

from pytypeinput import analyze_function, analyze_pydantic_model, analyze_dataclass, analyze_class_init

# Functions
params = analyze_function(my_func)

# Pydantic models
params = analyze_pydantic_model(MyModel)

# Dataclasses
params = analyze_dataclass(MyDataclass)

# Any class with __init__
params = analyze_class_init(MyClass)

Widget Resolution

The frontend determines which widget to render based on the metadata fields:

Metadata Widget Example
param_type = str Text input name: str
param_type = int or float Number input age: int
param_type = bool Checkbox flag: bool
param_type = date Date picker birthday: date
param_type = time Time picker meeting: time
choices present Dropdown Enum, Literal, Dropdown(func)
item_ui.is_slider = True Slider Annotated[int, Slider()]
item_ui.is_password = True Password Annotated[str, IsPassword()]
item_ui.rows set Textarea Annotated[str, Rows(4)]
special_widget = "Color" Color picker Color
special_widget = "File" File input ImageFile, File, etc.

UI Metadata

Annotate parameters with descriptive metadata that frontends can use to render labels, placeholders, and more:

from pytypeinput.types import Label, Description, Placeholder, Step, Rows, PatternMessage

# Label and description
name: Annotated[str, Label("Your Name"), Description("As it appears on your ID")]

# Placeholder text
city: Annotated[str, Placeholder("e.g., Madrid")]

# Numeric step
quantity: Annotated[int, Step(5)]

# Textarea
notes: Annotated[str, Rows(4)]

# Custom pattern error message
code: Annotated[str, Field(pattern=r"^\d{4}$"), PatternMessage("Must be a 4-digit code")]

Constraints

Constraints come from Pydantic's Field() and are validated at analysis time and at runtime:

from pydantic import Field

# Numeric bounds
age: Annotated[int, Field(ge=0, le=150)]
price: Annotated[float, Field(gt=0, lt=10000)]

# String constraints
username: Annotated[str, Field(min_length=3, max_length=20)]
hex_code: Annotated[str, Field(pattern=r"^#[0-9a-fA-F]{6}$")]

Choices

Three ways to define a set of valid options:

from enum import Enum
from typing import Literal
from pytypeinput.types import Dropdown

# Enum — options extracted from values
class Color(Enum):
    RED = "red"
    GREEN = "green"
    BLUE = "blue"

color: Color = Color.RED

# Literal — inline options
size: Literal["S", "M", "L", "XL"] = "M"

# Dropdown — dynamic options from a callable
def get_users():
    return ["alice", "bob", "charlie"]

user: Annotated[str, Dropdown(get_users)]

Dynamic dropdowns can be refreshed at any time via param.refresh_choices().


Lists

Supports list[T] with optional length constraints. All item-level annotations (choices, constraints, UI) apply to each element:

# Simple list
tags: list[str]

# List with length constraints
scores: Annotated[list[int], Field(min_length=1, max_length=10)]

# List of choices
colors: list[Literal["red", "green", "blue"]]

# Item-level constraints apply to each element
prices: list[Annotated[float, Field(ge=0.0, le=9999.99), Step(0.01)]]

# Item-level slider with bounds
volumes: list[Annotated[int, Slider(), Field(ge=0, le=100), Step(5)]]

# List constraints + item constraints combined
ratings: Annotated[list[Annotated[int, Field(ge=1, le=5)]], Field(min_length=1, max_length=10)]

# Items with special types
photos: list[ImageFile]
emails: Annotated[list[Email], Field(min_length=1, max_length=5)]

# Items with dropdown
tags: list[Annotated[str, Dropdown(get_available_tags)]]

Label and Description on lists follow a priority rule: if Label/Description is on the outer list annotation, it wins. If not, it's read from the inner item type. This means you can define reusable labeled items and the label propagates automatically:

# Label on the outer level → "Scores" is used
scores: Annotated[list[int], Label("Scores")]

# Label on the inner item → "Tag Name" propagates to the list
items: list[Annotated[str, Label("Tag Name")]]

# Both levels → outer "My Tags" wins over inner "Tag Name"
items: Annotated[list[Annotated[str, Label("Tag Name")]], Label("My Tags")]

Nested lists (list[list[...]]) are not supported by design.


Optional Fields

T | None marks a field as optional. Control the initial toggle state with defaults or explicit markers:

from pytypeinput.types import OptionalEnabled, OptionalDisabled

# Toggle off by default (no default or default is None)
nickname: str | None = None

# Toggle on by default (has a non-None default)
theme: str | None = "dark"

# Explicit control
notes: str | OptionalEnabled = None     # Toggle starts ON
code: str | OptionalDisabled = "ABC"    # Toggle starts OFF

Validation

validate_value coerces and validates runtime values (including raw strings from forms) against the analyzed metadata:

from pytypeinput import analyze_function
from pytypeinput.validate import validate_value

params = analyze_function(my_func)
meta = params[0]

# Coerces types: "42" → 42, "true" → True, "2024-01-15" → date(...)
value = validate_value(meta, "42")

# Validates constraints, choices, and types
# Raises ValueError or TypeError on invalid input

Coercion rules:

  • Strings to int, float, bool, date, time
  • Enum values or names to Enum instances
  • JSON form primitives to native Python types

Output Format

Every analyzed parameter returns a ParamMetadata dataclass:

param.name             # "age"
param.param_type       # int
param.default          # 25
param.special_widget   # "Color", "File", or None
param.optional         # OptionalMetadata(enabled=False) or None
param.constraints      # ConstraintsMetadata(ge=0, le=120, ...) or None
param.choices          # ChoiceMetadata(options=(...), ...) or None
param.item_ui          # ItemUIMetadata(is_slider=True, ...) or None
param.param_ui         # ParamUIMetadata(label="Age", ...) or None
param.list             # ListMetadata(min_length=..., ...) or None

# Serialize to dict for JSON/frontend consumption
# Only non-None fields are included
param.to_dict()

Special Types

Ready-to-use annotated types with built-in patterns and widget resolution:

from pytypeinput.types import Color, Email, ImageFile, VideoFile, AudioFile, DataFile, TextFile, DocumentFile, File

avatar: ImageFile          # Accepts .png, .jpg, .webp, etc.
document: DocumentFile     # Accepts .pdf, .doc, .docx, etc.
theme_color: Color         # Hex color with picker widget
contact: Email             # Email with validation and placeholder
attachment: File           # Any file

Composition

Types can be layered using Annotated to build reusable, composable building blocks. Each layer can add constraints, UI metadata, or both — and later layers override earlier ones for the same attribute.

# Base constrained types
PositiveInt = Annotated[int, Field(ge=0)]
BoundedInt = Annotated[int, Field(ge=0, le=100)]
SmallStr = Annotated[str, Field(min_length=1, max_length=50)]
LongStr = Annotated[str, Field(min_length=1, max_length=5000)]
UnitFloat = Annotated[float, Field(ge=0.0, le=1.0)]

# Add UI on top of constraints
SliderInt = Annotated[BoundedInt, Slider()]
SliderStep5 = Annotated[BoundedInt, Slider(), Step(5)]
PasswordStr = Annotated[SmallStr, IsPassword()]
TextAreaStr = Annotated[LongStr, Rows(10)]
StepFloat = Annotated[UnitFloat, Step(0.01)]

# Add labels/descriptions on top of everything
LabeledSlider = Annotated[SliderInt, Label("Volume")]
FullSlider = Annotated[SliderStep5, Label("Level"), Description("Set level")]
FullPassword = Annotated[PasswordStr, Label("Password"), Description("Enter password"), Placeholder("********")]
FullTextArea = Annotated[TextAreaStr, Label("Notes"), Description("Add notes"), Placeholder("Write...")]

Constraints merge across layers, and later values override earlier ones for the same attribute:

# ge=0 from PositiveInt, le=100 added → both apply
Annotated[PositiveInt, Field(le=100)]

# ge=0 from PositiveInt, then ge=10 overrides → ge=10
Annotated[PositiveInt, Field(ge=10)]

# Three levels deep: ge=0 → ge=5 → ge=10 → final ge=10
L1 = Annotated[int, Field(ge=0)]
L2 = Annotated[L1, Field(ge=5)]
L3 = Annotated[L2, Field(ge=10)]

This lets you define your type vocabulary once and reuse it across functions, models, and dataclasses without repeating constraints or UI hints.


Project Structure

pytypeinput/
├── analyzer.py          # Single-type analysis pipeline
├── analyzers.py         # Function, Pydantic, dataclass, class analyzers
├── validate.py          # Runtime validation and coercion
├── param.py             # Metadata dataclasses
├── types.py             # UI markers, special types, patterns
├── helpers.py           # Annotated rebuilding, serialization
└── extractors/          # 10-step pipeline (internal)
    ├── validate_type_01.py
    ├── validate_optional_02.py
    ├── extract_param_ui_03.py
    ├── extract_list_04.py
    ├── extract_item_ui_05.py
    ├── extract_choices_06.py
    ├── extract_constraints_07.py
    ├── validate_final_08.py
    ├── resolve_widget_09.py
    └── normalize_default_10.py

License

MIT

Release files for pytypeinput 1.0.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pytypeinput 1.0.2
File Size Uploaded
pytypeinput-1.0.2.tar.gz 48.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pytypeinput 1.0.2
File Interpreter ABI Platform
pytypeinput-1.0.2-py3-none-any.whl Python 3 none any Details

Total release size: 69.4 kB

Release files / pytypeinput-1.0.2.tar.gz

Download URL pytypeinput-1.0.2.tar.gz
Size 48.1 kB
Tags Source
SHA-256 checksum
How to use checksums
009f16f242446bfff7f92f1195e4dc391cbc89d2a640e65e89801f52f3233d7e
BLAKE2b-256 checksum
How to use checksums
bddcb301fa9878026f52534b20abedccca849f405b8ef0d14190ba28e1ee5c12
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.9

Release files / pytypeinput-1.0.2-py3-none-any.whl

Download URL pytypeinput-1.0.2-py3-none-any.whl
Size 21.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1d0384096249464f95286b1527f18d34dca0b59e96ed7a9f294675da7ee035ba
BLAKE2b-256 checksum
How to use checksums
976c7028ba0c67d091fd57f590dc7b8407b1c88c29878a297fd3556ce56ad8d6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.9

Release history Release notifications | RSS feed

This release

1.0.2 This release

2 release files

1.0.1

2 release files

1.0.0

2 release files

0.1.5

2 release files

0.1.4

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page