# 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

Awesome SaaS

API Docs

``` 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.

Card Footer
``` 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