Build a command line app in Python to understand setup.py, argparse, GitHub Actions

I thought it would be nice to make a command line app in Python to learn the techniques around setup.py, argparse, and GitHub Actions, so I tried it. We will not explain each technology in detail here, so please refer to the official documents and commentary articles introduced in the article.

Deliverables

I made a To-Do list app. Even so

todo add "Hoge"
todo complete "Hoge"
todo remove "Hoge"

Just add, complete, and delete to-dos. Also,

todo complete --all
todo remove -a

You can complete or delete everything with. The to-do list is stored in ~ / .todo.json and

$ todo show
□ Hoge
☑ Hogehoge

It can be displayed as.

Creating a command line interface

Use argparse to define the handling of command line arguments.

--argparse --- command line options, arguments, subcommand parsers — Python 3.8.0 documentation -A brief summary of how to use ArgumentParser --Qiita

argparse has too many functions to grasp, but this time I used the following. The entire code is here.

parser = argparse.ArgumentParser()

#Something like commit or run like git commit or docker run
#Is named a subcommand and added
parser.add_argument('subcommand', help='add, complete, or remove')

#The argument that comes after the subcommand is content.
#nargs is the number of arguments, one is nargs=1
# nagrgs='*'Will result in a variadic argument of 0 or more
# nargs='+'Is a variadic argument of 1 or more
# nargs='?'Is 0 or 1
# https://docs.python.org/ja/3/library/argparse.html#nargs
parser.add_argument('content', help='Content of To-Do', nargs='?')

# --all option(Shortened form-a)Add
#It's a flag, not an option that takes arguments`store_true=True`Specify
parser.add_argument('-a', '--all', action='store_true', help='remove or complete all To-Dos')

If the subcommand is show or remove --all, the argument'content' is not necessary, but it can be specified. I didn't know how to write it with argparse so that it couldn't be specified, so even if an argument is specified, it is ignored internally.

You can use this to call the process according to the specified argument (since there is no noteworthy knowledge about the process, source code See sample / blob / master / src / todo / main.py)).

Packaging

Write setup.py to have the command line app installed automatically.

-2. Write setup script — Python 3.8.0 documentation -How to make a Python package (written for internship) --Qiita

The written setup.py can be found at here (written in [pip](https: /) /github.com/pypa/pip/blob/master/setup.py) and numpy However, I think the following parts are important in this.

    entry_points={
        "console_scripts": [
            "todo=todo.main:main"
        ]
    },

This will install the command in your OS's PATH when you do python3 setup.py install. In my environment it looks like this [^ 3].

$ which todo
/home/linuxbrew/.linuxbrew/bin/todo
 $ cat $(which todo)
#!/home/linuxbrew/.linuxbrew/opt/python/bin/python3.7
# EASY-INSTALL-ENTRY-SCRIPT: 'todo==0.1.0','console_scripts','todo'
__requires__ = 'todo==0.1.0'
import re
import sys
from pkg_resources import load_entry_point

if __name__ == '__main__':
    sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
    sys.exit(
        load_entry_point('todo==0.1.0', 'console_scripts', 'todo')()
    )

Now, no matter what directory you are in, typing todo in the terminal will call a function called main in the main module of the todo package to do what you've written.

[^ 3]: I'm using Python that I put in Linuxbrew.

Install with pip

I installed it by running setup.py earlier, but you can also install it from the Git repository using pip as follows. If it's a general-purpose command or package, it's a good idea to register it with PyPI.

pip3 install git+https://github.com/pn11/python-command-line-sample

Automated testing with GitHub Actions

Before I knew it, [^ 1] I wanted to use Actions that was added to GitHub, which was the reason for writing this article [^ 2]. This time I will safely perform an automatic test.

[^ 1]: [Breaking news] GitHub Actions is now official. Build / test / deploy, etc. in GitHub, and realize CI / CD. GitHub Universe 2019-Publickey [^ 2]: It's amazing that new features have been added more and more since it was acquired by MS. ..

For the time being, write a fucking test that just deletes all the to-do lists and then adds them. I did.

import todo.main

def test_add():
    todo.main._remove_todo('', all_flag=True)
    todo.main._add_todo('foo')
    dic = todo.main._load_todos()
    assert dic['foo'] == 'Not Yet'

I would like to do this with nose using GitHub Actions.

Create YAML for Action

If you go to the Actions tab of the repository, you will see a screen like the one below, so let's try using Python package for the time being.

919fa3a4-176e-419d-b249-4892ffe4f18d.jpg

The screen will look like the one below, so just press Start Commit without thinking about anything.

71a5aa3e-4fbb-4a7b-aaad-32fcee02ad5f.jpg

The test fails.

9f8800a4-0658-4dc6-a264-a9aa1a1e1348.jpg

Looking at YAML, it looks like this:

name: Python package

on: [push]

