[pypy-svn] r5669 - in pypy/trunk/src/pypy: objspace objspace/test tool

rxe at codespeak.net rxe at codespeak.net
Sun Jul 25 22:56:24 CEST 2004


Author: rxe
Date: Sun Jul 25 22:56:23 2004
New Revision: 5669

Modified:
   pypy/trunk/src/pypy/objspace/test/test_traceobjspace.py
   pypy/trunk/src/pypy/objspace/trace.py
   pypy/trunk/src/pypy/tool/traceinteractive.py
   pypy/trunk/src/pypy/tool/traceop.py
Log:
Various refactors and fixes.
Completer class was completely broken in traceinteractive.py.
printer in traceop.py broken up into a class. 
Stole yet more code from interpreter/interactive.py :-)

TraceObjectSpace can wrap any arbitrary object space by extending its class and then
can return to its original form by calling reset_trace().

To see in action :

$ python tool/traceinteractive.py -S

and set __pytrace__ to in int > 0.  The higher the value, the more tracing it will do.

  



Modified: pypy/trunk/src/pypy/objspace/test/test_traceobjspace.py
==============================================================================
--- pypy/trunk/src/pypy/objspace/test/test_traceobjspace.py	(original)
+++ pypy/trunk/src/pypy/objspace/test/test_traceobjspace.py	Sun Jul 25 22:56:23 2004
@@ -5,19 +5,16 @@
 from pypy.tool import pydis
     
 class Test_TraceObjSpace(testit.IntTestCase):
-    tspace = None
-    
+
     def setUp(self):
-        # XXX hack so we only have one trace space
-        if Test_TraceObjSpace.tspace is None:
-            newspace = testit.objspace().__class__()
-            Test_TraceObjSpace.tspace = trace.create_trace_space(newspace)
+        self.space = testit.objspace()       
+        trace.create_trace_space(self.space)
         
     def tearDown(self):
-        pass
+        self.space.reset_trace()
 
     def perform_trace(self, app_func):
-        tspace = self.tspace
+        tspace = self.space
         func_gw = app2interp(app_func)
         func = func_gw.get_function(tspace)
         tspace.settrace()
@@ -26,7 +23,7 @@
         return res 
 
     def test_traceobjspace_basic(self):
-        tspace = self.tspace
+        tspace = self.space
         self.assert_(tspace.is_true(tspace.w_builtins))
         #for name, value in vars(self.space).items():
         #    if not name.startswith('_'):
@@ -65,7 +62,7 @@
             1 + 1
         res = self.perform_trace(app_f)
         disresult = pydis.pydis(app_f)
-        uw = self.tspace.unwrap
+        uw = self.space.unwrap
         ops = res.getoperations()
         op_start = self.get_operation(ops, trace.CallBegin, "add")
         args = [uw(x) for x in op_start.callinfo.args]

Modified: pypy/trunk/src/pypy/objspace/trace.py
==============================================================================
--- pypy/trunk/src/pypy/objspace/trace.py	(original)
+++ pypy/trunk/src/pypy/objspace/trace.py	Sun Jul 25 22:56:23 2004
@@ -28,6 +28,14 @@
     def __init__(self, frame):
         self.frame = frame
 
+class CallInfo(object):
+    """ encapsulates a function call with its arguments. """
+    def __init__(self, name, func, args, kwargs):
+        self.name = name
+        self.func = func
+        self.args = args
+        self.kwargs = kwargs
+
 class CallBegin(object):
     def __init__(self, callinfo):
         self.callinfo = callinfo
@@ -105,19 +113,6 @@
         """ called just before execution of a bytecode. """
         self.__result.append(ExecBytecode(frame))
 
-    #def exception_trace(self, operror):
-    #    "called if the current frame raises an operation error. "
-    #    print "exception trace", operror
-    #    return self.__ec.exception_trace(operror)
-
-class CallInfo(object):
-    """ encapsulates a function call with its arguments. """
-    def __init__(self, name, func, args, kwargs):
-        self.name = name
-        self.func = func
-        self.args = args
-        self.kwargs = kwargs
-
 class CallableTracer(object):
     def __init__(self, result, name, func):
         self.__result = result
