Skip to content
← ALL POSTS

Build a CLI application with Python and Typer

CLI applications are ubiquitous and we are using them every day whether it be Git, Curl, or Pip. In this blog, I will show you how to build a Site

SAIMON HOSSAIN3 MIN READ

CLI applications are ubiquitous and we are using them every day whether it be Git, Curl, or Pip. In this blog, I will show you how to build a Site Connectivity Checker using Python and Typer. Typer is a fantastic library for quickly building CLI apps using Python’s standard type hints. It’s written by the same author of FastAPI – Sebastián Ramírez.

Overview

    • Our application will make GET requests to the URLs provided by the users as a space-separated string or in a file, and display results.
    • By default, the app will make blocking requests with the help of the requests library.
    • We’ll make asynchronous requests to make our app faster with the aiohttp library.

URL Retriever

Users of our application can input URLs either as a space-separated string or in a file. Let’s implement the functions necessary to parse the URLs.

URLs from string

Python: url_string_parser takes a string and a separator defaulting to a space, and returns the string split on it

URLs from file

Python: url_file_parser opens a file and reads its lines, returning an empty list on FileNotFoundError and otherwise each line stripped

Blocking HTTP Requests

By default, our application will make blocking HTTP requests with the requests library. Let’s write our code for this default behavior.

Python: make_request loops the URLs inside a typer.progressbar, calling requests.get on each and appending the URL with its status code, or with 404 when the connection fails

Progressbar

You can display the progress of any time-consuming operations using the typer.progressbar.

Non-blocking HTTP Requests

For multiple websites, blocking HTTP request is a very time-consuming process. Instead we can do better with the help of aiohttp. Our application will perform asynchronous requests if the --a option is supplied. Let’s write some functions to facilitate non-blocking HTTP requests. Python: an async fetch that GETs one URL through an aiohttp ClientSession and returns the URL with its status, or 404 on a connection error, and make_async_request which builds a task per URL inside a progress bar and awaits them all with asyncio.gather

The same async fetch and make_async_request functions shown again

Displaying the results

Python: display_result loops the results and prints each one with typer.echo as the status code followed by the URL

Printing with Echo

You can use typer.echo() to print to the screen. It’s recommended to use echo() instead of the standard print() function.

Enter main()

Let’s dive deep into the entry point of our app – the main.

Python: the main function collects URLs from the urls string and the file, then runs either the async or the blocking request path depending on the a flag, and typer.run(main) is the entry point

CLI options

We have three CLI options, namely --urls, --file and --a.

    • If you supply --urls, the app will parse the URLs from the given string.
    • If you supply --file, the app will read all the URLs in that file.
    • If the --a flag is enabled, the app will make asynchronous HTTP requests.
    • The order of options does not matter.

Running our application

To run the app with a bunch of URLs separated by space, use the following command:

 python cli-app/main.py –urls “https://google.com https://facebook.com" 

By default, the app will make blocking HTTP requests.If you want to make non-blocking requests, supply the --a flag.

 python cli-app/main.py –urls “https://google.com https://facebook.com” –a 

If you want your app to read URLs from a file, just pass the --file option. You can also provide both --urls and --file options to allow your app to retrieve URLs from the given string and the file. 

 python cli-app/main.py –file “cli-app/urls.txt” –urls “https://google.com https://facebook.com” -a

NEWSLETTER

One engineering letter a month

New writing from our engineers, no marketing filler.

[ ONE CONVERSATION AWAY ]

Rather ask an engineer than read another post?

Thirty minutes, no sales script. Bring the problem you are actually stuck on and we will tell you honestly whether we are the right partner for it.