Soru Python argparse: Gerekli en az bir argüman yap


Kullanıyorum argparse bir Python programı için -prepare, -upload ya da her ikisi de:

parser = argparse.ArgumentParser(description='Log archiver arguments.')
parser.add_argument('-process', action='store_true')
parser.add_argument('-upload',  action='store_true')
args = parser.parse_args()

Program en az bir parametre olmadan anlamsızdır. Nasıl yapılandırabilirim argparse Seçilen en az bir parametre zorlamak için?

GÜNCELLEŞTİRME:

Yorumların ardından: Bir programı en az bir seçenekle parametrelendirmenin Pythonic yolu nedir?


60
2017-07-17 09:24


Menşei


-x evrensel olarak bir bayrak ve isteğe bağlı. Kes - eğer gerekliyse.
Yapamazdın process varsayılan davranış (herhangi bir seçenek belirtmeksizin) ve kullanıcının bunu değiştirmesine izin ver upload Eğer o seçenek belirlendi mi? Genellikle seçenekler isteğe bağlı olmalı, bu nedenle ad. Gerekli seçenekler önlenmelidir (bu aynı zamanda argparse  docs). - Tim Pietzcker
@AdamMatan Sorunuzu sorduğunuzdan neredeyse üç yıl geçmesine rağmen, gizlenen bu sorunu sevdim ve bu tür görevler için yeni çözümlerin avantajını kullandım. - Jan Vlcinsky


Cevaplar:


if not (args.process or args.upload):
    parser.error('No action requested, add -process or -upload')

66
2017-07-17 09:51



Muhtemelen tek yol buysa argparse Bunun için yerleşik bir seçeneği yoktur. - Adam Matan
Aferin Bay Hagemeister;) - Sven Hager


args = vars(parser.parse_args())
if not any(args.values()):
    parser.error('No arguments provided.')

22
2018-03-02 13:59



Genelleştirilmiş bir çözüm için +1. Kullanımı gibi vars()Ayrıca, dikkatli bir şekilde adlandırılan seçenekleri ** ile bir kurucuya iletmek için de yararlıdır. - Lenna
Tam olarak bununla ne yapıyorum. Teşekkürler! - brentlance
Dang, bunu sevdim vars. az önce yaptım .__dict__ ve daha önce aptal hissettim. - Theo Belaire
harika cevaplar. "Vars" ve "any" her ikisi de benim için yeniydi :-) - Vivek Jha


Eğer 'ya da her ikisi' kısmı olmasaydı (başlangıçta bunu kaçırdım) böyle bir şey kullanabilirsiniz:

parser = argparse.ArgumentParser(description='Log archiver arguments.')
parser.add_argument('--process', action='store_const', const='process', dest='mode')
parser.add_argument('--upload',  action='store_const', const='upload', dest='mode')
args = parser.parse_args()
if not args.mode:
    parser.error("One of --process or --upload must be given")

Yine de, muhtemelen kullanmak daha iyi bir fikir olurdu alt komutları yerine.


17
2017-07-17 09:52



Sanırım izin vermek istiyor --process VEYA --uploadXOR değil. Bu, her iki seçeneğin de aynı anda ayarlanmasını engeller. - phihag
+1 alt komutlardan bahsettiniz çünkü. Yine de - birisi yorumlarda işaret ettiği gibi -x ve --xxx tipik olarak isteğe bağlı parametrelerdir. - mac
@phihag: haklısın, sorumu yanlış anlıyorum. - Jacek Konieczny


Bunun kir gibi eski olduğunu biliyorum, ama bir seçeneğe gereksinim duymanın ancak birden fazla (XOR) yasaklamanın yolu şudur:

parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('-process', action='store_true')
group.add_argument('-upload',  action='store_true')
args = parser.parse_args()
print args

Çıktı:

>opt.py  
usage: multiplot.py [-h] (-process | -upload)  
multiplot.py: error: one of the arguments -process -upload is required  

>opt.py -upload  
Namespace(process=False, upload=True)  

>opt.py -process  
Namespace(process=True, upload=False)  

>opt.py -upload -process  
usage: multiplot.py [-h] (-process | -upload)  
multiplot.py: error: argument -process: not allowed with argument -upload  

10
2017-10-09 22:30



Ne yazık ki, OP bir XOR istemiyor. Ya ikisinden biri ya da ikisi de değil, hiçbiri değil, son test durumunuz onların gereksinimlerini karşılamıyor. - kdopen
Kdopen: Yanıtlayıcı, bunun faydalı olduğunu bulduğum orijinal sorudaki bir varyasyon olduğunu açıklığa kavuşturdu: "bir seçeneğe gereksinim duyma ama birden fazla yasaklama" Belki de Stack Exchange'in görgü kuralları bunun yerine yeni bir soru çağrısında bulundu . Ama burada bu yanıtı vermek bana yardımcı oldu ... - erik.weathers
Bu cevabın yararlılığını ikinciye kaçıracağım, bu tam olarak aradığım şey oldu. - Mark Edington
Bu gönderi ilk soruya cevap vermiyor - Marc
En azından ben tam olarak aradığım şey buydu, çok teşekkürler. - T.Nel


