Cross
Write once, run everywhere - A universal web framework adapter for Python that lets you write code once and use it across multiple web frameworks.
Installation
uv add cross-web
from cross_web import Response
Overview
Cross provides a unified interface for common web framework operations, allowing you to write framework-agnostic code that can be easily adapted to work with FastAPI, Flask, Django, and other popular Python web frameworks.
Testing
This project is in early development!
Cross also ships framework-specific test clients under cross_web.testing.clients.
Import a concrete client from its module:
from cross_web.testing.clients.starlette import StarletteHttpClient
from cross_web.testing.clients.flask import FlaskHttpClient
from cross_web.testing.clients.django import DjangoHttpClient
Do not import client classes from cross_web.testing.clients directly. The package only exports the shared base types so importing it does not pull optional framework dependencies.
The shared testing API lives in cross_web.testing:
from cross_web.testing import HttpClient, Response
Every concrete client exposes the same async interface:
await client.request(url, method, headers=None, **kwargs)await client.get(url, headers=None, **kwargs)await client.post(url, data=None, json=None, files=None, headers=None, **kwargs)
Response exposes:
response.status_coderesponse.headersresponse.textresponse.json
Example
import json
import pytest
from starlette.applications import Starlette
from starlette.requests import Request
from starlette.responses import JSONResponse
from starlette.routing import Route
from cross_web.request._starlette import StarletteRequestAdapter
from cross_web.testing.clients.starlette import StarletteHttpClient
async def echo(request: Request) -> JSONResponse:
adapter = StarletteRequestAdapter(request)
body = await adapter.get_body()
return JSONResponse(
{
"method": adapter.method,
"query": dict(adapter.query_params),
"body": json.loads(body.decode()),
}
)
app = Starlette(routes=[Route("/echo", echo, methods=["POST"])])
@pytest.mark.asyncio
async def test_echo() -> None:
client = StarletteHttpClient(app)
response = await client.post("/echo?debug=1", json={"hello": "world"})
assert response.status_code == 200
assert response.json == {
"method": "POST",
"query": {"debug": "1"},
"body": {"hello": "world"},
}
Django
DjangoHttpClient and AsyncDjangoHttpClient take a Django view callable, not a Django app object.
Django must already be configured by your test environment. The recommended setup is pytest-django with a test settings module:
[tool.pytest.ini_options]
DJANGO_SETTINGS_MODULE = "myproject.test_settings"
Available clients
cross_web.testing.clients.aiohttp.AiohttpHttpClientcross_web.testing.clients.chalice.ChaliceHttpClientcross_web.testing.clients.django.DjangoHttpClientcross_web.testing.clients.django.AsyncDjangoHttpClientcross_web.testing.clients.flask.FlaskHttpClientcross_web.testing.clients.flask.AsyncFlaskHttpClientcross_web.testing.clients.litestar.LitestarHttpClientcross_web.testing.clients.quart.QuartHttpClientcross_web.testing.clients.sanic.SanicHttpClientcross_web.testing.clients.starlette.StarletteHttpClient
Notes
- All client methods are async, including clients wrapping sync frameworks.
files=uses(filename, content_bytes, content_type)tuples.ChaliceHttpClientsupports JSON requests but does not support form data or file uploads.- The testing clients are intended for tests and integration-style handler checks, not as production HTTP clients.
Documentation website
The repository now includes a Cross-Docs-powered website in website/.
cd website
just setup
just dev
That setup uses cross-docs for the Python side, @usecross/docs on the frontend, the Inertia Vite plugin, and Inertia v3 packages.
Metadata
Release files for cross-web 0.7.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| cross_web-0.7.0.tar.gz | 332.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| cross_web-0.7.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 357.6 kB
Release files / cross_web-0.7.0.tar.gz
| Download URL | cross_web-0.7.0.tar.gz |
|---|---|
| Size | 332.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
15fbc8b9a824a055db8127fd6e43e0773074f620fdecb6b2b587d3d0a2bdd459
|
|
BLAKE2b-256 checksum How to use checksums |
4aa0bdb8370215987cc5bde497cb8b976b17ab14841566ecef2aab6ae3e6c7b0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.11.15 {"installer":{"name":"uv","version":"0.11.15","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / cross_web-0.7.0-py3-none-any.whl
| Download URL | cross_web-0.7.0-py3-none-any.whl |
|---|---|
| Size | 25.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ddea9be3c68b48eaf16561847a5831a559786949c544b3701432e00a4e8d19d9
|
|
BLAKE2b-256 checksum How to use checksums |
7a4a78b52ec2edcbd9b123638f4e0421fd8699ebe653ebf63ac5f82abd2665bc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.11.15 {"installer":{"name":"uv","version":"0.11.15","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|