DEV Community

arnu515
arnu515

Posted on • Edited on

Create a PyPI (pip) package, test it and publish it using Github Actions (PART 1)

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()
Enter fullscreen mode Exit fullscreen mode

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"))
Enter fullscreen mode Exit fullscreen mode
OUTPUT:
test
TEST
There Was A Man!
generate-a-slug-for-my-post
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Since tests is a package, it will be included in our pip package because setuptools.findpackage will add it to our pip package. This is not what we want, so let's do a quick change in setup.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
Enter fullscreen mode Exit fullscreen mode

The discover keyword tells unittest to find any files with the name of test_*.py and run the unittests inside them.

You may have gotten an error stating:

No module named pydash
Enter fullscreen mode Exit fullscreen mode

We can fix that by installing pydash using setup.py

python3 setup.py install
cd tests
python3 -m unittest discover
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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/*
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

And upload on PyPI:

python3 -m twine upload dist/*
Enter fullscreen mode Exit fullscreen mode

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)

Collapse
 
arnu515 profile image
arnu515