Session 13: Modules and Packages
Date: 06-06-2026 4:00 pm to 6:00 pm
Agenda
- Review of previous sessions
- Answers to queries
- Distributing Python Code
- Modules
- Packages
- Building Packages
Distributing Python Code
Python is an interpreted programming language. Python source code can be distributed and consumed in two different ways:
- Module is an individual Python file containing functions, classes, constants, and if necessary, executable statements. Name of the Python file is he name of the module. A module can be imported into another Python program with the
importkeyword. Typically a module is stored in the same directory as the program it is imported into although Python can import modules from other standard directories. - Package is a directory containing a special file named
__init__.pyand other Python files. Name of the directory containing the__init__.pyfile is the name of the package. A package can contain any number of modules and sub-directories within it. A sub-directory must contain a__init__.pyfile for it to be teated as a sub-package. If a sub-directory does not contain the__init__.pyfile, but contains Python modules in it, they are accessible to code within the package but can be called from outside the package.
Module
A Python module is a:
- A valid Python file containing Python code, consisting of functions, classes, variables, and optionally, executable statements. (Note: This code is executed only once, the first time the module is imported).
- Its contents can be imported into another Python program with the import module or from module import xyz statements.
- Name of the file, without the
.py, is the name of the module. - When an import module statement is executed, Python runs the module code to create a module object, and binds that module's name to the current scope.
- When from module import xyz is executed, the specific attribute xyz is imported directly into the current scope, allowing it to be used without the module. prefix.
- An application looks for modules in the same directory as the application and, if not found, looks for them in a set of predefined directories described later.
Package
A Python package is a:
- A directory with the file
__init__.py, which may be an empty file or optionally contain Python code. - A sub-directory containing a
__init__.pyfile within a directory that is a Python package (that itself contains a__init__.pyfile) is a sub-package of the said package. However, with Python 3.3 onwards, Python supports implicit Namespace Packages that allow one to create a package across multiple directories without an__init__.pyfile. - A package or a subpackage may contain modules.
- Contents of a package can be imported with the
import packagestatement. Similarly, a subpackage can be imported, usually with an alternate name asimport package.subpackage as alternatename. For exampleimport matplotlib.pyplot as plt. - Name of the directory is the name of the package.
- Executing
import packagein an application creates the namespacepackage. - An application looks for packages in the same directory as the application and if not found, looks for them in a set of predefined directories described later.
Where does an application look for modules and packages?
Python looks for a module (file with .py extension) or a package (a directory) in the same directory as the Python application being executed.
If a matching file or directory is not found, the application looks for them in the predefined locations. These predefined locations are stored in sys.path and can be viewed with the following lines of code in the Python REPL or a Python script:
>>> import sys
>>> for p in sys.path:
... print(p)
C:\Users\username\AppData\Local\Python\pythoncore-3.14-64\python314.zip
C:\Users\username\AppData\Local\Python\pythoncore-3.14-64\DLLs
C:\Users\username\AppData\Local\Python\pythoncore-3.14-64\Lib
C:\Users\username\AppData\Local\Python\pythoncore-3.14-64
C:\Projects\Python\ProjectName\.venv
C:\Projects\Python\ProjectName\.venv\Lib\site-packages
usernameis the name of the user on a Windows machine, andC:\Usersis the typical location for user home directories. It is similar on GNU/Linux and macOS machines.C:\Projects\Python\ProjectNameis the location where the Python project is located and is a choice of the programmer.
Example Module
Let us write a module with two functions to calculate the the nearest multiplt of a specified number for a given input number, either on the higher side (ceiling) or the lower side (floor). Let us call the module file utils.py so that the name of the module is utils.
| utils.py | |
|---|---|
main.py, which imports the module.
| main.py | |
|---|---|
Example Package
Let us develop a package named footing which is expected to contain modules for the proportioning of different types of footings, and make the module rect_footing.py a part of this packgae. The easiest way to get started is to crate a new project folder, and use uv to initialize a project to create a library. Assuming you are on a Windows machine and your Python projects are in the C:\Users\username\Projects\ directory, follow these command at the Command Prompt:
C:\Users\username\Projects > md footing
C:\Users\username\Projects > cd footing
C:\Users\username\Projects\footing > uv init --lib
Initialized project `footing`
C:\Users\username\Projects\footing > tree /f
Folder PATH listing for volume Windows-SSD
Volume serial number is 000000B4 2CED:96CE
C:.
│ .gitignore
│ .python-version
│ pyproject.toml
│ README.md
│
└───src
└───footing
py.typed
__init__.py
Note
Like any project created with uv init (except with uv init --bare option) you have the following components created by uv:
.gitdirectory for source code version management usinggit..gitignorefile, with names of directories and files patterns that are to be ignored bygit..python-versionfile containing the Python version to be used by the project. This file is managed byuvand must not be changed by the programmer.README.mdfile, is the file expected by GitHub and other onlinegitweb repositories such as GitLab and several others. It is in Markdown format and its contents are displayed on the front page of the repository. It serves as an introduction to the package.srcdirectory containes the source code for the package we intend to develop.src\footingdirectory contains the source code for the package.src\footing\__init__.pyfile makes this directory a package.py.typedfile tells type checkers (like mypy, pyright, pylance) that your package ships with type hints that should be trusted and enforced. You can ignore it at this point of time.
Check the status of the local Git repository with the command:
> git status
git status
On branch main
No commits yet
Untracked files:
(use "git add <file>..." to include in what will be committed)
.gitignore
.python-version
README.md
pyproject.toml
src/
nothing added to commit but untracked files present (use "git add" to track)
Now do the following:
- Copy the
rect_footing.pyfrom our previous application with the namerectangular.pyinto thesrc\footingdirectory with the command. Assuming therect_footing.pyfile to be in a directory namedC:\Users\username\Projects\footing_designand the destination directory isC:\Users\username\Projects\footing\src\footing. (change the command appropriately as needed).Alternatively, use Windows File Explorer and make a copy of the file> copy C:\Users\username\Projects\footing_design\rect_footing.py C:\Users\username\footing\src\fppting\rectangular.pyrect_footing.pyin the original folder, rename it torectangular.pyand copy it to the desitnation directory. - Create a directory named
testsin the project root and copy thetest_rectfooting.pyto that directory.
These are the directory tree and Git status after these changes:
> tree /f
C:.
│ .gitignore
│ .python-version
│ pyproject.toml
│ README.md
│
├───src
│ └───footing
│ py.typed
│ rectangular.py
│ __init__.py
│
└───tests
test_rectfooting.py
> git status
On branch main
No commits yet
Untracked files:
(use "git add <file>..." to include in what will be committed)
.gitignore
.python-version
README.md
pyproject.toml
src/
tests/
nothing added to commit but untracked files present (use "git add" to track)
We can now run the tests using pytest:
uv run -- pytest tests
================================================= test session starts =================================================
platform win32 -- Python 3.14.0, pytest-9.0.3, pluggy-1.6.0
rootdir: C:\Users\satish\Documents\Python\footing
configfile: pyproject.toml
plugins: cov-7.1.0
collected 2 items
tests\test_rectfooting.py .. [100%]
================================================== 2 passed in 0.06s ==================================================
Following points are worth noting:
- The package we inted to develop is named
footingand its source code resides in the directorysrc\footingwithin the project directory. - The file
src\footing\__init__.pymust be present for the directory to be considered as a package. - At present, the package consists of a single module named
rectangularand its source code resides in the filesrc\footing\rectangular.py. - To import the package into any file within the project directory, it is sufficient to refer to the name of the package
footing. For example,RectFootingclass from the modulerectangularin the packagefootingis imported into the test filetests\test_rectfooting.pyusing the statementfrom footing.rectangular import RectFooting.
Git Repository for the Package
We took a preliminary llok at Git in Source Code Management. We will now create a repository for this package on GitHub. It will serve several purposes:
- Source coode management: All versions and branches along the entire history of its development
- Storage on a remote repository: This enables recreating the source code to a local repository on a different machine
- Distribution of the package to others: This becomes possible because
pipand by extensionuvcan install packages from GitHub (in addition to their main purpose of installing packages from PyPI).
Building a Module or a Package for Distribution
If you wish to distribute the module or the package to other users, you must build a package in a way that it can be distributed to other users and they should be able to install it and use it in their applications. A package can be distributed directly from the GitHub repository, assuming one has access to it, or from PyPI (Python Packaging Index).
Distributing from GitHub is simple and straightforward as uv or pip understand how to request, download and install from a Git repository. Distributing via PyPI requires additional steps such as building a distributable package, optionally testing the package on Test PyPI before uploading to PyPI, following security requirements, a PyPI account and API token etc.
Here are the requirements that must be met:
- A valid project structure
- A
pyproject.tomlwith PEP 621 metadata - A build backend to build a wheel (
.whl) or a source distribution (sdist) - Build artifacts, sdist + wheel
- A PyPI account and API token
- A unique module or package name
- Upload tool
uv manages steps 1 to 4. By default uv uses uv_build as its build backend, but can work with other backends such as flit or hatchling from Hatch. Hatch is a more capable and flexible tool compared to both flit and uv_build and is preferred for complex projects.
Study the contents of the following files:
pyproject.tomlis the project configuration file. Specifically study the project meta-data and dependency group. This file is normally managed byuvand must be changed manually only when necessary.README.mdfile is the opening page of the repository on GitHub. The defaultREADME.mdfile created byuvis empty. Add content to this file to introduce the package to the visitors to the GitHub repository. It must be in Markdown format..gitignorefilename and directory name patternswhich are not to be tracked for changes. You can add more patterns if you wish Git ignore them..python-versioncontains the version of Python to be used by the project. This file is managed byuvand must not be changed manually.uv.lockif present, contains the details of the versions of all installed packages to enable recreation of the project on a different machine. This file is managed byuvand must never be changed manually.src\footing\__init__.pyfile is the file required to consider thesrc\footingdirectory as a package. It can be empty, but if it contains valid Python code, the code will be executed once when imported into an application. In the default__init__.pyfile created byuv, there is a function namedhello()which can be safely deleted. An empty__init__.pyis allowed.