From: Jan Vrany <jan.vrany@labware.com>
To: gdb-patches@sourceware.org
Cc: Jan Vrany <jan.vrany@labware.com>
Subject: [PATCH 1/2] gdb/python: generalize serialize_mi_result()
Date: Fri, 8 Sep 2023 22:05:03 +0100 [thread overview]
Message-ID: <20230908210504.89194-1-jan.vrany@labware.com> (raw)
This commit renames serialize_mi_result() to serialize_mi_data() and makes
it usable in different contexts than printing result of custom MI command
(hence the rename).
To do so, the check whether passed Python object is a dictionary has been
moved to the caller - at the very least, different usages require different
error messages. Also it seemed to me that py-utils.c is a better place
for its code as it is now a utility function not specific to Python MI
commands.
This is a preparation for implementing Python support for sending custom
MI async events.
---
gdb/python/py-micmd.c | 179 ++---------------------------------
gdb/python/py-utils.c | 145 ++++++++++++++++++++++++++++
gdb/python/python-internal.h | 15 +++
3 files changed, 166 insertions(+), 173 deletions(-)
diff --git a/gdb/python/py-micmd.c b/gdb/python/py-micmd.c
index 01fc6060ece..26231fc37e8 100644
--- a/gdb/python/py-micmd.c
+++ b/gdb/python/py-micmd.c
@@ -173,178 +173,6 @@ extern PyTypeObject micmdpy_object_type
static PyObject *invoke_cst;
-/* Convert KEY_OBJ into a string that can be used as a field name in MI
- output. KEY_OBJ must be a Python string object, and must only contain
- characters suitable for use as an MI field name.
-
- If KEY_OBJ is not a string, or if KEY_OBJ contains invalid characters,
- then an error is thrown. Otherwise, KEY_OBJ is converted to a string
- and returned. */
-
-static gdb::unique_xmalloc_ptr<char>
-py_object_to_mi_key (PyObject *key_obj)
-{
- /* The key must be a string. */
- if (!PyUnicode_Check (key_obj))
- {
- gdbpy_ref<> key_repr (PyObject_Repr (key_obj));
- gdb::unique_xmalloc_ptr<char> key_repr_string;
- if (key_repr != nullptr)
- key_repr_string = python_string_to_target_string (key_repr.get ());
- if (key_repr_string == nullptr)
- gdbpy_handle_exception ();
-
- gdbpy_error (_("non-string object used as key: %s"),
- key_repr_string.get ());
- }
-
- gdb::unique_xmalloc_ptr<char> key_string
- = python_string_to_target_string (key_obj);
- if (key_string == nullptr)
- gdbpy_handle_exception ();
-
- /* Predicate function, returns true if NAME is a valid field name for use
- in MI result output, otherwise, returns false. */
- auto is_valid_key_name = [] (const char *name) -> bool
- {
- gdb_assert (name != nullptr);
-
- if (*name == '\0' || !isalpha (*name))
- return false;
-
- for (; *name != '\0'; ++name)
- if (!isalnum (*name) && *name != '_' && *name != '-')
- return false;
-
- return true;
- };
-
- if (!is_valid_key_name (key_string.get ()))
- {
- if (*key_string.get () == '\0')
- gdbpy_error (_("Invalid empty key in MI result"));
- else
- gdbpy_error (_("Invalid key in MI result: %s"), key_string.get ());
- }
-
- return key_string;
-}
-
-/* Serialize RESULT and print it in MI format to the current_uiout.
- FIELD_NAME is used as the name of this result field.
-
- RESULT can be a dictionary, a sequence, an iterator, or an object that
- can be converted to a string, these are converted to the matching MI
- output format (dictionaries as tuples, sequences and iterators as lists,
- and strings as named fields).
-
- If anything goes wrong while formatting the output then an error is
- thrown.
-
- This function is the recursive inner core of serialize_mi_result, and
- should only be called from that function. */
-
-static void
-serialize_mi_result_1 (PyObject *result, const char *field_name)
-{
- struct ui_out *uiout = current_uiout;
-
- if (PyDict_Check (result))
- {
- PyObject *key, *value;
- Py_ssize_t pos = 0;
- ui_out_emit_tuple tuple_emitter (uiout, field_name);
- while (PyDict_Next (result, &pos, &key, &value))
- {
- gdb::unique_xmalloc_ptr<char> key_string
- (py_object_to_mi_key (key));
- serialize_mi_result_1 (value, key_string.get ());
- }
- }
- else if (PySequence_Check (result) && !PyUnicode_Check (result))
- {
- ui_out_emit_list list_emitter (uiout, field_name);
- Py_ssize_t len = PySequence_Size (result);
- if (len == -1)
- gdbpy_handle_exception ();
- for (Py_ssize_t i = 0; i < len; ++i)
- {
- gdbpy_ref<> item (PySequence_ITEM (result, i));
- if (item == nullptr)
- gdbpy_handle_exception ();
- serialize_mi_result_1 (item.get (), nullptr);
- }
- }
- else if (PyIter_Check (result))
- {
- gdbpy_ref<> item;
- ui_out_emit_list list_emitter (uiout, field_name);
- while (true)
- {
- item.reset (PyIter_Next (result));
- if (item == nullptr)
- {
- if (PyErr_Occurred () != nullptr)
- gdbpy_handle_exception ();
- break;
- }
- serialize_mi_result_1 (item.get (), nullptr);
- }
- }
- else
- {
- if (PyLong_Check (result))
- {
- int overflow = 0;
- gdb_py_longest val = gdb_py_long_as_long_and_overflow (result,
- &overflow);
- if (PyErr_Occurred () != nullptr)
- gdbpy_handle_exception ();
- if (overflow == 0)
- {
- uiout->field_signed (field_name, val);
- return;
- }
- /* Fall through to the string case on overflow. */
- }
-
- gdb::unique_xmalloc_ptr<char> string (gdbpy_obj_to_string (result));
- if (string == nullptr)
- gdbpy_handle_exception ();
- uiout->field_string (field_name, string.get ());
- }
-}
-
-/* Serialize RESULT and print it in MI format to the current_uiout.
-
- This function handles the top-level result initially returned from the
- invoke method of the Python command implementation. At the top-level
- the result must be a dictionary. The values within this dictionary can
- be a wider range of types. Handling the values of the top-level
- dictionary is done by serialize_mi_result_1, see that function for more
- details.
-
- If anything goes wrong while parsing and printing the MI output then an
- error is thrown. */
-
-static void
-serialize_mi_result (PyObject *result)
-{
- /* At the top-level, the result must be a dictionary. */
-
- if (!PyDict_Check (result))
- gdbpy_error (_("Result from invoke must be a dictionary"));
-
- PyObject *key, *value;
- Py_ssize_t pos = 0;
- while (PyDict_Next (result, &pos, &key, &value))
- {
- gdb::unique_xmalloc_ptr<char> key_string
- (py_object_to_mi_key (key));
- serialize_mi_result_1 (value, key_string.get ());
- }
-}
-
/* Called when the MI command is invoked. PARSE contains the parsed
command line arguments from the user. */
@@ -388,7 +216,12 @@ mi_command_py::invoke (struct mi_parse *parse) const
gdbpy_handle_exception ();
if (result != Py_None)
- serialize_mi_result (result.get ());
+ {
+ /* At the top-level, the result must be a dictionary. */
+ if (!PyDict_Check (result.get ()))
+ gdbpy_error (_("Result from invoke must be a dictionary"));
+ serialize_mi_data (result.get ());
+ }
}
/* See declaration above. */
diff --git a/gdb/python/py-utils.c b/gdb/python/py-utils.c
index d5b07a80d82..72a232f251d 100644
--- a/gdb/python/py-utils.c
+++ b/gdb/python/py-utils.c
@@ -597,3 +597,148 @@ gdbpy_fix_doc_string_indentation (gdb::unique_xmalloc_ptr<char> doc)
return doc;
}
+
+/* Convert KEY_OBJ into a string that can be used as a field name in MI
+ output. KEY_OBJ must be a Python string object, and must only contain
+ characters suitable for use as an MI field name.
+
+ If KEY_OBJ is not a string, or if KEY_OBJ contains invalid characters,
+ then an error is thrown. Otherwise, KEY_OBJ is converted to a string
+ and returned. */
+
+static gdb::unique_xmalloc_ptr<char>
+py_object_to_mi_key (PyObject *key_obj)
+{
+ /* The key must be a string. */
+ if (!PyUnicode_Check (key_obj))
+ {
+ gdbpy_ref<> key_repr (PyObject_Repr (key_obj));
+ gdb::unique_xmalloc_ptr<char> key_repr_string;
+ if (key_repr != nullptr)
+ key_repr_string = python_string_to_target_string (key_repr.get ());
+ if (key_repr_string == nullptr)
+ gdbpy_handle_exception ();
+
+ gdbpy_error (_("non-string object used as key: %s"),
+ key_repr_string.get ());
+ }
+
+ gdb::unique_xmalloc_ptr<char> key_string
+ = python_string_to_target_string (key_obj);
+ if (key_string == nullptr)
+ gdbpy_handle_exception ();
+
+ /* Predicate function, returns true if NAME is a valid field name for use
+ in MI result output, otherwise, returns false. */
+ auto is_valid_key_name = [] (const char *name) -> bool
+ {
+ gdb_assert (name != nullptr);
+
+ if (*name == '\0' || !isalpha (*name))
+ return false;
+
+ for (; *name != '\0'; ++name)
+ if (!isalnum (*name) && *name != '_' && *name != '-')
+ return false;
+
+ return true;
+ };
+
+ if (!is_valid_key_name (key_string.get ()))
+ {
+ if (*key_string.get () == '\0')
+ gdbpy_error (_("Invalid empty key in MI result"));
+ else
+ gdbpy_error (_("Invalid key in MI result: %s"), key_string.get ());
+ }
+
+ return key_string;
+}
+
+static void
+serialize_mi_data_1 (PyObject *data, const char *field_name)
+{
+ struct ui_out *uiout = current_uiout;
+
+ if (PyDict_Check (data))
+ {
+ PyObject *key, *value;
+ Py_ssize_t pos = 0;
+ ui_out_emit_tuple tuple_emitter (uiout, field_name);
+ while (PyDict_Next (data, &pos, &key, &value))
+ {
+ gdb::unique_xmalloc_ptr<char> key_string
+ (py_object_to_mi_key (key));
+ serialize_mi_data_1 (value, key_string.get ());
+ }
+ }
+ else if (PySequence_Check (data) && !PyUnicode_Check (data))
+ {
+ ui_out_emit_list list_emitter (uiout, field_name);
+ Py_ssize_t len = PySequence_Size (data);
+ if (len == -1)
+ gdbpy_handle_exception ();
+ for (Py_ssize_t i = 0; i < len; ++i)
+ {
+ gdbpy_ref<> item (PySequence_ITEM (data, i));
+ if (item == nullptr)
+ gdbpy_handle_exception ();
+ serialize_mi_data_1 (item.get (), nullptr);
+ }
+ }
+ else if (PyIter_Check (data))
+ {
+ gdbpy_ref<> item;
+ ui_out_emit_list list_emitter (uiout, field_name);
+ while (true)
+ {
+ item.reset (PyIter_Next (data));
+ if (item == nullptr)
+ {
+ if (PyErr_Occurred () != nullptr)
+ gdbpy_handle_exception ();
+ break;
+ }
+ serialize_mi_data_1 (item.get (), nullptr);
+ }
+ }
+ else
+ {
+ if (PyLong_Check (data))
+ {
+ int overflow = 0;
+ gdb_py_longest val = gdb_py_long_as_long_and_overflow (data,
+ &overflow);
+ if (PyErr_Occurred () != nullptr)
+ gdbpy_handle_exception ();
+ if (overflow == 0)
+ {
+ uiout->field_signed (field_name, val);
+ return;
+ }
+ /* Fall through to the string case on overflow. */
+ }
+
+ gdb::unique_xmalloc_ptr<char> string (gdbpy_obj_to_string (data));
+ if (string == nullptr)
+ gdbpy_handle_exception ();
+ uiout->field_string (field_name, string.get ());
+ }
+}
+
+/* See python-internal.h. */
+
+void
+serialize_mi_data (PyObject *data)
+{
+ gdb_assert (PyDict_Check (data));
+
+ PyObject *key, *value;
+ Py_ssize_t pos = 0;
+ while (PyDict_Next (data, &pos, &key, &value))
+ {
+ gdb::unique_xmalloc_ptr<char> key_string
+ (py_object_to_mi_key (key));
+ serialize_mi_data_1 (value, key_string.get ());
+ }
+}
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index 93217375cc5..3f53b0ab6f0 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -486,6 +486,21 @@ struct gdbarch *arch_object_to_gdbarch (PyObject *obj);
extern PyObject *gdbpy_execute_mi_command (PyObject *self, PyObject *args,
PyObject *kw);
+/* Serialize DATA and print it in MI format to the current_uiout.
+
+ This function handles the top-level result initially returned from the
+ invoke method of the Python command implementation. At the top-level
+ the data must be a dictionary. Caller of this function is responsible
+ for ensuring that. The values within this dictionary can
+ be a wider range of types. Handling the values of the top-level
+ dictionary is done by serialize_mi_data_1, see that function for more
+ details.
+
+ If anything goes wrong while parsing and printing the MI output then an
+ error is thrown. */
+
+extern void serialize_mi_data (PyObject *result);
+
/* Convert Python object OBJ to a program_space pointer. OBJ must be a
gdb.Progspace reference. Return nullptr if the gdb.Progspace is not
valid (see gdb.Progspace.is_valid), otherwise return the program_space
--
2.40.1
next reply other threads:[~2023-09-08 21:05 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-09-08 21:05 Jan Vrany [this message]
2023-09-08 21:05 ` [PATCH 2/2] gdb/python: implement support for sending custom MI async notifications Jan Vrany
2023-09-09 6:39 ` Eli Zaretskii
2023-09-11 12:42 ` Andrew Burgess
2023-09-11 13:02 ` Jan Vraný
2023-09-11 13:43 ` Eli Zaretskii
2023-09-11 14:22 ` Andrew Burgess
2023-09-11 14:14 ` Andrew Burgess
2023-09-12 10:58 ` Jan Vraný
2023-09-12 13:07 ` Andrew Burgess
2023-09-12 13:45 ` Jan Vraný
2023-09-12 13:53 ` Andrew Burgess
2023-09-11 14:21 ` Andrew Burgess
2023-09-11 14:24 ` Jan Vraný
2023-09-11 14:18 ` [PATCH 1/2] gdb/python: generalize serialize_mi_result() Andrew Burgess
2023-09-12 16:35 ` Tom Tromey
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20230908210504.89194-1-jan.vrany@labware.com \
--to=jan.vrany@labware.com \
--cc=gdb-patches@sourceware.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).