argparse-Unterbefehle, die mit verschachtelten namespaces
Tut argparse bieten integrierte Funktionen für Sie analysieren Gruppen-oder Parser in Ihre eigenen namespaces? Ich fühle mich wie ich muss fehlt eine option irgendwo.
Bearbeiten: Dieses Beispiel ist wohl nicht genau, was ich tun sollte, um die Struktur der parser, um mein Ziel, aber es war, was ich gearbeitet bisher. Mein Ziel ist es, in der Lage sein zu geben, subparsers Gruppen von Optionen, die analysiert werden, in namespace Felder. Die Idee hatte ich mit den Eltern war einfach zu bedienen Allgemeine Optionen für diesen gleichen Zweck.
Beispiel:
import argparse
# Main parser
main_parser = argparse.ArgumentParser()
main_parser.add_argument("-common")
# filter parser
filter_parser = argparse.ArgumentParser(add_help=False)
filter_parser.add_argument("-filter1")
filter_parser.add_argument("-filter2")
# sub commands
subparsers = main_parser.add_subparsers(help='sub-command help')
parser_a = subparsers.add_parser('command_a', help="command_a help", parents=[filter_parser])
parser_a.add_argument("-foo")
parser_a.add_argument("-bar")
parser_b = subparsers.add_parser('command_b', help="command_b help", parents=[filter_parser])
parser_b.add_argument("-biz")
parser_b.add_argument("-baz")
# parse
namespace = main_parser.parse_args()
print namespace
Dies ist, was ich bekomme, sind offensichtlich:
$ python test.py command_a -foo bar -filter1 val
Namespace(bar=None, common=None, filter1='val', filter2=None, foo='bar')
Aber das ist, was ich bin wirklich nach:
Namespace(bar=None, common=None, foo='bar',
filter=Namespace(filter1='val', filter2=None))
Und dann auch eher Gruppen von Optionen bereits untersucht und in namespaces:
Namespace(common=None,
foo='bar', bar=None,
filter=Namespace(filter1='val', filter2=None),
anotherGroup=Namespace(bazers='val'),
anotherGroup2=Namespace(fooers='val'),
)
Habe ich gefunden, Verwandte Frage hier aber es beinhaltet einige benutzerdefinierte analysieren und scheint, deckt nur einen wirklich spezifischen Umstand.
Gibt es eine Möglichkeit irgendwo zu sagen, argparse zu analysieren, die bestimmten Gruppen in einem Namensraum Felder?
- Ich bin mir nicht sicher, wie Sie erwarten, dass dies funktioniert. Wie haben Sie es geschrieben,
filter1
undfilter2
sind auf der top-level-parser, die nicht in einem Kind-parser namensfilter
. Wie könnte argparse wissen, dass Sie es wollen, zu handeln wie ein Kind von jedem sub-parser, wenn es gar nicht? - Vielleicht sollte ich neu formatieren mein Beispiel basiert auf Ihre Frage. Weil wirklich die Struktur, die ich zusammen setzen ist nicht sinnvoll, da du darauf hingewiesen hast. Mein Ziel ist wirklich in der Lage sein zu gelten Gruppen von Optionen, um subparsers, und haben Sie analysiert in einem namespace. Es wäre schön, wenn Sie könnten gemein sein, das ist, warum ich habe versucht mit der übergeordneten Struktur.
- So sind Sie auf der Suche nach so etwas wie
pip
,git
usw., wo gibt es, neben der top-level-globalen Optionen und Optionen, die spezifisch für jedes subkommando, auch Optionen, die gemeinsam von mehreren verschiedenen Unterbefehle (z.B. die--verbose
,--upgrade
, und--user
Optionenpip
, beziehungsweise), und in der Lage sein zu vertreten, dass die Freigabe direkt anstatt es implizit (durch kopieren die option Gruppen zu mehreren subparsers)? - Oder Sie wollen einfach nur genau das, was
add_argument_group
hat (und Sie zufrieden sind, kopieren Sie die Gruppe um), außer, Sie möchten die zusammengefassten Argumente erscheinen in einem sub-namespace in den Ergebnissen? Denn das wäre sehr einfach mit einem post-Prozessor: für jede Gruppe, erstellen Sie eine sub-namespace, die Iteration der Haupt-namespace, und jedes argument, das ein Mitglied der Gruppe, verschieben Sie es auf der sub-namespace. Aber die Arbeit mit sub-Parser wird ein wenig komplizierter, wenn Sie das so gut. - Yep, du hast Recht. Ich sollte mit einem argument-Gruppe, und das tun des post-processing nach der Tat. Danke für die Antwort!
- Offtopic, sobald Sie versuchen
docopt
Sie werden nie gehen zurück zu argparse/optparse/was auch immer...
Du musst angemeldet sein, um einen Kommentar abzugeben.
Wenn der Fokus auf nur darum, ausgewählte Argumente in Ihrer eigenen
namespace
, und die Verwendung von subparsers (und Eltern) ist beiläufig um das Problem, diese benutzerdefinierte Aktion könnte den trick tun.Es gibt verschiedene Möglichkeiten zur Angabe der
group
Namen. Es könnte sein, das als argument übergeben bei der Definition der Aktion. Es könnte Hinzugefügt werden, als parameter. Hier wählte ich zu analysieren, es aus derdest
(sonamespace.filter.filter1
können den Wert abrufen, derfilter.filter1
.Musste ich hinzufügen
default=argparse.SUPPRESS
so einbazers=None
Eintrag erscheint nicht in den Hauptnamensraum.Ergebnis:
Wenn Sie brauchen, default-Einträge in der geschachtelten namespaces definieren Sie den namespace vor der hand:
Ergebnis wie vorher, außer für:
dest
wiegroup.dest
, und erstellt die erforderlichen verschachtelten Objekten? Der Namensraum der Klasse definiert ist, ist sehr einfach. So lange, wie Sie Ihre neue Klasse arbeitet mitgetattr
,hasattr
undsetattr
es kann eine Menge hermacht.Ich bin mir nicht ganz sicher, was du fragst, aber ich denke, was Sie wollen, ist für eine argument-Gruppe oder sub-Befehl, um seine Argumente in einen sub-namespace.
Soweit ich weiß,
argparse
tut dies nicht aus der box. Aber es ist wirklich nicht schwer zu tun, durch die Nachbearbeitung das Ergebnis, so lange, wie Sie bereit sind, Graben sich unter der Decke ein bisschen. (Ich vermute, es ist noch einfacher, es zu tun, indem bilden von UnterklassenArgumentParser
haben, aber Sie ausdrücklich gesagt, Sie wollen nicht zu tun, also ich nicht versuchen.)Nun, die Liste aller Reiseziele für
breakfast
Optionen:- Und Schlüssel-Wert-Paare in
args
ist:So, alles, was wir haben, um zu bewegen, diejenigen, die übereinstimmen. Es wird ein wenig einfacher, wenn wir konstruieren, Wörterbücher zu erstellen, die namespaces von:
... Und das ist es;
top_namespace
aussieht:Natürlich in diesem Fall, wir haben eine statische Gruppe. Was, wenn Sie wollten, eine Allgemeine Lösung? Einfach.
parser._action_groups
ist eine Liste aller Gruppen, aber die ersten beiden sind die globalen positions-und Schlüsselwort-Gruppen. Also einfach nur Durchlaufenparser._action_groups[2:]
, und das gleiche tun für jeden, der Sie haben fürbreakfast
oben.Was über die sub-Befehle anstelle von Gruppen? Ähnlich, aber die details sind unterschiedlich. Wenn Sie gehalten habe, um jedes
subparser
Objekt, es ist nur ganz andereArgumentParser
. Wenn nicht, aber du hast das halten dersubparsers
Objekt, es ist eine spezielle Art vonAction
, derenchoices
ist ein dict dessen Schlüssel die subparser-Namen und deren Werte sind die subparsers selbst. Wenn Sie gehalten, weder... beginnen beiparser._subparsers
und es herausfinden von dort.Jedenfalls, sobald Sie wissen, wie zu finden, die Namen, die Sie verschieben möchten und wo Sie möchten, um Sie zu bewegen, es ist das gleiche wie mit Gruppen.
Wenn Sie haben, zusätzlich zu globalen args und/oder Gruppen und subparser-spezifische Argumente und/oder Gruppen, einige Gruppen, die gemeinsam von mehreren subparsers... dann konzeptionell an der es schwierig wird, weil jeder subparser endet mit Referenzen zu der gleichen Gruppe, und Sie können nicht verschieben Sie es an al von Ihnen. Aber zum Glück, du bist nur den Umgang mit genau einer subparser (oder keine), so können Sie einfach ignorieren die anderen subparsers und verschieben Sie alle Gruppen unter dem ausgewählten subparser (und jede Gruppe, die nicht existieren in der ausgewählten subparser, entweder oben verlassen, oder wegwerfen, oder wählen Sie eine subparser willkürlich).
Schachteln mit
Action
Unterklassen ist in Ordnung für eine Art von Aktion, aber ist ein ärgernis, wenn Sie brauchen, um eine Unterklasse mehrere Arten (store, speichern, true, 'append', etc). Hier ist eine andere Idee - Unterklasse-Namespace. Tun die gleiche Art von Namen split und setattr, aber tun Sie es in den Namespace statt der Aktion. Dann erstellen Sie einfach eine Instanz der neuen Klasse, und übergeben es anparse_args
.Herstellung:
Den
__getattr__
für diesen namespace (notwendig für Tätigkeiten wie zählen und Anhängen) könnten sein:Habe ich vorgeschlagen, verschiedene andere Optionen, aber wie dieser ist das beste. Es stellt die storage-details, wo Sie hingehören, in den Namespace, nicht der parser.
--help
Erweiterung nicht mehr funktioniert aufgrundTypeError: unsupported operand type(s) for %: 'tuple' and 'dict'
. Funktioniert diese Methode verhindern, dass--help
Erweiterung? Ich werde erkunden Sie diese mehr und recomment/löschen je nach Ergebnis.Ab abarnert Antwort, ich habe die folgenden MWE++; -), mit der multiple Konfiguration von Gruppen mit ähnlichen option Namen.
Dann können Sie es so nennen (Eselsbrücke:
--mod1-...
sind die Optionen für "mod1", usw.):In diesem script habe ich geändert, die
__call__
Methode des argparse._SubParsersAction. Statt der übergabe dernamespace
auf der subparser, es geht eine neue. Anschließend fügt hinzu, dass zu den wichtigstennamespace
. Ich ändern nur 3 Zeilen__call__
.Diese produziert:
Ich verwendet
parse_known_args
um zu testen, wie extra-strings übergeben werden, zurück auf die Haupt-parser.Ließ ich die
parents
Zeug, weil es nicht alles hinzufügen, was zu diesem namespace ändern. es ist einfach eine bequeme Art und Weise zu definieren, die eine Reihe von Argumenten, die mehrere subparsers verwenden.argparse
nicht notieren, welche Argumente wurden Hinzugefügt überparents
, und die wurden direkt aufgenommen. Es ist nicht eine Gruppierung toolargument_groups
nicht viel helfen, entweder. Sie werden durch die Hilfe formatter, aber nicht durchparse_args
.Ich könnte Unterklasse
_SubParsersAction
(statt Neuzuweisen__call__
), aber dann müsste ich ändern dasmain_parse.register
.Basiert auf der Antwort von @abarnert, schrieb ich eine einfache Funktion, die tut, was der OP will:
Sie nur geben es die
ArgumentParser
Instanz und es gibt eine verschachtelteNameSpace
nach der Struktur der Gruppe der Argumente.Bitte überprüfen Sie die argpext Modul auf PyPi, es kann Ihnen helfen!