# Publish your first Python package in 5 minutes

> A real Python package from an empty folder to pip install, step by step, using tinyhash (published on Zaryn) as the example. Project layout, a name that's free, building, a publish token, uploading, installing, putting the code on Zaryn, and the next version.

Source: https://zaryn.xyz/docs/en/publish-python-package

Everything below happened for real: [tinyhash](/arvand/tinyhash) is a small Python tool, published on Zaryn this way. Try it first:

```bash
pip install --index-url https://api.zaryn.xyz/v1/packages/pypi/all/simple/ tinyhash
tinyhash .
```

```diagram
type: flow
title: From a folder of code to pip install
---
Write | Your code and a pyproject.toml | | 📝
~ python -m build
Build | A wheel and a source archive | | 🛠
~ twine upload, with a publish token
Publish | On Zaryn, under a name nobody else has | accent | ⇡
~ pip install
Anyone installs it | From the same index as everything on pypi.org | good | ✓
```

## 1. Lay out the project

```diagram
type: cards
---
📄 | pyproject.toml | The package's name, version, description and command | accent
📁 | src/tinyhash/ | The code: `__init__.py` and the modules |
🧪 | tests/ | So you know it works before anyone else installs it |
📘 | README.md | Shown on the package's page and the repository's front page | good
⚖ | LICENSE | Tells people what they may do with it (MIT is short and common) |
```

The whole `pyproject.toml` of tinyhash:

```toml
[build-system]
requires = ["setuptools>=77"]
build-backend = "setuptools.build_meta"

[project]
name = "tinyhash"
version = "0.1.0"
description = "A short fingerprint for any file or folder."
readme = "README.md"
license = "MIT"
requires-python = ">=3.9"

[project.urls]
Source = "https://zaryn.xyz/arvand/tinyhash"

[project.scripts]
tinyhash = "tinyhash.cli:main"

[tool.setuptools.packages.find]
where = ["src"]
```

`[project.scripts]` is what turns `pip install tinyhash` into a `tinyhash` command. Leave it out for a library.

## 2. Pick a name that's free

A **new** name on Zaryn must not exist on pypi.org: that keeps one index safe for everyone (see [why](/docs/en/packages-pypi#choosing-a-name)). Check before you build:

```bash
curl -s -o /dev/null -w "%{http_code}\n" https://pypi.org/pypi/tinyhash/json
```

`404` means free. If it's taken, add your own prefix (`acme-tinyhash`).

## 3. Build it

```bash
python -m pip install build twine
python -m build
twine check dist/*
```

`dist/` now has `tinyhash-0.1.0-py3-none-any.whl` and `tinyhash-0.1.0.tar.gz`. `twine check` makes sure the README will show properly.

## 4. Get a publish token

On [Packages](/packages), choose **Create a publish token**. Copy it at once: it's shown only once. It can publish packages and nothing else, so even if it leaks, nobody can touch your repositories or account with it.

## 5. Upload it

```bash
twine upload --repository-url https://api.zaryn.xyz/v1/packages/pypi/ \
  -u __token__ -p YOUR_TOKEN dist/*
```

`__token__` is typed as it is. The package now has its own page, and anyone can install it:

```bash
pip install --index-url https://api.zaryn.xyz/v1/packages/pypi/all/simple/ tinyhash
```

Its dependencies from pypi.org come through the same address.

## 6. Put the code on Zaryn too

A package is the result; the repository is where people read the code, see the history and send changes. From the project folder:

```bash
zev new
zev add .
zev commit -m "tinyhash 0.1.0"
zev push
```

`zev new` creates the repository and connects it; the README becomes its front page. Add a one-line description in the repository's **Settings**.

## 7. The next version

```diagram
type: cards
---
🔢 | Raise the version | `0.1.0` → `0.1.1` in pyproject.toml: a version once uploaded can't be replaced | accent
🛠 | Build again | Delete the old `dist/` first, then `python -m build` |
⇡ | Upload | The same twine command; pip picks the newest version | good
```

More: private packages, uv and Poetry, and every error explained in [Python packages](/docs/en/packages-pypi).