@@ -164,7 +159,11 @@
 
 def create_trace_space(space = None, operations = None):    
     """ Will create a trace object space if no space supplied.  Otherwise
-    the object."""
+    will turn the supplied into a tracable space by extending its class."""
+
+    # Don't trace an already tracable space
+    if hasattr(space, "__pypytrace___"):
+        return space
     
     if space is None:
         # make up a TrivialObjSpace by default
@@ -175,29 +174,42 @@
     if operations is None:
         operations = get_operations()
 
-    class TraceObjSpace(space.__class__):
+    class Trace(space.__class__):
+
         def __getattribute__(self, name):
-            obj = super(TraceObjSpace, self).__getattribute__(name)
-            if callable(obj) and name in operations:
-                return CallableTracer(self.__result, name, obj)
+            obj = super(Trace, self).__getattribute__(name)
+            if name in operations:
+                assert callable(obj)
+                obj = CallableTracer(self._result, name, obj)
             return obj
 
+        def __pypytrace___(self):
+            pass
+
         def settrace(self):
-            self.__result = TraceResult(self)
+            self._result = TraceResult(self)
 
         def getresult(self):
-            return self.__result
-
+            return self._result
+            
         def getexecutioncontext(self):
-            ec = super(TraceObjSpace, self).getexecutioncontext()
+            ec = super(Trace, self).getexecutioncontext()
             assert not isinstance(ec, ExecutionContextTracer)
-            return ExecutionContextTracer(self.__result, ec)
-    space.__class__ = TraceObjSpace
+            return ExecutionContextTracer(self._result, ec)
+        
+        def reset_trace(self):
+            """ Returns the class to it's original form. """
+            space.__class__ = space.__oldclass___
+            del space.__oldclass___
+
+            if hasattr(self, "_result"):
+                del self._result            
+
+    trace_clz = type("Trace" + space.__class__.__name__, (Trace,), {})
+    space.__oldclass___, space.__class__ = space.__class__, trace_clz
     space.settrace()
     return space
 
-TraceObjSpace = Space = create_trace_space
-
 # ______________________________________________________________________
 # End of trace.py
 

Modified: pypy/trunk/src/pypy/tool/traceinteractive.py
==============================================================================
--- pypy/trunk/src/pypy/tool/traceinteractive.py	(original)
+++ pypy/trunk/src/pypy/tool/traceinteractive.py	Sun Jul 25 22:56:23 2004
@@ -1,4 +1,5 @@
 # Standard imports
+import re
 import sys
 import code
 import keyword
@@ -11,93 +12,158 @@
     
 except:
     have_readline = False
-    
+
+
 # PyPy imports
 import autopath
 
 from pypy.tool import pydis
-from pypy.tool.traceop import print_result
+from pypy.tool.traceop import ResultPrinter
 
 from pypy.interpreter import executioncontext, pyframe, baseobjspace
 from pypy.interpreter.baseobjspace import ObjSpace
 
 from pypy.objspace import trace
 
-if have_readline:
+class Completer:
+    """ Stolen mostly from CPython's rlcompleter.py """
+    def __init__(self, space):
+        self.space = space
+
+    def complete(self, text, state):
+        if state == 0:
+            if "." in text:
+                self.matches = self.attr_matches(text)
+            else:
+                self.matches = self.global_matches(text)
+        try:
+            return self.matches[state]
 
-    class Completer:
-        def __init__(self, space):
-            self.space = space
-
-        def complete(self, text, state):
-
-            if state == 0:
-                if "." in text:
-                    self.matches = self.attr_matches(text)
-                else:
-                    self.matches = self.global_matches(text)
-            try:
-                return self.matches[state]
+        except IndexError:
+            return None
 
-            except IndexError:
-                return None
+    def global_matches(self, text):
 
-        def global_matches(self, text):
-            
-            import __builtin__            
+        w_res = self.space.call_method(self.space.w_globals, "keys")
+        namespace_keys = self.space.unwrap(w_res)
 
