[PATCH] Allow nested python pretty printers.

andrew
2011-08-16T22:23:35Z

From: Andrew Oakley <andrew@ado.is-a-geek.net>

I don't think this is quite ready to commit - MI support needs to be added and
documentation needs to be updated. I thought it was sensible to post it now
though so I can deal with any feedback and so that others don't find the need
to implement the same thing again.

Will resend to gdb-patches when I think it's ready.

By allowing the children iterator of pretty printers to return more
pretty printers multi levels of display can be created.

Most of the changes here are simple refactoring to make the desired APIs
avaliable:
- try_convert_value_from_python trys to convert a python PyObject to a
gdb struct value like convert_value_from_python but does not raise an
error if the PyObject type was not valid.
- run_pretty_printer performs all of the steps to print a value given a
pretty printer rather having this split between
apply_val_pretty_printer and print_children.

---
gdb/python/py-prettyprint.c | 69 +++++++++++++++++----------
gdb/python/py-value.c | 50 ++++++++++++-------
gdb/python/python-internal.h | 1 +
gdb/testsuite/gdb.python/py-prettyprint.c | 5 ++
gdb/testsuite/gdb.python/py-prettyprint.exp | 2 +
gdb/testsuite/gdb.python/py-prettyprint.py | 23 +++++++++
6 files changed, 107 insertions(+), 43 deletions(-)

diff --git a/gdb/python/py-prettyprint.c b/gdb/python/py-prettyprint.c
index b3660de..df71fdf 100644
--- a/gdb/python/py-prettyprint.c
+++ b/gdb/python/py-prettyprint.c
@@ -458,20 +458,31 @@ push_dummy_python_frame (void)
return (PyObject *) frame;
}

