# AIR
Air is a Python web framework that provides a friendly layer over FastAPI for building modern web applications that combine beautiful HTML pages with powerful REST APIs.
# Usage documentation
*AIR: The new web framework that breathes fresh air into Python web development. Built with FastAPI, Starlette, and Pydantic.*
## Why use Air?
- **Powered by FastAPI** - Designed to work with FastAPI so you can serve your API and web pages from one app
- **Fast to code** - Tons of intuitive shortcuts and optimizations designed to expedite coding HTML with FastAPI
- **Air Tags** - Easy to write and performant HTML content generation using Python classes to render HTML
- **Jinja Friendly** - No need to write `response_class=HtmlResponse` and `templates.TemplateResponse` for every HTML view
- **Mix Jinja and Air Tags** - Jinja and Air Tags both are first class citizens. Use either or both in the same view!
- **HTMX friendly** - We love HTMX and provide utilities to use it with Air
- **HTML form validation powered by pydantic** - We love using pydantic to validate incoming data. Air Forms provide two ways to use pydantic with HTML forms (dependency injection or from within views)
- **Easy to learn yet well documented** - Hopefully Air is so intuitive and well-typed you'll barely need to use the documentation. In case you do need to look something up we're taking our experience writing technical books and using it to make documentation worth boasting about
______________________________________________________________________
**Documentation**:
**Source Code**:
## Installation
Install using `pip install -U air` or `conda install air -c conda-forge`.
For `uv` users, just create a virtualenv and install the air package, like:
```
uv venv
source .venv/bin/activate
uv init
uv add air
uv add "fastapi[standard]"
```
## A Simple Example
Create a `main.py` with:
```
import air
app = air.Air()
@app.get("/")
async def index():
return air.Html(air.H1("Hello, world!", style="color: blue;"))
```
Note
This example uses [Air Tags](api/tags/), which are Python classes that render as HTML. Air Tags are typed and documented, designed to work well with any code completion tool.
## Combining FastAPI and Air
Air is just a layer over FastAPI. So it is trivial to combine sophisticated HTML pages and a REST API into one app.
```
import air
from fastapi import FastAPI
app = air.Air()
api = FastAPI()
@app.get("/")
def landing_page():
return air.Html(
air.Head(air.Title("Awesome SaaS")),
air.Body(
air.H1("Awesome SaaS"),
air.P(air.A("API Docs", target="_blank", href="/api/docs")),
),
)
@api.get("/")
def api_root():
return {"message": "Awesome SaaS is powered by FastAPI"}
# Combining the Air and FastAPI apps into one
app.mount("/api", api)
```
## Combining FastAPI and Air using Jinja2
Want to use Jinja2 instead of Air Tags? We've got you covered.
```
import air
from fastapi import FastAPI
app = air.Air()
api = FastAPI()
# Air's JinjaRenderer is a shortcut for using Jinja templates
jinja = air.JinjaRenderer(directory="templates")
@app.get("/")
def index(request: air.Request):
return jinja(request, name="home.html")
@api.get("/")
def api_root():
return {"message": "Awesome SaaS is powered by FastAPI"}
# Combining the Air and and FastAPI apps into one
app.mount("/api", api)
```
Don't forget the Jinja template!
```
Awesome SaaS
```
Note
Using Jinja with Air is easier than with FastAPI. That's because as much as we enjoy Air Tags, we also love Jinja!
## Contributing
For guidance on setting up a development environment and how to make a contribution to Air, see [Contributing to Air](https://github.com/feldroy/air/blob/main/CONTRIBUTING.md).
# Quickstart
The TL;DR for getting started with Air.
## Installation
To start a new Air project, create a directory and set up your environment:
```
mkdir helloair
cd helloair
uv venv
source .venv/bin/activate
uv init
uv add air
uv add "fastapi[standard]"
```
Note
You can also do:
```
pip install -U air "fastapi[standard]"
```
or even
```
conda install air -c conda-forge
conda install "fastapi[standard]" -c conda-forge
```
## Hello, Air! Example
Create a `main.py` file in your new directory with:
main.py
```
import air
app = air.Air()
@app.get("/")
async def index():
return air.layouts.mvpcss(
air.H1("Hello, Air!"),
air.P("Breathe it in.")
)
```
Serve your app with:
```
fastapi dev
```
Open your page by clicking this link:
Here's a few interesting things about this page:
1. The page has an attractive layout and typography
1. The Python for this app is similar in design to how FastAPI code is written
1. If you typed the code out in an IDE with intellisense, you'll have seen every Air object includes useful instruction. Air is designed to be friendly to both humans and LLMs, hence every object is carefully typed and documented
## Routing
Routing is how users on paths are directed to the correct 'view' function that handles their request.
### Basics
Air wraps FastAPI so you can use the same decorator patterns for specifying URLs:
```
import air
app = air.Air()
@app.get("/")
def index():
return air.layouts.mvpcss(
air.H1("Hello, Air!"),
air.P("Breathe it in.")
)
@app.get("/air-is-grounded")
def air_is_grounded():
return air.layouts.mvpcss(
air.H1("Air is Grounded"),
air.P("Built on industry standard libraries including:"),
air.Ul(
air.Li('FastAPI'),
air.Li('Starlette'),
air.Li('Pydantic'),
air.Li('Jinja'),
)
)
@app.post('/form-handler')
async def form_handler(request: air.Request): # (1)!
...
```
1. Form handling in Air requires `async` functions and usually an `air.Request` argument. We cover forms later on this page as well as in numerous places across the Air documentation.
### app.page decorator
To expedite `HTTP GET` pages we provide the `app.page` decorator, which can replace the `app.get()` decorator for views without arguments. `app.page` converts the name of the function to the route, converting underscores to dashes:
```
import air
app = air.Air()
@app.page # Renders as '/'
def index(): # (1)!
return air.layouts.mvpcss(
air.H1("Hello, Air!"),
air.P("Breathe it in.")
)
@app.page # Renders as '/air-is-grounded'
def air_is_grounded(): # (2)!
return air.layouts.mvpcss(
air.H1("Air is Grounded"),
air.P("Built on industry standard libraries including:"),
air.Ul(
air.Li('FastAPI'),
air.Li('Starlette'),
air.Li('Pydantic'),
air.Li('Jinja'),
)
)
```
1. `app.page` used over functions named `index` are converted to the `/` route.
1. `app.page` used over functions are converted to a route based on their name, with underscores converted to dashes.
### Variables in Paths
Variables can be added to URLs by marking them in curly braces like `{variable}` in the `application.get`, `application.post`, `application.put`, and `application.delete` function decorators. The function receives the `{variable}` so long as it is the correct type specified by the function.
```
import air
app = air.Air()
@app.get('/users/{username}') # (1)!
def user_detail(username: str): # (2)!
return air.layouts.mvpcss(
air.Title(username),
air.H1(username)
)
```
1. We've specified a variable called `username`.
1. We have defined a function argument named `username`, which is identical to the variable specified in the decorator. We also specified the Python type in this definition.
Try it out by going to
### Variables in URLs
If you specify variables in in the function definition but not the function decorator, those become URL parameters.
The function receives the `{variable}` so long as it is the correct type specified by the function.
```
import air
app = air.Air()
@app.get('/users')
def user_detail(username: str): # (1)!
return air.layouts.mvpcss(
air.Title(username),
air.H1(username)
)
```
1. We have defined a function argument named `username`. Because `username` is not part of the decorator's URL path ('/users'), Air automatically treats it as a query parameter.
Try it out by going to
### Generating URLs
Air allows you to generate URLs programmatically through the `.url()` method accessible on route functions:
```
@app.get('/users/{username}')
def user_detail(username: str):
return air.H1(username)
# Generate URL with path parameters
url = user_detail.url(username="Aang")
# Returns: "/users/Aang"
```
This is useful for creating links and redirects without hardcoding URLs:
```
@app.page
def index():
return air.layouts.mvpcss(
air.H1("Home"),
air.A("View user profile", href=user_detail.url(username="Aang"))
)
```
### Other HTTP Methods
Warning
By default all HTML forms can only send `GET` and `POST` requests. If you set the form method to something else, like `PUT`, `PATCH`, or `DELETE`, the browser will actually fall back to a GET request. However, the magic of HTMX allows you to send other HTTP methods from forms and links.
Air supports the `PATCH`, `PUT`, or `DELETE` methods natively:
```
import air
app = air.Air()
@app.patch('/partial-update/{slug}')
async def partial_update(request: air.Request, slug: str): # (1)!
...
@app.put('/create-item')
async def create_item(request: air.Request): # (2)!
...
@app.delete('/delete/{slug}')
async def delete_item(request: air.Request, slug: str): # (3)!
...
```
1. `PATCH` requests are used for partial updates of resources, such as one field being updated. The `slug` variable in the URL is passed as an argument to the function. While `POST` requests can be used for updates and is the classic method, `PATCH` is more specific to the action being taken.
1. `PUT` requests are used for creating or replacing resources. The function can handle the incoming data, typically from the request body. Like `POST`, `PUT` requests usually require `async` functions and an `air.Request` argument.
1. `DELETE` requests are used to delete resources. Similar to `PATCH`, the `slug` variable in the URL is passed as an argument to the function.
Calling these can be done via HTMX or other methods that support these HTTP verbs. Here are examples using HTMX in Air:
```
air.Form(
# form elements here
hx_patch=partial_update.url(slug='airbook'),
)
air.Form(
# form elements here
hx_put=create_item.url(),
)
air.Form(
# form elements here
hx_delete=delete_item.url(slug='firebook'),
)
```
## Air Tags
[Air Tags](../air_tags/) are one of Air's two ways to generate HTML output. They are useful for keeping file size down, general HTML delivery, and especially with fragment responses via HTMX.
### JavaScript Files
Using Air Tags to call external JavaScript files:
```
import air
app = air.Air()
@app.page
def index():
return air.Script(src="https://cdn.jsdelivr.net/npm/htmx.org@2.0.7/dist/htmx.min.js")
```
### Inline Scripts
When you need to use JavaScript inline in Air Tags:
```
import air
app = air.Air()
@app.page
def index():
return air.Script("alert('The Last Airbender is an awesome series.')")
```
### CSS Files
Here's how to use Air Tags to call external CSS files:
```
import air
app = air.Air()
@app.page
def index():
return air.Html(
air.Head(
air.Link(rel="stylesheet", href="https://unpkg.com/mvp.css"),
),
air.Body(
air.Main(
air.H1("Air Web Framework"),
air.P("The web framework for Air Nomads.")
)
)
)
```
### Inline CSS Styles
Inline CSS styles via Air are a good way to control design elements at runtime.
```
import air
app = air.Air()
@app.page
def index():
return air.Html(
air.Head(
air.Style("h1 {color: red;}"),
),
air.Body(
air.H1("Air Web Framework"),
air.P("The web framework for Air Nomads.")
)
)
```
## Jinja
In addition to Air Tags, Air supports Jinja natively. In addition to being great at delivering HTML content, Jinja can be used to render all kinds of content.
Here's a simple Jinja template:
templates/base.html
```
{{title}}
{{message}}
```
And here's the view that calls it:
main.py
```
import air
app = air.Air()
# Set the Jinja render function
jinja = air.JinjaRenderer(directory="templates") #(1)!
@app.page
def index(request: air.Request):
return jinja( #(2)!
request,
name="base.html",
# You can pass in individual keyword arguments
title="Hello, Air Benders", #(3)!
# Or a dict for the context
context={"message": "Air + Jinja is awesome"} #(4)!
)
```
1. This sets up the Jinja environment for calling and rendering of templates.
1. Air automatically handles turning the `jinja` response into an HTML response.
1. Individual keyword arguments for values can be passed, these are added to the Jinja template's context dictionary.
1. This is the standard Jinja context dictionary, which is added to each template.
### Jinja + Air Tags
It is very easy to include Air Tags in Jinja. Let's first create our template:
templates/avatar.html
```
{{title}}
{{fragment|safe}} {# (1)! #}
```
1. The `safe` filter is necessary for using Air Tags in Jinja. This has security implications, so be careful what content you allow.
And here is our Python code describing the view:
main.py
```
import air
app = air.Air()
jinja = air.JinjaRenderer(directory="templates")
@app.get("/avatar")
def avatar(request: air.Request):
return jinja(
request,
name="avatar.html",
title="Hello, Air Benders",
fragment=air.Div(
air.P("We are fans of the Last Avatar"),
class_="thing"
) #(1)!
)
```
1. We can pass Air Tags into the context of a Jinja template.
Tip
Where Jinja + Air Tags truly come alive is when the base templates for a project are in Jinja. For some people this makes styling pages a bit easier. Then content, especially HTMX swaps and other fragments are rendered via Air Tags. This keeps the developer in Python, which means less context switching while working on challenges.
## Forms
In HTML, forms are the primary method of receiving data from users. Most forms receive `POST` data. Here's a basic yet workable example of receiving data using a `Request` object.
```
import air
app = air.Air()
@app.page
def index():
return air.layouts.mvpcss(
air.H1('Email form'),
air.Form(
air.Label("Email:", for_="email"),
air.Input(type="email", name="email", required=True),
air.Button("Submit", type="submit"),
method="POST",
action="/submit"
)
)
@app.post('/submit')
async def email_handler(request: air.Request): #(1)!
form = await request.form() #(2)!
return air.layouts.mvpcss(
air.H1('Email form data'),
air.Pre(
air.Code(form),
air.Code(form.keys()),
air.Code(form.values()),
)
)
```
1. As Air is based off starlette, when we receive data from a form it needs to occur within an `async` view. Also, the form data is contained within the `air.Request` object. 2.Form data needs to be received via an `await` keyword on `request.form()`.
FormData is a dict-like object
While the value `FormData([('email', 'aang@example.com')])` might be displayed, the keys and values are accessed via traditional methods.
### AirForms: pydantic+forms
The pydantic library isn't just a component of Air and FastAPI, it's an industry standard validation library using Python type annotations to determine the validity of incoming data. Here's how to use it with AirForms, which use pydantic models to determine how a form is constructed.
```
from pydantic import BaseModel, Field
import air
class ContactModel(BaseModel): #(1)!
name: str = Field(min_length=2, max_length=50)
age: int = Field(ge=1, le=120) # Age between 1 and 120
email: str = Field(pattern=r"^[^@]+@[^@]+\.[^@]+$") # Basic email pattern
class ContactForm(air.AirForm): #(2)!
model = ContactModel
app = air.Air()
@app.page
async def index():
"""Show the form initially."""
form = ContactForm() #(3)!
return air.layouts.picocss(
air.Title("Enhanced Form Errors Demo"),
air.H1("Contact Form - Error Message Demo"),
air.Form(
form.render(), #(4)!
air.Button("Submit", type="submit"),
method="post",
action="/submit",
)
)
@app.post("/submit")
async def handle_form(request: air.Request):
"""Handle form submission and show errors."""
form = await ContactForm.from_request(request) #(5)!
if form.is_valid: #(6)!
return air.layouts.picocss(
air.Title("Success"),
air.H1("Success!"),
air.P(f"Name: {form.data.name}"),
air.P(f"Age: {form.data.age}"),
air.P(f"Email: {form.data.email}"),
)
# Show form with enhanced error messages
return air.layouts.picocss(
air.Title("Enhanced Form Errors Demo"),
air.H1("Contact Form - With Enhanced Error Messages"),
air.P("Notice the specific, user-friendly error messages below:"),
air.Form(
form.render(), #(7)!
air.Br(),
air.Button("Submit", type="submit"),
method="post",
action="/submit",
),
air.Hr(),
air.Details(
air.Summary("Technical Error Details (for developers)"),
air.P(str(form.errors)) if form.errors else "No errors",
)
)
```
1. `ContactModel` is a pydantic model that represents data we want to collect from the user.
1. `ContactForm` is an `AirForm` whose model is the `ContactModel`.
1. This instantiates the form without data.
1. Calling `.render()` on an AirForm generates the form in HTML. This follows a common pattern in Air with `.render()` methods.
1. AirForms have `.from_request()` method which takes the form from an `air.Request` and loads it into the form.
1. The `.is_valid` property of an AirForm is powered by pydantic. It returns a `bool` that can be used to control logic of what to do with form successes or failures.
1. Calling `.render()` on an AirForm generates the form in HTML. This follows a common pattern in Air with `.render()` methods.
## Server-Sent Events
Part of the HTTP specification, Server-Sent Events (SSE) allow the server to push things to the user. Air makes SSE easy to implement and maintain. Here's an example of using it to generate random lottery numbers every second.
```
import random
from asyncio import sleep
import air
app = air.Air()
@app.page
def index():
return air.layouts.mvpcss(
air.Script(src="https://unpkg.com/htmx-ext-sse@2.2.1/sse.js"), #(1)!
air.Title("Server Sent Event Demo"),
air.H1("Server Sent Event Demo"),
air.P("Lottery number generator"),
air.Section(
hx_ext="sse", #(2)!
sse_connect="/lottery-numbers", #(3)!
hx_swap="beforeend show:bottom", #(4)!
sse_swap="message", #(5)!
),
)
async def lottery_generator(): #(6)!
while True:
lottery_numbers = ", ".join([str(random.randint(1, 40)) for x in range(6)])
# Tags work seamlessly
yield air.Aside(lottery_numbers) #(7)!
await sleep(1)
@app.page
async def lottery_numbers():
return air.SSEResponse(lottery_generator()) #(8)!
```
1. To use SSE, the source for the HTMX plugin for them has to be included in the page.
1. The `hx_ext` attribute is used to initialize the SSE plugin.
1. `sse_connect` is the endpoint where the SSE pushes from.
1. `hx_swap` tells HTMX how to swap or place elements. In this case, it says to place the incoming HTML underneath all the other content in this section. The move the focus of the page to that location.
1. The `sse_swap` attribute informs HTMX that we only want to receive SSE events of the `message` type. This is a common response and shouldn't be changed unless you have a good reason.
1. The `air.SSEResponse` needs a generator function or generator expression. Our example just generates random numbers, but people use similar functions to query databases and fetch data from APIs. Of note is that in our example instead of using `return` statements we use `yield` statements to ensure control is not lost.
1. Air Tags work great, but any type of data can be passed back.
1. Air does all heavy lifting of setting up a streaming response for us. All we need to do is pass generator functions or generator expressions into it and it just works!
## Want to learn more?
Check out these documentation sections:
- [Learn](../)
- [API Reference](../../api/)
## Future Segments
What we plan to include in the Quick Start:
- Jinja
- The Jinja + Air Tags pattern the core devs love to use
- Forms:
- Using Pydantic-powered AirForms for validation of incoming data
- `HTTP GET` forms, like those used in search forms
- File uploads (part of forms)
- HTMX basics
- Routing
- Variables in URLs
- Variables in paths
- Generating URLs
- Custom exception handlers
- Sessions
- Cookies
- Large File downloads
- Server Sent Events
# Air Tags
**Air Tags**, sometimes shortened to **Tags**, are Python classes that render HTML. They can be combined to render web pages or small components. **Air Tags** are typed and documented, working well with any code completion tool. They are designed to be an easy to write and performant HTML content generation system using Python classes to render HTML.
Note
This document covers how **Air Tags** work. The full reference for them is the [Air Tags reference](../../api/tags/).
## How **Air Tags** work
Used individually or combined into a greater whole, every Air Tag includes a `render()` method. When the `render()` method is called it returns a HTML representation of the Air Tag, as well as all the children of the Air Tag.
This example:
```
>>> from air import Article, H1, P
>>> content = Article(
H1("Air Tags"),
P("Air Tags are a fast, expressive way to generate HTML.",
class_="subtitle")
)
>>> content
```
In constructing this example, the `Article` tag has wrapped the `H1` and `P` tags. You can't see that the `H1` and `P` tags are inside, but they have been carefully stored.
This is the output of the `render()` method for the example above:
```
>>> content.render()
```
```
Air Tags
Air Tags are a fast, expressive way to generate HTML.
```
A shortcut for the `render()` method is the `str()` built-ins.
```
>>> str(content)
```
```
Air Tags
Air Tags are a fast, expressive way to generate HTML.
```
The `print()` built-in also does this conversion, but the result goes to `stdout`, so can't be saved to a variable.
```
>>> print(content)
```
```
Air Tags
Air Tags are a fast, expressive way to generate HTML.
```
Note
When returned from an Air view this conversion to HTML happens automatically, much like how FastAPI automatically converts `dict` responses to JSON.
## Pretty HTML renders
What if we want a more human-friendly display of HTML? We can use `.pretty_render()` method on any Air Tag:
```
>>> print(content.pretty_render())
```
```
Air Tags
Air Tags are a fast, expressive way to generate HTML.
```
Tip
Combine Air Tag's `.pretty_render()` method with the [rich package](https://github.com/Textualize/rich) for truly lovely colorized output.
## Attributes
**Air Tags** convert keyword arguments into attributes. So:
```
air.P('Hello', id="mine")
```
renders as:
```
Hello
```
Let's take a look at some additional scenarios.
### Python reserved words as attributes
Some HTML attributes are reserved words in Python. To get around that, **Air Tags** uses specific keyword arguments for these reserved words.
| Reserved Word | Use This Keyword Argument |
| ------------- | ------------------------- |
| class | class\_ |
| for | for\_ |
| async | async\_ |
| id | id\_ |
| as | as\_ |
Note
These are the reserved words that we've discocevered that conflict with HTML attributes. If you run into others, please open an issue on GitHub.
#### Setting the `class` attribute
In Python `class` is a protected word. To set the `class` attribute in **Air Tags**, use the `class_` keyword.
```
air.P('Hello', class_='plain')
```
renders as
```
Hello
```
#### Setting the `for` attribute
In Python `for` is a protected word. To set the `for` attribute in **Air Tags**, use the `for_` keyword.
```
air.Label(
'Email',
air.Input(name='email', type='email')
for_='email'
)
```
renders as
```
```
#### Setting the `async` attribute
In Python `async` is a protected word. To set the `async` attribute in **Air Tags**, use the `async_` keyword.
```
air.Script(
'console.log("Hello, world!");',
async_=True,
)
```
renders as
```
```
### Attributes starting with special characters
To get around that in Python we can't begin function arguments with special characters, we lean into how **Air Tags** is kwargs friendly.
```
air.P('Hello', class_='plain', **{'@data': 6})
```
Renders as:
```
Hello
```
### Single word attributes
To set or hide single word attributes like `@selected`, set the tag to `True` or `False` respectively.
```
air.Select(
air.Option('South America', value='SA', selected=True),
air.Option('North America', value='NA', selected=False)
)
```
Renders as:
```
```
Note
For the sake of clarity this example was rendered using `print(content.pretty_render())`.
If you need a value set to `true`, use `"true"` in Python. For example:
```
air.P("Air makes FastAPI web pages easy", draggable="true")
```
Renders as:
```
Air makes FastAPI web pages easy
```
## Works well with SVGs
Unlike HTML, SVG tags are case-sensitive. You can access SVG tags by importing them from the `air.svg` module. Here's a simple example:
```
from air import svg
svg.Svg(
svg.Circle(cx='50', cy='50', r='40', fill='blue'),
width='100',
height='100'
)
```
This will render the following SVG:
```
```
Note
For the sake of clarity this example was rendered using `print(content.pretty_render())`.
## Custom Air Tags
The best way to define your own **Air Tags** is to subclass the `air.Tag` class. Here's a simple example:
```
from air import Tag
class Tasty(Tag):
pass
```
Let's instantiate this class and call its `render()` method:
```
Tasty('Ice Cream', class_='dessert').render()
```
This will produce the following HTML:
```
Ice Cream
```
## Functions as Custom **Air Tags**
Subclasses are not the only way to create custom Air Tags. You can also use functions to create Air Tags. This is particularly useful for putting together components quickly without needing to define a class. Here's an example of a function that creates a custom Air Tag for a [picocss card](https://picocss.com/docs/card):
```
def card(*content, header:str, footer:str):
return air.Article(
air.Header(header),
*content,
air.Footer(footer)
)
```
We can use this function to create a card:
```
card(
air.P("This is a card with some content."),
air.P("It can have multiple paragraphs."),
header="Card Header",
footer="Card Footer",
).render()
```
Which produces the following HTML:
```
Card Header
This is a card with some content.
It can have multiple paragraphs.
```
Note
For the sake of clarity this example was rendered using `print(card(...).pretty_render())`.
## Returning Multiple Children (used in HTMX)
When using HTMX to add reactivity to pages, it is common to return several **Air Tags** so that HTMX can then replace existing DOM elements with new ones. **Air Tags** are hierarchical, you need a base tag that just serves as a wrapper that doesn't generate any HTML. That tag is the `air.Tags`. Here's how to use it:
```
import
@app.post('/cart/add/{product_id}/')
def update_cart(request: air.Request, product_id: int):
"This is a simplified update cart view"
# air.Tags renders the child tags without adding anything of its own
return air.Tags(
# Mark that an item has been added to the cart
Button('Added!', hx_post='/cart/add/{{product.id}}', hx_swap_oob='true', id='add-button'),
# Cart icon quantity changed
A(f'Cart {count}', id='cart-icon', href='/cart', hx_trigger='polling 30s', hx_get='/cart-icon', hx_swap_oob='true'),
)
```
This will generate HTML that looks something like this, without any wrapping text around the elements we are passing to the user's browser:
```
Cart 2
```
## Converting HTML to Air Tags
The easiest way to do that is with the [air-convert](https://pypi.org/project/air-convert/) package.
```
pip install air-convert
```
```
from air_convert import html_to_airtags
html_to_airtags("""
Hello, World
""")
```
This generates:
```
air.Html(
air.Body(
air.Main(
air.H1('Hello, World', class_='header')
)
)
)
```
Removal of the `air.` prefix is done with the `air_prefix` boolean:
```
html = """
Hello, World
"""
print(air.html_to_airtags(html, air_prefix=False))
```
This will generate:
```
Html(
Body(
Main(
H1('Hello, World', class_='header')
)
)
)
```
# Escaping HTML
Escaping HTML is where text is converted from tags and script that will interact with the DOM to representations of those things. For example:
Unscaped HTML:
```
Hello, World!
```
Escaped HTML:
```
<h1>Hello, World!</h1>
```
This is useful for preventing security issues like code injection by malignant users who have access to text fields that display text they enter. Escaping blocks the addition of tags, JavaScript, and CSS.
## Jinja2 doesn't play it safe
By default, Jinja2 escapes nothing. It puts the burden of safety on the developer. To make Jinja2 escape text, use the `e` filter.
```
{% set h1 = '
Hello, World!
' %}
{{ h1|e }}
```
> ## "Jinja2 not playing it safe isn't a bad thing"
>
> We want to make it clear that Jinja2 not playing it safe isn't wrong. It can expedite development. However, it is important to note that the default can open the door to trouble. The design of Jinja2 accommodates this by making the `e` filter be so short - so it is easy and quick to use.
## Air Tags plays it safe
In contrast, by default Air Tags escapes everything.
```
air.H1("
Hello, World!
")
```
renders as:
```
<h1>Hello, World!</h1>
```
To provide unescaped code, Air Tags provides three options: the `Style`, `Script`, and `Raw` tags - which are described below.
### `Style`: Unescaped CSS
To avoid escaping CSS, use the `Style` tag:
```
air.Style("""
p {
font-size: 1.2rem;
line-height: 1.6;
color: #333;
max-width: 60ch;
margin: 1em auto;
}
""")
```
Renders as:
```
```
### `Script`: Unescaped JavaScript
To avoid escaping JavaScript, use the `Script` tag:
```
air.Script("""
function capitalize(str) {
if (!str) return '';
return str[0].toUpperCase() + str.slice(1);
}
""")
```
Renders as:
```
```
### `Raw`: Unescaped text
To avoid escaping anything and everything, use the `Raw` tag:
```
air.Raw("
Hello, World
")
```
Renders as:
```
Hello, World
```
# Forms
Forms are how data is collected from users on web pages.
> ## Note
>
> This document covers how **Forms** work. The full reference for them is the [Forms reference](https://feldroy.github.io/air/api/forms/).
## A simple form example
This contact form is in the classic Starlette way, with no validation of data. However, it does show a method to build forms quickly.
```
import air
app = air.Air()
@app.page
def index():
return air.layouts.mvpcss(
air.Title("Contact Form"),
air.H1("Contact Form"),
air.Article(
air.Form(
air.Label("Email", air.Input(name="email", type="email"), for_="Email"),
air.Label(
"Name",
air.Input(name="name"),
),
air.Button("submit", type="submit"),
action="/add-contact",
method="post",
)
),
)
@app.post("/add-contact")
async def add(request: air.Request):
form = await request.body()
return air.layouts.mvpcss(
air.Title("Contact Form Result"),
air.H1("Contact Form Result"),
air.Pre(air.Code(form)),
)
```
## Air Forms
Air Forms are powered by Air Models, which inherit directly from `pydantic.BaseModel`. That includes both their display and validation of data. If you have any experience with Pydantic, that will go a long way towards helping your understanding of Air Forms.
### A Sample Contact Air Form
```
from air import AirForm, AirField, AirModel
class ContactModel(AirModel):
name: str
email: str = AirField(type="email", label="Email")
# Create an Air Form instance from the Air Model
contact_form = ContactModel.to_form()
```
### Displaying an Air Form
```
contact_form.render()
```
```
```
## Validation using forms
```
# This empty dict represents a user who submitted without adding data
empty_form = {}
contact_form.validate(empty_form)
```
## Displaying a failed form
```
contact_form.render()
```
```
```
## Converting Pydantic Models to Air Forms
You can easily convert any Pydantic model into an Air Form using the `to_form` function:
```
from pydantic import BaseModel, EmailStr
class ContactModel(BaseModel):
name: str
email: EmailStr
ContactForm = air.to_form(ContactModel)
contact_form = ContactForm()
```
## Enhanced Error Messages
Air Forms automatically display user-friendly error messages that clearly explain validation failures:
- **Missing fields**: "This field is required."
- **Invalid numbers**: "Please enter a valid number."
- **Invalid email addresses**: "Please enter a valid email address."
- **Values too short/long**: "This value is too short." / "This value is too long."
- **URL validation**: "Please enter a valid URL."
- **And many more...**
For unknown error types, the system falls back to the technical Pydantic error message, ensuring developers always get meaningful feedback.
# Using Jinja with Air
We love Jinja. Proven and fast, it's our go-to for when we want to manually craft templates containing programmatic content. To that end, we've ensured Air works great at combining Jinja and Air Tags together.
Note
This document covers the concepts and how Jinja2 works in Air. The full reference for the tooling can be found at the [Templates API Reference](https://feldroy.github.io/air/api/templates/).
Jinja or Jinja2?
While the package is listed on [PyPI as Jinja2](https://pypi.org/project/jinja2/), that package and the [official Jinja docs](https://jinja.palletsprojects.com/) refers to Jinja as just "Jinja". Also, Jinja was released in 2008 and is well into the 3.x release cycle. If we want to lean into pedantry, we are arguably using Jinja 5 (base plus major releases cycles of 0.x, 1.x, 2.x, and 3.x).
Most importantly, it is the intent of the maintainer of Jinja to not only document the package as 'Jinja' but to even provide a `jinja` namespace in addition to `jinja2`.
In short, to match the Jinja documentation and the intent of the maintainer, in the Air documentation we use the term "Jinja".
## Using Jinja for the HTML Layout
Air Tags are powerful but for those of us with a lot of experience with HTML, sometimes it's easy to construct layouts using Jinja. As it is closer in look-and-feel to HTML for some of us that makes ensuring the end result looks good is easier.
Here's a simple Jinja layout file:
templates/base.html
```
{{title}}
{# We need to safe the content, which can be
a security risk. We'll cover mitigation
of such issues later on this page.
#}
{{content|safe}}
```
If you've used Jinja before this should look familiar. Now let's add in our Air Tags-powered content, which we'll do from the view.
main.py
```
from air import Air
from air.requests import Request
import air
app = Air()
# Set the Jinja render function
jinja = air.JinjaRenderer(directory="tests/templates")
@app.get('/')
def index(request: Request):
content = air.Main(
air.H2('Does Jinja work with Air Tags?'),
air.P("Jinja works great with Air Tags")
)
return jinja(
request,
name="base.html",
title="FAQ",
content=content,
)
```
When run, this will look like this:
TODO: ADD SCREEN CAPTURE
Why don't we call the `jinja()` function `render()`?
Because Air uses `.render()` as a method name in a lot of places, especially with Air Tags. So even though `render()` instead of `jinja()` is more semantically correct, to avoid confusion and collision, we use `jinja()` instead.
# Layouts
Layouts in Air provide a way to structure complete HTML documents without the repetitive boilerplate. Air's layout system automatically handles the separation of head and body content and makes it easy to create your own custom layouts.
> ## Note
>
> This document covers how **Layouts** work. The full reference for them is the [Layouts reference](https://feldroy.github.io/air/api/layouts/).
## Understanding Air's Layout Philosophy
Air's layout functions automatically sort your tags into the right places using intelligent filtering. This allows you eliminate repetitive `air.Html`, `air.Body`, and `air.Head` boilerplate.
```
# Verbose Way
air.Html(
air.Head(
air.Title("My App"),
air.Link(rel="stylesheet", href="style.css")
),
air.Body(
air.H1("Welcome"),
air.P("Content here")
)
)
# Air Layouts
air.layouts.mvpcss(
air.Title("My App"), # Automatically goes to
air.Link(rel="stylesheet", href="style.css"), # Also goes to
air.H1("Welcome"), # Goes to
air.P("Content here") # Also goes to
)
```
## The Tag Filtering System
Air layouts use two core functions to organize content:
- `filter_head_tags`: Returns only tags in `(Title, Style, Meta, Link, Script, Base)`
- `filter_body_tags`: Returns all tags except `(Title, Style, Meta, Link, Script, Base)`
### Why This Matters
This automatic separation means you can focus on your content and let Air handle the document structure:
```
@app.get("/")
def home():
return air.layouts.mvpcss(
# Mix head and body tags freely - Air sorts them
air.Title("Dashboard"),
air.H1("Welcome to the Dashboard"),
air.Meta(name="description", content="User dashboard"),
air.P("Your stats here"),
air.Script(src="dashboard.js")
)
```
Air transforms this into proper HTML structure automatically.
## Built-in Minimal Layouts
Air provides minimal ready-to-use layouts for rapid prototyping, `mvpcss` and `picocss` for MVP.css and PicoCSS respectively. They both work and are used in the exact same way.
```
import air
@app.get("/")
def home():
return air.layouts.picocss( # or mvpcss
air.Title("My App"),
air.H1("Welcome"),
air.P("This automatically looks great!"),
air.Button("Get Started")
)
```
**What you get:**
- [MVP.css](https://andybrewer.github.io/mvp/) styling or [PicoCSS](https://picocss.com/)
- HTMX included by default for interactivity
- Container wrapper for proper spacing
**Perfect for:**
- Prototypes and demos
- Internal tools and dashboards
- Quick proofs of concept
- Learning Air basics
## Beyond Built-in Layouts
The included layouts are designed for **quick prototyping**, not production commercial applications. Custom layouts give you complete control while preserving Air's automatic tag filtering benefits.
Here's the foundational pattern for any Air layout:
```
import air
def my_layout(*children, **kwargs):
"""My custom layout function."""
# 1. Separate head and body content
head_tags = air.layouts.filter_head_tags(children)
body_tags = air.layouts.filter_body_tags(children)
# 2. Build your custom structure
return air.Html(
air.Head(
# Your custom head content
...
*head_tags # User's head tags
),
air.Body(
# Your custom body structure
... # Header content
air.Main(*body_tags), # User's body content
... # Footer Content
),
).render()
```
**Key principles:**
1. **Always filter tags** using Air's helper functions
1. **Use `*head_tags` and `*body_tags`** to include user content
1. **Return `.render()`** to get the final HTML string
# Cookbook
A handy Air-themed list of recipes for common web app tasks.
This section contains short recipes. See the topic pages for details:
- **[app.page decorator](page-decorator/)** - how to use the `@app.page` decorator to quickly create web pages
- **[Authentication](authentication/)** - how to handle login and authentication in your application
- **[Bigger applications](bigger-applications/)** - how to structure larger applications
- **[Charts](charts/)** - Examples of using charts in Air
- **[Minimal app](minimal/)** - The smallest possible Air app
- **[Forms & validation](forms/)** - how to create forms and validate user inputs
- **[Static files](static/)** - how to serve static files like CSS and JavaScript
If you'd like to contribute a recipe, open a PR with a new file under `docs/cookbook`.
# Authentication
How to handle login and authentication in your application. It does not cover authorization, which is a separate topic covering what permissions a user has.
## A minimal authentication example
This first example shows how to create a password-less authentication flow using Air. This is a toy example that uses sessions to keep track of the logged-in user. In a real application, you would want to use a more secure method of authentication, with passwords or OAuth.
```
import air
from time import time
app = air.Air()
app.add_middleware(air.SessionMiddleware, secret_key="change-me")
@app.page
async def index(request: air.Request):
if "username" in request.session:
# User is logged in provide a logout link
action = air.Tags(
air.H1(request.session["username"]),
air.P(request.session.get("logged_in_at")),
air.P(air.A("Logout", href="/logout")),
)
else:
# login the user
action = air.Form(
air.Label("Name:", for_="username"),
air.Input(type="text", name="username", id="username", required=True, autofocus=True),
air.Button("Login", type="submit"),
action="/login",
method="post",
)
return air.layouts.mvpcss(action)
@app.post("/login")
async def login(request: air.Request):
form = await request.form()
if username := form.get("username"):
# Create session
request.session["username"] = username
request.session["logged_in_at"] = time()
return air.responses.RedirectResponse("/", status_code=302)
@app.page
async def logout(request: air.Request):
request.session.pop("username")
return air.responses.RedirectResponse("/")
```
## Authentication with Dependencies
In Air and FastAPI apps we frequently rely on dependencies to handle authentication. Dependencies are a powerful way to share logic between different parts of your application. Here's a simple authentication dependency example:
```
import air
from fastapi import HTTPException
def require_login(request: air.Request):
# Replace this with your actual login check
user = request.session.get("user") if hasattr(request, "session") else None
if not user:
# Redirect if not logged in
raise HTTPException(
status_code=307,
headers={"Location": "/login"},
)
return user
```
In Air, like FastAPI, session objects need to be serializable to JSON. That means you can't store complex objects like database models directly in the session. Instead, store simple identifiers (like user IDs) and fetch the full user details from your database as needed.
Attaching this dependency to a route ensures that only authenticated users can access it. If a user is not authenticated, they will be redirected to the login page. Here's how you can use the `require_login` dependency in a route:
```
import air
app = air.Air()
air.add_middleware(air.SessionMiddleware, secret_key="change-me")
# --- Dependency ---
def require_login(request: air.Request):
# Replace this with your actual login check
user = request.session.get("user") if hasattr(request, "session") else None
if not user:
# Redirect if not logged in
raise HTTPException(
status_code=307,
headers={"Location": "/login"},
)
return user
# --- Routes ---
@app.page
async def dashboard(request: air.Request, user=Depends(require_login)):
return air.layouts.mvpcss(
air.H1(f"Dashboard for {request.session['user']['username']}"),
air.P(air.A('Logout', href='/logout'))
)
```
Here's a more complete example that includes a login page, a protected dashboard page, and logout functionality using the `require_login` dependency:
```
import air
from fastapi import Depends, HTTPException
app = air.Air()
app.add_middleware(air.SessionMiddleware, secret_key="change-me")
# --- Dependency ---
def require_login(request: air.Request):
# Replace this with your actual login check
user = request.session.get("user") if hasattr(request, "session") else None
if not user:
# Redirect if not logged in
raise HTTPException(
status_code=307,
headers={"Location": "/login"},
)
return user
# --- Routes ---
@app.page
async def index(request: air.Request):
return air.layouts.mvpcss(
air.H1('Landing page'),
air.P(air.A('Dashboard', href='/dashboard'))
)
@app.page
async def login():
return air.layouts.mvpcss(
air.H1('Login'),
# login the user
air.Form(
air.Label("Name:", for_="username"),
air.Input(type="text", name="username", id="username", required=True, autofocus=True),
air.Label("Password:", for_="password"),
air.Input(type="password", name="password", id="password", required=True, autofocus=True),
air.Button("Login", type="submit"),
action="/login",
method="post",
)
)
@app.page
async def dashboard(request: air.Request, user=Depends(require_login)):
return air.layouts.mvpcss(
air.H1(f"Dashboard for {request.session['user']['username']}"),
air.P(air.A('Logout', href='/logout'))
)
@app.post('/login')
async def login(request: air.Request):
form = await request.form()
request.session['user'] = dict(username=form.get('username'))
return air.RedirectResponse('/dashboard', status_code=303)
@app.page
async def logout(request: air.Request):
request.session.pop('user')
return air.RedirectResponse('/', status_code=303)
```
# Bigger Applications
When building larger applications with Air, you may find yourself needing to organize your code better, manage multiple routes, or even mount different applications together. This guide will help you understand how to structure your Air applications for scalability and maintainability.
## Routing Separate Apps Together
Note
This approach shares state between the composited efforts. This means that authentication, database pooling, and other things will be usable between components. The [API reference](../../../api/routing/) for this documentation displays options for more controls like Router-specific lifespans, URL prefixes, and more.
Let's imagine we have a landing page that links to a sophisticated dashboard. While our example dashboard is trivial, let's assume it is complicated enough that we want it in a separate Python module yet share state. We design the `main.py` as we would a normal Air application:
main.py
```
import air
app = air.Air()
@app.page
def index():
return air.layouts.mvpcss(
air.H1('Avatar Data'),
air.P(air.A('Dashboard', href='/dashboard'))
)
```
Now for the dashboard, instead of using the typical `air.Air` tool to instantiate our application, we use `air.AirRouter` like so:
dashboard.py
```
import air
router = air.AirRouter()
@router.page
def dashboard():
return air.layouts.mvpcss(
air.H1('Avatar Data Dashboard'),
air.P(air.A('<- Home', href='/'))
)
```
Now if we go back to our `main.py` we can use the `app.include_router()` method to include the dashboard in our app:
main.py
```
import air
from .dashboard import router
app = air.Air()
app.include_router(router)
@app.page
def index():
return air.layouts.mvpcss(
air.H1('Avatar Data'),
air.P(air.A('Dashboard', href='/dashboard'))
)
```
If run locally these links should work:
- http://localhost:8000
- http://localhost:8000/dashboard
## Mounting Air and FastAPI apps inside Air apps
Warning
This approach does not share state between the apps. That means attempting to share authentication and database pooling will not function predictably, if at all.
One of the really nice features of Air is the ability to mount apps inside each other. This allows you to create modular applications where different parts of your app can be developed and maintained independently. To do this, we lean on Starlette's `mount` functionality that Air inherits through FastAPI.
```
import air
# Create the main app, which serves as the entry point
app = air.Air(title='Air')
@app.page
def index():
return air.layouts.mvpcss(
air.H1('Air landing page'),
air.P(air.A('Shop', href='/shop'))
)
# Creating a separate app for the shop,
# which could be placed in a different file
shop = air.Air(title='Air shop')
@shop.page
def index():
return air.layouts.mvpcss(
air.H1('Shop for Air things')
)
# Mount the shop app to the main app
# This allows you to access the shop at /shop
app.mount('/shop', shop)
```
## Mounting FastAPI inside of Air apps
You can easily mount a FastAPI app inside an Air app. A common scenario is to have a FastAPI app that serves an API, while your main Air app serves the landing, billing, and usage frontends.
```
import air
from fastapi import FastAPI
# Create the landing page app using Air
app = air.Air()
@app.get("/")
def landing_page():
return air.Html(
air.Head(air.Title("Awesome SaaS")),
air.Body(
air.H1("Awesome SaaS"),
air.P(air.A("API Docs", target="_blank", href="/api/docs")),
),
)
api = FastAPI()
@api.get("/")
def api_root():
return {"message": "Awesome SaaS is powered by FastAPI"}
# Combining the Air and and FastAPI apps into one
app.mount("/api", api)
```
## Mounting FastAPI apps inside each other
Mounting one FastAPI app is outside the scope of this guide. We recommend reading [FastAPI's Bigger Application](https://fastapi.tiangolo.com/tutorial/bigger-applications) reference.
# Charts
Air is great for building charts. Using [plotly](https://plotly.com/javascript/), here's a simple chart example.
```
import air
import json
app = air.Air()
@app.get("/")
def index():
title = "Air Chart Demo"
data = json.dumps(
{
"data": [
{"x": [0, 4, 5, 7, 8, 10], "y": [2, 9, 0, 4, 3, 6], "type": "scatter"},
{"x": [0, 1, 2, 4, 8, 10], "y": [9, 2, 4, 3, 5, 0], "type": "scatter"},
],
"title": "Fun charts with Plotly and Air",
"description": "This is a demonstration of how to build a chart using Plotly and Air",
"type": "scatter",
}
)
return air.layouts.mvpcss(
air.Script(src="https://cdn.plot.ly/plotly-3.0.1.min.js"),
air.Title(title),
air.H1(title),
air.Article(
air.P(
"Made with ",
air.A("Air", href="https://github.com/feldroy/air"),
" and ",
air.A("Plotly", href="https://plotly.com/javascript/"),
),
air.Div(id="chart"),
air.Script(f"var data = {data}; Plotly.newPlot('chart', data);"),
),
)
```
Air makes it possible to build charts that pull data from servers and animate the results. Here's an example being supplied with random numbers for the Air server.
```
air.Children(
air.Div(id="randomChart"),
air.Script("""
var data = {"data": [{"x": [0, 4, 5, 7, 8, 10], "y": [2, 9, 0, 4, 3, 6], "type": "scatter"}, {"x": [0, 1, 2, 4, 8, 10], "y": [9, 2, 4, 3, 5, 0], "type": "scatter"}], "title": "Fun charts with Plotly and Air", "description": "This is a demonstration of how to build a chart using Plotly and Air", "type": "scatter"};
Plotly.newPlot('randomChart', data);""",
# ID is used to help HTMX know where to replace data
id="dataSource",
# Trigger HTMX to call new data every 2 seconds
hx_trigger="every 2s",
# Use HTMX to fetch new info from the /data route
hx_get="/data",
# When the data is fetched, replace the whole tag
hx_swap="outerHTML",
)
)
```
# Cookbook checklist
All the things that may go into this page:
- Mounting static files
- Mounting with a FastAPI app
- File uploads
- SSE
- Web sockets
- Charts with plotly
- Debug mode
- Routing
- Using layouts
- Making your own layout
- Adding markdown
- Defining components (function-based tags, class-based tags)
- Custom exception handlers
- Cookies
- Sessions
- Authentication
- Redirects
# Forms & validation
Built on Pydantic's `BaseModel`, the `air.AirForm` class is used to validate data coming from HTML forms.
```
from typing import Annotated
from fastapi import Depends, Request
from pydantic import BaseModel, Field
import air
app = air.Air()
class FlightModel(BaseModel):
flight_number: str = Field(..., min_length=1)
destination: str = Field(..., min_length=1)
class FlightForm(air.AirForm):
model = FlightModel
@app.page
async def index():
return air.layouts.mvpcss(
air.H1("Flight Form"),
air.Form(
air.Input(name="flight_number", placeholder='flight number'),
air.Input(name="destination", placeholder='destination'),
air.Button("Submit", type="submit"),
method="post",
action="/flight-info",
),
)
@app.post("/flight-info")
async def flight_info(request: Request):
flight = await FlightForm.from_request(request)
if flight.is_valid:
return air.Html(air.H1(f'{flight.data.flight_number} → {flight.data.destination}'))
# Show form with enhanced error messages and preserved user input
return air.layouts.mvpcss(
air.H1("Flight Form"),
air.P("Please correct the errors below:"),
air.Form(
flight.render(), # Automatically shows user-friendly error messages
air.Button("Submit", type="submit"),
method="post",
action="/flight-info",
),
)
```
## Enhanced Form Features
### User-Friendly Error Messages
Air Forms automatically convert technical Pydantic validation errors into clear, actionable messages:
```
# Instead of: "Input should be a valid integer, unable to parse string as an integer"
# Users see: "Please enter a valid number."
# Instead of: "Field required"
# Users see: "This field is required."
```
### Value Preservation
When validation fails, user input is automatically preserved, so users don't have to re-enter their data:
```
# User submits: {"flight_number": "AB123", "destination": ""}
# After validation error, the form still shows "AB123" in the flight_number field
flight = await FlightForm.from_request(request)
if not flight.is_valid:
return show_form_with_errors(flight) # Values are preserved automatically
```
## Coming Soon: Dependency-Injection Form Handling
It is possible to use dependency injection to manage form validation.
NOTE: This functionality is currently in development. This feature was working before but currently does not work.
```
from typing import Annotated
from fastapi import Depends
from pydantic import BaseModel
import air
app = air.Air()
class FlightModel(BaseModel):
flight_number: str
destination: str
class FlightForm(air.AirForm):
model = FlightModel
@app.page
async def flight():
return air.Html(
air.H1("Flight Form"),
air.Form(
air.Input(name="flight_number"),
air.Input(name="destination"),
air.Button("Submit", type="submit"),
method="post",
action="/flight-info",
),
)
@app.post("/flight-info")
async def flight_info(flight: Annotated[FlightForm, Depends(FlightForm.validate)]):
if flight.is_valid:
return air.Html(air.H1(f'{flight.data.flight_number} → {flight.data.destination}'))
return air.Html(air.H1(f"Errors {len(flight.errors)}"))
```
# Minimal app
The "Hello, World" of Air is:
```
import air
app = air.Air()
@app.get("/")
async def index():
return air.H1("Hello, Air!", style="color: blue;")
```
# The app.page decorator
For simple HTTP GET requests, Air provides the handy @app.page shortcut. It converts the name of the function to a URL, where underscores are replaced with dashes and `index` is replaced with '/'.
```
import air
app = air.Air()
@app.page
def index():
# Same as route app.get('/')
return air.H1('Welcome to our site!')
@app.page
def dashboard():
# Same as route app.get('/dashboard')
return air.H1('Dashboard')
@app.page
def show_item():
# same as app.get('/show-item')
return air.H1('Showing an item')
```
An option has been added to change the path separator to forward slashes instead of dashes.
```
import air
app = air.Air(path_separator="/")
@app.page
def about_us():
# same as app.get('/about/us`)
return air.H1('About us!')
```
# Serving static files
You can serve static files like CSS, JavaScript, and images using Air's built-in static file serving capabilities. In this example, we’ll create a simple Air app that serves static files from a `static` directory, but the name of the directory can be anything (`public` is also common).
```
import air
app = air.Air()
app.mount("/static", air.StaticFiles(directory="static"), name="static")
@app.page
def index():
return air.layouts.mvpcss(
air.H1("Welcome to My Site!"),
air.Link(rel="stylesheet", href="/static/styles.css"),
air.Script(src="/static/scripts.js"),
air.Img(src="/static/images/logo.png", alt="Logo"),
)
```
# Air Reference
Here is the Air reference documentation. It explains how to do things, as well as providing reference details for nearly every object in the Air code.
- [Applications](applications/) - The app instantiator for Air
- [Background Tasks](background/) - Background tasks for Air
- [Exception Handlers](exception_handlers/) - Exceptions are returned to the user, specifically 404 and 500
- [Exceptions](exceptions/) - Sometimes it's good to know exactly what is breaking
- [Forms](forms/) - Receive and validate data from users on web pages
- [Layouts](layouts/) - Utilities for building layout functions and two example layouts for css microframeworks (mvcss and picocss)
- [Middleware](middleware/) - Middleware for Air
- [Requests](requests/) - HTMX utility function that can be used with dependency injection
- [Responses](responses/) - AirResponse for normal responses and SSEResponse for Server Sent Events
- [Routing](routing/) - For compositing multiple apps inside each other
- [SVG](svg/) - Reference for the entire SVG tags specification, all of which are supported by Air
- [Tags](tags/) - Reference for the entire HTML tags specification, all of which are supported by Air
- [Templating](templating/) - Describes Jinja and Air Tag renderers for use in both projects and third-party installable packages
- [Utils](utils/) - Utilities that don't fall into one of the above categories
# Applications
Instantiating Air applications.
## Air
```
Air(
*,
debug=False,
routes=None,
servers=None,
dependencies=None,
default_response_class=AirResponse,
redirect_slashes=True,
middleware=None,
exception_handlers=None,
on_startup=None,
on_shutdown=None,
lifespan=None,
webhooks=None,
deprecated=None,
docs_url=None,
redoc_url=None,
openapi_url=None,
path_separator="-",
**extra,
)
```
Bases: `FastAPI`, `RouterMixin`
FastAPI wrapper class with AirResponse as the default response class.
Parameters:
| Name | Type | Description | Default |
| ------------------------ | --------------------------------- | ------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| `debug` | `bool` | Enables additional logging or diagnostic output. | `False` |
| `dependencies` | \`Sequence[Depends] | None\` | A list of global dependencies, they will be applied to each path operation, including in sub-routers. |
| `middleware` | \`Sequence[Middleware] | None\` | List of middleware to be added when creating the application. |
| `default_response_class` | `type[Response]` | The default response class to be used. | `AirResponse` |
| `redirect_slashes` | `bool` | Whether to detect and redirect slashes in URLs when the client doesn't use the same format. | `True` |
| `on_startup` | \`Sequence\[Callable\[[], Any\]\] | None\` | A list of startup event handler functions. |
| `on_shutdown` | \`Sequence\[Callable\[[], Any\]\] | None\` | A list of shutdown event handler functions. |
| `lifespan` | \`Lifespan[AppType] | None\` | A Lifespan context manager handler. This replaces startup and shutdown functions with a single context manager. |
| `path_separator` | `Literal['/', '-']` | An optional path separator, default to "-". valid option available ["/", "-"] | `'-'` |
Example:
```
import air
app = air.Air()
```
This preserves most FastAPI initialization parameters while setting
- AirResponse as the default response class.
- AirRoute as the default route class.
Source code in `src/air/applications.py`
````
def __init__(
self: AppType,
*,
debug: Annotated[
bool,
Doc(
"""
Boolean indicating if debug tracebacks should be returned on server
errors.
Read more in the
[Starlette docs for Applications](https://www.starlette.io/applications/#instantiating-the-application).
"""
),
] = False,
routes: Annotated[
list[BaseRoute] | None,
Doc(
"""
**Note**: you probably shouldn't use this parameter, it is inherited
from Starlette and supported for compatibility.
---
A list of routes to serve incoming HTTP and WebSocket requests.
"""
),
deprecated(
"""
You normally wouldn't use this parameter with FastAPI, it is inherited
from Starlette and supported for compatibility.
In FastAPI, you normally would use the *path operation methods*,
like `app.get()`, `app.post()`, etc.
"""
),
] = None,
servers: Annotated[
list[dict[str, str | Any]] | None,
Doc(
"""
A `list` of `dict`s with connectivity information to a target server.
You would use it, for example, if your application is served from
different domains and you want to use the same Swagger UI in the
browser to interact with each of them (instead of having multiple
browser tabs open). Or if you want to leave fixed the possible URLs.
If the servers `list` is not provided, or is an empty `list`, the
default value would be a `dict` with a `url` value of `/`.
Each item in the `list` is a `dict` containing:
* `url`: A URL to the target host. This URL supports Server Variables
and MAY be relative, to indicate that the host location is relative
to the location where the OpenAPI document is being served. Variable
substitutions will be made when a variable is named in `{`brackets`}`.
* `description`: An optional string describing the host designated by
the URL. [CommonMark syntax](https://commonmark.org/) MAY be used for
rich text representation.
* `variables`: A `dict` between a variable name and its value. The value
is used for substitution in the server's URL template.
Read more in the
[FastAPI docs for Behind a Proxy](https://fastapi.tiangolo.com/advanced/behind-a-proxy/#additional-servers).
**Example**
```python
from fastapi import FastAPI
app = FastAPI(
servers=[
{"url": "https://stag.example.com", "description": "Staging environment"},
{"url": "https://prod.example.com", "description": "Production environment"},
]
)
```
"""
),
] = None,
dependencies: Annotated[
Sequence[Depends] | None,
Doc(
"""
A list of global dependencies, they will be applied to each
*path operation*, including in sub-routers.
Read more about it in the
[FastAPI docs for Global Dependencies](https://fastapi.tiangolo.com/tutorial/dependencies/global-dependencies/).
**Example**
```python
from fastapi import Depends, FastAPI
from .dependencies import func_dep_1, func_dep_2
app = FastAPI(dependencies=[Depends(func_dep_1), Depends(func_dep_2)])
```
"""
),
] = None,
default_response_class: Annotated[
type[Response],
Doc(
"""
The default response class to be used.
Read more in the
[FastAPI docs for Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/#default-response-class).
**Analogy**
```python
from fastapi import FastAPI
from air import AirResponse
app = FastAPI(default_response_class=AirResponse)
```
"""
),
] = AirResponse,
redirect_slashes: Annotated[
bool,
Doc(
"""
Whether to detect and redirect slashes in URLs when the client doesn't
use the same format.
**Example**
```python
from fastapi import FastAPI
app = FastAPI(redirect_slashes=True) # the default
@app.get("/items/")
async def read_items():
return [{"item_id": "Foo"}]
```
With this app, if a client goes to `/items` (without a trailing slash),
they will be automatically redirected with an HTTP status code of 307
to `/items/`.
"""
),
] = True,
middleware: Annotated[
Sequence[Middleware] | None,
Doc(
"""
List of middleware to be added when creating the application.
In FastAPI you would normally do this with `app.add_middleware()`
instead.
Read more in the
[FastAPI docs for Middleware](https://fastapi.tiangolo.com/tutorial/middleware/).
"""
),
] = None,
exception_handlers: Annotated[
ExceptionHandlersType | None,
Doc(
"""
A dictionary with handlers for exceptions.
In FastAPI, you would normally use the decorator
`@app.exception_handler()`.
Read more in the
[FastAPI docs for Handling Errors](https://fastapi.tiangolo.com/tutorial/handling-errors/).
"""
),
] = None,
on_startup: Annotated[
Sequence[Callable[[], Any]] | None,
Doc(
"""
A list of startup event handler functions.
You should instead use the `lifespan` handlers.
Read more in the [FastAPI docs for `lifespan`](https://fastapi.tiangolo.com/advanced/events/).
"""
),
] = None,
on_shutdown: Annotated[
Sequence[Callable[[], Any]] | None,
Doc(
"""
A list of shutdown event handler functions.
You should instead use the `lifespan` handlers.
Read more in the
[FastAPI docs for `lifespan`](https://fastapi.tiangolo.com/advanced/events/).
"""
),
] = None,
lifespan: Annotated[
Lifespan[AppType] | None,
Doc(
"""
A `Lifespan` context manager handler. This replaces `startup` and
`shutdown` functions with a single context manager.
Read more in the
[FastAPI docs for `lifespan`](https://fastapi.tiangolo.com/advanced/events/).
"""
),
] = None,
webhooks: Annotated[
routing.APIRouter | None,
Doc(
"""
Add OpenAPI webhooks. This is similar to `callbacks` but it doesn't
depend on specific *path operations*.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
**Note**: This is available since OpenAPI 3.1.0, FastAPI 0.99.0.
Read more about it in the
[FastAPI docs for OpenAPI Webhooks](https://fastapi.tiangolo.com/advanced/openapi-webhooks/).
"""
),
] = None,
deprecated: Annotated[
bool | None,
Doc(
"""
Mark all *path operations* as deprecated. You probably don't need it,
but it's available.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/).
"""
),
] = None,
docs_url: Annotated[
str | None,
Doc(
"""
The path at which to serve the Swagger UI documentation.
Set to `None` to disable it.
"""
),
] = None,
redoc_url: Annotated[
str | None,
Doc(
"""
The path at which to serve the ReDoc documentation.
Set to `None` to disable it.
"""
),
] = None,
openapi_url: Annotated[
str | None,
Doc(
"""
The URL where the OpenAPI schema will be served from.
Set to `None` to disable it.
"""
),
] = None,
path_separator: Annotated[Literal["/", "-"], Doc("An optional path separator.")] = "-",
**extra: Annotated[
Any,
Doc(
"""
Extra keyword arguments to be stored in the app, not used by FastAPI
anywhere.
"""
),
],
) -> None:
"""Initialize Air app with AirResponse as default response class.
This preserves most FastAPI initialization parameters while setting:
- AirResponse as the default response class.
- AirRoute as the default route class.
"""
self.path_separator = path_separator
if exception_handlers is None:
exception_handlers = {}
exception_handlers |= DEFAULT_EXCEPTION_HANDLERS
super().__init__(
debug=debug,
routes=routes,
servers=servers,
dependencies=dependencies,
default_response_class=default_response_class,
middleware=middleware,
exception_handlers=exception_handlers, # ty: ignore[invalid-argument-type]
on_startup=on_startup,
on_shutdown=on_shutdown,
lifespan=lifespan,
docs_url=docs_url,
redoc_url=redoc_url,
openapi_url=openapi_url,
webhooks=webhooks,
deprecated=deprecated,
**extra,
)
self.router.route_class = AirRoute
````
### delete
```
delete(
path,
*,
response_model=None,
status_code=None,
tags=None,
dependencies=None,
summary=None,
description=None,
response_description="Successful Response",
responses=None,
deprecated=None,
operation_id=None,
response_model_include=None,
response_model_exclude=None,
response_model_by_alias=True,
response_model_exclude_unset=False,
response_model_exclude_defaults=False,
response_model_exclude_none=False,
include_in_schema=True,
response_class=AirResponse,
name=None,
callbacks=None,
openapi_extra=None,
generate_unique_id_function=generate_unique_id,
)
```
Add a *path operation* using an HTTP DELETE operation.
Source code in `src/air/applications.py`
```
def delete(
self,
path: Annotated[
str,
Doc(
"""
The URL path to be used for this *path operation*.
For example, in `http://example.com/items`, the path is `/items`.
"""
),
],
*,
response_model: Annotated[
Any,
Doc(
"""
The type to use for the response.
It could be any valid Pydantic *field* type. So, it doesn't have to
be a Pydantic model, it could be other things, like a `list`, `dict`,
etc.
It will be used for:
* Documentation: the generated OpenAPI (and the UI at `/docs`) will
show it as the response (JSON Schema).
* Serialization: you could return an arbitrary object and the
`response_model` would be used to serialize that object into the
corresponding JSON.
* Filtering: the JSON sent to the client will only contain the data
(fields) defined in the `response_model`. If you returned an object
that contains an attribute `password` but the `response_model` does
not include that field, the JSON sent to the client would not have
that `password`.
* Validation: whatever you return will be serialized with the
`response_model`, converting any data as necessary to generate the
corresponding JSON. But if the data in the object returned is not
valid, that would mean a violation of the contract with the client,
so it's an error from the API developer. So, FastAPI will raise an
error and return a 500 error code (Internal Server Error).
Read more about it in the
[FastAPI docs for Response Model](https://fastapi.tiangolo.com/tutorial/response-model/).
"""
),
] = None,
status_code: Annotated[
int | None,
Doc(
"""
The default status code to be used for the response.
You could override the status code by returning a response directly.
Read more about it in the
[FastAPI docs for Response Status Code](https://fastapi.tiangolo.com/tutorial/response-status-code/).
"""
),
] = None,
tags: Annotated[
list[str | Enum] | None,
Doc(
"""
A list of tags to be applied to the *path operation*.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/#tags).
"""
),
] = None,
dependencies: Annotated[
Sequence[Depends] | None,
Doc(
"""
A list of dependencies (using `Depends()`) to be applied to the
*path operation*.
Read more about it in the
[FastAPI docs for Dependencies in path operation decorators](https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-in-path-operation-decorators/).
"""
),
] = None,
summary: Annotated[
str | None,
Doc(
"""
A summary for the *path operation*.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/).
"""
),
] = None,
description: Annotated[
str | None,
Doc(
"""
A description for the *path operation*.
If not provided, it will be extracted automatically from the docstring
of the *path operation function*.
It can contain Markdown.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/).
"""
),
] = None,
response_description: Annotated[
str,
Doc(
"""
The description for the default response.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
"""
),
] = "Successful Response",
responses: Annotated[
dict[int | str, dict[str, Any]] | None,
Doc(
"""
Additional responses that could be returned by this *path operation*.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
"""
),
] = None,
deprecated: Annotated[
bool | None,
Doc(
"""
Mark this *path operation* as deprecated.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
"""
),
] = None,
operation_id: Annotated[
str | None,
Doc(
"""
Custom operation ID to be used by this *path operation*.
By default, it is generated automatically.
If you provide a custom operation ID, you need to make sure it is
unique for the whole API.
You can customize the
operation ID generation with the parameter
`generate_unique_id_function` in the `FastAPI` class.
Read more about it in the
[FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function).
"""
),
] = None,
response_model_include: Annotated[
IncEx | None,
Doc(
"""
Configuration passed to Pydantic to include only certain fields in the
response data.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
"""
),
] = None,
response_model_exclude: Annotated[
IncEx | None,
Doc(
"""
Configuration passed to Pydantic to exclude certain fields in the
response data.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
"""
),
] = None,
response_model_by_alias: Annotated[
bool,
Doc(
"""
Configuration passed to Pydantic to define if the response model
should be serialized by alias when an alias is used.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
"""
),
] = True,
response_model_exclude_unset: Annotated[
bool,
Doc(
"""
Configuration passed to Pydantic to define if the response data
should have all the fields, including the ones that were not set and
have their default values. This is different from
`response_model_exclude_defaults` in that if the fields are set,
they will be included in the response, even if the value is the same
as the default.
When `True`, default values are omitted from the response.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter).
"""
),
] = False,
response_model_exclude_defaults: Annotated[
bool,
Doc(
"""
Configuration passed to Pydantic to define if the response data
should have all the fields, including the ones that have the same value
as the default. This is different from `response_model_exclude_unset`
in that if the fields are set but contain the same default values,
they will be excluded from the response.
When `True`, default values are omitted from the response.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter).
"""
),
] = False,
response_model_exclude_none: Annotated[
bool,
Doc(
"""
Configuration passed to Pydantic to define if the response data should
exclude fields set to `None`.
This is much simpler (less smart) than `response_model_exclude_unset`
and `response_model_exclude_defaults`. You probably want to use one of
those two instead of this one, as those allow returning `None` values
when it makes sense.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_exclude_none).
"""
),
] = False,
include_in_schema: Annotated[
bool,
Doc(
"""
Include this *path operation* in the generated OpenAPI schema.
This affects the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-parameters-from-openapi).
"""
),
] = True,
response_class: Annotated[
type[Response],
Doc(
"""
Response class to be used for this *path operation*.
This will not be used if you return a response directly.
Read more about it in the
[FastAPI docs for Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/#redirectresponse).
"""
),
] = AirResponse,
name: Annotated[
str | None,
Doc(
"""
Name for this *path operation*. Only used internally.
"""
),
] = None,
callbacks: Annotated[
list[BaseRoute] | None,
Doc(
"""
List of *path operations* that will be used as OpenAPI callbacks.
This is only for OpenAPI documentation, the callbacks won't be used
directly.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for OpenAPI Callbacks](https://fastapi.tiangolo.com/advanced/openapi-callbacks/).
"""
),
] = None,
openapi_extra: Annotated[
dict[str, Any] | None,
Doc(
"""
Extra metadata to be included in the OpenAPI schema for this *path
operation*.
Read more about it in the
[FastAPI docs for Path Operation Advanced Configuration](https://fastapi.tiangolo.com/advanced/path-operation-advanced-configuration/#custom-openapi-path-operation-schema).
"""
),
] = None,
generate_unique_id_function: Annotated[
Callable[[routing.APIRoute], str],
Doc(
"""
Customize the function used to generate unique IDs for the *path
operations* shown in the generated OpenAPI.
This is particularly useful when automatically generating clients or
SDKs for your API.
Read more about it in the
[FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function).
"""
),
] = generate_unique_id,
) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
"""
Add a *path operation* using an HTTP DELETE operation.
"""
def decorator[**P, R](func: Callable[P, MaybeAwaitable[R]]) -> RouteCallable:
@wraps(func)
async def endpoint(*args: P.args, **kw: P.kwargs) -> Response:
result = func(*args, **kw)
if inspect.isawaitable(result):
result = await result
if isinstance(result, Response):
return result
return response_class(result)
decorated = super(Air, self).delete(
path,
response_model=response_model,
status_code=status_code,
tags=tags,
dependencies=dependencies,
summary=summary,
description=description,
response_description=response_description,
responses=responses,
deprecated=deprecated,
operation_id=operation_id,
response_model_include=response_model_include,
response_model_exclude=response_model_exclude,
response_model_by_alias=response_model_by_alias,
response_model_exclude_unset=response_model_exclude_unset,
response_model_exclude_defaults=response_model_exclude_defaults,
response_model_exclude_none=response_model_exclude_none,
include_in_schema=include_in_schema,
response_class=response_class,
name=name,
callbacks=callbacks,
openapi_extra=openapi_extra,
generate_unique_id_function=generate_unique_id_function,
)(endpoint)
decorated.url = self._url_helper(name or endpoint.__name__)
return decorated
return decorator
```
### get
```
get(
path,
*,
response_model=None,
status_code=None,
tags=None,
dependencies=None,
summary=None,
description=None,
response_description="Successful Response",
responses=None,
deprecated=None,
operation_id=None,
response_model_include=None,
response_model_exclude=None,
response_model_by_alias=True,
response_model_exclude_unset=False,
response_model_exclude_defaults=False,
response_model_exclude_none=False,
include_in_schema=True,
response_class=AirResponse,
name=None,
callbacks=None,
openapi_extra=None,
generate_unique_id_function=generate_unique_id,
)
```
Add a *path operation* using an HTTP GET operation.
Example:
```
import air
app = air.Air()
@app.get("/hello")
def hello_world():
# Simple GET endpoint returning HTML.
return air.H1("Hello, World!")
@app.get("/users/{user_id}")
def get_user(user_id: int):
# GET endpoint with path parameter.
return air.Div(
air.H2(f"User ID: {user_id}"),
air.P("This is a user profile page"),
)
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
```
Source code in `src/air/applications.py`
```
def get(
self,
path: Annotated[
str,
Doc(
"""
The URL path to be used for this *path operation*.
For example, in `http://example.com/items`, the path is `/items`.
"""
),
],
*,
response_model: Annotated[
Any,
Doc(
"""
The type to use for the response.
It could be any valid Pydantic *field* type. So, it doesn't have to
be a Pydantic model, it could be other things, like a `list`, `dict`,
etc.
It will be used for:
* Documentation: the generated OpenAPI (and the UI at `/docs`) will
show it as the response (JSON Schema).
* Serialization: you could return an arbitrary object and the
`response_model` would be used to serialize that object into the
corresponding JSON.
* Filtering: the JSON sent to the client will only contain the data
(fields) defined in the `response_model`. If you returned an object
that contains an attribute `password` but the `response_model` does
not include that field, the JSON sent to the client would not have
that `password`.
* Validation: whatever you return will be serialized with the
`response_model`, converting any data as necessary to generate the
corresponding JSON. But if the data in the object returned is not
valid, that would mean a violation of the contract with the client,
so it's an error from the API developer. So, FastAPI will raise an
error and return a 500 error code (Internal Server Error).
Read more about it in the
[FastAPI docs for Response Model](https://fastapi.tiangolo.com/tutorial/response-model/).
"""
),
] = None,
status_code: Annotated[
int | None,
Doc(
"""
The default status code to be used for the response.
You could override the status code by returning a response directly.
Read more about it in the
[FastAPI docs for Response Status Code](https://fastapi.tiangolo.com/tutorial/response-status-code/).
"""
),
] = None,
tags: Annotated[
list[str | Enum] | None,
Doc(
"""
A list of tags to be applied to the *path operation*.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/#tags).
"""
),
] = None,
dependencies: Annotated[
Sequence[Depends] | None,
Doc(
"""
A list of dependencies (using `Depends()`) to be applied to the
*path operation*.
Read more about it in the
[FastAPI docs for Dependencies in path operation decorators](https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-in-path-operation-decorators/).
"""
),
] = None,
summary: Annotated[
str | None,
Doc(
"""
A summary for the *path operation*.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/).
"""
),
] = None,
description: Annotated[
str | None,
Doc(
"""
A description for the *path operation*.
If not provided, it will be extracted automatically from the docstring
of the *path operation function*.
It can contain Markdown.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/).
"""
),
] = None,
response_description: Annotated[
str,
Doc(
"""
The description for the default response.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
"""
),
] = "Successful Response",
responses: Annotated[
dict[int | str, dict[str, Any]] | None,
Doc(
"""
Additional responses that could be returned by this *path operation*.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
"""
),
] = None,
deprecated: Annotated[
bool | None,
Doc(
"""
Mark this *path operation* as deprecated.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
"""
),
] = None,
operation_id: Annotated[
str | None,
Doc(
"""
Custom operation ID to be used by this *path operation*.
By default, it is generated automatically.
If you provide a custom operation ID, you need to make sure it is
unique for the whole API.
You can customize the
operation ID generation with the parameter
`generate_unique_id_function` in the `FastAPI` class.
Read more about it in the
[FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function).
"""
),
] = None,
response_model_include: Annotated[
IncEx | None,
Doc(
"""
Configuration passed to Pydantic to include only certain fields in the
response data.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
"""
),
] = None,
response_model_exclude: Annotated[
IncEx | None,
Doc(
"""
Configuration passed to Pydantic to exclude certain fields in the
response data.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
"""
),
] = None,
response_model_by_alias: Annotated[
bool,
Doc(
"""
Configuration passed to Pydantic to define if the response model
should be serialized by alias when an alias is used.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
"""
),
] = True,
response_model_exclude_unset: Annotated[
bool,
Doc(
"""
Configuration passed to Pydantic to define if the response data
should have all the fields, including the ones that were not set and
have their default values. This is different from
`response_model_exclude_defaults` in that if the fields are set,
they will be included in the response, even if the value is the same
as the default.
When `True`, default values are omitted from the response.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter).
"""
),
] = False,
response_model_exclude_defaults: Annotated[
bool,
Doc(
"""
Configuration passed to Pydantic to define if the response data
should have all the fields, including the ones that have the same value
as the default. This is different from `response_model_exclude_unset`
in that if the fields are set but contain the same default values,
they will be excluded from the response.
When `True`, default values are omitted from the response.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter).
"""
),
] = False,
response_model_exclude_none: Annotated[
bool,
Doc(
"""
Configuration passed to Pydantic to define if the response data should
exclude fields set to `None`.
This is much simpler (less smart) than `response_model_exclude_unset`
and `response_model_exclude_defaults`. You probably want to use one of
those two instead of this one, as those allow returning `None` values
when it makes sense.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_exclude_none).
"""
),
] = False,
include_in_schema: Annotated[
bool,
Doc(
"""
Include this *path operation* in the generated OpenAPI schema.
This affects the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-parameters-from-openapi).
"""
),
] = True,
response_class: Annotated[
type[Response],
Doc(
"""
Response class to be used for this *path operation*.
This will not be used if you return a response directly.
Read more about it in the
[FastAPI docs for Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/#redirectresponse).
"""
),
] = AirResponse,
name: Annotated[
str | None,
Doc(
"""
Name for this *path operation*. Only used internally.
"""
),
] = None,
callbacks: Annotated[
list[BaseRoute] | None,
Doc(
"""
List of *path operations* that will be used as OpenAPI callbacks.
This is only for OpenAPI documentation, the callbacks won't be used
directly.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for OpenAPI Callbacks](https://fastapi.tiangolo.com/advanced/openapi-callbacks/).
"""
),
] = None,
openapi_extra: Annotated[
dict[str, Any] | None,
Doc(
"""
Extra metadata to be included in the OpenAPI schema for this *path
operation*.
Read more about it in the
[FastAPI docs for Path Operation Advanced Configuration](https://fastapi.tiangolo.com/advanced/path-operation-advanced-configuration/#custom-openapi-path-operation-schema).
"""
),
] = None,
generate_unique_id_function: Annotated[
Callable[[routing.APIRoute], str],
Doc(
"""
Customize the function used to generate unique IDs for the *path
operations* shown in the generated OpenAPI.
This is particularly useful when automatically generating clients or
SDKs for your API.
Read more about it in the
[FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function).
"""
),
] = generate_unique_id,
) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
"""Add a *path operation* using an HTTP GET operation.
Example:
import air
app = air.Air()
@app.get("/hello")
def hello_world():
# Simple GET endpoint returning HTML.
return air.H1("Hello, World!")
@app.get("/users/{user_id}")
def get_user(user_id: int):
# GET endpoint with path parameter.
return air.Div(
air.H2(f"User ID: {user_id}"),
air.P("This is a user profile page"),
)
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
"""
def decorator[**P, R](func: Callable[P, MaybeAwaitable[R]]) -> RouteCallable:
@wraps(func)
async def endpoint(*args: P.args, **kw: P.kwargs) -> Response:
result = func(*args, **kw)
if inspect.isawaitable(result):
result = await result
if isinstance(result, Response):
return result
# Force HTML for non-Response results
return response_class(result)
decorated = super(Air, self).get(
path,
response_model=response_model,
status_code=status_code,
tags=tags,
dependencies=dependencies,
summary=summary,
description=description,
response_description=response_description,
responses=responses,
deprecated=deprecated,
operation_id=operation_id,
response_model_include=response_model_include,
response_model_exclude=response_model_exclude,
response_model_by_alias=response_model_by_alias,
response_model_exclude_unset=response_model_exclude_unset,
response_model_exclude_defaults=response_model_exclude_defaults,
response_model_exclude_none=response_model_exclude_none,
include_in_schema=include_in_schema,
response_class=response_class,
name=name,
callbacks=callbacks,
openapi_extra=openapi_extra,
generate_unique_id_function=generate_unique_id_function,
)(endpoint)
decorated.url = self._url_helper(name or endpoint.__name__)
return decorated
return decorator
```
### patch
```
patch(
path,
*,
response_model=None,
status_code=None,
tags=None,
dependencies=None,
summary=None,
description=None,
response_description="Successful Response",
responses=None,
deprecated=None,
operation_id=None,
response_model_include=None,
response_model_exclude=None,
response_model_by_alias=True,
response_model_exclude_unset=False,
response_model_exclude_defaults=False,
response_model_exclude_none=False,
include_in_schema=True,
response_class=AirResponse,
name=None,
callbacks=None,
openapi_extra=None,
generate_unique_id_function=generate_unique_id,
)
```
Add a *path operation* using an HTTP PATCH operation.
Source code in `src/air/applications.py`
```
def patch(
self,
path: Annotated[
str,
Doc(
"""
The URL path to be used for this *path operation*.
For example, in `http://example.com/items`, the path is `/items`.
"""
),
],
*,
response_model: Annotated[
Any,
Doc(
"""
The type to use for the response.
It could be any valid Pydantic *field* type. So, it doesn't have to
be a Pydantic model, it could be other things, like a `list`, `dict`,
etc.
It will be used for:
* Documentation: the generated OpenAPI (and the UI at `/docs`) will
show it as the response (JSON Schema).
* Serialization: you could return an arbitrary object and the
`response_model` would be used to serialize that object into the
corresponding JSON.
* Filtering: the JSON sent to the client will only contain the data
(fields) defined in the `response_model`. If you returned an object
that contains an attribute `password` but the `response_model` does
not include that field, the JSON sent to the client would not have
that `password`.
* Validation: whatever you return will be serialized with the
`response_model`, converting any data as necessary to generate the
corresponding JSON. But if the data in the object returned is not
valid, that would mean a violation of the contract with the client,
so it's an error from the API developer. So, FastAPI will raise an
error and return a 500 error code (Internal Server Error).
Read more about it in the
[FastAPI docs for Response Model](https://fastapi.tiangolo.com/tutorial/response-model/).
"""
),
] = None,
status_code: Annotated[
int | None,
Doc(
"""
The default status code to be used for the response.
You could override the status code by returning a response directly.
Read more about it in the
[FastAPI docs for Response Status Code](https://fastapi.tiangolo.com/tutorial/response-status-code/).
"""
),
] = None,
tags: Annotated[
list[str | Enum] | None,
Doc(
"""
A list of tags to be applied to the *path operation*.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/#tags).
"""
),
] = None,
dependencies: Annotated[
Sequence[Depends] | None,
Doc(
"""
A list of dependencies (using `Depends()`) to be applied to the
*path operation*.
Read more about it in the
[FastAPI docs for Dependencies in path operation decorators](https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-in-path-operation-decorators/).
"""
),
] = None,
summary: Annotated[
str | None,
Doc(
"""
A summary for the *path operation*.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/).
"""
),
] = None,
description: Annotated[
str | None,
Doc(
"""
A description for the *path operation*.
If not provided, it will be extracted automatically from the docstring
of the *path operation function*.
It can contain Markdown.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/).
"""
),
] = None,
response_description: Annotated[
str,
Doc(
"""
The description for the default response.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
"""
),
] = "Successful Response",
responses: Annotated[
dict[int | str, dict[str, Any]] | None,
Doc(
"""
Additional responses that could be returned by this *path operation*.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
"""
),
] = None,
deprecated: Annotated[
bool | None,
Doc(
"""
Mark this *path operation* as deprecated.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
"""
),
] = None,
operation_id: Annotated[
str | None,
Doc(
"""
Custom operation ID to be used by this *path operation*.
By default, it is generated automatically.
If you provide a custom operation ID, you need to make sure it is
unique for the whole API.
You can customize the
operation ID generation with the parameter
`generate_unique_id_function` in the `FastAPI` class.
Read more about it in the
[FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function).
"""
),
] = None,
response_model_include: Annotated[
IncEx | None,
Doc(
"""
Configuration passed to Pydantic to include only certain fields in the
response data.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
"""
),
] = None,
response_model_exclude: Annotated[
IncEx | None,
Doc(
"""
Configuration passed to Pydantic to exclude certain fields in the
response data.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
"""
),
] = None,
response_model_by_alias: Annotated[
bool,
Doc(
"""
Configuration passed to Pydantic to define if the response model
should be serialized by alias when an alias is used.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
"""
),
] = True,
response_model_exclude_unset: Annotated[
bool,
Doc(
"""
Configuration passed to Pydantic to define if the response data
should have all the fields, including the ones that were not set and
have their default values. This is different from
`response_model_exclude_defaults` in that if the fields are set,
they will be included in the response, even if the value is the same
as the default.
When `True`, default values are omitted from the response.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter).
"""
),
] = False,
response_model_exclude_defaults: Annotated[
bool,
Doc(
"""
Configuration passed to Pydantic to define if the response data
should have all the fields, including the ones that have the same value
as the default. This is different from `response_model_exclude_unset`
in that if the fields are set but contain the same default values,
they will be excluded from the response.
When `True`, default values are omitted from the response.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter).
"""
),
] = False,
response_model_exclude_none: Annotated[
bool,
Doc(
"""
Configuration passed to Pydantic to define if the response data should
exclude fields set to `None`.
This is much simpler (less smart) than `response_model_exclude_unset`
and `response_model_exclude_defaults`. You probably want to use one of
those two instead of this one, as those allow returning `None` values
when it makes sense.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_exclude_none).
"""
),
] = False,
include_in_schema: Annotated[
bool,
Doc(
"""
Include this *path operation* in the generated OpenAPI schema.
This affects the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-parameters-from-openapi).
"""
),
] = True,
response_class: Annotated[
type[Response],
Doc(
"""
Response class to be used for this *path operation*.
This will not be used if you return a response directly.
Read more about it in the
[FastAPI docs for Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/#redirectresponse).
"""
),
] = AirResponse,
name: Annotated[
str | None,
Doc(
"""
Name for this *path operation*. Only used internally.
"""
),
] = None,
callbacks: Annotated[
list[BaseRoute] | None,
Doc(
"""
List of *path operations* that will be used as OpenAPI callbacks.
This is only for OpenAPI documentation, the callbacks won't be used
directly.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for OpenAPI Callbacks](https://fastapi.tiangolo.com/advanced/openapi-callbacks/).
"""
),
] = None,
openapi_extra: Annotated[
dict[str, Any] | None,
Doc(
"""
Extra metadata to be included in the OpenAPI schema for this *path
operation*.
Read more about it in the
[FastAPI docs for Path Operation Advanced Configuration](https://fastapi.tiangolo.com/advanced/path-operation-advanced-configuration/#custom-openapi-path-operation-schema).
"""
),
] = None,
generate_unique_id_function: Annotated[
Callable[[routing.APIRoute], str],
Doc(
"""
Customize the function used to generate unique IDs for the *path
operations* shown in the generated OpenAPI.
This is particularly useful when automatically generating clients or
SDKs for your API.
Read more about it in the
[FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function).
"""
),
] = generate_unique_id,
) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
"""
Add a *path operation* using an HTTP PATCH operation.
"""
def decorator[**P, R](func: Callable[P, MaybeAwaitable[R]]) -> RouteCallable:
@wraps(func)
async def endpoint(*args: P.args, **kw: P.kwargs) -> Response:
result = func(*args, **kw)
if inspect.isawaitable(result):
result = await result
if isinstance(result, Response):
return result
return response_class(result)
decorated = super(Air, self).patch(
path,
response_model=response_model,
status_code=status_code,
tags=tags,
dependencies=dependencies,
summary=summary,
description=description,
response_description=response_description,
responses=responses,
deprecated=deprecated,
operation_id=operation_id,
response_model_include=response_model_include,
response_model_exclude=response_model_exclude,
response_model_by_alias=response_model_by_alias,
response_model_exclude_unset=response_model_exclude_unset,
response_model_exclude_defaults=response_model_exclude_defaults,
response_model_exclude_none=response_model_exclude_none,
include_in_schema=include_in_schema,
response_class=response_class,
name=name,
callbacks=callbacks,
openapi_extra=openapi_extra,
generate_unique_id_function=generate_unique_id_function,
)(endpoint)
decorated.url = self._url_helper(name or endpoint.__name__)
return decorated
return decorator
```
### post
```
post(
path,
*,
response_model=None,
status_code=None,
tags=None,
dependencies=None,
summary=None,
description=None,
response_description="Successful Response",
responses=None,
deprecated=None,
operation_id=None,
response_model_include=None,
response_model_exclude=None,
response_model_by_alias=True,
response_model_exclude_unset=False,
response_model_exclude_defaults=False,
response_model_exclude_none=False,
include_in_schema=True,
response_class=AirResponse,
name=None,
callbacks=None,
openapi_extra=None,
generate_unique_id_function=generate_unique_id,
)
```
Add a *path operation* using an HTTP POST operation.
Example:
```
from pydantic import BaseModel
import air
class UserCreate(BaseModel):
# User creation model.
name: str
email: str
app = air.Air()
@app.post("/submit")
def submit_form():
# Simple POST endpoint.
return air.Div(
air.H2("Form Submitted!"),
air.P("Thank you for your submission"),
)
@app.post("/users")
def create_user(user: UserCreate):
# POST endpoint with request body.
return air.Div(
air.H2("User Created"),
air.P(f"Name: {user.name}"),
air.P(f"Email: {user.email}"),
)
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
```
Source code in `src/air/applications.py`
```
def post(
self,
path: Annotated[
str,
Doc(
"""
The URL path to be used for this *path operation*.
For example, in `http://example.com/items`, the path is `/items`.
"""
),
],
*,
response_model: Annotated[
Any,
Doc(
"""
The type to use for the response.
It could be any valid Pydantic *field* type. So, it doesn't have to
be a Pydantic model, it could be other things, like a `list`, `dict`,
etc.
It will be used for:
* Documentation: the generated OpenAPI (and the UI at `/docs`) will
show it as the response (JSON Schema).
* Serialization: you could return an arbitrary object and the
`response_model` would be used to serialize that object into the
corresponding JSON.
* Filtering: the JSON sent to the client will only contain the data
(fields) defined in the `response_model`. If you returned an object
that contains an attribute `password` but the `response_model` does
not include that field, the JSON sent to the client would not have
that `password`.
* Validation: whatever you return will be serialized with the
`response_model`, converting any data as necessary to generate the
corresponding JSON. But if the data in the object returned is not
valid, that would mean a violation of the contract with the client,
so it's an error from the API developer. So, FastAPI will raise an
error and return a 500 error code (Internal Server Error).
Read more about it in the
[FastAPI docs for Response Model](https://fastapi.tiangolo.com/tutorial/response-model/).
"""
),
] = None,
status_code: Annotated[
int | None,
Doc(
"""
The default status code to be used for the response.
You could override the status code by returning a response directly.
Read more about it in the
[FastAPI docs for Response Status Code](https://fastapi.tiangolo.com/tutorial/response-status-code/).
"""
),
] = None,
tags: Annotated[
list[str | Enum] | None,
Doc(
"""
A list of tags to be applied to the *path operation*.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/#tags).
"""
),
] = None,
dependencies: Annotated[
Sequence[Depends] | None,
Doc(
"""
A list of dependencies (using `Depends()`) to be applied to the
*path operation*.
Read more about it in the
[FastAPI docs for Dependencies in path operation decorators](https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-in-path-operation-decorators/).
"""
),
] = None,
summary: Annotated[
str | None,
Doc(
"""
A summary for the *path operation*.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/).
"""
),
] = None,
description: Annotated[
str | None,
Doc(
"""
A description for the *path operation*.
If not provided, it will be extracted automatically from the docstring
of the *path operation function*.
It can contain Markdown.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/).
"""
),
] = None,
response_description: Annotated[
str,
Doc(
"""
The description for the default response.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
"""
),
] = "Successful Response",
responses: Annotated[
dict[int | str, dict[str, Any]] | None,
Doc(
"""
Additional responses that could be returned by this *path operation*.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
"""
),
] = None,
deprecated: Annotated[
bool | None,
Doc(
"""
Mark this *path operation* as deprecated.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
"""
),
] = None,
operation_id: Annotated[
str | None,
Doc(
"""
Custom operation ID to be used by this *path operation*.
By default, it is generated automatically.
If you provide a custom operation ID, you need to make sure it is
unique for the whole API.
You can customize the
operation ID generation with the parameter
`generate_unique_id_function` in the `FastAPI` class.
Read more about it in the
[FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function).
"""
),
] = None,
response_model_include: Annotated[
IncEx | None,
Doc(
"""
Configuration passed to Pydantic to include only certain fields in the
response data.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
"""
),
] = None,
response_model_exclude: Annotated[
IncEx | None,
Doc(
"""
Configuration passed to Pydantic to exclude certain fields in the
response data.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
"""
),
] = None,
response_model_by_alias: Annotated[
bool,
Doc(
"""
Configuration passed to Pydantic to define if the response model
should be serialized by alias when an alias is used.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
"""
),
] = True,
response_model_exclude_unset: Annotated[
bool,
Doc(
"""
Configuration passed to Pydantic to define if the response data
should have all the fields, including the ones that were not set and
have their default values. This is different from
`response_model_exclude_defaults` in that if the fields are set,
they will be included in the response, even if the value is the same
as the default.
When `True`, default values are omitted from the response.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter).
"""
),
] = False,
response_model_exclude_defaults: Annotated[
bool,
Doc(
"""
Configuration passed to Pydantic to define if the response data
should have all the fields, including the ones that have the same value
as the default. This is different from `response_model_exclude_unset`
in that if the fields are set but contain the same default values,
they will be excluded from the response.
When `True`, default values are omitted from the response.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter).
"""
),
] = False,
response_model_exclude_none: Annotated[
bool,
Doc(
"""
Configuration passed to Pydantic to define if the response data should
exclude fields set to `None`.
This is much simpler (less smart) than `response_model_exclude_unset`
and `response_model_exclude_defaults`. You probably want to use one of
those two instead of this one, as those allow returning `None` values
when it makes sense.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_exclude_none).
"""
),
] = False,
include_in_schema: Annotated[
bool,
Doc(
"""
Include this *path operation* in the generated OpenAPI schema.
This affects the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-parameters-from-openapi).
"""
),
] = True,
response_class: Annotated[
type[Response],
Doc(
"""
Response class to be used for this *path operation*.
This will not be used if you return a response directly.
Read more about it in the
[FastAPI docs for Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/#redirectresponse).
"""
),
] = AirResponse,
name: Annotated[
str | None,
Doc(
"""
Name for this *path operation*. Only used internally.
"""
),
] = None,
callbacks: Annotated[
list[BaseRoute] | None,
Doc(
"""
List of *path operations* that will be used as OpenAPI callbacks.
This is only for OpenAPI documentation, the callbacks won't be used
directly.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for OpenAPI Callbacks](https://fastapi.tiangolo.com/advanced/openapi-callbacks/).
"""
),
] = None,
openapi_extra: Annotated[
dict[str, Any] | None,
Doc(
"""
Extra metadata to be included in the OpenAPI schema for this *path
operation*.
Read more about it in the
[FastAPI docs for Path Operation Advanced Configuration](https://fastapi.tiangolo.com/advanced/path-operation-advanced-configuration/#custom-openapi-path-operation-schema).
"""
),
] = None,
generate_unique_id_function: Annotated[
Callable[[routing.APIRoute], str],
Doc(
"""
Customize the function used to generate unique IDs for the *path
operations* shown in the generated OpenAPI.
This is particularly useful when automatically generating clients or
SDKs for your API.
Read more about it in the
[FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function).
"""
),
] = generate_unique_id,
) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
"""Add a *path operation* using an HTTP POST operation.
Example:
from pydantic import BaseModel
import air
class UserCreate(BaseModel):
# User creation model.
name: str
email: str
app = air.Air()
@app.post("/submit")
def submit_form():
# Simple POST endpoint.
return air.Div(
air.H2("Form Submitted!"),
air.P("Thank you for your submission"),
)
@app.post("/users")
def create_user(user: UserCreate):
# POST endpoint with request body.
return air.Div(
air.H2("User Created"),
air.P(f"Name: {user.name}"),
air.P(f"Email: {user.email}"),
)
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
"""
def decorator[**P, R](func: Callable[P, MaybeAwaitable[R]]) -> RouteCallable:
@wraps(func)
async def endpoint(*args: P.args, **kw: P.kwargs) -> Response:
result = func(*args, **kw)
if inspect.isawaitable(result):
result = await result
if isinstance(result, Response):
return result
# Force HTML for non-Response results
return response_class(result)
decorated = super(Air, self).post(
path,
response_model=response_model,
status_code=status_code,
tags=tags,
dependencies=dependencies,
summary=summary,
description=description,
response_description=response_description,
responses=responses,
deprecated=deprecated,
operation_id=operation_id,
response_model_include=response_model_include,
response_model_exclude=response_model_exclude,
response_model_by_alias=response_model_by_alias,
response_model_exclude_unset=response_model_exclude_unset,
response_model_exclude_defaults=response_model_exclude_defaults,
response_model_exclude_none=response_model_exclude_none,
include_in_schema=include_in_schema,
response_class=response_class,
name=name,
callbacks=callbacks,
openapi_extra=openapi_extra,
generate_unique_id_function=generate_unique_id_function,
)(endpoint)
decorated.url = self._url_helper(name or endpoint.__name__)
return decorated
return decorator
```
### put
```
put(
path,
*,
response_model=None,
status_code=None,
tags=None,
dependencies=None,
summary=None,
description=None,
response_description="Successful Response",
responses=None,
deprecated=None,
operation_id=None,
response_model_include=None,
response_model_exclude=None,
response_model_by_alias=True,
response_model_exclude_unset=False,
response_model_exclude_defaults=False,
response_model_exclude_none=False,
include_in_schema=True,
response_class=AirResponse,
name=None,
callbacks=None,
openapi_extra=None,
generate_unique_id_function=generate_unique_id,
)
```
Add a *path operation* using an HTTP PUT operation.
Source code in `src/air/applications.py`
```
def put(
self,
path: Annotated[
str,
Doc(
"""
The URL path to be used for this *path operation*.
For example, in `http://example.com/items`, the path is `/items`.
"""
),
],
*,
response_model: Annotated[
Any,
Doc(
"""
The type to use for the response.
It could be any valid Pydantic *field* type. So, it doesn't have to
be a Pydantic model, it could be other things, like a `list`, `dict`,
etc.
It will be used for:
* Documentation: the generated OpenAPI (and the UI at `/docs`) will
show it as the response (JSON Schema).
* Serialization: you could return an arbitrary object and the
`response_model` would be used to serialize that object into the
corresponding JSON.
* Filtering: the JSON sent to the client will only contain the data
(fields) defined in the `response_model`. If you returned an object
that contains an attribute `password` but the `response_model` does
not include that field, the JSON sent to the client would not have
that `password`.
* Validation: whatever you return will be serialized with the
`response_model`, converting any data as necessary to generate the
corresponding JSON. But if the data in the object returned is not
valid, that would mean a violation of the contract with the client,
so it's an error from the API developer. So, FastAPI will raise an
error and return a 500 error code (Internal Server Error).
Read more about it in the
[FastAPI docs for Response Model](https://fastapi.tiangolo.com/tutorial/response-model/).
"""
),
] = None,
status_code: Annotated[
int | None,
Doc(
"""
The default status code to be used for the response.
You could override the status code by returning a response directly.
Read more about it in the
[FastAPI docs for Response Status Code](https://fastapi.tiangolo.com/tutorial/response-status-code/).
"""
),
] = None,
tags: Annotated[
list[str | Enum] | None,
Doc(
"""
A list of tags to be applied to the *path operation*.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/#tags).
"""
),
] = None,
dependencies: Annotated[
Sequence[Depends] | None,
Doc(
"""
A list of dependencies (using `Depends()`) to be applied to the
*path operation*.
Read more about it in the
[FastAPI docs for Dependencies in path operation decorators](https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-in-path-operation-decorators/).
"""
),
] = None,
summary: Annotated[
str | None,
Doc(
"""
A summary for the *path operation*.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/).
"""
),
] = None,
description: Annotated[
str | None,
Doc(
"""
A description for the *path operation*.
If not provided, it will be extracted automatically from the docstring
of the *path operation function*.
It can contain Markdown.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/).
"""
),
] = None,
response_description: Annotated[
str,
Doc(
"""
The description for the default response.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
"""
),
] = "Successful Response",
responses: Annotated[
dict[int | str, dict[str, Any]] | None,
Doc(
"""
Additional responses that could be returned by this *path operation*.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
"""
),
] = None,
deprecated: Annotated[
bool | None,
Doc(
"""
Mark this *path operation* as deprecated.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
"""
),
] = None,
operation_id: Annotated[
str | None,
Doc(
"""
Custom operation ID to be used by this *path operation*.
By default, it is generated automatically.
If you provide a custom operation ID, you need to make sure it is
unique for the whole API.
You can customize the
operation ID generation with the parameter
`generate_unique_id_function` in the `FastAPI` class.
Read more about it in the
[FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function).
"""
),
] = None,
response_model_include: Annotated[
IncEx | None,
Doc(
"""
Configuration passed to Pydantic to include only certain fields in the
response data.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
"""
),
] = None,
response_model_exclude: Annotated[
IncEx | None,
Doc(
"""
Configuration passed to Pydantic to exclude certain fields in the
response data.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
"""
),
] = None,
response_model_by_alias: Annotated[
bool,
Doc(
"""
Configuration passed to Pydantic to define if the response model
should be serialized by alias when an alias is used.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
"""
),
] = True,
response_model_exclude_unset: Annotated[
bool,
Doc(
"""
Configuration passed to Pydantic to define if the response data
should have all the fields, including the ones that were not set and
have their default values. This is different from
`response_model_exclude_defaults` in that if the fields are set,
they will be included in the response, even if the value is the same
as the default.
When `True`, default values are omitted from the response.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter).
"""
),
] = False,
response_model_exclude_defaults: Annotated[
bool,
Doc(
"""
Configuration passed to Pydantic to define if the response data
should have all the fields, including the ones that have the same value
as the default. This is different from `response_model_exclude_unset`
in that if the fields are set but contain the same default values,
they will be excluded from the response.
When `True`, default values are omitted from the response.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter).
"""
),
] = False,
response_model_exclude_none: Annotated[
bool,
Doc(
"""
Configuration passed to Pydantic to define if the response data should
exclude fields set to `None`.
This is much simpler (less smart) than `response_model_exclude_unset`
and `response_model_exclude_defaults`. You probably want to use one of
those two instead of this one, as those allow returning `None` values
when it makes sense.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_exclude_none).
"""
),
] = False,
include_in_schema: Annotated[
bool,
Doc(
"""
Include this *path operation* in the generated OpenAPI schema.
This affects the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-parameters-from-openapi).
"""
),
] = True,
response_class: Annotated[
type[Response],
Doc(
"""
Response class to be used for this *path operation*.
This will not be used if you return a response directly.
Read more about it in the
[FastAPI docs for Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/#redirectresponse).
"""
),
] = AirResponse,
name: Annotated[
str | None,
Doc(
"""
Name for this *path operation*. Only used internally.
"""
),
] = None,
callbacks: Annotated[
list[BaseRoute] | None,
Doc(
"""
List of *path operations* that will be used as OpenAPI callbacks.
This is only for OpenAPI documentation, the callbacks won't be used
directly.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for OpenAPI Callbacks](https://fastapi.tiangolo.com/advanced/openapi-callbacks/).
"""
),
] = None,
openapi_extra: Annotated[
dict[str, Any] | None,
Doc(
"""
Extra metadata to be included in the OpenAPI schema for this *path
operation*.
Read more about it in the
[FastAPI docs for Path Operation Advanced Configuration](https://fastapi.tiangolo.com/advanced/path-operation-advanced-configuration/#custom-openapi-path-operation-schema).
"""
),
] = None,
generate_unique_id_function: Annotated[
Callable[[routing.APIRoute], str],
Doc(
"""
Customize the function used to generate unique IDs for the *path
operations* shown in the generated OpenAPI.
This is particularly useful when automatically generating clients or
SDKs for your API.
Read more about it in the
[FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function).
"""
),
] = generate_unique_id,
) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
"""
Add a *path operation* using an HTTP PUT operation.
"""
def decorator[**P, R](func: Callable[P, MaybeAwaitable[R]]) -> RouteCallable:
@wraps(func)
async def endpoint(*args: P.args, **kw: P.kwargs) -> Response:
result = func(*args, **kw)
if inspect.isawaitable(result):
result = await result
if isinstance(result, Response):
return result
return response_class(result)
decorated = super(Air, self).put(
path,
response_model=response_model,
status_code=status_code,
tags=tags,
dependencies=dependencies,
summary=summary,
description=description,
response_description=response_description,
responses=responses,
deprecated=deprecated,
operation_id=operation_id,
response_model_include=response_model_include,
response_model_exclude=response_model_exclude,
response_model_by_alias=response_model_by_alias,
response_model_exclude_unset=response_model_exclude_unset,
response_model_exclude_defaults=response_model_exclude_defaults,
response_model_exclude_none=response_model_exclude_none,
include_in_schema=include_in_schema,
response_class=response_class,
name=name,
callbacks=callbacks,
openapi_extra=openapi_extra,
generate_unique_id_function=generate_unique_id_function,
)(endpoint)
decorated.url = self._url_helper(name or endpoint.__name__)
return decorated
return decorator
```
# Background Tasks
Background Tasks are Python functions that are called after a response is returned to the user. They are useful for long running tasks like emails or batch processing.
Background tasks in Air, for those times a long-running process is needed that doesn't force the user to wait.
## BackgroundTasks
Bases: `BackgroundTasks`
A collection of background tasks that will be called after a response has been sent to the client.
Example:
```
import pathlib
import air
app = air.Air()
def write_notification(email: str, message=""):
with pathlib.Path("log.txt").open(mode="w") as email_file:
content = f"notification for {email}: {message}"
email_file.write(content)
@app.post("/send-notification/{email}")
def send_notification(email: str, background_tasks: air.BackgroundTasks):
message = "some notification"
background_tasks.add_task(write_notification, email, message=message)
return air.P(f"Notification sent to {email}")
```
### add_task
```
add_task(func, *args, **kwargs)
```
Add a function to be called in the background after the response is sent.
Source code in `src/air/background.py`
```
def add_task[**P, T](
self,
func: Annotated[
Callable[P, T],
Doc(
"""
The function to call after the response is sent.
It can be a regular `def` function or an `async def` function.
"""
),
],
*args: P.args,
**kwargs: P.kwargs,
) -> None:
"""Add a function to be called in the background after the response is sent."""
return super().add_task(func, *args, **kwargs)
```
# Dependencies
Air provides dependency injection utilities to help you build responsive web applications.
## HTMX Request Detection
The `is_htmx_request` dependency allows you to detect whether a request is coming from an HTMX action, enabling you to return different responses for HTMX vs regular HTTP requests.
### Common Use Cases
- Return partial HTML fragments for HTMX requests and full pages for regular requests
- Provide different response formats (JSON for HTMX, redirects for forms)
- Implement progressive enhancement patterns
### Examples
#### Basic Usage
```
import air
from fastapi import Depends
app = air.App()
@app.get("/users")
def get_users(is_htmx: bool = Depends(air.is_htmx_request)):
users = ["Alice", "Bob", "Charlie"]
if is_htmx:
# Return just the user list for HTMX partial updates
return air.Ul([air.Li(user) for user in users])
else:
# Return full page for regular requests
return air.Html([
air.Head(air.Title("Users")),
air.Body([
air.H1("User List"),
air.Ul([air.Li(user) for user in users])
])
])
```
#### Form Handling
```
@app.post("/submit")
def submit_form(is_htmx: bool = Depends(air.is_htmx_request)):
if is_htmx:
return air.Div("Success!", class_="alert-success")
else:
return RedirectResponse("/success", status_code=303)
```
Tools for handling dependencies, for things like handling incoming data from client libraries like HTMX.
## is_htmx_request
```
is_htmx_request = Depends(_is_htmx_request)
```
# Exceptions
## default_404_exception_handler
```
default_404_exception_handler(request, exc)
```
Default 404 exception handler. Can be overloaded.
Example:
```
import air
app = air.Air()
@app.get("/")
def index():
return air.AirResponse(air.P("404 Not Found"), status_code=404)
```
Source code in `src/air/exception_handlers.py`
```
def default_404_exception_handler(request: Request, exc: Exception) -> AirResponse:
"""Default 404 exception handler. Can be overloaded.
Example:
import air
app = air.Air()
@app.get("/")
def index():
return air.AirResponse(air.P("404 Not Found"), status_code=404)
"""
return AirResponse(
mvpcss(
Title("404 Not Found"),
H1("404 Not Found"),
P("The requested resource was not found on this server."),
P(f"URL: {request.url}"),
),
status_code=404,
)
```
## default_404_router_handler
```
default_404_router_handler(router_name)
```
Build an ASGI app that delegates the 404 to the default_404_exception_handler.
Example:
```
import air
app = air.Air()
router = air.AirRouter()
@router.get("/example")
def index():
return air.AirResponse(air.P("I am an example route."), status_code=404)
app.include_router(router)
```
Source code in `src/air/exception_handlers.py`
```
def default_404_router_handler(router_name: str) -> ASGIApp:
"""Build an ASGI app that delegates the 404 to the default_404_exception_handler.
Example:
import air
app = air.Air()
router = air.AirRouter()
@router.get("/example")
def index():
return air.AirResponse(air.P("I am an example route."), status_code=404)
app.include_router(router)
"""
async def app(scope: Scope, receive: Receive, send: Send) -> None:
# Create a Request so router handler can render URL and other context.
request: Request = Request(scope, receive=receive)
# Use a concrete HTTPException (status 404). Your handler accepts Exception.
exc: HTTPException = HTTPException(
status_code=404,
detail=f"Not Found in router '{router_name}'",
)
# Delegate to the project's default 404 renderer (AirResponse).
response = default_404_exception_handler(request, exc)
# AirResponse is a Starlette-compatible Response, so call it as ASGI.
await response(scope, receive, send)
return app
```
## default_500_exception_handler
```
default_500_exception_handler(request, exc)
```
Default 500 exception handler. Can be overloaded.
Example:
```
import air
app = air.Air()
@app.get("/")
def index():
return air.AirResponse(air.P("500 Internal Server Error"), status_code=500)
```
Source code in `src/air/exception_handlers.py`
```
def default_500_exception_handler(request: Request, exc: Exception) -> AirResponse:
"""Default 500 exception handler. Can be overloaded.
Example:
import air
app = air.Air()
@app.get("/")
def index():
return air.AirResponse(air.P("500 Internal Server Error"), status_code=500)
"""
return AirResponse(
mvpcss(
Title("500 Internal Server Error"),
H1("500 Internal Server Error"),
P("An internal server error occurred."),
),
status_code=500,
)
```
# Exceptions
## BaseAirException
Bases: `Exception`
Base AIR Exception
## BrowserOpenError
Bases: `RuntimeError`
Opening the browser failed.
## HTTPException
Bases: `HTTPException`
Convenience import from FastAPI
## ObjectDoesNotExist
Bases: `HTTPException`
Thrown when a record in a persistence store can't be found.
## RenderException
Bases: `BaseAirException`
Error thrown when a render function fails.
# Forms
Display and Validation of HTML forms. Powered by pydantic.
Pro-tip: Always validate incoming data.
## AirForm
```
AirForm(initial_data=None)
```
A form handler that validates incoming form data against a Pydantic model. Can be used with awaited form data or with FastAPI's dependency injection system.
Example:
```
from typing import Annotated
from fastapi import Depends
from pydantic import BaseModel
import air
app = air.Air()
class FlightModel(BaseModel):
flight_number: str
destination: str
class FlightForm(air.AirForm):
model = FlightModel
@app.post("/flight")
async def flight_form(request: air.Request):
"Awaited form data"
flight = await FlightForm.from_request(request)
if flight.is_valid:
return air.Html(air.H1(flight.data.flight_number))
errors = len(flight.errors or [])
return air.Html(air.H1(air.Raw(str(errors))))
@app.post("/flight-depends")
async def flight_form_depends(flight: Annotated[FlightForm, Depends(FlightForm.from_request)]):
"Dependency injection"
if flight.is_valid:
return air.Html(air.H1(flight.data.flight_number))
errors = len(flight.errors or [])
return air.Html(air.H1(air.Raw(str(errors))))
```
Source code in `src/air/forms.py`
```
def __init__(self, initial_data: dict | None = None) -> None:
if self.model is None:
msg = "model"
raise NotImplementedError(msg)
self.initial_data = initial_data
```
### widget
```
widget
```
Widget for rendering of form in HTML
If you want a custom widget, replace with a function that accepts:
```
- model: BaseModel
- data: dict|None
- errors:dict|None=None
```
Example:
```
import air
from air.forms import default_form_widget
app = air.Air()
class ContactModel(air.AirModel):
# Note: This uses `str` for email. For stricter server-side validation,
# you can use `EmailStr` from pydantic.
name: str
email: str
message: str
def contact_widget(*, model, data, errors, includes):
base_html = default_form_widget(
model=model,
data=data,
errors=errors,
includes=includes,
)
return air.Div(
air.P("Custom widget wrapper"),
air.Raw(base_html),
class_="contact-form",
)
def get_contact_form() -> air.AirForm:
return ContactModel.to_form(widget=contact_widget)
@app.page
def contact(request: air.Request):
form = get_contact_form()
return air.layouts.mvpcss(
air.H1("Contact Us"),
air.P("This example uses a custom AirForm.widget to wrap the default form HTML."),
air.Form(
form.render(),
air.Button("Send message", type="submit"),
method="post",
action="/contact",
),
)
@app.post("/contact")
async def submit_contact(request: air.Request):
form = get_contact_form()
form_data = await request.form()
if form.validate(form_data):
return air.Html(
air.H1("Thank you for your message!"),
air.P("Your contact form was submitted successfully."),
)
error_count = len(form.errors or [])
return air.Html(
air.H1("Please fix the errors below."),
air.P(f"Found {error_count} validation error(s)."),
air.Form(
form.render(),
air.Button("Send message", type="submit"),
method="post",
action="/contact",
),
)
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="127.0.0.1", port=8000)
```
### from_request
```
from_request(request)
```
Create and validate an AirForm instance from a request.
Parameters:
| Name | Type | Description | Default |
| --------- | --------- | ----------------------------------------- | ---------- |
| `request` | `Request` | The incoming request containing form data | *required* |
Returns:
| Type | Description |
| ------ | ------------------------------------------- |
| `Self` | An AirForm instance with validation results |
Example:
```
import air
app = air.Air()
class FlightModel(air.AirModel):
flight_number: str
destination: str
FlightForm = FlightModel.to_form()
@app.post("/flight")
async def submit_flight(request: air.Request):
flight = await FlightForm.from_request(request)
if flight.is_valid:
# Form is valid
return air.Html(
air.H1("Flight Submitted"),
air.P(f"Flight: {flight.data.flight_number}"),
air.P(f"Destination: {flight.data.destination}"),
)
# Form has errors
return air.Html(
air.H1("Validation Failed"),
air.P(f"Errors: {len(flight.errors or [])}"),
)
```
Source code in `src/air/forms.py`
```
@classmethod
async def from_request(cls, request: Request) -> Self:
"""Create and validate an AirForm instance from a request.
Args:
request: The incoming request containing form data
Returns:
An AirForm instance with validation results
Example:
import air
app = air.Air()
class FlightModel(air.AirModel):
flight_number: str
destination: str
FlightForm = FlightModel.to_form()
@app.post("/flight")
async def submit_flight(request: air.Request):
flight = await FlightForm.from_request(request)
if flight.is_valid:
# Form is valid
return air.Html(
air.H1("Flight Submitted"),
air.P(f"Flight: {flight.data.flight_number}"),
air.P(f"Destination: {flight.data.destination}"),
)
# Form has errors
return air.Html(
air.H1("Validation Failed"),
air.P(f"Errors: {len(flight.errors or [])}"),
)
"""
form_data = await request.form()
self = cls()
await self(form_data=form_data)
return self
```
### validate
```
validate(form_data)
```
Validate form data against the model.
Parameters:
| Name | Type | Description | Default |
| ----------- | ---------------- | ----------- | ------------------------------------------------------------- |
| `form_data` | \`dict[Any, Any] | FormData\` | Dictionary or FormData containing the form fields to validate |
Returns:
| Type | Description |
| ------ | -------------------------------------------- |
| `bool` | True if validation succeeds, False otherwise |
Example:
```
import air
app = air.Air()
class FlightModel(air.AirModel):
flight_number: str
destination: str
@app.post("/flight")
async def submit_flight(request: air.Request):
form_data = await request.form()
flight_form = FlightModel.to_form()
if flight_form.validate(form_data):
# Form is valid
return air.Html(
air.H1("Flight Submitted"),
air.P(f"Flight: {flight_form.data.flight_number}"),
air.P(f"Destination: {flight_form.data.destination}"),
)
# Form has errors
return air.Html(
air.H1("Validation Failed"),
air.P(f"Errors: {len(flight_form.errors or [])}"),
)
```
Source code in `src/air/forms.py`
```
def validate(self, form_data: dict[Any, Any] | FormData) -> bool:
"""Validate form data against the model.
Args:
form_data: Dictionary or FormData containing the form fields to validate
Returns:
True if validation succeeds, False otherwise
Example:
import air
app = air.Air()
class FlightModel(air.AirModel):
flight_number: str
destination: str
@app.post("/flight")
async def submit_flight(request: air.Request):
form_data = await request.form()
flight_form = FlightModel.to_form()
if flight_form.validate(form_data):
# Form is valid
return air.Html(
air.H1("Flight Submitted"),
air.P(f"Flight: {flight_form.data.flight_number}"),
air.P(f"Destination: {flight_form.data.destination}"),
)
# Form has errors
return air.Html(
air.H1("Validation Failed"),
air.P(f"Errors: {len(flight_form.errors or [])}"),
)
"""
# Store the submitted data to preserve values on error
self.submitted_data = dict(form_data) if hasattr(form_data, "items") else form_data
try:
assert self.model is not None
self.data = self.model(**form_data)
self.is_valid = True
except ValidationError as e:
self.errors = e.errors()
return self.is_valid
```
## AirField
```
AirField(
default=PydanticUndefined,
*,
default_factory=None,
alias=None,
alias_priority=None,
validation_alias=None,
serialization_alias=None,
title=None,
field_title_generator=None,
description=None,
examples=None,
exclude=None,
exclude_if=None,
discriminator=None,
deprecated=None,
json_schema_extra=None,
frozen=None,
validate_default=None,
repr=None,
init=None,
init_var=None,
kw_only=None,
pattern=None,
strict=None,
coerce_numbers_to_str=None,
gt=None,
ge=None,
lt=None,
le=None,
multiple_of=None,
allow_inf_nan=None,
max_digits=None,
decimal_places=None,
min_length=None,
max_length=None,
union_mode=None,
fail_fast=None,
type=None,
label=None,
autofocus=False,
**extra,
)
```
A wrapper around pydantic.Field to provide a cleaner interface for defining special input types and labels in air forms.
NOTE: This is named AirField to adhere to the same naming convention as AirForm.
Example:
```
# This example demonstrates:
# - How AirField customizes HTML input types (e.g. email, datetime-local)
# - How labels and autofocus attributes appear in rendered forms
# - How AirForm binds to a Pydantic model for validation
# - How the form behaves when submitted with valid and invalid data
# Run:
# uv run examples/src/forms__AirField.py
# Then visit http://127.0.0.1:8000/ in your browser.
from pydantic import BaseModel
import air
app = air.Air()
class ContactModel(BaseModel):
name: str = air.AirField(label="Full Name", min_length=2, max_length=50)
# Note: This uses `str` for email. For stricter server-side validation,
# you can use `EmailStr` from pydantic.
email: str = air.AirField(type="email", label="Email Address")
message: str = air.AirField(label="Message", min_length=10, max_length=500)
preferred_datetime: str = air.AirField(
type="datedatetime-local",
label="Preferred Date & Time",
)
class ContactForm(air.AirForm):
model = ContactModel
@app.page
def index(request: air.Request):
# Render a simple page containing the contact form.
form = ContactForm()
return air.layouts.mvpcss(
air.H1("Contact Form Example Using AirField"),
air.P("Submit the form below to see AirField + AirForm in action."),
air.Form(
form.render(),
air.Button("Submit", type="submit"),
method="post",
action=submit.url(), # ty: ignore[unresolved-attribute]
),
)
@app.post("/submit")
async def submit(request: air.Request) -> air.Html:
# Handle POSTed form data and re-render with errors if invalid.
form = ContactForm()
# Parse form data from the incoming request and validate
form_data = await request.form()
form.validate(form_data)
if form.is_valid:
return air.Html(
air.layouts.mvpcss(
air.H1("Thanks for your message!"),
air.P("Here is what you sent:"),
air.Ul(
air.Li(f"Name: {form.data.name}"),
air.Li(f"Email: {form.data.email}"),
air.Li(f"Message: {form.data.message}"),
air.Li(f"Preferred Date & Time: {form.data.preferred_datetime}"),
),
)
)
# If invalid, re-render the form with errors and values preserved
return air.Html(
air.layouts.mvpcss(
air.H1("Please fix the errors below."),
air.Form(
form.render(),
air.Button("Submit", type="submit"),
method="post",
action=submit.url(), # ty: ignore[unresolved-attribute]
),
)
)
if __name__ == "__main__":
# Run this example with:
# uv run examples/src/forms__AirField.py
import uvicorn
uvicorn.run(app, host="127.0.0.1", port=8000)
```
Source code in `src/air/forms.py`
```
def AirField(
default: Any = PydanticUndefined,
*,
default_factory: Callable[[], Any] | Callable[[dict[str, Any]], Any] | None = None,
alias: str | None = None,
alias_priority: int | None = None,
validation_alias: str | AliasPath | AliasChoices | None = None,
serialization_alias: str | None = None,
title: str | None = None,
field_title_generator: Callable[[str, FieldInfo], str] | None = None,
description: str | None = None,
examples: list[Any] | None = None,
exclude: bool | None = None,
exclude_if: Callable[[Any], bool] | None = None,
discriminator: str | Discriminator | None = None,
deprecated: Deprecated | str | bool | None = None,
json_schema_extra: dict[str, JsonValue] | None = None,
frozen: bool | None = None,
validate_default: bool | None = None,
repr: bool | None = None,
init: bool | None = None,
init_var: bool | None = None,
kw_only: bool | None = None,
pattern: str | re.Pattern[str] | None = None,
strict: bool | None = None,
coerce_numbers_to_str: bool | None = None,
gt: annotated_types.SupportsGt | None = None,
ge: annotated_types.SupportsGe | None = None,
lt: annotated_types.SupportsLt | None = None,
le: annotated_types.SupportsLe | None = None,
multiple_of: float | None = None,
allow_inf_nan: bool | None = None,
max_digits: int | None = None,
decimal_places: int | None = None,
min_length: int | None = None,
max_length: int | None = None,
union_mode: Literal["smart", "left_to_right"] | None = None,
fail_fast: bool | None = None,
# Not in pydantic.Field:
type: str | None = None,
label: str | None = None,
autofocus: bool = False,
**extra: Any,
) -> Any:
"""A wrapper around pydantic.Field to provide a cleaner interface for defining
special input types and labels in air forms.
NOTE: This is named AirField to adhere to the same naming convention as AirForm.
Example:
# This example demonstrates:
# - How AirField customizes HTML input types (e.g. email, datetime-local)
# - How labels and autofocus attributes appear in rendered forms
# - How AirForm binds to a Pydantic model for validation
# - How the form behaves when submitted with valid and invalid data
# Run:
# uv run examples/src/forms__AirField.py
# Then visit http://127.0.0.1:8000/ in your browser.
from pydantic import BaseModel
import air
app = air.Air()
class ContactModel(BaseModel):
name: str = air.AirField(label="Full Name", min_length=2, max_length=50)
# Note: This uses `str` for email. For stricter server-side validation,
# you can use `EmailStr` from pydantic.
email: str = air.AirField(type="email", label="Email Address")
message: str = air.AirField(label="Message", min_length=10, max_length=500)
preferred_datetime: str = air.AirField(
type="datedatetime-local",
label="Preferred Date & Time",
)
class ContactForm(air.AirForm):
model = ContactModel
@app.page
def index(request: air.Request):
# Render a simple page containing the contact form.
form = ContactForm()
return air.layouts.mvpcss(
air.H1("Contact Form Example Using AirField"),
air.P("Submit the form below to see AirField + AirForm in action."),
air.Form(
form.render(),
air.Button("Submit", type="submit"),
method="post",
action=submit.url(), # ty: ignore[unresolved-attribute]
),
)
@app.post("/submit")
async def submit(request: air.Request) -> air.Html:
# Handle POSTed form data and re-render with errors if invalid.
form = ContactForm()
# Parse form data from the incoming request and validate
form_data = await request.form()
form.validate(form_data)
if form.is_valid:
return air.Html(
air.layouts.mvpcss(
air.H1("Thanks for your message!"),
air.P("Here is what you sent:"),
air.Ul(
air.Li(f"Name: {form.data.name}"),
air.Li(f"Email: {form.data.email}"),
air.Li(f"Message: {form.data.message}"),
air.Li(f"Preferred Date & Time: {form.data.preferred_datetime}"),
),
)
)
# If invalid, re-render the form with errors and values preserved
return air.Html(
air.layouts.mvpcss(
air.H1("Please fix the errors below."),
air.Form(
form.render(),
air.Button("Submit", type="submit"),
method="post",
action=submit.url(), # ty: ignore[unresolved-attribute]
),
)
)
if __name__ == "__main__":
# Run this example with:
# uv run examples/src/forms__AirField.py
import uvicorn
uvicorn.run(app, host="127.0.0.1", port=8000)
"""
schema_extra: dict[str, JsonValue] = json_schema_extra or {}
if type:
schema_extra[type] = True
if label:
schema_extra["label"] = label
if autofocus:
schema_extra["autofocus"] = True
if extra:
schema_extra.update(extra)
# noinspection PyArgumentList
return Field(
default,
default_factory=default_factory,
alias=alias,
alias_priority=alias_priority,
validation_alias=validation_alias,
serialization_alias=serialization_alias,
title=title,
field_title_generator=field_title_generator,
description=description,
examples=examples,
exclude=exclude,
exclude_if=exclude_if,
discriminator=discriminator,
deprecated=deprecated,
json_schema_extra=schema_extra,
frozen=frozen,
validate_default=validate_default,
repr=repr,
init=init,
init_var=init_var,
kw_only=kw_only,
pattern=pattern,
strict=strict,
coerce_numbers_to_str=coerce_numbers_to_str,
gt=gt,
ge=ge,
lt=lt,
le=le,
multiple_of=multiple_of,
allow_inf_nan=allow_inf_nan,
max_digits=max_digits,
decimal_places=decimal_places,
min_length=min_length,
max_length=max_length,
union_mode=union_mode,
fail_fast=fail_fast,
) # ty: ignore[no-matching-overload]
```
## default_form_widget
```
default_form_widget(
model, data=None, errors=None, includes=None
)
```
Render a form widget for a given Pydantic model.
Parameters:
| Name | Type | Description | Default |
| ---------- | ----------------- | ---------------------------------- | --------------------------------------------------- |
| `model` | `type[BaseModel]` | The Pydantic model class to render | *required* |
| `data` | \`dict | None\` | Dictionary of data to pre-populate |
| `errors` | \`list | None\` | List of Pydantic validation errors |
| `includes` | \`Sequence[str] | None\` | Sequence of field names to include (None means all) |
Returns:
| Type | Description |
| ----- | --------------------------------- |
| `str` | HTML string representing the form |
Example:
```
import air
from air.forms import default_form_widget
app = air.Air()
class FlightModel(air.AirModel):
flight_number: str
destination: str
passengers: int
@app.page
def index(request: air.Request):
# Render different field groups separately using includes parameter
basic_info = default_form_widget(
model=FlightModel,
data={"flight_number": "AA123"}, # Pre-populate flight_number
includes=["flight_number", "destination"],
)
passenger_info = default_form_widget(
model=FlightModel,
includes=["passengers"],
)
return air.Html(
air.H1("Flight Booking"),
air.Form(
air.Fieldset(
air.Legend("Flight Information"),
air.Raw(basic_info),
),
air.Fieldset(
air.Legend("Passenger Count"),
air.Raw(passenger_info),
),
air.Button("Submit", type="submit"),
method="post",
action="/submit",
),
)
@app.post("/submit")
async def submit(request: air.Request):
form_data = await request.form()
flight_form = FlightModel.to_form()
if flight_form.validate(form_data):
return air.Html(
air.H1("Flight Booked"),
air.P(f"Flight: {flight_form.data.flight_number}"),
air.P(f"Destination: {flight_form.data.destination}"),
air.P(f"Passengers: {flight_form.data.passengers}"),
)
# Re-render with custom layout and errors
basic_info = default_form_widget(
model=FlightModel,
data=dict(form_data),
errors=flight_form.errors,
includes=["flight_number", "destination"],
)
passenger_info = default_form_widget(
model=FlightModel,
data=dict(form_data),
errors=flight_form.errors,
includes=["passengers"],
)
return air.Html(
air.H1("Please fix the errors"),
air.Form(
air.Fieldset(
air.Legend("Flight Information"),
air.Raw(basic_info),
),
air.Fieldset(
air.Legend("Passenger Count"),
air.Raw(passenger_info),
),
air.Button("Submit", type="submit"),
method="post",
action="/submit",
),
)
```
Source code in `src/air/forms.py`
```
def default_form_widget( # noqa: C901
model: type[BaseModel],
data: dict | None = None,
errors: list | None = None,
includes: Sequence[str] | None = None,
) -> str:
"""Render a form widget for a given Pydantic model.
Args:
model: The Pydantic model class to render
data: Dictionary of data to pre-populate
errors: List of Pydantic validation errors
includes: Sequence of field names to include (None means all)
Returns:
HTML string representing the form
Example:
import air
from air.forms import default_form_widget
app = air.Air()
class FlightModel(air.AirModel):
flight_number: str
destination: str
passengers: int
@app.page
def index(request: air.Request):
# Render different field groups separately using includes parameter
basic_info = default_form_widget(
model=FlightModel,
data={"flight_number": "AA123"}, # Pre-populate flight_number
includes=["flight_number", "destination"],
)
passenger_info = default_form_widget(
model=FlightModel,
includes=["passengers"],
)
return air.Html(
air.H1("Flight Booking"),
air.Form(
air.Fieldset(
air.Legend("Flight Information"),
air.Raw(basic_info),
),
air.Fieldset(
air.Legend("Passenger Count"),
air.Raw(passenger_info),
),
air.Button("Submit", type="submit"),
method="post",
action="/submit",
),
)
@app.post("/submit")
async def submit(request: air.Request):
form_data = await request.form()
flight_form = FlightModel.to_form()
if flight_form.validate(form_data):
return air.Html(
air.H1("Flight Booked"),
air.P(f"Flight: {flight_form.data.flight_number}"),
air.P(f"Destination: {flight_form.data.destination}"),
air.P(f"Passengers: {flight_form.data.passengers}"),
)
# Re-render with custom layout and errors
basic_info = default_form_widget(
model=FlightModel,
data=dict(form_data),
errors=flight_form.errors,
includes=["flight_number", "destination"],
)
passenger_info = default_form_widget(
model=FlightModel,
data=dict(form_data),
errors=flight_form.errors,
includes=["passengers"],
)
return air.Html(
air.H1("Please fix the errors"),
air.Form(
air.Fieldset(
air.Legend("Flight Information"),
air.Raw(basic_info),
),
air.Fieldset(
air.Legend("Passenger Count"),
air.Raw(passenger_info),
),
air.Button("Submit", type="submit"),
method="post",
action="/submit",
),
)
"""
error_dict = errors_to_dict(errors)
fields = []
for field_name, field_info in model.model_fields.items():
if includes is not None and field_name not in includes:
continue
input_type = pydantic_type_to_html_type(field_info)
kwargs = {}
# Inject values
if data is not None and field_name in data:
kwargs["value"] = data[field_name]
error = error_dict.get(field_name)
if error:
kwargs["aria-invalid"] = "true"
json_schema_extra: dict = field_info.json_schema_extra or {}
if json_schema_extra.get("autofocus"):
kwargs["autofocus"] = True
# Add HTML5 validation attributes from Pydantic constraints
# Check if field is optional (Union with None)
annotation = field_info.annotation
origin = get_origin(annotation)
is_optional = (origin is Union or origin is UnionType) and type(None) in get_args(annotation)
# Add required attribute for non-optional required fields
if field_info.is_required() and not is_optional:
kwargs["required"] = True
# Extract min_length and max_length from field metadata
for meta in getattr(field_info, "metadata", []):
if isinstance(meta, annotated_types.MinLen):
kwargs["minlength"] = meta.min_length
elif isinstance(meta, annotated_types.MaxLen):
kwargs["maxlength"] = meta.max_length
elif hasattr(annotated_types, "Len") and isinstance(meta, annotated_types.Len):
if getattr(meta, "min_length", None) is not None:
kwargs["minlength"] = meta.min_length
if getattr(meta, "max_length", None) is not None:
kwargs["maxlength"] = meta.max_length
# Fallback to field_info attributes if present
if hasattr(field_info, "min_length") and field_info.min_length is not None:
kwargs.setdefault("minlength", field_info.min_length)
if hasattr(field_info, "max_length") and field_info.max_length is not None:
kwargs.setdefault("maxlength", field_info.max_length)
fields.append(
tags.Tags(
tags.Label(
json_schema_extra.get("label") or field_name,
for_=field_name,
),
tags.Input(name=field_name, type=input_type, id=field_name, **kwargs),
(tags.Small(get_user_error_message(error), id=f"{field_name}-error") if error else ""),
),
)
return tags.Tags(*fields).render()
```
## errors_to_dict
```
errors_to_dict(errors)
```
Converts a pydantic error list to a dictionary for easier reference.
Source code in `src/air/forms.py`
```
def errors_to_dict(errors: list[dict] | None) -> dict[str, dict]:
"""Converts a pydantic error list to a dictionary for easier reference."""
if errors is None:
return {}
return {error["loc"][0]: error for error in errors}
```
## get_user_error_message
```
get_user_error_message(error)
```
Convert technical pydantic error to user-friendly message.
Source code in `src/air/forms.py`
```
def get_user_error_message(error: dict) -> str:
"""Convert technical pydantic error to user-friendly message."""
error_type = error.get("type", "")
technical_msg = error.get("msg", "")
# Map error types to user-friendly messages
messages = {
"missing": "This field is required.",
"int_parsing": "Please enter a valid number.",
"float_parsing": "Please enter a valid number.",
"bool_parsing": "Please select a valid option.",
"string_too_short": "This value is too short.",
"string_too_long": "This value is too long.",
"value_error": "This value is not valid.",
"type_error": "Please enter the correct type of value.",
"assertion_error": "This value doesn't meet the requirements.",
"url_parsing": "Please enter a valid URL.",
"email": "Please enter a valid email address.",
"json_invalid": "Please enter valid JSON.",
"enum": "Please select a valid option.",
"greater_than": "This value must be greater than the minimum.",
"greater_than_equal": "This value must be at least the minimum.",
"less_than": "This value must be less than the maximum.",
"less_than_equal": "This value must be at most the maximum.",
}
# Get user-friendly message or fallback to technical message
return messages.get(error_type, technical_msg or "Please correct this error.")
```
## pydantic_type_to_html_type
```
pydantic_type_to_html_type(field_info)
```
Return HTML type from pydantic type.
Default to 'text' for unknown types.
Source code in `src/air/forms.py`
```
def pydantic_type_to_html_type(field_info: Any) -> str:
"""Return HTML type from pydantic type.
Default to 'text' for unknown types.
"""
special_fields = [
"hidden",
"email",
"password",
"url",
"datedatetime-local",
"month",
"time",
"color",
"file",
]
for field in special_fields:
if field_info.json_schema_extra and field_info.json_schema_extra.get(field, False):
return field
return {int: "number", float: "number", bool: "checkbox", str: "text"}.get(field_info.annotation, "text")
```
## to_form
```
to_form(model, *, name=None, includes=None, widget=None)
```
Generate an :class:`AirForm` instance for the given Pydantic model.
Parameters:
| Name | Type | Description | Default |
| ---------- | ----------------- | ---------------------------------------------------------- | ------------------------------------------------------------------------------ |
| `model` | `type[BaseModel]` | The Pydantic model class the form should validate against. | *required* |
| `name` | \`str | None\` | Optional explicit class name for the generated form. |
| `includes` | \`Sequence[str] | None\` | Optional iterable of field names to render (defaults to all fields). |
| `widget` | \`Callable | None\` | Optional callable to render the form. Falls back to :func:default_form_widget. |
Returns:
| Type | Description |
| --------- | --------------------------------------------- |
| `AirForm` | A new :class:AirForm instance bound to model. |
Source code in `src/air/forms.py`
```
def to_form(
model: type[BaseModel],
*,
name: str | None = None,
includes: Sequence[str] | None = None,
widget: Callable | None = None,
) -> "AirForm":
"""Generate an :class:`AirForm` instance for the given Pydantic model.
Args:
model: The Pydantic model class the form should validate against.
name: Optional explicit class name for the generated form.
includes: Optional iterable of field names to render (defaults to all fields).
widget: Optional callable to render the form. Falls back to :func:`default_form_widget`.
Returns:
A new :class:`AirForm` instance bound to ``model``.
"""
attrs: dict[str, Any] = {"model": model, "__module__": model.__module__}
if includes is not None:
attrs["includes"] = tuple(includes)
if widget is not None:
def _widget(self: AirForm, _widget: Callable = widget) -> Callable:
return _widget
attrs["widget"] = property(_widget)
form_name = name or f"{model.__name__}Form"
generated_form = type(form_name, (AirForm,), attrs)
generated_form.__doc__ = f"Auto-generated AirForm for {model.__module__}.{model.__name__}."
return generated_form()
```
# Layouts
Tools for building layouts and several simple layouts for quick prototyping.
## filter_body_tags
```
filter_body_tags(tags)
```
Given a list of tags, only list the ones that belong in body of an HTML document.
Source code in `src/air/layouts.py`
```
def filter_body_tags(tags) -> list:
"""Given a list of tags, only list the ones that belong in body of an HTML document."""
return [t for t in tags if not isinstance(t, HEAD_TAG_TYPES)]
```
## filter_head_tags
```
filter_head_tags(tags)
```
Given a list of tags, only list the ones that belong in head of an HTML document.
Source code in `src/air/layouts.py`
```
def filter_head_tags(tags) -> list:
"""Given a list of tags, only list the ones that belong in head of an HTML document."""
return [t for t in tags if isinstance(t, HEAD_TAG_TYPES)]
```
## mvpcss
```
mvpcss(*children, is_htmx=False, **kwargs)
```
Renders the basic layout with MVP.css and HTMX for quick prototyping
1. At the top level HTML head tags are put in the `` tag
1. Otherwise everything is put in the ``
1. `Header` and `Nav` tags are placed in the top of the body above the `Main` tag
1. If `is_htmx` is True, then the layout isn't included. This is to support the `hx_boost` feature of HTMX
The `mvpcss` function is a quick prototyping tool. It isn't designed to be extensible. Rather the `mvpcss` layout function makes it easy to roll out quick demonstrations and proofs-of-concept. For more advanced layouts like Eidos or a full-fledged MVP.css implementation, you'll have to create your own layouts.
Parameters:
| Name | Type | Description | Default |
| ---------- | ------ | -------------------------------------------------------- | ------- |
| `children` | `Any` | These typically inherit from air.Tag but can be anything | `()` |
| `is_htmx` | `bool` | Whether or not HTMX sent the request from the page | `False` |
Example:
```
import air
app = air.Air()
@app.page
async def index(request: air.Request):
return air.layouts.mvpcss(
air.Title("Home"),
air.Article(
air.H1("Welcome to Air"),
air.P(air.A("Click to go to Dashboard", href="/dashboard")),
hx_boost="true",
),
is_htmx=request.htmx.is_hx_request,
)
@app.page
async def dashboard(request: air.Request):
return air.layouts.mvpcss(
air.Title("Dashboard"),
air.Article(
air.H1("Dashboard"),
air.P(air.A("Go home", href="/")),
hx_boost="true",
),
is_htmx=request.htmx.is_hx_request,
)
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="127.0.0.1", port=8000)
```
Source code in `src/air/layouts.py`
```
def mvpcss(*children: Any, is_htmx: bool = False, **kwargs: AttributeType) -> Html | Children:
"""Renders the basic layout with MVP.css and HTMX for quick prototyping
1. At the top level HTML head tags are put in the `` tag
2. Otherwise everything is put in the ``
3. `Header` and `Nav` tags are placed in the top of the body above the `Main` tag
4. If `is_htmx` is True, then the layout isn't included. This is to support the `hx_boost`
feature of HTMX
The `mvpcss` function is a quick prototyping tool. It isn't designed to be extensible.
Rather the `mvpcss` layout function makes it easy to roll out quick demonstrations and proofs-of-concept.
For more advanced layouts like Eidos or a full-fledged MVP.css implementation,
you'll have to create your own layouts.
Args:
children: These typically inherit from air.Tag but can be anything
is_htmx: Whether or not HTMX sent the request from the page
Example:
import air
app = air.Air()
@app.page
async def index(request: air.Request):
return air.layouts.mvpcss(
air.Title("Home"),
air.Article(
air.H1("Welcome to Air"),
air.P(air.A("Click to go to Dashboard", href="/dashboard")),
hx_boost="true",
),
is_htmx=request.htmx.is_hx_request,
)
@app.page
async def dashboard(request: air.Request):
return air.layouts.mvpcss(
air.Title("Dashboard"),
air.Article(
air.H1("Dashboard"),
air.P(air.A("Go home", href="/")),
hx_boost="true",
),
is_htmx=request.htmx.is_hx_request,
)
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="127.0.0.1", port=8000)
"""
body_tags = filter_body_tags(children)
head_tags = filter_head_tags(children)
if is_htmx:
return Children(Main(*body_tags), *head_tags)
return Html(
Head(
Link(rel="stylesheet", href="https://unpkg.com/mvp.css"),
Style("footer, header, main { padding: 1rem; } nav {margin-bottom: 1rem;}"),
Script(
src="https://cdn.jsdelivr.net/npm/htmx.org@2.0.6/dist/htmx.min.js",
integrity="sha384-Akqfrbj/HpNVo8k11SXBb6TlBWmXXlYQrCSqEWmyKJe+hDm3Z/B2WVG4smwBkRVm",
crossorigin="anonymous",
),
*head_tags,
),
Body(
_header(body_tags),
Main(*[x for x in body_tags if not isinstance(x, Header)]),
),
)
```
## picocss
```
picocss(*children, is_htmx=False, **kwargs)
```
Renders the basic layout with PicoCSS and HTMX for quick prototyping
1. At the top level HTML head tags are put in the `` tag
1. Otherwise everything is put in the ``
1. If `is_htmx` is True, then the layout isn't included. This is to support the `hx_boost` feature of HTMX
`PicoCSS` is a quick prototyping tool. It isn't designed to be extensible.
Rather the `picocss` layout function makes it easy to roll out quick demonstrations and proofs-of-concept. For more advanced layouts like Eidos or a full-fledged PicoCSS implementation, you'll have to create your own layouts.
Parameters:
| Name | Type | Description | Default |
| ---------- | ------ | -------------------------------------------------------- | ------- |
| `children` | `Any` | These typically inherit from air.Tag but can be anything | `()` |
| `is_htmx` | `bool` | Whether or not HTMX sent the request from the page | `False` |
Source code in `src/air/layouts.py`
```
def picocss(*children: Any, is_htmx: bool = False, **kwargs: AttributeType) -> Html | Children:
"""Renders the basic layout with PicoCSS and HTMX for quick prototyping
1. At the top level HTML head tags are put in the `` tag
2. Otherwise everything is put in the ``
3. If `is_htmx` is True, then the layout isn't included. This is to support the `hx_boost`
feature of HTMX
Note: `PicoCSS` is a quick prototyping tool. It isn't designed to be extensible.
Rather the `picocss` layout function makes it easy to roll out quick demonstrations and proofs-of-concept.
For more advanced layouts like Eidos or a full-fledged PicoCSS implementation,
you'll have to create your own layouts.
Args:
children: These typically inherit from air.Tag but can be anything
is_htmx: Whether or not HTMX sent the request from the page
"""
body_tags = filter_body_tags(children)
head_tags = filter_head_tags(children)
if is_htmx:
return Children(Main(*body_tags, class_="container"), *head_tags)
return Html(
Head(
Link(
rel="stylesheet",
href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css",
),
Script(
src="https://cdn.jsdelivr.net/npm/htmx.org@2.0.6/dist/htmx.min.js",
integrity="sha384-Akqfrbj/HpNVo8k11SXBb6TlBWmXXlYQrCSqEWmyKJe+hDm3Z/B2WVG4smwBkRVm",
crossorigin="anonymous",
),
*head_tags,
),
Body(Main(*body_tags, class_="container")),
)
```
# Middleware
Middleware are callables (functions, classes) that works with every request before it is processed by views. And also with every response before returning it.
- Middleware takes each request that comes to applications
- It can then do something to that request or run code
- Then it passes the request to be processed by the rest of the application (by individual views)
- Takes the response generated by the application
- Can do something to that response or run code
- Finally returns the response.
Background tasks run *after* middleware.
## SessionMiddleware
Bases: `SessionMiddleware`
Used to manage sessions.
Example:
```
from time import time
import air
app = air.Air()
app.add_middleware(air.SessionMiddleware, secret_key="change-me")
@app.page
async def index(request: air.Request):
if "first-visited" not in request.session:
request.session["first-visited"] = time()
return air.layouts.mvpcss(
air.H1(str(request.session.get("first-visited"))),
air.P("Refresh the page and the timestamp won't change"),
air.P(air.A("Reset the time stamp", href="/reset")),
)
@app.page
async def reset(request: air.Request):
request.session.pop("first-visited")
return air.responses.RedirectResponse("/")
```
# Forms
Model utilities for Air.
Provides a thin wrapper around :class:`pydantic.BaseModel` that knows how to generate matching :class:`air.forms.AirForm` subclasses on demand.
## AirModel
Bases: `BaseModel`
Base class for models that integrate tightly with Air forms.
### to_form
```
to_form(*, name=None, includes=None, widget=None)
```
Return an :class:`AirForm` instance bound to `cls`.
Parameters:
| Name | Type | Description | Default |
| ---------- | --------------- | ----------- | -------------------------------------------------------------------- |
| `name` | \`str | None\` | Optional explicit class name for the generated form. |
| `includes` | \`Sequence[str] | None\` | Optional iterable of field names to render (defaults to all fields). |
| `widget` | \`Callable | None\` | Optional custom rendering callable. |
Returns:
| Type | Description |
| --------- | --------------------------------------------------------- |
| `AirForm` | An instance of :class:AirForm that validates against cls. |
Source code in `src/air/models.py`
```
@classmethod
def to_form(
cls,
*,
name: str | None = None,
includes: Sequence[str] | None = None,
widget: Callable | None = None,
) -> AirForm:
"""Return an :class:`AirForm` instance bound to ``cls``.
Args:
name: Optional explicit class name for the generated form.
includes: Optional iterable of field names to render (defaults to all fields).
widget: Optional custom rendering callable.
Returns:
An instance of :class:`AirForm` that validates against ``cls``.
"""
return to_form(cls, name=name, includes=includes, widget=widget)
```
# Requests
`air.requests.Request` is an wrapper for [`starlette.requests.Request`](https://www.starlette.io/requests/), giving Air users a consistent import path. It adds an `htmx` object that includes a lot of quite useful utility methods.
______________________________________________________________________
## Usage
```
import air
from air.requests import Request
app = air.Air()
@app.page
async def request_info(request: Request):
return air.layouts.mvpcss(
air.H1("Request Info"),
air.P(f"Method: {request.method}"),
air.P(f"URL: {request.url}"),
air.P(f"Headers: {dict(request.headers)}"),
air.P(f"Query Params: {dict(request.query_params)}"),
)
```
## Practical Recipes
Here are smaller, focused examples for specific use cases:
### Accessing Query Parameters
```
import air
from air.requests import Request
app = air.Air()
@app.get("/search")
async def search(request: Request):
query = request.query_params.get("q", "none")
return air.Pre(query)
```
### Reading JSON Body
```
import air
from air.requests import Request
from air.responses import JSONResponse
app = air.Air()
@app.post("/items")
async def create_item(request: Request):
data = await request.json()
return JSONResponse({"item": data})
```
### Reading Form Data
```
import air
from air.requests import Request
from air.responses import JSONResponse
app = air.Air()
@app.post("/login")
async def login(request: Request):
form = await request.form()
return air.layouts.mvpcss(
air.Section(
air.Aside({"username": form.get("username")})
)
)
```
### Accessing the HTMX object
```
import air
app = air.Air()
@app.page
def index(request: air.Request):
return air.layouts.mvpcss(
air.H1(f'From HTMX?'),
air.P(f"This request came from an HTMX element on a page: {request.htmx}")
)
```
Tools for handling requests.
## AirRequest
Bases: `Request`
A wrapper around `starlette.requests.Request` that includes the `HtmxDetails` object.
Note
AirRequest is available in Air 0.36.0+
## HtmxDetails
```
HtmxDetails(headers, url)
```
Attached to every Request served by Air; provides helpers for HTMX-aware handling. Derived values are computed once in `__post_init__`.
### __bool__
```
__bool__()
```
`True` if the request was made with htmx, otherwise `False`. Detected by checking if the `HX-Request` header equals `true`.
This method allows you to change content for requests made with htmx:
Example:
```
import air
from random import randint
app = air.Air()
@app.page
def index(request: air.Request):
if request.htmx:
return air.H1(
"Click me: ", randint(1, 100),
id="number",
hx_get="/",
hx_swap="outerHTML"
)
return air.layouts.mvpcss(
air.H1(
"Click me: ", randint(1, 100),
id="number",
hx_get="/",
hx_swap="outerHTML"
)
)
```
Source code in `src/air/requests.py`
```
def __bool__(self) -> bool:
"""`True` if the request was made with htmx, otherwise `False`. Detected by checking if the `HX-Request` header equals `true`.
This method allows you to change content for requests made with htmx:
Example:
import air
from random import randint
app = air.Air()
@app.page
def index(request: air.Request):
if request.htmx:
return air.H1(
"Click me: ", randint(1, 100),
id="number",
hx_get="/",
hx_swap="outerHTML"
)
return air.layouts.mvpcss(
air.H1(
"Click me: ", randint(1, 100),
id="number",
hx_get="/",
hx_swap="outerHTML"
)
)
"""
return self.headers.get("HX-Request") == "true"
```
### boosted
```
boosted = field(init=False)
```
`True` if the request came from an element with the `hx-boost` attribute. Detected by checking if the `HX-Boosted` header equals `true`.
Example:
```
import air
from random import randint
app = air.Air()
@app.page
def index(request: air.Request):
if request.htmx.boosted:
# Do something here
```
### current_url
```
current_url = field(init=False)
```
The current URL in the browser that htmx made this request from, or `None` for non-htmx requests. Based on the `HX-Current-URL` header.
### current_url_abs_path
```
current_url_abs_path = field(init=False)
```
The absolute-path form of `current_url`, that is the URL without scheme or netloc, or None for non-htmx requests.
This value will also be `None` if the scheme and netloc do not match the request. This could happen if the request is cross-origin, or if Air is not configured correctly.
### history_restore_request
```
history_restore_request = field(init=False)
```
`True` if the request is for history restoration after a miss in the local history cache. Detected by checking if the `HX-History-Restore-Request` header equals `true`.
### prompt
```
prompt = field(init=False)
```
The user response to `hx-prompt` if it was used, or `None`.
### target
```
target = field(init=False)
```
The `id` of the target element if it exists, or `None`. Based on the `HX-Target` header.
### trigger
```
trigger = field(init=False)
```
The `id` of the triggered element if it exists, or `None`. Based on the `HX-Trigger` header.
### trigger_name
```
trigger_name = field(init=False)
```
The name of the triggered element if it exists, or `None`. Based on the `HX-Trigger-Name` header.
## Request
```
Request = AirRequest
```
Alias for the `AirRequest` Request class; use it if it improves clarity.
# Responses
Air uses custom response classes to improve the developer experience.
## TagResponse
```
TagResponse = AirResponse
```
Alias for the `AirResponse` Response class; use it if it improves clarity.
## AirResponse
Bases: `HTMLResponse`
Response class to handle air.tags.Tags or HTML (from Jinja2).
### render
```
render(tag)
```
Render Tag elements to bytes of HTML.
Source code in `src/air/responses.py`
```
@override
def render(self, tag: BaseTag | str) -> bytes | memoryview:
"""Render Tag elements to bytes of HTML."""
return super().render(str(tag))
```
## SSEResponse
Bases: `StreamingResponse`
Response class for Server Sent Events
Example:
```
# For tags
import random
from asyncio import sleep
import air
app = air.Air()
@app.page
def index():
return air.layouts.mvpcss(
air.Script(src="https://unpkg.com/htmx-ext-sse@2.2.1/sse.js"),
air.Title("Server Sent Event Demo"),
air.H1("Server Sent Event Demo"),
air.P("Lottery number generator"),
air.Section(
hx_ext="sse",
sse_connect="/lottery-numbers",
hx_swap="beforeend show:bottom",
sse_swap="message",
),
)
async def lottery_generator():
while True:
lottery_numbers = ", ".join([str(random.randint(1, 40)) for x in range(6)])
# Tags work seamlessly
yield air.Aside(lottery_numbers)
# As do strings. Non-strings are cast to strings via the str built-in
yield "Hello, world"
await sleep(1)
@app.get("/lottery-numbers")
async def get():
return air.SSEResponse(lottery_generator())
```
Routing
If you need to knit several Python modules with their own Air views into one, that's where Routing is used. They allow the near seamless combination of multiple Air apps into one. Larger sites are often built from multiple routers.
Let's imagine we have an e-commerce store with a shopping cart app. Use instantiate a `router` object using `air.AirRouter()` just as we would with `air.App()`:
```
# cart.py
import air
router = air.AirRouter()
@router.page
def cart():
return air.H1('I am a shopping cart')
```
Then in our main page we can load that and tie it into our main `app`.
```
import air
from cart import router as cart_router
app = air.Air()
app.include_router(cart_router)
@app.page
def index():
return air.H1('Home page')
```
Note that the router allows sharing of sessions and other application states.
In addition, we can add links through the `.url()` method available on route functions, which generates URLs programmatically:
```
import air
from cart import router as cart_router, cart
app = air.Air()
app.include_router(cart_router)
@app.page
def index():
return air.Div(
air.H1('Home page'),
air.A('View cart', href=cart.url())
)
```
______________________________________________________________________
Use routing if you want a single cohesive app where all routes share middlewares and error handling.
## AirRoute
Bases: `APIRoute`
Custom APIRoute that uses Air's custom AirRequest class.
## AirRouter
```
AirRouter(
*,
prefix="",
tags=None,
dependencies=None,
default_response_class=AirResponse,
responses=None,
callbacks=None,
routes=None,
redirect_slashes=True,
default=None,
dependency_overrides_provider=None,
route_class=AirRoute,
on_startup=None,
on_shutdown=None,
lifespan=None,
deprecated=None,
include_in_schema=True,
generate_unique_id_function=default_generate_unique_id,
path_separator="-",
)
```
Bases: `APIRouter`, `RouterMixin`
`AirRouter` class, used to group *path operations*, for example to structure an app in multiple files. It would then be included in the `App` app, or in another `AirRouter` (ultimately included in the app).
Example
````
```python
import air
app = air.Air()
router = air.AirRouter()
@router.get("/users/", tags=["users"])
async def read_users():
return [{"username": "Rick"}, {"username": "Morty"}]
app.include_router(router)
````
```
Source code in `src/air/routing.py`
```
def __init__( self, \*, prefix: Annotated[str, Doc("An optional path prefix for the router.")] = "", tags: Annotated\[ list[str | Enum] | None, Doc( """ A list of tags to be applied to all the *path operations* in this router.
```
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/).
"""
),
] = None,
dependencies: Annotated[
Sequence[params.Depends] | None,
Doc(
"""
A list of dependencies (using `Depends()`) to be applied to all the
*path operations* in this router.
Read more about it in the
[FastAPI docs for Bigger Applications - Multiple Files](https://fastapi.tiangolo.com/tutorial/bigger-applications/#include-an-apirouter-with-a-custom-prefix-tags-responses-and-dependencies).
"""
),
] = None,
default_response_class: Annotated[
type[Response],
Doc(
"""
The default response class to be used.
Read more in the
[FastAPI docs for Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/#default-response-class).
"""
),
] = AirResponse,
responses: Annotated[
dict[int | str, dict[str, Any]] | None,
Doc(
"""
Additional responses to be shown in OpenAPI.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for Additional Responses in OpenAPI](https://fastapi.tiangolo.com/advanced/additional-responses/).
And in the
[FastAPI docs for Bigger Applications](https://fastapi.tiangolo.com/tutorial/bigger-applications/#include-an-apirouter-with-a-custom-prefix-tags-responses-and-dependencies).
"""
),
] = None,
callbacks: Annotated[
list[BaseRoute] | None,
Doc(
"""
OpenAPI callbacks that should apply to all *path operations* in this
router.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for OpenAPI Callbacks](https://fastapi.tiangolo.com/advanced/openapi-callbacks/).
"""
),
] = None,
routes: Annotated[
list[BaseRoute] | None,
Doc(
"""
**Note**: you probably shouldn't use this parameter, it is inherited
from Starlette and supported for compatibility.
---
A list of routes to serve incoming HTTP and WebSocket requests.
"""
),
deprecated(
"""
You normally wouldn't use this parameter with FastAPI, it is inherited
from Starlette and supported for compatibility.
In FastAPI, you normally would use the *path operation methods*,
like `router.get()`, `router.post()`, etc.
"""
),
] = None,
redirect_slashes: Annotated[
bool,
Doc(
"""
Whether to detect and redirect slashes in URLs when the client doesn't
use the same format.
"""
),
] = True,
default: Annotated[
ASGIApp | None,
Doc(
"""
Default function handler for this router. Used to handle
404 Not Found errors.
"""
),
] = None,
dependency_overrides_provider: Annotated[
Any | None,
Doc(
"""
Only used internally by FastAPI to handle dependency overrides.
You shouldn't need to use it. It normally points to the `FastAPI` app
object.
"""
),
] = None,
route_class: Annotated[
type[AirRoute],
Doc(
"""
Custom route (*path operation*) class to be used by this router.
Read more about it in the
[FastAPI docs for Custom Request and APIRoute class](https://fastapi.tiangolo.com/how-to/custom-request-and-route/#custom-apiroute-class-in-a-router).
"""
),
] = AirRoute,
on_startup: Annotated[
Sequence[Callable[[], Any]] | None,
Doc(
"""
A list of startup event handler functions.
You should instead use the `lifespan` handlers.
Read more in the [FastAPI docs for `lifespan`](https://fastapi.tiangolo.com/advanced/events/).
"""
),
] = None,
on_shutdown: Annotated[
Sequence[Callable[[], Any]] | None,
Doc(
"""
A list of shutdown event handler functions.
You should instead use the `lifespan` handlers.
Read more in the
[FastAPI docs for `lifespan`](https://fastapi.tiangolo.com/advanced/events/).
"""
),
] = None,
# the generic to Lifespan[AppType] is the type of the top level application
# which the router cannot know statically, so we use typing.Any
lifespan: Annotated[
Lifespan[Any] | None,
Doc(
"""
A `Lifespan` context manager handler. This replaces `startup` and
`shutdown` functions with a single context manager.
Read more in the
[FastAPI docs for `lifespan`](https://fastapi.tiangolo.com/advanced/events/).
"""
),
] = None,
deprecated: Annotated[
bool | None,
Doc(
"""
Mark all *path operations* in this router as deprecated.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/).
"""
),
] = None,
include_in_schema: Annotated[
bool,
Doc(
"""
To include (or not) all the *path operations* in this router in the
generated OpenAPI.
This affects the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-parameters-from-openapi).
"""
),
] = True,
generate_unique_id_function: Annotated[
Callable[[APIRoute], str],
Doc(
"""
Customize the function used to generate unique IDs for the *path
operations* shown in the generated OpenAPI.
This is particularly useful when automatically generating clients or
SDKs for your API.
Read more about it in the
[FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function).
"""
),
] = default_generate_unique_id,
path_separator: Annotated[Literal["/", "-"], Doc("An optional path separator.")] = "-",
```
) -> None: self.path_separator = path_separator if default is None: default = default_404_router_handler super().__init__( prefix=prefix, tags=tags, dependencies=dependencies, default_response_class=default_response_class, responses=responses, callbacks=callbacks, routes=routes, redirect_slashes=redirect_slashes, default=default, dependency_overrides_provider=dependency_overrides_provider, route_class=route_class, on_startup=on_startup, on_shutdown=on_shutdown, lifespan=lifespan, deprecated=deprecated, include_in_schema=include_in_schema, generate_unique_id_function=generate_unique_id_function, ) if prefix: assert prefix.startswith("/"), "A path prefix must start with '/'" assert not prefix.endswith("/"), "A path prefix must not end with '/' except for the root path"
```
### delete
```
delete( path, \*, response_model=None, status_code=None, tags=None, dependencies=None, summary=None, description=None, response_description="Successful Response", responses=None, deprecated=None, operation_id=None, response_model_include=None, response_model_exclude=None, response_model_by_alias=True, response_model_exclude_unset=False, response_model_exclude_defaults=False, response_model_exclude_none=False, include_in_schema=True, response_class=AirResponse, name=None, callbacks=None, openapi_extra=None, generate_unique_id_function=generate_unique_id, )
```
Add a *path operation* using an HTTP DELETE operation.
Source code in `src/air/routing.py`
```
def delete( self, path: Annotated\[ str, Doc( """ The URL path to be used for this *path operation*.
```
For example, in `http://example.com/items`, the path is `/items`.
"""
),
],
*,
response_model: Annotated[
Any,
Doc(
"""
The type to use for the response.
It could be any valid Pydantic *field* type. So, it doesn't have to
be a Pydantic model, it could be other things, like a `list`, `dict`,
etc.
It will be used for:
* Documentation: the generated OpenAPI (and the UI at `/docs`) will
show it as the response (JSON Schema).
* Serialization: you could return an arbitrary object and the
`response_model` would be used to serialize that object into the
corresponding JSON.
* Filtering: the JSON sent to the client will only contain the data
(fields) defined in the `response_model`. If you returned an object
that contains an attribute `password` but the `response_model` does
not include that field, the JSON sent to the client would not have
that `password`.
* Validation: whatever you return will be serialized with the
`response_model`, converting any data as necessary to generate the
corresponding JSON. But if the data in the object returned is not
valid, that would mean a violation of the contract with the client,
so it's an error from the API developer. So, FastAPI will raise an
error and return a 500 error code (Internal Server Error).
Read more about it in the
[FastAPI docs for Response Model](https://fastapi.tiangolo.com/tutorial/response-model/).
"""
),
] = None,
status_code: Annotated[
int | None,
Doc(
"""
The default status code to be used for the response.
You could override the status code by returning a response directly.
Read more about it in the
[FastAPI docs for Response Status Code](https://fastapi.tiangolo.com/tutorial/response-status-code/).
"""
),
] = None,
tags: Annotated[
list[str | Enum] | None,
Doc(
"""
A list of tags to be applied to the *path operation*.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/#tags).
"""
),
] = None,
dependencies: Annotated[
Sequence[params.Depends] | None,
Doc(
"""
A list of dependencies (using `Depends()`) to be applied to the
*path operation*.
Read more about it in the
[FastAPI docs for Dependencies in path operation decorators](https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-in-path-operation-decorators/).
"""
),
] = None,
summary: Annotated[
str | None,
Doc(
"""
A summary for the *path operation*.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/).
"""
),
] = None,
description: Annotated[
str | None,
Doc(
"""
A description for the *path operation*.
If not provided, it will be extracted automatically from the docstring
of the *path operation function*.
It can contain Markdown.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/).
"""
),
] = None,
response_description: Annotated[
str,
Doc(
"""
The description for the default response.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
"""
),
] = "Successful Response",
responses: Annotated[
dict[int | str, dict[str, Any]] | None,
Doc(
"""
Additional responses that could be returned by this *path operation*.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
"""
),
] = None,
deprecated: Annotated[
bool | None,
Doc(
"""
Mark this *path operation* as deprecated.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
"""
),
] = None,
operation_id: Annotated[
str | None,
Doc(
"""
Custom operation ID to be used by this *path operation*.
By default, it is generated automatically.
If you provide a custom operation ID, you need to make sure it is
unique for the whole API.
You can customize the
operation ID generation with the parameter
`generate_unique_id_function` in the `FastAPI` class.
Read more about it in the
[FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function).
"""
),
] = None,
response_model_include: Annotated[
IncEx | None,
Doc(
"""
Configuration passed to Pydantic to include only certain fields in the
response data.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
"""
),
] = None,
response_model_exclude: Annotated[
IncEx | None,
Doc(
"""
Configuration passed to Pydantic to exclude certain fields in the
response data.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
"""
),
] = None,
response_model_by_alias: Annotated[
bool,
Doc(
"""
Configuration passed to Pydantic to define if the response model
should be serialized by alias when an alias is used.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
"""
),
] = True,
response_model_exclude_unset: Annotated[
bool,
Doc(
"""
Configuration passed to Pydantic to define if the response data
should have all the fields, including the ones that were not set and
have their default values. This is different from
`response_model_exclude_defaults` in that if the fields are set,
they will be included in the response, even if the value is the same
as the default.
When `True`, default values are omitted from the response.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter).
"""
),
] = False,
response_model_exclude_defaults: Annotated[
bool,
Doc(
"""
Configuration passed to Pydantic to define if the response data
should have all the fields, including the ones that have the same value
as the default. This is different from `response_model_exclude_unset`
in that if the fields are set but contain the same default values,
they will be excluded from the response.
When `True`, default values are omitted from the response.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter).
"""
),
] = False,
response_model_exclude_none: Annotated[
bool,
Doc(
"""
Configuration passed to Pydantic to define if the response data should
exclude fields set to `None`.
This is much simpler (less smart) than `response_model_exclude_unset`
and `response_model_exclude_defaults`. You probably want to use one of
those two instead of this one, as those allow returning `None` values
when it makes sense.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_exclude_none).
"""
),
] = False,
include_in_schema: Annotated[
bool,
Doc(
"""
Include this *path operation* in the generated OpenAPI schema.
This affects the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-from-openapi).
"""
),
] = True,
response_class: Annotated[
type[Response],
Doc(
"""
Response class to be used for this *path operation*.
This will not be used if you return a response directly.
Read more about it in the
[FastAPI docs for Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/#redirectresponse).
"""
),
] = AirResponse,
name: Annotated[
str | None,
Doc(
"""
Name for this *path operation*. Only used internally.
"""
),
] = None,
callbacks: Annotated[
list[BaseRoute] | None,
Doc(
"""
List of *path operations* that will be used as OpenAPI callbacks.
This is only for OpenAPI documentation, the callbacks won't be used
directly.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for OpenAPI Callbacks](https://fastapi.tiangolo.com/advanced/openapi-callbacks/).
"""
),
] = None,
openapi_extra: Annotated[
dict[str, Any] | None,
Doc(
"""
Extra metadata to be included in the OpenAPI schema for this *path
operation*.
Read more about it in the
[FastAPI docs for Path Operation Advanced Configuration](https://fastapi.tiangolo.com/advanced/path-operation-advanced-configuration/#custom-openapi-path-operation-schema).
"""
),
] = None,
generate_unique_id_function: Annotated[
Callable[[APIRoute], str],
Doc(
"""
Customize the function used to generate unique IDs for the *path
operations* shown in the generated OpenAPI.
This is particularly useful when automatically generating clients or
SDKs for your API.
Read more about it in the
[FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function).
"""
),
] = generate_unique_id,
```
) -> Callable\[\[Callable[..., Any]\], Callable[..., Any]\]: """ Add a *path operation* using an HTTP DELETE operation. """
```
def decorator[**P, R](func: Callable[P, MaybeAwaitable[R]]) -> RouteCallable:
@wraps(func)
async def endpoint(*args: P.args, **kw: P.kwargs) -> Response:
result = func(*args, **kw)
if inspect.isawaitable(result):
result = await result
if isinstance(result, Response):
return result
return response_class(result)
decorated = super(AirRouter, self).delete(
path,
response_model=response_model,
status_code=status_code,
tags=tags,
dependencies=dependencies,
summary=summary,
description=description,
response_description=response_description,
responses=responses,
deprecated=deprecated,
operation_id=operation_id,
response_model_include=response_model_include,
response_model_exclude=response_model_exclude,
response_model_by_alias=response_model_by_alias,
response_model_exclude_unset=response_model_exclude_unset,
response_model_exclude_defaults=response_model_exclude_defaults,
response_model_exclude_none=response_model_exclude_none,
include_in_schema=include_in_schema,
response_class=response_class,
name=name,
callbacks=callbacks,
openapi_extra=openapi_extra,
generate_unique_id_function=generate_unique_id_function,
)(endpoint)
decorated.url = self._url_helper(name or endpoint.__name__)
return decorated
return decorator
```
```
### get
```
get( path, \*, response_model=None, status_code=None, tags=None, dependencies=None, summary=None, description=None, response_description="Successful Response", responses=None, deprecated=None, operation_id=None, response_model_include=None, response_model_exclude=None, response_model_by_alias=True, response_model_exclude_unset=False, response_model_exclude_defaults=False, response_model_exclude_none=False, include_in_schema=True, response_class=AirResponse, name=None, callbacks=None, openapi_extra=None, generate_unique_id_function=generate_unique_id, )
```
Add a *path operation* using an HTTP GET operation.
##### Example
```
from air import Air, AirRouter
app = Air() router = AirRouter()
@app.get("/hello") def hello_world(): return air.H1("Hello, World!")
app.include_router(router)
```
Source code in `src/air/routing.py`
```
def get( self, path: Annotated\[ str, Doc( """ The URL path to be used for this *path operation*.
```
For example, in `http://example.com/items`, the path is `/items`.
"""
),
],
*,
response_model: Annotated[
Any,
Doc(
"""
The type to use for the response.
It could be any valid Pydantic *field* type. So, it doesn't have to
be a Pydantic model, it could be other things, like a `list`, `dict`,
etc.
It will be used for:
* Documentation: the generated OpenAPI (and the UI at `/docs`) will
show it as the response (JSON Schema).
* Serialization: you could return an arbitrary object and the
`response_model` would be used to serialize that object into the
corresponding JSON.
* Filtering: the JSON sent to the client will only contain the data
(fields) defined in the `response_model`. If you returned an object
that contains an attribute `password` but the `response_model` does
not include that field, the JSON sent to the client would not have
that `password`.
* Validation: whatever you return will be serialized with the
`response_model`, converting any data as necessary to generate the
corresponding JSON. But if the data in the object returned is not
valid, that would mean a violation of the contract with the client,
so it's an error from the API developer. So, FastAPI will raise an
error and return a 500 error code (Internal Server Error).
Read more about it in the
[FastAPI docs for Response Model](https://fastapi.tiangolo.com/tutorial/response-model/).
"""
),
] = None,
status_code: Annotated[
int | None,
Doc(
"""
The default status code to be used for the response.
You could override the status code by returning a response directly.
Read more about it in the
[FastAPI docs for Response Status Code](https://fastapi.tiangolo.com/tutorial/response-status-code/).
"""
),
] = None,
tags: Annotated[
list[str | Enum] | None,
Doc(
"""
A list of tags to be applied to the *path operation*.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/#tags).
"""
),
] = None,
dependencies: Annotated[
Sequence[params.Depends] | None,
Doc(
"""
A list of dependencies (using `Depends()`) to be applied to the
*path operation*.
Read more about it in the
[FastAPI docs for Dependencies in path operation decorators](https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-in-path-operation-decorators/).
"""
),
] = None,
summary: Annotated[
str | None,
Doc(
"""
A summary for the *path operation*.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/).
"""
),
] = None,
description: Annotated[
str | None,
Doc(
"""
A description for the *path operation*.
If not provided, it will be extracted automatically from the docstring
of the *path operation function*.
It can contain Markdown.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/).
"""
),
] = None,
response_description: Annotated[
str,
Doc(
"""
The description for the default response.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
"""
),
] = "Successful Response",
responses: Annotated[
dict[int | str, dict[str, Any]] | None,
Doc(
"""
Additional responses that could be returned by this *path operation*.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
"""
),
] = None,
deprecated: Annotated[
bool | None,
Doc(
"""
Mark this *path operation* as deprecated.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
"""
),
] = None,
operation_id: Annotated[
str | None,
Doc(
"""
Custom operation ID to be used by this *path operation*.
By default, it is generated automatically.
If you provide a custom operation ID, you need to make sure it is
unique for the whole API.
You can customize the
operation ID generation with the parameter
`generate_unique_id_function` in the `FastAPI` class.
Read more about it in the
[FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function).
"""
),
] = None,
response_model_include: Annotated[
IncEx | None,
Doc(
"""
Configuration passed to Pydantic to include only certain fields in the
response data.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
"""
),
] = None,
response_model_exclude: Annotated[
IncEx | None,
Doc(
"""
Configuration passed to Pydantic to exclude certain fields in the
response data.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
"""
),
] = None,
response_model_by_alias: Annotated[
bool,
Doc(
"""
Configuration passed to Pydantic to define if the response model
should be serialized by alias when an alias is used.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
"""
),
] = True,
response_model_exclude_unset: Annotated[
bool,
Doc(
"""
Configuration passed to Pydantic to define if the response data
should have all the fields, including the ones that were not set and
have their default values. This is different from
`response_model_exclude_defaults` in that if the fields are set,
they will be included in the response, even if the value is the same
as the default.
When `True`, default values are omitted from the response.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter).
"""
),
] = False,
response_model_exclude_defaults: Annotated[
bool,
Doc(
"""
Configuration passed to Pydantic to define if the response data
should have all the fields, including the ones that have the same value
as the default. This is different from `response_model_exclude_unset`
in that if the fields are set but contain the same default values,
they will be excluded from the response.
When `True`, default values are omitted from the response.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter).
"""
),
] = False,
response_model_exclude_none: Annotated[
bool,
Doc(
"""
Configuration passed to Pydantic to define if the response data should
exclude fields set to `None`.
This is much simpler (less smart) than `response_model_exclude_unset`
and `response_model_exclude_defaults`. You probably want to use one of
those two instead of this one, as those allow returning `None` values
when it makes sense.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_exclude_none).
"""
),
] = False,
include_in_schema: Annotated[
bool,
Doc(
"""
Include this *path operation* in the generated OpenAPI schema.
This affects the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-parameters-from-openapi).
"""
),
] = True,
response_class: Annotated[
type[Response],
Doc(
"""
Response class to be used for this *path operation*.
This will not be used if you return a response directly.
Read more about it in the
[FastAPI docs for Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/#redirectresponse).
"""
),
] = AirResponse,
name: Annotated[
str | None,
Doc(
"""
Name for this *path operation*. Only used internally.
"""
),
] = None,
callbacks: Annotated[
list[BaseRoute] | None,
Doc(
"""
List of *path operations* that will be used as OpenAPI callbacks.
This is only for OpenAPI documentation, the callbacks won't be used
directly.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for OpenAPI Callbacks](https://fastapi.tiangolo.com/advanced/openapi-callbacks/).
"""
),
] = None,
openapi_extra: Annotated[
dict[str, Any] | None,
Doc(
"""
Extra metadata to be included in the OpenAPI schema for this *path
operation*.
Read more about it in the
[FastAPI docs for Path Operation Advanced Configuration](https://fastapi.tiangolo.com/advanced/path-operation-advanced-configuration/#custom-openapi-path-operation-schema).
"""
),
] = None,
generate_unique_id_function: Annotated[
Callable[[APIRoute], str],
Doc(
"""
Customize the function used to generate unique IDs for the *path
operations* shown in the generated OpenAPI.
This is particularly useful when automatically generating clients or
SDKs for your API.
Read more about it in the
[FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function).
"""
),
] = generate_unique_id,
```
) -> Callable\[\[Callable[..., Any]\], Callable[..., Any]\]: """ Add a *path operation* using an HTTP GET operation.
````
## Example
```python
from air import Air, AirRouter
app = Air()
router = AirRouter()
@app.get("/hello")
def hello_world():
return air.H1("Hello, World!")
app.include_router(router)
```
"""
def decorator[**P, R](func: Callable[P, MaybeAwaitable[R]]) -> RouteCallable:
@wraps(func)
async def endpoint(*args: P.args, **kw: P.kwargs) -> Response:
result = func(*args, **kw)
if inspect.isawaitable(result):
result = await result
if isinstance(result, Response):
return result
# Force HTML for non-Response results
return response_class(result)
decorated = super(AirRouter, self).get(
path,
response_model=response_model,
status_code=status_code,
tags=tags,
dependencies=dependencies,
summary=summary,
description=description,
response_description=response_description,
responses=responses,
deprecated=deprecated,
operation_id=operation_id,
response_model_include=response_model_include,
response_model_exclude=response_model_exclude,
response_model_by_alias=response_model_by_alias,
response_model_exclude_unset=response_model_exclude_unset,
response_model_exclude_defaults=response_model_exclude_defaults,
response_model_exclude_none=response_model_exclude_none,
include_in_schema=include_in_schema,
response_class=response_class,
name=name,
callbacks=callbacks,
openapi_extra=openapi_extra,
generate_unique_id_function=generate_unique_id_function,
)(endpoint)
decorated.url = self._url_helper(name or endpoint.__name__)
return decorated
return decorator
````
```
### patch
```
patch( path, \*, response_model=None, status_code=None, tags=None, dependencies=None, summary=None, description=None, response_description="Successful Response", responses=None, deprecated=None, operation_id=None, response_model_include=None, response_model_exclude=None, response_model_by_alias=True, response_model_exclude_unset=False, response_model_exclude_defaults=False, response_model_exclude_none=False, include_in_schema=True, response_class=AirResponse, name=None, callbacks=None, openapi_extra=None, generate_unique_id_function=generate_unique_id, )
```
Add a *path operation* using an HTTP PATCH operation.
Source code in `src/air/routing.py`
```
def patch( self, path: Annotated\[ str, Doc( """ The URL path to be used for this *path operation*.
```
For example, in `http://example.com/items`, the path is `/items`.
"""
),
],
*,
response_model: Annotated[
Any,
Doc(
"""
The type to use for the response.
It could be any valid Pydantic *field* type. So, it doesn't have to
be a Pydantic model, it could be other things, like a `list`, `dict`,
etc.
It will be used for:
* Documentation: the generated OpenAPI (and the UI at `/docs`) will
show it as the response (JSON Schema).
* Serialization: you could return an arbitrary object and the
`response_model` would be used to serialize that object into the
corresponding JSON.
* Filtering: the JSON sent to the client will only contain the data
(fields) defined in the `response_model`. If you returned an object
that contains an attribute `password` but the `response_model` does
not include that field, the JSON sent to the client would not have
that `password`.
* Validation: whatever you return will be serialized with the
`response_model`, converting any data as necessary to generate the
corresponding JSON. But if the data in the object returned is not
valid, that would mean a violation of the contract with the client,
so it's an error from the API developer. So, FastAPI will raise an
error and return a 500 error code (Internal Server Error).
Read more about it in the
[FastAPI docs for Response Model](https://fastapi.tiangolo.com/tutorial/response-model/).
"""
),
] = None,
status_code: Annotated[
int | None,
Doc(
"""
The default status code to be used for the response.
You could override the status code by returning a response directly.
Read more about it in the
[FastAPI docs for Response Status Code](https://fastapi.tiangolo.com/tutorial/response-status-code/).
"""
),
] = None,
tags: Annotated[
list[str | Enum] | None,
Doc(
"""
A list of tags to be applied to the *path operation*.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/#tags).
"""
),
] = None,
dependencies: Annotated[
Sequence[Depends] | None,
Doc(
"""
A list of dependencies (using `Depends()`) to be applied to the
*path operation*.
Read more about it in the
[FastAPI docs for Dependencies in path operation decorators](https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-in-path-operation-decorators/).
"""
),
] = None,
summary: Annotated[
str | None,
Doc(
"""
A summary for the *path operation*.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/).
"""
),
] = None,
description: Annotated[
str | None,
Doc(
"""
A description for the *path operation*.
If not provided, it will be extracted automatically from the docstring
of the *path operation function*.
It can contain Markdown.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/).
"""
),
] = None,
response_description: Annotated[
str,
Doc(
"""
The description for the default response.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
"""
),
] = "Successful Response",
responses: Annotated[
dict[int | str, dict[str, Any]] | None,
Doc(
"""
Additional responses that could be returned by this *path operation*.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
"""
),
] = None,
deprecated: Annotated[
bool | None,
Doc(
"""
Mark this *path operation* as deprecated.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
"""
),
] = None,
operation_id: Annotated[
str | None,
Doc(
"""
Custom operation ID to be used by this *path operation*.
By default, it is generated automatically.
If you provide a custom operation ID, you need to make sure it is
unique for the whole API.
You can customize the
operation ID generation with the parameter
`generate_unique_id_function` in the `FastAPI` class.
Read more about it in the
[FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function).
"""
),
] = None,
response_model_include: Annotated[
IncEx | None,
Doc(
"""
Configuration passed to Pydantic to include only certain fields in the
response data.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
"""
),
] = None,
response_model_exclude: Annotated[
IncEx | None,
Doc(
"""
Configuration passed to Pydantic to exclude certain fields in the
response data.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
"""
),
] = None,
response_model_by_alias: Annotated[
bool,
Doc(
"""
Configuration passed to Pydantic to define if the response model
should be serialized by alias when an alias is used.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
"""
),
] = True,
response_model_exclude_unset: Annotated[
bool,
Doc(
"""
Configuration passed to Pydantic to define if the response data
should have all the fields, including the ones that were not set and
have their default values. This is different from
`response_model_exclude_defaults` in that if the fields are set,
they will be included in the response, even if the value is the same
as the default.
When `True`, default values are omitted from the response.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter).
"""
),
] = False,
response_model_exclude_defaults: Annotated[
bool,
Doc(
"""
Configuration passed to Pydantic to define if the response data
should have all the fields, including the ones that have the same value
as the default. This is different from `response_model_exclude_unset`
in that if the fields are set but contain the same default values,
they will be excluded from the response.
When `True`, default values are omitted from the response.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter).
"""
),
] = False,
response_model_exclude_none: Annotated[
bool,
Doc(
"""
Configuration passed to Pydantic to define if the response data should
exclude fields set to `None`.
This is much simpler (less smart) than `response_model_exclude_unset`
and `response_model_exclude_defaults`. You probably want to use one of
those two instead of this one, as those allow returning `None` values
when it makes sense.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_exclude_none).
"""
),
] = False,
include_in_schema: Annotated[
bool,
Doc(
"""
Include this *path operation* in the generated OpenAPI schema.
This affects the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-parameters-from-openapi).
"""
),
] = True,
response_class: Annotated[
type[Response],
Doc(
"""
Response class to be used for this *path operation*.
This will not be used if you return a response directly.
Read more about it in the
[FastAPI docs for Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/#redirectresponse).
"""
),
] = AirResponse,
name: Annotated[
str | None,
Doc(
"""
Name for this *path operation*. Only used internally.
"""
),
] = None,
callbacks: Annotated[
list[BaseRoute] | None,
Doc(
"""
List of *path operations* that will be used as OpenAPI callbacks.
This is only for OpenAPI documentation, the callbacks won't be used
directly.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for OpenAPI Callbacks](https://fastapi.tiangolo.com/advanced/openapi-callbacks/).
"""
),
] = None,
openapi_extra: Annotated[
dict[str, Any] | None,
Doc(
"""
Extra metadata to be included in the OpenAPI schema for this *path
operation*.
Read more about it in the
[FastAPI docs for Path Operation Advanced Configuration](https://fastapi.tiangolo.com/advanced/path-operation-advanced-configuration/#custom-openapi-path-operation-schema).
"""
),
] = None,
generate_unique_id_function: Annotated[
Callable[[APIRoute], str],
Doc(
"""
Customize the function used to generate unique IDs for the *path
operations* shown in the generated OpenAPI.
This is particularly useful when automatically generating clients or
SDKs for your API.
Read more about it in the
[FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function).
"""
),
] = generate_unique_id,
```
) -> Callable\[\[Callable[..., Any]\], Callable[..., Any]\]: """ Add a *path operation* using an HTTP PATCH operation. """
```
def decorator[**P, R](func: Callable[P, MaybeAwaitable[R]]) -> RouteCallable:
@wraps(func)
async def endpoint(*args: P.args, **kw: P.kwargs) -> Response:
result = func(*args, **kw)
if inspect.isawaitable(result):
result = await result
if isinstance(result, Response):
return result
return response_class(result)
decorated = super(AirRouter, self).patch(
path,
response_model=response_model,
status_code=status_code,
tags=tags,
dependencies=dependencies,
summary=summary,
description=description,
response_description=response_description,
responses=responses,
deprecated=deprecated,
operation_id=operation_id,
response_model_include=response_model_include,
response_model_exclude=response_model_exclude,
response_model_by_alias=response_model_by_alias,
response_model_exclude_unset=response_model_exclude_unset,
response_model_exclude_defaults=response_model_exclude_defaults,
response_model_exclude_none=response_model_exclude_none,
include_in_schema=include_in_schema,
response_class=response_class,
name=name,
callbacks=callbacks,
openapi_extra=openapi_extra,
generate_unique_id_function=generate_unique_id_function,
)(endpoint)
decorated.url = self._url_helper(name or endpoint.__name__)
return decorated
return decorator
```
```
### post
```
post( path, \*, response_model=None, status_code=None, tags=None, dependencies=None, summary=None, description=None, response_description="Successful Response", responses=None, deprecated=None, operation_id=None, response_model_include=None, response_model_exclude=None, response_model_by_alias=True, response_model_exclude_unset=False, response_model_exclude_defaults=False, response_model_exclude_none=False, include_in_schema=True, response_class=AirResponse, name=None, callbacks=None, openapi_extra=None, generate_unique_id_function=generate_unique_id, )
```
Add a *path operation* using an HTTP POST operation.
Source code in `src/air/routing.py`
```
def post( self, path: Annotated\[ str, Doc( """ The URL path to be used for this *path operation*.
```
For example, in `http://example.com/items`, the path is `/items`.
"""
),
],
*,
response_model: Annotated[
Any,
Doc(
"""
The type to use for the response.
It could be any valid Pydantic *field* type. So, it doesn't have to
be a Pydantic model, it could be other things, like a `list`, `dict`,
etc.
It will be used for:
* Documentation: the generated OpenAPI (and the UI at `/docs`) will
show it as the response (JSON Schema).
* Serialization: you could return an arbitrary object and the
`response_model` would be used to serialize that object into the
corresponding JSON.
* Filtering: the JSON sent to the client will only contain the data
(fields) defined in the `response_model`. If you returned an object
that contains an attribute `password` but the `response_model` does
not include that field, the JSON sent to the client would not have
that `password`.
* Validation: whatever you return will be serialized with the
`response_model`, converting any data as necessary to generate the
corresponding JSON. But if the data in the object returned is not
valid, that would mean a violation of the contract with the client,
so it's an error from the API developer. So, FastAPI will raise an
error and return a 500 error code (Internal Server Error).
Read more about it in the
[FastAPI docs for Response Model](https://fastapi.tiangolo.com/tutorial/response-model/).
"""
),
] = None,
status_code: Annotated[
int | None,
Doc(
"""
The default status code to be used for the response.
You could override the status code by returning a response directly.
Read more about it in the
[FastAPI docs for Response Status Code](https://fastapi.tiangolo.com/tutorial/response-status-code/).
"""
),
] = None,
tags: Annotated[
list[str | Enum] | None,
Doc(
"""
A list of tags to be applied to the *path operation*.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/#tags).
"""
),
] = None,
dependencies: Annotated[
Sequence[params.Depends] | None,
Doc(
"""
A list of dependencies (using `Depends()`) to be applied to the
*path operation*.
Read more about it in the
[FastAPI docs for Dependencies in path operation decorators](https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-in-path-operation-decorators/).
"""
),
] = None,
summary: Annotated[
str | None,
Doc(
"""
A summary for the *path operation*.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/).
"""
),
] = None,
description: Annotated[
str | None,
Doc(
"""
A description for the *path operation*.
If not provided, it will be extracted automatically from the docstring
of the *path operation function*.
It can contain Markdown.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/).
"""
),
] = None,
response_description: Annotated[
str,
Doc(
"""
The description for the default response.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
"""
),
] = "Successful Response",
responses: Annotated[
dict[int | str, dict[str, Any]] | None,
Doc(
"""
Additional responses that could be returned by this *path operation*.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
"""
),
] = None,
deprecated: Annotated[
bool | None,
Doc(
"""
Mark this *path operation* as deprecated.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
"""
),
] = None,
operation_id: Annotated[
str | None,
Doc(
"""
Custom operation ID to be used by this *path operation*.
By default, it is generated automatically.
If you provide a custom operation ID, you need to make sure it is
unique for the whole API.
You can customize the
operation ID generation with the parameter
`generate_unique_id_function` in the `FastAPI` class.
Read more about it in the
[FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function).
"""
),
] = None,
response_model_include: Annotated[
IncEx | None,
Doc(
"""
Configuration passed to Pydantic to include only certain fields in the
response data.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
"""
),
] = None,
response_model_exclude: Annotated[
IncEx | None,
Doc(
"""
Configuration passed to Pydantic to exclude certain fields in the
response data.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
"""
),
] = None,
response_model_by_alias: Annotated[
bool,
Doc(
"""
Configuration passed to Pydantic to define if the response model
should be serialized by alias when an alias is used.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
"""
),
] = True,
response_model_exclude_unset: Annotated[
bool,
Doc(
"""
Configuration passed to Pydantic to define if the response data
should have all the fields, including the ones that were not set and
have their default values. This is different from
`response_model_exclude_defaults` in that if the fields are set,
they will be included in the response, even if the value is the same
as the default.
When `True`, default values are omitted from the response.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter).
"""
),
] = False,
response_model_exclude_defaults: Annotated[
bool,
Doc(
"""
Configuration passed to Pydantic to define if the response data
should have all the fields, including the ones that have the same value
as the default. This is different from `response_model_exclude_unset`
in that if the fields are set but contain the same default values,
they will be excluded from the response.
When `True`, default values are omitted from the response.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter).
"""
),
] = False,
response_model_exclude_none: Annotated[
bool,
Doc(
"""
Configuration passed to Pydantic to define if the response data should
exclude fields set to `None`.
This is much simpler (less smart) than `response_model_exclude_unset`
and `response_model_exclude_defaults`. You probably want to use one of
those two instead of this one, as those allow returning `None` values
when it makes sense.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_exclude_none).
"""
),
] = False,
include_in_schema: Annotated[
bool,
Doc(
"""
Include this *path operation* in the generated OpenAPI schema.
This affects the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-parameters-from-openapi).
"""
),
] = True,
response_class: Annotated[
type[Response],
Doc(
"""
Response class to be used for this *path operation*.
This will not be used if you return a response directly.
Read more about it in the
[FastAPI docs for Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/#redirectresponse).
"""
),
] = AirResponse,
name: Annotated[
str | None,
Doc(
"""
Name for this *path operation*. Only used internally.
"""
),
] = None,
callbacks: Annotated[
list[BaseRoute] | None,
Doc(
"""
List of *path operations* that will be used as OpenAPI callbacks.
This is only for OpenAPI documentation, the callbacks won't be used
directly.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for OpenAPI Callbacks](https://fastapi.tiangolo.com/advanced/openapi-callbacks/).
"""
),
] = None,
openapi_extra: Annotated[
dict[str, Any] | None,
Doc(
"""
Extra metadata to be included in the OpenAPI schema for this *path
operation*.
Read more about it in the
[FastAPI docs for Path Operation Advanced Configuration](https://fastapi.tiangolo.com/advanced/path-operation-advanced-configuration/#custom-openapi-path-operation-schema).
"""
),
] = None,
generate_unique_id_function: Annotated[
Callable[[APIRoute], str],
Doc(
"""
Customize the function used to generate unique IDs for the *path
operations* shown in the generated OpenAPI.
This is particularly useful when automatically generating clients or
SDKs for your API.
Read more about it in the
[FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function).
"""
),
] = generate_unique_id,
```
) -> Callable\[\[Callable[..., Any]\], Callable[..., Any]\]: """ Add a *path operation* using an HTTP POST operation. """
```
def decorator[**P, R](func: Callable[P, MaybeAwaitable[R]]) -> RouteCallable:
@wraps(func)
async def endpoint(*args: P.args, **kw: P.kwargs) -> Response:
result = func(*args, **kw)
if inspect.isawaitable(result):
result = await result
if isinstance(result, Response):
return result
# Force HTML for non-Response results
return response_class(result)
decorated = super(AirRouter, self).post(
path,
response_model=response_model,
status_code=status_code,
tags=tags,
dependencies=dependencies,
summary=summary,
description=description,
response_description=response_description,
responses=responses,
deprecated=deprecated,
operation_id=operation_id,
response_model_include=response_model_include,
response_model_exclude=response_model_exclude,
response_model_by_alias=response_model_by_alias,
response_model_exclude_unset=response_model_exclude_unset,
response_model_exclude_defaults=response_model_exclude_defaults,
response_model_exclude_none=response_model_exclude_none,
include_in_schema=include_in_schema,
response_class=response_class,
name=name,
callbacks=callbacks,
openapi_extra=openapi_extra,
generate_unique_id_function=generate_unique_id_function,
)(endpoint)
decorated.url = self._url_helper(name or endpoint.__name__)
return decorated
return decorator
```
```
### put
```
put( path, \*, response_model=None, status_code=None, tags=None, dependencies=None, summary=None, description=None, response_description="Successful Response", responses=None, deprecated=None, operation_id=None, response_model_include=None, response_model_exclude=None, response_model_by_alias=True, response_model_exclude_unset=False, response_model_exclude_defaults=False, response_model_exclude_none=False, include_in_schema=True, response_class=AirResponse, name=None, callbacks=None, openapi_extra=None, generate_unique_id_function=generate_unique_id, )
```
Add a *path operation* using an HTTP PUT operation.
Source code in `src/air/routing.py`
```
def put( self, path: Annotated\[ str, Doc( """ The URL path to be used for this *path operation*.
```
For example, in `http://example.com/items`, the path is `/items`.
"""
),
],
*,
response_model: Annotated[
Any,
Doc(
"""
The type to use for the response.
It could be any valid Pydantic *field* type. So, it doesn't have to
be a Pydantic model, it could be other things, like a `list`, `dict`,
etc.
It will be used for:
* Documentation: the generated OpenAPI (and the UI at `/docs`) will
show it as the response (JSON Schema).
* Serialization: you could return an arbitrary object and the
`response_model` would be used to serialize that object into the
corresponding JSON.
* Filtering: the JSON sent to the client will only contain the data
(fields) defined in the `response_model`. If you returned an object
that contains an attribute `password` but the `response_model` does
not include that field, the JSON sent to the client would not have
that `password`.
* Validation: whatever you return will be serialized with the
`response_model`, converting any data as necessary to generate the
corresponding JSON. But if the data in the object returned is not
valid, that would mean a violation of the contract with the client,
so it's an error from the API developer. So, FastAPI will raise an
error and return a 500 error code (Internal Server Error).
Read more about it in the
[FastAPI docs for Response Model](https://fastapi.tiangolo.com/tutorial/response-model/).
"""
),
] = None,
status_code: Annotated[
int | None,
Doc(
"""
The default status code to be used for the response.
You could override the status code by returning a response directly.
Read more about it in the
[FastAPI docs for Response Status Code](https://fastapi.tiangolo.com/tutorial/response-status-code/).
"""
),
] = None,
tags: Annotated[
list[str | Enum] | None,
Doc(
"""
A list of tags to be applied to the *path operation*.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/#tags).
"""
),
] = None,
dependencies: Annotated[
Sequence[Depends] | None,
Doc(
"""
A list of dependencies (using `Depends()`) to be applied to the
*path operation*.
Read more about it in the
[FastAPI docs for Dependencies in path operation decorators](https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-in-path-operation-decorators/).
"""
),
] = None,
summary: Annotated[
str | None,
Doc(
"""
A summary for the *path operation*.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/).
"""
),
] = None,
description: Annotated[
str | None,
Doc(
"""
A description for the *path operation*.
If not provided, it will be extracted automatically from the docstring
of the *path operation function*.
It can contain Markdown.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/).
"""
),
] = None,
response_description: Annotated[
str,
Doc(
"""
The description for the default response.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
"""
),
] = "Successful Response",
responses: Annotated[
dict[int | str, dict[str, Any]] | None,
Doc(
"""
Additional responses that could be returned by this *path operation*.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
"""
),
] = None,
deprecated: Annotated[
bool | None,
Doc(
"""
Mark this *path operation* as deprecated.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
"""
),
] = None,
operation_id: Annotated[
str | None,
Doc(
"""
Custom operation ID to be used by this *path operation*.
By default, it is generated automatically.
If you provide a custom operation ID, you need to make sure it is
unique for the whole API.
You can customize the
operation ID generation with the parameter
`generate_unique_id_function` in the `FastAPI` class.
Read more about it in the
[FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function).
"""
),
] = None,
response_model_include: Annotated[
IncEx | None,
Doc(
"""
Configuration passed to Pydantic to include only certain fields in the
response data.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
"""
),
] = None,
response_model_exclude: Annotated[
IncEx | None,
Doc(
"""
Configuration passed to Pydantic to exclude certain fields in the
response data.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
"""
),
] = None,
response_model_by_alias: Annotated[
bool,
Doc(
"""
Configuration passed to Pydantic to define if the response model
should be serialized by alias when an alias is used.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
"""
),
] = True,
response_model_exclude_unset: Annotated[
bool,
Doc(
"""
Configuration passed to Pydantic to define if the response data
should have all the fields, including the ones that were not set and
have their default values. This is different from
`response_model_exclude_defaults` in that if the fields are set,
they will be included in the response, even if the value is the same
as the default.
When `True`, default values are omitted from the response.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter).
"""
),
] = False,
response_model_exclude_defaults: Annotated[
bool,
Doc(
"""
Configuration passed to Pydantic to define if the response data
should have all the fields, including the ones that have the same value
as the default. This is different from `response_model_exclude_unset`
in that if the fields are set but contain the same default values,
they will be excluded from the response.
When `True`, default values are omitted from the response.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#use-the-response_model_exclude_unset-parameter).
"""
),
] = False,
response_model_exclude_none: Annotated[
bool,
Doc(
"""
Configuration passed to Pydantic to define if the response data should
exclude fields set to `None`.
This is much simpler (less smart) than `response_model_exclude_unset`
and `response_model_exclude_defaults`. You probably want to use one of
those two instead of this one, as those allow returning `None` values
when it makes sense.
Read more about it in the
[FastAPI docs for Response Model - Return Type](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_exclude_none).
"""
),
] = False,
include_in_schema: Annotated[
bool,
Doc(
"""
Include this *path operation* in the generated OpenAPI schema.
This affects the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#exclude-parameters-from-openapi).
"""
),
] = True,
response_class: Annotated[
type[Response],
Doc(
"""
Response class to be used for this *path operation*.
This will not be used if you return a response directly.
Read more about it in the
[FastAPI docs for Custom Response - HTML, Stream, File, others](https://fastapi.tiangolo.com/advanced/custom-response/#redirectresponse).
"""
),
] = AirResponse,
name: Annotated[
str | None,
Doc(
"""
Name for this *path operation*. Only used internally.
"""
),
] = None,
callbacks: Annotated[
list[BaseRoute] | None,
Doc(
"""
List of *path operations* that will be used as OpenAPI callbacks.
This is only for OpenAPI documentation, the callbacks won't be used
directly.
It will be added to the generated OpenAPI (e.g. visible at `/docs`).
Read more about it in the
[FastAPI docs for OpenAPI Callbacks](https://fastapi.tiangolo.com/advanced/openapi-callbacks/).
"""
),
] = None,
openapi_extra: Annotated[
dict[str, Any] | None,
Doc(
"""
Extra metadata to be included in the OpenAPI schema for this *path
operation*.
Read more about it in the
[FastAPI docs for Path Operation Advanced Configuration](https://fastapi.tiangolo.com/advanced/path-operation-advanced-configuration/#custom-openapi-path-operation-schema).
"""
),
] = None,
generate_unique_id_function: Annotated[
Callable[[APIRoute], str],
Doc(
"""
Customize the function used to generate unique IDs for the *path
operations* shown in the generated OpenAPI.
This is particularly useful when automatically generating clients or
SDKs for your API.
Read more about it in the
[FastAPI docs about how to Generate Clients](https://fastapi.tiangolo.com/advanced/generate-clients/#custom-generate-unique-id-function).
"""
),
] = generate_unique_id,
```
) -> Callable\[\[Callable[..., Any]\], Callable[..., Any]\]: """ Add a *path operation* using an HTTP PUT operation. """
```
def decorator[**P, R](func: Callable[P, MaybeAwaitable[R]]) -> RouteCallable:
@wraps(func)
async def endpoint(*args: P.args, **kw: P.kwargs) -> Response:
result = func(*args, **kw)
if inspect.isawaitable(result):
result = await result
if isinstance(result, Response):
return result
return response_class(result)
decorated = super(AirRouter, self).put(
path,
response_model=response_model,
status_code=status_code,
tags=tags,
dependencies=dependencies,
summary=summary,
description=description,
response_description=response_description,
responses=responses,
deprecated=deprecated,
operation_id=operation_id,
response_model_include=response_model_include,
response_model_exclude=response_model_exclude,
response_model_by_alias=response_model_by_alias,
response_model_exclude_unset=response_model_exclude_unset,
response_model_exclude_defaults=response_model_exclude_defaults,
response_model_exclude_none=response_model_exclude_none,
include_in_schema=include_in_schema,
response_class=response_class,
name=name,
callbacks=callbacks,
openapi_extra=openapi_extra,
generate_unique_id_function=generate_unique_id_function,
)(endpoint)
decorated.url = self._url_helper(name or endpoint.__name__)
return decorated
return decorator
```
```
## RouteCallable
Bases: `Protocol`
Protocol for route functions.
This protocol represents the interface of functions after being decorated
by route decorators like @app.get(), @app.post(), or @app.page(). The decorator
adds a .url() method to the function, allowing programmatic URL generation.
Example
@app.get("/users/{user_id}")
def get_user(user_id: int) -> air.H1:
return air.H1(f"User {user_id}")
### The decorated function now has a .url() method
url = get_user.url(user_id=123) # Returns: "/users/123"
## RouterMixin
### get
```
get(\*args, \*\*kwargs)
```
Stub for type checking - implemented by subclasses.
Source code in `src/air/routing.py`
```
def get(self, \*args: Any, \*\*kwargs: Any) -> Any: """Stub for type checking - implemented by subclasses.""" raise NotImplementedError
```
### page
```
page(func)
```
Decorator that creates a GET route using the function name as the path.
If the name of the function is "index", then the route is "/".
Example:
```
import air
app = air.Air() router = air.AirRouter()
@app.page def index(): # route is "/" return air.H1("I am the home page")
@router.page def data(): # route is "/data" return air.H1("I am the data page")
@router.page def about_us(): # route is "/about-us" return air.H1("I am the about page")
app.include_router(router)
```
Source code in `src/air/routing.py`
```
def page(self, func: FunctionType) -> RouteCallable: """Decorator that creates a GET route using the function name as the path.
```
If the name of the function is "index", then the route is "/".
Example:
import air
app = air.Air()
router = air.AirRouter()
@app.page
def index(): # route is "/"
return air.H1("I am the home page")
@router.page
def data(): # route is "/data"
return air.H1("I am the data page")
@router.page
def about_us(): # route is "/about-us"
return air.H1("I am the about page")
app.include_router(router)
"""
page_path = compute_page_path(func.__name__, separator=self.path_separator)
# Pin the route's response_class for belt-and-suspenders robustness
return self.get(page_path)(func)
```
```
### url_path_for
```
url_path_for(name, \*\*params)
```
Stub for type checking - implemented by subclasses.
Source code in `src/air/routing.py`
```
def url_path_for(self, name: str, \*\*params: Any) -> str: """Stub for type checking - implemented by subclasses.""" raise NotImplementedError
```
```
# Templating
Air loves Jinja!
A common pattern is to use a Jinja template as the project base and then use Air Tags for individual content.
## JinjaRenderer
```
JinjaRenderer(directory, context_processors=None, env=None)
```
Template renderer to make Jinja easier in Air.
Parameters:
| Name | Type | Description | Default |
| -------------------- | ----------------------------------------------- | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `directory` | \`str | PathLike[str] | Sequence\[str |
| `context_processors` | \`list\[Callable\[[Request], dict[str, Any]\]\] | None\` | A list of Jinja-style context processors, functions that automatically injects variables or functions into the template context so they're available in every rendered template without passing them explicitly. |
| `env` | \`Environment | None\` | The env is the central Jinja object that holds configuration, filters, globals, and template loading settings, and is responsible for compiling and rendering templates. |
Example:
```
# Instantiate the render callable
jinja = JinjaRenderer('templates')
# Use for returning Jinja from views
@app.get('/')
async def home(request: Request):
return jinja(
request,
'home.html',
context={'id': 5}
)
# Can also pass in kwargs, which will be added to the context:
return jinja(
request,
'home.html',
name='Parmesan'
)
# Will render Air Tags sent into Jinja context
return jinja(
request,
'home.html',
content=air.Article(air.P('Cheddar'))
)
```
Source code in `src/air/templating.py`
```
def __init__(
self,
directory: str | PathLike[str] | Sequence[str | PathLike[str]],
context_processors: list[Callable[[StarletteRequest], dict[str, Any]]] | None = None,
env: jinja2.Environment | None = None,
) -> None:
"""Initialize with template directory path"""
self.templates = Jinja2Templates(directory=directory, context_processors=context_processors, env=env)
```
### __call__
```
__call__(request, name, context=None, **kwargs)
```
Render template with request and context. If an Air Tag is found in the context, try to render it.
Source code in `src/air/templating.py`
```
def __call__(
self,
request: Request,
name: str,
context: dict[Any, Any] | None = None,
**kwargs: Any,
) -> _TemplateResponse:
"""Render template with request and context. If an Air Tag
is found in the context, try to render it.
"""
if context is None:
context = {}
if kwargs:
context |= kwargs
# Attempt to render any Tags in the context
context = {k: _jinja_context_item(v) for k, v in context.items()}
return self.templates.TemplateResponse(request=request, name=name, context=context)
```
## Renderer
```
Renderer(
directory,
context_processors=None,
env=None,
package=None,
)
```
Template/Tag renderer to make composing pluggable functions easier.
Parameters:
| Name | Type | Description | Default |
| -------------------- | ----------------------------------------------- | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `directory` | \`str | PathLike[str] | Sequence\[str |
| `context_processors` | \`list\[Callable\[[Request], dict[str, Any]\]\] | None\` | A list of Jinja-style context processors, functions that automatically injects variables or functions into the template context so they're available in every rendered template without passing them explicitly. |
| `env` | \`Environment | None\` | The env is the central Jinja object that holds configuration, filters, globals, and template loading settings, and is responsible for compiling and rendering templates. |
Example:
```
import air
app = air.Air()
# Instantiate the render callable
render = air.Renderer('templates')
# Use for returning Jinja from views
@app.get('/')
async def home(request: Request):
return render(
name='home.html',
request=request,
context={'id': 5}
)
# Will render name of Air Tags
return render(
request,
'components.home',
context={'id': 5}
)
# Will render callables to HTML
return render(
air.layouts.mvpcss,
air.Title("Test Page"),
air.H1("Hello, World")
)
```
Source code in `src/air/templating.py`
```
def __init__(
self,
directory: str | PathLike[str] | Sequence[str | PathLike[str]],
context_processors: list[Callable[[StarletteRequest], dict[str, Any]]] | None = None,
env: jinja2.Environment | None = None,
package: str | None = None,
) -> None:
"""Initialize with template directory path"""
self.templates = Jinja2Templates(directory=directory, context_processors=context_processors, env=env)
self.package = package
```
### __call__
```
__call__(
name, *children, request=None, context=None, **kwargs
)
```
Render template with request and context. If an Air Tag is found in the context, try to render it.
Source code in `src/air/templating.py`
```
def __call__(
self,
name: str | Callable,
*children: Any,
request: Request | None = None,
context: dict[Any, Any] | None = None,
**kwargs: Any,
) -> str | _TemplateResponse:
"""Render template with request and context. If an Air Tag
is found in the context, try to render it.
"""
context = self._prepare_context(context, kwargs)
if callable(name):
assert not isinstance(name, str)
result = name(**context)
if isinstance(result, str):
return result
if hasattr(result, "render"):
return result.render()
msg = "Callable in name arg must a string or object with a render method."
raise TypeError(msg)
assert isinstance(name, str)
if name.endswith((".html", ".jinja")):
return self._render_template(name, request, context)
if "." in name:
return self._render_tag_callable(name, children, request, context)
msg = "No callable or Jinja template found."
raise RenderException(msg)
```
Utils
## compute_page_path
```
compute_page_path(endpoint_name, separator='-')
```
index -> '/', otherwise '/name-with-dashes'.
Source code in `src/air/utils.py`
```
def compute_page_path(endpoint_name: str, separator: Literal["/", "-"] = "-") -> str:
"""index -> '/', otherwise '/name-with-dashes'."""
return "/" if endpoint_name == "index" else f"/{endpoint_name.replace('_', separator)}"
```
# SVG
In the spirit of helping our users, every **Air SVG Tag** has copious documentation—enough that sometimes it breaks the documentation build process. Therefore, **Air SVG Tag** that directly correspond to their SVG equivalents can be found in smaller, easier-to-compile pages.
- [SVG Air Tags A-D](svg-tags-a-d/)
- [SVG Air Tags E-M](svg-tags-e-m/)
- [SVG Air Tags N-S](svg-tags-n-s/)
- [SVG Air Tags T-Z](svg-tags-t-z/)
What remains on this page are core **Air SVG Tag** that either have great utility or are base classes for other tags.
Air is proud to provide first class SVG support. The entire SVG specification is supported.
## CaseTag
```
CaseTag(*children, **kwargs)
```
Bases: `BaseTag`
This is for case-sensitive tags like those used in SVG generation.
Source code in `src/air/tags/models/base.py`
```
def __init__(self, *children: Renderable, **kwargs: AttributeType) -> None:
"""Initialize a tag with renderable children and HTML attributes.
Args:
children: Renderable objects that become the tag's inner content.
kwargs: Attribute names and values applied to the tag element.
"""
self._name = self.__class__.__name__
self._module = self.__class__.__module__
self._children: tuple[Renderable, ...] = children
self._attrs: TagAttributesType = kwargs
```
# SVG Tags A-D
Air is proud to provide first class SVG support. The entire SVG specification is supported.
## A
```
A(
*children,
href=None,
target=None,
download=None,
hreflang=None,
ping=None,
referrerpolicy=None,
rel=None,
type=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
Defines an SVG hyperlink
Parameters:
| Name | Type | Description | Default |
| ---------------- | --------------- | ----------------------------------------- | -------------------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `href` | \`str | None\` | Hyperlink target URL. |
| `target` | \`str | None\` | Where to display linked URL (\_self |
| `download` | \`str | None\` | Instructs browser to download instead of navigate. |
| `hreflang` | \`str | None\` | Human language of the linked URL. |
| `ping` | \`str | None\` | Space-separated list of URLs for tracking. |
| `referrerpolicy` | \`str | None\` | Referrer policy when fetching the URL. |
| `rel` | \`str | None\` | Relationship to target object. |
| `type` | \`str | None\` | MIME type of linked URL. |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
href: str | None = None,
target: str | None = None,
download: str | None = None,
hreflang: str | None = None,
ping: str | None = None,
referrerpolicy: str | None = None,
rel: str | None = None,
type: str | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## Animate
```
Animate(
*children,
attributeName=None,
attributeType=None,
values=None,
dur=None,
repeatCount=None,
repeatDur=None,
from_=None,
to=None,
by=None,
begin=None,
end=None,
calcMode=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
Defines animation on an SVG element
Parameters:
| Name | Type | Description | Default |
| --------------- | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `attributeName` | \`str | None\` | Target attribute to animate. |
| `attributeType` | \`str | None\` | Type of target attribute. |
| `values` | \`str | None\` | Values to animate through. |
| `dur` | \`str | None\` | Total animation duration. |
| `repeatCount` | \`str | float | None\` |
| `repeatDur` | \`str | None\` | Total duration for repeating. |
| `from_` | \`str | None\` | Starting value (from is reserved). |
| `to` | \`str | None\` | Ending value. |
| `by` | \`str | None\` | Relative animation value. |
| `begin` | \`str | None\` | Animation start time. |
| `end` | \`str | None\` | Animation end time. |
| `calcMode` | \`str | None\` | Interpolation mode (discrete |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
attributeName: str | None = None,
attributeType: str | None = None,
values: str | None = None,
dur: str | None = None,
repeatCount: str | float | None = None,
repeatDur: str | None = None,
from_: str | None = None,
to: str | None = None,
by: str | None = None,
begin: str | None = None,
end: str | None = None,
calcMode: str | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## AnimateMotion
```
AnimateMotion(
*children,
path=None,
keyPoints=None,
rotate=None,
dur=None,
repeatCount=None,
begin=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
Defines how an element moves along a motion path
Parameters:
| Name | Type | Description | Default |
| ------------- | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `path` | \`str | None\` | Motion path using path syntax. |
| `keyPoints` | \`str | None\` | Progress points along path (0-1 range). |
| `rotate` | \`str | float | None\` |
| `dur` | \`str | None\` | Total animation duration. |
| `repeatCount` | \`str | float | None\` |
| `begin` | \`str | None\` | Animation start time. |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
path: str | None = None,
keyPoints: str | None = None,
rotate: str | float | None = None,
dur: str | None = None,
repeatCount: str | float | None = None,
begin: str | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## AnimateTransform
```
AnimateTransform(
*children,
type=None,
by=None,
from_=None,
to=None,
dur=None,
repeatCount=None,
begin=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
Animates transform attributes on an element
Parameters:
| Name | Type | Description | Default |
| ------------- | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `type` | \`str | None\` | Transformation type (rotate |
| `by` | \`str | None\` | Relative animation value. |
| `from_` | \`str | None\` | Starting transformation value. |
| `to` | \`str | None\` | Ending transformation value. |
| `dur` | \`str | None\` | Total animation duration. |
| `repeatCount` | \`str | float | None\` |
| `begin` | \`str | None\` | Animation start time. |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
type: str | None = None,
by: str | None = None,
from_: str | None = None,
to: str | None = None,
dur: str | None = None,
repeatCount: str | float | None = None,
begin: str | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## Circle
```
Circle(
*children,
cx=None,
cy=None,
r=None,
pathLength=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
Defines a circle
Parameters:
| Name | Type | Description | Default |
| ------------ | --------------- | ----------------------------------------- | ----------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `cx` | \`str | float | None\` |
| `cy` | \`str | float | None\` |
| `r` | \`str | float | None\` |
| `pathLength` | \`float | None\` | Total circumference length in user units. |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
cx: str | float | None = None,
cy: str | float | None = None,
r: str | float | None = None,
pathLength: float | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## ClipPath
```
ClipPath(
*children,
clipPathUnits=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
Defines a clipping path
Parameters:
| Name | Type | Description | Default |
| --------------- | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `clipPathUnits` | \`str | None\` | Coordinate system (userSpaceOnUse |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
clipPathUnits: str | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## Defs
```
Defs(*children, class_=None, id=None, style=None, **kwargs)
```
Bases: `CaseTag`
Defines reusable objects
Parameters:
| Name | Type | Description | Default |
| ---------- | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## Desc
```
Desc(*children, class_=None, id=None, style=None, **kwargs)
```
Bases: `CaseTag`
Defines a description of an element
Parameters:
| Name | Type | Description | Default |
| ---------- | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
# SVG Tags E-M
Air is proud to provide first class SVG support. The entire SVG specification is supported.
## Ellipse
```
Ellipse(
*children,
cx=None,
cy=None,
rx=None,
ry=None,
pathLength=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
Defines an ellipse
Parameters:
| Name | Type | Description | Default |
| ------------ | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `cx` | \`str | float | None\` |
| `cy` | \`str | float | None\` |
| `rx` | \`str | float | None\` |
| `ry` | \`str | float | None\` |
| `pathLength` | \`float | None\` | Total path length in user units. |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
cx: str | float | None = None,
cy: str | float | None = None,
rx: str | float | None = None,
ry: str | float | None = None,
pathLength: float | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## FeBlend
```
FeBlend(
*children,
in_=None,
in2=None,
mode=None,
result=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
Defines image blending
Parameters:
| Name | Type | Description | Default |
| ---------- | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `in_` | \`str | None\` | Input image reference. |
| `in2` | \`str | None\` | Second input image reference. |
| `mode` | \`str | None\` | Blending mode. |
| `result` | \`str | None\` | Result identifier. |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
in_: str | None = None,
in2: str | None = None,
mode: str | None = None,
result: str | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## FeColorMatrix
```
FeColorMatrix(
*children,
in_=None,
type=None,
values=None,
result=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
Applies a matrix transformation on color values
Parameters:
| Name | Type | Description | Default |
| ---------- | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `in_` | \`str | None\` | Input image reference. |
| `type` | \`str | None\` | Matrix type (matrix |
| `values` | \`str | None\` | Matrix values. |
| `result` | \`str | None\` | Result identifier. |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
in_: str | None = None,
type: str | None = None,
values: str | None = None,
result: str | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## FeComponentTransfer
```
FeComponentTransfer(
*children,
in_=None,
result=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
Performs component-wise remapping of data
Parameters:
| Name | Type | Description | Default |
| ---------- | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `in_` | \`str | None\` | Input image reference. |
| `result` | \`str | None\` | Result identifier. |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
in_: str | None = None,
result: str | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## FeComposite
```
FeComposite(
*children,
in_=None,
in2=None,
operator=None,
k1=None,
k2=None,
k3=None,
k4=None,
result=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
Performs image compositing
Parameters:
| Name | Type | Description | Default |
| ---------- | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `in_` | \`str | None\` | Input image reference. |
| `in2` | \`str | None\` | Second input image reference. |
| `operator` | \`str | None\` | Compositing operation. |
| `k1` | \`float | None\` | Coefficient for arithmetic operation. |
| `k2` | \`float | None\` | Coefficient for arithmetic operation. |
| `k3` | \`float | None\` | Coefficient for arithmetic operation. |
| `k4` | \`float | None\` | Coefficient for arithmetic operation. |
| `result` | \`str | None\` | Result identifier. |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
in_: str | None = None,
in2: str | None = None,
operator: str | None = None,
k1: float | None = None,
k2: float | None = None,
k3: float | None = None,
k4: float | None = None,
result: str | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## FeConvolveMatrix
```
FeConvolveMatrix(
*children,
in_=None,
order=None,
kernelMatrix=None,
divisor=None,
bias=None,
targetX=None,
targetY=None,
edgeMode=None,
preserveAlpha=None,
result=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
Applies a matrix convolution filter
Parameters:
| Name | Type | Description | Default |
| --------------- | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `in_` | \`str | None\` | Input image reference. |
| `order` | \`str | None\` | Matrix dimensions. |
| `kernelMatrix` | \`str | None\` | Matrix values. |
| `divisor` | \`float | None\` | Divisor for matrix sum. |
| `bias` | \`float | None\` | Bias value. |
| `targetX` | \`int | None\` | Target X position. |
| `targetY` | \`int | None\` | Target Y position. |
| `edgeMode` | \`str | None\` | Edge handling mode. |
| `preserveAlpha` | \`str | None\` | Preserve alpha channel. |
| `result` | \`str | None\` | Result identifier. |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
in_: str | None = None,
order: str | None = None,
kernelMatrix: str | None = None,
divisor: float | None = None,
bias: float | None = None,
targetX: int | None = None,
targetY: int | None = None,
edgeMode: str | None = None,
preserveAlpha: str | None = None,
result: str | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## FeDiffuseLighting
```
FeDiffuseLighting(
*children,
in_=None,
surfaceScale=None,
diffuseConstant=None,
kernelUnitLength=None,
result=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
Lights an image using diffuse lighting
Parameters:
| Name | Type | Description | Default |
| ------------------ | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `in_` | \`str | None\` | Input image reference. |
| `surfaceScale` | \`float | None\` | Surface height scale. |
| `diffuseConstant` | \`float | None\` | Diffuse lighting constant. |
| `kernelUnitLength` | \`str | None\` | Kernel unit length. |
| `result` | \`str | None\` | Result identifier. |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
in_: str | None = None,
surfaceScale: float | None = None,
diffuseConstant: float | None = None,
kernelUnitLength: str | None = None,
result: str | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## FeDisplacementMap
```
FeDisplacementMap(
*children,
in_=None,
in2=None,
scale=None,
xChannelSelector=None,
yChannelSelector=None,
result=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
Displaces an image using another image
Parameters:
| Name | Type | Description | Default |
| ------------------ | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `in_` | \`str | None\` | Input image reference. |
| `in2` | \`str | None\` | Displacement map reference. |
| `scale` | \`float | None\` | Displacement scale factor. |
| `xChannelSelector` | \`str | None\` | X displacement channel (R |
| `yChannelSelector` | \`str | None\` | Y displacement channel (R |
| `result` | \`str | None\` | Result identifier. |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
in_: str | None = None,
in2: str | None = None,
scale: float | None = None,
xChannelSelector: str | None = None,
yChannelSelector: str | None = None,
result: str | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## FeDistantLight
```
FeDistantLight(
*children,
azimuth=None,
elevation=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
Defines a distant light source
Parameters:
| Name | Type | Description | Default |
| ----------- | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `azimuth` | \`str | float | None\` |
| `elevation` | \`str | float | None\` |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
azimuth: str | float | None = None,
elevation: str | float | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## FeDropShadow
```
FeDropShadow(
*children,
dx=None,
dy=None,
stdDeviation=None,
flood_color=None,
flood_opacity=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
Creates a drop shadow effect
Parameters:
| Name | Type | Description | Default |
| --------------- | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `dx` | \`str | float | None\` |
| `dy` | \`str | float | None\` |
| `stdDeviation` | \`str | float | None\` |
| `flood_color` | \`str | None\` | Shadow color. |
| `flood_opacity` | \`str | float | None\` |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
dx: str | float | None = None,
dy: str | float | None = None,
stdDeviation: str | float | None = None,
flood_color: str | None = None,
flood_opacity: str | float | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## FeFlood
```
FeFlood(
*children,
flood_color=None,
flood_opacity=None,
result=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
Fills the filter region with a color
Parameters:
| Name | Type | Description | Default |
| --------------- | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `flood_color` | \`str | None\` | Fill color. |
| `flood_opacity` | \`str | float | None\` |
| `result` | \`str | None\` | Result identifier. |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
flood_color: str | None = None,
flood_opacity: str | float | None = None,
result: str | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## FeFuncA
```
FeFuncA(
*children,
type=None,
tableValues=None,
slope=None,
intercept=None,
amplitude=None,
exponent=None,
offset=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
Defines the alpha transfer function
Parameters:
| Name | Type | Description | Default |
| ------------- | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `type` | \`str | None\` | Transfer function type. |
| `tableValues` | \`str | None\` | Lookup table values. |
| `slope` | \`float | None\` | Linear function slope. |
| `intercept` | \`float | None\` | Linear function intercept. |
| `amplitude` | \`float | None\` | Gamma function amplitude. |
| `exponent` | \`float | None\` | Gamma function exponent. |
| `offset` | \`float | None\` | Gamma function offset. |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
type: str | None = None,
tableValues: str | None = None,
slope: float | None = None,
intercept: float | None = None,
amplitude: float | None = None,
exponent: float | None = None,
offset: float | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## FeFuncB
```
FeFuncB(
*children,
type=None,
tableValues=None,
slope=None,
intercept=None,
amplitude=None,
exponent=None,
offset=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
Defines the blue transfer function
Parameters:
| Name | Type | Description | Default |
| ------------- | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `type` | \`str | None\` | Transfer function type. |
| `tableValues` | \`str | None\` | Lookup table values. |
| `slope` | \`float | None\` | Linear function slope. |
| `intercept` | \`float | None\` | Linear function intercept. |
| `amplitude` | \`float | None\` | Gamma function amplitude. |
| `exponent` | \`float | None\` | Gamma function exponent. |
| `offset` | \`float | None\` | Gamma function offset. |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
type: str | None = None,
tableValues: str | None = None,
slope: float | None = None,
intercept: float | None = None,
amplitude: float | None = None,
exponent: float | None = None,
offset: float | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## FeFuncG
```
FeFuncG(
*children,
type=None,
tableValues=None,
slope=None,
intercept=None,
amplitude=None,
exponent=None,
offset=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
Defines the green transfer function
Parameters:
| Name | Type | Description | Default |
| ------------- | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `type` | \`str | None\` | Transfer function type. |
| `tableValues` | \`str | None\` | Lookup table values. |
| `slope` | \`float | None\` | Linear function slope. |
| `intercept` | \`float | None\` | Linear function intercept. |
| `amplitude` | \`float | None\` | Gamma function amplitude. |
| `exponent` | \`float | None\` | Gamma function exponent. |
| `offset` | \`float | None\` | Gamma function offset. |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
type: str | None = None,
tableValues: str | None = None,
slope: float | None = None,
intercept: float | None = None,
amplitude: float | None = None,
exponent: float | None = None,
offset: float | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## FeFuncR
```
FeFuncR(
*children,
type=None,
tableValues=None,
slope=None,
intercept=None,
amplitude=None,
exponent=None,
offset=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
Defines the red transfer function
Parameters:
| Name | Type | Description | Default |
| ------------- | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `type` | \`str | None\` | Transfer function type. |
| `tableValues` | \`str | None\` | Lookup table values. |
| `slope` | \`float | None\` | Linear function slope. |
| `intercept` | \`float | None\` | Linear function intercept. |
| `amplitude` | \`float | None\` | Gamma function amplitude. |
| `exponent` | \`float | None\` | Gamma function exponent. |
| `offset` | \`float | None\` | Gamma function offset. |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
type: str | None = None,
tableValues: str | None = None,
slope: float | None = None,
intercept: float | None = None,
amplitude: float | None = None,
exponent: float | None = None,
offset: float | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## FeGaussianBlur
```
FeGaussianBlur(
*children,
in_=None,
stdDeviation=None,
edgeMode=None,
result=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
Applies Gaussian blur to an image
Parameters:
| Name | Type | Description | Default |
| -------------- | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `in_` | \`str | None\` | Input image reference. |
| `stdDeviation` | \`str | float | None\` |
| `edgeMode` | \`str | None\` | Edge handling during blur. |
| `result` | \`str | None\` | Result identifier. |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
in_: str | None = None,
stdDeviation: str | float | None = None,
edgeMode: str | None = None,
result: str | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## FeImage
```
FeImage(
*children,
href=None,
preserveAspectRatio=None,
crossorigin=None,
result=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
Refers to an external image
Parameters:
| Name | Type | Description | Default |
| --------------------- | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `href` | \`str | None\` | URL to image file. |
| `preserveAspectRatio` | \`str | None\` | Image scaling control. |
| `crossorigin` | \`str | None\` | CORS credentials flag. |
| `result` | \`str | None\` | Result identifier. |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
href: str | None = None,
preserveAspectRatio: str | None = None,
crossorigin: str | None = None,
result: str | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## FeMerge
```
FeMerge(
*children,
result=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
Merges multiple filter nodes
Parameters:
| Name | Type | Description | Default |
| ---------- | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `result` | \`str | None\` | Result identifier. |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
result: str | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## FeMergeNode
```
FeMergeNode(
*children,
in_=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
Defines a node for feMerge
Parameters:
| Name | Type | Description | Default |
| ---------- | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `in_` | \`str | None\` | Input image reference. |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
in_: str | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## FeMorphology
```
FeMorphology(
*children,
in_=None,
operator=None,
radius=None,
result=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
Applies morphological operations
Parameters:
| Name | Type | Description | Default |
| ---------- | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `in_` | \`str | None\` | Input image reference. |
| `operator` | \`str | None\` | Morphology operator (erode |
| `radius` | \`str | float | None\` |
| `result` | \`str | None\` | Result identifier. |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
in_: str | None = None,
operator: str | None = None,
radius: str | float | None = None,
result: str | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## FeOffset
```
FeOffset(
*children,
in_=None,
dx=None,
dy=None,
result=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
Offsets an image
Parameters:
| Name | Type | Description | Default |
| ---------- | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `in_` | \`str | None\` | Input graphic reference. |
| `dx` | \`str | float | None\` |
| `dy` | \`str | float | None\` |
| `result` | \`str | None\` | Result identifier. |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
in_: str | None = None,
dx: str | float | None = None,
dy: str | float | None = None,
result: str | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## FePointLight
```
FePointLight(
*children,
x=None,
y=None,
z=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
Defines a point light source
Parameters:
| Name | Type | Description | Default |
| ---------- | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `x` | \`str | float | None\` |
| `y` | \`str | float | None\` |
| `z` | \`str | float | None\` |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
x: str | float | None = None,
y: str | float | None = None,
z: str | float | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## FeSpecularLighting
```
FeSpecularLighting(
*children,
in_=None,
surfaceScale=None,
specularConstant=None,
specularExponent=None,
kernelUnitLength=None,
result=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
Lights an image using specular lighting
Parameters:
| Name | Type | Description | Default |
| ------------------ | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `in_` | \`str | None\` | Input image reference. |
| `surfaceScale` | \`float | None\` | Surface height scale. |
| `specularConstant` | \`float | None\` | Specular lighting constant. |
| `specularExponent` | \`float | None\` | Specular lighting exponent. |
| `kernelUnitLength` | \`str | None\` | Kernel unit length. |
| `result` | \`str | None\` | Result identifier. |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
in_: str | None = None,
surfaceScale: float | None = None,
specularConstant: float | None = None,
specularExponent: float | None = None,
kernelUnitLength: str | None = None,
result: str | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## FeSpotLight
```
FeSpotLight(
*children,
x=None,
y=None,
z=None,
pointsAtX=None,
pointsAtY=None,
pointsAtZ=None,
specularExponent=None,
limitingConeAngle=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
Defines a spot light source
Parameters:
| Name | Type | Description | Default |
| ------------------- | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `x` | \`str | float | None\` |
| `y` | \`str | float | None\` |
| `z` | \`str | float | None\` |
| `pointsAtX` | \`str | float | None\` |
| `pointsAtY` | \`str | float | None\` |
| `pointsAtZ` | \`str | float | None\` |
| `specularExponent` | \`float | None\` | Focus control for light source. |
| `limitingConeAngle` | \`float | None\` | Angle of spot light cone. |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
x: str | float | None = None,
y: str | float | None = None,
z: str | float | None = None,
pointsAtX: str | float | None = None,
pointsAtY: str | float | None = None,
pointsAtZ: str | float | None = None,
specularExponent: float | None = None,
limitingConeAngle: float | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## FeTile
```
FeTile(
*children,
in_=None,
result=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
Tiles an image to fill a rectangle
Parameters:
| Name | Type | Description | Default |
| ---------- | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `in_` | \`str | None\` | Input image reference. |
| `result` | \`str | None\` | Result identifier. |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
in_: str | None = None,
result: str | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## FeTurbulence
```
FeTurbulence(
*children,
baseFrequency=None,
numOctaves=None,
seed=None,
stitchTiles=None,
type=None,
result=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
Creates turbulence noise
Parameters:
| Name | Type | Description | Default |
| --------------- | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `baseFrequency` | \`str | float | None\` |
| `numOctaves` | \`int | None\` | Number of noise octaves. |
| `seed` | \`float | None\` | Random seed for turbulence. |
| `stitchTiles` | \`str | None\` | Tile stitching mode (stitch |
| `type` | \`str | None\` | Turbulence type (fractalNoise |
| `result` | \`str | None\` | Result identifier. |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
baseFrequency: str | float | None = None,
numOctaves: int | None = None,
seed: float | None = None,
stitchTiles: str | None = None,
type: str | None = None,
result: str | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## Filter
```
Filter(
*children,
x=None,
y=None,
width=None,
height=None,
filterUnits=None,
primitiveUnits=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
Defines a filter effect
Parameters:
| Name | Type | Description | Default |
| ---------------- | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `x` | \`str | float | None\` |
| `y` | \`str | float | None\` |
| `width` | \`str | float | None\` |
| `height` | \`str | float | None\` |
| `filterUnits` | \`str | None\` | Coordinate system for position/size. |
| `primitiveUnits` | \`str | None\` | Coordinate system for primitives. |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
x: str | float | None = None,
y: str | float | None = None,
width: str | float | None = None,
height: str | float | None = None,
filterUnits: str | None = None,
primitiveUnits: str | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## ForeignObject
```
ForeignObject(
*children,
x=None,
y=None,
width=None,
height=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
Allows inclusion of foreign XML
Parameters:
| Name | Type | Description | Default |
| ---------- | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `x` | \`str | float | None\` |
| `y` | \`str | float | None\` |
| `width` | \`str | float | None\` |
| `height` | \`str | float | None\` |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
x: str | float | None = None,
y: str | float | None = None,
width: str | float | None = None,
height: str | float | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## G
```
G(*children, class_=None, id=None, style=None, **kwargs)
```
Bases: `CaseTag`
Groups SVG elements
Parameters:
| Name | Type | Description | Default |
| ---------- | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## Image
```
Image(
*children,
x=None,
y=None,
width=None,
height=None,
href=None,
preserveAspectRatio=None,
crossorigin=None,
decoding=None,
fetchpriority=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
Embeds an image
Parameters:
| Name | Type | Description | Default |
| --------------------- | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `x` | \`str | float | None\` |
| `y` | \`str | float | None\` |
| `width` | \`str | float | None\` |
| `height` | \`str | float | None\` |
| `href` | \`str | None\` | URL to image file. |
| `preserveAspectRatio` | \`str | None\` | Image scaling control. |
| `crossorigin` | \`str | None\` | CORS credentials flag. |
| `decoding` | \`str | None\` | Image decoding hint. |
| `fetchpriority` | \`str | None\` | Fetch priority hint (experimental). |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
x: str | float | None = None,
y: str | float | None = None,
width: str | float | None = None,
height: str | float | None = None,
href: str | None = None,
preserveAspectRatio: str | None = None,
crossorigin: str | None = None,
decoding: str | None = None,
fetchpriority: str | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## Line
```
Line(
*children,
x1=None,
y1=None,
x2=None,
y2=None,
pathLength=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
Defines a line
Parameters:
| Name | Type | Description | Default |
| ------------ | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `x1` | \`str | float | None\` |
| `y1` | \`str | float | None\` |
| `x2` | \`str | float | None\` |
| `y2` | \`str | float | None\` |
| `pathLength` | \`float | None\` | Total path length in user units. |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
x1: str | float | None = None,
y1: str | float | None = None,
x2: str | float | None = None,
y2: str | float | None = None,
pathLength: float | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## LinearGradient
```
LinearGradient(
*children,
x1=None,
y1=None,
x2=None,
y2=None,
gradientUnits=None,
gradientTransform=None,
href=None,
spreadMethod=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
Defines a linear gradient
Parameters:
| Name | Type | Description | Default |
| ------------------- | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `x1` | \`str | float | None\` |
| `y1` | \`str | float | None\` |
| `x2` | \`str | float | None\` |
| `y2` | \`str | float | None\` |
| `gradientUnits` | \`str | None\` | Coordinate system. |
| `gradientTransform` | \`str | None\` | Additional transformation. |
| `href` | \`str | None\` | Reference to template gradient. |
| `spreadMethod` | \`str | None\` | Gradient behavior outside bounds. |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
x1: str | float | None = None,
y1: str | float | None = None,
x2: str | float | None = None,
y2: str | float | None = None,
gradientUnits: str | None = None,
gradientTransform: str | None = None,
href: str | None = None,
spreadMethod: str | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## Marker
```
Marker(
*children,
markerWidth=None,
markerHeight=None,
markerUnits=None,
refX=None,
refY=None,
orient=None,
viewBox=None,
preserveAspectRatio=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
Defines a graphic for drawing on lines
Parameters:
| Name | Type | Description | Default |
| --------------------- | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `markerWidth` | \`str | float | None\` |
| `markerHeight` | \`str | float | None\` |
| `markerUnits` | \`str | None\` | Coordinate system. |
| `refX` | \`str | float | None\` |
| `refY` | \`str | float | None\` |
| `orient` | \`str | float | None\` |
| `viewBox` | \`str | None\` | Viewport bounds. |
| `preserveAspectRatio` | \`str | None\` | Aspect ratio handling. |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
markerWidth: str | float | None = None,
markerHeight: str | float | None = None,
markerUnits: str | None = None,
refX: str | float | None = None,
refY: str | float | None = None,
orient: str | float | None = None,
viewBox: str | None = None,
preserveAspectRatio: str | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## Mask
```
Mask(
*children,
x=None,
y=None,
width=None,
height=None,
maskUnits=None,
maskContentUnits=None,
mask_type=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
Defines a mask
Parameters:
| Name | Type | Description | Default |
| ------------------ | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `x` | \`str | float | None\` |
| `y` | \`str | float | None\` |
| `width` | \`str | float | None\` |
| `height` | \`str | float | None\` |
| `maskUnits` | \`str | None\` | Coordinate system for position/size. |
| `maskContentUnits` | \`str | None\` | Coordinate system for contents. |
| `mask_type` | \`str | None\` | Mask mode (alpha |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
x: str | float | None = None,
y: str | float | None = None,
width: str | float | None = None,
height: str | float | None = None,
maskUnits: str | None = None,
maskContentUnits: str | None = None,
mask_type: str | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## Metadata
```
Metadata(
*children, class_=None, id=None, style=None, **kwargs
)
```
Bases: `CaseTag`
Defines metadata
Parameters:
| Name | Type | Description | Default |
| ---------- | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## Mpath
```
Mpath(
*children,
href=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
Defines a motion path
Parameters:
| Name | Type | Description | Default |
| ---------- | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `href` | \`str | None\` | Reference to path element. |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
href: str | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
# SVG Tags N-S
Air is proud to provide first class SVG support. The entire SVG specification is supported.
## Path
```
Path(
*children,
d=None,
pathLength=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
Defines a path
Parameters:
| Name | Type | Description | Default |
| ------------ | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `d` | \`str | None\` | Path data defining the shape. |
| `pathLength` | \`float | None\` | Total path length in user units. |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
d: str | None = None,
pathLength: float | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## Pattern
```
Pattern(
*children,
x=None,
y=None,
width=None,
height=None,
patternUnits=None,
patternContentUnits=None,
patternTransform=None,
href=None,
viewBox=None,
preserveAspectRatio=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
Defines a pattern
Parameters:
| Name | Type | Description | Default |
| --------------------- | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `x` | \`str | float | None\` |
| `y` | \`str | float | None\` |
| `width` | \`str | float | None\` |
| `height` | \`str | float | None\` |
| `patternUnits` | \`str | None\` | Coordinate system for position/size. |
| `patternContentUnits` | \`str | None\` | Coordinate system for contents. |
| `patternTransform` | \`str | None\` | Additional transformation. |
| `href` | \`str | None\` | Reference to template pattern. |
| `viewBox` | \`str | None\` | Viewport bounds for pattern. |
| `preserveAspectRatio` | \`str | None\` | Aspect ratio handling. |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
x: str | float | None = None,
y: str | float | None = None,
width: str | float | None = None,
height: str | float | None = None,
patternUnits: str | None = None,
patternContentUnits: str | None = None,
patternTransform: str | None = None,
href: str | None = None,
viewBox: str | None = None,
preserveAspectRatio: str | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## Polygon
```
Polygon(
*children,
points=None,
pathLength=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
Defines a polygon
Parameters:
| Name | Type | Description | Default |
| ------------ | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `points` | \`str | None\` | List of x,y coordinate pairs. |
| `pathLength` | \`float | None\` | Total path length in user units. |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
points: str | None = None,
pathLength: float | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## Polyline
```
Polyline(
*children,
points=None,
pathLength=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
Defines a polyline
Parameters:
| Name | Type | Description | Default |
| ------------ | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `points` | \`str | None\` | List of x,y coordinate pairs. |
| `pathLength` | \`float | None\` | Total path length in user units. |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
points: str | None = None,
pathLength: float | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## RadialGradient
```
RadialGradient(
*children,
cx=None,
cy=None,
r=None,
fx=None,
fy=None,
fr=None,
gradientUnits=None,
gradientTransform=None,
href=None,
spreadMethod=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
Defines a radial gradient
Parameters:
| Name | Type | Description | Default |
| ------------------- | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `cx` | \`str | float | None\` |
| `cy` | \`str | float | None\` |
| `r` | \`str | float | None\` |
| `fx` | \`str | float | None\` |
| `fy` | \`str | float | None\` |
| `fr` | \`str | float | None\` |
| `gradientUnits` | \`str | None\` | Coordinate system. |
| `gradientTransform` | \`str | None\` | Additional transformation. |
| `href` | \`str | None\` | Reference to template gradient. |
| `spreadMethod` | \`str | None\` | Gradient behavior. |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
cx: str | float | None = None,
cy: str | float | None = None,
r: str | float | None = None,
fx: str | float | None = None,
fy: str | float | None = None,
fr: str | float | None = None,
gradientUnits: str | None = None,
gradientTransform: str | None = None,
href: str | None = None,
spreadMethod: str | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## Rect
```
Rect(
*children,
x=None,
y=None,
width=None,
height=None,
rx=None,
ry=None,
pathLength=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
Defines a rectangle
Parameters:
| Name | Type | Description | Default |
| ------------ | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `x` | \`str | float | None\` |
| `y` | \`str | float | None\` |
| `width` | \`str | float | None\` |
| `height` | \`str | float | None\` |
| `rx` | \`str | float | None\` |
| `ry` | \`str | float | None\` |
| `pathLength` | \`float | None\` | Total perimeter length in user units. |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
x: str | float | None = None,
y: str | float | None = None,
width: str | float | None = None,
height: str | float | None = None,
rx: str | float | None = None,
ry: str | float | None = None,
pathLength: float | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## Script
```
Script(
*children,
type=None,
href=None,
crossorigin=None,
fetchpriority=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
Defines a script
Parameters:
| Name | Type | Description | Default |
| --------------- | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `type` | \`str | None\` | Script MIME type. |
| `href` | \`str | None\` | External script URL. |
| `crossorigin` | \`str | None\` | CORS credentials flag. |
| `fetchpriority` | \`str | None\` | Fetch priority hint (experimental). |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
type: str | None = None,
href: str | None = None,
crossorigin: str | None = None,
fetchpriority: str | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## Set
```
Set(
*children,
to=None,
attributeName=None,
begin=None,
dur=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
Sets an attribute value
Parameters:
| Name | Type | Description | Default |
| --------------- | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `to` | \`str | None\` | Value to apply for animation duration. |
| `attributeName` | \`str | None\` | Target attribute to set. |
| `begin` | \`str | None\` | Animation start time. |
| `dur` | \`str | None\` | Animation duration. |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
to: str | None = None,
attributeName: str | None = None,
begin: str | None = None,
dur: str | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## Stop
```
Stop(
*children,
offset=None,
stop_color=None,
stop_opacity=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
Defines a gradient stop
Parameters:
| Name | Type | Description | Default |
| -------------- | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `offset` | \`str | float | None\` |
| `stop_color` | \`str | None\` | Color of gradient stop. |
| `stop_opacity` | \`str | float | None\` |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
offset: str | float | None = None,
stop_color: str | None = None,
stop_opacity: str | float | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## Style
```
Style(
*children,
type=None,
media=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
Defines style information
Parameters:
| Name | Type | Description | Default |
| ---------- | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `type` | \`str | None\` | Style sheet language MIME type. |
| `media` | \`str | None\` | Media query for when styles apply. |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
type: str | None = None,
media: str | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## Svg
```
Svg(
*children,
width=None,
height=None,
x=None,
y=None,
viewBox=None,
preserveAspectRatio=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
Defines an SVG document fragment
Parameters:
| Name | Type | Description | Default |
| --------------------- | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `width` | \`str | float | None\` |
| `height` | \`str | float | None\` |
| `x` | \`str | float | None\` |
| `y` | \`str | float | None\` |
| `viewBox` | \`str | None\` | SVG viewport coordinates. |
| `preserveAspectRatio` | \`str | None\` | Aspect ratio handling. |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
width: str | float | None = None,
height: str | float | None = None,
x: str | float | None = None,
y: str | float | None = None,
viewBox: str | None = None,
preserveAspectRatio: str | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## Switch
```
Switch(
*children, class_=None, id=None, style=None, **kwargs
)
```
Bases: `CaseTag`
Defines conditional processing
Parameters:
| Name | Type | Description | Default |
| ---------- | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## Symbol
```
Symbol(
*children,
width=None,
height=None,
x=None,
y=None,
viewBox=None,
preserveAspectRatio=None,
refX=None,
refY=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
Defines a reusable symbol
Parameters:
| Name | Type | Description | Default |
| --------------------- | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `width` | \`str | float | None\` |
| `height` | \`str | float | None\` |
| `x` | \`str | float | None\` |
| `y` | \`str | float | None\` |
| `viewBox` | \`str | None\` | Viewport bounds for symbol. |
| `preserveAspectRatio` | \`str | None\` | Aspect ratio handling. |
| `refX` | \`str | float | None\` |
| `refY` | \`str | float | None\` |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
width: str | float | None = None,
height: str | float | None = None,
x: str | float | None = None,
y: str | float | None = None,
viewBox: str | None = None,
preserveAspectRatio: str | None = None,
refX: str | float | None = None,
refY: str | float | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
# SVG Tags T-Z
Air is proud to provide first class SVG support. The entire SVG specification is supported.
## Text
```
Text(
*children,
x=None,
y=None,
dx=None,
dy=None,
rotate=None,
lengthAdjust=None,
textLength=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
Defines text content
Parameters:
| Name | Type | Description | Default |
| -------------- | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `x` | \`str | float | None\` |
| `y` | \`str | float | None\` |
| `dx` | \`str | float | None\` |
| `dy` | \`str | float | None\` |
| `rotate` | \`str | None\` | Rotation of individual glyphs. |
| `lengthAdjust` | \`str | None\` | Text stretching method. |
| `textLength` | \`str | float | None\` |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
x: str | float | None = None,
y: str | float | None = None,
dx: str | float | None = None,
dy: str | float | None = None,
rotate: str | None = None,
lengthAdjust: str | None = None,
textLength: str | float | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## TextPath
```
TextPath(
*children,
href=None,
lengthAdjust=None,
method=None,
path=None,
side=None,
spacing=None,
startOffset=None,
textLength=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
Defines text along a path
Parameters:
| Name | Type | Description | Default |
| -------------- | --------------- | ----------------------------------------- | ------------------------------------------ |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `href` | \`str | None\` | Reference to path element for text layout. |
| `lengthAdjust` | \`str | None\` | Length adjustment method. |
| `method` | \`str | None\` | Glyph rendering method. |
| `path` | \`str | None\` | Path data for text layout. |
| `side` | \`str | None\` | Which side of path to render text. |
| `spacing` | \`str | None\` | Glyph spacing handling. |
| `startOffset` | \`str | float | None\` |
| `textLength` | \`str | float | None\` |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
href: str | None = None,
lengthAdjust: str | None = None,
method: str | None = None,
path: str | None = None,
side: str | None = None,
spacing: str | None = None,
startOffset: str | float | None = None,
textLength: str | float | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## Title
```
Title(
*children, class_=None, id=None, style=None, **kwargs
)
```
Bases: `CaseTag`
Defines a title for the SVG document
Parameters:
| Name | Type | Description | Default |
| ---------- | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## Tspan
```
Tspan(
*children,
x=None,
y=None,
dx=None,
dy=None,
rotate=None,
lengthAdjust=None,
textLength=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
Defines a text span
Parameters:
| Name | Type | Description | Default |
| -------------- | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `x` | \`str | float | None\` |
| `y` | \`str | float | None\` |
| `dx` | \`str | float | None\` |
| `dy` | \`str | float | None\` |
| `rotate` | \`str | None\` | Rotation of individual glyphs. |
| `lengthAdjust` | \`str | None\` | Text stretching method. |
| `textLength` | \`str | float | None\` |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
x: str | float | None = None,
y: str | float | None = None,
dx: str | float | None = None,
dy: str | float | None = None,
rotate: str | None = None,
lengthAdjust: str | None = None,
textLength: str | float | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## Use
```
Use(
*children,
href=None,
x=None,
y=None,
width=None,
height=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
References another element
Parameters:
| Name | Type | Description | Default |
| ---------- | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `href` | \`str | None\` | Reference to element to duplicate. |
| `x` | \`str | float | None\` |
| `y` | \`str | float | None\` |
| `width` | \`str | float | None\` |
| `height` | \`str | float | None\` |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
href: str | None = None,
x: str | float | None = None,
y: str | float | None = None,
width: str | float | None = None,
height: str | float | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## View
```
View(
*children,
viewBox=None,
preserveAspectRatio=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `CaseTag`
Defines a view
Parameters:
| Name | Type | Description | Default |
| --------------------- | --------------- | ----------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `viewBox` | \`str | None\` | Viewport bounds. |
| `preserveAspectRatio` | \`str | None\` | Aspect ratio handling. |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `**kwargs` | `AttributeType` | Additional attributes. | `{}` |
Source code in `src/air/tags/models/svg.py`
```
def __init__(
self,
*children: Renderable,
viewBox: str | None = None,
preserveAspectRatio: str | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
# Tags
Note
Tags, or **Air Tag**, are explained in the [concepts document about tags](../../learn/air_tags/).
In the spirit of helping our users, every **Air Tag** has copious documentation—enough that sometimes it breaks the documentation build process. Therefore, **Air Tag** that directly correspond to their HTML equivalents can be found in smaller, easier-to-compile pages.
- [HTML Air Tags A-D](tags-a-d/)
- [HTML Air Tags E-M](tags-e-m/)
- [HTML Air Tags N-S](tags-n-s/)
- [HTML Air Tags T-Z](tags-t-z/)
What remains on this page are core **Air Tag** that either have great utility (**Raw** and **Children** come to mind), or are base classes for other tags.
## Tag
```
Tag(*children)
```
Bases: `Transparent`
Alias for the `Transparent` tag; use it if it improves clarity.
Source code in `src/air/tags/models/special.py`
```
def __init__(
self,
*children: Renderable,
) -> None:
super().__init__(*children)
```
## Raw
```
Raw(text_child='', /, **kwargs)
```
Bases: `UnSafeTag`, `Transparent`
Renders raw HTML content without escaping.
Raises:
| Type | Description |
| ----------- | --------------------------------- |
| `TypeError` | If non-string content is provided |
Example: Raw('**Bold** text')
# Produces '**Bold** text'
# Use with other tags
Div( P("Safe content"), Raw('
______________________________________________________________________
'), P("More safe content") )
Source code in `src/air/tags/models/special.py`
```
@override
def __init__(self, text_child: str = "", /, **kwargs: AttributeType) -> None:
if not isinstance(text_child, str):
msg = f"{self!r} only accepts string content"
raise TypeError(msg)
super().__init__(text_child, **kwargs)
```
## Children
```
Children(*children)
```
Bases: `Transparent`
Alias for the `Transparent` tag; use it if it improves clarity.
Source code in `src/air/tags/models/special.py`
```
def __init__(
self,
*children: Renderable,
) -> None:
super().__init__(*children)
```
## SafeStr
Bases: `str`
String subclass that bypasses HTML escaping when rendered.
# Tags A-D
## A
```
A(
*children,
href=None,
target=None,
download=None,
rel=None,
hreflang=None,
type=None,
referrerpolicy=None,
media=None,
ping=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `BaseTag`
Defines a hyperlink
Parameters:
| Name | Type | Description | Default |
| ---------------- | --------------- | -------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `href` | \`str | None\` | Specifies the URL of the page the link goes to. |
| `target` | \`str | None\` | Specifies where to open the linked document. |
| `download` | \`str | None\` | Specifies that the target will be downloaded when a user clicks on the hyperlink. |
| `rel` | \`str | None\` | Specifies the relationship between the current document and the linked document. |
| `hreflang` | \`str | None\` | Specifies the language of the linked document. |
| `type` | \`str | None\` | Specifies the media type of the linked document. |
| `referrerpolicy` | \`str | None\` | Specifies which referrer information to send with the link. |
| `media` | \`str | None\` | Specifies what media/device the linked document is optimized for. |
| `ping` | \`str | None\` | Specifies a space-separated list of URLs to which, when the link is followed, post requests with the body ping will be sent by the browser (in the background). Typically used for tracking. |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `kwargs` | `AttributeType` | Keyword arguments transformed into tag attributes. | `{}` |
Source code in `src/air/tags/models/stock.py`
```
def __init__(
self,
*children: Renderable,
href: str | None = None,
target: str | None = None,
download: str | None = None,
rel: str | None = None,
hreflang: str | None = None,
type: str | None = None,
referrerpolicy: str | None = None,
media: str | None = None,
ping: str | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## Area
```
Area(
*,
alt=None,
coords=None,
download=None,
href=None,
ping=None,
referrerpolicy=None,
rel=None,
shape=None,
target=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `SelfClosingTag`
Defines an area inside an image map
Parameters:
| Name | Type | Description | Default |
| ---------------- | --------------- | -------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `alt` | \`str | None\` | Specifies an alternate text for an area. Required if the href attribute is present. |
| `coords` | \`str | None\` | Specifies the coordinates of an area. |
| `download` | \`str | None\` | Specifies that the target will be downloaded when a user clicks on the hyperlink. |
| `href` | \`str | None\` | Specifies the URL of the page the link goes to. |
| `ping` | \`str | None\` | Specifies a space-separated list of URLs to which, when the link is followed, post requests with the body ping will be sent by the browser (in the background). Typically used for tracking. |
| `referrerpolicy` | \`str | None\` | Specifies which referrer information to send with the link. |
| `rel` | \`str | None\` | Specifies the relationship between the current document and the linked document. |
| `shape` | \`str | None\` | Specifies the shape of an area. |
| `target` | \`str | None\` | Specifies where to open the linked document. |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `kwargs` | `AttributeType` | Keyword arguments transformed into tag attributes. | `{}` |
Source code in `src/air/tags/models/stock.py`
```
def __init__(
self,
*,
alt: str | None = None,
coords: str | None = None,
download: str | None = None,
href: str | None = None,
ping: str | None = None,
referrerpolicy: str | None = None,
rel: str | None = None,
shape: str | None = None,
target: str | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(**kwargs | locals_cleanup(locals()))
```
## Audio
```
Audio(
*children,
autoplay=None,
controls=None,
loop=None,
muted=None,
preload=None,
src=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `BaseTag`
Defines embedded sound content
Parameters:
| Name | Type | Description | Default |
| ---------- | --------------- | -------------------------------------------------- | -------------------------------------------------------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `autoplay` | \`str | None\` | Specifies that the audio will start playing as soon as it is ready. |
| `controls` | \`str | None\` | Specifies that audio controls should be displayed (such as a play/pause button etc). |
| `loop` | \`str | None\` | Specifies that the audio will start over again, every time it is finished. |
| `muted` | \`str | None\` | Specifies that the audio output should be muted. |
| `preload` | \`str | None\` | Specifies if and how the author thinks the audio should be loaded when the page loads. |
| `src` | \`str | None\` | Specifies the URL of the audio file. |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `kwargs` | `AttributeType` | Keyword arguments transformed into tag attributes. | `{}` |
Source code in `src/air/tags/models/stock.py`
```
def __init__(
self,
*children: Renderable,
autoplay: str | None = None,
controls: str | None = None,
loop: str | None = None,
muted: str | None = None,
preload: str | None = None,
src: str | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## Base
```
Base(
*,
href=None,
target=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `SelfClosingTag`
Specifies the base URL/target for all relative URLs in a document
Parameters:
| Name | Type | Description | Default |
| -------- | --------------- | -------------------------------------------------- | --------------------------------------- |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `kwargs` | `AttributeType` | Keyword arguments transformed into tag attributes. | `{}` |
Source code in `src/air/tags/models/stock.py`
```
def __init__(
self,
*,
href: str | None = None,
target: str | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(**kwargs | locals_cleanup(locals()))
```
## Bdi
```
Bdi(*children, class_=None, id=None, style=None, **kwargs)
```
Bases: `BaseTag`
Isolates a part of text that might be formatted in a different direction from other text outside it
Parameters:
| Name | Type | Description | Default |
| ---------- | --------------- | -------------------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `kwargs` | `AttributeType` | Keyword arguments transformed into tag attributes. | `{}` |
Source code in `src/air/tags/models/stock.py`
```
def __init__(
self,
*children: Renderable,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## Blockquote
```
Blockquote(
*children,
cite=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `BaseTag`
Defines a section that is quoted from another source
Parameters:
| Name | Type | Description | Default |
| ---------- | --------------- | -------------------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `cite` | \`str | None\` | Specifies the source of the quotation. |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `kwargs` | `AttributeType` | Keyword arguments transformed into tag attributes. | `{}` |
Source code in `src/air/tags/models/stock.py`
```
def __init__(
self,
*children: Renderable,
cite: str | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## Button
```
Button(
*children,
name=None,
type=None,
value=None,
autofocus=None,
disabled=None,
form=None,
formaction=None,
formenctype=None,
formmethod=None,
formnovalidate=None,
formtarget=None,
popovertarget=None,
popovertargetaction=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `BaseTag`
Defines a clickable button
Parameters:
| Name | Type | Description | Default |
| --------------------- | --------------- | -------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `name` | \`str | None\` | Specifies a name for the button. |
| `type` | \`str | None\` | Specifies the type of button. |
| `value` | \`str | None\` | Specifies an initial value for the button. |
| `autofocus` | \`str | None\` | Specifies that a button should automatically get focus when the page loads. |
| `disabled` | \`str | None\` | Specifies that a button should be disabled. |
| `form` | \`str | None\` | Specifies which form the button belongs to. |
| `formaction` | \`str | None\` | Specifies where to send the form-data when a form is submitted. Only for type="submit". |
| `formenctype` | \`str | None\` | Specifies how the form-data should be encoded before sending it to a server. Only for type="submit". |
| `formmethod` | \`str | None\` | Specifies how to send the form-data (which HTTP method to use). Only for type="submit". |
| `formnovalidate` | \`str | None\` | Specifies that the form-data should not be validated on submission. Only for type="submit". |
| `formtarget` | \`str | None\` | Specifies where to display the response that is received after submitting the form. Only for type="submit". |
| `popovertarget` | \`str | None\` | Specifies which popover element to invoke. |
| `popovertargetaction` | \`str | None\` | Specifies what action to perform on the popover element. |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `kwargs` | `AttributeType` | Keyword arguments transformed into tag attributes. | `{}` |
Source code in `src/air/tags/models/stock.py`
```
def __init__(
self,
*children: Renderable,
name: str | None = None,
type: str | None = None,
value: str | None = None,
autofocus: str | None = None,
disabled: str | None = None,
form: str | None = None,
formaction: str | None = None,
formenctype: str | None = None,
formmethod: str | None = None,
formnovalidate: str | None = None,
formtarget: str | None = None,
popovertarget: str | None = None,
popovertargetaction: str | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## Canvas
```
Canvas(
*children,
width=None,
height=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `BaseTag`
Used to draw graphics, on the fly, via scripting (usually JavaScript)
Parameters:
| Name | Type | Description | Default |
| ---------- | --------------- | -------------------------------------------------- | --------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `width` | \`str | int | None\` |
| `height` | \`str | int | None\` |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `kwargs` | `AttributeType` | Keyword arguments transformed into tag attributes. | `{}` |
Source code in `src/air/tags/models/stock.py`
```
def __init__(
self,
*children: Renderable,
width: str | int | None = None,
height: str | int | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## Col
```
Col(
*, span=None, class_=None, id=None, style=None, **kwargs
)
```
Bases: `SelfClosingTag`
Specifies column properties for each column within a element
Parameters:
| Name | Type | Description | Default |
| -------- | --------------- | -------------------------------------------------- | ------------------------------------------------------ |
| `span` | \`str | None\` | Specifies the number of columns a element should span. |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `kwargs` | `AttributeType` | Keyword arguments transformed into tag attributes. | `{}` |
Source code in `src/air/tags/models/stock.py`
```
def __init__(
self,
*,
span: str | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(**kwargs | locals_cleanup(locals()))
```
## Colgroup
```
Colgroup(
*children,
span=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `BaseTag`
Specifies a group of one or more columns in a table for formatting
Parameters:
| Name | Type | Description | Default |
| ---------- | --------------- | -------------------------------------------------- | ------------------------------------------------------ |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `span` | \`str | None\` | Specifies the number of columns a element should span. |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `kwargs` | `AttributeType` | Keyword arguments transformed into tag attributes. | `{}` |
Source code in `src/air/tags/models/stock.py`
```
def __init__(
self,
*children: Renderable,
span: str | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## Data
```
Data(
*children,
value=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `BaseTag`
Adds a machine-readable translation of a given content
Parameters:
| Name | Type | Description | Default |
| ---------- | --------------- | -------------------------------------------------- | ---------------------------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `value` | \`str | None\` | Specifies the machine-readable translation of the content. |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `kwargs` | `AttributeType` | Keyword arguments transformed into tag attributes. | `{}` |
Source code in `src/air/tags/models/stock.py`
```
def __init__(
self,
*children: Renderable,
value: str | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## Dd
```
Dd(
*children,
cite=None,
datetime=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `BaseTag`
Defines a description/value of a term in a description list
Parameters:
| Name | Type | Description | Default |
| ---------- | --------------- | -------------------------------------------------- | --------------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `cite` | \`str | None\` | Specifies the source of the quotation. |
| `datetime` | \`str | None\` | Specifies the date and time of the quotation. |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `kwargs` | `AttributeType` | Keyword arguments transformed into tag attributes. | `{}` |
Source code in `src/air/tags/models/stock.py`
```
def __init__(
self,
*children: Renderable,
cite: str | None = None,
datetime: str | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## Details
```
Details(
*children,
open=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `BaseTag`
Defines additional details that the user can view or hide
Parameters:
| Name | Type | Description | Default |
| ---------- | --------------- | -------------------------------------------------- | ---------------------------------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `open` | \`str | None\` | Specifies that the details should be visible (open) to the user. |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `kwargs` | `AttributeType` | Keyword arguments transformed into tag attributes. | `{}` |
Source code in `src/air/tags/models/stock.py`
```
def __init__(
self,
*children: Renderable,
open: str | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## Dialog
```
Dialog(
*children,
open=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `BaseTag`
Defines a dialog box or window
Parameters:
| Name | Type | Description | Default |
| ---------- | --------------- | -------------------------------------------------- | ------------------------------------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `open` | \`str | None\` | Specifies that the dialog box should be visible (open) to the user. |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `kwargs` | `AttributeType` | Keyword arguments transformed into tag attributes. | `{}` |
Source code in `src/air/tags/models/stock.py`
```
def __init__(
self,
*children: Renderable,
open: str | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
# Tags E-M
## Embed
```
Embed(
*,
src=None,
type=None,
width=None,
height=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `SelfClosingTag`
Defines a container for an external application
Parameters:
| Name | Type | Description | Default |
| -------- | --------------- | -------------------------------------------------- | ---------------------------------------------------- |
| `src` | \`str | None\` | Specifies the address of the external file to embed. |
| `type` | \`str | None\` | Specifies the media type of the embedded content. |
| `width` | \`str | int | None\` |
| `height` | \`str | int | None\` |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `kwargs` | `AttributeType` | Keyword arguments transformed into tag attributes. | `{}` |
Source code in `src/air/tags/models/stock.py`
```
def __init__(
self,
*,
src: str | None = None,
type: str | None = None,
width: str | int | None = None,
height: str | int | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(**kwargs | locals_cleanup(locals()))
```
## Fieldset
```
Fieldset(
*children,
disabled=None,
form=None,
name=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `BaseTag`
Groups related elements in a form
Parameters:
| Name | Type | Description | Default |
| ---------- | --------------- | -------------------------------------------------- | ------------------------------------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `disabled` | \`str | None\` | Specifies that a group of related form elements should be disabled. |
| `form` | \`str | None\` | Specifies which form the fieldset belongs to. |
| `name` | \`str | None\` | Specifies a name for the fieldset. |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `kwargs` | `AttributeType` | Keyword arguments transformed into tag attributes. | `{}` |
Source code in `src/air/tags/models/stock.py`
```
def __init__(
self,
*children: Renderable,
disabled: str | None = None,
form: str | None = None,
name: str | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## Form
```
Form(
*children,
action=None,
method=None,
accept_charset=None,
autocomplete=None,
enctype=None,
name=None,
novalidate=None,
rel=None,
target=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `BaseTag`
Defines an HTML form for user input
Parameters:
| Name | Type | Description | Default |
| ---------------- | --------------- | -------------------------------------------------- | ----------------------------------------------------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `action` | \`str | None\` | Specifies where to send the form-data when a form is submitted. |
| `method` | \`str | None\` | Specifies the HTTP method to use when sending form-data. |
| `accept_charset` | \`str | None\` | Specifies the character encodings that are to be used for the form submission. |
| `autocomplete` | \`str | None\` | Specifies whether a form should have autocomplete on or off. |
| `enctype` | \`str | None\` | Specifies how the form-data should be encoded when submitting it to the server. |
| `name` | \`str | None\` | Specifies the name of the form. |
| `novalidate` | \`str | None\` | Specifies that the form should not be validated when submitted. |
| `rel` | \`str | None\` | Specifies the relationship between a linked resource and the current document. |
| `target` | \`str | None\` | Specifies where to display the response that is received after submitting the form. |
| `class_` | \`str | None\` | Substituted as the DOM class attribute. |
| `id` | \`str | None\` | DOM ID attribute. |
| `style` | \`str | None\` | Inline style attribute. |
| `kwargs` | `AttributeType` | Keyword arguments transformed into tag attributes. | `{}` |
Source code in `src/air/tags/models/stock.py`
```
def __init__(
self,
*children: Renderable,
action: str | None = None,
method: str | None = None,
accept_charset: str | None = None,
autocomplete: str | None = None,
enctype: str | None = None,
name: str | None = None,
novalidate: str | None = None,
rel: str | None = None,
target: str | None = None,
class_: str | None = None,
id: str | None = None,
style: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## Head
```
Head(*children, profile=None, **kwargs)
```
Bases: `BaseTag`
Contains metadata/information for the document
Parameters:
| Name | Type | Description | Default |
| ---------- | --------------- | -------------------------------------------------- | ----------------------------------------------------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `profile` | \`str | None\` | Specifies the URL of a document that contains a line-break-separated list of links. |
| `kwargs` | `AttributeType` | Keyword arguments transformed into tag attributes. | `{}` |
Source code in `src/air/tags/models/stock.py`
```
def __init__(
self,
*children: Renderable,
profile: str | None = None,
**kwargs: AttributeType,
) -> None:
super().__init__(*children, **kwargs | locals_cleanup(locals()))
```
## Html
```
Html(*children, **kwargs)
```
Bases: `BaseTag`
Defines the root of an HTML document
Source code in `src/air/tags/models/base.py`
```
def __init__(self, *children: Renderable, **kwargs: AttributeType) -> None:
"""Initialize a tag with renderable children and HTML attributes.
Args:
children: Renderable objects that become the tag's inner content.
kwargs: Attribute names and values applied to the tag element.
"""
self._name = self.__class__.__name__
self._module = self.__class__.__module__
self._children: tuple[Renderable, ...] = children
self._attrs: TagAttributesType = kwargs
```
### pretty_render
```
pretty_render(
*, with_body=False, with_head=False, with_doctype=True
)
```
Pretty-print without escaping.
Source code in `src/air/tags/models/special.py`
```
@override
def pretty_render(
self,
*,
with_body: bool = False,
with_head: bool = False,
with_doctype: bool = True,
) -> str:
"""Pretty-print without escaping."""
return super().pretty_render(with_body=with_body, with_head=with_head, with_doctype=with_doctype)
```
## Iframe
```
Iframe(
*children,
src=None,
srcdoc=None,
width=None,
height=None,
allow=None,
allowfullscreen=None,
allowpaymentrequest=None,
loading=None,
name=None,
referrerpolicy=None,
sandbox=None,
class_=None,
id=None,
style=None,
**kwargs,
)
```
Bases: `BaseTag`
Defines an inline frame
Parameters:
| Name | Type | Description | Default |
| --------------------- | --------------- | -------------------------------------------------- | ------------------------------------------------------------------------------------------- |
| `children` | `Renderable` | Tags, strings, or other rendered content. | `()` |
| `src` | \`str | None\` | Specifies the URL of the page to embed. |
| `srcdoc` | \`str | None\` | Specifies the HTML content of the page to show in the