Content from Why Python Packages?


Last updated on 2025-10-22 | Edit this page

Overview

Questions

  • Why must we package our Python code?
  • What precisely may be packaged?
  • Why is __init_.py file important?
  • What is the Python Package Index (PyPI), and what purpose does it serve?
  • What is a build?

Objectives

  • To understand the benefits of Python packaging.
  • To determine whether packaging is suitable for your use case.
  • To understand the importance of __init_.py file.
  • To acquire knowledge of components of Python packaging and understand Python Package Index (PyPI).
  • To become familiar with the build process.

image Ref : https://xkcd.com/1987/

Introduction


Software development is a creative pursuit, and it is satisfying to write code that solves interesting problems. However, once our code is ready, how can we share it globally? This course provides instruction in how to distribute Python code using Pixi. You might liken a package to the old-style setup.exe: a standardised method of distributing software.

By packaging code, you address several important challenges, including ensuring reproducibility, achieving cross-platform compatibility, and supporting multiple environments. Distributing code as a package offers many advantages over merely sharing source files, for example via GitHub:

Dependency management: A package can explicitly declare its dependencies. Tools such as pip (or uv) can then automatically install them for the user.

Versioning: You may version your package and distribute multiple versions, thereby supporting backwards compatibility.

Standardisation: Packaging is the established method of distributing code via recognised repositories such as PyPI.

Metadata: Packages include project-specific metadata, which is essential for end users.

Python Package Structure Hierarchy


  • Function : A block of reusable code that performs a single specific task.
  • Module : A single Python file (.py) that groups related classes, functions and variables.
  • Package : A folder/ collection containing multiple modules and an init.py file to organize them.
  • The init.py files are required to make Python treat folders containing the file as packages
  • Library : A collection of related packages or modules that provide broad functionality for reuse.
  • Library can have Package(s), which can have module(s) which can have function(s). It can also be considered a Project.
  • Python package : A Python package is a collection of related code modules (files) bundled with metadata describing how the package should be installed and used *
image

GIT PyPI

Steps to create a Python Package :


image

What may be packaged ?


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

What Is init.Py File in Python?


The init.py file is a Python file that is executed when a package is imported. It serves two main purposes:

  • It marks the directory as a Python Package so that the interpreter can find the modules inside it.

  • It can contain initialization code for the Package, such as importing submodules, defining variables, or executing other code.

    source

What is PyPI


PyPI is the repository where all released Python packages are made available for end users. You may explore it here: PyPI. There is also TestPyPI, a repository which allows us to experiment with distribution tools and procedures without affecting the live PyPI. In this course, we will publish our package to TestPyPI.

What is a Build ?


A build is the process by which your project’s source code is transformed into a distributable format. These build artefacts can then be installed using tools such as pip. A build may be created using the command in the terminal : bash python -m build The successful build process produces files such as a wheel (.whl) or a source archive (.tar.gz), which can be installed via pip or uploaded to PyPI. It is vital to version your build and supply the requisite metadata.

Key Points
  • To make Python code installable, reusable and distributable via PyPI or TestPyPI, one must package the code.
  • The Package should have modules and init.py file.
  • The code must be versioned.
  • Project dependencies must be managed.
  • The project’s metadata must be clearly defined.

Content from Why choose Pixi for Python Packaging ?


Last updated on 2025-10-22 | Edit this page

Overview

Questions

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

Objectives

  • To understand the advantages offered by Pixi.
  • To learn about pixi.toml and pyproject.toml
  • To understand the role of pixi.lock.
  • To explore the concept of multi-environment support.

Introduction


Pixi is a fast, modern and reproducible package management tool. It has lots of features which are not all present in a single tool at this point in time. These capabilities make Pixi an attractive choice for managing Python and multi-language projects.

Key features include:

  • Support for both PyPI and Conda packages : enabling flexibility in sourcing dependencies.
  • Performance :lightweight and modern, designed for speed.
  • Multi-language dependency management : e.g. Python with Rust, or Python with C/C++.
  • Integration with uv : leveraging a high-performance package installer.
  • Reproducibility: guaranteed through the use of pixi.lock.
  • Configuration via TOML files: supports both pixi.toml and pyproject.toml.

Configuration files (pixi.toml and pyproject.toml)


pixi.toml: The configuration file used by Pixi to define environments, dependencies, and tasks. pyproject.toml: A standard configuration file within the Python ecosystem (PEP 518/621). It is required by build tools such as Poetry, Hatch, Flit, and setuptools. This file specifies project metadata (e.g. name, version, author) as well as dependencies and build settings.

Multi-environment support


Pixi allows you to define dependencies for specific operating systems (e.g. Windows, macOS, Linux) or for distinct environments such as development, testing, and production. This makes it easier to tailor the project configuration to match the context in which the software is being deployed or developed.