Gereksinimler Gözden Geçirme

  • kullanım argparse (Bunu görmezden geleceğim)
  • Bir veya iki eylemin çağrılmasına izin ver (en az bir tane gerekli).
  • Pythonic ile denemek ("POSIX" gibi)

Komut satırında yaşarken bazı dolaylı gereksinimler vardır:

  • Kullanıcıya kullanımı kolay anlaşılır bir şekilde açıklar.
  • seçenekler isteğe bağlı olacaktır
  • bayrakların ve seçeneklerin belirtilmesine izin ver
  • Diğer parametrelerle birleştirmeye izin ver (dosya adı veya isimleri gibi).

Kullanarak örnek çözüm docopt (dosya managelog.py):

"""Manage logfiles
Usage:
    managelog.py [options] process -- <logfile>...
    managelog.py [options] upload -- <logfile>...
    managelog.py [options] process upload -- <logfile>...
    managelog.py -h

Options:
    -V, --verbose      Be verbose
    -U, --user <user>  Username
    -P, --pswd <pswd>  Password

Manage log file by processing and/or uploading it.
If upload requires authentication, you shall specify <user> and <password>
"""
if __name__ == "__main__":
    from docopt import docopt
    args = docopt(__doc__)
    print args

Çalıştırmayı dene:

$ python managelog.py
Usage:
    managelog.py [options] process -- <logfile>...
    managelog.py [options] upload -- <logfile>...
    managelog.py [options] process upload -- <logfile>...
    managelog.py -h

Yardımı göster:

$ python managelog.py -h
Manage logfiles
Usage:
    managelog.py [options] process -- <logfile>...
    managelog.py [options] upload -- <logfile>...
    managelog.py [options] process upload -- <logfile>...
    managelog.py -h

Options:
    -V, --verbose      Be verbose
    -U, --user <user>  Username
    -P, --pswd <pswd>  P    managelog.py [options] upload -- <logfile>...

Manage log file by processing and/or uploading it.
If upload requires authentication, you shall specify <user> and <password>

Ve kullan:

$ python managelog.py -V -U user -P secret upload -- alfa.log beta.log
{'--': True,
 '--pswd': 'secret',
 '--user': 'user',
 '--verbose': True,
 '-h': False,
 '<logfile>': ['alfa.log', 'beta.log'],
 'process': False,
 'upload': True}

Kısa alternatif short.py

Daha kısa bir değişken olabilir:

"""Manage logfiles
Usage:
    short.py [options] (process|upload)... -- <logfile>...
    short.py -h

Options:
    -V, --verbose      Be verbose
    -U, --user <user>  Username
    -P, --pswd <pswd>  Password

Manage log file by processing and/or uploading it.
If upload requires authentication, you shall specify <user> and <password>
"""
if __name__ == "__main__":
    from docopt import docopt
    args = docopt(__doc__)
    print args

Kullanım böyle görünüyor:

$ python short.py -V process upload  -- alfa.log beta.log
{'--': True,
 '--pswd': None,
 '--user': None,
 '--verbose': True,
 '-h': False,
 '<logfile>': ['alfa.log', 'beta.log'],
 'process': 1,
 'upload': 1}

"İşlem" ve "yükleme" tuşlarının boolean değerleri yerine, sayaçların olduğunu unutmayın.

Bu çıkıyor, bu kelimelerin çoğalmasını engelleyemiyoruz:

$ python short.py -V process process upload  -- alfa.log beta.log
{'--': True,
 '--pswd': None,
 '--user': None,
 '--verbose': True,
 '-h': False,
 '<logfile>': ['alfa.log', 'beta.log'],
 'process': 2,
 'upload': 1}

Sonuçlar

İyi bir komut satırı arayüzü tasarlamak bazen zor olabilir.

Komut satırı tabanlı programın birden çok yönü vardır:

  • Komut satırının iyi tasarımı
  • uygun ayrıştırıcıyı seçerek / kullanarak

argparse Çok şey sunar, ancak olası senaryoları kısıtlar ve çok karmaşık olabilir.

İle docopt okunabilirliği korurken ve yüksek derecede esneklik sunarken, işler çok daha kısa sürer. Bağımsız değişkenleri sözlükten ayrıştırmayı başarabilir ve dönüşümlerin bir kısmını (tamsayıya, dosyaları açmaya) manuel olarak (veya başka bir kütüphaneyle) yönetirseniz schema), bulabilirsin docopt Komut satırı ayrıştırma için iyi uyum.


