Argparse: 'x' mevcutsa gerekli bağımsız değişken 'y'


118

Aşağıdaki gibi bir şartım var:

./xyifier --prox --lport lport --rport rport

prox argümanı için, var olup olmadığını kontrol etmek için action = 'store_true' kullanıyorum. Herhangi bir argümana ihtiyacım yok. Ancak, --prox ayarlanmışsa , rport ve lport'a da ihtiyacım var . Özel koşullu kodlama yazmadan bunu argparse ile yapmanın kolay bir yolu var mı?

Daha Fazla Kod:

non_int.add_argument('--prox', action='store_true', help='Flag to turn on proxy')
non_int.add_argument('--lport', type=int, help='Listen Port.')
non_int.add_argument('--rport', type=int, help='Proxy port.')

Tıkalıyor, ama kütüphanem joffrey'den bahsetmek istedim . Bu sorunun istediğini, örneğin, her şeyi kendiniz doğrulamanıza (kabul edilen cevapta olduğu gibi) veya bir kaçamak hackine güvenmeden (ikinci en yüksek oyu alan yanıtta olduğu gibi) yapmanıza izin verir.
MI Wright

Buraya
gelenler için

Yanıtlar:


121

Hayır, argparse'de karşılıklı olarak kapsamlı seçenekler oluşturma seçeneği yoktur.

Bununla başa çıkmanın en basit yolu şudur:

if args.prox and (args.lport is None or args.rport is None):
    parser.error("--prox requires --lport and --rport.")

2

20
parser.errorYöntem için teşekkürler , aradığım şey buydu!
MarSoft

7
'veya' kullanmamalı mısınız? sonuçta her iki değiştirgeye de ihtiyacınız var if args.prox and (args.lport is None or args.rport is None):
yossiz74

1
Bunun yerine args.lport is Nonekullanabilirsiniz not args.lport. Sanırım biraz daha pitonik.
CGFoX

7
Yani ayar durdurmak olacağını --lportya --rportkadar 0programa geçerli bir girdi olabilir, hangi.
2018

53

Koşullu olarak gerekli tartışmalar yapmaktan bahsediyorsunuz. @Borntyping'in dediği gibi, hatayı kontrol edip yapabileceğinizi parser.error()veya --proxyeni bir argüman eklediğiniz zamanla ilgili bir şart uygulayabilirsiniz .

Örneğiniz için basit bir çözüm şunlar olabilir:

non_int.add_argument('--prox', action='store_true', help='Flag to turn on proxy')
non_int.add_argument('--lport', required='--prox' in sys.argv, type=int)
non_int.add_argument('--rport', required='--prox' in sys.argv, type=int)

Bu yol , kullanıcının kullanıldığı şekilde requiredya Trueda buna Falsebağlı olarak alır --prox. Bu aynı zamanda bunu garanti eder -lportve -rportbirbirleri arasında bağımsız bir davranışa sahiptir.


8
ArgumentParserBunun dışındaki bir listedeki argümanları ayrıştırmak için kullanılabileceğini unutmayın sys.argv, bu durumda bu başarısız olur.
BallpointBen

Ayrıca --prox=<value>sözdizimi kullanılırsa bu başarısız olacaktır .
fnkr

11

parser.parse_known_args()Yöntemi kullanmaya ve varsa gerekli args olarak --lportve --rportargs eklemeye ne dersiniz --prox?

# just add --prox arg now
non_int = argparse.ArgumentParser(description="stackoverflow question", 
                                  usage="%(prog)s [-h] [--prox --lport port --rport port]")
non_int.add_argument('--prox', action='store_true', 
                     help='Flag to turn on proxy, requires additional args lport and rport')
opts, rem_args = non_int.parse_known_args()
if opts.prox:
    non_int.add_argument('--lport', required=True, type=int, help='Listen Port.')
    non_int.add_argument('--rport', required=True, type=int, help='Proxy port.')
    # use options and namespace from first parsing
    non_int.parse_args(rem_args, namespace = opts)

Ayrıca opts, kalan bağımsız değişkenleri ikinci kez ayrıştırırken, ilk çözümlemeden sonra oluşturulan ad alanını sağlayabileceğinizi unutmayın . Bu şekilde, sonunda, tüm ayrıştırma tamamlandıktan sonra, tüm seçenekleri içeren tek bir ad alanına sahip olursunuz.

Dezavantajları:

  • Eğer --proxmevcut değilse diğer iki bağımlı seçenekler ad alanında bile bulunmaz. Kullanım durumunuza bağlı olmasına rağmen, --proxmevcut değilse , diğer seçeneklere ne olduğu ilgisizdir.
  • Ayrıştırıcı tam yapıyı bilmediği için kullanım mesajının değiştirilmesi gerekiyor
  • --lportve --rportyardım mesajında ​​görünme

5

Ayarlanmadığı lportzaman kullanıyor musunuz? proxDeğilse, neden yapmayalım lportve rporttartışmasın prox? Örneğin

parser.add_argument('--prox', nargs=2, type=int, help='Prox: listen and proxy ports')

Bu, kullanıcılarınızı yazarken kurtarır. Test etmek if args.prox is not None:kadar kolaydır if args.prox:.


1
Örneğin bütünlüğü için, anlatılarınız> 1 olduğunda, ayrıştırılmış argümanda vb. Olağan şekilde ele alabileceğiniz bir liste alırsınız. Örneğin, a,b = args.prox, a = args.prox[0]vb
Dannid

1

Kabul edilen cevap benim için harika çalıştı! Tüm kodlar test edilmeden kırıldığından, kabul edilen cevabı burada nasıl test ettim. parser.error()bir argparse.ArgumentErrorhata oluşturmaz, bunun yerine işlemden çıkar. İçin test etmelisiniz SystemExit.

pytest ile

import pytest
from . import parse_arguments  # code that rasises parse.error()


def test_args_parsed_raises_error():
    with pytest.raises(SystemExit):
        parse_arguments(["argument that raises error"])

birim testleri ile

from unittest import TestCase
from . import parse_arguments  # code that rasises parse.error()

class TestArgs(TestCase):

    def test_args_parsed_raises_error():
        with self.assertRaises(SystemExit) as cm:
            parse_arguments(["argument that raises error"])

esin kaynağı: argparse'ı test etmek için unittest kullanma - çıkış hataları

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.