
Hallo,
mein erster Post zu dieser Liste und leider nicht einmal zum coding selbst (vielleicht).
Ich habe vor einiger Zeit mit dem cmd-module eine schöne interaktive Shell für mein Tool gebaut und war davon begeistert. Denke das war noch unter python3.2, wo ich mit Python angefangen habe.
Jetzt bin ich auf Python3.4 umgestiegen (Vorgabe) und habe mit der Verwendung des cmd-Modules Probleme.
Wenn ich den cmdloop() starte gelange ich wie erwartet zum Prompt. Dort kann ich auch einen Befehl eingeben, der korrekt abgearbeitet wird, bis der nächste Prompt erscheint. Von da an allerdings nimmt die Konsole keine Tastatureingaben mehr entgegen, auch STRG+C nicht.
Hat jemand anderes dieses Problem ebenfalls?
Kann es evtl damit zusammenhängen, dass ich im unterliegenden Code mit mehreren Threads arbeite?
Danke,
Oliver
Hier noch der Code der Shell-Klasse dazu:
class Shell(cmd.Cmd):
def __init__(self, args): self.monitor = None self.args = args
def do_prepare(self, arg): self.Monitor = Monitor(self.args.config, self.args.delay) self.Monitor.readConfig()
def do_start(self, arg): self.Monitor.start()
def do_stop(self, arg): self.Monitor.stop()
def do_status(self, arg): print(self.Monitor.entryQeue.qsize())
def precmd(self, line): line = line.lower() return line

Am 26.03.2014 13:26, schrieb Oliver Friedrich:
Hier noch der Code der Shell-Klasse dazu:
Bitte poste lauffähigen Code, der den Fehler demonstriert, damit man das nachvollziehen kann. In deinem Beispiel fehlen Imports, die Monitor-Klasse und die __init__-Methode der Superklasse von Shell wird nicht aufgerufen.
Chris

Am 26.03.2014 15:37, schrieb Christopher Arndt:
Bitte poste lauffähigen Code, der den Fehler demonstriert, damit man das nachvollziehen kann. In deinem Beispiel fehlen Imports, die Monitor-Klasse und die __init__-Methode der Superklasse von Shell wird nicht aufgerufen.
Danke Chris, den Code habe ich in eine eigene Mail verfasst für die Übersichtlichkeit. Die __init__-Methode der Superklasse (cmd.Cmd) habe ich nicht aufgerufen, weil das in dem Beispielcode der Dokumentation auch nicht gemacht wird.

Oliver Friedrich wrote:
Ich habe vor einiger Zeit mit dem cmd-module eine schöne interaktive Shell für mein Tool gebaut und war davon begeistert. Denke das war noch unter python3.2, wo ich mit Python angefangen habe.
Jetzt bin ich auf Python3.4 umgestiegen (Vorgabe) und habe mit der Verwendung des cmd-Modules Probleme.
Heißt das, dass das gleiche Programm mit Python 3.2 arbeitet wie von dir erwartet, mit 3.4 jedoch nicht -- oder handelt es sich um verschiedene Skripte?
Wenn ich den cmdloop() starte gelange ich wie erwartet zum Prompt. Dort kann ich auch einen Befehl eingeben, der korrekt abgearbeitet wird, bis der nächste Prompt erscheint. Von da an allerdings nimmt die Konsole keine Tastatureingaben mehr entgegen, auch STRG+C nicht.
Hat jemand anderes dieses Problem ebenfalls?
Kann es evtl damit zusammenhängen, dass ich im unterliegenden Code mit mehreren Threads arbeite?
Wenn im Hintergrund noch weitere nicht-dämon Threads laufen, beendet sich das Programm nach Strg+C im Hauptthread nicht, sprich: eventuell ja ;)
Hier noch der Code der Shell-Klasse dazu:
class Shell(cmd.Cmd):
def __init__(self, args): self.monitor = None
self.args = args
def do_prepare(self, arg): self.Monitor = Monitor(self.args.config, self.args.delay) self.Monitor.readConfig() def do_start(self, arg): self.Monitor.start() def do_stop(self, arg): self.Monitor.stop() def do_status(self, arg): print(self.Monitor.entryQeue.qsize()) def precmd(self, line): line = line.lower() return line
Das ist reichlich nichtssagend, du müsstest schon eine eine minimale Monitor-Implementierung mitliefern, so dass wir das Problem reproduzieren können. Am besten postest du ein kleines für sich alleine lauffähiges Skript.

Am 26.03.2014 16:04, schrieb Peter Otten:
Jetzt bin ich auf Python3.4 umgestiegen (Vorgabe) und habe mit der Verwendung des cmd-Modules Probleme. Heißt das, dass das gleiche Programm mit Python 3.2 arbeitet wie von dir erwartet, mit 3.4 jedoch nicht -- oder handelt es sich um verschiedene Skripte?
Nein, sind zwei verschiedene Scripte. Hoffte eigentlich auf eine Antwort ala: "Ja, die cmd.Cmd Klasse hat seit der Version X.X nen Bug.". Aber so leicht wird das wohl nicht.
Kann es evtl damit zusammenhängen, dass ich im unterliegenden Code mit mehreren Threads arbeite?
Wenn im Hintergrund noch weitere nicht-dämon Threads laufen, beendet sich das Programm nach Strg+C im Hauptthread nicht, sprich: eventuell ja ;)
Ich verwende sowohl eigene Threads, wie auch die subprocess.
Das ist reichlich nichtssagend, du müsstest schon eine eine minimale Monitor-Implementierung mitliefern, so dass wir das Problem reproduzieren können. Am besten postest du ein kleines für sich alleine lauffähiges Skript.
Geschehen, in eigener Mail. Danke.

