We can install packages from the Python Package Index (PyPI) through pip(PyPI Install Packages). Furthermore, we can import these through import
.
But …
how can we create our custom package? Let’s walk through how to structure a custom Python package, add subpackages, the roles of
__init__.py
and how to publish the custom package to PyPI. Additionally, we look at setting up dependencies and version management using Poetry.
1. Basic Package Structure
Let’s say we want to build a simple package named mypackage
. Here is a minimal project structure:
mypackage/
├── package/
│ ├── __init__.py
│ ├── maths.py
│ └── subpackage/
│ ├── __init__.py
│ └── mult.py
├── mytest/
│ ├── __init__.py
│ └── test.py
└── pyproject.toml
This structure includes:
package/
: the actual Python packagesubpackage/
: a nested subpackagemytest/
: a place to test and experimentpyproject.toml
: metadata and configuration for Poetry
Understanding __init__.py
__init__.py
tells Python that a directory is a package.
Here’s what __init__.py
is used for.
1. Mark a Directory as a Package
If __init__.py
exists, Python knows the directory is importable. For example, in mytest/test.py
, you can import package
.
# in mytest/test.py
from package import maths
2. Control What’s Exposed
You can expose a public API at the package level:
# in package/__init__.py
from .maths import add
from .subpackage.mult import multipy
__all__ = ['add', 'multiply']
Then, you can import add
and multiply
as follows:
# in mytest/test.py
from package import add, multiply
3. Alias or rename the submodule
You can also give alias to the submodules:
# in package/__init__.py
from . import maths as m
you can import maths
as m
, i.e.
import package
print(package.m.add(2,3)) # output: 5
4. Define Package Version
You can define the package version:
# in package/__init__.py
__version__ = "0.1.0"
You can check the version by running the code below:
import package
print(package.__version__) # output: 0.1.0
5. Run Package Initialization Logic
# in package/__init__.py
print("Initializing package...")
Please note that __init__.py
runs every time the package is imported. So we should avoid heavy logic here.
When you load the module package
and execute the code in mytest/test.py
, the code should be run from the project root.
# Command line
python -m mytest.test
If we run python3 mytest/test.py
, Python adds the sibling directory mytest/
to sys.path
so that package
directory cannot be discovered.
2. Create a PyPI Package with Poetry
We can turn this into an instalable package using Poetry.
1. Initialize with Poetry
poetry init
This will guide you through filling in the pyproject.toml in the terminal. The result will look something like this:
[project]
name = "mypackage"
version = "0.1.0"
description = "A custom math package"
authors = [
{name = "Your Name",email = "you@example.com"}
]
readme = "README.md"
requires-python = ">=3.11"
dependencies = [
]
[build-system]
requires = ["poetry-core>=2.0.0,<3.0.0"]
build-backend = "poetry.core.masonry.api"
2. Build and Publish
poetry build
poetry publish --username YOUR_USERNAME
You will need an account at PyPI first.