Wie analysiere ich mehrere verschachtelte Unterbefehle mit Python Argparse?


81

Ich implementiere ein Befehlszeilenprogramm mit folgender Schnittstelle:

cmd [GLOBAL_OPTIONS] {command [COMMAND_OPTS]} [{command [COMMAND_OPTS]} ...]

Ich habe die Argparse-Dokumentation durchgesehen . Ich kann GLOBAL_OPTIONSals optionales Argument mit add_argumentin implementieren argparse. Und die {command [COMMAND_OPTS]}Verwendung von Unterbefehlen .

Aus der Dokumentation geht hervor, dass ich nur einen Unterbefehl haben kann. Aber wie Sie sehen, muss ich einen oder mehrere Unterbefehle implementieren. Was ist der beste Weg, um solche Befehlszeilenargumente mit zu analysieren argparse?


1
Ich denke nicht, dass dies das ist, wofür Unterbefehle gedacht sind. Aus der Dokumentation geht hervor, dass dies im Wesentlichen dazu dient, separate Unterprogramme zu steuern . Haben Sie sich mit Argumentationsgruppen befasst ?
Chris

distutils ./setup.pyhat auch diese Stil-CLI-Schnittstelle, wäre interessant, in ihren Quellcode zu schauen.
Ciro Santilli 4 冠状 病. 事件 4

Antworten:


26

Ich habe mir die gleiche Frage gestellt, und anscheinend habe ich eine bessere Antwort.

Die Lösung ist, dass wir Subparser nicht einfach mit einem anderen Subparser verschachteln, sondern Subparser mit einem Parser nach einem anderen Subparser hinzufügen können.

Code sagt Ihnen, wie:

parent_parser = argparse.ArgumentParser(add_help=False)                                                                                                  
parent_parser.add_argument('--user', '-u',                                                                                                               
                    default=getpass.getuser(),                                                                                                           
                    help='username')                                                                                                                     
parent_parser.add_argument('--debug', default=False, required=False,                                                                                     
                           action='store_true', dest="debug", help='debug flag')                                                                         
main_parser = argparse.ArgumentParser()                                                                                                                  
service_subparsers = main_parser.add_subparsers(title="service",                                                                                         
                    dest="service_command")                                                                                                              
service_parser = service_subparsers.add_parser("first", help="first",                                                                                    
                    parents=[parent_parser])                                                                                                             
action_subparser = service_parser.add_subparsers(title="action",                                                                                         
                    dest="action_command")                                                                                                               
action_parser = action_subparser.add_parser("second", help="second",                                                                                     
                    parents=[parent_parser])                                                                                                             

args = main_parser.parse_args()   

Ja, argparseerlaubt verschachtelte Unterparser. Aber ich habe sie nur an einer anderen Stelle verwendet gesehen - in einem Testfall für ein Python-Problem, bugs.python.org/issue14365
hpaulj

9
Dies setzt voraus, dass die Befehle eine verschachtelte Struktur haben. Aber die Frage ist nach "parallelen" Befehlen
Augurar

25

@mgilson hat eine schöne Antwort auf diese Frage. Aber das Problem beim Teilen von sys.argv selbst ist, dass ich all die nette Hilfemeldung verliere, die Argparse für den Benutzer generiert. Also habe ich folgendes gemacht:

import argparse

## This function takes the 'extra' attribute from global namespace and re-parses it to create separate namespaces for all other chained commands.
def parse_extra (parser, namespace):
  namespaces = []
  extra = namespace.extra
  while extra:
    n = parser.parse_args(extra)
    extra = n.extra
    namespaces.append(n)

  return namespaces

argparser=argparse.ArgumentParser()
subparsers = argparser.add_subparsers(help='sub-command help', dest='subparser_name')

parser_a = subparsers.add_parser('command_a', help = "command_a help")
## Setup options for parser_a

## Add nargs="*" for zero or more other commands
argparser.add_argument('extra', nargs = "*", help = 'Other commands')

## Do similar stuff for other sub-parsers

Nach dem ersten Parsen werden nun alle verketteten Befehle in gespeichert extra. Ich repariere es, solange es nicht leer ist, um alle verketteten Befehle abzurufen und separate Namespaces für sie zu erstellen. Und ich bekomme eine schönere Verwendungszeichenfolge, die Argparse generiert.