-            w_res = self.space.call_method(self.space.w_globals, "keys")
-            namespace_keys = self.space.unwrap(w_res)
+        w_res = self.space.call_method(self.space.w_builtins, "keys")
+        builtin_keys = self.space.unwrap(w_res)
 
-            matches = []
-            n = len(text)
-            
-            for l in [namespace_keys, __builtin__.__dict__.keys(), keyword.kwlist]:
-                for word in l:
-                    if word[:n] == text and word != "__builtins__":
-                        matches.append(word)
-            
-            return matches
+        matches = []
+        n = len(text)
 
-        def attr_matches(self, text):
-            import re
-            m = re.match(r"(\w+(\.\w+)*)\.(\w*)", text)
-            if not m:
-                return
+        for l in [namespace_keys, builtin_keys, keyword.kwlist]:
+            for word in l:
+                if word[:n] == text and word != "__builtins__":
+                    matches.append(word)
 
-            expr, attr = m.group(1, 3)
-            s = self.space
-            w_obj = s.getitem(s.w_globals, s.wrap(expr))
-            w_func = s.getitem(s.w_builtins, s.wrap("dir"))
-            w_res = s.call_function(w_func, w_obj)
-            words = s.unwrap(w_res)
-            matches = []
-            n = len(attr)
-            for word in words:
+        return matches
 
-                if word[:n] == attr and word != "__builtins__":
-                    matches.append("%s.%s" % (expr, word))
+    def attr_matches(self, text):
+        m = re.match(r"(\w+(\.\w+)*)\.(\w*)", text)
+        if not m:
+            return
+
+        expr, attr = m.group(1, 3)
+        s = self.space
+        w_obj = s.eval(expr, s.w_globals, s.w_globals)
+        words = self.get_words(w_obj)
+
+        w_clz = s.getattr(w_obj, s.wrap("__class__"))
+        words += self.get_class_members(w_clz)
+
+        matches = []
+        n = len(attr)
+        for word in words:
+            if word[:n] == attr and word != "__builtins__":
+                matches.append("%s.%s" % (expr, word))
+
+        return matches
+
+    def get_words(self, w_clz):
+        s = self.space
+        w_dir_func = s.getitem(s.w_builtins, s.wrap("dir"))
+        w_res = s.call_function(w_dir_func, w_clz)
+        return s.unwrap(w_res)
+
+    def get_class_members(self, w_clz):
+        s = self.space
+        words = self.get_words(w_clz)
+        try:                
+            w_bases = s.getattr(w_clz, s.wrap("__bases__"))             
+            bases_w = s.unpacktuple(w_bases)
 
-            return matches
+        except OperationError:
+            return words
+
+        for w_clz in bases_w:
+            words += self.get_class_members(w_clz)
+
+        return words
 
 class TraceConsole(code.InteractiveConsole):
     def __init__(self, space):
         code.InteractiveConsole.__init__(self)
-        s = self.space = trace.create_trace_space(space)
-        s.setitem(s.w_globals, s.wrap("__pytrace__"), s.w_True)
-        self.objspacename = space.__class__.__bases__[0].__name__
-
+        s = self.space = space
+        s.setitem(s.w_globals, s.wrap("__pytrace__"), s.wrap(0)) 
+        # Trace is binary (on or off), but we have different print levels
+        # for tracelevel > 0
+        self.tracelevel = 0
+        self.resprinter = ResultPrinter()
+        
     def interact(self, banner=None):
         if banner is None:
-            banner = "PyPy in TraceObjSpace(%s) on top of %s\n%s" % (
-                self.objspacename, sys.version.split()[0],
-                " [Use  __pytrace__ flag to turn off tracing]" )
+            banner = self.get_banner()
         code.InteractiveConsole.interact(self, banner)
 
+    def get_banner(self):
+        banner = "PyPy in %s on top of CPython %s\n%s" % (
+            space.__class__.__name__, sys.version.split()[0],
+            " [Use  __pytrace__ to set trace level (zero is default and does no tracing)]" )
+        return banner
+
     def raw_input(self, prompt=""):
         # add a character to the PyPy prompt so that you know where you
         # are when you debug it with "python -i py.py"
