Bagaimana cara mengimplementasikan opsi --verbose atau -v ke dalam skrip?

95

Saya tahu --verboseatau -vdari beberapa alat dan saya ingin menerapkan ini ke dalam beberapa skrip dan alat saya sendiri.

Saya berpikir untuk menempatkan:

if verbose:
    print ...

melalui kode sumber saya, sehingga jika pengguna melewati -vopsi, variabel verboseakan disetel ke Truedan teks akan dicetak.

Apakah ini pendekatan yang tepat atau ada cara yang lebih umum?

Penambahan: Saya tidak meminta cara untuk mengimplementasikan parsing argumen. Bahwa saya tahu bagaimana hal itu dilakukan. Saya hanya tertarik secara khusus pada opsi verbose.

Aufwind
sumber
9
Mengapa tidak menggunakan modul logging dan mengatur INFO level log secara default, dan DEBUG ketika --verbose dilewatkan? Sebaiknya tidak menerapkan ulang apa pun yang sudah tersedia dalam bahasa ...
Tim
3
@ Tim, saya setuju, tetapi modul logging cukup menyakitkan.
mlissner

Jawaban:

109

Saran saya adalah menggunakan suatu fungsi. Tetapi alih-alih memasukkan iffungsi, yang mungkin Anda tergoda untuk melakukannya, lakukan seperti ini:

if verbose:
    def verboseprint(*args):
        # Print each argument separately so caller doesn't need to
        # stuff everything to be printed into a single string
        for arg in args:
           print arg,
        print
else:   
    verboseprint = lambda *a: None      # do-nothing function

(Ya, Anda dapat menentukan fungsi dalam ifpernyataan, dan itu hanya akan ditentukan jika kondisinya benar!)

Jika Anda menggunakan Python 3, di mana printsudah menjadi fungsi (atau jika Anda ingin menggunakan printsebagai fungsi di 2.x menggunakan from __future__ import print_function) itu bahkan lebih sederhana:

verboseprint = print if verbose else lambda *a, **k: None

Dengan cara ini, fungsi didefinisikan sebagai tidak melakukan apa-apa jika mode verbose tidak aktif (menggunakan lambda), alih-alih terus-menerus menguji verboseflag.

Jika pengguna dapat mengubah mode verbositas selama menjalankan program Anda, ini akan menjadi pendekatan yang salah (Anda memerlukan iffungsi in), tetapi karena Anda menyetelnya dengan panji baris perintah, Anda hanya perlu buat keputusan sekali.

Anda kemudian menggunakan mis verboseprint("look at all my verbosity!", object(), 3)kapan pun Anda ingin mencetak pesan "verbose".

kindall
sumber
1
Lebih baik lagi, lakukan sebagai printfungsi: Terima banyak argumen. Ini dapat diimplementasikan seperti print(*args)di 3.x dan for arg in args: print arg,di 2.x. Keuntungan utama adalah memungkinkan pencampuran string dan hal-hal dari jenis lain dalam satu pesan tanpa strpanggilan / pemformatan dan penggabungan eksplisit .
Untuk apa koma digunakan di akhir print arg,baris?
SamK
Itu mudah ditentukan untuk diri sendiri secara eksperimental atau dengan memeriksa dokumentasi, tetapi itu menekan jeda baris yang biasanya dicetak.
kindall
5
Fungsi cetak Python 3 juga mengambil argumen kata kunci opsional, jadi untuk sepenuhnya mereproduksi fungsionalitas cetak:def verboseprint(*args, **kwargs): print(*args, **kwargs)
lstyls
63

Gunakan loggingmodul:

import logging as log
…
args = p.parse_args()
if args.verbose:
    log.basicConfig(format="%(levelname)s: %(message)s", level=log.DEBUG)
    log.info("Verbose output.")
else:
    log.basicConfig(format="%(levelname)s: %(message)s")

log.info("This should be verbose.")
log.warning("This is a warning.")
log.error("This is an error.")

Semua ini otomatis masuk ke stderr:

% python myprogram.py
WARNING: This is a warning.
ERROR: This is an error.

% python myprogram.py -v
INFO: Verbose output.
INFO: This should be verbose.
WARNING: This is a warning.
ERROR: This is an error.

Untuk info lebih lanjut, lihat Python Docs dan tutorialnya .

Profpatsch
sumber
8
Sesuai dengan Python Docs di sini , logging tidak boleh digunakan dalam kasus di mana Anda hanya perlu mencetak output dalam eksekusi normal program. Sepertinya itulah yang diinginkan OP.
SANDeveloper
1
Ini tampaknya bagus untuk masalah dasar tetapi banyak perintah * nix juga mendukung berbagai tingkat verbositas (-v -v -v, dll), yang mungkin menjadi berantakan dengan cara ini.
TextGeek
12

Membangun dan menyederhanakan jawaban @ kindall, inilah yang biasanya saya gunakan:

v_print = None
def main()
    parser = argparse.ArgumentParser()
    parser.add_argument('-v', '--verbosity', action="count", 
                        help="increase output verbosity (e.g., -vv is more than -v)")

    args = parser.parse_args()

    if args.verbosity:
        def _v_print(*verb_args):
            if verb_args[0] > (3 - args.verbosity):
                print verb_args[1]  
    else:
        _v_print = lambda *a: None  # do-nothing function

    global v_print
    v_print = _v_print