2
@Flavius, nachdem ich namespacedurch einen Anruf vom Parser komme, rufe namespace = argparser.parse_args()ich parse_extramit parserund an namespace. extra_namespaces = parse_extra( argparser, namespace )
Vikas

Ich glaube, ich verstehe die Logik, aber was ist parserin dem Code, den Sie haben. Ich sehe nur, dass es verwendet wird, um das extraArgument hinzuzufügen . Dann haben Sie es im obigen Kommentar noch einmal erwähnt. Ist es sein soll argparser?
Jmlopez

@jmlopez ja es sollte sein argparser. Wird es bearbeiten.
Vikas

1
Beachten Sie, dass diese Lösung für unterbefehlsspezifische optionale Argumente fehlschlägt. Eine alternative Lösung finden Sie in meiner Lösung unten ( stackoverflow.com/a/49977713/428542 ).
MacFreek

1
Hier ist ein Beispiel dafür, wie dies fehlschlägt. Fügen Sie die folgenden 3 Zeilen hinzu : parser_b = subparsers.add_parser('command_b', help='command_b help'); parser_b.add_argument('--baz', choices='XYZ', help='baz help');; options = argparser.parse_args(['--foo', 'command_a', 'command_b', '--baz', 'Z']);; Dies schlägt mit einem Fehler fehl PROG: error: unrecognized arguments: --baz Z. Der Grund ist, dass während des Parsens von command_adie optionalen Argumente von command_bbereits analysiert werden (und für den Subparser von unbekannt sind command_a).
MacFreek

14

parse_known_argsGibt einen Namespace und eine Liste unbekannter Zeichenfolgen zurück. Dies ähnelt der extrain der aktivierten Antwort.

import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--foo')
sub = parser.add_subparsers()
for i in range(1,4):
    sp = sub.add_parser('cmd%i'%i)
    sp.add_argument('--foo%i'%i) # optionals have to be distinct

rest = '--foo 0 cmd2 --foo2 2 cmd3 --foo3 3 cmd1 --foo1 1'.split() # or sys.argv
args = argparse.Namespace()
while rest:
    args,rest =  parser.parse_known_args(rest,namespace=args)
    print args, rest

produziert:

Namespace(foo='0', foo2='2') ['cmd3', '--foo3', '3', 'cmd1', '--foo1', '1']
Namespace(foo='0', foo2='2', foo3='3') ['cmd1', '--foo1', '1']
Namespace(foo='0', foo1='1', foo2='2', foo3='3') []

Eine alternative Schleife würde jedem Subparser einen eigenen Namespace geben. Dies ermöglicht eine Überlappung der Positionsnamen.

argslist = []
while rest:
    args,rest =  parser.parse_known_args(rest)
    argslist.append(args)

Funktioniert gut. Es gibt jedoch einen Fehler: Wenn irgendwo eine falsch geschriebene Option vorhanden ist (z. B. rest = '--foo 0 cmd2 --foo2 2 --bar cmd3 --foo3 3 cmd1 --foo1 1'.split()), endet argparse mit, error: too few argumentsanstatt auf die ungültige Option hinzuweisen. Dies liegt daran, dass die schlechte Option so lange beibehalten wird, restbis keine Befehlsargumente mehr vorhanden sind.
Adrian W

Der Kommentar # or sys.argvsollte sein # or sys.argv[1:].
Adrian W

5

Sie können die Befehlszeile jederzeit selbst aufteilen ( sys.argvauf Ihre Befehlsnamen aufteilen ) und dann nur den Teil übergeben, der dem jeweiligen Befehl entspricht parse_args- Sie können sogar denselben verwendenNamespace mit dem Schlüsselwort namespace verwenden, wenn Sie möchten.

Das Gruppieren der Befehlszeile ist einfach mit itertools.groupby:

import sys
import itertools
import argparse    

mycommands=['cmd1','cmd2','cmd3']

def groupargs(arg,currentarg=[None]):
    if(arg in mycommands):currentarg[0]=arg
    return currentarg[0]

commandlines=[list(args) for cmd,args in intertools.groupby(sys.argv,groupargs)]

#setup parser here...
parser=argparse.ArgumentParser()
#...

