Publishing Python Package to PyPI

Standard

Hello everyone,

While designing our projects, we always constantly use the structures we plan to use. PyPI comes to the rescue when these structures are generally useful for us when we want to make a package of these structures instead of writing them over and over again…

First, let’s have a sample package to pack. Let’s say we have a structure that converts the given text to a list. We also want to package this structure. We are starting to create our project named “text_to_list”. When creating a library for PyPI, it is necessary to follow its standards;

textToListPackage/
├── LICENSE
├── pyproject.toml
├── setup.py
├── README.md
│── text_to_list/
│     ├── __init__.py
│     └── text_to_list.py
└── tests/

Let’s take a look at the file names in order;

LICENSE, as you can imagine, contains the details of your library’s license. It specifies details such as which packager the clients downloading your pyproject.toml library will install your package, who wrote it, and version dependencies. README.md is the visible documentation of the project in PyPI. Our package itself should have library files under text_to_list. The tests contains the tests of the library.

Preparing the Project for Packaging

In the standard README file of the project, we write how we can load and use our library using Markdown;

 # How to Install
```shell
pip install text_to_list
```
# How to Use It
```python
from text_to_list import text_to_list
text_to_list("Sample Sentence for Library")
```

You can do a quick Google search for the LICENSE file, I prefer to using the LICENSE file from the official document;

Copyright (c) 2022 The Python Packaging Authority

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

After defining the structures such as classes and functions to be used in our library in text_to_list.py, we do not forget to call these files in init.py, so they will be accessible after the package is installed.

text_to_list.py;

def text_to_list(text: str):
    """
    :param text: The text to be taken as a string and converted to a list.
    :return: Return parameter of function converted to list
    """
    listed_text = text.split(sep=" ")
    return listed_text

__init__.py;

from .text_to_list import text_to_list

Our setup.py file is a summary of the project, we specify the version information, ownership and dependencies here. There are multiple alternatives to package your project (setuptools, Hatcling, Flit etc.), but since the most common one is setuptools, I wanted to explain it in the same structure;

import setuptools

with open('README.md', 'r', encoding='utf-8') as fh:
    long_description = fh.read()

setuptools.setup(
    name='text_to_list',
    author='Sezer BOZKIR',
    version="0.0.1",
    author_email='admin@sezerbozkir.com',
    description='Example PyPI (Python Package Index) Package',
    keywords='example, pypi, package',
    long_description=long_description,
    long_description_content_type='text/markdown',
    url='https://github.com/natgho/text_to_list',
    project_urls={
        'Documentation': 'https://github.com/natgho/text_to_list',
        'Bug Reports':
        'https://github.com/natgho/text_to_list/issues',
        'Source Code': 'https://github.com/natgho/text_to_list',
    },
    package_dir={'': 'text_to_list'},
    packages=setuptools.find_packages(where='text_to_list'),
    classifiers=[
        # see https://pypi.org/classifiers/
        'Development Status :: 5 - Production/Stable',

        'Intended Audience :: Developers',
        'Topic :: Software Development :: Build Tools',

        'Programming Language :: Python :: 3',
        'Programming Language :: Python :: 3.6',
        'Programming Language :: Python :: 3.7',
        'Programming Language :: Python :: 3.8',
        'Programming Language :: Python :: 3.9',
        'Programming Language :: Python :: 3 :: Only',
        'License :: OSI Approved :: MIT License',
        'Operating System :: OS Independent',
    ],
    python_requires='>=3.7',
    install_requires=['requests'],
)

Here, I added the “requests” library as a dependency as an example of installing dependencies. When we install our package, the requests library will be automatically installed in this way.

If all is well, we can now install and test our package;

pip install .

When our library and dependencies are loaded, we should be able to open the interactive console and run it as follows;

from text_to_list import text_to_list
print(text_to_list("This is very funny sample."))
# output: ['this', 'is', 'very', 'funny', 'sample.']

Now we can install our package and move on to the pull step via pypi.

Testing Our Package in a Test PyPI Environment

Before we install the package in the real environment, we test it by registering and importing our package into PyPI’s test environment to make sure everything is working fine. (link)

To install our package on the PyPI environment, we need to install the Twine library. This library will help us install our package. (details);

pip install twine

We build our package;

python3 -m build

When this step is completed successfully, we should be able to see the “dist” folder under our project. The dist folder is the folder containing the details of our package. To transfer our project to the test environment;

python -m twine upload --repository testpypi dist/* 

In the output of this command, console ask for your username and password, and enter the information you registered in this step;

Uploading distributions to https://test.pypi.org/legacy/
Enter your username: natgho
Enter your password:
Uploading text_to_list-0.0.1-py3-none-any.whl
100% ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 7.4/7.4 kB • 00:00 • ?
Uploading text_to_list-0.0.1.tar.gz
100% ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 7.3/7.3 kB • 00:00 • ?

View at:
https://test.pypi.org/project/text-to-list/0.0.1/

When we enter the URL address, we see that our README file has been added as a description and everything is fine. Now it’s time to take our package to the real environment;

twine upload dist/*

We see the same steps, this time on the console for the real environment;

Uploading distributions to https://upload.pypi.org/legacy/
Enter your username: natgho
Enter your password:
Uploading text_to_list-0.0.1-py3-none-any.whl
100% ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 7.4/7.4 kB • 00:00 • ?
Uploading text_to_list-0.0.1.tar.gz
100% ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 7.3/7.3 kB • 00:00 • ?

View at:
https://pypi.org/project/text-to-list/0.0.1/

Now that everything is completed, we try to install our package, this time by calling it via pip;

sezer@optimist ~> pip install text-to-list
pip install text-to-list
Collecting text-to-list
  Using cached text_to_list-0.0.1-py3-none-any.whl (2.3 kB)
Collecting requests
  Using cached requests-2.28.1-py3-none-any.whl (62 kB)
Collecting certifi>=2017.4.17
  Using cached certifi-2022.9.24-py3-none-any.whl (161 kB)
Collecting idna<4,>=2.5
  Using cached idna-3.4-py3-none-any.whl (61 kB)
Collecting charset-normalizer<3,>=2
  Using cached charset_normalizer-2.1.1-py3-none-any.whl (39 kB)
Collecting urllib3<1.27,>=1.21.1
  Using cached urllib3-1.26.12-py2.py3-none-any.whl (140 kB)
Installing collected packages: urllib3, idna, charset-normalizer, certifi, requests, text-to-list
Successfully installed certifi-2022.9.24 charset-normalizer-2.1.1 idna-3.4 requests-2.28.1 text-to-list-0.0.1 urllib3-1.26.12

https://github.com/Natgho/text-to-list

See you in another article, you can reach the project example from the Github link above 🙂

References;

https://packaging.python.org/en/latest/tutorials/packaging-projects/
https://github.com/psf/requests
https://github.com/tomchen/example_pypi_package
https://towardsdatascience.com/create-your-own-python-package-and-publish-it-into-pypi-9306a29bc116

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.