Hiển thị thông báo trợ giúp với python argparse khi tập lệnh được gọi mà không có bất kỳ đối số nào


226

Đây có thể là một đơn giản. Giả sử tôi có một chương trình sử dụng argparse để xử lý các đối số / tùy chọn dòng lệnh. Sau đây sẽ in thông báo 'trợ giúp':

./myprogram -h

hoặc là:

./myprogram --help

Nhưng, nếu tôi chạy tập lệnh mà không có bất kỳ đối số nào, nó sẽ không làm gì cả. Những gì tôi muốn nó làm là hiển thị thông báo sử dụng khi nó được gọi mà không có đối số. Làm thế nào được thực hiện?

Câu trả lời:


273

Câu trả lời này đến từ Steven Bethard trên các nhóm Google . Tôi đang đăng lại ở đây để giúp mọi người không có tài khoản Google truy cập dễ dàng hơn.

Bạn có thể ghi đè hành vi mặc định của errorphương thức:

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()

Lưu ý rằng giải pháp trên sẽ in thông báo trợ giúp bất cứ khi nào error phương thức được kích hoạt. Ví dụ: test.py --blahcũng sẽ in thông báo trợ giúp nếu --blahkhông phải là một tùy chọn hợp lệ.

Nếu bạn chỉ muốn in thông báo trợ giúp nếu không có đối số nào được cung cấp trên dòng lệnh, thì có lẽ đây vẫn là cách dễ nhất:

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()

Lưu ý rằng parser.print_help()in ra thiết bị xuất chuẩn theo mặc định. Như init_js gợi ý , sử dụng parser.print_help(sys.stderr)để in ra stderr.


Vâng .. đó là những gì tôi đã tự hỏi, liệu có cách nào để argparse xử lý tình huống này. Cảm ơn!
musashiXXX

6
Trong giải pháp thứ hai tôi sử dụng parser.print_usage()thay cho parser.print_help()- thông báo trợ giúp bao gồm việc sử dụng nhưng nó dài dòng hơn.
user2314737

5
Tôi đã bỏ phiếu cho phần thứ hai của câu trả lời, nhưng ghi đè error()có vẻ là một ý tưởng khủng khiếp đối với tôi. Nó phục vụ một mục đích khác, nó không được thiết kế để in một cách sử dụng hoặc trợ giúp thân thiện.
Peterino

@Peterino - việc ghi đè đang xảy ra trong một lớp con, vì vậy đây không phải là vấn đề. Nó rõ ràng.
Marcel Wilson

