Keyword arguments mit normalen vermischt...?

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Zuzu_Typ

Lieber Leser, liebe Leserin,

Wer direkt zum Punkt will, skippt ambesten zum :?:

ich habe ein kleines Verständnisproblem mit meinem Programm...

Um komplikationen zu vermeiden Poste ich einfach mal den ganzen Code.

Also dieses Programm hat in erster Linie den Zweck Aktionen zu loggen und Exceptions zu verhindern.

Code: Alles auswählen

# -*- coding: cp1252 -*-
#Version 0.2
#Calllback is a little logging module designed by Zuzu_Typ, for private use ONLY!
import os, sys, time, string
import traceback as traceb

usage = """This Module wasn't designed for being run as a standalone file.
Please import it with
from Callback import Callback
or
import Callback

Create a Callback instance with VAR = Callback()
You can then call a function:
VAR.call(FUNCTION,ARGUMENTS,KEYWORD ARGUMENTS)
"""

class Callback:
    def __init__(self,filepath=os.getcwd(),filename="CB_LOGGER.LOG",foldername="CB_LOGGER_BACKUP",log=True):
        self.log = log
        if log:
            self.filename = filename
            self.filepath = filepath
            self.foldername = foldername
            self.logfilecontent = ["#LOGFILE 'n'#\n"]
            self.logfilecontent.append(";Logfile generiert von Callback v 0.2\n")
            timeline = "(vom "+str(time.localtime().tm_mday)+"."+str(time.localtime().tm_mon)+"."+str(time.localtime().tm_year)+" um "+str(time.localtime().tm_hour)+"."+str(time.localtime().tm_min)+"."+str(time.localtime().tm_sec)+" Uhr)"
            self.logfilecontent.append(";"+timeline)
            self.backupfile = self.filepath+"//"+self.foldername+"//"+"Logfile "+timeline+self.filename[-4:]
            try:
                os.mkdir(self.foldername)
            except WindowsError:
                pass
            
            file_ = open(self.backupfile,"w")
            file_.close()
            try:
                file_ = open(self.filepath+"//"+self.filename,"w")
            except:
                raise IOError("Callback Logfile creation failed! Path or Filename: "+"´"+self.filepath+self.filename+"´")

            file_.close()
            self.update()

    def update(self):
        file_ = open(self.filepath+"//"+self.filename,"w")
        file_.writelines(self.logfilecontent)
        file_.close()
        file_ = open(self.backupfile,"w")
        file_.writelines(self.logfilecontent)
        file_.close()

    def get_current_time(self):
        return "["+str(time.localtime().tm_mday)+"."+str(time.localtime().tm_mon)+"."+str(time.localtime().tm_year)+"|"+str(time.localtime().tm_hour)+":"+str(time.localtime().tm_min)+"."+str(time.localtime().tm_sec)+"]"
        
    def call(self,function,log=False,*arg,**kw):
        if self.log and log:
            self.logfilecontent.append("\n")
            self.logfilecontent.append(self.get_current_time())
            self.logfilecontent.append("Calling "+str(function)+" with ( arg: "+str(arg)+" | kw: "+str(kw)+" )")
            self.update()
        try:
            return  function(*arg,**kw)
        except:
            if self.log and log:
                self.logfilecontent.append("\n")
                self.logfilecontent.append(self.get_current_time())
                self.logfilecontent.append("Exception raised: \n\n")
                exc_info = sys.exc_info()
                traceback_info = traceb.extract_tb(exc_info[2])
                self.logfilecontent.append('Traceback (most recent call last):')
                for i in traceback_info:
                    self.logfilecontent.append("\n")
                    self.logfilecontent.append('  File "'+i[0]+'", line '+str(i[1])+', in '+i[2]+"\n    "+i[3])
                string_ = str(exc_info[0])
                exception = string.split(string.split(string_,"<type 'exceptions.")[1],"'>")[0]
                self.logfilecontent.append("\n"+exception+": "+str(exc_info[1])+"\n")
                
                self.update()
            else:
                exc_info = sys.exc_info()
                traceback_info = traceb.extract_tb(exc_info[2])
                print "Exception raised: \n",'Traceback (most recent call last):'
                for i in traceback_info:
                    print('  File "'+i[0]+'", line '+str(i[1])+', in '+i[2]+"\n    "+i[3])
                string_ = str(exc_info[0])
                exception = string.split(string.split(string_,"<type 'exceptions.")[1],"'>")[0]
                print(exception+": "+str(exc_info[1]))

