인수없이 스크립트를 호출하면 Python Argparse와 함께 도움말 메시지 표시


226

이것은 간단한 것일 수 있습니다. argparse를 사용하여 명령 줄 인수 / 옵션을 처리하는 프로그램이 있다고 가정합니다. 다음은 '도움말'메시지를 인쇄합니다.

./myprogram -h

또는:

./myprogram --help

그러나 인수없이 스크립트를 실행하면 아무것도하지 않습니다. 내가 원하는 것은 인수없이 호출 될 때 사용법 메시지를 표시하는 것입니다. 어떻게됩니까?

답변:


273

이 답변은 Google 그룹의 Steven Bethard 가 제공합니다 . Google 계정이없는 사용자가 더 쉽게 액세스 할 수 있도록 여기에 다시 게시하고 있습니다.

error메소드 의 기본 동작을 대체 할 수 있습니다 .

import argparse
import sys

class MyParser(argparse.ArgumentParser):
    def error(self, message):
        sys.stderr.write('error: %s\n' % message)
        self.print_help()
        sys.exit(2)

parser = MyParser()
parser.add_argument('foo', nargs='+')
args = parser.parse_args()

위의 솔루션은 error 메소드가 트리거 될 때마다 도움말 메시지를 인쇄합니다 . 예를 들어, 유효한 옵션이 아닌 test.py --blah경우 도움말 메시지도 인쇄합니다 --blah.

명령 행에 인수가 제공되지 않은 경우에만 도움말 메시지를 인쇄하려는 경우 여전히 가장 쉬운 방법입니다.

import argparse
import sys

parser=argparse.ArgumentParser()
parser.add_argument('foo', nargs='+')
if len(sys.argv)==1:
    parser.print_help(sys.stderr)
    sys.exit(1)
args=parser.parse_args()

참고 parser.print_help()기본적으로 표준 출력으로 인쇄합니다. init_js가 제안 했듯이 parser.print_help(sys.stderr)stderr에 인쇄하는 데 사용하십시오 .


예 ..이 시나리오를 처리 할 수있는 방법이 있는지 궁금합니다. 감사!
musashiXXX

6
내가 parser.print_usage()대신 사용하는 두 번째 솔루션 parser.print_help()-도움말 메시지에 사용법이 포함되어 있지만 더 자세합니다.
user2314737

5
나는 대답의 두 번째 부분에 투표했지만 재정의 error()는 나에게 끔찍한 생각 인 것 같습니다. 다른 용도로 사용되며 친숙한 사용법이나 도움말을 인쇄하도록 설계되지 않았습니다.
Peterino

@Peterino-자식 클래스에서 재정의가 발생하므로 문제가되지 않습니다. 명시 적입니다.
Marcel Wilson

1
@unutbu 원더풀! 정확히 내가 필요한 것. 한 가지 질문은 이것도 하위 명령에도 적용될 수 있습니까? 나는 보통``Namespace (output = None)``을 얻습니다. 모든 부속 명령에서 오류를 쉽게 트리거 할 수 있습니까? 거기에 오류를 발생시키고 싶습니다.
Jonathan Komar

56

클래스를 작성하는 대신 try / except를 대신 사용할 수 있습니다.

try:
    options = parser.parse_args()
except:
    parser.print_help()
    sys.exit(0)

단점은 워크 플로가 더 명확하고 스텁 클래스가 필요 없다는 것입니다. 단점은 첫 번째 '사용'줄이 두 번 인쇄된다는 것입니다.

이것은 적어도 하나의 필수 주장이 필요합니다. 필수 인수가 없으면 명령 행에서 0 개의 인수를 제공하는 것이 유효합니다.


나도 받아 들인 대답보다 이것을 선호합니다. 인수가 예상치 못한 경우 클래스를 추가하면 도움말을 인쇄하기 위해 과도하게 사용됩니다. 우수한 모듈 argparse가 오류 사례를 처리하도록합니다.
Nicole Finnie

