[pypy-commit] pypy default: refactor the code and start to write tests

antocuni noreply at buildbot.pypy.org
Thu Aug 4 17:02:49 CEST 2011


Author: Antonio Cuni <anto.cuni at gmail.com>
Branch: 
Changeset: r46274:6acca3ec3ace
Date: 2011-08-04 16:49 +0200
http://bitbucket.org/pypy/pypy/changeset/6acca3ec3ace/

Log:	refactor the code and start to write tests

diff --git a/pypy/tool/gdb_pypy.py b/pypy/tool/gdb_pypy.py
--- a/pypy/tool/gdb_pypy.py
+++ b/pypy/tool/gdb_pypy.py
@@ -10,9 +10,48 @@
 
 import sys
 import os.path
-import gdb
 
-class RPyType (gdb.Command):
+try:
+    # when running inside gdb
+    from gdb import Command
+except ImportError:
+    # whenn running outside gdb: mock class for testing
+    class Command(object):
+        def __init__(self, name, command_class):
+            pass
+
+
+def find_field_with_suffix(val, suffix):
+    """
+    Return ``val[field]``, where ``field`` is the only one whose name ends
+    with ``suffix``.  If there is no such field, or more than one, raise KeyError.
+    """
+    names = []
+    for field in val.type.fields():
+        if field.name.endswith(suffix):
+            names.append(field.name)
+    #
+    if len(names) == 1:
+        return val[names[0]]
+    elif len(names) == 0:
+        raise KeyError, "cannot find field *%s" % suffix
+    else:
+        raise KeyError, "too many matching fields: %s" % ', '.join(names)
+
+def lookup(val, suffix):
+    """
+    Lookup a field which ends with ``suffix`` following the rpython struct
+    inheritance hierarchy (i.e., looking both at ``val`` and
+    ``val['*_super']``, recursively.
+    """
+    try:
+        return find_field_with_suffix(val, suffix)
+    except KeyError:
+        baseobj = find_field_with_suffix(val, '_super')
+        return lookup(baseobj, suffix)
+
+
+class RPyType(Command):
     """
     Prints the RPython type of the expression (remember to dereference it!)
     It assumes to find ``typeids.txt`` in the current directory.
@@ -24,8 +63,12 @@
 
     prog2typeids = {}
  
-    def __init__(self):
-        gdb.Command.__init__(self, "rpy_type", gdb.COMMAND_NONE)
+    def __init__(self, gdb=None):
+        # dependency injection, for tests
+        if gdb is None:
+            import gdb
+        self.gdb = gdb
+        Command.__init__(self, "rpy_type", self.gdb.COMMAND_NONE)
 
     # some magic code to automatically reload the python file while developing
     def invoke(self, arg, from_tty):
@@ -36,8 +79,8 @@
         self.do_invoke(arg, from_tty)
 
     def do_invoke(self, arg, from_tty):
-        obj = gdb.parse_and_eval(arg)
-        hdr = self.get_gc_header(obj)
+        obj = self.gdb.parse_and_eval(arg)
+        hdr = lookup(obj, '_gcheader')
         tid = hdr['h_tid']
         offset = tid & 0xFFFFFFFF # 64bit only
         offset = int(offset) # convert from gdb.Value to python int
@@ -48,7 +91,7 @@
             print 'Cannot find the type with offset %d' % offset
 
     def get_typeids(self):
-        progspace = gdb.current_progspace()
+        progspace = self.gdb.current_progspace()
         try:
             return self.prog2typeids[progspace]
         except KeyError:
@@ -68,26 +111,12 @@
         for line in open('typeids.txt'):
             member, descr = map(str.strip, line.split(None, 1))
             expr = "((char*)(&pypy_g_typeinfo.%s)) - (char*)&pypy_g_typeinfo" % member
-            offset = int(gdb.parse_and_eval(expr))
+            offset = int(self.gdb.parse_and_eval(expr))
             typeids[offset] = descr
         return typeids
 
-    def get_first_field_if(self, obj, suffix):
-        ctype = obj.type
-        field = ctype.fields()[0]
-        if field.name.endswith(suffix):
-            return obj[field.name]
-        return None
-
-    def get_super(self, obj):
-        return self.get_first_field_if(obj, '_super')
-
-    def get_gc_header(self, obj):
-        while True:
-            sup = self.get_super(obj)
-            if sup is None:
-                break
-            obj = sup
-        return self.get_first_field_if(obj, '_gcheader')
-
-RPyType() # side effects
+try:
+    import gdb
+    RPyType() # side effects
+except ImportError:
+    pass
diff --git a/pypy/tool/test/test_gdb_pypy.py b/pypy/tool/test/test_gdb_pypy.py
new file mode 100644
--- /dev/null
+++ b/pypy/tool/test/test_gdb_pypy.py
@@ -0,0 +1,62 @@
+import py
+from pypy.tool import gdb_pypy
+
+class Mock(object):
+    def __init__(self, **attrs):
+        self.__dict__.update(attrs)
+
+class Field(Mock):
+    pass
+
+class Struct(object):
+    def __init__(self, fieldnames):
+        self._fields = [Field(name=name) for name in fieldnames]
+
+    def fields(self):
+        return self._fields[:]
+
+class Value(dict):
+    def __init__(self, *args, **kwds):
+        dict.__init__(self, *args, **kwds)
+        self.type = Struct(self.keys())
+        for key, val in self.iteritems():
+            if isinstance(val, dict):
+                self[key] = Value(val)
+
+def test_mock_objects():
+    d = {'a': 1,
+         'b': 2,
+         'super': {
+            'c': 3,
+            }
+         }
+    val = Value(d)
+    assert val['a'] == 1
+    assert val['b'] == 2
+    assert isinstance(val['super'], Value)
+    assert val['super']['c'] == 3
+    fields = val.type.fields()
+    names = [f.name for f in fields]
+    assert sorted(names) == ['a', 'b', 'super']
+
+def test_find_field_with_suffix():
+    obj = Value(x_foo = 1,
+                y_bar = 2,
+                z_foobar = 3)
+    assert gdb_pypy.find_field_with_suffix(obj, 'foo') == 1
+    assert gdb_pypy.find_field_with_suffix(obj, 'foobar') == 3
+    py.test.raises(KeyError, "gdb_pypy.find_field_with_suffix(obj, 'bar')")
+    py.test.raises(KeyError, "gdb_pypy.find_field_with_suffix(obj, 'xxx')")
+
+def test_lookup():
+    d = {'r_super': {
+            '_gcheader': {
+                'h_tid': 123,
+                }
+            },
+         'r_foo': 42,
+         }
+    obj = Value(d)
+    assert gdb_pypy.lookup(obj, 'foo') == 42
+    hdr = gdb_pypy.lookup(obj, 'gcheader')
+    assert hdr['h_tid'] == 123


More information about the pypy-commit mailing list