Tampilkan pesan bantuan dengan python argparse saat skrip dipanggil tanpa argumen apa pun


226

Ini mungkin yang sederhana. Asumsikan saya memiliki program yang menggunakan argparse untuk memproses argumen / opsi baris perintah. Berikut ini akan mencetak pesan 'bantuan':

./myprogram -h

atau:

./myprogram --help

Tetapi, jika saya menjalankan skrip tanpa argumen apa pun, itu tidak melakukan apa-apa. Yang ingin saya lakukan adalah untuk menampilkan pesan penggunaan ketika dipanggil tanpa argumen. Bagaimana itu dilakukan?

Jawaban:


273

Jawaban ini datang dari Steven Bethard di grup Google . Saya memposting ulang di sini untuk memudahkan orang tanpa akun Google untuk mengakses.

Anda dapat mengganti perilaku default errormetode:

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

Perhatikan bahwa solusi di atas akan mencetak pesan bantuan setiap kali error metode ini dipicu. Misalnya, test.py --blahakan mencetak pesan bantuan juga jika --blahbukan opsi yang valid.

Jika Anda ingin mencetak pesan bantuan hanya jika tidak ada argumen yang diberikan pada baris perintah, maka mungkin ini masih cara termudah:

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

Perhatikan bahwa parser.print_help()mencetak ke stdout secara default. Seperti yang disarankan init_js , gunakan parser.print_help(sys.stderr)untuk mencetak ke stderr.


Ya .. itu yang saya pikirkan, apakah ada cara bagi argparse untuk menangani skenario ini. Terima kasih!
musashiXXX

6
Dalam solusi kedua yang saya gunakan parser.print_usage()sebagai pengganti parser.print_help()- pesan bantuan termasuk penggunaan tetapi lebih bertele-tele.
user2314737

5
Saya akan memilih bagian kedua dari jawaban, tetapi mengesampingkan error()tampaknya ide yang buruk bagi saya. Ini melayani tujuan yang berbeda, ini tidak dirancang untuk mencetak penggunaan atau bantuan yang ramah.
Peterino

@ Peterino - override terjadi di kelas anak, jadi ini seharusnya tidak menjadi masalah. Itu eksplisit.
Marcel Wilson

