Python
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax and Data Types
-
Section 3: Collections
-
39: Set Operations: Union, Intersection, Difference
-
Section 4: Control Flow
-
Section 5: Functions
-
Section 6: Turtle Graphics and Early Practice Projects
-
Section 7: Working with Files and I/O
-
Section 8: Regular Expressions
-
Section 9: Object-Oriented Python
-
Section 10: Error Handling
-
Section 11: Modules and Packages
-
Section 12: Iterators, Generators, and Functional Tools
-
Section 13: Decorators and Metaprogramming
-
Section 14: Concurrency and Parallelism
-
Section 15: Working with Dates, Times, and Numbers
-
Section 16: Standard Library Deep Dive I: Data Structures
-
Section 17: Standard Library Deep Dive II: System and Introspection
-
Section 18: Standard Library Deep Dive III: Security and Encoding
-
Section 19: Standard Library Deep Dive IV: Text and Data Utilities
-
Section 20: Networking and Web Basics
-
Section 21: Working with Databases
-
Section 22: Testing and Quality
-
Section 23: Advanced Typing
-
Section 24: Context Managers and Resource Handling
-
Section 25: Text, Unicode, and Binary Data
-
Section 26: More Functional and Iteration Tools
-
Section 27: Data Validation and Configuration
-
Section 28: Working with Images and Media
-
Section 29: Property-Based and Documentation Testing
-
Section 30: Packaging and Deployment
-
Section 31: Performance and Internals
-
Section 32: Design Patterns in Python
-
Section 33: GUI Programming
-
Section 34: Security Basics
-
Section 35: Data Structures and Algorithms
-
Section 36: Practical Projects
-
Section 37: Capstone Projects
-
Section 38: Interview and Algorithm Practice
-
Section 39: Writing Idiomatic Python
345: Publishing a Package to PyPI
I've got this little utility script I've been using for months called title-scrubber. It does one thing: it takes a messy news headline—you know, the ones with "[BREAKING]" or "(EXCLUSIVE)" tacked onto the front—and cleans them up for a database. I'm tired of copying the scrubber.py file into every new project I start. It's time to just put it on PyPI so I can pip install it and move on with my life.
Why can't I just import this?
Right now, my code is just sitting in a folder. If I try to import it from another directory, Python tells me ModuleNotFoundError. I could mess around with sys.path, but that's a hack. To make this a real package, I need to structure it properly. I'll move the logic into a directory called title_scrubber and add an __init__.py file so Python treats the directory as a package.
title-scrubber/
├── pyproject.toml
└── src/
└── title_scrubber/
├── __init__.py
└── cleaner.py
I've put the actual cleaning logic in cleaner.py. But if I try to run pip install . from the root, it might work locally, but it's not "packaged." It's just a local link. If I want other people (or my future self on a different machine) to use it, I need a blueprint.
The missing blueprint
Back in the day, we used setup.py for everything. Now, the standard is pyproject.toml. It's cleaner and doesn't require executing a Python script just to find out what the package metadata is. I'll create one now.
[build-system]
requires = ["setuptools>=61.0"]
build-backend = "setuptools.build_meta"
[project]
name = "title-scrubber-yourname" # I'll add my name so it doesn't clash with existing packages
version = "0.1.0"
authors = [
{ name="Your Name", email="you@example.com" },
]
description = "A small utility to strip junk from news headlines"
readme = "README.md"
requires-python = ">=3.7"
classifiers = [
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
]
I noticed I forgot the README.md. PyPI uses that for the project home page, and if it's missing, the upload might complain or just look empty. I'll whip up a quick markdown file explaining how to use title_scrubber.clean().
Baking the wheel
Now I have the code and the blueprint, but PyPI doesn't take raw source folders. It wants "distributions"—specifically source distributions (sdist) and wheels. I'll use the build module for this.
pip install build
python -m build
I see a new dist/ folder appeared. Inside, there's a .tar.gz file and a .whl file. The wheel is the "built" version that makes installation fast for the end user. I'm tempted to just upload these now, but I've made mistakes before—like typos in the version number—and once something is on PyPI, you can't "delete" a version; you can only yank it, which is a mess.
The TestPyPI safety net
I'm going to use TestPyPI first. It's a separate instance of the package index specifically for testing. To upload, I'll use twine, which is the industry standard for securely pushing packages.
pip install twine
python -m twine upload --repository testpypi dist/*
It asked for a username and password. I realized quickly that PyPI now heavily pushes API tokens for security. I'll head to my account settings, generate an "API Token," and use __token__ as the username and the token string as the password. Much better than putting my main password in a terminal.
Now I can try installing it from the test server: pip install --index-url https://test.pypi.org/simple/ title-scrubber-yourname. It works. The headlines are getting scrubbed, and the versioning is correct.
Going live on the real PyPI
The final jump is easy. Since the TestPyPI run was successful, I just run the upload command again, but this time I omit the --repository testpypi flag to target the production server.
python -m twine upload dist/*
And that's it. I can now go to any machine, run pip install title-scrubber-yourname, and my utility is right there. It feels a bit overkill for a 20-line script, but the workflow is the same whether it's a tiny utility or the next big data science library.
📋 Practical Task
Publishing the "Slugify-Pro" Utility
Your task is to package and prepare a small utility called slugify-pro for publication. This utility should take a string and convert it into a URL-friendly slug (e.g., "Hello World! This is Python" becomes "hello-world-this-is-python").
- The Code: Create a directory structure with
src/slugify_pro/containing an__init__.pyand aslugifier.pyfile that implements the slugging logic. - The Blueprint: Create a
pyproject.tomlfile. Ensure you give the package a unique name (e.g.,slugify-pro-yourusername) to avoid naming conflicts on the public index. - The Build: Use the
buildmodule to generate thedist/folder containing the wheel and source distribution. - The Verification: Use
twine check dist/*to verify that your package description and metadata are valid before attempting any upload.
Note: You do not need to actually upload to the real PyPI for this exercise, but you must provide the pyproject.toml and the command sequence you would use to move from code to the final dist/ folder.
There are no comments for now.