---
title: "Python SDK"
description: "Install uv, choose a Python version, and add the Python SDK, tatolab-stream, to a new or an existing project."
type: "docs-page"
url: "https://tatolab.com/docs/install/python"
site: "Tatolab"
section: "Install"
---

# Python SDK

> Install uv, choose a Python version, and add the Python SDK, tatolab-stream, to a new or an existing project.

With the Python SDK, a stream is an ordinary Python project: a `pyproject.toml`, your code, and a virtual environment in `.venv/`. The runtime runs your nodes with that environment's Python, so whatever you install there, your nodes can import.

Tatolab's projects are set up for [uv](https://docs.astral.sh/uv/), a fast Python package and project manager. You can use other tools, as long as the project ends up with a `.venv/` folder.

## Install uv

On macOS or Linux:

```sh
curl -LsSf https://astral.sh/uv/install.sh | sh
```

Or with Homebrew: `brew install uv`. Check it with `uv --version`.

## Python versions

`tatolab-stream` supports CPython 3.10 to 3.13. Use the standard builds; the free-threaded ("no GIL") builds aren't supported.

`tatolab new` pins Python 3.12 in the project's `.python-version` file, and uv downloads that version if you don't have it. You don't need to install Python yourself.

## Start a new project

If you're going on to the quickstart, skip this section: the quickstart creates `my-stream` in its first step.

```sh
tatolab new my-stream
cd my-stream
uv sync
```

`uv sync` creates `.venv/` with Python 3.12, `tatolab-stream` and numpy.

## Add Tatolab to an existing project

```sh
uv add tatolab-stream
uv sync
```

Then:

1. Add a `stream.py` next to your `pyproject.toml`, with one function decorated `@stream`. The [quickstart's](https://tatolab.com/docs/quickstart) project is a good template.
2. Put each node class in a module of its own, such as `nodes/detector.py`, never in `stream.py` itself. Each node runs in its own process, which imports the class by name.
3. Run `tatolab dev` from the project folder.

The runtime looks for Python at `<project>/.venv/bin/python` and nowhere else. If you manage environments without uv, create one there: `python3.12 -m venv .venv`, then `.venv/bin/pip install tatolab-stream`.

## Add packages for your nodes

```sh
uv add torch
```

Every node in the project can now `import torch`. `tatolab dev` notices changes to `pyproject.toml` and reloads the stream, so you don't have to restart it.

## Editor support

`tatolab-stream` ships with complete type hints. Point your editor at the project's `.venv` and you get completion for the API, and a type checker like Pyright will flag a misspelled config key on a built-in node:

```python
stream_builder.add(DisplayWindow, config={"titel": "Oops"})  # flagged: no key "titel"
```

`tatolab-stream` installs and imports without the runtime, so your editor, type checker and unit tests all work on a machine that has no runtime at all.

## Settings and secrets

The runtime starts your nodes, not your shell. Environment variables you `export` in a terminal don't reach them. Your nodes run in the project folder, with the runtime's environment.

So keep a node's settings in its `config` (see [Write a node](https://tatolab.com/docs/build/nodes)), or in a file in the project that the node reads itself. For secrets, a `.env` file read with [python-dotenv](https://pypi.org/project/python-dotenv/) works well:

```sh
uv add python-dotenv
```

```python title="nodes/publisher.py"
import os

from dotenv import load_dotenv

load_dotenv()  # reads .env from the project folder, where the node runs

WHIP_URL = os.environ["WHIP_URL"]
```

Add `.env` to `.gitignore` so it never gets committed.
