Content from Why Python Packages?


Last updated on 2025-08-25 | Edit this page

Overview

Questions

  • Why do you need to package your python code?
  • What can you actually package?
  • What is Python Package Index (PyPI) and its purpose?
  • What is a build?

Objectives

  • benefits of python packaging
  • does it fit your use case
  • understand some componets of python packaing like PyPi
  • understand the build process

Introduction


We all love to write code, to solve interesting problems. What if we have our code ready and we want to share it across the world. Well there are many ways to do that ofcourse ! By doing this course we will learn to distribute our code via Python Packaging using a tool call Pixi. You can think of the package as setup.exe from yester years.

When we make a package, we solve some key challenges like reproducibility, making it platform independent and also make it multi- environment. Distributing our code as package is better than sharing the souce code via for instance in a Github repository for many reasons :

  • Dependency management : A Package can explicityl declare its deepndencies. Tools like ‘pip’ or ‘uv’ can be then used to automatically install them for a user.

  • Versioning : You version your package and can make and distribute several versions of your code for backwards compatability.

  • Standerdisation : Packaging is an accepted way to distribute the code via repositories like PyPI

  • Metadata : A package has project specific metadata which are important for the end users.

    What can you package ?

    Any .py files (modules) or directories with init.py (packages).

    What is PyPI

    PyPi is the repository where all the Python packages which are released are available for end users to use. You can visit it here to have a look : https://pypi.org/ There is also a repository called https://test.pypi.org/, which allows us to try distribution tools and processes without affecting the real PyPi. In this course , we will publish our package to TestPyPI.

    What is a build ?

    It can be defined as a process of creation of distribution of your project from source code. This distribution can then be installed using tools like ‘pip’ or ‘uv’ etc. You can create a build for your project using the following command in the terminal : python -m build.

    The end product of a successful build process in a .whl or .tar.gz file. These can then be installed via pip or uploaded to PyPI. It is important to version your build and provide necessary metadata.

Key Points
  • The code needs to be versioned.
  • The dependencies of the projects needs to be managed.
  • The metadata for the project needs to be managed.
  • To make our python code installable, reusable and shareable via PyPi or TestPyPI, we need to package our code.

Content from Why choose Pixi for Python Packaging ?


Last updated on 2025-08-25 | Edit this page

Overview

Questions

  • Why Pixi ?
  • What are the benefits of Pixi ?

Objectives

  • To understand the benefits offered by Pixi
  • What is pixi.toml and pyproject.toml
  • What is pixi.lock
  • What do we mean by multi-environment support

Introduction


Pixi is a fast and reproducible package management tool. It has lots of features which are not all present in a single tool at this point in time. Hence we choose this tool. It comes with following features :

  • Native supprt for both PyPI packages and conda
  • Modern
  • Support for multi-language dependency ( E.g. RUST + Python, or Python + C++)
  • Uses uv under the hood.
  • Helps with reproducibility via pixi.lock

Configuration files (pixi.toml and pyproject.toml)


pixi.toml is the file used by Pixi for defining the environment , dependencies and tasks. pyproject.toml is the file in Python ecosystem (PEP 518/621) for configuring the build, distribution and configration of python projects. It is required in build tools like Poetry, Hatch, Flit , setuptools. It specifies metadata (name, version, author). you can also specifiy dependencies here.

Multi-environment support


You can specify the installation of certain tools and packages specific to a particular OS or environmentsl like ( dev, test prod) etc.

Content from Set up a project directory


Last updated on 2025-08-25 | Edit this page

Overview

Questions

  • How should we structure our project ?
  • What is init.py ?
  • How should be name our packages ?

Objectives

  • To understand how to structure your project to be able to package it well.
  • Understand the importance of init.py and where to place it.
  • Giving unique but understandable names to our packages.

Introduction


It is important to structure your project properly and include the necessary files, while giving unique yet understandable package names.

Packages are a way to organize modules in a Python project. A project can contain several modules, and grouping them into packages helps keep the code organized and maintainable.

Every package should have a special file called init.py in its folder. The presence of this file signals to Python that the folder is a package, allowing it to be imported later in your code.

Project Structure


A typical project would look like :

└── my_package/
    ├── __init__.py
    ├── happy.py
    └── sad.py

Lets create the same structure for our project in the codespace.

BASH

pixi init greet_me

OUTPUT

✔ Created ...greet_me/pixi.toml

This create a following structure for us.

image

Please note, if you want to use a pyproject.toml, you need to use the syntax mentioned below. This is also our preferance and we will use this file in the next lesson.

BASH

pixi init --format pyproject

pixi.toml : Lets view and edit the pixi.toml file generated for us.

TOML

[workspace]
authors = ["Priyanka O"]
channels = ["conda-forge"]
name = "greet_me"
version = "0.1.0"
platforms = ["linux-64"]
description = "A simple greeting package."

