Python
This page covers the requirements and internals of deploying Python content to ricochet.
Project requirements
Section titled “Project requirements”Every Python deployment requires two files in the project root:
| File | Purpose |
|---|---|
.python-version | Specifies the target Python version (e.g. 3.12 or 3.12.7). |
uv.lock | The lockfile generated by uv lock. |
The version in .python-version must satisfy the requires-python constraint recorded in uv.lock.
Example _ricochet.toml
Section titled “Example _ricochet.toml”[content]name = "My FastAPI App"entrypoint = "app.py"access_type = "external"content_type = "fast-api"
[language]name = "python"packages = "uv.lock"
[serve]min_instances = 0max_instances = 5Supported content types
Section titled “Supported content types”Python content items can be deployed as apps or tasks.
Content Type | Description |
|---|---|
fast-api | An API developed using FastAPI. Launched with uvicorn. |
flask | A web app or API developed using Flask. |
streamlit | A data app developed using Streamlit. |
dash | A data app developed using Dash. |
shiny-py | A web app developed using Shiny for Python. |
python-service | A generic Python web app or API. The entrypoint script is run directly; it must bind to PORT from the environment. |
Content Type | Description |
|---|---|
python | A Python script with the extension .py. |
quarto-py | A Quarto document using a Python runtime. Can be served as a static site. |
How ricochet runs Python content
Section titled “How ricochet runs Python content”uv binary resolution
Section titled “uv binary resolution”Ricochet resolves the uv binary in the following order:
- Vendored —
$RICOCHET_HOME/vendor/py/uv(installed with the ricochet package) - System PATH — falls back to
uvonPATHif the vendored binary is not present
Python version discovery
Section titled “Python version discovery”Ricochet discovers Python installations on the host machine.
We recommend installing Python versions via uv for the best experience:
uv python install 3.12When a deployment is received, ricochet matches the version in .python-version against available installations and selects the closest match.
Environment restoration
Section titled “Environment restoration”When a bundle is deployed, ricochet restores the Python environment using:
uv sync --python <matched-python> --cache-dir $RICOCHET_HOME/.cache/uv --no-python-downloads --frozenThe --frozen flag ensures the lockfile is used exactly as provided — no resolution or updates occur on the server.
The --no-python-downloads flag prevents uv from downloading Python versions at deploy time; they must already be installed on the host.
uv cache
Section titled “uv cache”Resolved packages are cached at $RICOCHET_HOME/.cache/uv.
This directory is created automatically during the first Python deployment.
The cache is shared across all Python content items on the server.
Using the API
Section titled “Using the API”You can deploy and manage Python content programmatically using the ricochet REST API.
The examples below use requests.
API keys are created in the ricochet UI under Credentials → API Keys.
Deploy a bundle
Section titled “Deploy a bundle”Bundle your project as a .tar.gz archive and upload it with the _ricochet.toml config.
import osimport tarfileimport tempfileimport requests
server = "https://try.ricochet.rs"api_key = os.environ["RICOCHET_API_KEY"]headers = {"Authorization": f"Key {api_key}"}
# Create a tar.gz bundle of the project directorybundle_path = tempfile.mktemp(suffix=".tar.gz")with tarfile.open(bundle_path, "w:gz") as tar: tar.add(".", arcname=".")
# Deploy new contentresp = requests.post( f"{server}/api/v0/content/upload", headers=headers, files={ "bundle": ("bundle.tar.gz", open(bundle_path, "rb"), "application/gzip"), "config": ("_ricochet.toml", open("_ricochet.toml", "rb"), "application/toml"), },)result = resp.json()result["id"]#> "01JSZAXZ3TSTAYXP56ARDVFJCJ"To redeploy existing content, pass id instead of config:
resp = requests.post( f"{server}/api/v0/content/upload", headers=headers, files={"bundle": ("bundle.tar.gz", open(bundle_path, "rb"), "application/gzip")}, data={"id": "01JSZAXZ3TSTAYXP56ARDVFJCJ"},)Invoke a task
Section titled “Invoke a task”content_id = "01JSZAXZ3TSTAYXP56ARDVFJCJ"
resp = requests.post( f"{server}/api/v0/content/{content_id}/invoke", headers=headers, json={"params": {"n": 100}},)resp.json()List your content
Section titled “List your content”resp = requests.get( f"{server}/api/v0/user/items", headers=headers,)items = resp.json()