7
이 코드는 -h플래그를 사용 하면 도움말을 두 번 인쇄하고 플래그를 사용하면 불필요한 인쇄 도움말을 인쇄합니다 --version. 이러한 문제를 완화하기 위해 다음과 같은 오류 유형을 확인할 수 있습니다.except SystemExit as err: if err.code == 2: parser.print_help()
pkowalczyk

25

argparse를 사용하면 다음을 수행 할 수 있습니다.

parser.argparse.ArgumentParser()
#parser.add_args here

#sys.argv includes a list of elements starting with the program
if len(sys.argv) < 2:
    parser.print_usage()
    sys.exit(1)

5
이 와야 하기 전에 호출parser.parse_args()
밥 스타

18

스크립트를 실행하기 위해 지정해야하는 인수가있는 경우 다음 과 같이 ArgumentParser에 필요한 매개 변수를 사용하십시오 .

parser.add_argument('--foo', required=True)

인수없이 스크립트를 실행하면 parse_args ()가 오류를보고합니다.


2
이것은 가장 간단한 솔루션이며 지정된 유효하지 않은 옵션에서도 작동합니다.
Steve Scherer

1
동의했다. 필자는 인수 파서의 내장 기능을 활용 한 다음 어떤 종류의 추가 처리기를 작성하는 것이 항상 더 낫다고 생각합니다.
Christopher Hunter

18

아래 add_subparsers에 언급 된 것처럼 (하위) 파서에 대한 기본 기능을 연결 하면 간단히 기본 작업으로 추가 할 수 있습니다.

parser = argparse.ArgumentParser()
parser.set_defaults(func=lambda x: parser.print_usage())
args = parser.parse_args()
args.func(args)

위치 인수가 누락되어 예외가 발생하면 try-except를 추가하십시오.


1
이 답변은 너무 과소 평가되었습니다. 단순하고 하위 파서와 매우 잘 작동합니다.
orodbhen

좋은 대답입니다! 내가 한 유일한 변경은 매개 변수없이 람다를 사용하는 것입니다.
boh717

12

가장 깨끗한 해결책은 명령 줄에 기본 인수가 없으면 수동으로 기본 인수를 전달하는 것입니다.

parser.parse_args(args=None if sys.argv[1:] else ['--help'])

완전한 예 :

import argparse, sys

parser = argparse.ArgumentParser()
parser.add_argument('--host', default='localhost', help='Host to connect to')
# parse arguments
args = parser.parse_args(args=None if sys.argv[1:] else ['--help'])

# use your args
print("connecting to {}".format(args.host))

인수없이 호출되면 완전한 도움말 (짧은 사용법이 아님)을 인쇄합니다.


2
sys.argv[1:]매우 일반적인 관용구입니다. 나는 parser.parse_args(None if sys.argv[1:] else ['-h'])더 관용적이고 더 깨끗합니다.
Nuno André

1
@ NunoAndré 감사합니다-답변을 업데이트했습니다. 실제로 더 비로소 느낌.
Ievgen Popovych

10

내 버전을 더미에 던지기 :

import argparse

parser = argparse.ArgumentParser()
args = parser.parse_args()
if not vars(args):
    parser.print_help()
    parser.exit(1)

당신은 알 수 있습니다 parser.exit- sys파일에서 유일한 이유라면 가져 오기 줄을 저장하기 때문에 주로 그렇게 합니다 ...


parser.exit (1)이 좋습니다! 좋은 추가.
cgseller

4
불행히도 위치 인수가 없으면 parser.parse_args ()가 종료됩니다. 따라서 이것은 선택적 인수를 사용할 때만 작동합니다.
Marcel Wilson

1
@MarcelWilson, 그것은 실제로 않습니다-잘 잡습니다! 변경하는 방법에 대해 생각하겠습니다.
pauricthelodger

