If you've used a framework like Django or FastAPI, you've likely used a request
parameter (or a Request
type). View has something similiar, called Context
.
The Context
instance contains information about the incoming request, including:
Info
Context
is an extension type, and is defined in the _view
module. It's Python signatures are defined in the _view
type stub.
The context can be added to a route via a route input, which is done through the context
decorator. Note that context
has a standard and direct variation (App.context
is available to prevent imports).
For example:
from view import new_app, context, Context
app = new_app()
@app.get("/")
@context
async def index(ctx: Context):
print(ctx.headers["user-agent"])
return "..."
app.run()
Since context
is a route input, it can be used alongside other route inputs:
from view import new_app, Context
app = new_app()
@app.get("/")
@app.query("greeting", str)
@app.context # direct variation
async def index(greeting: str, ctx: Context):
return f"{greeting}, {ctx.headers['place']}"
app.run()
Context
works well with the automatic input API (similar to how you would do it in FastAPI), like so:
from view import new_app, Context
app = new_app()
@app.get("/")
async def index(ctx: Context): # this is allowed
...
app.run()
Context
can also be used to detect whether the route is being used via App.test
, through the http_version
attribute.
Info
App.test
is a more internal detail, but is available to use publically. It looks like this:
When a route is being used via App.test
, http_version
is set to view_test
. For example:
from view import new_app, Context
import asyncio
app = new_app()
@app.get("/")
@app.context
async def index(context: Context):
if context.http_version == "view_test":
return "this is a test!"
return "hello, view.py"
async def main():
async with app.test() as test:
res = await test.get("/")
assert res.message == "this is a test!"
if __name__ == "__main__":
asyncio.run(main())
Technically speaking, cookies in HTTP are done via headers, but typically cookies in Python frameworks are done in a dict
instead. View is no exception to this.
Cookies can be viewed from the cookies
attribute. However, since the Context
is not related to the response, you must use cookie
on a Response
object to mutate a cookie. For example:
from view import new_app, Context, Response
app = new_app()
@app.get("/")
async def index(ctx: Context): # automatic route input
count = int(ctx.cookies.get("count") or 0)
count += 1
res = Response(f"you have been to this page {count} time(s)")
res.set_cookie("count", str(count))
return res
app.run()
The Context
also provides information about the servers binding address, as well as the address of the client. This information is provided in the form of an ipaddress.IPv4Address
or ipaddress.IPv6Address
(see the ipaddress module), and then the port is in a seperate attribute.
Both Context.client
and Context.client_port
may be None
, but note that if either of them is not None
, the other one will be non None
. For example:
from view import new_app, Context
app = new_app()
@app.get('/')
@app.context
async def index(ctx: Context):
if ctx.client:
port = ctx.client_port # this will always be an int in this case
...
app.run()
Danger
The above is not type safe. The type checker will still believe the port
is int | None
.
Context
is similiar to Request
in other web frameworks, and is considered to be a route input in View, meaning you can add it to a route via the context
decorator (or App.context
, to prevent an extra import), or by the automatic route input system (i.e. adding a parameter annotated with type Context
).
Context
contains eight attributes:
headers
, of type dict[str, str]
.cookies
, of type dict[str, str]
.client
, of type ipaddress.IPv4Address
, ipaddress.IPv6Address
, or None
.server
, of type ipaddress.IPv4Address
, ipaddress.IPv6Address
, or None
.method
, of type StrMethodASGI
(uppercase string containing the method, such as "GET"
).path
, of type str
.scheme
, which can be the string "http"
, "https"
.http_version
, which can be the string "1.0"
, "1.1"
, "2.0"
, "view_test"
.