if __name__ == "__main__":
    def test(a):
        print(ab)
    CB = Callback(log=False)
    CB.call(test,1,2)
Der wichtige Teil des Codes ist ganz am Ende.
Dort wird eine Funktion "test" definiert, die ein normales Argument "a" anfordert und versucht eine nicht definierte Variable auszugeben "print(ab)"
Dann wird eine Instanz von der Klasse Callback erstellt, wobei das logging ausgeschaltet wird.
Danach wird ein Callback aufgerufen und zwar mit der Funktion test und dem Argument 1.
Was nun passieren sollte, ist dass das Programm die folgende Meldung ausgibt (geprintet und nicht als exception):

Code: Alles auswählen

Exception raised: 
Traceback (most recent call last):
  File "C:\Users\Zuzu_Typ\Desktop\Callback.py", line 64, in call
    return  function(*arg,**kw)
  File "C:\Users\Zuzu_Typ\Desktop\Callback.py", line 93, in test
    print ab
NameError: global name 'ab' is not defined
Stattdessen kommt das folgende:

Code: Alles auswählen

Exception raised: 
Traceback (most recent call last):
  File "C:\Users\Zuzu_Typ\Desktop\Callback.py", line 64, in call
    return  function(*arg,**kw)
TypeError: test() takes exactly 1 argument (0 given)
Bemerkung:
Die call Funktion fordert eigentlich nur das Argument function, das die auszuführende Funktion angibt und das keyword argument log, mit dem man explizit für eine bestimmte Funktion das Logging ausschalten kann. Alle anderen (keyword) arguments werden mit den Variablen arg und kw aufgenommen.
wird die Funktion call nun mit (test,1) ausgeführt, wird function die Funktion test zugewiesen und nun kommen wir zu meinem nicht-verständnis: :?:
dem Keyword argument log wird der Wert 1 zugewiesen, was eigentlich nicht passieren sollte.

Ist das eine Normale reaktion?

Vielen Dank für eure Hilfe!

Beste Grüße,
--Zuzu_Typ--
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

@Zuzu_Typ: in Python 2 gibt es keine expliziten Keywordargumente. Beim Aufruf werden die Argumente der Reihenfolge der Definition zugewiesen, oder, falls sie als Keywordargument angegeben sind, dem Namen nach. Also function=test, log=1 und arg = (2,). Will man explizite Keywordargumente haben, muß man sie über **kw simulieren.

Zum Code:
- um einen Datumsstring zu erzeugen, nimmt man datetime.datetime und format und ruft nicht 6 mal localtime auf.
- was glaubst Du bewirkt ein '//' in einem Dateipfad? Um Pfade zusammenzusetzen gibt es os.path.join.
- Jeden Fehler durch ein möglicherweise falschen IOError zu ersetzen ist unsinnig.
- Exceptions so zusammenzustückeln ist nicht nur unnötige Arbeit, sondern bestimmt auch fehleranfällig.

Allgemein: für Logging gibt es das Modul logging.
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Und abseits vom Datum gibt es auch `format`, z.B.

Code: Alles auswählen

"Callback Logfile creation failed! Path or Filename: "+"´"+self.filepath+self.filename+"´"
#mit format
"Callback Logfile creation failed! Path or Filename: ´{}{}´".format(self.filepath, self.filename)
Grundsaetzlich stimmt die Namensgebung nicht: `Callback` ist kein Callback, sondern das `test` Argument der `call` Methode ist der Callback. Was du da hast ist ein Logger fuer bestimmte Funktionsaufrufe.
Zuzu_Typ

Vielen Dank erstmal, aber ich hab da noch eine Frage:
Was meinst du hiermit:
Sirius3 hat geschrieben:- Jeden Fehler durch ein möglicherweise falschen IOError zu ersetzen ist unsinnig.
?
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

@Zuzu_Typ: ich beziehe mich da auf Zeile 40.

Was für einen Vorteil hat es traceback mit traceb abzukürzen?

In update schreibst Du wirklich jedesmal den kompletten logfilecontent neu???? Und das gleich in zwei Dateien? Es gibt einen append-Mode für Dateien.

Ab Zeile 66 machst Du zweimal das selbe, einmal für logfilecontent und einmal für print. Das sollte eigentlich in eine Funktion wandern.
Und oh Wunder, es gibt diese Funktion schon: traceback.format_exc()

Trotzdem, falls Du doch nochmal auf die Idee kommst, etwas ähnliches selbst zu machen: statt die String-Representation eines Typs per string.split zu parsen, könnte man auch gleich direkt auf das __name__-Attribut zugreifen. "string.split" benutzt wahrscheinlich seit Python 2.0 niemand mehr, denn es gibt die Methode direkt auf jedem String-Object.
Zuzu_Typ