Hier, aufgrund der Nachfragen, eine eingedampfte Version des Programms.
Ziel ist es, diverse Logdateien von Unix-Servern zu holen in einer Umgebung mit sehr eingeschränkten Möglichkeiten, daher wird über Putty eine Verbindung zu den Servern aufgebaut und mittels Netcat die gewünschten Dateien übertragen. Diese werden anschließend in einzelne Logeinträge geparsed und später weiter verarbeitet (Auswertung von Performance-Ausgaben, etc).
Der Code ist noch ein Prototyp, daher ziemlich gemixt aus Prozedural und OOP. Ok, sein wir ehrlich, er wird auch nie über den Prototyp-Status hinaus kommen ;-)
#!/usr/bin/env python3 # -*- coding=UTF-8 -*-
from queue import Queue, Empty from threading import Thread, Lock from time import sleep
import codecs import os, re import subprocess import hashlib import shelve import argparse import csv import cmd import socket
timestampregex = [] def readTimestampRegex(): with codecs.open('timestamp.regex', encoding='utf-8') as file: reader = csv.reader(file, delimiter=';') for line in reader: if not line[0][0] == '#': timestampregex.append(re.compile(line[0] + '.*?(?=' + line[0]+')', re.M | re.S)) print(line[1] + ':' + line[0]) readTimestampRegex()
class RemoteFileMonitor():
def __init__(self, plink, outdir, dumpfile, queue, host, user, pw, name, path): self.host = host self.user = user self.pw = pw self.name = name self.path = path self.plink = plink self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.socketConn = None self.recvHost = '' self.recvPort = 0 self.entryQueue = queue self.link = None self.readingThread = Thread(name=self.name + '_reader', target=self.enqueue_output, daemon=True) self.parsingThread = Thread(name=self.name + '_parser', target=self.parse, daemon=True) self.buffer = '' self.bufferQueue = Queue() def enqueue_output(self): while True: self.bufferQueue.put(self.socketConn.recv(1024).decode('utf-8','ignore')) def getPlinkPrefix(self): pre = self.plink pre += ' -l ' + self.user if not self.pw == '-': pre += ' -pw ' + self.pw pre += ' ' + self.host + ' ' return pre def connect(self): self.socket.bind((self.recvHost, self.recvPort)) self.recvPort = self.socket.getsockname()[1] self.recvHost = socket.gethostbyname(socket.gethostname()) self.socket.listen(1) cmd = 'tail -F ' + self.path + ' | nc ' + self.recvHost + ' ' + str(self.recvPort) self.link = subprocess.Popen(self.getPlinkPrefix() + cmd, bufsize=1, stdout=subprocess.PIPE) self.socketConn = self.socket.accept()[0] def parse(self): buf = '' while True: try: line = self.bufferQueue.get_nowait() except Empty: pass else: buf += line for r in timestampregex: for m in r.finditer(buf): self.entryQueue.put(self.path,m.group()) buf = buf.replace(m.group(),'') def start(self): if self.link == None: self.connect() self.readingThread.start() self.parsingThread.start() def stop(self): self.parsingThread.stop() self.readingThread.stop() if self.link != None: self.link.terminate() class Monitor():
def __init__(self, configpath, delay, plink, outdir, dumpfile): self.config = configpath self.delay = delay self.outdir = outdir self.dumpfile = dumpfile self.plink = plink self.fileMonitors = [] self.entryQueue = Queue() def readConfig(self): with codecs.open(self.config, encoding='utf-8') as file: reader = csv.reader(file, delimiter=';') for line in reader: if len(line) >= 5: filemon = RemoteFileMonitor(self.plink, self.outdir, self.dumpfile, self.entryQueue, line[0], line[1], line[2], line[3], line[4]) self.fileMonitors.append(filemon) def start(self): for fileMon in self.fileMonitors: fileMon.start() print(fileMon.name + ' started') def stop(self): for fileMon in self.fileMonitors: fileMon.stop()
class Shell(cmd.Cmd): def __init__(self): self.monitor = None def do_prepare(self, arg): self.Monitor = Monitor('logs.csv', 30, r'c:\Portable\Putty\plink.exe', r'c:\tmp', True) self.Monitor.readConfig() def do_start(self, arg): self.Monitor.start() def do_stop(self, arg): self.Monitor.stop() def do_status(self, arg): print(self.Monitor.entryQeue.qsize()) def precmd(self, line): line = line.lower() return line if __name__ == '__main__': Shell().cmdloop()

Am 27.03.2014 09:06, schrieb Oliver Friedrich:
self.link = subprocess.Popen(self.getPlinkPrefix() + cmd, bufsize=1, stdout=subprocess.PIPE)
Ok, hat sich erledigt. Das Problem besteht darin, dass die geöffneten Subprozesse irgendwie den STDIN übernehmen. Das ändern der Zeile oben in folgende löst das Problem:
self.link = subprocess.Popen(self.getPlinkPrefix() + cmd, bufsize=1, stdin=subprocess.DEVNULL, stderr=subprocess.DEVNULL, stdout=subprocess.PIPE)
Danke an alle, die sich mit der Sache beschäftigt haben.
participants (3)
-
Christopher Arndt
-
Oliver Friedrich
-
Peter Otten