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.
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 *
Steps to create a Python Package :
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.
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.
- 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
andpyproject.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
andpyproject.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/
- 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.
OUTPUT
✔ Created /workspaces/pixi_demo/pyproject.toml
This generates the following structure:
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:
OUTPUT
✔ Added requests >=2.32.5,<3
This will create/update the [tool.pixi.dependencies]
section in pyproject.toml
.
It also generates a pixi.lock
file which may look
somewhat like the image below.
To remove a package, use this command and check that
pyproject.toml
is corrected and the package is removed from
there.
OUTPUT
✔ Removed requests
To add libraries from PyPI via Pixi:
OUTPUT
✔ Added requests >=2.32.5, <3
Added these as pypi-dependencies.
Check the pyproject.toml
file. These get added under the
[project]
section
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:
OUTPUT
✔ Lock-file was already up-to-date
Alternatively, when cloning a repository, you can install
dependencies from the pixi.lock
file:
OUTPUT
✔ The default environment has been installed.
To upgrade dependencies to the latest compatible versions:
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.
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
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:
pyproject.toml
file:
Then execute:
OUTPUT
Yay! happy day! 😀
You can read more about tasks here, which contains all the advanced use cases needed in a professional setting.
- Follow the appropriate folder structure.
- Always include the
__init__.py
file in packages. - Sequence of Pixi commands: init → add → run → lock → install → update.
- Define / check
[project]
,[dependencies]
and[tasks]
in yourpyproject.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:
-
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. -
build-backend
: A string reference to the Python object (the “backend”) that will be invoked by packaging tools to create the distributable packages.
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.
Dependency Management
For dependency handling, the pyproject.toml file should include the requires-python field, which specifies the supported Python versions. For example:
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.
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
andtwine
. - 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.tom
l 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.
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"
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
).
## 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.
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.
TOML
[project]
dependencies = []
[tool.pixi.pypi-dependencies]
requests = ">=2.32.5,<3"
build = ">=1.3.0,<2"
Now upload the package to TestPyI
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/)
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.
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 'greet-me' 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.
Step 4: Test your package
Create a new Repository with a readme file and install your package from TestPyPI via this command :
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
:
Run it:
OUTPUT
Yay! happy day! 😀
- 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:
If required, restart your shell:
Verify that Pixi has been installed correctly.
Now run
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
- Define tasks such as
start
in yourpixi.toml
orpyproject.toml
. - Use
pixi run <task-name>
to execute those tasks. -
pixi run start
ensures consistency and reproducibility when launching a project.