8
2018-06-09 21:13



Docopt'u hiç duymadım, harika bir öneri! - Ton van den Heuvel
@TonvandenHeuvel İyi. Sadece onaylamak istiyorum, hala komut satırı arayüzleri için tercih ettiğim çözüm olarak kullanıyorum. - Jan Vlcinsky
En iyi cevap evar, detaylı örnekler için teşekkür ederim. - jnovack


En az bir parametre ile çalışacak bir python programına ihtiyacınız varsa, değil seçenek önekine sahip olmak (- veya - - varsayılan olarak) ve nargs=+ (En az bir argüman gereklidir). Bulduğum bu yöntemle ilgili sorun, argümanı belirtmezseniz, argparse bir "çok az argüman" hatası oluşturur ve yardım menüsünü yazdırmaz. Bu işlevselliğe ihtiyacınız yoksa, kodda bunu nasıl yapabilirsiniz:

import argparse

parser = argparse.ArgumentParser(description='Your program description')
parser.add_argument('command', nargs="+", help='describe what a command is')
args = parser.parse_args()

ben düşünmek seçenek önekleriyle bir argüman eklediğinizde, nargiler yalnızca seçeneği değil tüm argüman çözümleyicisini yönetir. (Demek istediğim, eğer varsa --option bayrakla nargs="+", sonra --option bayrak en az bir argüman bekliyor. Eğer varsa optionile nargs="+"Genel olarak en az bir argüman bekler.)


4
2017-07-20 00:12



Ekleyebilirsiniz choices=['process','upload'] bu argümana. - hpaulj


İçin http://bugs.python.org/issue11588 Genelleme yollarını araştırıyorum mutually_exclusive_group Böyle davaları işlemek için konsept.

Bu gelişme ile argparse.py, https://github.com/hpaulj/argparse_issues/blob/nested/argparse.py  Yazabiliyorum:

parser = argparse.ArgumentParser(prog='PROG', 
    description='Log archiver arguments.')
group = parser.add_usage_group(kind='any', required=True,
    title='possible actions (at least one is required)')
group.add_argument('-p', '--process', action='store_true')
group.add_argument('-u', '--upload',  action='store_true')
args = parser.parse_args()
print(args)

Aşağıdakileri üreten help:

usage: PROG [-h] (-p | -u)

Log archiver arguments.

optional arguments:
  -h, --help     show this help message and exit

possible actions (at least one is required):
  -p, --process
  -u, --upload

Bu '-u', '-up', '--proc - up' vb. Gibi girdileri kabul eder.

Benzer bir test çalıştırması biter https://stackoverflow.com/a/6723066/901925Hata mesajının daha açık olması gerekiyorsa:

usage: PROG [-h] (-p | -u)
PROG: error: some of the arguments process upload is required

Merak ediyorum:

  • parametreler kind='any', required=True yeterince açık (grubun herhangi birini kabul et; en az bir tane gereklidir)?

  • kullanım (-p | -u) açık? Mutually_exclusive_group gerekli bir şey aynı şeyi üretir. Alternatif bir gösterim var mı?

  • bu gibi bir grup daha sezgisel kullanıyor phihag's basit test?


3
2018-06-10 20:42



Hiç bahsetmiyorum add_usage_group bu sayfada: docs.python.org/2/library/argparse.html; bunun için belgelere bir bağlantı sağlar mısınız lütfen? - P. Myer Nore
@ P.MyerNore, bu cevabın başında bir bağlantı sağladım. Bu, üretime alınmadı. - hpaulj


Bir eylemler listesine append_const öğesini kullanın ve listenin doldurulduğunu kontrol edin:

parser.add_argument('-process', dest=actions, const="process", action='append_const')
parser.add_argument('-upload',  dest=actions, const="upload", action='append_const')

args = parser.parse_args()

if(args.actions == None):
    parser.error('Error: No actions requested')

Yöntemleri doğrudan sabitler içinde bile belirtebilirsiniz.

def upload:
    ...

parser.add_argument('-upload',  dest=actions, const=upload, action='append_const')
args = parser.parse_args()

if(args.actions == None):
    parser.error('Error: No actions requested')

else:
    for action in args.actions:
        action()

1
2018-06-10 18:35





Bunu yapmanın en iyi yolu, python dahili modülünü kullanmaktır. add_mutually_exclusive_group.

parser = argparse.ArgumentParser(description='Log archiver arguments.')
group = parser.add_mutually_exclusive_group()
group.add_argument('-process', action='store_true')
group.add_argument('-upload',  action='store_true')
args = parser.parse_args()

Komut satırı tarafından seçilecek tek bir argümanın kullanılması gerekiyorsa sadece gerekli = = Grup için bir argüman olarak doğru

group = parser.add_mutually_exclusive_group(required=True)

1
2018-06-10 04:56