Creating a PyPI package is easier than you think, because all you have to do is create a python package, a setup.py
file that defines your package and upload it on PyPI (The Python Package Index) for millions of users to install via pip
.
Writing our code
First, we need to create a python package. I have a very simple package in mind, one that allows you to interact and manipulate strings, like lodash.
I'm gonna be using PyCharm as my IDE of choice, but feel free to use anything you want (VSCode, Spyder, IDLE, Vim, etc).
My folder will be named pydash
, as it is the name of my pip package. Inside the pydash
folder, let's create another folder called pydash
. This new pydash
folder will house all of our code. Create an __init__.py
file in that folder. We'll be editing that file.
class PyDash:
def __init__(self):
pass
@staticmethod
def lower(string: str):
"""
Converts a string to lowercase
"""
return string.lower()
@staticmethod
def upper(string: str):
"""
Converts a string to uppercase
"""
return string.upper()
@staticmethod
def title(string: str):
"""
Converts a string to titlecase
"""
return string.title()
@staticmethod
def kebab(string: str):
"""
Converts a string to kebabcase
"""
return string.replace(" ", "-").lower()
Testing our package
Create a python file named test.py
, index.py
or whatever you want OUTSIDE the pydash
folder (the one containing __init__.py
. We can import and test our package like so:
from pydash import PyDash
print(pydash.lower("TEST"))
print(pydash.upper("test"))
print(pydash.title("there WAS a MAN!"))
print(pydash.kebab("Generate a Slug for my Post"))
OUTPUT:
test
TEST
There Was A Man!
generate-a-slug-for-my-post
Adding a setup.py
Our package works great! You can now delete your test python file. We're almost ready to get this package up and running on PyPI. There's just one problem. PyPI knows nothing about our project, so, let's tell it something. Create a file named setup.py
in your project directory. This directory listing should help:
- pydash
| - pydash
| - __init__.py
| - setup.py
First, we have to decide on a name that is not taken on PyPI. Unfortunately for me, PyDash already exists, so I'm gonna go with the name pydash-arnu515
.
Once you've gotten setup with your name, let's add some stuff to setup.py
Create some more files
Let's now add a README.md
file so that setup.py
can reference it.
Let's add a .gitignore
, so that we don't commit any files we aren't supposed to.
__pycache__/
build/
dist/
*.egg-info/
*.egg
venv
I recommend marking these directories as
excluded
in PyCharm to avoid getting errors like "Duplicated Code Fragment".
Now, one last file, the LICENSE
. In setup.py
, I set my license to be MIT
, so that's what I'm going for.
And that's it for our python package!
Testing our package with unittest
.
Unittest is a builtin python module that allows us to test our package. This blog post doesn't introduce unittest
to you. For that, there are many more tutorials online. Here, I'll just show you how you can test your python package.
Creating our tests.
Create a folder named tests
in the project folder and create a file named __init__.py
in it. This should be how your folder looks like:
- pydash
| - pydash
| - __init__.py
| - tests
| - __init__.py
| - setup.py
Since
tests
is a package, it will be included in our pip package becausesetuptools.findpackage
will add it to our pip package. This is not what we want, so let's do a quick change insetup.py
packages=find_packages(exclude="tests")
Adding our first test
Create a file named test_string.py
.
PyCharm allows me to run unittests automatically, but I'm not going to do that, since not everyone uses PyCharm. Instead, we'll use the command line to test our app.
cd tests
python3 -m unittest discover
The
discover
keyword tellsunittest
to find any files with the name oftest_*.py
and run the unittests inside them.
You may have gotten an error stating:
No module named pydash
We can fix that by installing pydash using setup.py
python3 setup.py install
cd tests
python3 -m unittest discover
Your tests should've run successfully.
Uploading to TestPyPI
Now, for the fun, wait, what is TestPyPI? TestPyPI is a separate instance of PyPI meant to test your packages first, so let's upload our package there first.
We will need to install a few packages first.
pip install twine wheel
We need to create an account on TestPyPI first. Head over to TestPyPI and create an account (or login if you have one already). You will need to verify your email address in order to upload a package.
Create an access token
Let's create an access token which will allow us to upload to TestPyPI without having to type in our email and password all the time. Go to your API tokens in Account Settings and create a full access API token. Give it a name and set its scope to Entire Account.
Copy your API Token and store it in a safe place once it is displayed to you, as it will never be displayed to you again
It is VERY important that you don't share your API token and don't commit it to git because anyone with access to that token can upload packages to your account.
Building and Uploading your package to TestPyPI
Now comes the fun! Let's build your package using this command:
python3 setup.py sdist bdist_wheel
This should create a new folder called dist
in your current directory.
Let's upload that folder using twine
:
python3 -m twine upload -r testpypi dist/*
For the username enter
__token__
, and for the password, paste your API Token.
Voila! Your package has successfully been uploaded to TestPyPI!
View it at https://test.pypi.org/project/YOUR_PROJECT_NAME
For example, mine would be https://test.pypi.org/project/pydash-arnu515
Uploading to PyPI
Now that we've uploaded our package to TestPyPI, we can do the same thing to regular PyPI. Create an account on PyPI.org and then create an API Token.
Build your package again:
python3 setup.py sdist bdist_wheel
And upload on PyPI:
python3 -m twine upload dist/*
For the username enter
__token__
, and for the password, paste your API Token from PyPI.
And we're done
Congratulations! You have a new package up and running on PyPI. For automation with Github Actions, view Part 2
Top comments (1)
Part 2 is out!