python

How to Build and Publish a Professional Python Package with Poetry

Learn to create, test, and publish a robust Python package using Poetry, pre-commit hooks, and GitHub Actions.

How to Build and Publish a Professional Python Package with Poetry

It’s my turn to show you something. For years, I’ve seen brilliant Python code trapped in messy, hard-to-share projects. A great script sits on one computer. A useful module gets copied and pasted until it breaks. This happens because the step from writing code to building a real, shareable package feels murky and complex. Today, that changes. I’m going to walk you through creating a Python package so robust, so clean, and so automated that you’ll be proud to share it with the world. Follow along, and by the end, you’ll not just have code—you’ll have a product.

Why does this matter now? Because the way we build software has evolved. Sharing a requirements.txt file and a setup.py is no longer enough. Modern tools help us create packages that are consistent, secure, and easy to maintain from day one. This is about setting a professional standard from the very first line of code.

Let’s start with the foundation: dependency management. Have you ever had a project work on your machine but fail elsewhere? The classic “it works for me” problem often starts here. We’re going to use Poetry. It replaces the jumble of setup.py, requirements.txt, and setup.cfg with one clear file: pyproject.toml.

poetry new my_awesome_package
cd my_awesome_package

This command creates a logical structure for you. Inside the new pyproject.toml file, you define everything about your package.

[tool.poetry]
name = "my_awesome_package"
version = "0.1.0"
description = "A package built the right way."
authors = ["Your Name <you@example.com>"]

[tool.poetry.dependencies]
python = "^3.9"
pydantic = "^2.5.0"

[tool.poetry.group.dev.dependencies]
pytest = "^7.4.0"
black = "^23.0.0"
ruff = "^0.1.0"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

Run poetry install. This does two key things. It creates a dedicated virtual environment for your project and generates a poetry.lock file. This lock file is your guarantee. It ensures that every developer, and every deployment system, uses the exact same versions of every library. Consistency problems? Solved.

Now, let’s write some actual code. Imagine we’re building a simple utility to check if a string is a palindrome. We’ll put our core logic in my_awesome_package/core.py.

def is_palindrome(text: str, ignore_spaces: bool = False) -> bool:
    """
    Check if a string reads the same forwards and backwards.

    Args:
        text: The string to check.
        ignore_spaces: If True, spaces are removed before checking.

    Returns:
        True if the string is a palindrome, False otherwise.
    """
    if not isinstance(text, str):
        raise TypeError("Input must be a string.")

    to_check = text.lower()
    if ignore_spaces:
        to_check = to_check.replace(" ", "")

    return to_check == to_check[::-1]

It’s a simple function. But is it good code? How can you be sure? You could manually run linters and formatters every time you save, but you’ll forget. This is where automation before you commit changes the game. It’s called a pre-commit hook.

Think of it as a quality checkpoint that runs automatically. Before your code is saved to Git, it must pass. Here’s how you set it up. First, install the pre-commit tool in your Poetry environment: poetry add --group dev pre-commit. Then, create a file called .pre-commit-config.yaml in your project’s root.

repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.5.0
    hooks:
      - id: trailing-whitespace
      - id: end-of-file-fixer
      - id: check-yaml

  - repo: https://github.com/psf/black
    rev: 23.12.0
    hooks:
      - id: black

  - repo: https://github.com/charliermarsh/ruff-pre-commit
    rev: v0.1.8
    hooks:
      - id: ruff
        args: [--fix, --exit-non-zero-on-fix]

  - repo: https://github.com/pre-commit/mirrors-mypy
    rev: v1.7.0
    hooks:
      - id: mypy
        additional_dependencies: [types-all]

Run pre-commit install. Now, try to commit some messy code. You’ll see Black instantly reformat it. Ruff will flag unused imports. Mypy will catch type mismatches. Your code is cleaned and checked before it even enters the repository. How much time does this save you in code reviews?

But what about when you collaborate? Or when you want to make sure your package works on different Python versions? This is where continuous integration (CI) comes in. We can use GitHub Actions to create a robotic assistant that tests every single change.

Create the directory .github/workflows/ and inside it, a file named test.yml.

name: Test Package

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ["3.9", "3.10", "3.11", "3.12"]

    steps:
    - uses: actions/checkout@v4
    - name: Set up Python ${{ matrix.python-version }}
      uses: actions/setup-python@v4
      with:
        python-version: ${{ matrix.python-version }}

    - name: Install Poetry
      run: pipx install poetry

    - name: Install dependencies
      run: poetry install --with dev

    - name: Run linting and formatting
      run: poetry run pre-commit run --all-files

    - name: Run tests with pytest
      run: poetry run pytest --cov=./ --cov-report=xml

    - name: Upload coverage to Codecov
      uses: codecov/codecov-action@v3
      with:
        file: ./coverage.xml

