Python argparse: En az bir argüman gerekli hale getirin


97

Ben kullanıyorum argparsebir Python can program için -process, -uploadya da her ikisi:

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. argparseEn az bir parametrenin seçilmesini zorlamak için nasıl yapılandırabilirim ?

GÜNCELLEME:

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


9
-xevrensel olarak bir bayraktır ve isteğe bağlıdır. -Gerekirse kesin .

1
Eğer gelemedi processve içine kullanıcının değiştirmesine izin (herhangi bir seçenekleri belirlemek gerek kalmadan) varsayılan davranışı uploadeğer bu seçenek ayarlanır? Genellikle, seçenekler isteğe bağlı olmalıdır, dolayısıyla adı. Gerekli seçeneklerden kaçınılmalıdır (bu aynı zamanda argparse belgelerde de mevcuttur).
Tim Pietzcker

@AdamMatan Sorunuzu sormanızın üzerinden neredeyse üç yıl geçti ama ben içinde saklı olan zorlukları beğendim ve bu tür görevler için mevcut olan yeni çözümlerin avantajını kullandım.
Jan Vlcinsky

Yanıtlar:


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

1
Bunun için argparseyerleşik bir seçenek yoksa , muhtemelen tek yol budur .
Adam Matan

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

3
Genel bir çözüm için +1. Ayrıca vars(), dikkatlice adlandırılmış seçeneklerin ** ile bir kurucuya iletilmesi için de yararlı olan kullanımı gibi .
Lenna

Ben de onunla ne yapıyorum. Teşekkürler!
brentlance

1
Dang, bunu beğendim vars. Daha önce yaptım .__dict__ve kendimi aptal hissettim.
Theo Belaire

1
harika cevaplar. Hem "vars" hem de "herhangi" benim için
yeniydi

21

'Veya her ikisi' kısmı değilse (başlangıçta bunu kaçırdım) şö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, bunun yerine alt komutları kullanmak muhtemelen daha iyi bir fikir olacaktır .


4
Bence --processameliyathaneye izin vermek istiyor --upload, XOR'a değil. Bu, her iki seçeneğin aynı anda ayarlanmasını önler.
phihag

+1, çünkü alt komutlardan bahsetmiştin. Yine de - birisinin yorumlarda işaret ettiği gibi -xve --xxxtipik olarak isteğe bağlı parametrelerdir.
mac

21

Bunun pislik kadar eski olduğunu biliyorum, ancak bir seçenek gerektirip birden fazlasını (XOR) yasaklamanın yolu şöyle:

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  

4
Ne yazık ki, OP bir XOR istemiyor. Ya ya da ikisi birden, ama hiçbiri değil, bu yüzden son test durumunuz onların gereksinimlerini karşılamıyor.
kdopen

3
@kdopen: Katılımcı, bunun yararlı bulduğum orijinal sorunun bir varyasyonu olduğunu açıkladı: "bir seçeneği gerektirip birden fazlasını yasaklamanın yolu" Belki de Stack Exchange görgü kuralları bunun yerine yeni bir soru gerektirebilir . Ama bu cevabı burada sunmak bana yardımcı oldu ...
erik.weathers

2
Bu gönderi ilk soruyu cevaplamıyor
Marc

2
Bu "en az bir" sorusuna nasıl cevap veriyor?
xaxxon

2
Ne yazık ki, OP bir XOR istemiyor.
iamthedrake

8

Gereksinimlerin İncelenmesi

  • kullan argparse(bunu görmezden geleceğim)
  • bir veya iki eylemin çağrılmasına izin verin (en az bir tane gereklidir).
  • Pythonic ile deneyin ("POSIX" benzeri demeyi tercih ederim)

Komut satırında yaşarken bazı örtük gereksinimler de vardır:

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

docopt(File managelog.py) kullanarak örnek çözüm :

