Cover image was generated by ChatGPT and edited by the author.
Recently, I’ve been learning how to package Python code for quick sharing and easy installation. After exploring various distribution methods, I realized that creating a Python module is probably one of the most convenient options, mainly because it saves me from having to reinstall everything repeatedly.
The following section documents my creation process in written form.
Writing the Module
When it comes to preparing for packaging a module, the first step is, of course, to have a working module ready.
Recently, I’ve been experimenting with porting the spatial interpolation tool autoFRK, originally developed by Wen-Ting Wang based on the paper Resolution Adaptive Fixed Rank Kriging by Sheng-Li Tzeng and Hsin-Cheng Huang (link), from R to Python.
The goal is to enable the model to be used within deep learning environments built on PyTorch.
A portion of the module’s source code is shown below:
# import modulesimporttorchimporttorch.nnasnnfromtypingimportOptional,UnionfromautoFRK.utils.loggerimportsetup_loggerfromautoFRK.utils.deviceimportsetup_devicefromautoFRK.utils.utilsimport*fromautoFRK.utils.predictorimport*# logger configLOGGER=setup_logger()# class AutoFRKclassAutoFRK(nn.Module):"""
Automatic Fixed Rank Kriging
This function performs resolution-adaptive fixed rank kriging on spatial
data observed at one or multiple time points using a spatial random-effects
model.
...
"""def__init__(self,mu:Union[float,torch.Tensor]=0.0,D:torch.Tensor=None,G:torch.Tensor=None,finescale:bool=False,maxit:int=50,tolerance:float=1e-6,maxK:int=None,Kseq:torch.Tensor=None,method:str="fast",n_neighbor:int=3,maxknot:int=5000,dtype:torch.dtype=torch.float64,device:Optional[Union[torch.device,str]]=None):"""
Initialize autoFRK model with tensor-safe and device-aware configuration.
"""super().__init__()...
Preparation
Before packaging the module, we need to organize the module files. The directory structure should look like this:
This is the project directory of the module.
Within the project_dir/src/module_name/ directory, all the module’s source code is placed. Each subdirectory must include an __init__.py file to declare the directory as a Python module, allowing Python to correctly import its contents.
At the root of the project, there are usually several auxiliary files and folders, such as tests/ for unit or integration tests; pyproject.toml for defining the project’s basic information, dependencies, and build configuration; README.md as the project’s documentation; LICENSE for the module’s licensing terms; and setup.cfg or setup.py for compatibility with older packaging methods (optional). Additionally, MANIFEST.in specifies extra non-code files to include during packaging, such as documents, data files, or static resources. These files form the structure required for module distribution, keeping the project organized and consistent during development, testing, and packaging.
All operations below are performed on Windows 11.
In this module, the pyproject.toml and MANIFEST.in are as follows:
These files can be used for installation, distribution, or uploading to the Python Package Index (PyPI).
Testing the Module
Before sharing the completed module with others, we should test it to ensure that installation and functionality work correctly.
In the project root directory, we can run the following command in editable mode, allowing us to test changes to the source code without needing to rebuild the package each time:
1
pipinstall-e.
If the module provides GPU-related functionality, you can install the optional dependencies using the following command:
Microsoft Windows [版本 10.0.26100.6584](c) Microsoft Corporation. 著作權所有,並保留一切權利。
C:\Users\user\Desktop\github\autoFRK-python\autoFRK>python -m build
* Creating isolated environment: venv+pip...
* Installing packages in isolated environment:
- setuptools>=61.0
- wheel
* Getting build dependencies for sdist...
C:\Users\user\AppData\Local\Temp\build-env-vieuwf3x\Lib\site-packages\setuptools\config\_apply_pyprojecttoml.py:82: SetuptoolsDeprecationWarning: `project.license` as a TOML table is deprecated
!!
********************************************************************************
Please use a simple string containing a SPDX expression for`project.license`. You can also use `project.license-files`. (Both options available on setuptools>=77.0.0).
By 2026-Feb-18, you need to update your project and remove deprecated calls
or your builds will no longer be supported.
See https://packaging.python.org/en/latest/guides/writing-pyproject-toml/#license for details.
********************************************************************************
!!
corresp(dist, value, root_dir)running egg_info
creating src\autoFRK.egg-info
writing src\autoFRK.egg-info\PKG-INFO
writing dependency_links to src\autoFRK.egg-info\dependency_links.txt
writing requirements to src\autoFRK.egg-info\requires.txt
writing top-level names to src\autoFRK.egg-info\top_level.txt
writing manifest file 'src\autoFRK.egg-info\SOURCES.txt'reading manifest file 'src\autoFRK.egg-info\SOURCES.txt'reading manifest template 'MANIFEST.in'warning: no files found matching '*.txt' under directory 'src\autoFRK'warning: no files found matching '*.md' under directory 'src\autoFRK'adding license file 'LICENSE'writing manifest file 'src\autoFRK.egg-info\SOURCES.txt'* Building sdist...
C:\Users\user\AppData\Local\Temp\build-env-vieuwf3x\Lib\site-packages\setuptools\config\_apply_pyprojecttoml.py:82: SetuptoolsDeprecationWarning: `project.license` as a TOML table is deprecated
!!
********************************************************************************
Please use a simple string containing a SPDX expression for`project.license`. You can also use `project.license-files`. (Both options available on setuptools>=77.0.0).
By 2026-Feb-18, you need to update your project and remove deprecated calls
or your builds will no longer be supported.
See https://packaging.python.org/en/latest/guides/writing-pyproject-toml/#license for details.
********************************************************************************
!!
corresp(dist, value, root_dir)running sdist
running egg_info
writing src\autoFRK.egg-info\PKG-INFO
writing dependency_links to src\autoFRK.egg-info\dependency_links.txt
writing requirements to src\autoFRK.egg-info\requires.txt
writing top-level names to src\autoFRK.egg-info\top_level.txt
reading manifest file 'src\autoFRK.egg-info\SOURCES.txt'reading manifest template 'MANIFEST.in'warning: no files found matching '*.txt' under directory 'src\autoFRK'warning: no files found matching '*.md' under directory 'src\autoFRK'adding license file 'LICENSE'writing manifest file 'src\autoFRK.egg-info\SOURCES.txt'running check
creating autofrk-0.1.0
creating autofrk-0.1.0\src\autoFRK
creating autofrk-0.1.0\src\autoFRK.egg-info
creating autofrk-0.1.0\src\autoFRK\utils
copying files to autofrk-0.1.0...
copying LICENSE -> autofrk-0.1.0
copying MANIFEST.in -> autofrk-0.1.0
copying README.md -> autofrk-0.1.0
copying pyproject.toml -> autofrk-0.1.0
copying src\autoFRK\___init__.py -> autofrk-0.1.0\src\autoFRK
copying src\autoFRK\autoFRK.py -> autofrk-0.1.0\src\autoFRK
copying src\autoFRK\mrts.py -> autofrk-0.1.0\src\autoFRK
copying src\autoFRK.egg-info\PKG-INFO -> autofrk-0.1.0\src\autoFRK.egg-info
copying src\autoFRK.egg-info\SOURCES.txt -> autofrk-0.1.0\src\autoFRK.egg-info
copying src\autoFRK.egg-info\dependency_links.txt -> autofrk-0.1.0\src\autoFRK.egg-info
copying src\autoFRK.egg-info\requires.txt -> autofrk-0.1.0\src\autoFRK.egg-info
copying src\autoFRK.egg-info\top_level.txt -> autofrk-0.1.0\src\autoFRK.egg-info
copying src\autoFRK\utils\___init__.py -> autofrk-0.1.0\src\autoFRK\utils
copying src\autoFRK\utils\device.py -> autofrk-0.1.0\src\autoFRK\utils
copying src\autoFRK\utils\logger.py -> autofrk-0.1.0\src\autoFRK\utils
copying src\autoFRK\utils\predictor.py -> autofrk-0.1.0\src\autoFRK\utils
copying src\autoFRK\utils\utils.py -> autofrk-0.1.0\src\autoFRK\utils
copying src\autoFRK.egg-info\SOURCES.txt -> autofrk-0.1.0\src\autoFRK.egg-info
Writing autofrk-0.1.0\setup.cfg
Creating tar archive
removing 'autofrk-0.1.0'(and everything under it)* Building wheel from sdist
* Creating isolated environment: venv+pip...
* Installing packages in isolated environment:
- setuptools>=61.0
- wheel
* Getting build dependencies for wheel...
C:\Users\user\AppData\Local\Temp\build-env-exul05dl\Lib\site-packages\setuptools\config\_apply_pyprojecttoml.py:82: SetuptoolsDeprecationWarning: `project.license` as a TOML table is deprecated
!!
********************************************************************************
Please use a simple string containing a SPDX expression for`project.license`. You can also use `project.license-files`. (Both options available on setuptools>=77.0.0).
By 2026-Feb-18, you need to update your project and remove deprecated calls
or your builds will no longer be supported.
See https://packaging.python.org/en/latest/guides/writing-pyproject-toml/#license for details.
********************************************************************************
!!
corresp(dist, value, root_dir)running egg_info
writing src\autoFRK.egg-info\PKG-INFO
writing dependency_links to src\autoFRK.egg-info\dependency_links.txt
writing requirements to src\autoFRK.egg-info\requires.txt
writing top-level names to src\autoFRK.egg-info\top_level.txt
reading manifest file 'src\autoFRK.egg-info\SOURCES.txt'reading manifest template 'MANIFEST.in'warning: no files found matching '*.txt' under directory 'src\autoFRK'warning: no files found matching '*.md' under directory 'src\autoFRK'adding license file 'LICENSE'writing manifest file 'src\autoFRK.egg-info\SOURCES.txt'* Building wheel...
C:\Users\user\AppData\Local\Temp\build-env-exul05dl\Lib\site-packages\setuptools\config\_apply_pyprojecttoml.py:82: SetuptoolsDeprecationWarning: `project.license` as a TOML table is deprecated
!!
********************************************************************************
Please use a simple string containing a SPDX expression for`project.license`. You can also use `project.license-files`. (Both options available on setuptools>=77.0.0).
By 2026-Feb-18, you need to update your project and remove deprecated calls
or your builds will no longer be supported.
See https://packaging.python.org/en/latest/guides/writing-pyproject-toml/#license for details.
********************************************************************************
!!
corresp(dist, value, root_dir)running bdist_wheel
running build
running build_py
creating build\lib\autoFRK
copying src\autoFRK\autoFRK.py -> build\lib\autoFRK
copying src\autoFRK\mrts.py -> build\lib\autoFRK
copying src\autoFRK\___init__.py -> build\lib\autoFRK
creating build\lib\autoFRK\utils
copying src\autoFRK\utils\device.py -> build\lib\autoFRK\utils
copying src\autoFRK\utils\logger.py -> build\lib\autoFRK\utils
copying src\autoFRK\utils\predictor.py -> build\lib\autoFRK\utils
copying src\autoFRK\utils\utils.py -> build\lib\autoFRK\utils
copying src\autoFRK\utils\___init__.py -> build\lib\autoFRK\utils
running egg_info
writing src\autoFRK.egg-info\PKG-INFO
writing dependency_links to src\autoFRK.egg-info\dependency_links.txt
writing requirements to src\autoFRK.egg-info\requires.txt
writing top-level names to src\autoFRK.egg-info\top_level.txt
reading manifest file 'src\autoFRK.egg-info\SOURCES.txt'reading manifest template 'MANIFEST.in'warning: no files found matching '*.txt' under directory 'src\autoFRK'warning: no files found matching '*.md' under directory 'src\autoFRK'adding license file 'LICENSE'writing manifest file 'src\autoFRK.egg-info\SOURCES.txt'installing to build\bdist.win-amd64\wheel
running install
running install_lib
creating build\bdist.win-amd64\wheel
creating build\bdist.win-amd64\wheel\autoFRK
copying build\lib\autoFRK\autoFRK.py -> build\bdist.win-amd64\wheel\.\autoFRK
copying build\lib\autoFRK\mrts.py -> build\bdist.win-amd64\wheel\.\autoFRK
creating build\bdist.win-amd64\wheel\autoFRK\utils
copying build\lib\autoFRK\utils\device.py -> build\bdist.win-amd64\wheel\.\autoFRK\utils
copying build\lib\autoFRK\utils\logger.py -> build\bdist.win-amd64\wheel\.\autoFRK\utils
copying build\lib\autoFRK\utils\predictor.py -> build\bdist.win-amd64\wheel\.\autoFRK\utils
copying build\lib\autoFRK\utils\utils.py -> build\bdist.win-amd64\wheel\.\autoFRK\utils
copying build\lib\autoFRK\utils\___init__.py -> build\bdist.win-amd64\wheel\.\autoFRK\utils
copying build\lib\autoFRK\___init__.py -> build\bdist.win-amd64\wheel\.\autoFRK
running install_egg_info
Copying src\autoFRK.egg-info to build\bdist.win-amd64\wheel\.\autoFRK-0.1.0-py3.12.egg-info
running install_scripts
creating build\bdist.win-amd64\wheel\autofrk-0.1.0.dist-info\WHEEL
creating 'C:\Users\user\Desktop\github\autoFRK-python\autoFRK\dist\.tmp-mxuu22cz\autofrk-0.1.0-py3-none-any.whl' and adding 'build\bdist.win-amd64\wheel' to it
adding 'autoFRK/___init__.py'adding 'autoFRK/autoFRK.py'adding 'autoFRK/mrts.py'adding 'autoFRK/utils/___init__.py'adding 'autoFRK/utils/device.py'adding 'autoFRK/utils/logger.py'adding 'autoFRK/utils/predictor.py'adding 'autoFRK/utils/utils.py'adding 'autofrk-0.1.0.dist-info/licenses/LICENSE'adding 'autofrk-0.1.0.dist-info/METADATA'adding 'autofrk-0.1.0.dist-info/WHEEL'adding 'autofrk-0.1.0.dist-info/top_level.txt'adding 'autofrk-0.1.0.dist-info/RECORD'removing build\bdist.win-amd64\wheel
Successfully built autofrk-0.1.0.tar.gz and autofrk-0.1.0-py3-none-any.whl
C:\Users\user\Desktop\github\autoFRK-python\autoFRK>
Uploading the Module
We can use the twine tool to upload the packaged files we just created to PyPI (Python Package Index), allowing others to install the module via pip install.
PyPI is the default source for pip, where modules can be downloaded and installed.
1
pipinstallmodule_name
First, install the upload tool twine:
1
pipinstall--upgradetwine
TestPyPI
PyPI provides a testing environment, so we can choose to first upload and test the package on TestPyPI before publishing it to the official PyPI.
TestPyPI account registration page.
If this is your first time using TestPyPI, register a TestPyPI account and check your email for the verification message.
Confirming the account.
For first-time users, upon returning to TestPyPI, save the recovery code for account access in case of loss.
Enter a saved recovery code to confirm.
Next, enable 2FA verification to proceed with other operations.
2FA verification.
Now, in Account settings under API tokens, generate a token to allow terminal uploads of the module.
Manage tokens.Create a token.
The token is displayed only once. After obtaining it, create the file C:\Users\your_username\.pypirc and enter the following content to save the token:
After returning to TestPyPI, you can see the newly uploaded module in the Your projects section.
Recently uploaded module.
Once the upload is confirmed, you can test the installation using the following command. The --extra-index-url https://pypi.org/simple flag ensures that any required dependencies from the official PyPI are also found.
Once everything is ready, you can officially upload the module to PyPI. The detailed upload process is documented below.
PyPI account registration page.
First, if this is your first time using PyPI, create an account. Follow the registration steps and check your email for the verification message, just like with TestPyPI.
Confirming the account.
After returning to PyPI, save the recovery code as a precaution.
Save the recovery code.
Enter your password to continue. Enter password.
It is recommended to click Save to store the recovery code locally.
Save the recovery code.
Next, enter a saved recovery code to confirm your account.
Enter a saved recovery code to confirm.
As with TestPyPI, 2FA verification must be enabled to proceed.
Enable 2FA verification.
At this point, account registration is complete.
Now, just like with TestPyPI, you need to create a token to upload the module from your local machine.
First, go to Account settings.
Account settings.
Locate the API tokens section and click Add API token.
API tokens.
Provide a name for the token for future reference.
Token name.
Select the Scope (should be Entire account) and click Create token.
Select scope and create token.
Copy the token displayed in the red box below; it will be used later.
PyPI token.
On your local machine, create a file .pypirc in C:\Users\your_username\ and enter the following content to store the token. If you previously used TestPyPI, its token will also be saved here.
Save PyPI token.
Finally, open a terminal in the project directory and run the following command to upload the module to PyPI:
1
twineuploaddist/*
Uploading the module.
The expected output should look like this:
Upload successful.
Note
PyPI and TestPyPI do not accept modules with the same version number. Remember to update the module version in pyproject.toml for each upload.
After returning to PyPI, you can see the newly uploaded module in the Your projects section.
Recently uploaded module.
You can also click View to see the module’s page, which will display the README content you wrote earlier.
Module page.
Once the upload is confirmed, you can install the module using the following command:
1
pipinstallmodule_name
Conclusion
Creating a custom Python module is an exciting experience. Sharing a module that I built myself gives me a strong sense of accomplishment. During the development of this module, I conducted extensive testing and realized the importance of maintaining a well-structured README and organizing the module files systematically. In the future, I hope to further refine this module, continuously optimizing its functionality and user interface to maximize its practical benefits.
The last update time of this article is October 22, 2025, and the content may be outdated. Please be aware. If you notice any errors or broken images, feel free to leave a comment for corrections.