[dependencies]
python = ">=3.10"

[tasks]

Lets go inside the project directory:

BASH

cd greet_me

To add libraries via pixi command use this syntax :

BASH

pixi add requests

OUTPUT

✔ Added requests >=2.32.5,<3

Let us see that gets added to the pixi.toml

TOML

[dependencies]
requests = ">=2.32.5,<3"

You can also generate/ update pixi.lock file via this command :

BASH

pixi lock

OUTPUT

✔ Lock-file was already up-to-date

Alternatively, Lets install the dependencies now, which wiil generate the pixi.lock file. This is also useful when you clone someone else repo, the dependencies are installed via the pixi.lock file

BASH

pixi install

OUTPUT

✔ The default environment has been installed.

There is also a command to update your dependencies to newer versions where possible with latest compatible versions.

BASH

pixi update

OUTPUT

▪ solving              [━━━━━━━━━━━━━━━━━━━━]  0/1
▪ updating lock-files    [━━━━━━━━━━━━━━━━━━━━] 0/2

OUTPUT

✔ Lock-file was already up-to-date

Let us now create a folder named my_package and add 3 files inside it namely happy.py, sad.py and __init__.py

PYTHON

# happy.py <- A module
def greet_happy():
    return "Yay! happy day! 😀"

PYTHON

# sad.py <- A module
def greet_sad():
    return "Oh no… I’m feeling a bit down today. 😢"

Ultimately, our project structure should look like this :

greet_me/
├── LICENSE
├── pyproject.toml
├── README.md
├── pixi.toml
├── pixi.lock         # auto-generated, do not edit
├── tests/
│   └── test_greetings.py
└── my_package/
    ├── __init__.py
    ├── happy.py
    └── sad.py

Add the following task to the pixi.toml file and then run via pixi

TOML


[tasks]
start = "python -c 'from my_package  import happy; print(happy.greet_happy())'"

BASH

pixi run start

OUTPUT

Yay! happy day! 😀
Key Points
  • Follow the folder structure
  • dont forget to add the init.py file.
  • Sequence of pixi commands : init -> add -> run -> lock ->install -> update

Content from Metadata for Python packging


Last updated on 2025-08-25 | Edit this page

Overview

Questions

  • What is pyproject.toml?

Objectives

  • To understand the structure of pyproject.toml

Introduction


In the previos lesson, we touched upon pixi.toml briefly. In this lesson, we will learn about pyproject.toml. pyproject.toml is the most common format for Python projects. Please not, for a project maintained using pixi, either of the 2 i.e. pyproject.toml or pixi.toml are fine. You just need to take care of some specific syntaxes.

To add pyproject.toml file, we give the following command :

BASH

pixi init --format pyproject

OUTPUT

✔ Created .../greet_me/pyproject.toml

Whats inside pyproject.toml


  • Manifest metadata

TOML

[project]
name = "greet_me"
version = "0.1.0"
description = "greet_me Pixi-managed package"
authors = [{ name = "Priyanka", email = "" }]
readme = "README.md"
license = { text = "MIT" }
requires-python = ">=3.11"
dependencies = []

The [build-system] table in a pyproject.toml file tells packaging tools like pip what software is needed to build your Python project. It specifies the build backend that will be used to create distributable packages, like wheels (.whl) or source distributions (.sdist).

This section was introduced by PEP 518 and is essential for modern Python packaging. The [build-system] table has two main keys:

  1. requires: This is a list of strings specifying the packages needed to build your project. These packages will be downloaded and installed into a temporary, isolated environment before the build process begins. You must include the build backend itself here.
  2. build-backend: This is a string that points to the specific Python object (the “backend”) that packaging tools will use to execute the build. It’s the entry point for creating your project’s packages.

TOML

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
  • Editable project install

This means that the package is installed in editable mode, so you can make changes to the package and see the changes reflected in the environment, without having to re-install the environment. The greet_me package itself is added as an editable dependency.

TOML

[tool.pixi.pypi-dependencies]
greet_me = { path = ".", editable = true }
  • Dependency handling For dependency management, following lines are necessary in your pyproject.toml file. requires_python tag is used to specify the version of Python.

    [tool.pixi.workspace]: This section controls where packages come from and what platforms Pixi should resolve for. It is also used to define project-wide settings.

    [tool.pixi.pypi-dependencies] : This section is used to delare the dependencies of our project that will be installed via Pip or similar tools from Python Package Index. In short they are libraries necessary for our project and will be installed via pip.

TOML

[project]
name = "greet_me"
requires-python = ">=3.11"

[tool.pixi.workspace]
channels = ["conda-forge"]
platforms = ["linux-64", "win-64"]

