Seite 1 von 1

Keyword arguments mit normalen vermischt...?

Verfasst: Sonntag 28. Juni 2015, 16:55
von 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--

Re: Keyword arguments mit normalen vermischt...?

Verfasst: Sonntag 28. Juni 2015, 17:49
von Sirius3
@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.

Re: Keyword arguments mit normalen vermischt...?

Verfasst: Sonntag 28. Juni 2015, 18:06
von cofi
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.

Re: Keyword arguments mit normalen vermischt...?

Verfasst: Sonntag 28. Juni 2015, 18:08
von 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.
?

Re: Keyword arguments mit normalen vermischt...?

Verfasst: Sonntag 28. Juni 2015, 18:25
von Sirius3
@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.

Re: Keyword arguments mit normalen vermischt...?

Verfasst: Sonntag 28. Juni 2015, 18:58
von 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

Re: Keyword arguments mit normalen vermischt...?

Verfasst: Dienstag 30. Juni 2015, 18:59
von 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)

Re: Keyword arguments mit normalen vermischt...?

Verfasst: Dienstag 30. Juni 2015, 19:31
von cofi
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)

Re: Keyword arguments mit normalen vermischt...?

Verfasst: Dienstag 30. Juni 2015, 20:07
von Zuzu_Typ
Dankeschön! :D

Re: Keyword arguments mit normalen vermischt...?

Verfasst: Mittwoch 1. Juli 2015, 07:11
von Sirius3
@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.

Re: Keyword arguments mit normalen vermischt...?

Verfasst: Mittwoch 1. Juli 2015, 10:14
von cofi
Zu Zeile 30 & 32:

Code: Alles auswählen

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