I'm trying to generate a bunch of functions based on the respective methods of a logging.Logger
instance from inside a function scope. The functions/methods I am interested in are:
['debug', 'info', 'warning', 'error', 'critical', 'exception']
which all share the same prototype.
Now I can obviously state:
global debug, info, warning, error critical, exception
and then go ahead and def
each of them. I checked it and that works fine. Problem is that I have to repeat myself.
So I have an instance of a logging.Logger
named mylog
which is already a global. The following does what I want, but by hardcoding the name of the function/method respectively:
global debug
def debug(msg, *args, **kwargs):
if mylog: mylog.debug(msg, *args, **kwargs)
For the sake of brevity I'm going to write my examples all with only debug
in mind, skipping the rest. Unfortunately the real code is somewhat more complex so that the requirements for using a lambda
don't seem to be fulfilled (single statement, right?).
So my obvious thinking was to use getattr
with the name of "debug"
on mylog
and simply call whatever I get back inside the defined function and add that by name to the globals()
dict
. Example:
for fct in ['debug']:
def x(msg, *args, **kwargs):
if mylog: getattr(mylog, fct)(msg, *args, **kwargs)
globals()[fct] = x
But I think this is not actually legal as I am calling what's supposed to be a class method as if it were a static method. Nevertheless this "works", but each output line is followed by another line saying: None
.
So I thought to myself that I could possibly pass the instance as the first parameter. After all that's what the self
is all about:
for fct in ['debug']:
def x(msg, *args, **kwargs):
if mylog: getattr(mylog, fct)(mylog, msg, *args, **kwargs)
globals()[fct] = x
which gives me:
TypeError: not all arguments converted during string formatting
which is the identical effect that I get from this:
for fct in ['debug']:
import types
def x(msg, *args, **kwargs):
if mylog: types.MethodType(getattr(mylog, fct), mylog)(msg, *args, **kwargs)
globals()[fct] = x
which I based on the accepted answer of this question: Dynamically bind method to class instance in python
Applying the answer from Alex Martelli here gives the same error.
What am I doing wrong? How can I achieve the desired behavior without having to repeat myself?
Full traceback:
Traceback (most recent call last):
File "/usr/lib64/python2.6/logging/__init__.py", line 776, in emit
msg = self.format(record)
File "/usr/lib64/python2.6/logging/__init__.py", line 654, in format
return fmt.format(record)
File "/usr/lib64/python2.6/logging/__init__.py", line 436, in format
record.message = record.getMessage()
File "/usr/lib64/python2.6/logging/__init__.py", line 306, in getMessage
msg = msg % self.args
TypeError: not all arguments converted during string formatting
Complete script, based on the very first example code above:
#!/usr/bin/env python
import sys, logging
def setup_logging():
global mylog
mylog = logging.getLogger('foobar')
mylog.addHandler(logging.StreamHandler())
for fct in ['debug']:
def x(msg, *args, **kwargs):
if mylog: getattr(mylog, fct)(mylog, msg, *args, **kwargs)
globals()[fct] = x
def main():
setup_logging()
debug('TEST LOG MESSAGE')
if __name__ == '__main__':
sys.exit(main())