mirror of
https://github.com/intel/llvm.git
synced 2026-01-22 23:49:22 +08:00
[lldb] Add Pythonic API to SBStructuredData extension (#155061)
* Adds `dynamic` property to automatically convert `SBStructuredData`
instances to the associated Python type (`str`, `int`, `float`, `bool`,
`NoneType`, etc)
* Implements `__getitem__` for Pythonic array and dictionary
subscripting
* Subscripting return the result of the `dynamic` property
* Updates `__iter__` to support dictionary instances (supporting `for`
loops)
* Adds conversion to `str`, `int`, and `float`
* Adds Pythonic `bool` conversion
With these changes, these two expressions are equal:
```py
data["name"] == data.GetValueForKey("name").GetStringValue(1024)
```
Additionally did some cleanup in TestStructuredDataAPI.py.
This commit is contained in:
@@ -3,16 +3,133 @@ STRING_EXTENSION_OUTSIDE(SBStructuredData)
|
||||
%extend lldb::SBStructuredData {
|
||||
#ifdef SWIGPYTHON
|
||||
%pythoncode%{
|
||||
def __int__(self):
|
||||
return self.GetSignedInteger()
|
||||
|
||||
def __len__(self):
|
||||
'''Return the number of element in a lldb.SBStructuredData object.'''
|
||||
return self.GetSize()
|
||||
|
||||
def __iter__(self):
|
||||
'''Iterate over all the elements in a lldb.SBStructuredData object.'''
|
||||
return lldb_iter(self, 'GetSize', 'GetItemAtIndex')
|
||||
data_type = self.GetType()
|
||||
if data_type == eStructuredDataTypeArray:
|
||||
for i in range(self.GetSize()):
|
||||
yield self.GetItemAtIndex(i).dynamic
|
||||
return
|
||||
elif data_type == eStructuredDataTypeDictionary:
|
||||
keys = SBStringList()
|
||||
self.GetKeys(keys)
|
||||
return iter(keys)
|
||||
else:
|
||||
raise TypeError(f"cannot iterate {self.type_name(data_type)} type")
|
||||
|
||||
def __getitem__(self, key):
|
||||
data_type = self.GetType()
|
||||
if data_type == eStructuredDataTypeArray:
|
||||
if not isinstance(key, int):
|
||||
raise TypeError("subscript index must be an integer")
|
||||
count = len(self)
|
||||
if -count <= key < count:
|
||||
key %= count
|
||||
return self.GetItemAtIndex(key).dynamic
|
||||
raise IndexError("index out of range")
|
||||
elif data_type == eStructuredDataTypeDictionary:
|
||||
if not isinstance(key, str):
|
||||
raise TypeError("subscript key must be a string")
|
||||
return self.GetValueForKey(key).dynamic
|
||||
else:
|
||||
raise TypeError(f"cannot subscript {self.type_name(data_type)} type")
|
||||
|
||||
def __bool__(self):
|
||||
data_type = self.GetType()
|
||||
if data_type == eStructuredDataTypeInvalid:
|
||||
return False
|
||||
elif data_type in (
|
||||
eStructuredDataTypeArray,
|
||||
eStructuredDataTypeDictionary,
|
||||
):
|
||||
return self.GetSize() != 0
|
||||
elif data_type != eStructuredDataTypeGeneric:
|
||||
return bool(self.dynamic)
|
||||
else:
|
||||
raise TypeError("cannot convert generic to bool")
|
||||
|
||||
def __str__(self):
|
||||
data_type = self.GetType()
|
||||
if data_type in (
|
||||
eStructuredDataTypeString,
|
||||
eStructuredDataTypeInteger,
|
||||
eStructuredDataTypeSignedInteger,
|
||||
eStructuredDataTypeFloat,
|
||||
):
|
||||
return str(self.dynamic)
|
||||
else:
|
||||
raise TypeError(f"cannot convert {self.type_name(data_type)} to string")
|
||||
|
||||
def __int__(self):
|
||||
data_type = self.GetType()
|
||||
if data_type in (
|
||||
eStructuredDataTypeInteger,
|
||||
eStructuredDataTypeSignedInteger,
|
||||
):
|
||||
return int(self.dynamic)
|
||||
else:
|
||||
raise TypeError(f"cannot convert {self.type_name(data_type)} to int")
|
||||
|
||||
def __float__(self):
|
||||
data_type = self.GetType()
|
||||
if data_type in (
|
||||
eStructuredDataTypeFloat,
|
||||
eStructuredDataTypeInteger,
|
||||
eStructuredDataTypeSignedInteger,
|
||||
):
|
||||
return float(self.dynamic)
|
||||
else:
|
||||
raise TypeError(f"cannot convert {self.type_name(data_type)} to float")
|
||||
|
||||
@property
|
||||
def dynamic(self):
|
||||
data_type = self.GetType()
|
||||
if data_type == eStructuredDataTypeNull:
|
||||
return None
|
||||
elif data_type == eStructuredDataTypeBoolean:
|
||||
return self.GetBooleanValue()
|
||||
elif data_type == eStructuredDataTypeInteger:
|
||||
return self.GetUnsignedIntegerValue()
|
||||
elif data_type == eStructuredDataTypeSignedInteger:
|
||||
return self.GetSignedIntegerValue()
|
||||
elif data_type == eStructuredDataTypeFloat:
|
||||
return self.GetFloatValue()
|
||||
elif data_type == eStructuredDataTypeString:
|
||||
size = len(self) or 1023
|
||||
return self.GetStringValue(size + 1)
|
||||
elif data_type == eStructuredDataTypeGeneric:
|
||||
return self.GetGenericValue()
|
||||
else:
|
||||
return self
|
||||
|
||||
@staticmethod
|
||||
def type_name(t):
|
||||
if t == eStructuredDataTypeNull:
|
||||
return "null"
|
||||
elif t == eStructuredDataTypeBoolean:
|
||||
return "boolean"
|
||||
elif t == eStructuredDataTypeInteger:
|
||||
return "integer"
|
||||
elif t == eStructuredDataTypeSignedInteger:
|
||||
return "integer"
|
||||
elif t == eStructuredDataTypeFloat:
|
||||
return "float"
|
||||
elif t == eStructuredDataTypeString:
|
||||
return "string"
|
||||
elif t == eStructuredDataTypeArray:
|
||||
return "array"
|
||||
elif t == eStructuredDataTypeDictionary:
|
||||
return "dictionary"
|
||||
elif t == eStructuredDataTypeGeneric:
|
||||
return "generic"
|
||||
elif t == eStructuredDataTypeInvalid:
|
||||
return "invalid"
|
||||
else:
|
||||
raise TypeError(f"unknown structured data type: {t}")
|
||||
%}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -51,7 +51,6 @@ class TestStructuredDataAPI(TestBase):
|
||||
if not "key_float" in s.GetData():
|
||||
self.fail("FAILED: could not find key_float in description output")
|
||||
|
||||
dict_struct = lldb.SBStructuredData()
|
||||
dict_struct = example.GetValueForKey("key_dict")
|
||||
|
||||
# Tests for dictionary data type
|
||||
@@ -113,21 +112,26 @@ class TestStructuredDataAPI(TestBase):
|
||||
self.assertSuccess(example.SetFromJSON("1"))
|
||||
self.assertEqual(example.GetType(), lldb.eStructuredDataTypeInteger)
|
||||
self.assertEqual(example.GetIntegerValue(), 1)
|
||||
self.assertEqual(int(example), 1)
|
||||
|
||||
self.assertSuccess(example.SetFromJSON("4.19"))
|
||||
self.assertEqual(example.GetType(), lldb.eStructuredDataTypeFloat)
|
||||
self.assertEqual(example.GetFloatValue(), 4.19)
|
||||
self.assertEqual(float(example), 4.19)
|
||||
|
||||
self.assertSuccess(example.SetFromJSON('"Bonjour, 123!"'))
|
||||
self.assertEqual(example.GetType(), lldb.eStructuredDataTypeString)
|
||||
self.assertEqual(example.GetStringValue(42), "Bonjour, 123!")
|
||||
self.assertEqual(str(example), "Bonjour, 123!")
|
||||
|
||||
self.assertSuccess(example.SetFromJSON("true"))
|
||||
self.assertEqual(example.GetType(), lldb.eStructuredDataTypeBoolean)
|
||||
self.assertTrue(example.GetBooleanValue())
|
||||
self.assertTrue(example)
|
||||
|
||||
self.assertSuccess(example.SetFromJSON("null"))
|
||||
self.assertEqual(example.GetType(), lldb.eStructuredDataTypeNull)
|
||||
self.assertFalse(example)
|
||||
|
||||
example = lldb.SBStructuredData()
|
||||
example.SetUnsignedIntegerValue(1)
|
||||
@@ -187,38 +191,35 @@ class TestStructuredDataAPI(TestBase):
|
||||
self.assertEqual(sb_data, example_arr)
|
||||
|
||||
def invalid_struct_test(self, example):
|
||||
invalid_struct = lldb.SBStructuredData()
|
||||
invalid_struct = example.GetValueForKey("invalid_key")
|
||||
if invalid_struct.IsValid():
|
||||
self.fail("An invalid object should have been returned")
|
||||
|
||||
# Check Type API
|
||||
if not invalid_struct.GetType() == lldb.eStructuredDataTypeInvalid:
|
||||
if invalid_struct.GetType() != lldb.eStructuredDataTypeInvalid:
|
||||
self.fail("Wrong type returned: " + str(invalid_struct.GetType()))
|
||||
|
||||
def dictionary_struct_test(self, example):
|
||||
# Check API returning a valid SBStructuredData of 'dictionary' type
|
||||
dict_struct = lldb.SBStructuredData()
|
||||
dict_struct = example.GetValueForKey("key_dict")
|
||||
if not dict_struct.IsValid():
|
||||
self.fail("A valid object should have been returned")
|
||||
|
||||
# Check Type API
|
||||
if not dict_struct.GetType() == lldb.eStructuredDataTypeDictionary:
|
||||
if dict_struct.GetType() != lldb.eStructuredDataTypeDictionary:
|
||||
self.fail("Wrong type returned: " + str(dict_struct.GetType()))
|
||||
|
||||
# Check Size API for 'dictionary' type
|
||||
if not dict_struct.GetSize() == 6:
|
||||
if dict_struct.GetSize() != 6:
|
||||
self.fail("Wrong no of elements returned: " + str(dict_struct.GetSize()))
|
||||
|
||||
def string_struct_test(self, dict_struct):
|
||||
string_struct = lldb.SBStructuredData()
|
||||
string_struct = dict_struct.GetValueForKey("key_string")
|
||||
if not string_struct.IsValid():
|
||||
self.fail("A valid object should have been returned")
|
||||
|
||||
# Check Type API
|
||||
if not string_struct.GetType() == lldb.eStructuredDataTypeString:
|
||||
if string_struct.GetType() != lldb.eStructuredDataTypeString:
|
||||
self.fail("Wrong type returned: " + str(string_struct.GetType()))
|
||||
|
||||
# Check API returning 'string' value
|
||||
@@ -238,18 +239,17 @@ class TestStructuredDataAPI(TestBase):
|
||||
# Check a valid SBStructuredData containing an unsigned integer.
|
||||
# We intentionally make this larger than what an int64_t can hold but
|
||||
# still small enough to fit a uint64_t
|
||||
uint_struct = lldb.SBStructuredData()
|
||||
uint_struct = dict_struct.GetValueForKey("key_uint")
|
||||
if not uint_struct.IsValid():
|
||||
self.fail("A valid object should have been returned")
|
||||
|
||||
# Check Type API
|
||||
if not uint_struct.GetType() == lldb.eStructuredDataTypeInteger:
|
||||
if uint_struct.GetType() != lldb.eStructuredDataTypeInteger:
|
||||
self.fail("Wrong type returned: " + str(uint_struct.GetType()))
|
||||
|
||||
# Check API returning unsigned integer value
|
||||
output = uint_struct.GetUnsignedIntegerValue()
|
||||
if not output == 0xFFFFFFFF00000000:
|
||||
if output != 0xFFFFFFFF00000000:
|
||||
self.fail("wrong output: " + str(output))
|
||||
|
||||
# Calling wrong API on a SBStructuredData
|
||||
@@ -262,18 +262,17 @@ class TestStructuredDataAPI(TestBase):
|
||||
# Check a valid SBStructuredData containing an signed integer.
|
||||
# We intentionally make this smaller than what an uint64_t can hold but
|
||||
# still small enough to fit a int64_t
|
||||
sint_struct = lldb.SBStructuredData()
|
||||
sint_struct = dict_struct.GetValueForKey("key_sint")
|
||||
if not sint_struct.IsValid():
|
||||
self.fail("A valid object should have been returned")
|
||||
|
||||
# Check Type API
|
||||
if not sint_struct.GetType() == lldb.eStructuredDataTypeSignedInteger:
|
||||
if sint_struct.GetType() != lldb.eStructuredDataTypeSignedInteger:
|
||||
self.fail("Wrong type returned: " + str(sint_struct.GetType()))
|
||||
|
||||
# Check API returning signed integer value
|
||||
output = sint_struct.GetSignedIntegerValue()
|
||||
if not output == -42:
|
||||
if output != -42:
|
||||
self.fail("wrong output: " + str(output))
|
||||
|
||||
# Calling wrong API on a SBStructuredData
|
||||
@@ -283,28 +282,26 @@ class TestStructuredDataAPI(TestBase):
|
||||
self.fail("Valid string " + output + " returned for an integer object")
|
||||
|
||||
def double_struct_test(self, dict_struct):
|
||||
floating_point_struct = lldb.SBStructuredData()
|
||||
floating_point_struct = dict_struct.GetValueForKey("key_float")
|
||||
if not floating_point_struct.IsValid():
|
||||
self.fail("A valid object should have been returned")
|
||||
|
||||
# Check Type API
|
||||
if not floating_point_struct.GetType() == lldb.eStructuredDataTypeFloat:
|
||||
if floating_point_struct.GetType() != lldb.eStructuredDataTypeFloat:
|
||||
self.fail("Wrong type returned: " + str(floating_point_struct.GetType()))
|
||||
|
||||
# Check API returning 'double' value
|
||||
output = floating_point_struct.GetFloatValue()
|
||||
if not output == 2.99:
|
||||
if output != 2.99:
|
||||
self.fail("wrong output: " + str(output))
|
||||
|
||||
def bool_struct_test(self, dict_struct):
|
||||
bool_struct = lldb.SBStructuredData()
|
||||
bool_struct = dict_struct.GetValueForKey("key_bool")
|
||||
if not bool_struct.IsValid():
|
||||
self.fail("A valid object should have been returned")
|
||||
|
||||
# Check Type API
|
||||
if not bool_struct.GetType() == lldb.eStructuredDataTypeBoolean:
|
||||
if bool_struct.GetType() != lldb.eStructuredDataTypeBoolean:
|
||||
self.fail("Wrong type returned: " + str(bool_struct.GetType()))
|
||||
|
||||
# Check API returning 'bool' value
|
||||
@@ -314,17 +311,16 @@ class TestStructuredDataAPI(TestBase):
|
||||
|
||||
def array_struct_test(self, dict_struct):
|
||||
# Check API returning a valid SBStructuredData of 'array' type
|
||||
array_struct = lldb.SBStructuredData()
|
||||
array_struct = dict_struct.GetValueForKey("key_array")
|
||||
if not array_struct.IsValid():
|
||||
self.fail("A valid object should have been returned")
|
||||
|
||||
# Check Type API
|
||||
if not array_struct.GetType() == lldb.eStructuredDataTypeArray:
|
||||
if array_struct.GetType() != lldb.eStructuredDataTypeArray:
|
||||
self.fail("Wrong type returned: " + str(array_struct.GetType()))
|
||||
|
||||
# Check Size API for 'array' type
|
||||
if not array_struct.GetSize() == 2:
|
||||
if array_struct.GetSize() != 2:
|
||||
self.fail("Wrong no of elements returned: " + str(array_struct.GetSize()))
|
||||
|
||||
# Check API returning a valid SBStructuredData for different 'array'
|
||||
@@ -332,17 +328,98 @@ class TestStructuredDataAPI(TestBase):
|
||||
string_struct = array_struct.GetItemAtIndex(0)
|
||||
if not string_struct.IsValid():
|
||||
self.fail("A valid object should have been returned")
|
||||
if not string_struct.GetType() == lldb.eStructuredDataTypeString:
|
||||
if string_struct.GetType() != lldb.eStructuredDataTypeString:
|
||||
self.fail("Wrong type returned: " + str(string_struct.GetType()))
|
||||
output = string_struct.GetStringValue(5)
|
||||
if not output == "23":
|
||||
if output != "23":
|
||||
self.fail("wrong output: " + str(output))
|
||||
|
||||
string_struct = array_struct.GetItemAtIndex(1)
|
||||
if not string_struct.IsValid():
|
||||
self.fail("A valid object should have been returned")
|
||||
if not string_struct.GetType() == lldb.eStructuredDataTypeString:
|
||||
if string_struct.GetType() != lldb.eStructuredDataTypeString:
|
||||
self.fail("Wrong type returned: " + str(string_struct.GetType()))
|
||||
output = string_struct.GetStringValue(5)
|
||||
if not output == "arr":
|
||||
if output != "arr":
|
||||
self.fail("wrong output: " + str(output))
|
||||
|
||||
def test_round_trip_scalars(self):
|
||||
for original in (0, 11, -1, 0.0, 4.5, -0.25, "", "dirk", True, False):
|
||||
constructor = type(original)
|
||||
data = lldb.SBStructuredData()
|
||||
data.SetFromJSON(json.dumps(original))
|
||||
round_tripped = constructor(data)
|
||||
self.assertEqual(round_tripped, original)
|
||||
|
||||
def test_dynamic(self):
|
||||
for original in (0, 11, -1, 0.0, 4.5, -0.25, "", "dirk", True, False):
|
||||
data = lldb.SBStructuredData()
|
||||
data.SetFromJSON(json.dumps(original))
|
||||
self.assertEqual(data.dynamic, original)
|
||||
|
||||
def test_round_trip_string(self):
|
||||
# No 0.0, it inherently does not round trip.
|
||||
for original in (0, 11, -1, 4.5, -0.25, "", "dirk"):
|
||||
data = lldb.SBStructuredData()
|
||||
data.SetFromJSON(json.dumps(original))
|
||||
self.assertEqual(str(data), str(original))
|
||||
|
||||
def test_round_trip_int(self):
|
||||
for original in (0, 11, -1):
|
||||
data = lldb.SBStructuredData()
|
||||
data.SetFromJSON(json.dumps(original))
|
||||
self.assertEqual(int(data), int(original))
|
||||
|
||||
def test_round_trip_float(self):
|
||||
for original in (0, 11, -1, 0.0, 4.5, -0.25):
|
||||
data = lldb.SBStructuredData()
|
||||
data.SetFromJSON(json.dumps(original))
|
||||
self.assertEqual(float(data), float(original))
|
||||
|
||||
def test_round_trip_bool(self):
|
||||
for original in (0, 11, -1, 0.0, 4.5, -0.25, "0.0", "4.5", "-0.25"):
|
||||
data = lldb.SBStructuredData()
|
||||
data.SetFromJSON(json.dumps(original))
|
||||
self.assertEqual(bool(data), bool(original))
|
||||
|
||||
for original in ([], {}, [1], {1: 1}):
|
||||
data = lldb.SBStructuredData()
|
||||
data.SetFromJSON(json.dumps(original))
|
||||
self.assertEqual(bool(data), bool(original))
|
||||
|
||||
def test_assert_false(self):
|
||||
self.assertFalse(lldb.SBStructuredData())
|
||||
for original in ("0", "0.0", '""', "[]", "{}"):
|
||||
data = lldb.SBStructuredData()
|
||||
data.SetFromJSON(original)
|
||||
self.assertFalse(data)
|
||||
|
||||
def test_iterate_array(self):
|
||||
array = [0, 1, 2]
|
||||
data = lldb.SBStructuredData()
|
||||
data.SetFromJSON(json.dumps(array))
|
||||
for value in data:
|
||||
self.assertEqual(value, array.pop(0))
|
||||
|
||||
def test_iterate_dictionary(self):
|
||||
dictionary = {"0": 0, "1": 1, "2": 2}
|
||||
keys = set(dictionary.keys())
|
||||
data = lldb.SBStructuredData()
|
||||
data.SetFromJSON(json.dumps(dictionary))
|
||||
for key in data:
|
||||
self.assertIn(key, keys)
|
||||
keys.remove(key)
|
||||
|
||||
def test_getitem_array(self):
|
||||
array = [1, 2, 3]
|
||||
data = lldb.SBStructuredData()
|
||||
data.SetFromJSON(json.dumps(array))
|
||||
for i in range(len(array)):
|
||||
self.assertEqual(data[i], array[i])
|
||||
|
||||
def test_getitem_dictionary(self):
|
||||
dictionary = {"one": 1, "two": 2, "three": 3}
|
||||
data = lldb.SBStructuredData()
|
||||
data.SetFromJSON(json.dumps(dictionary))
|
||||
for key in dictionary:
|
||||
self.assertEqual(data[key], dictionary[key])
|
||||
|
||||
Reference in New Issue
Block a user