Use gdb.StyleParameterSet(NAME) to create 'set style NAME ...'
and 'show style NAME ...' parameters.
+ ** The gdb.write() function now takes an additional, optional,
+ 'style' argument, which can be used to style the output.
+
*** Changes in GDB 17
* Debugging Linux programs that use x86-64 or x86-64 with 32-bit pointer
historical compatibility.
@end defun
-@defun gdb.write (string @r{[}, stream@r{]})
+@defun gdb.write (string @r{[}, stream@r{]} @r{[}, style@r{]})
Print a string to @value{GDBN}'s paginated output stream. The
optional @var{stream} determines the stream to print to. The default
stream is @value{GDBN}'s standard output stream. Possible stream
@value{GDBN}'s log stream (@pxref{Logging Output}).
@end table
+The @var{style} should be a @code{gdb.Style} object (@pxref{Styles In
+Python}), or @code{None} (the default). If @var{style} is @code{None}
+then the current style for @var{stream} will be applied to @var{text}.
+If @var{style} is a @code{gdb.Style} object, then this style is
+applied to @var{text}, after which the default output style is
+restored.
+
Writing to @code{sys.stdout} or @code{sys.stderr} will automatically
call this function and will automatically direct the output to the
relevant stream.
\f
+/* See python-internal.h. */
+
+bool
+gdbpy_is_style (PyObject *obj)
+{
+ gdb_assert (obj != nullptr);
+ return PyObject_TypeCheck (obj, &style_object_type);
+}
+
/* Return the ui_file_style for STYLEPY. If the style cannot be found,
then return an empty optional, and set a Python error. */
return style;
}
+/* See python-internal.h. */
+
+std::optional<ui_file_style>
+gdbpy_style_object_to_ui_file_style (PyObject *obj)
+{
+ gdb_assert (obj != nullptr);
+ gdb_assert (gdbpy_is_style (obj));
+
+ style_object *style_obj = (style_object *) obj;
+ return stylepy_to_style (style_obj);
+}
+
/* Implementation of gdb.Style.escape_sequence(). Return the escape
sequence to apply Style. If styling is turned off, then this returns
the empty string. Can raise an exception if a named style can no longer
frame_info_ptr frame_object_to_frame_info (PyObject *frame_obj);
struct gdbarch *arch_object_to_gdbarch (PyObject *obj);
+/* Return true if OBJ is a gdb.Style object. OBJ must not be NULL. */
+
+extern bool gdbpy_is_style (PyObject *obj);
+
+/* Return the ui_file_style from OBJ, a gdb.Style object. OBJ must not be
+ NULL.
+
+ It is possible that OBJ is a gdb.Style object, but the underlying style
+ cannot be fetched for some reason. If this happens then a Python error
+ is set and an empty optional is returned. */
+
+extern std::optional<ui_file_style>
+ gdbpy_style_object_to_ui_file_style (PyObject *obj);
+
extern PyObject *gdbpy_execute_mi_command (PyObject *self, PyObject *args,
PyObject *kw);
gdbpy_write (PyObject *self, PyObject *args, PyObject *kw)
{
const char *arg;
- static const char *keywords[] = { "text", "stream", NULL };
+ static const char *keywords[] = { "text", "stream", "style", nullptr };
int stream_type = 0;
+ PyObject *style_obj = Py_None;
- if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "s|i", keywords, &arg,
- &stream_type))
- return NULL;
+ if (!gdb_PyArg_ParseTupleAndKeywords (args, kw, "s|iO", keywords, &arg,
+ &stream_type, &style_obj))
+ return nullptr;
+
+ if (style_obj != Py_None && !gdbpy_is_style (style_obj))
+ {
+ PyErr_Format
+ (PyExc_TypeError,
+ _("'style' argument must be gdb.Style or None, not %s."),
+ Py_TYPE (style_obj)->tp_name);
+ return nullptr;
+ }
try
{
break;
}
- gdb_puts (arg, stream);
+ if (style_obj == Py_None)
+ gdb_puts (arg, stream);
+ else
+ {
+ std::optional<ui_file_style> style
+ = gdbpy_style_object_to_ui_file_style (style_obj);
+ if (!style.has_value ())
+ return nullptr;
+
+ fputs_styled (arg, style.value (), stream);
+ }
}
catch (const gdb_exception &except)
{
}
}
+# Run the command 'style-fill-v2' which fills the screen with output and
+# triggers the pagination prompt. Check that styling is applied correctly
+# to the output. This v2 command is exercising passing a style to
+# gdb.write() rather than passing the escape sequence for the style.
+proc test_pagination_v2 { } {
+ set saw_bad_color_handling false
+ set expected_restore_color ""
+ set last_color ""
+ gdb_test_multiple "style-fill-v2" "" {
+ -re "^style-fill-v2\r\n" {
+ exp_continue
+ }
+
+ -re "^(${::any_color}\033\\\[m)(${::any_color})$::str\033\\\[m" {
+ # After a continuation prompt GDB will restore the previous
+ # color, and then we immediately switch to a new color.
+ set restored_color $expect_out(1,string)
+ if { $restored_color ne $expected_restore_color } {
+ set saw_bad_color_handling true
+ }
+ set last_color $expect_out(2,string)
+ exp_continue
+ }
+
+ -re "^(${::any_color})$::str\033\\\[m" {
+ # This pattern matches printing STR in all cases that are not
+ # immediately after a pagination prompt. In this case there is
+ # a single escape sequence to set the color.
+ set last_color $expect_out(1,string)
+ exp_continue
+ }
+
+ -re "^$::pagination_prompt$" {
+ # After a pagination prompt we expect GDB to restore the last
+ # color, but this will then be disabled due to a styled
+ # gdb.write emitting a return to default style escape sequence.
+ set expected_restore_color "$last_color\033\[m"
+
+ # Send '\n' to view more output.
+ send_gdb "\n"
+ exp_continue
+ }
+
+ -re "^\r\n" {
+ # The matches the newline sent to the continuation prompt.
+ exp_continue
+ }
+
+ -re "^$::gdb_prompt $" {
+ gdb_assert { !$saw_bad_color_handling } $gdb_test_name
+ }
+ }
+}
+
foreach_with_prefix type { color style } {
foreach_with_prefix mode { write print } {
test_pagination $type $mode
}
}
+
+test_pagination_v2
write(mode, "\n")
+class StyleTester2(gdb.Command):
+ def __init__(self):
+ super().__init__("style-fill-v2", gdb.COMMAND_USER)
+
+ def invoke(self, args, from_tty):
+ str = "<" + "-" * 78 + ">"
+ for i in range(0, 20):
+ for color_name in basic_colors:
+ c = gdb.Color(color_name)
+ s = gdb.Style(foreground=c)
+ gdb.write(str, style=s)
+
+ gdb.write("\n")
+
+
ColorTester()
StyleTester()
+StyleTester2()
gdb_test "python print(output_text)" \
"^this is a unique string that is unlikely to appear elsewhere 12345" \
"check the output_text is still valid"
+
+# Test gdb.write passing in a style. Define a helper function to
+# ensure all output is flushed before we return to the prompt.
+gdb_test_multiline "create function to call gdb.write then flush" \
+ "python" "" \
+ "def write_and_flush(*args, **kwargs):" "" \
+ " gdb.write(*args, **kwargs)" "" \
+ " gdb.write(\"\\n\")" "" \
+ " gdb.flush(gdb.STDOUT)" "" \
+ "end" ""
+
+gdb_test "python write_and_flush(\"some text\")" \
+ "^some text" "unstyled text, no style passed"
+
+gdb_test "python write_and_flush(\"some text\", style=None)" \
+ "^some text" "unstyled text, pass style as None"
+
+gdb_test "python write_and_flush(\"some text\", style=filename_style)" \
+ "^\033\\\[34;41;2;23;24;27msome text\033\\\[m" \
+ "styled output, pass style by keyword"
+
+gdb_test "python write_and_flush(\"some text\", gdb.STDOUT, filename_style)" \
+ "^\033\\\[34;41;2;23;24;27msome text\033\\\[m" \
+ "styled output, pass style by position"
+
+gdb_test "python write_and_flush(\"some text\", style='filename')" \
+ [multi_line \
+ "Python Exception <class 'TypeError'>: 'style' argument must be gdb\\.Style or None, not str\\." \
+ "Error occurred in Python: 'style' argument must be gdb\\.Style or None, not str\\."]