Derzeit arbeite ich an einem kleinen Projekt ... dann dachte ich mir "Complete wäre ein nettes Feature" ... und schon wurde aus einer Mücke ein Elefant ...
Ich hab die die Dokumentationen bei Python und die der Gnu readline Bibliothek durchgelesen ...
Ich hab im Internet nach Beispielen gesucht ...
Entweder ich nicht richtig gesucht, hab nichts brauchbares gefunden oder bin nicht in der Lage das Gefundene zu verstehen ...
(Wobei ich letzteres doch bezweifle )
Was bedeuten die Parameter "text" und "state", die die Funkltion übergeben bekommt?
Und was soll die Funktion mit diesen Parametern machen?
Nachdem ich mir seit 3 Tagen den Kopf zerbreche wäre ich für Hilfe zu dem Thema dankbar.
Complete mit readline
- __blackjack__
- User
- Beiträge: 13110
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
@midan23: Also die Dokumentation sagt:
Was genau ist daran unverständlich? Und warum hast Du das nicht einfach mal ausproblert?
Code: Alles auswählen
set_completer([function]) -> None
Set or remove the completer function.
The function is called as function(text, state),
for state in 0, 1, 2, ..., until it returns a non-string.
It should return the next possible completion starting with 'text'.
Code: Alles auswählen
#!/usr/bin/env python3
import readline
def completer(text, state):
#print(text, state)
return f"{text}{state}" if state < 5 else None
def main():
readline.set_completer(completer)
readline.parse_and_bind("tab: complete")
text = input("Eingabe: ")
print(text)
if __name__ == '__main__':
main()
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Ich hab so einiges ausprobiert ... darunter auch so etwas:
Aber sobald es mehr als ein Wort ist das vervollständigt werden soll weiß ich nicht, wie es gehen soll ...
Damit meine ich so was wie: command subcommand ...
Code: Alles auswählen
def completer(self, text, state):
result = [word for word in self.get_words() if word.startswith(text)]
if len(result) == 1:
result = [f"{result[0]} "]
result.append(None)
return result[state]
Damit meine ich so was wie: command subcommand ...
- __blackjack__
- User
- Beiträge: 13110
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
@midan23: Das musst Du halt programmieren. Das Du erst schaust wie der komplette Eingabepuffer aussieht, dass der mit "command" anfängt, und dann entsprechend die Wortliste für "subcommand" und/oder mögliche Optionen für "command" anbietet. Die komplette bisherige Eingabe bekommt man mit `readline.get_line_buffer()`.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Und was hat "state" für eine Bedeutung?
- __blackjack__
- User
- Beiträge: 13110
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
@midan23: Ich verstehe die Frage nicht so ganz, Du verwendest das doch. Das ist eine fortlaufende Zahl um die verschiedenen Varianten abzufragen. `readline` ruft die Funktion nach einer Anfrage zur Vervollständigung so oft auf bis die keine Zeichenkette mehr liefert. Und man muss bei jedem Aufruf ja wissen wie oft schon aufgerufen wurde. Und das muss man sich nicht selbst merken, das liefert `state` von aussen schon. Das ist aus Python-Sicht natürlich eine etwas komische API aber `libreadline` ist eine C-Bibliothek und die Entwickler von dem Modul haben sich entschieden die möglichst 1:1 weiterzureichen.
Ich würde da auch nicht bei jedem Aufruf alle Möglichkeiten in einer Liste packen nur um dann *eine* davon auszuwählen. Bei `state` gleich 0 kann man die Möglichkeiten erstellen und sich einfach merken.
Was genau willst Du da eigentlich machen? Würde das `cmd`-Modul aus der Standardbibliothek Dein Leben eventuell vereinfachen? Oder das python-prompt-toolkit eventuell noch mehr vereinfachen (und bunter machen)?
Ich würde da auch nicht bei jedem Aufruf alle Möglichkeiten in einer Liste packen nur um dann *eine* davon auszuwählen. Bei `state` gleich 0 kann man die Möglichkeiten erstellen und sich einfach merken.
Was genau willst Du da eigentlich machen? Würde das `cmd`-Modul aus der Standardbibliothek Dein Leben eventuell vereinfachen? Oder das python-prompt-toolkit eventuell noch mehr vereinfachen (und bunter machen)?
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Beides habe ich ausprobiert ... und irgendwie bin ich mit keines der beiden Module/Klassen wirklich zufrieden ...
Mit dem "cmd"-Modul hab ich einen Befehlsinterpreter gebaut ... das Ergebnis ist ein Monster das ich nicht wirklich verstehe und damit auch nicht ändern kann.
Ich hab zwar versucht "cmd" mit "argparse" zu nutzen, aber sobald die Parser komplexer werden wird es unübersichtlich ... und das obwohl vieles davon im Prinzip nur Copy/Paste ist ...
"python-prompt-toolkit" bietet zwar viel, aber die Befehle müsste ich trotzdem erst überprüfen.
"cmd2" hab ich auch schon versucht ... Bietet alles von "cmd" und "argparse" mit Autocomplete ... aber die mangelnde Übersicht bleibt ... und die Parser werden auf Klassenebene erstellt, was mir auch nicht wirklich gefällt.
Nachdem ich nichts wirklich brauchbares gefunden hatte, habe ich angefangen mein eigenes Framework zu bauen ...
Mit dem "cmd"-Modul hab ich einen Befehlsinterpreter gebaut ... das Ergebnis ist ein Monster das ich nicht wirklich verstehe und damit auch nicht ändern kann.
Ich hab zwar versucht "cmd" mit "argparse" zu nutzen, aber sobald die Parser komplexer werden wird es unübersichtlich ... und das obwohl vieles davon im Prinzip nur Copy/Paste ist ...
"python-prompt-toolkit" bietet zwar viel, aber die Befehle müsste ich trotzdem erst überprüfen.
"cmd2" hab ich auch schon versucht ... Bietet alles von "cmd" und "argparse" mit Autocomplete ... aber die mangelnde Übersicht bleibt ... und die Parser werden auf Klassenebene erstellt, was mir auch nicht wirklich gefällt.
Nachdem ich nichts wirklich brauchbares gefunden hatte, habe ich angefangen mein eigenes Framework zu bauen ...
Das Wichtigste zuerst:
@__blackjack__: Danke für deine Hilfe
Hat etwas gedauert aber letzten Endes habe ich verstanden, das die Funktion die an "readline.set_completer" übergeben wird vom Prinzip her nicht anderes ist als ein Generator der nach und nach die Elemente einer Liste aus Zeichenketten zurück gibt. Dabei ist der Parameter "state" der Index für die Liste. Wenn "state" gleich 0 ist sollte man die Liste erstellen und das letzte Element der Liste sollte keine Zeichenkette sein.
Und ich konnte sogar noch was anderes lernen: Das es Comprehensions für Listen gibt wusste ich ... das es aber auch welche für dicts und sets gibt war mir neu ...
Wie dem auch sei, meine CMD-Alternative häng ich mal unten dran. Vielleicht kann jemand diese brauchen ...
@__blackjack__: Danke für deine Hilfe
Hat etwas gedauert aber letzten Endes habe ich verstanden, das die Funktion die an "readline.set_completer" übergeben wird vom Prinzip her nicht anderes ist als ein Generator der nach und nach die Elemente einer Liste aus Zeichenketten zurück gibt. Dabei ist der Parameter "state" der Index für die Liste. Wenn "state" gleich 0 ist sollte man die Liste erstellen und das letzte Element der Liste sollte keine Zeichenkette sein.
Und ich konnte sogar noch was anderes lernen: Das es Comprehensions für Listen gibt wusste ich ... das es aber auch welche für dicts und sets gibt war mir neu ...
Wie dem auch sei, meine CMD-Alternative häng ich mal unten dran. Vielleicht kann jemand diese brauchen ...
Code: Alles auswählen
class MyCmd():
# ---< def __init__(self, completekey=None) >---
def __init__(self, completekey=None):
self.running = True
self.prompt = "> "
self._cmds = dict()
for cmd in [s[3:] for s in dir(self.__class__) if s.startswith("do_")]:
key = cmd.replace("_", " ")
self._cmds[key] = {"function": getattr(self, f"do_{cmd}")}
completer = getattr(self, f"complete_{cmd}", None)
if completer:
self._cmds[key]["completer"] = completer
self.completekey = completekey if completekey else "tab"
self._old_completer = readline.get_completer()
readline.set_completer(self.completer)
readline.parse_and_bind(f"{self.completekey}: complete")
self._completions = list()
# ---< def normalize(self, line, keep_last_space) >---
def normalize(self, line, keep_last_space):
result = ""
if line:
last = keep_last_space and (line[-1] == " ")
result = " ".join(line.split())
if last:
result = f"{result} "
return result
# ---< def completer(self, text, state) >---
def completer(self, text, state):
if not state:
self._completions = list()
line = self.normalize(readline.get_line_buffer(), True)
spaces = line.count(" ")
if spaces == 0:
self._completions = list({s.split()[0] for s in self._cmds if s.startswith(text)})
elif spaces == 1:
cmd = line.split()[0]
if cmd in self._cmds:
completer = self._cmds[cmd].get("completer", None)
if completer:
self._completions = [s for s in completer(state) if s.startswith(text)]
else:
subcmds = list({s.split()[1] for s in self._cmds if " " in s})
self._completions = [s for s in subcmds if s.startswith(text)]
else:
cmd = " ".join(line.split()[:2])
if cmd in self._cmds:
completer = self._cmds[cmd].get("completer", None)
if completer:
self._completions = [s for s in completer(state) if s.startswith(text)]
if len(self._completions) == 1:
self._completions = [f"{self._completions[0]} "]
self._completions.append(None)
return self._completions[state]
# ---< def cmdloop(self) >---
def cmdloop(self):
self.preloop()
while True:
line = self.get_line()
if not self.running:
break
line = self.precmd(line)
self.onecmd(line)
self.postcmd(line)
self.postloop()
# ---< def get_line(self) >---
def get_line(self):
line = ""
try:
line = input(self.prompt)
except (EOFError, KeyboardInterrupt):
print()
self.running = False
return self.normalize(line, False)
# ---< def preloop(self) >---
def preloop(self):
pass
# ---< def postloop(self) >---
def postloop(self):
readline.set_completer(self._old_completer)
# ---< def precmd(self, line) >---
def precmd(self, line):
return line
# ---< def postcmd(self, line) >---
def postcmd(self, line):
pass
# ---< def onecmd(self, line) >---
def onecmd(self, line):
if line:
cmd, _, rest = line.partition(" ")
if cmd not in self._cmds:
subcmd, _, rest = rest.partition(" ")
cmd = f"{cmd} {subcmd}"
func = self._cmds.get(cmd, None)
if func:
func["function"](rest)
else:
self.default(line)
# ---< def default(self, line) >---
def default(self, line):
print(f"Unknown Syntax: {line}")