# How to Build a Python Package

Python, Golang, Javascript, etc libraries require packaging. It's done to easily distribute code among users thereby avoiding problems in future development.

In Python, a library is distributed through the Python Package Index (PyPI), a public hosting instance for packages.

## Building the Python Application

The Python application we will push to PyPI will be a simple hello world (or hello user) application. It will be executable from the command line with a simple *hello-world* command or *hello-world --name YOUR_NAME* command.

### Folder Structure

First, let's create the initial files needed. We do this with the following:

```Bash
mkdir -p hello-user/hello_user
touch hello-user/hello_user/main.py hello-user/hello_user/__init__.py
```

This will result in the following structure:

```Shell
.
hello-user
    └── hello_user
        ├── __init__.py
        └── main.py
```

The project has a top-level directory called **hello-world** and a subdirectory, **hello_world** which contains two files.

### Code

Open the **main.py** and fill it up with the code below:

```Python
#!/usr/bin/env python
"""A CLI tool built with Click """

import click

@click.command()
@click.option("--greeting", default='Hello',help="How do you want to greet?")
@click.option("--name", default="World", help="Who to greet?")
def greet(greeting, name):
    print(f"{greeting} {name}")

if __name__ == '__main__':
    greet()
```

First, we're making use of an easy-to-use library called `click`. Click helps us to easily create command-line programs.

**click.command** shows that a function should be exposed to command-line access.
**click.option** adds an argument to the command-line, automatically linking it to the function parameter of the same name (**--greeting** to greet and **--name** to name).
click does some work behind the scenes so that we can call our greet method in our main block without parameters that are covered by the options decorators.

These decorators handle parsing command-line arguments and automatically produce help messages.

## Packaging the Python Application

### Installing the Required Tools
```Bash
pip install setuptools twine
```
### Setup Project
Now we've developed a simple application to be deployed to PyPI, the next thing to do is to configure the package.
To keep things simple (like our python application), we will focus on the minimum amount of files needed to produce a package.

First, let's create *setup.py* file in the top-level directory. This is how this file looks:

```Python
from setuptools import setup, find_packages

setup(
    name="edeediong-hello-user",
    version="0.0.1",
    author="Example Author",
    author_email="author@example.com",
    url="https://edeediong.me",
    description="A hello user package",
    packages=find_packages(),
    classifiers=[
        "Programming Language :: Python :: 3",
        "License :: OSI Approved :: MIT License",
        "Operating System :: OS Independent",
    ],
    install_requires=["click"],
    entry_points={
        "console_scripts": ["hello-world = hello_user.main:greet"]
    }
)
```

The *setup.py* imports two helpers from the setuptools module: *setup* and *find_packages*. The *find_package* discovers all subpackages (Python files) in the code automatically. The *setup* is what defines the package.

The [documentation](https://setuptools.readthedocs.io/en/latest/setuptools.html#basic-use) explains some of the parameters defined in the *setup* function and most are self-explanatory. I choose to explain the parameters that make our application special:

* install_requires: Here we include the package our application depends on.
* entry_points: Here, we ensure that our package can be executed by the user using `hello-world` on the terminal.

To produce a source distribution from the packages built, run the following command, and compare the results:

```Bash
python setup.py sdist
```

A *dist* folder is created at the top-level directory with its source distribution. Details below:

```Shell
.
├── dist
│   └── edeediong-hello-user-0.0.1.tar.gz
├── edeediong_hello_user.egg-info
│   ├── dependency_links.txt
│   ├── entry_points.txt
│   ├── PKG-INFO
│   ├── requires.txt
│   ├── SOURCES.txt
│   └── top_level.txt
├── hello_user
│   ├── __init__.py
│   └── main.py
├── README
└── setup.py
```

## Publishing the Package to PyPI

Python Package Index (PyPI) is a repository of Python software that allows users to host Python packages and also install it.
A Python package called **twine** is used to deploy packages from the local environment to the PyPI repository.

First, we upload the package to TestPyPI to ensure everything works as expected:

``` Shell
twine upload --repository-url https://test.pypi.org/legacy/ dist/edeediong-hello-user-0.0.1.tar.gz
```

> Registration for the test instance of PyPI is important as Twine will ask for username and password to upload packages.

Then we test the package, we install it with pip locally:

```Bash
pip install -i https://test.pypi.org/simple/ edeediong-hello-user
```

Finally, we test our application locally to see if it works fine:

```Bash
$ hello-world
Hello World
$ hello-world --name Eddie
Hello Eddie
```

We don't want to mess with the Python Package Index which is why we won't upload our package to the actual PyPI.
But if we have a live package that needs deployment, we deploy to PyPI with the command below:

```Shell
twine upload dist/edeediong-hello-user-0.0.1.tar.gz
```

The difference here is we don't specify a repository url, we deploy straight to PyPI.

## Conclusion

At the end of this tutorial, we have built a python application, packaged it, and deployed it to PyPI.
I hope this guide helps anyone in the future when building their Python packages.

### Glossary

**edeediong**: this is my name and I used it to avoid naming issues as PyPI needs unique names.
