Seite 1 von 1

Python Konsolenausschnitte ausführen

Verfasst: Mittwoch 31. März 2010, 15:35
von snafu
Bei Beispielen in Dokumentation ist es ja oft so, dass ein paar Zeilen aus der Interpreter-Session einfach in den Text reinkopiert werden. An sich ist das auch recht anschaulich, aber wenn man den gezeigten Code mal ausprobieren will und zu faul ist, alles abzuschreiben, wird Copy&Paste nervig, da man den Interpreter-Prompt händisch wieder rauskriegen muss.

Das folgende Skript nimmt an, dass es alle Vorkommen von `>>>` bzw. `...` am Zeilenanfang als Prompt interpretieren und daher löschen darf. Zusätzlich werden Zeilen, die mit keins von beiden anfangen, komplett gelöscht. Das hat den Vorteil, dass etwaige Rückgabewerte rausgenommen werden und man das bereinigte Ergebnis direkt via `exec` ausführen oder irgendwo zum späteren Ausführen zwischenspeichern kann.

Wer `xsel` installiert hat (also i.d.R. Linux-User), braucht eigentlich nur den gewünschten Code in die Zwischenablage zu kopieren und die Hauptfunktion des Skriptes auszuführen, da dann die Ausgabe des besagten Programms (also Clipboard-Inhalt) zum Bereinigen genommen wird. Windows-Benutzer müssen die Konsolenausgabe leider händisch übergeben - zumindest solange es keine "Paste-Funktion" für sie gibt...

Was man noch machen könnte, wären Kommandozeilen-Parameter. Aktuell wird bei einem Ausführen "von Außen" standardmäßig das "Tastatur-Clipboard" verwendet (`xsel -b`). Vielleicht nutzt der Ein oder andere aber auch lieber die mittlere Maustaste für solche Aktionen oder möchte gar nichts mit der Zwischenablage machen, sondern nur die Ausgabe sehen. Außerdem finde ich den Weg, wie ich in die Pipe von `xsel` schreibe, nicht so toll. Ich denke, das geht doch bestimmt auch ohne `echo`.

Code: Alles auswählen

from subprocess import Popen, PIPE

def _get_clipboard_contents():
    xsel = Popen(['xsel', '-b'], stdout=PIPE)
    clipboard_contents = xsel.communicate()[0]
    return clipboard_contents

def _write_to_clipboard(s):
    echo = Popen(['echo', s], stdout=PIPE)
    xsel = Popen(['xsel', '-b', '-i'], stdin=echo.stdout, stdout=PIPE)
    xsel.communicate()

def pycon2source(s=None, copy_result=True):
    s = s or _get_clipboard_contents()
    result = '\n'.join(
        line[4:] for line in s.splitlines()
        if line.startswith(('>>>', '...'))
    )
    if copy_result:
        _write_to_clipboard(result)
    return result

if __name__ == '__main__':
    print pycon2source()

Verfasst: Mittwoch 31. März 2010, 16:03
von cofi
Da man den Prompt veraendern kann, sollte man den Prompt-Stripper als Funktion uebergeben koennen.

Code: Alles auswählen

def strip_std_prompt(s):
    if s.startswith(('>>>', '...')):
        return s[4:]
    else
        return s

def pycon2source(s=None, copy_result=True, strip=strip_std_prompt):
    s = s or _get_clipboard_contents()
    result = '\n'.join(strip(line) for line in s.splitlines())
    if copy_result:
        _write_to_clipboard(result)
    return result

Verfasst: Mittwoch 31. März 2010, 16:12
von snafu
Das ist eigentlich unnötig, da man die möglichen Prompts auch als Tupel für die Funktion übergeben könnte, wobei der Standard-Prompt der Default-Wert wäre. Außerdem unterschlägt deine Funktion das Feature, dass Konsolenausgaben rausgelöscht werden, da man natürlich nur die Eingaben ausführen möchte. ;)

Verfasst: Mittwoch 31. März 2010, 16:20
von cofi
Ok, das "Feature" hatte ich uebersehen, aber das liesse sich fixen ;) Das ist uebrigens genau das schoene bei so einer strip-Funktion, da du das dem Anwender ueberlaesst.

Der Punkt ist allerdings, dass es mit einem Tupel der Prompts nicht getan ist, da du dich auf die Laenge 3 beschraenkst.

Verfasst: Mittwoch 31. März 2010, 16:25
von snafu
Okay, dann gibt man den Prompt eben korrekt mit den trennenden Leerzeichen an und in der Hauptfunktion schneide ich halt exakt diese Länge ab. Ich sehe für den Benutzer keinen Vorteil, wenn er die Funktion selber schreiben soll.

Re: Python Konsolenausschnitte ausführen

Verfasst: Mittwoch 31. März 2010, 20:00
von crs
snafu hat geschrieben:Bei Beispielen in Dokumentation ist es ja oft so, dass ein paar Zeilen aus der Interpreter-Session einfach in den Text reinkopiert werden. An sich ist das auch recht anschaulich, aber wenn man den gezeigten Code mal ausprobieren will und zu faul ist, alles abzuschreiben, wird Copy&Paste nervig, da man den Interpreter-Prompt händisch wieder rauskriegen muss.
mit dem column-edit feature/mode von emacs/vim geht sowas eigentlich relativ komfortabel ;)

oder natuerlich

Code: Alles auswählen

sed 's/>*//' foo.py
8)

Verfasst: Mittwoch 31. März 2010, 20:31
von derdon
Du willst jedes > aus einem Python-Skript entfernen? Das ist wohl ein Scherz, oder? Aus ``a < b`` wird ``a b`` etc.

Verfasst: Mittwoch 31. März 2010, 20:32
von jbs
derdon hat geschrieben:Du willst jedes > aus einem Python-Skript entfernen? Das ist wohl ein Scherz, oder? Aus ``a < b`` wird ``a b`` etc.
Nein, aber aus `a > b` wird `a b` :D

SCNR

Verfasst: Mittwoch 31. März 2010, 20:33
von derdon
:oops:

Verfasst: Mittwoch 31. März 2010, 20:56
von crs
hm, habt ihr das getestet?

Code: Alles auswählen

?: echo "a > b" | sed 's/>*//'
a > b
?: echo "> a > b" | sed 's/>*//'
a > b
?: echo ">>>> a > b" | sed 's/>*//'
a > b
?: sed --version
GNU sed version 4.2.1
mag aber sein das man noch ein '^' vor die regex setzen sollte o.ae... der smiley sollte auch andeuten das ich eher die emacs/vim variante bevorzugen wuerde.

Verfasst: Mittwoch 31. März 2010, 21:24
von derdon
Hab vergessen, dass sed zeilenbasiert arbeitet.

Verfasst: Donnerstag 1. April 2010, 09:01
von snafu
Der sed-Ausdruck kommt ziemlich schlecht an das vorgegebene Ziel. Er lasst die Dreier-Punkte, das Leerzeichen zwischen Prompt und Eingabe und die jeweilige Ausgabe einfach so bestehen. Davon hat man nicht wirklich was bei mehrzeiligem Code. Ich werde mich aber nun in die sed-Syntax einlesen, um zu sehen, ob das vielleicht tatsächlich halbwegs verständlich damit geht. Mit `xsel` könnte man ja dann viel direkter die Daten reinschicken und bei Bedarf wieder in die Zwischenablage packen. Vielleicht hat ja auch jemand ne Lösung mit Perl?