not vars(args)인수에 default메소드 가 있으면 작동하지 않을 수 있습니다 .
funkid

5

작업을 수행 할 수있는 한 줄짜리 쌍 sys.argv[1:](명령 줄 인수를 참조하는 매우 일반적인 파이썬 관용구 sys.argv[0])이 있습니다.

첫 번째는 자명하고 깨끗하며 피 토닉입니다.

args = parser.parse_args(None if sys.argv[1:] else ['-h'])

두 번째는 약간의 해커입니다. 이전에 평가 한 사실을 빈 목록이 같고 동등 하다는 것을 결합하면 False다음 True == 1False == 0같습니다.

args = parser.parse_args([None, ['-h']][not sys.argv[1:]])

대괄호가 너무 많을 수도 있지만 이전에 인수를 선택했다면 분명합니다.

_, *av = sys.argv
args = parser.parse_args([None, ['-h']][not av])

1
parser.print_help()
parser.exit()

parser.exit메소드는 status(returncode) 및 message값 (후행 줄 바꿈 포함 ) 도 허용합니다 .

의견이 많은 예 :)

#!/usr/bin/env python3

""" Example argparser based python file
"""

import argparse

ARGP = argparse.ArgumentParser(
    description=__doc__,
    formatter_class=argparse.RawTextHelpFormatter,
)
ARGP.add_argument('--example', action='store_true', help='Example Argument')


def main(argp=None):
    if argp is None:
        argp = ARGP.parse_args()  # pragma: no cover

    if 'soemthing_went_wrong' and not argp.example:
        ARGP.print_help()
        ARGP.exit(status=128, message="\nI just don't know what went wrong, maybe missing --example condition?\n")


if __name__ == '__main__':
    main()  # pragma: no cover

호출 예 :

$ python3 ~ / helloworld.py; 에코 $?
사용법 : helloworld.py [-h] [-예]

 argparser 기반 파이썬 파일 예제

선택적 인수 :
  -h, --help이 도움말 메시지를 표시하고 종료
  --example 인수 예

나는 단지 무엇이 잘못되었는지, 아마도 실례가 아닌지 모릅니다.
128
$ python3 ~ / helloworld.py --example; 에코 $?
0

0

nargs를 사용하여 위치 인수를 설정하고 위치 인수가 비어 있는지 확인하십시오.

import argparse
parser = argparse.ArgumentParser()
parser.add_argument('file', nargs='?')
args = parser.parse_args()
if not args.file:
    parser.print_help()

파이썬 nargs 참조


0

특정 매개 변수가 전달되는 경우 도움말을 표시하려는 위치에 융통성있는 무언가가 필요한 경우 다음과 같은 다른 방법이 있습니다.

import argparse
import sys

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('-d', '--days', required=False,  help="Check mapped inventory that is x days old", default=None)
    parser.add_argument('-e', '--event', required=False, action="store", dest="event_id",
                        help="Check mapped inventory for a specific event", default=None)
    parser.add_argument('-b', '--broker', required=False, action="store", dest="broker_id",
                        help="Check mapped inventory for a broker", default=None)
    parser.add_argument('-k', '--keyword', required=False, action="store", dest="event_keyword",
                        help="Check mapped inventory for a specific event keyword", default=None)
    parser.add_argument('-p', '--product', required=False, action="store", dest="product_id",
                        help="Check mapped inventory for a specific product", default=None)
    parser.add_argument('-m', '--metadata', required=False, action="store", dest="metadata",
                        help="Check mapped inventory for specific metadata, good for debugging past tix", default=None)
    parser.add_argument('-u', '--update', required=False, action="store_true", dest="make_updates",
                        help="Update the event for a product if there is a difference, default No", default=False)
    args = parser.parse_args()

    days = args.days
    event_id = args.event_id
    broker_id = args.broker_id
    event_keyword = args.event_keyword
    product_id = args.product_id
    metadata = args.metadata
    make_updates = args.make_updates

    no_change_counter = 0
    change_counter = 0

    req_arg = bool(days) + bool(event_id) + bool(broker_id) + bool(product_id) + bool(event_keyword) + bool(metadata)
    if not req_arg:
        print("Need to specify days, broker id, event id, event keyword or past tickets full metadata")
        parser.print_help()
        sys.exit()
    elif req_arg != 1:
        print("More than one option specified. Need to specify only one required option")
        parser.print_help()
        sys.exit()

    # Processing logic here ...

