Command Palette

Search for a command to run…

ZarynZaryn
Packages and mirrors·3 min read·

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.

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).