-        return code.InteractiveConsole.raw_input(self, prompt[0] + prompt)
+        try:
+            return code.InteractiveConsole.raw_input(self, prompt[0] + prompt)
+ 
+        except KeyboardInterrupt:
+            s = self.space
+            # fires into an interpreter-level console
+            print
+            banner = ("Python %s on %s\n" % (sys.version, sys.platform) +
+                      "*** Entering interpreter-level console ***")
+            local = self.__dict__.copy()
+            for w_name in s.unpackiterable(s.w_globals):
+                local['w_' + s.unwrap(w_name)] = (
+                    s.getitem(s.w_globals, w_name))
+            code.interact(banner=banner, local=local)
+            # copy back 'w_' names
+            for name in local:
+                if name.startswith('w_'):
+                    s.setitem(s.w_globals,
+                              s.wrap(name[2:]),
+                              local[name])
+            print '*** Leaving interpreter-level console ***'
+            raise
+
+    def set_tracelevel(self, tracelevel):
+        # Disable tracing altogether?
+        if self.tracelevel > 0 and tracelevel == 0:
+            self.space.reset_trace()
+            print self.get_banner()
+            
+        if self.tracelevel == 0 and tracelevel > 0:
+            trace.create_trace_space(self.space)
+            print self.get_banner()
+
+        self.tracelevel = tracelevel
+
+        # XXX Do something better than this - I'm not really sure what is useful
+        # and what is (rxe)
+        self.resprinter.operations_level = tracelevel
 
     def runcode(self, code):
         # 'code' is a CPython code object
@@ -105,23 +171,33 @@
         pycode = PyCode()._from_code(code)
 
         s = self.space
-        trace_flag = s.is_true(s.getitem(s.w_globals,
-                                         s.wrap("__pytrace__")))
-        if trace_flag:
-            s.settrace()
 
         try:
+            if self.tracelevel:
+                s.settrace()
+                
             pycode.exec_code(s, s.w_globals, s.w_globals)
-            if trace_flag:
+
+            res = None
+            if self.tracelevel:
                 res = s.getresult()
+                
+            # Did we modify __pytrace__
+            tracelevel = s.unwrap(s.getitem(s.w_globals,
+                                            s.wrap("__pytrace__")))
+            
+            if tracelevel != self.tracelevel:
+                self.set_tracelevel(tracelevel)
+
+            if res is not None and self.tracelevel:
                 s.settrace()
-                print_result(s, res)
+                self.resprinter.print_result(s, res)
                 
         except baseobjspace.OperationError, operationerr:
-            if trace_flag:
+            if self.tracelevel:
                 res = s.getresult()
                 s.settrace()
-                print_result(s, res)
+                self.resprinter.print_result(s, res)
 
             # XXX insert exception info into the application-level sys.last_xxx
             print
@@ -172,8 +248,9 @@
         atexit.register(readline.write_history_file)
 
     console.interact(banner)
-
+    
 if __name__ == '__main__':
+    
     from pypy.tool import option
     args = option.process_options(option.get_standard_options(),
                                   option.Options)
@@ -181,4 +258,3 @@
     # Create objspace...
     space = option.objspace()
     trace_interactive(space)
-    

Modified: pypy/trunk/src/pypy/tool/traceop.py
==============================================================================
--- pypy/trunk/src/pypy/tool/traceop.py	(original)
+++ pypy/trunk/src/pypy/tool/traceop.py	Sun Jul 25 22:56:23 2004
@@ -16,86 +16,195 @@
         disresult = _cache[obj] = pydis.pydis(obj)
         return disresult
 
-def line_begin(indent):
-    if indent:
-        return ("  " * indent) + "|-"
-    else:
-        return ""
-    
-def print_result(space, traceres, operations_level = 1000):
-    # XXX Refactor this - make more configurable.
-    indentor = '    '
-    lastframe = None
-    frame_count = 0
-    indent = ""
-    skip_frame_count = None
-    stack_info = []
-    for event in traceres.getevents():
+class Stack(list):
+    push = list.append
+
+    def pop(self):
+        return super(Stack, self).pop(-1)
+
+    def top(self):
+        try:
+            return self[-1]
+        except IndexError:
+            return None
+
+class ResultPrinter:
+
+    def __init__(self,
+                 skip_all_below_op = True,
+                 operations_level = 2,
+                 indentor = '  ',
+                 skip_bytecodes = ["PRINT_EXPR", "PRINT_ITEM", "PRINT_NEWLINE"],
+                 ):
         