This workflow is your safety net. Every time you or a teammate pushes code, it fires up a fresh virtual machine. It installs your package with Poetry and runs your entire quality pipeline. It does this across four different Python versions. If a test fails on Python 3.12, you’ll know immediately. The package is proven to be robust.

Finally, the moment of truth: sharing your work. Publishing to the Python Package Index (PyPI) with Poetry is straightforward. Configure your credentials, then run a single command.

poetry config pypi-token.pypi your-api-token
poetry publish --build

But here’s a pro tip: don’t run this manually. Automate it too. You can extend your GitHub Actions workflow to publish a new version to PyPI automatically whenever you create a Git tag like v1.0.2. The entire process—testing, building, publishing—becomes a seamless, reliable pipeline.

So, what have we built? We started with an idea and used Poetry to give it a solid, reproducible structure. We enforced code quality at the very door of our repository with pre-commit hooks. We built a CI system that acts as a tireless guardian, proving our package works everywhere. Finally, we made sharing it as simple as tagging a release.

This isn’t just about making a package. It’s about adopting a professional practice that saves you time, reduces errors, and builds confidence. Your code deserves this level of care. Take these steps, apply them to your next project, and feel the difference. What will you build and share with the world?

If this guide clarified the path for you, please share it with a fellow developer. Have questions or your own tips? Leave a comment below—let’s keep the conversation going


As a best-selling author, I invite you to explore my books on Amazon. Don’t forget to follow me on Medium and show your support. Thank you! Your support means the world!


101 Books

101 Books is an AI-driven publishing company co-founded by author Aarav Joshi. By leveraging advanced AI technology, we keep our publishing costs incredibly low—some books are priced as low as $4—making quality knowledge accessible to everyone.

Check out our book Golang Clean Code available on Amazon.

Stay tuned for updates and exciting news. When shopping for books, search for Aarav Joshi to find more of our titles. Use the provided link to enjoy special discounts!


📘 Checkout my latest ebook for free on my channel!
Be sure to like, share, comment, and subscribe to the channel!


Our Creations

Be sure to check out our creations:

Investor Central | Investor Central Spanish | Investor Central German | Smart Living | Epochs & Echoes | Puzzling Mysteries | Hindutva | Elite Dev | JS Schools


We are on Medium

Tech Koala Insights | Epochs & Echoes World | Investor Central Medium | Puzzling Mysteries Medium | Science & Epochs Medium | Modern Hindutva

Keywords: python packaging,poetry python,pre-commit hooks,github actions,python ci cd



Similar Posts
Blog Image
Production-Ready GraphQL API with Strawberry FastAPI: Authentication, Caching, and Performance Optimization Guide

Learn to build production-ready GraphQL APIs using Strawberry & FastAPI with JWT auth, Redis caching, WebSocket subscriptions & performance optimization. Complete tutorial included.

Blog Image
Production-Ready Microservices with FastAPI, SQLAlchemy, and Docker: Complete Implementation Guide

Learn to build scalable production-ready microservices with FastAPI, SQLAlchemy & Docker. Complete guide covering auth, testing, deployment & best practices.

Blog Image
Build Type-Safe Event-Driven Systems with Python: Pydantic, asyncio, and Redis Pub/Sub Complete Guide

Learn to build scalable, type-safe event-driven systems in Python using Pydantic, asyncio, and Redis Pub/Sub. Master async event processing & error handling.

Blog Image
FastAPI Microservices: Event-Driven Architecture with Apache Kafka and Pydantic Complete Guide

Learn to build scalable event-driven microservices using FastAPI, Apache Kafka, and Pydantic. Master async messaging, event sourcing, and production deployment techniques.

Blog Image
Build Production-Ready Background Task Processing with Celery and Redis in Python 2024

Learn to build production-ready background task processing with Celery and Redis in Python. Complete guide covering setup, advanced patterns, monitoring, and deployment strategies.

Blog Image
Build Production-Ready GraphQL APIs with Strawberry and FastAPI: Complete Developer Guide

Learn to build production-ready GraphQL APIs with Strawberry and FastAPI. Complete guide covers schema design, authentication, performance optimization, and deployment.