πŸ”„ Quick Recap (Day 24)

  • You wrote tests with unittest/pytest and learned how to debug with breakpoints and tracebacks.

🎯 What You’ll Learn Today

  1. A simple project structure for a Python package.

  2. How to configure pyproject.toml with setuptools.

  3. How to build sdist (source) and wheel (binary) files.

  4. How to install your package locally and add a CLI command.

  5. How to (optionally) publish to TestPyPI with twine.

  6. Friendly versioning and metadata tips.

πŸ“– Why Package Your Code?

Packaging lets anyone (including you on another computer) install your project with one command: pip install yourpackage. It’s useful for:

  • Reusing utilities across projects.

  • Sharing tools with classmates or teammates.

  • Creating small command‑line apps you can run from any folder.

❝

Two build types:
β€’ sdist = a source archive of your code.
β€’ wheel = a ready‑to‑install build (faster to install).

πŸ“– Project Layout

Create a new folder, e.g. dmagogreet/, with this structure:

dmagogreet/
β”œβ”€ dmagogreet/
β”‚  β”œβ”€ __init__.py
β”‚  └─ cli.py
β”œβ”€ pyproject.toml
β”œβ”€ README.md
└─ LICENSE   (optional)

What each file does:

  • dmagogreet/: your actual Python package (same name as the project).

  • __init__.py: makes it a package; good place for __version__.

  • cli.py: code for a tiny command‑line tool.

  • pyproject.toml: build configuration (who you are, version, entry points).

  • README.md: a short β€œwhat it does / how to use it.”

dmagogreet/__init__.py

__version__ = "0.1.0"

def greet(name: str) -> str:
    return f"Hello from Digital Mago, {name}!"

dmagogreet/cli.py

from . import greet, __version__


def main() -> None:
    import argparse
    p = argparse.ArgumentParser(description="Digital Mago greeter")
    p.add_argument("name", help="Your name")
    p.add_argument("--shout", action="store_true", help="Uppercase greeting")
    args = p.parse_args()

    msg = greet(args.name)
    if args.shout:
        msg = msg.upper()
    print(f"{msg} (v{__version__})")

README.md

Explain in 4–6 lines what the tool does and show one example command.

πŸ“– pyproject.toml with setuptools

This file tells build tools how to package your project.

[build-system]
requires = ["setuptools>=68", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "dmagogreet"
version = "0.1.0"
description = "A tiny Digital Mago greeting CLI"
readme = "README.md"
requires-python = ">=3.8"
authors = [{ name = "Your Name", email = "[email protected]" }]
license = { text = "MIT" }
classifiers = [
  "Programming Language :: Python :: 3",
  "License :: OSI Approved :: MIT License",
  "Operating System :: OS Independent",
]

# No runtime dependencies for this tiny tool
dependencies = []

[project.scripts]
# Creates a console command named `dmagogreet`
dmagogreet = "dmagogreet.cli:main"

[tool.setuptools.packages.find]
include = ["dmagogreet*"]
❝

Why this matters: project.scripts wires your main() to a real terminal command, so you can type dmagogreet anywhere after install.

πŸ“– Build Your Package

❝

Do these in your virtual environment from Day 16.

  1. Install build tools:

pip install build twine
  1. Build:

python -m build

This creates a dist/ folder with:

  • dmagogreet-0.1.0.tar.gz (sdist)

  • dmagogreet-0.1.0-py3-none-any.whl (wheel)

Try a Local Install

pip install dist/dmagogreet-0.1.0-py3-none-any.whl

Run your CLI:

dmagogreet YourName

Expected output:

Hello from Digital Mago, YourName! (v0.1.0)

Try the shout option:

dmagogreet YourName --shout

Expected output:

HELLO FROM DIGITAL MAGO, YOURNAME! (V0.1.0)

Uninstall during testing:

pip uninstall dmagogreet -y

πŸ“– (Optional) Publish to TestPyPI

  1. Create an account/token on the TestPyPI index.

  2. Upload your files:

twine upload --repository testpypi dist/*
  1. Test install from there (keeping the main PyPI for dependency fallback):

python -m pip install --index-url https://test.pypi.org/simple/ \
  --extra-index-url https://pypi.org/simple dmagogreet==0.1.0

Ready for the real thing later? Replace testpypi with the default repository:

twine upload dist/*

πŸ“– Versioning & Metadata Tips

  • Use Semantic Versioning: MAJOR.MINOR.PATCH (e.g., 0.1.1 for small fixes).

  • Update both __version__ and project.version together.

  • Keep a friendly CHANGELOG.md.

  • Write a clear README with a quick start.

  • Include a LICENSE file (MIT is beginner‑friendly).

🧰 Troubleshooting

  • Command not found after install
    Close and reopen your terminal, or ensure your virtual environment is active. On Windows, the script lives in env\Scripts; on macOS/Linux in env/bin.

  • Build fails
    Check that pyproject.toml is valid TOML (matching quotes/brackets) and your package folder name matches include.

  • Upload denied
    Make sure the version number is new (PyPI/TestPyPI doesn’t allow replacing the same version).

πŸ§™β€β™‚οΈ Take the Wand and Try Yourself

Goal: Ship a tiny package with a CLI and install it locally.

  1. Create the project layout shown above.

  2. Fill in __init__.py, cli.py, pyproject.toml, and README.md.

  3. Build with python -m build.

  4. Install the wheel and run dmagogreet with and without --shout.

  5. Bump the version to 0.1.1, rebuild, reinstall, and confirm the new version prints in the CLI output.

  6. (Optional) Upload to TestPyPI and test remote installation.

Expected terminal output (abridged):

$ python -m build
... Successfully built dmagogreet-0.1.0 ...
$ pip install dist/dmagogreet-0.1.0-py3-none-any.whl
Successfully installed dmagogreet-0.1.0
$ dmagogreet YourName
Hello from Digital Mago, YourName! (v0.1.0)
$ dmagogreet YourName --shout
HELLO FROM DIGITAL MAGO, YOURNAME! (V0.1.0)

Up next: Day 26: Introduction to Frameworks β€” a tour of popular web and data frameworks and when to use each.

Keep Reading