if __name__ == '__main__':
    main()

Ini kemudian memberikan penggunaan berikut di seluruh skrip Anda:

v_print(1, "INFO message")
v_print(2, "WARN message")
v_print(3, "ERROR message")

Dan skrip Anda bisa disebut seperti ini:

% python verbose-tester.py -v
ERROR message

% python verbose=tester.py -vv
WARN message
ERROR message

% python verbose-tester.py -vvv
INFO message
WARN message
ERROR message

Beberapa catatan:

  1. Argumen pertama Anda adalah tingkat kesalahan Anda, dan yang kedua adalah pesan Anda. Ini memiliki angka ajaib 3yang menetapkan batas atas untuk logging Anda, tetapi saya menerimanya sebagai kompromi untuk kesederhanaan.
  2. Jika Anda ingin v_printbekerja di seluruh program Anda, Anda harus melakukan sampah dengan global. Ini tidak menyenangkan, tapi saya menantang seseorang untuk menemukan cara yang lebih baik.
mlissner
sumber
1
Mengapa Anda tidak menggunakan modul logging untuk INFO dan WARN? Itu mengimpornya saat -vdigunakan. Dalam solusi Anda saat ini semuanya dibuang ke stdout bukan stderr. Dan: Anda biasanya ingin menyampaikan setiap kesalahan kepada pengguna, bukan?
Profpatsch
2
Ya, itu poin yang adil. Logging memiliki beberapa overhead kognitif yang saya coba hindari, tetapi mungkin itu adalah hal yang "benar" untuk dilakukan. Itu hanya membuatku kesal di masa lalu ...
mlissner
9

Apa yang saya lakukan di skrip saya adalah memeriksa saat runtime jika opsi 'verbose' disetel, lalu setel tingkat logging saya ke debug. Jika tidak disetel, saya setel ke info. Dengan cara ini Anda tidak memiliki pemeriksaan 'if verbose' di seluruh kode Anda.

jonesy
sumber
2

Mungkin lebih bersih jika Anda memiliki fungsi, katakanlah dipanggil vprint, yang memeriksa tanda verbose untuk Anda. Kemudian Anda cukup memanggil vprintfungsi Anda sendiri di mana pun Anda ingin verbositas opsional.

Lee-Man
sumber
2

Saya mencuri kode logging dari virtualenv untuk proyek saya. Lihat di main()dari virtualenv.pyuntuk melihat bagaimana hal itu diinisialisasi. Kode ini ditaburi dengan logger.notify(), logger.info(), logger.warn(), dan sejenisnya. Metode sebenarnya keluaran memancarkan ditentukan oleh apakah virtualenv itu dipanggil dengan yang -v, -vv, -vvv, atau -q.

George V. Reilly
sumber
2

Solusi @ kindall tidak berfungsi dengan Python versi 3.5 saya. @styles dengan benar menyatakan dalam komentarnya bahwa alasannya adalah argumen kata kunci opsional tambahan . Oleh karena itu versi saya yang sedikit disempurnakan untuk Python 3 terlihat seperti ini:

if VERBOSE:
    def verboseprint(*args, **kwargs):
        print(*args, **kwargs)
else:
    verboseprint = lambda *a, **k: None # do-nothing function
stefanct
sumber
1

Mungkin ada variabel global, kemungkinan diatur dengan argparsefrom sys.argv, yang menunjukkan apakah program harus bertele-tele atau tidak. Kemudian dekorator dapat ditulis sedemikian rupa sehingga jika verbositas aktif, maka input standar akan dialihkan ke perangkat null selama fungsinya berjalan:

import os
from contextlib import redirect_stdout
verbose = False

def louder(f):
    def loud_f(*args, **kwargs):
        if not verbose:
            with open(os.devnull, 'w') as void:
                with redirect_stdout(void):
                    return f(*args, **kwargs)
        return f(*args, **kwargs)
    return loud_f

@louder
def foo(s):
    print(s*3)

foo("bar")

Jawaban ini terinspirasi oleh kode ini ; sebenarnya, saya hanya akan menggunakannya sebagai modul dalam program saya, tetapi saya mendapat kesalahan yang tidak dapat saya pahami, jadi saya menyesuaikan sebagian darinya.

Kelemahan dari solusi ini adalah bahwa verbositas adalah biner, tidak seperti with logging, yang memungkinkan penyetelan yang lebih baik tentang seberapa panjang program tersebut. Selain itu, semua print panggilan dialihkan, yang mungkin tidak diinginkan.

Daniel Diniz
sumber
0

Yang saya butuhkan adalah fungsi yang mencetak objek (obj), tetapi hanya jika variabel global verbose benar, jika tidak, ia tidak melakukan apa-apa.

Saya ingin dapat mengubah parameter global "verbose" kapan saja. Kesederhanaan dan keterbacaan bagi saya adalah yang terpenting. Jadi saya akan melanjutkan seperti yang ditunjukkan baris berikut:

ak@HP2000:~$ python3
Python 3.4.3 (default, Oct 14 2015, 20:28:29) 
[GCC 4.8.4] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> verbose = True
>>> def vprint(obj):
...     if verbose:
...         print(obj)
...     return
... 
>>> vprint('Norm and I')
Norm and I
>>> verbose = False
>>> vprint('I and Norm')
>>> 

Variabel global "verbose" juga dapat disetel dari daftar parameter.

pengguna377367
sumber