Dankeschön für eure Erweiterung meines Wissensstandes...
ich schätze mal man merkt, dass ich Python nie gelernt oder ähnliches habe.
Deshalb fehlt mir auch ein Teil der fortgeschrittenen Methoden usw.

Falls ich weitere Fragen habe, werde ich mich wieder an das Forum wenden.

Nochmals vielen Dank! :D
Zuzu_Typ

So. Jetzt kommt die überarbeitete Version.
Was haltet ihr davon?
Gibt es noch Verbesserungsmöglichkeiten?

Code: Alles auswählen

#Version 0.5
#Calllback is a little logging module designed by Zuzu_Typ, for private use ONLY!

import os, logging, datetime, traceback

version = "0.5"

usage = """This Module wasn't designed for being run as a standalone file.
Please import it with
from Callback import Callback
or
import Callback

Create a Callback instance with VAR = Callback()
You can then call a function:
VAR.call(FUNCTION,ARGUMENTS,KEYWORD ARGUMENTS)
"""

class Callback:
    def __init__(self,filepath=os.getcwd(),filename="Logfile.log",log=True,debug=False): #filepath = string - Path, where the logfile is stored | filename = string - Name of the logfile | log = boolean - If logging should be enabled | debug = boolean - If the Callback is supposed to be run in DEBUG mode
        
        self.log = log
        if log:
            self.debug = debug
            self.filename = filename
            self.filepath = filepath
            timeline = datetime.datetime.now().strftime("[%d.%m.%y %H_%M_%S]")
            try:
                if debug:
                    logging.basicConfig(filename=self.filepath+"//"+self.filename, level=logging.DEBUG, format="%(levelname)s\t%(asctime)s\t\t:\t%(message)s", datefmt="[%d.%m.%y %H:%M:%S]", filemode="w")
                else:
                    logging.basicConfig(filename=self.filepath+"//"+self.filename, level=logging.INFO,  format="%(levelname)s\t%(asctime)s\t\t:\t%(message)s", datefmt="[%d.%m.%y %H:%M:%S]", filemode="w")
            except:
                print(traceback.format_exc())
            logging.info("/* Logfile produced with Callback version %s /*" % version)
        

    def call(self,function,*arg,**kw): #function = Function - The Callback | log = boolean - If logging should be enabled for this Callback
        log = True
        for x in kw:
            if x == "log":
                log = kw["log"]
        if self.log and log:
            logging.debug("Calling %s with (arg: %s | kw: %s)" % (str(function),str(arg),str(kw)))
        try:
            return  function(*arg,**kw)
        except:
            if self.log and log:
                logging.warning("Exception raised:\n\t\t\t\t\t\t%s" % traceback.format_exc().replace("\n","\n\t\t\t\t\t\t")) 
            print("Exception raised:\n%s" % traceback.format_exc())

if __name__ == "__main__":
    def default():
        print(usage)
    Callback(log=False).call(default)
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Nun, `Callback` ist immernoch kein Callback.

- Die Importe sollten alle auf ihre eigene Zeile
- Statt `usage` solltest du das in den Module-Docstring schreiben, `print(usage)` wird dann zu `print(__doc__)`
- Die Kommentare nach dem Methodenkopf sollten auch besser in den Docstring der Methode
- `__version__` statt `version`
- Dateinamen setzt man mit `os.path.join` zusammen und nicht mit Stringoperationen, schon gar nicht mit "//" als Trenner, kein Betriebssystem benutzt das

Code: Alles auswählen

log = True
for x in kw:
    if x == "log":
        log = kw["log"]
Ist ganz schoen umstandlich fuer:

Code: Alles auswählen

if "log" in kw:
    log = kw["log"]
else:
    log = True
# oder
kw.get("log", True)
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

@Zuzu_Typ: warum die Trennung in filepath und filename? Die beiden werden sowieso immer zusammengesetzt.
Was ist der Unterschied zwischen Zeile 30 und 32? Wie kann man das in eine Zeile packen?
Zeile 44: die str-Funktionsaufrufe sind überflüssig, %s macht das automatisch.
Zeile 49: logging kennt schon logging.exception, da muß man also nichts eigenes bauen.
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Zu Zeile 30 & 32:

Code: Alles auswählen

level = logging.DEBUG if debug else logging.INFO
logging.basicConfig(..., level=level, ...)
Antworten