파이썬 argparse unrecognized arguments 오류, 원인별 확인 순서와 안전한 수정법

파이썬 argparse의 unrecognized arguments는 parser가 모르는 값이 들어왔다는 뜻이며, 먼저 sys.argv[0]을 잘못 넘긴 것인지 실행 환경이 추가한 인자인지부터 구분해야 합니다. 일반적인 터미널 실행은 parse_args()를 유지하고, 노트북처럼 외부 인자가 섞일 수 있는 환경은 애플리케이션 인자 목록을 따로 넘기거나 미지 인자를 확인한 뒤 처리하는 방식이 안전합니다. 아래 순서로 확인하면 parser 정의 문제와 실행 환경 문제를 빠르게 나눌 수 있습니다.

1. 오류 메시지에서 미지 인자를 먼저 분리하기

argparse는 정의하지 않은 옵션이나 positional 인자가 들어오면 파싱을 중단합니다. 자동화가 시작되기도 전에 실패하므로, 전체 코드를 고치기보다 오류 메시지에 표시된 값을 그대로 확인하는 편이 좋습니다.

먼저 다음을 확인합니다.

  • -f와 커널 JSON 경로처럼 notebook 실행에 필요한 값인가
  • ipykernel_launcher.py처럼 실행 프로그램 이름이 섞였는가
  • 실제로 명령행에서 입력한 옵션인데 parser에 등록하지 않았는가
  • positional 인자의 개수나 배치가 parser 선언과 맞는가

parse_args()는 인자를 생략하면 명령행 인자를 읽습니다. 따라서 터미널에서는 보통 다음처럼 쓰면 됩니다.

import argparse


def build_parser():
    parser = argparse.ArgumentParser()
    parser.add_argument("input_file")
    parser.add_argument("--dry-run", action="store_true")
    return parser


args = build_parser().parse_args()
print(args)

2. parse_args(sys.argv) 실수와 Jupyter의 -f 구분하기

sys.argv[0]은 스크립트 이름입니다. 그래서 sys.argv 전체를 parse_args()에 넘기면 프로그램 이름까지 애플리케이션의 첫 인자로 해석될 수 있습니다. 명시적으로 목록을 넘겨야 한다면 sys.argv[1:]처럼 실제 애플리케이션 인자부터 전달합니다.

args = build_parser().parse_args(sys.argv[1:])

반면 Jupyter 커널은 ipykernel_launcher와 -f, connection file 경로를 실행 인자에 포함할 수 있습니다. 이 값을 애플리케이션이 직접 파싱하면 -f가 등록되지 않은 옵션으로 보일 수 있습니다. 노트북에서 실행할 때는 테스트용 인자 목록을 명시적으로 분리하는 방법이 가장 예측 가능합니다.

notebook_args = ["example.csv", "--dry-run"]
args = build_parser().parse_args(notebook_args)

실제 명령행을 꼭 받아야 하는 코드라면 sys.argv를 무조건 지우기보다, 어떤 실행 환경에서 어떤 인자가 들어오는지 먼저 로그로 확인해야 합니다. Jupyter 사례의 -f 처리 방식은 Jupyter에서 argparse 오류가 발생하는 사례에서도 확인할 수 있습니다.

3. parse_known_args()는 임시 무시가 아니라 경계 확인용

실행 환경이 추가한 값을 분리해야 한다면 parse_known_args()가 선택지가 될 수 있습니다. 이 메서드는 parser가 아는 인자와 미지 인자를 함께 반환합니다.

args, unknown = build_parser().parse_known_args()

if unknown:
    print("확인되지 않은 인자:", unknown)

여기서 unknown을 항상 버리는 것은 위험합니다. 예약 실행이나 배치 작업에서는 오타 난 옵션도 미지 인자로 남을 수 있기 때문입니다. 운영 코드라면 다음처럼 허용할 범위를 정해 기록하고, 예상하지 못한 값은 실패시키는 편이 낫습니다.

args, unknown = build_parser().parse_known_args()
allowed_runtime_args = {"-f"}

if unknown and not set(unknown).issubset(allowed_runtime_args):
    raise ValueError(f"허용하지 않은 인자입니다: {unknown}")

다만 -f 다음의 connection file 경로처럼 값이 짝으로 따라오는 인자는 단순히 옵션 이름만 검사해서는 충분하지 않습니다. 실행 환경에서 들어오는 전체 목록을 확인하고, 애플리케이션이 정말 무시해도 되는 값인지 결정해야 합니다.

4. positional·optional 배치 문제라면 parser 구조를 점검하기

미지 옵션이 아니라 positional 인자가 문제라면 parser 선언과 호출 순서를 확인합니다. 옵션 이름을 오타 냈거나 positional 값을 너무 많이 전달한 경우에는 parse_known_args()로 숨기지 않는 것이 좋습니다.

positional과 optional 인자를 명령행에서 섞어 써야 하고 기본 파서 동작으로 원하는 결과가 나오지 않는 경우에는 parse_intermixed_args()를 검토할 수 있습니다. 다만 모든 parser 구성에 적용되는 것은 아니며, subparser나 일부 상호 배타 그룹과 함께 사용할 수 없는 제한이 있으므로 현재 parser 구조를 먼저 확인해야 합니다.

또 다른 방법은 -- 구분자를 사용하는 것입니다. -- 뒤의 값은 positional 인자로 처리되므로, 파일명이나 하위 명령에 옵션처럼 보이는 문자열을 전달할 때 경계를 분명히 할 수 있습니다.

운영 전에 결정할 기준

저라면 수정 전에 다음 순서로 판단합니다.

  1. 오류 메시지의 미지 값이 실행 환경 값인지 실제 사용자 입력인지 확인합니다.
  2. 코드가 sys.argv 전체를 넘기고 있다면 sys.argv[1:] 또는 명시적 목록으로 경계를 바로잡습니다.
  3. notebook과 애플리케이션의 인자를 분리할 수 있으면 분리합니다.
  4. 외부 인자를 받아야 한다면 parse_known_args()로 확인하되, 미지 인자를 기록하고 허용 목록 밖의 값은 거부합니다.
  5. positional 배치가 핵심이면 parse_intermixed_args()와 --를 parser 구조에 맞춰 검토합니다.

이 과정을 거쳐도 오류가 남는다면 parser에 실제 옵션이 등록되어 있는지, store_true 같은 불리언 옵션에 값을 잘못 붙이지 않았는지 확인하면 됩니다. 불리언 옵션의 True·False 해석은 별도 문제이므로 argparse 불리언 옵션 선택법에서 이어서 살펴볼 수 있습니다.

참고 자료

Leave a Comment