-        if isinstance(event, trace.EnterFrame):
-            if not skip_frame_count:
-                print line_begin(frame_count) + ("<<<<<enter %s @ %s>>>>>>>" % (event.frame.code.co_filename, event.frame.code.co_firstlineno))
-
-            lastframe = event.frame
-            frame_count += 1
-
-        elif isinstance(event, trace.LeaveFrame):
-            frame_count -= 1
-
-            # No more bytecodes to skip at this level
-            if frame_count < skip_frame_count:
-                skip_frame_count = 0
-
-            if not skip_frame_count:
-                print line_begin(frame_count) + ("<<<<<leave %s >>>>>>>" % lastframe.code.co_filename)
-        elif isinstance(event, trace.ExecBytecode):
-
-            if frame_count == skip_frame_count:
-                skip_frame_count = 0
-
-            disresult = getdisresult(event.frame) 
-            bytecode = disresult.getbytecode(event.index)
-
-            if not skip_frame_count:
-                print line_begin(frame_count), "%2d" % event.index, "      ", bytecode
-            lastframe = event.frame
-
-            if bytecode.name in ["PRINT_EXPR", "PRINT_ITEM", "PRINT_NEWLINE"]:
-                print line_begin(frame_count + 1), "..."
-                skip_frame_count = frame_count           
-
-        elif isinstance(event, trace.CallBegin):
-            info = event.callinfo
-            if not skip_frame_count:
-                stack_info.append(info)
-                if len(stack_info) <= operations_level:
-                    print line_begin(frame_count), " " * 17 + ">> ", info.name, repr_args(space, lastframe, info.args)
-                frame_count += 1
-                    
-        elif isinstance(event, trace.CallFinished):
-            info = event.callinfo
-            if not skip_frame_count:
-                assert stack_info.pop(-1) == event.callinfo
-                frame_count -= 1
-                if len(stack_info) < operations_level:
-                    print line_begin(frame_count), " " * 20, info.name, "=: ", repr_value(space, event.res)
+        # Configurable stuff
+        self.indentor = indentor
+        self.skip_bytecodes = skip_bytecodes
+        self.operations_level = operations_level
+        self.skip_all_below_op = skip_all_below_op
         