-/* Helper for apply_val_pretty_printer that formats children of the
- printer, if any exist. If is_py_none is true, then nothing has
- been printed by to_string, and format output accordingly. */
+/* Helper for apply_val_pretty_printer that runs a pretty printer,
+ including formattin of the children if any exist. */
static void
-print_children (PyObject *printer, const char *hint,
- struct ui_file *stream, int recurse,
- const struct value_print_options *options,
- const struct language_defn *language,
- int is_py_none)
+run_pretty_printer (PyObject *printer, struct ui_file *stream, int recurse,
+ const struct value_print_options *options,
+ const struct language_defn *language,
+ struct gdbarch *gdbarch)
{
- int is_map, is_array, done_flag, pretty;
+ int is_py_none, is_map, is_array, done_flag, pretty;
unsigned int i;
PyObject *children, *iter, *frame;
struct cleanup *cleanups;
+ enum string_repr_result print_result;
+
+ /* If we are printing a map, we want some special formatting. */
+ char * hint = gdbpy_get_display_hint (printer);
+ cleanups = make_cleanup(free_current_contents, &hint);
+
+ /* Print the section */
+ print_result = print_string_repr (printer, hint, stream, recurse,
+ options, language, gdbarch);
+ if (print_result == string_repr_error)
+ return;
+
+ is_py_none = print_result == string_repr_none;

if (! PyObject_HasAttr (printer, gdbpy_children_cst))
return;
@@ -489,7 +500,7 @@ print_children (PyObject *printer, const char *hint,
return;
}

- cleanups = make_cleanup_py_decref (children);
+ make_cleanup_py_decref (children);

iter = PyObject_GetIter (children);
if (!iter)
@@ -634,15 +645,33 @@ print_children (PyObject *printer, const char *hint,
}
else
{
- struct value *value = convert_value_from_python (py_v);
+ struct value *value;

- if (value == NULL)
+ if (try_convert_value_from_python (py_v, &value))
+ {
+ if (value == NULL)
+ {
+ gdbpy_print_stack ();
+ error (_("Error while executing Python code."));
+ }
+ else
+ common_val_print (value, stream, recurse + 1, options,
+ language);
+ }
+ else if (PyObject_HasAttr (py_v, gdbpy_to_string_cst))
+ {
+ /* have another pretty printer to run */
+ run_pretty_printer (py_v, stream, recurse + 1, options, language,
+ gdbarch);
+ }
+ else
{
+ PyErr_Format (PyExc_TypeError,
+ _("Could not convert Python object: %s."),
+ PyString_AsString (PyObject_Str (py_v)));
gdbpy_print_stack ();
error (_("Error while executing Python code."));
}
- else
- common_val_print (value, stream, recurse + 1, options, language);
}

if (is_map && i % 2 == 0)
@@ -722,20 +751,10 @@ apply_val_pretty_printer (struct type *type, const gdb_byte *valaddr,
if (! printer || printer == Py_None)
goto done;

- /* If we are printing a map, we want some special formatting. */
- hint = gdbpy_get_display_hint (printer);
- make_cleanup (free_current_contents, &hint);
-
- /* Print the section */
- print_result = print_string_repr (printer, hint, stream, recurse,
- options, language, gdbarch);
- if (print_result != string_repr_error)
- print_children (printer, hint, stream, recurse, options, language,
- print_result == string_repr_none);
+ run_pretty_printer (printer, stream, recurse, options, language, gdbarch);

result = 1;

-
done:
if (PyErr_Occurred ())
print_stack_unless_memory_error (stream);
diff --git a/gdb/python/py-value.c b/gdb/python/py-value.c
index fc5053a..dff3be6 100644
--- a/gdb/python/py-value.c
+++ b/gdb/python/py-value.c
@@ -1074,17 +1074,19 @@ value_object_to_value (PyObject *self)
}

/* Try to convert a Python value to a gdb value. If the value cannot
- be converted, set a Python exception and return NULL. Returns a
- reference to a new value on the all_values chain. */
-
-struct value *
-convert_value_from_python (PyObject *obj)
+ be converted because it was of the incorrect type return 0. If the value
+ was of the correct type set value to a reference to a new value on the
+ all_values chain and returns 1. If a Python exception occurs attempting to
+ convert the value then value is set to NULL and 1 is returned */
+int
+try_convert_value_from_python (PyObject *obj, struct value **value)
{
- struct value *value = NULL; /* -Wall */
+ int typeok = 1;
struct cleanup *old;
volatile struct gdb_exception except;
int cmp;

+ *value = NULL;
gdb_assert (obj != NULL);

TRY_CATCH (except, RETURN_MASK_ALL)
@@ -1093,14 +1095,14 @@ convert_value_from_python (PyObject *obj)
{
cmp = PyObject_IsTrue (obj);
if (cmp >= 0)
- value = value_from_longest (builtin_type_pybool, cmp);
+ *value = value_from_longest (builtin_type_pybool, cmp);
}
else if (PyInt_Check (obj))
{
long l = PyInt_AsLong (obj);

if (! PyErr_Occurred ())
- value = value_from_longest (builtin_type_pyint, l);
+ *value = value_from_longest (builtin_type_pyint, l);
}
else if (PyLong_Check (obj))
{
@@ -1124,7 +1126,7 @@ convert_value_from_python (PyObject *obj)

ul = PyLong_AsUnsignedLongLong (obj);
if (! PyErr_Occurred ())
- value = value_from_ulongest (builtin_type_upylong, ul);
+ *value = value_from_ulongest (builtin_type_upylong, ul);
}
else
/* There's nothing we can do. */
@@ -1134,14 +1136,14 @@ convert_value_from_python (PyObject *obj)
}
}
else
- value = value_from_longest (builtin_type_pylong, l);
+ *value = value_from_longest (builtin_type_pylong, l);
}
else if (PyFloat_Check (obj))
{
double d = PyFloat_AsDouble (obj);

if (! PyErr_Occurred ())
- value = value_from_double (builtin_type_pyfloat, d);
+ *value = value_from_double (builtin_type_pyfloat, d);
}
else if (gdbpy_is_string (obj))
{
@@ -1151,32 +1153,44 @@ convert_value_from_python (PyObject *obj)
if (s != NULL)
{
old = make_cleanup (xfree, s);
- value = value_cstring (s, strlen (s), builtin_type_pychar);
+ *value = value_cstring (s, strlen (s), builtin_type_pychar);
do_cleanups (old);
}
}
else if (PyObject_TypeCheck (obj, &value_object_type))
- value = value_copy (((value_object *) obj)->value);
+ *value = value_copy (((value_object *) obj)->value);
else if (gdbpy_is_lazy_string (obj))
{
PyObject *result;

result = PyObject_CallMethodObjArgs (obj, gdbpy_value_cst, NULL);
- value = value_copy (((value_object *) result)->value);
+ *value = value_copy (((value_object *) result)->value);
}
else
- PyErr_Format (PyExc_TypeError,
- _("Could not convert Python object: %s."),
- PyString_AsString (PyObject_Str (obj)));
+ typeok = 0;
}
if (except.reason < 0)
{
PyErr_Format (except.reason == RETURN_QUIT
? PyExc_KeyboardInterrupt : PyExc_RuntimeError,
"%s", except.message);
- return NULL;
+ *value = NULL;
}

