Python Series track

Week 09 · Advanced Track

Week 9 — Packaging & Distribution

Turn your Python code into a distributable package. Learn pyproject.toml, build tools (hatch/setuptools), create CLI entry points, and publish to PyPI (or TestPyPI).

Advanced Track2 min readFree lesson

Level: Advanced | Prerequisite: Week 8

Learning Goals

  • Understand the structure of pyproject.toml
  • Build and install your own package
  • Provide CLI tools through console_scripts
  • Virtual environment (venv)
  • Upload to TestPyPI

1. Virtual Environment

Separate library versions for each project.

python -m venv .venv

#Activate
source .venv/bin/activate         # macOS/Linux
.venv\Scripts\activate            # Windows

#Terminate
deactivate

2. pyproject.toml

Standard configuration file for modern Python packages.

[project]
name = "mytool"
version = "0.1.0"
description = "My awesome CLI tool"
authors = [{name = "Hong Gil-dong", email = "[email protected]"}]
requires-python = ">=3.10"
dependencies = [
    "requests>=2.28",
    "rich>=13.0",
]

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

[build-system]
requires = ["setuptools>=61"]
build-backend = "setuptools.build_meta"

3. Package Structure

mytool_project/
├── pyproject.toml
├── README.md
├── LICENSE
├── src/
│   └── mytool/
│       ├── __init__.py
│       └── cli.py
└── tests/
    └── test_cli.py
# src/mytool/cli.py
def main():
    print("Hello from mytool!")

Installation:

pip install -e .          #Development Mode (Changes are Applied Immediately)
mytool                    #Run CLI

4. Build

pip install build
python -m build           #Create .whl and .tar.gz in dist/

5. Upload to TestPyPI

pip install twine
twine upload --repository testpypi dist/*

Install in a different environment:

pip install --index-url https://test.pypi.org/simple/ mytool

6. requirements.txt vs pyproject.toml

requirements.txt pyproject.toml
Purpose Environment Pinning (deploy) Library Metadata
Version Exact Pin Range
Build Not supported Supported
pip freeze > requirements.txt   #Export Current Environment
pip install -r requirements.txt #Reproduce

Key Examples (examples/)

File Content
pyproject.toml Package configuration
src/mytool/__init__.py Package
src/mytool/cli.py CLI entry point
README_PROJECT.md Package README example

⚠️ Common Mistakes

  1. Not activating the virtual environment — Installed in system Python
  2. Omitting __init__.py — Not recognized as a package
  3. Not using src/ layout — Possible import conflicts
  4. Redistribute without bumping version — PyPI rejects reuploading the same version

❓ FAQ

Q1. Don’t we use setup.py? A. For new projects, use pyproject.toml. Only for legacy compatibility, use setup.py.

Q2. poetry vs pip+venv? A. Poetry is an integrated tool (dependency + virtual environment + build). For new projects, poetry or uv is recommended.

Q3. What's the difference between PyPI and TestPyPI? A. PyPI is the real public repository. TestPyPI is for practice.

📝 Exercises (exercises/)

  • exercise1.md — Package the results of the intermediate comprehensive practice
  • exercise2.md — Install console_scripts CLI
  • exercise3.md — Upload to TestPyPI

Next Week

Week 10. Advanced Comprehensive Practice

Example files

The runnable files used in this lesson. Copy them into your own project and experiment.

examples/pyproject.tomlTOML
[project]
name = "mytool"
version = "0.1.0"
description = "My awesome CLI tool"
requires-python = ">=3.10"
dependencies = []

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

[build-system]
requires = ["setuptools>=61"]
build-backend = "setuptools.build_meta"
examples/README_PROJECT.mdMarkdown
# mytool

Sample package for learning.

## Installation

```bash
pip install -e .
```

## Usage

```bash
mytool --name Hong Gil-dong
#Hello, Hong Gil-dong!
```
examples/src/mytool/__init__.pyPython
"""mytool — CLI tool for learning"""
__version__ = "0.1.0"
examples/src/mytool/cli.pyPython
"""mytool CLI entry point"""
import argparse

def main():
    parser = argparse.ArgumentParser(prog="mytool")
    parser.add_argument("--name", default="World")
    parser.add_argument("--version", action="version", version="mytool 0.1.0")
    args = parser.parse_args()
    print(f"Hello, {args.name}!")

if __name__ == "__main__":
    main()

Assignment

Goal

Convert the vocabulary app from Week 10 of the intermediate course into a package based on pyproject.toml.

Requirements

wordbook/
├── pyproject.toml
├── src/wordbook/
│   ├── __init__.py
│   ├── models.py
│   ├── storage.py
│   └── cli.py
└── README.md

Verification

pip install -e .
wordbook --help

Assignment 2. Install console_scripts CLI

Goal

Ensure that after installing the package, it can be run directly from the terminal with the mytool command.

Requirements

  • Utilize [project.scripts] in pyproject.toml
  • Verify that the mytool command works after pip install -e .

Input/Output Example

$ pip install -e .
$ mytool --version
mytool 0.1.0
$ mytool --name Alice
Hello, Alice!

Assignment 3. Upload to TestPyPI

Goal

Actually build the package and upload it to TestPyPI.

Requirements

  • Create a TestPyPI account (https://test.pypi.org)
  • Issue an API token
  • pip install build twine
  • python -m build
  • twine upload --repository testpypi dist/*
  • Verify installation in a different environment:

pip install --index-url https://test.pypi.org/simple/


## Note
- Package names must be unique across PyPI
- Re-uploading the same version is not allowed → increase version
Show the reference solution

Try the assignment yourself first — comparing your attempt with a working solution is where most of the learning happens.

README_GUIDE.mdMarkdown
# Answer Guide — Packaging Step by Step

## 1. Creating Folder Structure

```
wordbook/
├── pyproject.toml
├── README.md
├── src/
│   └── wordbook/
│       ├── __init__.py
│       ├── models.py     (Intermediate Week 10 Code)
│       └── cli.py
└── tests/
    └── test_models.py
```

## 2. pyproject.toml

```toml
[project]
name = "wordbook"
version = "0.1.0"
description = "Console Vocabulary App"
requires-python = ">=3.10"

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

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

## 3. Installation

```bash
cd wordbook
python -m venv .venv
source .venv/bin/activate    # Windows: .venv\Scripts\activate
pip install -e .
wordbook                      #Execute immediately
```

## 4. Build

```bash
pip install build
python -m build
ls dist/
# wordbook-0.1.0-py3-none-any.whl
# wordbook-0.1.0.tar.gz
```

## 5. Upload to TestPyPI

```bash
pip install twine
twine upload --repository testpypi dist/*
```

## 6. Verification in Other Environments

```bash
python -m venv test_env && source test_env/bin/activate
pip install --index-url https://test.pypi.org/simple/ wordbook
wordbook
```

Learn this lesson with a mentor

Work through the Python Series track on your own, or join a guided cohort at our Pitampura campus and online.

Call NowEnroll Now