libdrgn/python: allow specifying Object value positionally

It's annoying to have to do value= when creating objects, especially in
interactive mode. Let's allow passing in the value positionally so that
`Object(prog, "int", value=0)` becomes `Object(prog, "int", 0)`. It's
clear enough that this is creating an int with value 0.
This commit is contained in:
Omar Sandoval 2020-05-15 15:32:25 -07:00
parent 1cc771605d
commit ab876f3dbd
3 changed files with 18 additions and 6 deletions

View File

@ -611,14 +611,14 @@ class Object:
language. For example, adding two objects from a program written in C language. For example, adding two objects from a program written in C
results in an object with a type and value according to the rules of C: results in an object with a type and value according to the rules of C:
>>> Object(prog, 'unsigned long', value=2**64 - 1) + Object(prog, 'int', value=1) >>> Object(prog, 'unsigned long', 2**64 - 1) + Object(prog, 'int', 1)
Object(prog, 'unsigned long', value=0) Object(prog, 'unsigned long', value=0)
If only one operand to a binary operator is an object, the other operand If only one operand to a binary operator is an object, the other operand
will be converted to an object according to the language's rules for will be converted to an object according to the language's rules for
literals: literals:
>>> Object(prog, 'char', value=0) - 1 >>> Object(prog, 'char', 0) - 1
Object(prog, 'int', value=-1) Object(prog, 'int', value=-1)
The standard :class:`int() <int>`, :class:`float() <float>`, and The standard :class:`int() <int>`, :class:`float() <float>`, and
@ -640,9 +640,9 @@ class Object:
:param prog: The program to create this object in. :param prog: The program to create this object in.
:param type: The type of the object. If omitted, this is deduced from :param type: The type of the object. If omitted, this is deduced from
*value* according to the language's rules for literals. *value* according to the language's rules for literals.
:param value: The value of this object. See :meth:`value_()`.
:param address: The address of this object in the program. Either this or :param address: The address of this object in the program. Either this or
*value* must be given, but not both. *value* must be given, but not both.
:param value: The value of this object. See :meth:`value_()`.
:param byteorder: Byte order of the object. This should be ``'little'`` or :param byteorder: Byte order of the object. This should be ``'little'`` or
``'big'``. The default is ``None``, which indicates the program byte ``'big'``. The default is ``None``, which indicates the program byte
order. This must be ``None`` for primitive values. order. This must be ``None`` for primitive values.
@ -657,9 +657,9 @@ class Object:
self, self,
prog: Program, prog: Program,
type: Union[str, Type, None] = None, type: Union[str, Type, None] = None,
value: Any = None,
*, *,
address: Optional[int] = None, address: Optional[int] = None,
value: Any = None,
byteorder: Optional[str] = None, byteorder: Optional[str] = None,
bit_offset: Optional[int] = None, bit_offset: Optional[int] = None,
bit_field_size: Optional[int] = None, bit_field_size: Optional[int] = None,
@ -935,7 +935,7 @@ def NULL(prog: Program, type: Union[str, Type]) -> Object:
""" """
Get an object representing ``NULL`` casted to the given type. Get an object representing ``NULL`` casted to the given type.
This is equivalent to ``Object(prog, type, value=0)``. This is equivalent to ``Object(prog, type, 0)``.
:param prog: The program. :param prog: The program.
:param type: The type. :param type: The type.

View File

@ -412,7 +412,7 @@ static int DrgnObject_init(DrgnObject *self, PyObject *args, PyObject *kwds)
struct index_arg bit_field_size = { .allow_none = true, .is_none = true }; struct index_arg bit_field_size = { .allow_none = true, .is_none = true };
struct drgn_qualified_type qualified_type; struct drgn_qualified_type qualified_type;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|O$OO&O&O&O&:Object", if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|OO$O&O&O&O&:Object",
keywords, &Program_type, &prog, keywords, &Program_type, &prog,
&type_obj, &value_obj, index_converter, &type_obj, &value_obj, index_converter,
&address, byteorder_converter, &address, byteorder_converter,

View File

@ -80,6 +80,15 @@ class TestInit(ObjectTestCase):
self.prog, self.prog,
"int", "int",
) )
self.assertRaisesRegex(
ValueError,
"object cannot have address and value",
Object,
self.prog,
"int",
0,
address=0,
)
self.assertRaisesRegex( self.assertRaisesRegex(
ValueError, ValueError,
"object cannot have address and value", "object cannot have address and value",
@ -373,6 +382,9 @@ class TestReference(ObjectTestCase):
class TestValue(ObjectTestCase): class TestValue(ObjectTestCase):
def test_positional(self):
self.assertEqual(Object(self.prog, "int", 1), Object(self.prog, "int", value=1))
def test_signed(self): def test_signed(self):
obj = Object(self.prog, "int", value=-4) obj = Object(self.prog, "int", value=-4)
self.assertIs(obj.prog_, self.prog) self.assertIs(obj.prog_, self.prog)