Hello, I'm Maneshwar. I'm building git-lrc, an AI code reviewer that runs on every commit. It is free, unlimited, and source-available on Github. Star Us to help devs discover the project. Do give it a try and share your feedback for improving the product.
Publishing your first Python package on PyPI (Python Package Index) feels like sending your code off to collegeโitโs out there in the world, ready to make you proud (and maybe help someone too).In my case, I was working on my own version of mkdocs-material.
Initially, I was all about the UIโjust trying to get it to look goodand work as intended. After some effort, I had something usable and thought, "Alright, let's figure out the deployment."
The original mkdocs uses a Python package for its installer, so you can just pip install mkdocs, mkdocs new ., and then mkdocs build to convert markdown files into HTML.
Itโs simple, elegant, and works like a charm.
I wanted the same experience for my projectโan easy pip install setup to make deploying my version of mkdocs-material.
So, it was my time to learn how to build my own Python package and publish it. And thatโs exactly what weโll do here, step by step.
Donโt worryโitโs easier than debugging a Friday night code bug XD
Step 1: Structure ๐
A well-structured project is like a clean kitchenโyou can find your tools (or code) easily and avoid unnecessary messes.
Here's what our project layout will look like:
โโโ dev_to/
โ โโโ __init__.py
โ โโโ say_hello.py # Your module code
โโโ tests/
โ โโโ test_say_hello.py
โโโ README.md
โโโ setup.py # Package metadata
โโโ pyproject.toml # Build system config
โโโ LICENSE
Step 2: Code ๐
This is the heart of your packageโwhat it does and why it exists.
-
dev_to/say_hello.pyA simple function that greets users:
def say_hello(name):
return f"Hello dev.to, from {name}!"
-
dev_to/__init__.pyMakes your function accessible at the package level:
from .say_hello import say_hello
Step 3: Tests โ
Tests ensure your code behaves as expected and helps you avoid embarrassing bugs. Trust me, your future self will thank you.
-
tests/test_say_hello.pyA basic test script:
from dev_to import say_hello
def test_say_hello():
result = say_hello("World")
assert result == "Hello dev.to, from World!"
print("Test passed!")
if __name__ == "__main__":
test_say_hello()
$ p tests/test_say_hello.py
Test passed!
Step 4: Metadata
Metadata describes your package what it is, who made it, and how others can use it.
-
setup.pyConfigure your package:
from setuptools import setup, find_packages
setup(
name="dev_to",
version="0.1.0",
author="lovestaco",
description="A simple example package",
long_description=open("README.md").read(),
long_description_content_type="text/markdown",
url="https://github.com/lovestaco/dev_to",
packages=find_packages(),
classifiers=[
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
],
python_requires=">=3.6",
)
-
pyproject.tomlDeclare the build system:
[build-system]
requires = ["setuptools", "wheel"]
build-backend = "setuptools.build_meta"
Step 5: Build the Package
Building generates the distributable files required for publishing.
Install the tools:
pip install build
Build your package:
python -m build
This creates a dist/ folder containing .tar.gz and .whl files.
$ python -m build
* Creating isolated environment: virtualenv+pip...
* Installing packages in isolated environment:
- setuptools
- wheel
.
.
.
adding 'dev_to-0.1.0.dist-info/RECORD'
removing build/bdist.linux-x86_64/wheel
Successfully built dev_to-0.1.0.tar.gz and dev_to-0.1.0-py3-none-any.whl
Step 6: Moment of Truth
Install the package locally:
pip install dist/dev_to-0.1.0-py3-none-any.whl
Try it out:
Step 7: Publish to PyPI ๐
Install Twine:
pip install twine
Upload your package:
python -m twine upload dist/*
You'll need your PyPI credentials. Once uploaded, your package is live! ๐
Install it like this:
pip install dev-to



Top comments (2)
Great information.
Thanks :)