Modifying a built-in function for logging purposes

Bengt Richter bokr at oz.net
Sun May 15 03:29:58 EDT 2005


On Sat, 14 May 2005 19:19:08 -0700, Robert Kern <rkern at ucsd.edu> wrote:

>qwweeeit at yahoo.it wrote:
>> Hi Greg,
>> thank for your replay, but I didn't succeed in any way. You must
>> consider however that I'm not a Python "expert"...
>> IMHO, it must be a script that change part of the interpreter, and
>> substitute a new module (py) in the place of the standard one (py or
>> pyc). The standard module must be saved in order to be able to undo the
>> changes and go back to the normal behaviour.
>> The problem is that I don't know if the built-in functions like open
>> (or file) are written in Python or in C and, besides that, if they can
>> be modified.
>
>Short answer: if you don't know stuff like this, then you probably 
>shouldn't mess around with the builtins in production code.
>
>Depending on your operating system, there are probably programs that let 
>you list all of the files that have been opened on your system. For 
>example, on OS X, lsof(1) does the trick.
>
>> Other solutions which modify the source to be logged, are not
>> solutions, because it is far simpler to introduce here and there print
>> statements... 
>
Playing with this a little:

----< logopen.py >---------------------------------------
import sys, time

class OpenLogger(object):
    def __init__(self, logfile=sys.stdout):
        self.logfile = logfile
        self.old_open = __builtins__.open
    def open(self, path, *mode):
        openargs = ', '.join(map(repr, mode and [path, mode[0]] or [path]))
        tt = time.time()
        thdr = '%s.%02d' % (time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(tt)), int(tt*100)%100) 
        print >> self.logfile, '%s: open(%s) called from %r in "%s" line %s' % ((thdr, openargs) + 
            [(f.f_code.co_name, f.f_code.co_filename, f.f_lineno) for f in [sys._getframe(1)]][0])
        return self.old_open(path, *mode)
    def on(self):
        __builtins__.open = self.open
    def off(self):
        __builtins__.open = self.old_open

def main(*logfile):
    logger = OpenLogger(*logfile)
    try:
        logger.on()
        script_to_monitor = sys.argv[1]
        sys.argv[0:] = sys.argv[1:]
        xdict = dict(__builtins__=__builtins__, __name__='__main__')
        execfile(script_to_monitor, xdict)
    finally:
        logger.off()

if __name__ == '__main__':
    if not sys.argv[2:]: raise SystemExit, """
        Usage: [python] logopen.py [-log logfile] script_to_monitor [script_to_monitor args]"""
    logfile = sys.argv[1] == '-log' and sys.argv.pop(1) and [open(sys.argv.pop(1), 'a')] or []
    main(*logfile)
---------------------------------------------------------

A script whose opens we can monitor, that opens a file from module and function scope:
----< pnlines.py >---------------------------------------
import sys
print '----< %r' % sys.argv[1]
for i, line in enumerate(open(sys.argv[1], 'r')):
    print '%4s: %s' %(i+1, line.rstrip())

def foo(nlines=1):
    for i, line in enumerate(open(sys.argv[1])):    # test default 'r'
        print '%4s: %s' %(i+1, line.rstrip())
        if i+1 >= nlines: break
print '----< 3 lines of %r' % sys.argv[1]
foo(3)
---------------------------------------------------------

Result (tested only as as far as you see here):

[ 0:23] C:\pywk\clp>del logopen.txt
Could Not Find C:\pywk\clp\logopen.txt

[ 0:23] C:\pywk\clp>py24 logopen.py

        Usage: [python] logopen.py [-log logfile] script_to_monitor [script_to_monitor args]

Ok, so we pass pnlines.py as the file for it itself to print:

[ 0:23] C:\pywk\clp>py24 logopen.py -log logopen.txt pnlines.py pnlines.py
----< 'pnlines.py'
   1: import sys
   2: print '----< %r' % sys.argv[1]
   3: for i, line in enumerate(open(sys.argv[1], 'r')):
   4:     print '%4s: %s' %(i+1, line.rstrip())
   5:
   6: def foo(nlines=1):
   7:     for i, line in enumerate(open(sys.argv[1])):    # test default 'r'
   8:         print '%4s: %s' %(i+1, line.rstrip())
   9:         if i+1 >= nlines: break
  10: print '----< 3 lines of %r' % sys.argv[1]
  11: foo(3)
  12:
----< 3 lines of 'pnlines.py'
   1: import sys
   2: print '----< %r' % sys.argv[1]
   3: for i, line in enumerate(open(sys.argv[1], 'r')):

[ 0:24] C:\pywk\clp>type logopen.txt
2005-05-15 00:24:06.55: open('pnlines.py', 'r') called from '?' in "pnlines.py" line 3
2005-05-15 00:24:06.55: open('pnlines.py') called from 'foo' in "pnlines.py" line 7

Maybe the OP can build on this and contribute back something more useful and tested ;-)

Regards,
Bengt Richter



More information about the Python-list mailing list