namespace=argparse.Namespace()
for cmdline in commandlines:
    parser.parse_args(cmdline,namespace=namespace)

#Now do something with namespace...

ungetestet


1
Danke mgilson. Dies ist eine schöne Lösung für meine Frage, aber am Ende habe ich es ein wenig anders gemacht. Ich habe eine weitere Antwort hinzugefügt .
Vikas

1
Gute Verwendung von itertools.groupby()! So habe ich dasselbe gemacht, bevor ich davon wusste groupby().
Kzyapkov

5

Um die Antwort von @mgilson zu verbessern, habe ich eine kleine Analysemethode geschrieben, die argv in Teile aufteilt und Werte von Argumenten von Befehlen in die Hierarchie von Namespaces einfügt:

import sys
import argparse


def parse_args(parser, commands):
    # Divide argv by commands
    split_argv = [[]]
    for c in sys.argv[1:]:
        if c in commands.choices:
            split_argv.append([c])
        else:
            split_argv[-1].append(c)
    # Initialize namespace
    args = argparse.Namespace()
    for c in commands.choices:
        setattr(args, c, None)
    # Parse each command
    parser.parse_args(split_argv[0], namespace=args)  # Without command
    for argv in split_argv[1:]:  # Commands
        n = argparse.Namespace()
        setattr(args, argv[0], n)
        parser.parse_args(argv, namespace=n)
    return args


parser = argparse.ArgumentParser()
commands = parser.add_subparsers(title='sub-commands')

cmd1_parser = commands.add_parser('cmd1')
cmd1_parser.add_argument('--foo')

cmd2_parser = commands.add_parser('cmd2')
cmd2_parser.add_argument('--foo')

cmd2_parser = commands.add_parser('cmd3')
cmd2_parser.add_argument('--foo')


args = parse_args(parser, commands)
print(args)

Es verhält sich richtig und bietet nette Hilfe:

Für ./test.py --help:

usage: test.py [-h] {cmd1,cmd2,cmd3} ...

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

sub-commands:
  {cmd1,cmd2,cmd3}

Für ./test.py cmd1 --help:

usage: test.py cmd1 [-h] [--foo FOO]

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

Und erstellt eine Hierarchie von Namespaces mit den Argumentwerten:

./test.py cmd1 --foo 3 cmd3 --foo 4
Namespace(cmd1=Namespace(foo='3'), cmd2=None, cmd3=Namespace(foo='4'))

Als ich Ihren Code oben überprüfte, stieß ich auf ein Problem. In Zeile 18, beziehen Sie sich auf split_argv[0]die tatsächlich leer ist split_argv, weil Sie anhängen [c]zu split_argv(intially gesetzt [[]]). Wenn Sie Zeile 7 in ändern split_argv = [], funktioniert alles wie erwartet.
HEADLESS_0NE