jobs:
  build:

    runs-on: ubuntu-latest
    strategy:
      max-parallel: 4
      matrix:
        python-version: [2.7, 3.5, 3.6, 3.7]

    steps:
    - uses: actions/checkout@v1
    - name: Set up Python ${{ matrix.python-version }}
      uses: actions/setup-python@v1
      with:
        python-version: ${{ matrix.python-version }}
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install -r requirements.txt
    - name: Lint with flake8
      run: |
        pip install flake8
        # stop the build if there are Python syntax errors or undefined names
        flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
        # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
        flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
    - name: Test with pytest
      run: |
        pip install pytest
        pytest

Somehow, under steps:

- name:things to do
  run: |
Command 1
Command 2

It seems good to play with something like this. So, I wrote the following process.

    - name: Install Package
      run: |
        python setup.py install
    - name: Test with nose
      run: |
        pip install nose
        nosetests

After installing the To-Do package with setup.py, also install nose and run the test.

When I committed this, the test passed successfully. I wasn't particularly aware of the execution directory, but it seems to be running at the root of the repository. Also, I used nose somehow, but since it is not a library-dependent test, should it work with pytest as it is?

2e3474ac-5bb2-49ed-a87f-c349eaf64855.jpg

From now on, the test will run every time you commit. If you pass the test, it looks good with the: white_check_mark: mark.

caaef1bb-00d2-4be6-b7b8-eceac4bb906a.jpg

Summary

So far, I created a command with argparse, packaged it with setup.py, and did an automated test with GitHub Actions. In the future, I would like to mass-produce fucking commands using this method.

Other

Although not covered in this article, it may be good to use click etc. in addition to argparse when creating command line tools.

-Create a Python console application easily with Click --Qiita -[Awesome Python: Great Python Framework Library Software Resources --Qiita](https://qiita.com/hatai/items/34c91d4ee0b54bd7cb8b#%E3%82%B3%E3%83%9E%E3% 83% B3% E3% 83% 89% E3% 83% A9% E3% 82% A4% E3% 83% B3% E3% 82% A2% E3% 83% 97% E3% 83% AA% E3% 82% B1% E3% 83% BC% E3% 82% B7% E3% 83% A7% E3% 83% B3% E9% 96% 8B% E7% 99% BA)

Recommended Posts

Build a command line app in Python to understand setup.py, argparse, GitHub Actions
How to get a string from a command line argument in python
A note I looked up to make a command line tool in Python
How to receive command line arguments in Python
How to execute a command using subprocess in Python
In the python command python points to python3.8
Create a command line tool to convert dollars to yen using Python
[Python] I tried to make a simple program that works on the command line using argparse.
Method to build Python environment in Xcode 6
Create a simple GUI app in Python
I want to build a Python environment
How to get a stacktrace in python
Launch a Flask app in Python Anywhere
Decompose command arguments in one line in Python
A way to understand Python duck typing
I have a private Python package but I want to install pipenv on GitHub Actions and build a Docker Image
Various ways to read the last line of a csv file in Python
I made an appdo command to execute a command in the context of the app
How to manage arguments when implementing a Python script as a command line tool
Build a Docker image containing the private repository Python library on GitHub Actions
Template for creating command line applications in Python
[Python] How to test command line parser click
Send a message to LINE with Python (LINE Notify)
Try to calculate a statistical problem in Python
How to clear tuples in a list (Python)
Deploy from GitHub Actions to Azure App Service
To execute a Python enumerate function in JavaScript
How to embed a variable in a python string
How to implement Discord Slash Command in Python
I want to create a window in Python
How to create a JSON file in Python
A clever way to time processing in Python
Make a rock-paper-scissors game in one line (python)
To add a module to python put in Julialang
My thoughts on python2.6 command line app template
How to notify a Discord channel in Python
[Introduction to Udemy Python3 + Application] 67. Command line arguments
[Python] How to draw a histogram in Matplotlib
I want to write a triple loop and conditional branch in one line in python
I can't sleep until I build a server !! (Introduction to Python server made in one day)
Understand Python yield If you put yield in a function, it will change to a generator
How to host web app backend processing in Python using a rental server subdomain
Parse a JSON string written to a file in Python
I want to easily implement a timeout in python
Try to make a Python module in C language
[Python] How to draw a line graph with Matplotlib
Try to make a command standby tool with python
[LINE Messaging API] Create a rich menu in Python
I want to write in Python! (2) Let's write a test
App development to tweet in Python from Visual Studio 2017
Create a plugin to run Python Doctest in Vim (2)
I tried to implement a pseudo pachislot in Python
A memorandum to run a python script in a bat file
I want to randomly sample a file in Python
How to build a Django (python) environment on docker
Steps from installing Python 3 to creating a Django app
I want to work with a robot in python.
Things to note when initializing a list in Python
Introduction to Linear Algebra in Python: A = LU Decomposition
[Python] Created a method to convert radix in 1 second
Write code to Unit Test a Python web app