[tool.pixi.pypi-dependencies]
requests = ">=2.31.0,<3"

You can specify a range or multiple supported Python versions using the syntax below.

TOML

requires-python = ">=3.11, <3.12"

If you were using pixi.toml file, the equivalent syntax would be

TOML

[tool.pixi.workspace]
name = "greet_me"
channels = ["conda-forge"]
platforms = ["linux-64", "win-64"]

[tool.pixi.pypi-dependencies]
python = ">=3.11"

[tool.pixi.tasks]
  • Tasks

Here you can specify various steps that you would want to run before making your package. It ususally lets you define and run custom cammands or scripts for your project.

TOML

[tool.pixi.tasks]
# This command will only be defined on Windows
greet  = { cmd = "echo 'Happy Python Packaging!'" }

Final pyproject.toml should look like this below, for reference.

TOML

[project]
authors = [{name = "Priyanka O"}]
dependencies = []
name = "po_greet_me"
requires-python = ">= 3.11"
version = "0.1.2"
description = "greet_me Pixi-managed package"

[build-system]
build-backend = "hatchling.build"
requires = ["hatchling"]

[tool.hatch.build.targets.wheel]
packages = ["my_package"]

[tool.pixi.workspace]
channels = ["conda-forge"]
platforms = ["linux-64"]

[tool.pixi.pypi-dependencies]
greet_me = { path = ".", editable = true }
requests = ">=2.32.5,<3"

[tool.pixi.tasks]
greet  = { cmd = "echo 'Happy Python Packaging!'" }
Key Points
  • Need to have a pyproject.toml file
  • Need to have [build-system] section with requires and build-backend specfied.
  • Need to have [project] section with atleast name and version specified.
  • Nice to have dependencies specified in [project] section.

Content from How to publish your Python project


Last updated on 2025-08-25 | Edit this page

Overview

Questions

  • What is Twine and why is it needed
  • What is build command and what does it do ?
  • How to create and upload the Python package ?
  • How to test/ use the uploaded Python package?

Objectives

  • Learn the usage for tools like build and twine
  • How to upload the Python Package to repositories like TestPyPi
  • How to use the uploaded package.

Introduction


Once we have created our project, defined all the necessary metadata in the toml file, its time to publish our project. Let see the tools and steps we need to acheive this.

We need to install the following tools i.e. build and twine

Before proceeding with the steps mentioned below, kindly rename or delete the pixi.toml file as we will mainly focus on pyproject.toml for the steps mentioned below. You can also try the build with pixi.toml at a later point in time by deleting or renaming the pyproject.toml file.

Create your build


build :A tool to read the pyproject.toml file and build the package files

BASH

pip install build

OUTPUT

Collecting build
   Downloading build-1.3.0-py3-none-any.whl.metadata (5.6 kB)
 Requirement already satisfied: packaging>=19.1 in /home/codespace/.local/lib/python3.12/site-packages (from build) (25.0)
 Collecting pyproject_hooks (from build)
   Downloading pyproject_hooks-1.2.0-py3-none-any.whl.metadata (1.3 kB)
 Downloading build-1.3.0-py3-none-any.whl (23 kB)
 Downloading pyproject_hooks-1.2.0-py3-none-any.whl (10 kB)
 Installing collected packages: pyproject_hooks, build
 Successfully installed build-1.3.0 pyproject_hooks-1.2.0

BASH

python -m build

OUTPUT

 * Creating isolated environment: venv+pip...
 * Installing packages in isolated environment:
   - hatchling
 * Getting build dependencies for sdist...
 * Building sdist...
 * Building wheel from sdist
 * Creating isolated environment: venv+pip...
 * Installing packages in isolated environment:
   - hatchling
 * Getting build dependencies for wheel...
 * Building wheel...
 Successfully built greet_me-0.1.0.tar.gz and greet_me-0.1.0-py3-none-any.whl

This command creates a dist directory containing two files: - A wheel file (greet_me-0.1.0-py3-none-any.whl). - A source archive (greet_me-0.1.0.tar.gz).

image

## Create an account on TestPyPI

Visit this URL and crete an account to generate the API keys, to be able to upload your package to TestPyPI in the next step.

Once you create your account, you can visit this link to generate the API key. Please copy the same in a notepad and paste it when prompted for it in the next step below.

image

Upload your build


twine :A tool for securely uploading packages to PyPI and TestPyPI. ```bash pip install build twine

twine upload –repository testpypi dist/* ``` You’ll be prompted to enter your TestPyPI username and password. It’s recommended to use an API token instead of your password. When prompted for your password, paste the token in.

OUTPUT