1
@unutbu TUYỆT VỜI! Chính xác những gì tôi cần. Một câu hỏi, điều này có thể được áp dụng cho các tiểu ban không? Tôi thường chỉ nhận được `` Không gian tên (đầu ra = Không) `. Làm cách nào tôi có thể dễ dàng gây ra lỗi trên TẤT CẢ các tiểu ban? Tôi muốn kích hoạt một lỗi ở đó.
Jonathan Komar

56

Thay vì viết một lớp, một thử / ngoại trừ có thể được sử dụng thay thế

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

Ưu điểm là quy trình làm việc rõ ràng hơn và bạn không cần một lớp sơ khai. Nhược điểm là dòng 'sử dụng' đầu tiên được in hai lần.

Điều này sẽ cần ít nhất một đối số bắt buộc. Không có đối số bắt buộc, việc cung cấp số không đối số trên dòng lệnh là hợp lệ.


tôi cũng vậy, tôi thích điều này hơn với câu trả lời được chấp nhận Thêm một lớp là quá mức cần thiết để in trợ giúp khi các đối số không mong muốn. Hãy để mô-đun argparse tuyệt vời xử lý các trường hợp lỗi cho bạn.
Nicole Finnie

7
Mã này in giúp 2 lần nếu -hcờ được sử dụng và in không cần thiết giúp nếu --versioncờ được sử dụng. Để giảm thiểu những vấn đề đó, bạn có thể kiểm tra loại lỗi như sau:except SystemExit as err: if err.code == 2: parser.print_help()
pkowalchot

25

Với argparse bạn có thể làm:

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
Điều này phải đến trước cuộc gọi đếnparser.parse_args()
Bob Stein

18

Nếu bạn có các đối số phải được chỉ định để tập lệnh chạy - hãy sử dụng tham số bắt buộc cho ArgumentParser như dưới đây: -

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

parse_args () sẽ báo lỗi nếu tập lệnh được chạy mà không có bất kỳ đối số nào.


2
Đây là giải pháp đơn giản nhất và cũng sẽ hoạt động với các tùy chọn không hợp lệ được chỉ định.
Steve Scherer

1
Đã đồng ý. Tôi nghĩ sẽ tốt hơn khi tận dụng các khả năng tích hợp của trình phân tích cú pháp đối số sau đó để viết một trình xử lý bổ sung nào đó.
Christopher Hunter

18

Nếu bạn liên kết các chức năng mặc định cho các trình phân tích cú pháp (phụ), như được đề cập bên dưới add_subparsers, bạn có thể chỉ cần thêm nó làm hành động mặc định:

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

Thêm thử - ngoại trừ nếu bạn đưa ra ngoại lệ do thiếu các đối số vị trí.


1
Câu trả lời này được đánh giá quá thấp. Đơn giản và hoạt động rất tốt với các trình phân tích cú pháp phụ.
orodbhen

Câu trả lời chính xác! Thay đổi duy nhất tôi đã thực hiện là sử dụng lambda không có tham số.
boh717

12

Giải pháp sạch nhất sẽ là truyền thủ công mặc định đối số nếu không được đưa ra trên dòng lệnh:

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

Ví dụ hoàn chỉnh:

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))

Điều này sẽ in trợ giúp hoàn chỉnh (không sử dụng ngắn) nếu được gọi là đối số w / o.


2
sys.argv[1:]là một thành ngữ rất phổ biến. Tôi thấy parser.parse_args(None if sys.argv[1:] else ['-h'])nhiều thành ngữ và sạch sẽ hơn.
Nuno André

1
@ NunoAndré cảm ơn - đã cập nhật câu trả lời. Cảm thấy nhiều pythonic thực sự.
Ievgen Popovych

10

Ném phiên bản của tôi vào đống ở đây:

import argparse

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

Bạn có thể nhận thấy parser.exit- Tôi chủ yếu làm như vậy vì nó lưu một dòng nhập nếu đó là lý do duy nhất systrong tệp ...


Parser.exit (1) là tốt đẹp! Tốt bổ sung.
Csseller

4
Thật không may, Parser.parse_args () sẽ thoát nếu thiếu đối số vị trí. Vì vậy, điều này chỉ hoạt động khi sử dụng các đối số tùy chọn.
Marcel Wilson

1
@MarcelWilson, nó thực sự - bắt tốt! Tôi sẽ suy nghĩ về cách thay đổi nó.
pauricthelodger

not vars(args)có thể không hoạt động khi đối số có defaultphương thức.
funkid

5

Có một cặp lớp lót với sys.argv[1:](thành ngữ Python rất phổ biến để chỉ các đối số dòng lệnh, làsys.argv[0] tên của tập lệnh) có thể thực hiện công việc.

Đầu tiên là tự giải thích, sạch sẽ và pythonic:

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

Cái thứ hai là một chút tin tặc. Kết hợp thực tế được đánh giá trước đó rằng một danh sách trống là Falsevới True == 1False == 0tương đương bạn có được điều này:

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

Có thể quá nhiều dấu ngoặc, nhưng khá rõ ràng nếu lựa chọn đối số trước đó được thực hiện.

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

1
parser.print_help()
parser.exit()

Các parser.exitphương pháp cũng chấp nhận một status(mã trả), và mộtmessage giá trị (bao gồm một ký tự dòng mới chính mình!).

một ví dụ đầy quan điểm, :)

#!/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

Các cuộc gọi ví dụ:

$ python3 ~ / helloworld.py; tiếng vang $?
cách sử dụng: helloworld.py [-h] [--example]

 Ví dụ tệp python dựa trên argparser

đối số tùy chọn:
  -h, --hỗ trợ hiển thị thông báo trợ giúp này và thoát
  - Ví dụ đối số ví dụ

Tôi chỉ không biết những gì đã sai, có thể thiếu - điều kiện mẫu?
128
$ python3 ~ / helloworld.py --example; tiếng vang $?
0

0

Đặt các đối số vị trí của bạn với các giá trị và kiểm tra xem các đối số vị trí có trống không.

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

Tham khảo Python


0

Đây là một cách khác để làm điều đó, nếu bạn cần một cái gì đó linh hoạt nơi bạn muốn hiển thị trợ giúp nếu thông số cụ thể được thông qua, không có gì cả hoặc nhiều hơn 1 đối số xung đột:

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 ...

Chúc mừng!


Tôi nghĩ rằng bạn sẽ dễ dàng hơn nhiều khi sử dụng các bộ lọc con hoặc lẫn nhau_exinating_group
Tim Bray

0

Nếu lệnh của bạn là thứ mà người dùng cần chọn một số hành động, thì hãy sử dụng nhóm loại trừ lẫn nhau với required = True .

Đây là một phần mở rộng cho câu trả lời được đưa ra bởi 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')

Đầu ra:

$ python3 a_test.py
sử dụng: a_test.py [-h] (--batch pay_id | --list | --all)
a_test.py: error: một trong các đối số --batch --list --all là bắt buộc

Điều này chỉ cung cấp cho sự giúp đỡ cơ bản. Và một số câu trả lời khác sẽ cung cấp cho bạn sự giúp đỡ đầy đủ. Nhưng ít nhất người dùng của bạn biết họ có thể làm -h


0

Điều này không tốt (cũng vậy, vì chặn tất cả các lỗi), nhưng:

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(...)
    ...

Dưới đây là định nghĩa về errorchức năng của ArgumentParserlớp:

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

. Như bạn thấy, sau chữ ký, phải mất hai đối số. Tuy nhiên, các hàm bên ngoài lớp không biết gì về đối số đầu tiên : self, bởi vì, đại khái, đây là tham số cho lớp. (Tôi biết, bạn biết ...) Qua đó, chỉ cần vượt qua riêng selfmessagetrong _error(...)không có thể (

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

    sys.exit(-1)

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

    parser.error = _error
    ...
...

sẽ xuất ra:

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

). Bạn có thể truyền parser( self) trong _errorhàm, bằng cách gọi nó:

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

    sys.exit(-1)

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

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

, nhưng bạn không muốn thoát khỏi chương trình, ngay bây giờ. Sau đó trả lại:

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

        sys.exit(-1)

    return wrapper
...

. Tuy nhiên, parserkhông biết rằng nó đã được sửa đổi, do đó, khi xảy ra lỗi, nó sẽ gửi nguyên nhân của nó (nhân tiện, bản dịch được bản địa hóa của nó). Chà, sau đó chặn nó lại:

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

        sys.exit(-1)

    return wrapper
...

. Bây giờ, khi xảy ra lỗi và parsersẽ gửi nguyên nhân của nó, bạn sẽ chặn nó, xem xét điều này và ... ném ra ngoài.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.