"""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ı deneyin:

$ 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 onu kullanın:

$ 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 da kısa varyantlar 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 şuna benzer:

$ 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" anahtarları için boole değerleri yerine sayaçların bulunduğunu unutmayın.

Görünüşe göre bu kelimelerin tekrarlanmasını önleyemeyiz:

$ 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 birçok yönü vardır:

  • iyi komut satırı tasarımı
  • uygun ayrıştırıcıyı seçme / kullanma

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

İle docoptokunabilirliği korumak ve esneklik yüksek derecede sunarken işler çok daha kısa gidin. Sözlükten ayrıştırılmış argümanlar almayı yönetirseniz ve bazı dönüşümleri (tamsayıya, dosyaları açmaya ..) elle (veya başka bir kitaplık ile schema) yaparsanız, docoptkomut satırı ayrıştırması için uygun bulabilirsiniz .


Docopt'u hiç duymadım, harika bir öneri!
Ton van den Heuvel

@TonvandenHeuvel İyi. Sadece onaylamak istiyorum, hala bunu 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

5

Bir python programının en az bir parametre ile çalışmasını istiyorsanız , seçenek önekine (varsayılan olarak - veya -) sahip olmayan bir bağımsız değişken ekleyin ve nargs=+(Minimum bir bağımsız değişken gereklidir). Bulduğum bu yöntemle ilgili sorun şu ki, eğer argümanı belirtmezseniz, argparse bir "çok az argüman" hatası üretecek ve yardım menüsünü yazdırmayacaktır. Bu işleve ihtiyacınız yoksa, kodda şu şekilde 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üşünüyorum Eğer opsiyon öneklerle bir tartışma eklediğinizde, nargs tüm argüman ayrıştırıcı ve sadece seçeneğini düzenlemektedir. (Ya demek olduğunu, bir varsa --optionişaretleyin nargs="+", sonra --option. Bayrağı beklediği en az bir argüman varsa optionile nargs="+", genel en az bir bağımsız değişken bekliyor.)


Bu choices=['process','upload']argümana ekleyebilirsiniz .
hpaulj

5

Http://bugs.python.org/issue11588 için , mutually_exclusive_groupbu gibi durumları ele almak için kavramı genelleştirmenin yollarını araştırıyorum .

Bu geliştirme argparse.pyile 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 üretir 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. Girdileri kabul eder.

Hata mesajının daha net olması gerekse de https://stackoverflow.com/a/6723066/901925'e benzer bir test çalıştırır :

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

Merak ediyorum:

  • parametreler kind='any', required=Trueyeterince açık mı (gruplardan herhangi birini kabul edin; en az biri gereklidir)?

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

  • bunun gibi bir grup kullanmak phihag'sbasit bir testten daha sezgisel mi?


add_usage_groupBu sayfada ile ilgili herhangi bir ifade bulamıyorum : docs.python.org/2/library/argparse.html ; lütfen bunun için belgelere bir bağlantı sağlar mısınız?
P. Myer Nore

@ P.MyerNore, bu cevabın başında bir bağlantı verdim. Bu üretime alınmadı.
hpaulj

5

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

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ıyla yalnızca bir bağımsız değişkenin seçilmesini istiyorsanız, grup için bağımsız değişken olarak yalnızca required = True kullanın

group = parser.add_mutually_exclusive_group(required=True)

2
Bu size nasıl "en az bir" kazandırır - "tam olarak bir" olmaz mı?
xaxxon

3
Ne yazık ki, OP bir XOR istemiyor. OP OR arıyor
iamthedrake

Bu OP'nin sorusuna cevap vermedi, ama benimkini yanıtladı, bu yüzden yine de teşekkürler (_ (ツ) _ / ¯
rosstex

2

Belki alt ayrıştırıcılar kullanın?

import argparse

parser = argparse.ArgumentParser(description='Log archiver arguments.')
subparsers = parser.add_subparsers(dest='subparser_name', help='sub-command help')
parser_process = subparsers.add_parser('process', help='Process logs')
parser_upload = subparsers.add_parser('upload', help='Upload logs')
args = parser.parse_args()

print("Subparser: ", args.subparser_name)

Şimdi --helpgösterir:

$ python /tmp/aaa.py --help
usage: aaa.py [-h] {process,upload} ...

Log archiver arguments.

positional arguments:
  {process,upload}  sub-command help
    process         Process logs
    upload          Upload logs

optional arguments:
  -h, --help        show this help message and exit
$ python /tmp/aaa.py
usage: aaa.py [-h] {process,upload} ...
aaa.py: error: too few arguments
$ python3 /tmp/aaa.py upload
Subparser:  upload

Bu alt ayrıştırıcılara ek seçenekler de ekleyebilirsiniz. Ayrıca dest='subparser_name'bunu kullanmak yerine, verilen alt komutta doğrudan çağrılacak işlevleri de bağlayabilirsiniz (bkz. Belgeler).


2

Bu, amaca ulaşır ve bu, --helpçoğu aklı başında programcının istediği şey olan argparse otomatik oluşturulmuş çıktıda da yeniden etkilenir (isteğe bağlı argümanlarla da çalışır):

parser.add_argument(
    'commands',
    nargs='+',                      # require at least 1
    choices=['process', 'upload'],  # restrict the choice
    help='commands to execute'
)

Bununla ilgili resmi belgeler: https://docs.python.org/3/library/argparse.html#choices


1

Bir eylemler listesine append_const komutunu kullanın ve ardından listenin doldurulup doldurulmadığını 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()
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.