건배!


하위 구문 분석기 또는 mutually_exclusive_group을 사용하는 것이 훨씬 쉬울 것이라고 생각합니다.
Tim Bray

0

명령이 사용자가 어떤 조치를 선택해야하는 항목 인 경우 required = True 인 상호 배타적 그룹을 사용하십시오. .

이것은 pd321의 답변에 대한 확장입니다.

import argparse

parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument("--batch", action='store', type=int,  metavar='pay_id')
group.add_argument("--list", action='store_true')
group.add_argument("--all", action='store_true', help='check all payments')

args=parser.parse_args()

if args.batch:
    print('batch {}'.format(args.batch))

if args.list:
    print('list')

if args.all:
    print('all')

산출:

$ python3 a_test.py
사용법 : a_test.py [-h] (--batch pay_id | --list | --all)
a_test.py : 오류 : 인수 중 하나 --batch --list --all이 필요합니다

이것은 기본적인 도움 만 제공합니다. 그리고 다른 답변 중 일부는 당신에게 완전한 도움을 줄 것입니다. 그러나 적어도 사용자는 그들이 할 수있는 알고 -h


0

이것은 좋지 않지만 (모든 오류를 가로 채기 때문에) :

def _error(parser):
    def wrapper(interceptor):
        parser.print_help()

        sys.exit(-1)

    return wrapper

def _args_get(args=sys.argv[1:]):
    parser = argparser.ArgumentParser()

    parser.error = _error(parser)

    parser.add_argument(...)
    ...

다음은 클래스 의 error기능에 대한 정의입니다 ArgumentParser.

https://github.com/python/cpython/blob/276eb67c29d05a93fbc22eea5470282e73700d20/Lib/argparse.py#L2374

. 보시다시피 서명에 따라 두 가지 인수가 필요합니다. 그러나 클래스 외부의 함수는 첫 번째 인수에 대해 아무것도 모릅니다. self대충 말하면 이것은 클래스의 매개 변수이기 때문입니다. 따라서, 단지 자신의 통과 (나는 ... 당신이 알고있는 것을 알고) selfmessage_error(...)(수 없습니다

def _error(self, message):
    self.print_help()

    sys.exit(-1)

def _args_get(args=sys.argv[1:]):
    parser = argparser.ArgumentParser()

    parser.error = _error
    ...
...

출력합니다 :

...
"AttributeError: 'str' object has no attribute 'print_help'"

). 함수를 호출하여 parser( self)를 전달할 수 있습니다 _error.

def _error(self, message):
    self.print_help()

    sys.exit(-1)

def _args_get(args=sys.argv[1:]):
    parser = argparser.ArgumentParser()

    parser.error = _error(parser)
    ...
...

하지만 지금 프로그램을 종료하고 싶지는 않습니다. 그런 다음 반환하십시오.

def _error(parser):
    def wrapper():
        parser.print_help()

        sys.exit(-1)

    return wrapper
...

. 그럼에도 불구하고, parser그것이 수정되었음을 알지 못하므로 오류가 발생하면 원인을 현지화 번역으로 보냅니다. 그럼 가로 채세요 :

def _error(parser):
    def wrapper(interceptor):
        parser.print_help()

        sys.exit(-1)

    return wrapper
...

. 이제 오류가 발생하여 parser원인을 보내면 오류를 가로 채서 이것을보고 ... 버리십시오.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.