2
Ich habe (erneut) einige Korrekturen an dem von Ihnen freigegebenen Code vorgenommen (einige Probleme behoben, auf die
ich

Diese Antwort ist ziemlich anständig, Sie können bestimmen, welche subparserverwendet wurde, indem Sie dest zur add_subparsersMethode stackoverflow.com/questions/8250010/…
wizebin

5

Die von @Vikas bereitgestellte Lösung schlägt für unterbefehlsspezifische optionale Argumente fehl, der Ansatz ist jedoch gültig. Hier ist eine verbesserte Version:

import argparse

# create the top-level parser
parser = argparse.ArgumentParser(prog='PROG')
parser.add_argument('--foo', action='store_true', help='foo help')
subparsers = parser.add_subparsers(help='sub-command help', dest='subparser_name')

# create the parser for the "command_a" command
parser_a = subparsers.add_parser('command_a', help='command_a help')
parser_a.add_argument('bar', type=int, help='bar help')

# create the parser for the "command_b" command
parser_b = subparsers.add_parser('command_b', help='command_b help')
parser_b.add_argument('--baz', choices='XYZ', help='baz help')

# parse some argument lists
argv = ['--foo', 'command_a', '12', 'command_b', '--baz', 'Z']
while argv:
    print(argv)
    options, argv = parser.parse_known_args(argv)
    print(options)
    if not options.subparser_name:
        break

Dies verwendet parse_known_argsanstelle von parse_args. parse_argsbricht ab, sobald ein dem aktuellen Subparser unbekanntes Argument gefunden wird.parse_known_args gibt es als zweiten Wert im zurückgegebenen Tupel zurück. Bei diesem Ansatz werden die verbleibenden Argumente erneut dem Parser zugeführt. Daher wird für jeden Befehl ein neuer Namespace erstellt.

Beachten Sie, dass in diesem grundlegenden Beispiel alle globalen Optionen nur dem Namespace der ersten Optionen und nicht den nachfolgenden Namespaces hinzugefügt werden.

Dieser Ansatz funktioniert in den meisten Situationen einwandfrei, weist jedoch drei wichtige Einschränkungen auf:

  • Es ist nicht möglich, dasselbe optionale Argument für verschiedene Unterbefehle wie zu verwenden myprog.py command_a --foo=bar command_b --foo=bar.
  • Es ist nicht möglich, Positionsargumente variabler Länge mit Unterbefehlen ( nargs='?'oder nargs='+'oder nargs='*') zu verwenden.
  • Jedes bekannte Argument wird analysiert, ohne den neuen Befehl zu "brechen". ZB in PROG --foo command_b command_a --baz Z 12mit dem obigen Code, --baz Zwird von verbraucht command_b, nicht von command_a.

Diese Einschränkungen sind eine direkte Einschränkung von Argparse. Hier ist ein einfaches Beispiel, das die Einschränkungen von argparse zeigt - auch wenn ein einzelner Unterbefehl verwendet wird:

import argparse

parser = argparse.ArgumentParser()
parser.add_argument('spam', nargs='?')
subparsers = parser.add_subparsers(help='sub-command help', dest='subparser_name')

# create the parser for the "command_a" command
parser_a = subparsers.add_parser('command_a', help='command_a help')
parser_a.add_argument('bar', type=int, help='bar help')

# create the parser for the "command_b" command
parser_b = subparsers.add_parser('command_b', help='command_b help')

options = parser.parse_args('command_a 42'.split())
print(options)

Dies wird die erhöhen error: argument subparser_name: invalid choice: '42' (choose from 'command_a', 'command_b').

Die Ursache ist, dass die interne Methode argparse.ArgParser._parse_known_args()zu gierig ist und davon ausgeht, dass dies command_ader Wert des optionalen spamArguments ist. Insbesondere beim Aufteilen von optionalen und positionellen Argumenten werden _parse_known_args()nicht die Namen der Arugments (wie command_aoder command_b) berücksichtigt , sondern nur, wo sie in der Argumentliste vorkommen. Es wird auch davon ausgegangen, dass jeder Unterbefehl alle verbleibenden Argumente verbraucht. Diese Einschränkung argparseverhindert auch eine ordnungsgemäße Implementierung von Unterbefehlen mit mehreren Befehlen. Dies bedeutet leider, dass eine ordnungsgemäße Implementierung ein vollständiges Umschreiben der argparse.ArgParser._parse_known_args()Methode erfordert , dh mehr als 200 Codezeilen.

Angesichts dieser Einschränkung kann es eine Option sein, einfach auf ein einzelnes Multiple-Choice-Argument anstelle von Unterbefehlen zurückzugreifen:

import argparse

parser = argparse.ArgumentParser()
parser.add_argument('--bar', type=int, help='bar help')
parser.add_argument('commands', nargs='*', metavar='COMMAND',
                 choices=['command_a', 'command_b'])

options = parser.parse_args('--bar 2 command_a command_b'.split())
print(options)
#options = parser.parse_args(['--help'])

Es ist sogar möglich, die verschiedenen Befehle in den Verwendungsinformationen aufzulisten, siehe meine Antwort https://stackoverflow.com/a/49999185/428542


4

Sie könnten Arghandler versuchen . Dies ist eine Erweiterung von argparse mit expliziter Unterstützung für Unterbefehle.


3
arghandler bietet eine gute Möglichkeit, Unterbefehle zu deklarieren. Ich sehe jedoch nicht, wie dies hilft, die Frage von OP zu lösen: Parsen mehrerer Unterbefehle. Der erste analysierte Unterbefehl verbraucht alle verbleibenden Argumente, sodass weitere Befehle niemals analysiert werden. Bitte geben Sie einen Hinweis, wie Sie dies mit arghandler lösen können. Vielen Dank.
Adrian W

1

Ein weiteres Paket, das parallele Parser unterstützt, ist "declative_parser".

import argparse
from declarative_parser import Parser, Argument

supported_formats = ['png', 'jpeg', 'gif']

class InputParser(Parser):
    path = Argument(type=argparse.FileType('rb'), optional=False)
    format = Argument(default='png', choices=supported_formats)

class OutputParser(Parser):
    format = Argument(default='jpeg', choices=supported_formats)

class ImageConverter(Parser):
    description = 'This app converts images'

    verbose = Argument(action='store_true')
    input = InputParser()
    output = OutputParser()

parser = ImageConverter()

commands = '--verbose input image.jpeg --format jpeg output --format gif'.split()

namespace = parser.parse_args(commands)

und Namespace wird:

Namespace(
    input=Namespace(format='jpeg', path=<_io.BufferedReader name='image.jpeg'>),
    output=Namespace(format='gif'),
    verbose=True
)

Haftungsausschluss: Ich bin der Autor. Benötigt Python 3.6. So installieren Sie:

pip3 install declarative_parser

Hier ist die Dokumentation und hier ist das Repo auf GitHub .


1

Errichtet ein vollständiges Python 3.2 Beispiel mit subparsers , parse_known_argsund parse_args( läuft auf IDEone ):

from __future__ import print_function

from argparse import ArgumentParser
from random import randint


def main():
    parser = get_parser()

    input_sum_cmd = ['sum_cmd', '--sum']
    input_min_cmd = ['min_cmd', '--min']

    args, rest = parser.parse_known_args(
        # `sum`
        input_sum_cmd +
        ['-a', str(randint(21, 30)),
         '-b', str(randint(51, 80))] +
        # `min`
        input_min_cmd +
        ['-y', str(float(randint(64, 79))),
         '-z', str(float(randint(91, 120)) + .5)]
    )

    print('args:\t ', args,
          '\nrest:\t ', rest, '\n', sep='')

    sum_cmd_result = args.sm((args.a, args.b))
    print(
        'a:\t\t {:02d}\n'.format(args.a),
        'b:\t\t {:02d}\n'.format(args.b),
        'sum_cmd: {:02d}\n'.format(sum_cmd_result), sep='')

    assert rest[0] == 'min_cmd'
    args = parser.parse_args(rest)
    min_cmd_result = args.mn((args.y, args.z))
    print(
        'y:\t\t {:05.2f}\n'.format(args.y),
        'z:\t\t {:05.2f}\n'.format(args.z),
        'min_cmd: {:05.2f}'.format(min_cmd_result), sep='')

def get_parser():
    # create the top-level parser
    parser = ArgumentParser(prog='PROG')
    subparsers = parser.add_subparsers(help='sub-command help')

    # create the parser for the "sum" command
    parser_a = subparsers.add_parser('sum_cmd', help='sum some integers')
    parser_a.add_argument('-a', type=int,
                          help='an integer for the accumulator')
    parser_a.add_argument('-b', type=int,
                          help='an integer for the accumulator')
    parser_a.add_argument('--sum', dest='sm', action='store_const',
                          const=sum, default=max,
                          help='sum the integers (default: find the max)')

    # create the parser for the "min" command
    parser_b = subparsers.add_parser('min_cmd', help='min some integers')
    parser_b.add_argument('-y', type=float,
                          help='an float for the accumulator')
    parser_b.add_argument('-z', type=float,
                          help='an float for the accumulator')
    parser_b.add_argument('--min', dest='mn', action='store_const',
                          const=min, default=0,
                          help='smallest integer (default: 0)')
    return parser


if __name__ == '__main__':
    main()

0

Ich hatte mehr oder weniger die gleichen Anforderungen: Globale Argumente setzen und Befehle verketten und in der Reihenfolge der Befehlszeile ausführen können .

Am Ende hatte ich den folgenden Code. Ich habe einige Teile des Codes aus diesem und anderen Threads verwendet.

# argtest.py
import sys
import argparse

def init_args():

    def parse_args_into_namespaces(parser, commands):
        '''
        Split all command arguments (without prefix, like --) in
        own namespaces. Each command accepts extra options for
        configuration.
        Example: `add 2 mul 5 --repeat 3` could be used to a sequencial
                 addition of 2, then multiply with 5 repeated 3 times.
        '''
        class OrderNamespace(argparse.Namespace):
            '''
            Add `command_order` attribute - a list of command
            in order on the command line. This allows sequencial
            processing of arguments.
            '''
            globals = None
            def __init__(self, **kwargs):
                self.command_order = []
                super(OrderNamespace, self).__init__(**kwargs)

            def __setattr__(self, attr, value):
                attr = attr.replace('-', '_')
                if value and attr not in self.command_order:
                    self.command_order.append(attr)
                super(OrderNamespace, self).__setattr__(attr, value)

        # Divide argv by commands
        split_argv = [[]]
        for c in sys.argv[1:]:
            if c in commands.choices:
                split_argv.append([c])
            else:
                split_argv[-1].append(c)

        # Globals arguments without commands
        args = OrderNamespace()
        cmd, args_raw = 'globals', split_argv.pop(0)
        args_parsed = parser.parse_args(args_raw, namespace=OrderNamespace())
        setattr(args, cmd, args_parsed)

        # Split all commands to separate namespace
        pos = 0
        while len(split_argv):
            pos += 1
            cmd, *args_raw = split_argv.pop(0)
            assert cmd[0].isalpha(), 'Command must start with a letter.'
            args_parsed = commands.choices[cmd].parse_args(args_raw, namespace=OrderNamespace())
            setattr(args, f'{cmd}~{pos}', args_parsed)

        return args


    #
    # Supported commands and options
    #
    parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)

    parser.add_argument('--print', action='store_true')

    commands = parser.add_subparsers(title='Operation chain')

    cmd1_parser = commands.add_parser('add', formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    cmd1_parser.add_argument('add', help='Add this number.', type=float)
    cmd1_parser.add_argument('-r', '--repeat', help='Repeat this operation N times.',
                                               default=1, type=int)

    cmd2_parser = commands.add_parser('mult', formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    cmd2_parser.add_argument('mult', help='Multiply with this number.', type=float)
    cmd2_parser.add_argument('-r', '--repeat', help='Repeat this operation N times.',
                                               default=1, type=int)

    args = parse_args_into_namespaces(parser, commands)
    return args


#
# DEMO
#

args = init_args()

# print('Parsed arguments:')
# for cmd in args.command_order:
#     namespace = getattr(args, cmd)
#     for option_name in namespace.command_order:
#         option_value = getattr(namespace, option_name)
#         print((cmd, option_name, option_value))

print('Execution:')
result = 0
for cmd in args.command_order:
    namespace = getattr(args, cmd)
    cmd_name, cmd_position = cmd.split('~') if cmd.find('~') > -1 else (cmd, 0)
    if cmd_name == 'globals':
        pass
    elif cmd_name == 'add':
        for r in range(namespace.repeat):
            if args.globals.print:
                print(f'+ {namespace.add}')
            result = result + namespace.add
    elif cmd_name == 'mult':
        for r in range(namespace.repeat):
            if args.globals.print:
                print(f'* {namespace.mult}')
            result = result * namespace.mult
    else:
        raise NotImplementedError(f'Namespace `{cmd}` is not implemented.')
print(10*'-')
print(result)

Unten ein Beispiel:

$ python argstest.py --print add 1 -r 2 mult 5 add 3 mult -r 5 5

Execution:
+ 1.0
+ 1.0
* 5.0
+ 3.0
* 5.0
* 5.0
* 5.0
* 5.0
* 5.0
----------
40625.0

-4

Sie können das Paket optparse verwenden

import optparse
parser = optparse.OptionParser()
parser.add_option("-f", dest="filename", help="corpus filename")
parser.add_option("--alpha", dest="alpha", type="float", help="parameter alpha", default=0.5)
(options, args) = parser.parse_args()
fname = options.filename
alpha = options.alpha

1
Dies beantwortet die Frage nicht wirklich. Außerdem ist optparse veraltet (aus den Python-Dokumenten "Das optparse-Modul ist veraltet und wird nicht weiterentwickelt; die Entwicklung wird mit dem argparse-Modul fortgesetzt").
Chris

Entschuldigung für die Ablehnung, aber dies ist nicht die Antwort auf die Frage, die ich gestellt habe.
Vikas
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.