-        elif isinstance(event, trace.CallException):
-            info = event.callinfo
-            if not skip_frame_count:
-                assert stack_info.pop(-1) == event.callinfo
-                frame_count -= 1
-                if len(stack_info) < operations_level:
-                    print line_begin(frame_count), " " * 17 + "x= ", info.name, event.ex
-        else:
-            pass
-    
+        self.reset()
+
+    def reset(self):
+        # State stuff
+        self.ops = Stack()
+        self.frames = Stack()
+        self.frame_count = 0
+        self.skip_frame_count = None
+
+    def print_line(self, line, additional_indent = 0):
+        if self.skip_frame_count is not None:
+            return
+
+        if self.frame_count:
+            indent = self.frame_count + additional_indent - 1
+            assert (indent >= 0)
+            line = (self.indentor * indent) + "|-" + line 
+
+        print line
+
+    def print_line_operations(self, line, additional_indent = 0):
+        # Don't allow operations to be exposed if operations level is up
+        # but do allow operations to be printed
+        if len(self.ops) > self.operations_level:
+            return
+
+        self.print_line(line, additional_indent = additional_indent)
+        
+    def print_frame(self, print_type, frame):
+
+        # Don't allow frames to be exposed if operations level is up
+        if len(self.ops) >= self.operations_level:
+            return
+
+        code = getattr(frame, 'code', None)
+        filename = getattr(code, 'co_filename', "")
+        lineno = getattr(code, 'co_firstlineno', "")
+
+        s = "<<<<<%s %s @ %s>>>>>>>" % (print_type, filename, lineno)
+        self.print_line(s)        
+
+    def print_bytecode(self, index, bytecode):
+        # Don't allow bytecodes to be exposed if operations level is up
+        if len(self.ops) >= self.operations_level:
+            return
+
+        s = "%2d%s%s" % (index, (self.indentor * 2), bytecode)
+        self.print_line(s)
+
+
+    def print_op_enter(self, name, str_args):
+        s = " " * 17
+        s += ">> %s%s" % (name, str_args)
+        self.print_line_operations(s)
+
+
+    def print_op_leave(self, name, str_res):
+        s = " " * 20
+        s += "%s =: %s" % (name, str_res)
+        self.print_line_operations(s)
+
+
+    def print_op_exc(self, name, exc):
+        s = " " * 17
+        s += "x= %s %s" % (name, exc)
+        self.print_line_operations(s)
+
+       
+    def print_result(self, space, traceres):
+
+        self.reset()
+
+        for event in traceres.getevents():
+
+            if isinstance(event, trace.EnterFrame):
+                frame = event.frame
+                self.print_frame("enter", frame)
+
+                self.frames.push(frame)
+                self.frame_count += 1
+                
+            elif isinstance(event, trace.LeaveFrame):
+                lastframe = self.frames.pop()
+                self.frame_count -= 1
+                
+                # Reset skip frame count?
+                if self.frame_count < self.skip_frame_count:
+                    self.skip_frame_count = None
+                    
+                self.print_frame("leave", lastframe)           
+                
+            elif isinstance(event, trace.ExecBytecode):
+
+                # Reset skip frame count?
+                if self.frame_count == self.skip_frame_count:
+                    self.skip_frame_count = None
+
+                frame = event.frame 
+                assert (frame == self.frames.top())
+                
+                # Get bytecode from frame
+                disresult = getdisresult(frame)
+                bytecode = disresult.getbytecode(event.index)
+                self.print_bytecode(event.index, bytecode)
+
+                # When operations_level > 1, some bytecodes produce high number of
+                # operations / bytecodes (usually because they have been written at app
+                # level) - this hack avoids them recursing on them selves
+                if bytecode.name in self.skip_bytecodes:
+                    self.print_line("...", 1)
+                    self.skip_frame_count = self.frame_count           
+
+            elif isinstance(event, trace.CallBegin):
+                info = event.callinfo
+
+                self.ops.push(info)
+                lastframe = self.frames.top()
+                self.print_op_enter(info.name, repr_args(space, lastframe, info.args))
+                self.frame_count += 1
+
+            elif isinstance(event, trace.CallFinished):
+                info = event.callinfo
+
+                self.frame_count -= 1
+                self.print_op_leave(info.name, repr_value(space, event.res))
+                
+                assert self.ops.pop() == event.callinfo
+                    
+            elif isinstance(event, trace.CallException):
+                info = event.callinfo
+                self.frame_count -= 1
+                
+                self.print_op_exc(info.name, event.ex)
+                
+                assert self.ops.pop() == event.callinfo
+
+            else:
+                pass
+
+print_result = ResultPrinter().print_result
+
 def repr_value(space, value):
-##     try:
-##         res = str(space.unwrap(value))
-##     except:
-##         res = str(value)
+    """ representations for debugging purposes """        
     res = str(value)
+    try:
+        # XXX Sure this won't go down well - didn't really want
+        # to clutter up the interpeter code 
+        from pypy.interpreter.argument import Arguments
+        from pypy.interpreter.function import Function, Method
+
+        if isinstance(value, Function):
+            res = "Function(%s, %s)" % (value.name, value.code)
+
+        if isinstance(value, Method):
+            res = "Method(%s, %s)" % (value.w_function.name, value.w_function.code)
+
+        if isinstance(value, Arguments):
+            res = "Argument(%s, %s)" % (value.args_w, value.kwds_w)
+
+    except Exception, exc:
+        pass
+    
     return res[:240]
 
 def repr_args(space, frame, args):



More information about the Pypy-commit mailing list