Python 으로 간단한 CLI 를 만드는 두가지 방법

Shinsro·2023년 1월 21일
3

간단한 코드가 반복적으로 필요할 때 Command Line Interace, CLI 를 만들어두면 편리합니다. 이 글에서는 Python 으로 CLI 를 만드는 다양한 도구와 팁을 공유합니다.

SheBang + argpasre: 가장 간단한 방법

1. SheBang (!#) 문자로 스크립트파일 단독실행시키기

python3 가 설치된 리눅스 기반 환경이라면, 스크립트 파일 첫 줄에 SheBang (!#) 문자를 넣어 해당 스크립트의 인터프리터가 Python3 임을 명시할 수 있습니다. 아래는 SheBang 문자를 추가한, 문자열과 버전을 출력하는 파이썬 스크립트입니다.

#!/usr/bin/env python3

hello: str = "안녕, Python 으로부터"
version: str = f'{sys.version_info.major}.{sys.version_info.minor}'

print(f"{hello}, - python version : {version}")

이 스크립트 파일을 print-hello.py 라 하겠습니다. 이 print-hello.py 를 실행가능한 파일로 변경합니다.

chmod u+x print-hello.py

u+x 는 파일 소유 유저에게 실행권한을 부여하는 chmod 의 옵션입니다.

이제 print-hello.py 를 터미널에서 실행해보세요. 인삿말과 파이썬 버전이 잘 찍힐 겁니다.

약간의 콩고물을 첨가하자면, 이 스크립트 파일에 대한 링크파일을 만든 뒤, $PATH 에 링크 파일 경로를 추가 하는 것입니다. .py 를 떼고 링크파일을 만들면, 확장자 없이 쓸 수 있고, $PATH 상에 파일이 있으면 ./ 실행도 필요 없어서 약간 더 맛있게 쓸 수 있습니다.

mkdir ~/scripts
# 스크립트를 보관할 폴더를 홈 경로에 대충 만들었습니다.
# 저는 print-hello.py 도 ~/scripts 폴더에 뒀습니다.

ln -s $(pwd)/print-hello.py ~/scripts/phello
# phello 란 이름으로 심볼릭 링크를 생성합니다.

cd ~/scripts
PATH=$PATH:$(pwd)
# PATH 환경변수에 ~/scripts 경로를 추가했습니다.
# 일단 시연을 위해서 터미널 세션 스코프에서 적용된 것이니, 
# 방법이 맘에 드시면 .zshrc 등 터미널이 source 하는 곳에 셋팅하셔요.

혹은, alias 를 지정할 수도 있겠습니다.

ls
print-hello.py

❯ alias aphello="$(pwd)/print-hello.py"
❯ aphello
안녕, Python 으로부터, - python version : 3.10

SheBang (!#) 이란?

SheBang 이란 linux like 시스템 상 스크립트 첫 줄에서 프로그램 로더로 하여금 첫 줄을 인터프리터 디렉티브로 파싱하게끔 하는 2Byte 짜리 매직 문자입니다. #!/bin/sh 로 익숙하실 겁니다. 스크립트에 #! 디렉티브가 없다면 #!/bin/sh 를 default 로 삼습니다. #! 바로 뒤에 오는 경로는 그 인터프리터의 절대 경로가 올 수 있습니다. 한편, 본문에서 사용한 #!/usr/bin/env python3 에서 /usr/bin/env 는 리눅스의 일반적인 커맨드입니다. 인자에 커맨드를 넘기지 않고 실행하면 환경변수를 출력하고, 커맨드를 넘겨 실행하면 환경변수를 셋업 한 채 넘겨진 커맨드를 실행합니다. 따라서, #!/usr/bin/env python3 를 통해, 그 문자열 이하에서 작성한 스크립트를 python3 인터프리터로 하여금 실행시킬 수 있는 셈입니다.

2. argparse 로 인자 받기

python3 기본 패키지에는 argparse 모듈이 있습니다. 인자를 파싱하는 모듈입니다. 기본 내장되어있기 때문에 추가적인 설치가 불필요해 맘 편합니다. 위에서 작성한 print-hello.py 에 몇가지 인자를 테스트해서 기능을 추가하겠습니다.

#!/usr/bin/env python3
import argparse
import sys

__arg_parser = argparse.ArgumentParser(
    prog="PrintHello",
    description="인사하는 파이썬 CLI"
)

__arg_parser.add_argument("-v", "--version", action="store_true")
__arg_parser.add_argument("-u", "--username", default="")

__argv = __arg_parser.parse_args(sys.argv[1:])

username: str = __argv.username
show_version: bool = __argv.version

hello: str = f"안녕{f', {username}' if username else ''}. Python 으로부터"
version: str = f" - python version : {sys.version_info.major}.{sys.version_info.minor}" if show_version else ""

print(f"{hello}{version}")

이전에 만들어 둔 phello 링크 파일을 통해 실행해보면, 결과는 아래와 같습니다.

argparse 모듈의 document 링크 를 남겨두오니, 필요하신 기능을 사용하면 되겠습니다.

Typer + Poetry 로 만드는 CLI

다른 라이브러리를 사용할 수 있습니다. 개인적으로 쓸 CLI 라면 다소 과할 수 있겠으나, 만드실 CLI 가 누군가에게 주고 싶은 유틸리티라면 Typer 를 고려해볼만 합니다. 가독성이 좋아서 받으시는 분이 커마하기 좋을 겁니다.

터미널에 예쁘게 나오는 걸 원하신다면 rich 패키지도 받으세요. typer 에서 rich 를 쓰고 있어서 받으시면 출력이 예쁘게 나옵니다. 다만 받으시는 분에게 rich 인스톨하라고 말씀드리기엔 모양이 좋지 않으니, 다음 항목에서 venv 관련한 작업도 소개하겠습니다.

검색하시다보면 click 이란 패키지도 보이실텐데, typerclick 을 씁니다. 해서 click 쓰실거람 typer 쓰는게 나을 수도 있습니다.

1. Typer 로 인자 정의하기

typer-hello.py 란 이름으로 typer 에서 제공한 예제 스크립트를 그대로 가져왔습니다.

import typer

app = typer.Typer()


@app.command()
def main(name: str):
    print(f"Hello {name}")


if __name__ == "__main__":
    app()

실행해보면, 아래와 같습니다. --help 를 통해 볼 수 있는 --install-completion, --show-completiontyper 가 제공하는 default 인자인데, 관심있는 내용은 아니니 무시하겠습니다.

편하게 쓸 수 있는 기능들이 많으니, 필요한 기능은 앞서 드린 Typer 유저 가이드 링크 에서 찾아 사용해보세요. 저는 위의 스크립트를 약간 수정하여 CLI 를 만들어보겠습니다.

import sys

import typer

app = typer.Typer()


@app.command()
def main(
    username: str,
    show_version: bool = typer.Option(prompt="파이썬 버전을 표시할까요?")
):
    hello: str = f"안녕{f', {username}' if username else ''}. Python 으로부터"
    version: str = f" - python version : {sys.version_info.major}.{sys.version_info.minor}" if show_version else ""

    print(f"{hello}{version}")

위처럼 argpasre 보다 좀 더 보기 좋게 인자를 정의할 수 있습니다. 실행결과는 아래와 같습니다.

2. Poetry 을 통해 wheel 로 패키징하기

poetry 로 앞선 스크립트를 실행 가능한, thello 란 커맨드로 패키징해보겠습니다. 사전에 pip3 install poetry 로 인스톨해주세요.

❯ poetry new thello
Created package thello in thello

❯ cd thello
❯ poetry add "typer[all]"
Creating virtualenv thello-8nHNKlnj-py3.10 in .../virtualenvs
Using version ^0.7.0 for typer

Updating dependencies
Resolving dependencies... (2.0s)

Writing lock file

Package operations: 7 installs, 0 updates, 0 removals

  • Installing commonmark (0.9.1)
  • Installing pygments (2.14.0)
  • Installing click (8.1.3)
  • Installing colorama (0.4.6)
  • Installing rich (12.6.0)
  • Installing shellingham (1.5.0.post1)
  • Installing typer (0.7.0)
  
❯ poetry shell
Spawning shell within /Users/ssk/Library/Caches/pypoetry/virtualenvs/thello-8nHNKlnj-py3.10

poetry 를 통해 thello 라는 프로젝트를 만들었고, tpyer[all] 의존성을 프로젝트 venv 에 추가하였습니다. 그리고 peotry shell 을 통해 venv 를 활성화했습니다. 생성된 프로젝트 구조는 아래와 같습니다.

thello/thello 폴더에 위에서 작성한 스크립트를 main.py 란 파일 이름으로 넣겠습니다.

이후, pyproject.toml 파일에서 [tool.poetry.scripts] 섹션에 아래와 같이 적어 넣습니다. 여기서 thello =thello 는 우리가 cli 로서 호출할 이름입니다.

이제 poetry install 을 통해 우리가 작성한 내용을 반영한 뒤, thello 명령어를 실행시켜봅시다.

❯ poetry install
...
❯ thello shins
파이썬 버전을 표시할까요? [y/N]: y
안녕, shins. Python 으로부터 - python version : 3.10 

거의 다 됐습니다. 이제 poetry 로 빌드해봅시다.

❯ poetry build
Building thello (0.1.0)
  - Building sdist
  - Built thello-0.1.0.tar.gz
  - Building wheel
  - Built thello-0.1.0-py3-none-any.whl

이제 다른 터미널에서 생성된 thello-0.1.0-py3-none-any.whl 파일로 thello 를 설치해보세요. 그리고 thello 를 실행해보세요.

pip install --user thello-0.1.0-py3-none-any.whl

--user 옵션이 중요합니다.

--user 가 없으면 글로벌하게 설치가 되니 주의하세요.

thello --help

잘 설치가 되었습니다.

끝으로

이상으로 간단하게 python CLI 를 만드는 방법을 알아보았습니다.

감사합니다.

0개의 댓글