pyproject.toml


For this demo, we will mainly focus on pyproject.toml file. We make this choice due to following specifications and recomendations : PEP 621, PEP 517, and PEP 660

You can also read at these links : - https://pixi.sh/v0.40.1/reference/pixi_manifest/#pypi-dependencies - https://pixi.sh/v0.40.1/advanced/pyproject_toml/

Key Points
  • Choose a tool with good support and long term vision.
  • Choose a tool suitable for your project.
  • Focus on PEP specifications and recomendations.

Content from Set up a project directory


Last updated on 2025-10-22 | 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 a project effectively in order to package it successfully.
  • To recognise the importance of the __init__.py file and know where to place it.
  • To assign unique yet meaningful names to packages.

Introduction


It is essential to structure your project correctly, include the necessary files, and provide packages with names that are both unique and understandable.

Packages are used to organise modules within a Python project. As projects often consist of several modules, grouping them into packages helps to keep the codebase structured, maintainable, and easy to navigate.

Each package must contain a special file named __init__.py. The presence of this file indicates to Python that the folder is a package, thereby allowing it to be imported into your code.

Project Structure


A typical project would look like :

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

Let us create a similar structure within our codespace.

BASH

pixi init --format pyproject

OUTPUT

✔ Created /workspaces/pixi_demo/pyproject.toml

This generates the following structure:

image

pyproject.toml : The initial pyproject.toml file generated may look like this:

TOML

[project]
authors = [{name = "Priyanka Demo", email = "demo@users.noreply.github.com"}]
dependencies = []
name = "greet_me"
requires-python = ">= 3.11"
version = "0.1.0"

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

[tool.pixi.workspace]
channels = ["conda-forge"] #Download and install Conda packages from the conda-forge channel
platforms = ["linux-64"]

[tool.pixi.pypi-dependencies] #Add dependencies here which needs to be installed from PyPI
greet_me = { path = ".", editable = true }

[tool.pixi.tasks]

To add libraries via Pixi:

BASH

pixi add requests

OUTPUT

✔ Added requests >=2.32.5,<3

This will create/update the [tool.pixi.dependencies] section in pyproject.toml.

TOML

[tool.pixi.dependencies]
requests = ">=2.32.5,<3"

It also generates a pixi.lock file which may look somewhat like the image below.

image

To remove a package, use this command and check that pyproject.toml is corrected and the package is removed from there.

BASH

pixi remove requests

OUTPUT

✔ Removed requests

To add libraries from PyPI via Pixi:

BASH

pixi add --pypi requests

OUTPUT

✔ Added requests >=2.32.5, <3
Added these as pypi-dependencies.

Check the pyproject.toml file. These get added under the [project] section

TOML

[project]
dependencies = ["requests>=2.32.5,<3"]
name = "greet_me"

Please note : this wont create a problem when uploading the build but can create problems, when installing the builds. So best to keep it empty for now and move this to [tool.pixi.pypi-dependencies] section , for all projects which need to come from via PyPI namely build and twine which are used to create and upload build respectively.

TOML

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

Other commands, that can be later explored :

To generate or update the pixi.lock file:

BASH

pixi lock

OUTPUT

✔ Lock-file was already up-to-date

Alternatively, when cloning a repository, you can install dependencies from the pixi.lock file:

BASH

pixi install

OUTPUT

✔ The default environment has been installed.

To upgrade dependencies to the latest compatible versions:

BASH

pixi update

OUTPUT

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

OUTPUT

✔ Lock-file was already up-to-date

Adding Modules


Lets create these 2 files: happy.py, sad.py in the folder src/greet_me.

Please note, in VSCode inside GitHub codepsaces, you mabe be prompted for below, when creating a python file. So just Install it. image

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. 😢"

The overall project structure should then resemble:

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

Running a Task


Task is a command alias that you can execute easily via the CLI (e.g., pixi run ).

Run the following command to add a task and observe the changes in pyproject.toml file:

BASH

pixi task add start "python -c 'from greet_me import happy; print(happy.greet_happy())'"

pyproject.toml file:

TOML


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

Then execute:

BASH

pixi run start

OUTPUT

Yay! happy day! 😀

You can read more about tasks here, which contains all the advanced use cases needed in a professional setting.

Key Points
  • Follow the appropriate folder structure.
  • Always include the __init__.py file in packages.
  • Sequence of Pixi commands: initaddrunlockinstallupdate.
  • Define / check [project], [dependencies] and [tasks] in your pyproject.toml file .

Content from Metadata for Python packging


Last updated on 2025-10-22 | Edit this page

Overview

