This week I released my Python open source project go-go-web using PyPI! This means users no longer have to clone my repo in order to run my app. One simple pip install gogoweb
command can install the app for anyone!
Here's the process for releasing Python software using PyPI.
If you prefer to watch a video, I recommend PyPi - How to Publish your own PIP package by DevOpsJourney. In addition, please see official Python documentation on packaging projects.
1) Create a PyPI account and create an API token
- Register at PyPI.org
- Generate recovery codes (mandatory step)
- Activate Two Factor Authentication (mandatory step)
- Generate API token (save the value!)
2) Install / upgrade package manager, build, publishing tools
Windows
py -m pip install --upgrade pip
py -m pip install --upgrade build
py -m pip install --upgrade twine
Linux/MAC OS
python3 -m pip install --upgrade pip
python3 -m pip install --upgrade build
python3 -m pip install --upgrade twine
3) Restructure your project to have the following structure
project_root/
├── LICENSE
├── pyproject.toml
├── README.md
├── setup.cfg
├── src/
│ └── package_name/
│ ├── __init__.py
│ └── programcode.py
└── tests/
package_name/
: Name this directory the name you would like your software to have when users do pip install
. For example, I used gogoweb
, so users use pip install gogoweb
. This name must not be already taken.
To check if a name is taken:
- run
pip install package_name
to check if a package installs, followed bypip uninstall package_name
- or, check if the url exists, i.e. https://pypi.org/project/package_name
There must be an __init__.py
file in the package_name
folder.
All source files and referenced files for running your project should be inside the package_name
directory.
This is my project structure:
4) Make sure your program still runs successfully on local after restructuring
I had to make changes to my source code in order for the package to run properly:
- When importing modules from the current directory, changed
import module_name
tofrom . import module_name
- This was a fix I did later after releasing my package, because I found that the packaged file, when run, would try to import from the current working directory instead of where the packaged file is situated unless I employ this fix
- Changed the logic in my code to output files by default to a
til
folder in the current working directory instead of the source code folder- This is for user convenience, because the installed package will be in an obscure location like
C:\Users\katie\AppData\Local\Programs\Python\Python312\Lib\site-packages\gogoweb
and users probably won't want to navigate there to get their output files
- This is for user convenience, because the installed package will be in an obscure location like
Updated the import statements in my unit test files and unit test config file since the paths of source files changed
Updated the pylint command in my CI Workflow .yml file since the directory containing the source files to be linted changed
5) Contents of pyproject.toml
[build-system]
requires = [
"setuptools>=42",
"wheel"
]
build-backend = "setuptools.build_meta"
[project]
name = "gogoweb"
authors = [
{name = "Katie Liu", email = "liu_katie@outlook.com"}
]
readme = "README.md"
requires-python = ">=3.7"
classifiers = ["License :: OSI Approved :: MIT License"]
dynamic = ["version", "description"]
dependencies = [
"tomlkit",
"python-frontmatter"
]
[project.urls]
Homepage = "https://github.com/kliu57/go-go-web"
Issues = "https://github.com/kliu57/go-go-web/issues"
Not everything I have in pyproject.toml
is mandatory. The mandatory items are:
- [build-system] section
- [project] section - dependencies (Packages that need to be installed for your program to run successfully. I.e. my program requires the user to do
pip install tomlkit
andpip install python-frontmatter
before running the program.)
6) Contents of setup.cfg
Replace with your own project values.
[metadata]
name = gogoweb
version = 1.0.3
author = Katie Liu
author_email = liu_katie@outlook.com
description = Command-line tool that converts .txt or .md files into .html files.
long_description = file: README.md
long_description_content_type = text/markdown
url = https://github.com/kliu57/go-go-web
project_urls =
Bug Tracker = https://github.com/kliu57/go-go-web/issues
classifiers =
Programming Language :: Python :: 3
License :: OSI Approved :: MIT License
Operating System :: OS Independent
[options]
package_dir =
= src
packages = find:
python_requires = >=3.7
[options.packages.find]
where = src
7) Create the build
Windows
py -m build
Linux/MAC OS
python3 -m build
After you run this command, you should get a "Successfully built" message with a file name that corresponds to the version number you specified in the setup.cfg. Check that the newly created dist/
directory contains that file. If not, troubleshoot before continuing further.
8) Upload your package
Windows
py -m twine upload dist/*
Linux/MAC OS
python3 -m twine upload dist/*
You will be prompted to enter a username and password. For the username, enter __token__
. For the password, copy and then paste your PyPI API token value from Step 1 (On a terminal you can right click to paste).
Successful upload to PyPI:
Once uploaded, my project has its own PyPI website!
Now that the project is uploaded to PyPI
, anyone can download it with a simple pip install
command:
Windows
pip install gogoweb
Linux/MAC OS
python3 -m pip install gogoweb
I can check details of the installation and where the files were installed to:
Windows
pip show gogoweb
Linux/MAC OS
python3 -m pip show gogoweb
After installing, my command-line app can be run with this command.
Windows
py -m gogoweb.convert <file or folder path>
Linux/MAC OS
python3 -m gogoweb.convert <file or folder path>
This will take the file or folder and convert any .txt
and .md
files into .html
files. Default output location is a til
folder in the current working directory. See go-go-web documentation for more details.
Run your package and more
Notice that gogoweb
the name of my package and convert
is the name of the .py
file I want to run. For your package, if you wanted to run a python file from that package:
Windows
py -m <your package>.<your file> [args]
Linux/MAC OS
python3 -m gogoweb.convert <your package>.<your file> [args]
There are many things you could do with your package once installed. For example you could call certain functions that are inside your package:
from <package_name> import <file_name> as pf
pf.somefunction()
Reuploading a package
- Increment the version number in
setup.cfg
- Repeat step 7 to recreate the build
- Repeat step 8 to upload the build
User Testing
Roy and Amnish were kind enough to test my app installation and run! Amnish had no issues with the app, and Roy helped me find and find a fix for a unique issue arising from multiple version of Python installed on his machine. The pip install
installed the app to his Python v3.11 directory, while his terminal python run command is looking for the app in the Python v3.12 directory.
The fix for this is to first find out what Python directory the app was installed to with pip show gogoweb
Then we specify a Python version with our run command:
py -<version number> -m gogoweb.convert [args]
I am very happy to have completed my first software release. Users can now read instructions to install my program with pip on my repo!
Top comments (2)
Woot! Congrats, Katie! 🙌
thanks!