Uploading distributions to https://test.pypi.org/legacy/
INFO     dist/po_greet_me-0.1.1-py3-none-any.whl (0.8 KB)
INFO     dist/po_greet_me-0.1.1.tar.gz (5.0 KB)
INFO     username set by command options
INFO     Querying keyring for password
INFO     No keyring backend found
Enter your API token:
INFO     username: __token__
INFO     password: <hidden>
Uploading po_greet_me-0.1.1-py3-none-any.whl
100% ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2.5/2.5 kB • 00:00 • ?
INFO     Response from https://test.pypi.org/legacy/:
       200 OK
INFO     <html>
        <head>
         <title>200 OK</title>
        </head>
        <body>
         <h1>200 OK</h1>
         <br/><br/>



        </body>
       </html>
Uploading po_greet_me-0.1.1.tar.gz
100% ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 6.7/6.7 kB • 00:00 • ?
INFO     Response from https://test.pypi.org/legacy/:
       200 OK
INFO     <html>
        <head>
         <title>200 OK</title>
        </head>
        <body>
         <h1>200 OK</h1>
         <br/><br/>



        </body>
       </html>

View at:
https://test.pypi.org/project/po-greet-me/0.1.1/

That’s it! After the upload is successful, your package will be available on TestPyPI. E.g. : https://test.pypi.org/project/po-greet-me/0.1.1/

image

It is possible that the name of your project already exists or is simialr to an existing project. In that case you may end up in an error like this :

OUTPUT

twine upload --repository testpypi dist/*
Uploading distributions to https://test.pypi.org/legacy/
Enter your API token:
Uploading greet_me-0.1.1-py3-none-any.whl
100% ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2.6/2.6 kB • 00:00 • ?
WARNING  Error during upload. Retry with the --verbose option for more details.
ERROR    HTTPError: 403 Forbidden from https://test.pypi.org/legacy/
    Forbidden

This isnt always helpful and you should try this command as tipped in the error message above to know more.

BASH

twine upload --repository testpypi dist/* --verbose

OUTPUT

Uploading distributions to https://test.pypi.org/legacy/
INFO     dist/greet_me-0.1.1-py3-none-any.whl (0.9 KB)
INFO     dist/greet_me-0.1.1.tar.gz (4.9 KB)
INFO     username set by command options
INFO     Querying keyring for password
INFO     No keyring backend found
Enter your API token:
INFO     username: __token__
INFO     password: <hidden>
Uploading greet_me-0.1.1-py3-none-any.whl
100% ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2.6/2.6 kB • 00:00 • ?
INFO     Response from https://test.pypi.org/legacy/:
         400 Bad Request
INFO     <html>
          <head>
           <title>400 The name 'greet-me' is too similar to an existing project. See https://test.pypi.org/help/#project-name for more information.</title>
          </head>
          <body>
           <h1>400 The name 'greet-me' is too similar to an existing project. See https://test.pypi.org/help/#project-name for more information.</h1>
           The server could not comply with the request since it is either malformed or otherwise incorrect.<br/><br/>
         The name &#x27;greet-me&#x27; is too similar to an existing project. See https://test.pypi.org/help/#project-name for more information.


          </body>
         </html>
ERROR    HTTPError: 400 Bad Request from https://test.pypi.org/legacy/
         Bad Request                   

To fix this, rename you project e.g. to po_greet_me , and add the following in pyproject.toml file. and try the steps below to upload it again.

TOML

[tool.hatch.build.targets.wheel]
packages = ["my_package"]

BASH

# 1. (Recommended) Remove the old build directory
rm -rf dist

# 2. Re-build your package with the new name
python -m build

# 3. Upload the new version to TestPyPI
twine upload --repository testpypi dist/*

Test your package


Install your package via this command :

BASH

pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple po-greet-me==0.1.1

OUTPUT

Looking in indexes: https://test.pypi.org/simple/, https://pypi.org/simple
Collecting po-greet-me==0.1.1
  Downloading https://test-files.pythonhosted.org/packages/52/85/dd6ebf4ee0a6ff76699a4c4f134059e6615b75c50e30cd40cd424370b81e/po_greet_me-0.1.1-py3-none-any.whl.metadata (137 bytes)
Downloading https://test-files.pythonhosted.org/packages/52/85/dd6ebf4ee0a6ff76699a4c4f134059e6615b75c50e30cd40cd424370b81e/po_greet_me-0.1.1-py3-none-any.whl (815 bytes)
Installing collected packages: po-greet-me
Successfully installed po-greet-me-0.1.1

Create a Python file named test_package.py

PYTHON

from my_package import happy, sad

print(happy.greet_happy())

BASH

python test_package.py 

OUTPUT

Yay! happy day! 😀
Key Points
  • Fill all the metadata and give your project a unique name
  • Build your Project
  • Create a TestPyPI account and generate the API token
  • Upload your package via twine to TestPyPI
  • Check your package via pip install