+ return typeok;
+}
+
+/* Try to convert a Python value to a gdb value. If the value cannot
+ be converted, set a Python exception and return NULL. Returns a
+ reference to a new value on the all_values chain. */
+struct value *
+convert_value_from_python (PyObject *obj)
+{
+ struct value * value;
+ if (!try_convert_value_from_python(obj, &value))
+ PyErr_Format (PyExc_TypeError,
+ _("Could not convert Python object: %s."),
+ PyString_AsString (PyObject_Str (obj)));
return value;
}

diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index 996b23b..1cfaf75 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -180,6 +180,7 @@ PyObject *inferior_to_inferior_object (struct inferior *inferior);
struct block *block_object_to_block (PyObject *obj);
struct symbol *symbol_object_to_symbol (PyObject *obj);
struct value *value_object_to_value (PyObject *self);
+int try_convert_value_from_python (PyObject *obj, struct value **value);
struct value *convert_value_from_python (PyObject *obj);
struct type *type_object_to_type (PyObject *obj);
struct symtab *symtab_object_to_symtab (PyObject *obj);
diff --git a/gdb/testsuite/gdb.python/py-prettyprint.c b/gdb/testsuite/gdb.python/py-prettyprint.c
index 30f649c..10ab025 100644
--- a/gdb/testsuite/gdb.python/py-prettyprint.c
+++ b/gdb/testsuite/gdb.python/py-prettyprint.c
@@ -48,6 +48,10 @@ struct hint_error {
int x;
};

+struct nested_printer {
+ int x;
+};
+
#ifdef __cplusplus
struct S : public s {
int zs;
@@ -236,6 +240,7 @@ main ()
struct ns ns, ns2;
struct lazystring estring, estring2;
struct hint_error hint_error;
+ struct nested_printer nested_printer;

nstype.elements = narray;
nstype.len = 0;
diff --git a/gdb/testsuite/gdb.python/py-prettyprint.exp b/gdb/testsuite/gdb.python/py-prettyprint.exp
index b0e7d62..f362cfd 100644
--- a/gdb/testsuite/gdb.python/py-prettyprint.exp
+++ b/gdb/testsuite/gdb.python/py-prettyprint.exp
@@ -102,6 +102,8 @@ proc run_lang_tests {exefile lang} {

gdb_test "print c" " = container \"container\" with 2 elements = {$nl *.0. = 23,$nl *.1. = 72$nl}"

+ gdb_test "print nested_printer" " = nested_printer_val = {$nl *child1 = child_val_1,$nl *child2 = child_val_2$nl}"
+
gdb_test "print nstype" " = {$nl *.0. = 7,$nl *.1. = 42$nl}"

gdb_test_no_output "set print pretty off"
diff --git a/gdb/testsuite/gdb.python/py-prettyprint.py b/gdb/testsuite/gdb.python/py-prettyprint.py
index 8bff3c0..be85682 100644
--- a/gdb/testsuite/gdb.python/py-prettyprint.py
+++ b/gdb/testsuite/gdb.python/py-prettyprint.py
@@ -187,6 +187,26 @@ class pp_outer:
yield 's', self.val['s']
yield 'x', self.val['x']

+class pp_nested_printer:
+ "Return a pretty printer as a child"
+
+ def __init__(self, val):
+ self.val = val
+
+ def to_string(self):
+ return 'nested_printer_val'
+
+ def children(self):
+ class pp_child:
+ def __init__(self, val):
+ self.val = val
+
+ def to_string(self):
+ return self.val
+
+ yield 'child1', pp_child('child_val_1')
+ yield 'child2', pp_child('child_val_2')
+
class MemoryErrorString:
"Raise an error"

@@ -274,6 +294,9 @@ def register_pretty_printers ():
pretty_printers_dict[re.compile ('^struct hint_error$')] = pp_hint_error
pretty_printers_dict[re.compile ('^hint_error$')] = pp_hint_error

+ pretty_printers_dict[re.compile ('^struct nested_printer$')] = pp_nested_printer
+ pretty_printers_dict[re.compile ('^nested_printer$')] = pp_nested_printer
+
pretty_printers_dict[re.compile ('^memory_error$')] = MemoryErrorString

pretty_printers_dict = {}
--
1.7.3.4