In software programming, there are lots of repetitive tasks which are popular in most projects. These tasks often require the same code and logic to be completed. This poses a huge problem for programmers as they always have to rewrite the same code and logic for each project. Examples of such tasks include validating an email, generating random strings, etc.
A popular solution to this problem is by packaging the code into an installable package (also known as a library). The package can then be installed and used in every project without having to rewrite the logic. In most cases, such packages are always open-sourced and available for the public to use for free.
This practice is common in most programming languages such as Python, JavaScript, etc. Through this tutorial, you will learn how to build and deploy a Python library to Pypi - the official repository for Python libraries.
Project Overview
This tutorial will guide you on how to build and deploy an open-source Python library that wraps a REST API and its endpoints.
The Python Library
The library you will build is a wrapper for the Exchange Rates REST API by Abstract API. The API has three endpoints which include the following:
- "/live": To get live exchange rates between currencies.
- "/convert": To get live exchange rates between two or more currencies by specifying a base amount, e.g 5 USD to GBP.
- "/historical": To get past exchange rates between currencies within a specific period.
The library will allow developers to communicate with these endpoints through regular Python code. Also, you can use this API without declaring the endpoints every time you need to use it
Project requirements
To take this tutorial, you are required to have a prior knowledge of coding with Python, and concepts such as Object-oriented programming (OOP), Python functions, etc.
You should also understand how to use git, create a Github repository(repo), and push a project to Github.
Tools and Packages
You need the following tools for this guide:
- Python: A Python interpreter(>=3.9) for running Python files.
- Requests: A Python library used for making HTTP requests.
- Abstract API account: You need an AbstractAPI account to access the API key for the Exchange rates API(steps will be provided in the tutorial).
- Dotenv: A Python library for handling environment variables.
- Poetry: A Python dependency management and packaging tool.
Create a new project.
Follow the steps below to create a new project:
1. Create a new folder using the terminal/command line with the
command below:
mkdir exchangeLibrary
2. Install virtualenv
with pip:
Run the command below to installvirtualenv
(skip the process if
you have it installed):
pip install virtualenv
3. Change your working folder to the exchangeLibrary folder:
cd exchangeLibrary
4. Create and activate a new virtual environment:
Run the command below to create a env virtual environment:
virtualenv env
Once it has been created, activate it with any of the commands below:
On Windows (Command prompt):
.\env\Scripts\activate
On Linux/macOS:
source env/bin/activate
5. Install required packages:
Run the command below to install required packages:
pip install requests poetry dotenv
6. Open the folder in a code editor like Pycharm/VScode:
This is required to write the code for the library.
Setup project resources
The project’s resources are the necessary folders and files required to build the library. Open up the project in a code editor like Pycharm or VScode.
Do the following to add new files to your project:
1. Create a new folder in your project:
The folder must be named src
. In this folder, add a new folder named exchange_python
. Also, within the exchange_python
folder, add a __init__.py
and exchange.py
file. Your project file structure should now look like the image below:
The src
folder separates the library files from the rest of the project. The exchange_python
folder is the main folder which will contain the files required by the Python library. The __init__.py
and exchange.py
file will contain the library's code (more information on this later).
2. Create a new file in your project:
The file should be named test.py
which will be used to write tests for the Python library.
3. Get api key from AbstractAPI:
Follow the steps below to get an API key for the Exchange Rates API:
a. Sign up on AbstractAPI
b. Login to your dashboard
c. Hover your cursor on the left panel of your dashboard
d. Select the Exchange Rates API
e. Get the API key labelled Primary key on the new page.
4. Create environment variables:
Create a new .env
file in your project. Add your API key to this file as seen below:
API_KEY=<your-api-key-here>
The file structure for your project should look like the image below:
Build the API wrapper
The next step is to write a Python class as a wrapper for the Exchange Rates API. Each endpoint in this API will be represented as a method within the wrapper. Add the code below to the exchange.py
file:
import requests
class ExchangeRatesAPI:
def __init__(self, api_key):
self.api_key: str = api_key
self.__ratesURL: str = "https://exchange-rates.abstractapi.com/v1/"
def __type_validation(self, type_, arg):
if not isinstance(arg, type_):
raise TypeError(f"Expected '{type_}' but got {type(arg).__name__}")
In the code above, the ExchangeRatesAPI
class has two attributes - an api-key to authenticate users, and the API's base URL. It also has a __type_validation()
method which will be used to validate users’ input. Both the attributes and method are prefixed with double underscore(__). This keeps them private and not easily accessible outside the class.
Although, it is not totally private as there are ways around it. But that is beyond the scope of this tutorial.
Wrap the /live endpoint
This endpoint accepts three request parameters - api-key(required), the base currency code(required), and the target currency code(optional).
The base currency is that currency you want to get the exchange rates for, relative to other currencies. For example, I may want to know the rate at which USD exchanges with other currencies of the world. Therefore, USD is the base currency code.
The target currency code is optional and limits the API response to the selected currencies. For example, CAD to USD, CAD, and GBP. This endpoint will be represented with the live()
method. Here is the code below:
import requests
class ExchangeRatesAPI:
...
def live(self, base: str, **kwargs):
params = {"api_key": self.api_key}
self.__type_validation(str, base)
params["base"] = base
target = kwargs.get("target", None)
if target:
self.__type_validation(str, target)
params["target"] = target
url = self.__ratesURL + "live"
response = requests.request("GET", url, params=params)
json_response = response.json()
json_response["status"] = response.status_code
return json_response
In the live()
method, the required parameters are set as positional arguments, while optional ones are set as keyword arguments. Type validation is also performed on each argument before they are added to the request parameters.
Finally, a request is sent to the /live endpoint of the API.
Wrap the /historical endpoint
The request parameters are, api-key(required), base currency code(required), date(required) and target currencies' codes(optional).
This is similar to the /live endpoint except that it is dated in the past. The format for the date argument is YYYY-MM-DD. This endpoint will be represented with the historical()
method. Below is the code for the method:
import requests
class ExchangeRatesAPI:
...
def historical(self, base: str, date: str, **kwargs):
params = {"api_key": self.api_key}
self.__type_validation(str, base)
self.__type_validation(str, date)
params["base"] = base
params["date"] = date
target = kwargs.get("target", None)
if target:
self.__type_validation(str, target)
params["target"] = target
url = self.__ratesURL + "historical"
response = requests.request("GET", url, params=params)
json_response = response.json()
json_response["status"] = response.status_code
return json_response
Wrap the /convert endpoint
The request parameters for this endpoint are api-key(required), base currency(required), target currency(required), date(optional), and base_amount(optional).
The date parameter is passed if you want the results to refer to a date in the past. The base amount is any amount of the base currency you want to convert. For example, 10 USD to CAD, where USD is the base currency and CAD is the target currency.
This endpoint only accepts one target currency per request. It will be represented with the convert()
method. Below is the code for this method:
import requests
class ExchangeRatesAPI:
...
def convert(self, base: str, target: str, **kwargs):
params = {"api_key": self.api_key}
self.__type_validation(str, base),
self.__type_validation(str, target)
params["base"] = base,
params["target"] = target
date = kwargs.get("date", None)
base_amount = kwargs.get("base_amount", None)
if date:
self.__type_validation(str, date)
params["date"] = date
if base_amount:
self.__type_validation(str, base_amount)
params["base_amount"] = base_amount
url = self.__ratesURL + "convert"
response = requests.request("GET", url, params=params)
json_response = response.json()
json_response["status"] = response.status_code
return json_response
Note that type enforcement is done for all arguments, e.g base: str
. While this may not validate each argument, it helps users know the data type for each argument when using the library.
Test the API wrapper
Bugs are inevitable. Writing tests is important before publishing the library. Python's unittest library will be used to test the ExchangeRatesAPI
class. For a complete guide on unittest, read the Beginner’s guide to unit tests in Python. Follow the steps below to test the codebase:
1. Open the init.py file and add the code below:
from .exchange import ExchangeRatesAPI
This allows you to import and use the ExchangeRatesAPI
class as
a package and not via a relative file path.
2. Add the code below to the test.py file:
from exchange_python import ExchangeRatesAPI
import unittest
import dotenv
import os
# Configure environment variables
dotenv.load_dotenv()
exchange_api_key = os.environ.get("API_KEY", "")
exchange = ExchangeRatesAPI(exchange_api_key)
class TestExchangeAPI(unittest.TestCase):
def test_live(self):
self.assertIsNotNone(exchange.live("USD"))
def test_convert(self):
self.assertIsNotNone(exchange.convert("USD", "BRL"))
def test_historical(self):
self.assertIsNotNone(exchange.historical("USD", "2022-02-02"))
if __name__ == "__main__":
unittest.main()
3. Run the test script:
If there are no errors, you should see a message like the one
below in the console:
Deploy the project
This section will show you how to deploy your library
Setup deployment requirements
Follow the steps below to set up deployment requirements:
1. Create a .gitignore file in your project
2. Add the following files to it:
env
.env
3. Create a README.md file and write a brief description about the
library
4. Create a pyproject.toml file in your project
5. Add the following content to this file:
[tool.poetry]
name = “<your python library name>”
version = "0.1.0"
description = "<a description for the library>"
authors = ["<Your name here> <Your email here>"]
license = "MIT"
readme = "README.md"
repository = "<a link to the your project’s github repo>"
keywords = []
include = [
{ path = "src"}
]
[tool.poetry.dependencies]
python = ">=3.8"
requests = ">=2.32.2"
[build-system]
requires = [
"poetry-core"
]
build-backend = "poetry.core.masonry.api"
This file contains the configuration details for your library. Fill in the spaces enclosed with these symbols – “<>”. The include
variable consists of folders that contain the library files.
6. Proceed to Github to create a new repo.
7. Initialize git in your project with the command below:
git init
8. Connect the repo to your project with the command below:
git remote add origin “https://github.com/<your-github-username>/<your-github-repo-name>”
9. Update the pyproject.toml file with the link to your repo:
repository = "https://github.com/<your-github-username>/<your-github-repo-name>"
10. Push your code to Github.
Deploy the library
Python libraries are deployed on pypi.org. Before you continue, use the search bar on pypi to check if a library with the same name as yours exists.
If you find one, select a different name for your library and update the pyproject.toml
file with the new name.
You need to create an account on Pypi to get an api-key for deployment. The following steps will show you how to do this:
1. Sign up and verify your email address:
This is required to complete the registration. A verification link will be sent to your mail.
2. Set up 2 factor authentication:
The 2-factor authentication (2FA) is meant to protect your account. You cannot deploy a Python package without completing this step.
3. Click Generate recovery code to begin THE 2FA:
The final part OF THE 2FA requires an authenticator app to scan a QR code and complete the 2FA
4. Download and configure an authenticator app:
Use an authenticator browser extension like authenticator by authenticator.cc. It works for both Chrome and Edge browsers. Once installed you can use it to scan the QR code from Pypi
5. Get the API token:
Click on the Account settings tab in your dashboard. Scroll to the
bottom of the page to get an API token to deploy your project (click
the Add API token button)
6. Open a terminal/command line in your project (if you are using
VScode) or open your project in a new terminal and activate the
virtual environment.
7. Run the command below to build your package (This will generate a
dist file in your project):
poetry build
8. Set your pypi api token with the command below:
poetry config pypi-token.pypi <your pypi api-token>
9. Publish your library with the command below:
poetry publish
Your Python package will be published on Pypi. You can always find the package in your dashboard or via Pypi’s search bar.
Project maintenance
It definitely does not end here. Overtime, you may need to refactor the codebase or make major changes in your project. After any update, ensure you push the changes to Github.
Run the following commands in the terminal to update the library with the new changes:
a. Activate virtual environment and build the package with the command
below:
poetry build
b. Publish to Pypi:
poetry publish
Note: The pyproject.toml
file manages your package’s version with the version variable. You may need to update the version
when you make new updates. This is useful to track your package’s version history.
For example, if your project’s version is 0.1.0
. You can change it to 0.1.1
or 1.0.0
, depending on how you want the version’s progression to be.
Installation and usage
Once the library is published, you can install it by running the command below:
pip install <your-python-library-name>
You can now import the library in your project as seen below:
from exchange_python import ExchangeRatesAPI
Conclusion
In this tutorial, you learnt how to build and deploy a Python library to Pypi. You also learnt how to update your project and re-publish the changes.
Building and deploying a Python library is an exciting process that allows you to share your code with the world. By following the steps outlined in this guide, you now have the knowledge to create, package, and publish your own libraries.
Whether you're solving common problems, providing useful utilities, or simply sharing your unique approach, your contribution helps make Python development more accessible and robust.
Top comments (0)