Questions

  • What is pyproject.toml?
  • What is a lockfile, and why is it necessary?

Objectives

  • To understand the structure and purpose of pyproject.toml
  • To appreciate the role and necessity of a lockfile.

Introduction


In the previous lesson, we briefly looked into pyproject.toml. In this lesson, we shall focus a bit more in detail on pyproject.toml, which is the most widely used configuration format for Python projects. Please note, for projects managed with Pixi, either pyproject.toml or pixi.toml may be employed. The primary distinction lies in syntax, so you need only ensure that you follow the appropriate conventions.

The [build-system] table


This section of a pyproject.toml file informs packaging tools such as pip which software is required to build your project. It specifies the build backend responsible for producing distributable packages such as wheels (.whl) or source distributions (.sdist).

This section was introduced by [PEP 518](https://peps.python.org/pep-0518/) and is essential for modern Python packaging. It has two main keys:

  1. requires: A list of packages required to build the project. These are downloaded and installed into a temporary, isolated environment prior to the build process. The build backend itself must also be listed here.
  2. build-backend: A string reference to the Python object (the “backend”) that will be invoked by packaging tools to create the distributable packages.

TOML

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

Some other build tools to read are - pdm.backend e.g. fastapi - mesonpy e.g. numpy - setuptools.build_meta e.g. parselmouth

Editable Installation


Projects may be installed in editable mode, which allows you to make changes to the source code and have them reflected immediately in the environment without reinstallation. For example, the greet_me package can be added as an editable dependency.

TOML

[tool.pixi.pypi-dependencies]
greet_me = { path = ".", editable = true }

Dependency Management


For dependency handling, the pyproject.toml file should include the requires-python field, which specifies the supported Python versions. For example:

TOML

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

Additional sections in pyproject.toml may include: [tool.pixi.workspace]: Defines project-wide settings, including package sources and target platforms for resolution.
[tool.pixi.pypi-dependencies] : Declares the dependencies to be installed from PyPI (or equivalent sources). These are the external libraries required by the project.

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

TOML

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

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

TOML

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

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

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

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

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

For insipiration, also check here

Lockfiles


A lockfile contains the complete set of dependencies, including specific versions, required to reproduce the project environment. It is automatically generated based on the dependencies listed in the .toml file, ensuring that builds remain consistent and reproducible.

Please add and update the README.md file, in case you havent done so. You can easily generate a README text on readme.so and copy its content to your READMe file. ::::::::::::::::::::::::::::::::::::: keypoints - Every project must include a pyproject.toml file - The [build-system] section is required and must define both requires and build-backend. - The [project] section must, at minimum, include the project name and version. - It is recommended to specify dependencies in the [project] section for clarity and reproducibility.

::::::::::::::::::::::::::::::::::::::::::::::::

Content from How to publish your Python project


Last updated on 2025-10-22 | Edit this page

Overview

Questions

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

Objectives

  • Learn how to use tools such as build and twine.
  • Understand how to upload a Python package to repositories such as TestPyPI.
  • Learn how to install and test an uploaded package.

Introduction


Once a project has been created and all the necessary metadata has been defined in the TOML file, the next step is to publish it. This lesson introduces the tools and steps required to achieve this.

The two key tools we need are:

Build – for generating distribution files from your project.

Twine – for securely uploading those distributions to PyPI or TestPyPI.

Note: Before proceeding, rename or remove the pixi.toml file, as we will focus on pyproject.toml. You may experiment with pixi.toml later by removing or renaming pyproject.toml.

Step 1: Create your build


The build tool reads your pyproject.toml file and generates the package distribution files.

BASH

pixi add --pypi build

OUTPUT

✔ Added build >=1.3.0, <2
Added these as pypi-dependencies.

Empty the depenencies under [project] and move/edit them to [tool.pixi.pypi-dependencies] section, like shown below :

TOML

[project]
dependencies = []

[tool.pixi.pypi-dependencies]
requests = ">=2.32.5,<3"
build = ">=1.3.0,<2"

BASH

pixi run 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

## Step 2 : Create an account on TestPyPI

  • Visit TestPyPI and create an account.
  • Generate an API token from your account settings.
  • Save the token securely, as you will use it during the upload process.
image

Step 3: Upload your build


The Twine tool is used to securely upload your package distributions. Install twine and modify the pyroject.toml file simmilar to what you did for build tool above.

BASH

pixi add --pypi twine

TOML

     [project]
     dependencies = []
     
     [tool.pixi.pypi-dependencies]
     requests = ">=2.32.5,<3"
     build = ">=1.3.0,<2"

Now upload the package to TestPyI

BASH

   pixi run twine upload --repository testpypi dist/*

You will be prompted for your TestPyPI API Token, so keep it handy and paste it, when prompted. Use the API token instead of your password by entering __token__ as the username and pasting the token when prompted for a password.

OUTPUT

Uploading distributions to https://test.pypi.org/legacy/
WARNING  This environment is not supported for trusted publishing
Enter your API token:
Uploading greet_me1-0.1.7-py3-none-any.whl
100% ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.3/4.3 kB • 00:00 • ?
Uploading greet_me1-0.1.7.tar.gz
100% ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 12.2/12.2 kB • 00:00 • ?

View at:
https://test.pypi.org/project/greet-me1/0.1.7/

After a successful upload, your package will be available at a URL such as: E.g. : [https://test.pypi.org/project/po-greet-me/0.1.1/](https://test.pypi.org/project/greet-me1/0.1.7/)

image

Handling Errors


If the package name is too similar to an existing project, TestPyPI may return a 403 Forbidden or 400 Bad Request error. 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

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

OUTPUT

Uploading distributions to https://test.pypi.org/legacy/
INFO     dist/greet_me-0.1.7-py3-none-any.whl (2.3 KB)
INFO     dist/greet_me-0.1.7.tar.gz (10.7 KB)
INFO     username set by command options
INFO     Querying keyring for password
INFO     No keyring backend found
INFO     Trying to use trusted publishing (no token was explicitly provided)
WARNING  This environment is not supported for trusted publishing
Enter your API token:
INFO     username: __token__
INFO     password: <hidden>
Uploading greet_me-0.1.7-py3-none-any.whl
100% ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.2/4.2 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 your Package Folder, [project].name (e.g. from greet_me to greet_me1) and also change this section, [tool.pixi.pypi-dependencies]

TOML

[project]
name = "greet-me1" # Changed

[tool.pixi.pypi-dependencies]
requests = ">=2.32.5,<3"
build = ">=1.3.0,<2"
twine = ">=6.2.0,<7"
greet_me1 = { path = ".", editable = true } # Changed

[tool.pixi.tasks]
start = "python -c 'from greet_me1 import happy; print(happy.greet_happy())'" # Changed
  • Rebuild and upload the package.

BASH

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

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

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

Step 4: Test your package


Create a new Repository with a readme file and install your package from TestPyPI via this command :

BASH

pip install -i https://test.pypi.org/simple/ greet-me1==0.1.7

OUTPUT

Looking in indexes: https://test.pypi.org/simple/
Collecting greet-me1==0.1.7
  Downloading https://test-files.pythonhosted.org/packages/92/84/76afd107870f18a144fe5df0cc9fd0d7698c69e82e4c085f2ba339f99218/greet_me1-0.1.7-py3-none-any.whl.metadata (304 bytes)
Downloading https://test-files.pythonhosted.org/packages/92/84/76afd107870f18a144fe5df0cc9fd0d7698c69e82e4c085f2ba339f99218/greet_me1-0.1.7-py3-none-any.whl (2.4 kB)
Installing collected packages: greet-me1
Successfully installed greet-me1-0.1.7

Then create a test script : test_package.py:

PYTHON

from greet_me1 import happy, sad

print(happy.greet_happy())

Run it:

BASH

python test_package.py 

OUTPUT

Yay! happy day! 😀
Key Points
  • Ensure all metadata is filled in and choose a unique project name.
  • Use build to generate distribution files
  • Create a TestPyPI account and generate an API token
  • Use twine upload to securely publish your package.
  • Test your package by installing it from TestPyPI via pip install.

Content from Extra


Last updated on 2025-10-22 | Edit this page

Overview

Questions

  • How can we use pixi run start?

Objectives

  • Learn how to use Pixi to run your project.

Introduction


After cloning a project, Install Pixi and Pixi makes it simple to run predefined tasks. If your pixi.toml (or pyproject.toml) contains a task named start, you can execute it directly using:

BASH

gh repo clone priya-gitTest/greet_me && cd greet_me
curl -fsSL https://pixi.sh/install.sh | sh

If required, restart your shell:

BASH

source ~/.bashrc

Verify that Pixi has been installed correctly.

BASH

pixi --version

Now run

BASH

pixi run start

OUTPUT

✨ Pixi task (start): python -c 'from greet_me1 import happy; print(happy.greet_happy())'
Yay! happy day! 😀

This command will: - Ensure that the required environment is installed (creating or updating it if necessary). - Run the start task exactly as defined in your configuration file.

This provides a convenient and reproducible way to launch your project without needing to manually manage dependencies or commands. You can check the example project here

Key Points
  • Define tasks such as start in your pixi.toml or pyproject.toml.
  • Use pixi run <task-name> to execute those tasks.
  • pixi run start ensures consistency and reproducibility when launching a project.