0tokens

Apply for AI Grants India

Financial support for innovators building the future of AI in India.

Apply now

Chat · python pypi package

How to Build and Publish a Python PyPI Package

  1. aigi

    PyPI is the distribution channel behind much of the Python ecosystem. For an Indian developer, research team, startup, or open-source maintainer, publishing a package can turn an internal utility into a reusable product—but only if the package is easy to install, well documented, tested, and maintained.

    This guide shows a modern workflow for creating a Python PyPI package in 2026. It uses pyproject.toml, isolated environments, standard build tools, TestPyPI, and trusted publishing rather than relying on the older setup.py-first approach.

    What PyPI does—and does not do

    The Python Package Index is a public registry for Python distribution files and their metadata. Users typically install packages with pip, while maintainers upload built artifacts with tools such as twine or a CI platform configured for trusted publishing.

    PyPI provides:

    • Package discovery and version history.
    • Distribution of source archives and wheels.
    • Dependency metadata used by installers.
    • Project pages containing documentation from your README.
    • Namespace ownership and release management.

    PyPI is not a substitute for source control, documentation hosting, security review, or support operations. Keep your source code in a repository such as GitHub or GitLab, and link it from the package metadata.

    A reusable package is especially valuable when it supports work such as Python scripts for automating data preprocessing, where consistent installation and dependency handling matter across notebooks, servers, and CI environments.

    Choose a package boundary before writing code

    Start with one clear problem. A package that converts Indian address data, wraps an internal model endpoint, validates API responses, or provides a reusable ML utility is easier to explain than a collection of unrelated helpers.

    Define:

    • Public API: Functions, classes, and command-line commands users should rely on.
    • Supported Python versions: Test only versions you intend to support.
    • Dependencies: Separate runtime, optional, development, and documentation dependencies.
    • License: Choose a license that matches how others may use your code.
    • Compatibility promise: State what counts as a breaking change.

    If the package will support AI applications, keep provider-specific integrations behind stable interfaces. A focused library can then serve projects involving LLM APIs in Python web apps without forcing every user to adopt the same web framework or deployment model.

    Use a modern project layout

    A src layout prevents accidental imports from the repository root and catches packaging mistakes earlier. A practical structure is:

    my-package/
    ├── pyproject.toml
    ├── README.md
    ├── LICENSE
    ├── src/
    │   └── my_package/
    │       ├── __init__.py
    │       └── core.py
    ├── tests/
    │   └── test_core.py
    └── .github/workflows/
        └── publish.yml

    The distribution name on PyPI may contain hyphens, while the import name normally uses underscores. For example, users might install my-package and write import my_package.

    Configure metadata in pyproject.toml

    Modern Python packaging tools read project metadata from pyproject.toml. Hatchling, Setuptools, and PDM are all viable build backends; choose one and follow its documentation consistently. The following example uses Hatchling:

    [build-system]
    requires = ["hatchling"]
    build-backend = "hatchling.build"
    
    [project]
    name = "my-package"
    version = "0.1.0"
    description = "A concise description of the package"
    readme = "README.md"
    requires-python = ">=3.10"
    license = { file = "LICENSE" }
    authors = [{ name = "Your Name", email = "you@example.com" }]
    dependencies = [
      "httpx>=0.27,<1.0"
    ]
    
    [project.optional-dependencies]
    dev = ["pytest", "ruff", "mypy", "build"]
    
    [project.urls]
    Homepage = "https://github.com/example/my-package"
    Issues = "https://github.com/example/my-package/issues"

    Avoid placing secrets, tokens, private URLs, or environment-specific configuration in this file. Keep dependencies bounded thoughtfully: overly loose ranges can introduce surprise breakage, while overly strict pins make reuse difficult.

    Write a small, stable public API

    Keep internal implementation details private and expose only what users need. For example:

    # src/my_package/core.py
    
    def greet(name: str) -> str:
        """Return a greeting for a non-empty name."""
        if not name.strip():
            raise ValueError("name must not be empty")
        return f"Hello, {name}!"

    Use type hints, docstrings, predictable exceptions, and deterministic behaviour. If your package handles data or model outputs, document encoding, timezone, missing-value, and retry assumptions explicitly. These details matter more than a long feature list when the package enters a production end-to-end ML pipeline in Python.

    Test the package as an installed user would

    Install development dependencies in an isolated environment:

    python -m venv .venv
    source .venv/bin/activate       # Linux/macOS
    # .venv\Scripts\activate       # Windows
    python -m pip install --upgrade pip
    python -m pip install -e ".[dev]"
    pytest

    Test behaviour, not private implementation details. Include cases for invalid input, empty values, network failures, and supported Python versions. Run format and quality checks as well:

    ruff check .
    ruff format --check .
    mypy src

    Before building, verify that the package works from a clean environment rather than only through an editable install. This catches missing package files and undeclared dependencies.

    Build and inspect distribution files

    Install the build frontend and create both a wheel and source archive:

    python -m pip install build
    python -m build

    The dist/ directory should contain files similar to:

    my_package-0.1.0-py3-none-any.whl
    my_package-0.1.0.tar.gz

    Inspect the artifacts before uploading:

    python -m pip install twine
    python -m twine check dist/*

    For packages with compiled extensions, platform-specific wheels, or large AI dependencies, test the built wheel separately. A package that works from source may fail when installed from the artifact users actually download.

    Use TestPyPI before the real index

    TestPyPI is a separate registry for release validation. Create an account there, upload a unique version, and install it into a fresh environment:

    python -m twine upload --repository testpypi dist/*
    python -m pip install --index-url https://test.pypi.org/simple/ \
        --extra-index-url https://pypi.org/simple/ my-package==0.1.0

    The extra index is useful when your package depends on public packages that are not mirrored on TestPyPI. Check the README rendering, metadata, imports, command-line entry points, and dependency resolution.

    Publish securely to PyPI

    Create the project on PyPI through the first upload, or configure the project after it exists. For manual releases, use an API token rather than a password:

    python -m twine upload dist/*

    For a serious project, prefer trusted publishing from GitHub Actions or another supported CI provider. It avoids storing a long-lived PyPI token in repository secrets and can release only from a protected tag or approved workflow. Restrict who can create release tags, require review for workflow changes, and keep build logs free of credentials.

    Never upload .env files, private keys, customer data, model weights with unclear rights, or generated files containing secrets. Review the archive contents before every release.

    Versioning and maintenance

    PyPI does not allow replacing a file uploaded under the same version. To correct a release, increment the version and publish a new artifact. Follow semantic versioning where it fits:

    • Patch releases fix backwards-compatible defects.
    • Minor releases add backwards-compatible features.
    • Major releases may change the public API.

    Maintain a changelog, deprecation notices, supported Python versions, and migration examples. Pin your own production applications with lockfiles, but avoid unnecessarily pinning every dependency in the published library metadata.

    Monitor dependency vulnerabilities, rotate credentials, respond to issue reports, and test against new Python releases before claiming support. If a package becomes part of a startup's data or AI stack, document operational limits such as rate limits, latency, licensing, and data residency.

    A release checklist

    Before publishing a version, confirm that:

    • The package name is available and not confusingly similar to another project.
    • Metadata, license, README, homepage, and supported Python versions are correct.
    • Tests pass in clean environments.
    • The wheel and source archive install successfully.
    • twine check reports no metadata or rendering errors.
    • Secrets and unwanted files are absent from dist/.
    • The version has changed since the previous release.
    • The release is tested on TestPyPI or an equivalent staging process.
    • CI permissions and publishing rules are restricted.

    A well-built Python PyPI package is more than an uploaded ZIP file. It is a maintained contract between your code and its users—one that should make installation predictable, upgrades understandable, and contribution straightforward. That discipline also improves technical portfolios, including Python developer portfolio projects for university students, because reviewers can inspect a real release workflow instead of a code dump.

    FAQ

    Can I publish a private package on public PyPI?
    No. Anything on PyPI is publicly accessible. Use a private package index or repository service for proprietary code.

    Do I still need `setup.py`?
    Usually not for project configuration. Modern projects can define metadata in pyproject.toml; some tools may still generate or use compatibility files.

    Should I upload a wheel, source archive, or both?
    Upload both when possible. Wheels install faster, while source archives provide a portable fallback and are important for some build workflows.

    Can I delete a published release?
    PyPI has limited deletion semantics and cached downloads may persist. Treat every release as public and immutable; publish a corrected version instead.

    Apply for AI Grants India

    If you are building an India-focused AI library, developer tool, or open-source infrastructure project, explore support through AI Grants India.

    Last updated 24 September 2026

AIGI may be inaccurate. Replies seeded from the guide above.