2017-02-19 3 views
4

Ich habe ein Upstream-System, das mein Programm mit verschiedenen Arg-Namen aufruft. Beispiel:Verwenden von Variablen Arg-Namen mit Argarse

foo --xyz1 10 --xyz2 25 --xyz3 31 

Ich mag das Ergebnis des argparsing ein xyz = [10, 25, 31] zu sein.

Die Namen meiner Argumente haben ein gemeinsames Präfix, müssen sich aber leider zumindest durch ein anderes numerisches Suffix unterscheiden, das auch die Reihenfolge angibt. Ich habe auch keine feste Anzahl von Argumenten.

Gibt es eine Möglichkeit, dies mit argparse zu modellieren? Entweder mit dem, was durch eine Kombination von integrierten Funktionen verfügbar ist, oder durch Überschreiben/Übernehmen einer benutzerdefinierten Parserbehandlung.

+1

Mit 'action = 'append' können Sie die' --xyz'-Flagge wiederverwenden und Werte in der Reihenfolge sammeln, in der sie angegeben wurden, z. '--xyz 10 --xyz 25 --xyz 31' – hpaulj

+0

Ich brauche verschiedene arg Namen; Das ist eine Anforderung von einem anderen Teil des Systems. Also muss ich --xyz1, --xyz2, --xyz3 und so weiter behandeln. –

+0

Wie viele dieser Variablennamenschlüssel? 3 so oder 100? – hpaulj

Antwort

3

ich ein bisschen Vorverarbeitung vorschlagen würde, um dies zu erreichen:

Code:

def get_xyz_cmd_line(xyz_cmd_line): 
    # build a generator to iterate the cmd_line 
    cmd_line_gen = iter(xyz_cmd_line) 

    # we will separate the xyz's from everything else 
    xyz = [] 
    remaining_cmd_line = [] 

    # go through the command line and extract the xyz's 
    for opt in cmd_line_gen: 
     if opt.startswith('--xyz'): 
      # grab the opt and the arg for it 
      xyz.append((opt, cmd_line_gen.next())) 
     else: 
      remaining_cmd_line.append(opt) 

    # sort the xyz's and return all of them as -xyz # -xyz # ... 
    return list(it.chain(*[ 
     ('--xyz', x[1]) for x in sorted(xyz)])) + remaining_cmd_line 

Zum Test:

import argparse 
import itertools as it 

parser = argparse.ArgumentParser(description='Get my Option') 
parser.add_argument('--an_opt', metavar='N', type=int, 
        help='An option') 
parser.add_argument('--xyz', metavar='N', type=int, action='append', 
        help='An option') 

cmd_line = "--an_opt 1 --xyz1 10 --xyz3 31 --xyz2 25 ".split() 
args = parser.parse_args(get_xyz_cmd_line(cmd_line)) 
print(args) 

Ausgang:

Namespace(an_opt=1, xyz=[10, 25, 31]) 

zu benutzen:

Nominell anstatt eines festen cmd_line wie im obigen Beispiel wäre dies mit so etwas wie genannt werden:

args = parser.parse_args(get_xyz_cmd_line(sys.argv[1:])) 

UPDATE: Wenn Sie benötigen --xyz = 31 (dh = Separator):

Dann müssen Sie ändern:

# grab the opt and the arg for it 
xyz.append((opt, cmd_line_gen.next())) 

An:

if '=' in opt: 
    xyz.append(tuple(opt.split('=', 1))) 
else: 
    # grab the opt and the arg for it 
    xyz.append((opt, cmd_line_gen.next())) 
+0

Ich mag Ihre Lösung im Verhältnis zu mir, weil meine (unten veröffentlicht auf ein internes Implementierungsdetail angewiesen). Ich weiß jedoch noch nicht, ob ich es benutzen kann, da ich eine Komponente habe, die die Argumentdefinitionen hinzufügt, und den Benutzer, der parse_args aufruft, und ich möchte, dass der Benutzer nicht weiß, was zu tun ist oder welche Argumente er ausführen soll Vorverarbeitung für. Mit meinem abgeleiteten Argparser und der benutzerdefinierten Aktion wird dieses Szenario ohne Benutzereingriff durchgeführt. –

1

Hier ist, was ich als Referenz tat (quick and dirty-Version), obwohl ich wie auch Stephen Rauchs Antwort (also werde ich das als Antwort markieren - esp. da ich für meine Lösung interne Implementierung Detail verwendet wird):

class CustomArgumentsParser(argparse.ArgumentParser): 

    def _parse_optional(self, arg_string): 
    suffix_index = arg_string.find(':') 
    if suffix_index < 0: 
     return super(CustomArgumentParser, self)._parse_optional(arg_string) 

    original_arg_string = arg_string 
    suffix = arg_string[suffix_index + 1:] 
    arg_string = arg_string[0:suffix_index] 

    option_tuple = super(CustomArgumentParser, self)._parse_optional(arg_string) 
    if not option_tuple: 
     return option_tuple 

    action, option_string, explicit_arg = option_tuple 
    if isinstance(action, BuildListAction): 
     return action, suffix, explicit_arg 
    else: 
     self.exit(-1, message='Unknown argument %s' % original_arg_string) 


class BuildListAction(argparse.Action): 
    def __init__(self, 
       option_strings, 
       dest, 
       nargs=None, 
       const=None, 
       default=None, 
       type=None, 
       choices=None, 
       required=False, 
       help=None, 
       metavar=None): 
    super(BuildListAction, self).__init__(
     option_strings=option_strings, 
     dest=dest, 
     nargs=nargs, 
     const=const, 
     default=default, 
     type=type, 
     choices=choices, 
     required=required, 
     help=help, 
     metavar=metavar) 

    def __call__(self, parser, namespace, values, option_string=None): 
    index = int(option_string) - 1 

    list = getattr(namespace, self.dest) 
    if list is None: 
     list = [] 
     setattr(namespace, self.dest, list) 

    if index >= len(list): 
     list.extend([self.default] * (index + 1 - len(list))) 
    list[index] = values 

Verbrauch:

argparser = CustomArgumentsParser() 
argparser.add_argument('--xyz', type=int, action=BuildListAction) 

Hinweis - Diese Argumente der Form --xyz unterstützt: 1, --xyz: 2, .. Das ist etwas anders als die ursprüngliche Frage.