1
@unutbu WONDERFUL! Apa yang saya butuhkan. Satu pertanyaan, dapatkah ini diterapkan pada sub-perintah juga? Saya biasanya hanya mendapatkan `` Namespace (output = None) `. Bagaimana saya bisa memicu kesalahan dengan mudah pada SEMUA sub-perintah? Saya ingin memicu kesalahan di sana.
Jonathan Komar

56

Alih-alih menulis kelas, coba / kecuali dapat digunakan sebagai gantinya

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

Keuntungannya adalah alur kerja lebih jelas dan Anda tidak perlu kelas rintisan. Kelemahannya adalah baris 'penggunaan' pertama dicetak dua kali.

Ini membutuhkan setidaknya satu argumen wajib. Tanpa argumen wajib, memberikan nol argumen di baris perintah valid.


saya juga, saya lebih suka ini daripada jawaban yang diterima. Menambahkan kelas terlalu banyak untuk mencetak bantuan ketika argumen tidak terduga. Biarkan argparse modul yang unggul menangani kasus kesalahan untuk Anda.
Nicole Finnie

7
Kode ini mencetak bantuan 2 kali jika -hflag digunakan, dan cetakan yang tidak perlu membantu jika --versionflag digunakan. Untuk mengurangi masalah-masalah tersebut Anda dapat memeriksa jenis kesalahan seperti ini:except SystemExit as err: if err.code == 2: parser.print_help()
pkowalczyk

25

Dengan argparse yang bisa Anda lakukan:

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
Ini harus datang sebelum panggilan keparser.parse_args()
Bob Stein

18

Jika Anda memiliki argumen yang harus ditentukan untuk menjalankan skrip - gunakan parameter yang diperlukan untuk ArgumentParser seperti yang ditunjukkan di bawah ini: -

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

parse_args () akan melaporkan kesalahan jika skrip dijalankan tanpa argumen.


2
Ini adalah solusi paling sederhana dan akan bekerja dengan opsi yang tidak valid yang ditentukan juga.
Steve Scherer

1
Sepakat. Saya pikir itu selalu lebih baik untuk memanfaatkan kemampuan built-in dari parser argumen kemudian menulis penangan tambahan dari beberapa jenis.
Christopher Hunter

18

Jika Anda mengaitkan fungsi default untuk (sub) parser, seperti yang disebutkan di bawah add_subparsers, Anda bisa menambahkannya sebagai tindakan default:

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

Tambahkan coba-kecuali jika Anda mengajukan pengecualian karena argumen posisi hilang.


1
Jawaban ini sangat diremehkan. Sederhana dan bekerja dengan sangat baik dengan sub-parser.
orodbhen

Jawaban bagus! Satu-satunya perubahan yang saya lakukan adalah menggunakan lambda tanpa parameter.
boh717

12

Solusi terbersih adalah dengan memberikan argumen default secara manual jika tidak ada yang diberikan pada baris perintah:

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

Contoh lengkap:

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

Ini akan mencetak bantuan lengkap (bukan penggunaan singkat) jika dipanggil tanpa argumen.


2
sys.argv[1:]adalah ungkapan yang sangat umum. Saya melihat parser.parse_args(None if sys.argv[1:] else ['-h'])lebih idiomatik dan lebih bersih.
Nuno André

1
@ NunoAndré terima kasih - memperbarui jawabannya. Terasa lebih pythonic.
Ievgen Popovych

10

Melemparkan versi saya ke tumpukan di sini:

import argparse

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

Anda mungkin memperhatikan parser.exit- Saya terutama melakukannya karena menyimpan garis impor jika itu satu-satunya alasan sysdalam file ...


parser.exit (1) bagus! Tambahan yang bagus
cgseller

4
Sayangnya parser.parse_args () akan keluar jika argumen posisional hilang. Jadi ini hanya berfungsi ketika menggunakan argumen opsional.
Marcel Wilson

1
@ MarscelWilson, memang - tangkapan yang bagus! Saya akan memikirkan bagaimana cara mengubahnya.
pauricthelodger

not vars(args)mungkin tidak berfungsi ketika argumen memiliki defaultmetode.
funkid

5

Ada sepasang one-liner dengan sys.argv[1:](idiom Python yang sangat umum untuk merujuk argumen baris perintah, menjadi sys.argv[0]nama skrip) yang dapat melakukan pekerjaan.

Yang pertama cukup jelas, bersih dan pythonic:

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

Yang kedua adalah peretas kecil. Menggabungkan fakta yang dievaluasi sebelumnya bahwa daftar kosong Falsedengan True == 1dan False == 0ekuivalen Anda dapatkan ini:

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

Mungkin terlalu banyak tanda kurung, tetapi cukup jelas jika pemilihan argumen sebelumnya dibuat.

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

1
parser.print_help()
parser.exit()

The parser.exitMetode juga menerima status(returnCode), danmessage nilai (termasuk baris baru sendiri!).

contoh yang disarankan, :)

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

Contoh panggilan:

$ python3 ~ / helloworld.py; echo $?
penggunaan: helloworld.py [-h] [--example]

 Contoh file python berbasis argparser

argumen opsional:
  -h, --help tampilkan pesan bantuan ini dan keluar
  --contoh Contoh Argumen

Saya hanya tidak tahu apa yang salah, mungkin hilang - kondisi contoh?
128
$ python3 ~ / helloworld.py --example; echo $?
0

0

Tetapkan argumen posisional Anda dengan nargs, dan periksa apakah argumen posisional kosong.

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

Referensi Python nargs


0

Berikut adalah cara lain untuk melakukannya, jika Anda memerlukan sesuatu yang fleksibel di mana Anda ingin menampilkan bantuan jika params tertentu dilewati, tidak ada sama sekali atau lebih dari 1 argumen yang saling bertentangan:

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

Bersulang!


Saya pikir Anda akan lebih mudah menggunakan subparser atau mutually_exclusive_group
Tim Bray

0

Jika perintah Anda adalah sesuatu di mana pengguna perlu memilih beberapa tindakan, maka gunakan grup yang saling eksklusif dengan wajib = Benar .

Ini adalah semacam ekstensi untuk jawaban yang diberikan oleh 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')

Keluaran:

$ python3 a_test.py
penggunaan: a_test.py [-h] (--batch pay_id | --list | --all)
a_test.py: error: salah satu argumen --batch --list --semua diperlukan

Ini hanya memberikan bantuan dasar. Dan beberapa jawaban lain akan memberi Anda bantuan penuh. Tapi setidaknya pengguna Anda tahu mereka bisa melakukan -h


0

Ini tidak baik (juga, karena mencegat semua kesalahan), tetapi:

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

Berikut adalah definisi errorfungsi ArgumentParserkelas:

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

. Seperti yang Anda lihat, mengikuti tanda tangan, dibutuhkan dua argumen. Namun, fungsi di luar kelas tidak ada yang tahu tentang argumen pertama:, selfkarena, secara kasar, ini adalah parameter untuk kelas. (Aku tahu, bahwa Anda tahu ...) demikian, hanya lulus sendiri selfdan messagedi _error(...)tidak bisa (

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

    sys.exit(-1)

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

    parser.error = _error
    ...
...

akan menampilkan:

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

). Anda dapat meneruskan parser( self) dalam _errorfungsi, dengan memanggilnya:

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

    sys.exit(-1)

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

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

, tetapi Anda tidak ingin keluar dari program, sekarang. Kemudian kembalikan:

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

        sys.exit(-1)

    return wrapper
...

. Meskipun demikian, parsertidak tahu, bahwa itu telah dimodifikasi, sehingga ketika kesalahan terjadi, itu akan mengirimkan penyebabnya (omong-omong, terjemahannya yang dilokalkan). Nah, lalu sadap saja:

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

        sys.exit(-1)

    return wrapper
...

. Sekarang, ketika kesalahan terjadi dan parserakan mengirimkan penyebabnya, Anda akan mencegatnya, lihat ini, dan ... buang.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.