fix: void validate arguments/properties name (#358)

This commit is contained in:
black_desk
2025-01-16 04:18:37 +08:00
committed by GitHub
parent e7750caed5
commit f58f1a6466
6 changed files with 89 additions and 27 deletions

View File

@@ -260,7 +260,11 @@ class MessageBus(BaseMessageBus):
return await future return await future
async def introspect( async def introspect(
self, bus_name: str, path: str, timeout: float = 30.0 self,
bus_name: str,
path: str,
timeout: float = 30.0,
validate_property_names: bool = True,
) -> intr.Node: ) -> intr.Node:
"""Get introspection data for the node at the given path from the given """Get introspection data for the node at the given path from the given
bus name. bus name.
@@ -274,6 +278,8 @@ class MessageBus(BaseMessageBus):
:type path: str :type path: str
:param timeout: The timeout to introspect. :param timeout: The timeout to introspect.
:type timeout: float :type timeout: float
:param validate_property_names: Whether to validate property names or not.
:type validate_property_names: bool
:returns: The introspection data for the name at the path. :returns: The introspection data for the name at the path.
:rtype: :class:`Node <dbus_fast.introspection.Node>` :rtype: :class:`Node <dbus_fast.introspection.Node>`
@@ -295,6 +301,7 @@ class MessageBus(BaseMessageBus):
path, path,
partial(self._reply_handler, future), partial(self._reply_handler, future),
check_callback_type=False, check_callback_type=False,
validate_property_names=validate_property_names,
) )
timer_handle = self._loop.call_later( timer_handle = self._loop.call_later(

View File

@@ -34,9 +34,6 @@ class Arg:
direction: Optional[list[ArgDirection]] = None, direction: Optional[list[ArgDirection]] = None,
name: Optional[str] = None, name: Optional[str] = None,
): ):
if name is not None:
assert_member_name_valid(name)
type_ = None type_ = None
if type(signature) is SignatureType: if type(signature) is SignatureType:
type_ = signature type_ = signature
@@ -245,8 +242,10 @@ class Property:
name: str, name: str,
signature: str, signature: str,
access: PropertyAccess = PropertyAccess.READWRITE, access: PropertyAccess = PropertyAccess.READWRITE,
validate: bool = True,
): ):
assert_member_name_valid(name) if validate:
assert_member_name_valid(name)
tree = get_signature_tree(signature) tree = get_signature_tree(signature)
if len(tree.types) != 1: if len(tree.types) != 1:
@@ -259,7 +258,7 @@ class Property:
self.access = access self.access = access
self.type = tree.types[0] self.type = tree.types[0]
def from_xml(element): def from_xml(element, validate: bool = True):
"""Convert an :class:`xml.etree.ElementTree.Element` to a :class:`Property`. """Convert an :class:`xml.etree.ElementTree.Element` to a :class:`Property`.
The element must be valid DBus introspection XML for a ``property``. The element must be valid DBus introspection XML for a ``property``.
@@ -279,7 +278,7 @@ class Property:
if not signature: if not signature:
raise InvalidIntrospectionError('properties must have a "type" attribute') raise InvalidIntrospectionError('properties must have a "type" attribute')
return Property(name, signature, access) return Property(name, signature, access, validate=validate)
def to_xml(self) -> ET.Element: def to_xml(self) -> ET.Element:
"""Convert this :class:`Property` into an :class:`xml.etree.ElementTree.Element`.""" """Convert this :class:`Property` into an :class:`xml.etree.ElementTree.Element`."""
@@ -324,7 +323,9 @@ class Interface:
self.properties = properties if properties is not None else [] self.properties = properties if properties is not None else []
@staticmethod @staticmethod
def from_xml(element: ET.Element) -> "Interface": def from_xml(
element: ET.Element, validate_property_names: bool = True
) -> "Interface":
"""Convert a :class:`xml.etree.ElementTree.Element` into a """Convert a :class:`xml.etree.ElementTree.Element` into a
:class:`Interface`. :class:`Interface`.
@@ -348,7 +349,9 @@ class Interface:
elif child.tag == "signal": elif child.tag == "signal":
interface.signals.append(Signal.from_xml(child)) interface.signals.append(Signal.from_xml(child))
elif child.tag == "property": elif child.tag == "property":
interface.properties.append(Property.from_xml(child)) interface.properties.append(
Property.from_xml(child, validate=validate_property_names)
)
return interface return interface
@@ -409,7 +412,9 @@ class Node:
self.is_root = is_root self.is_root = is_root
@staticmethod @staticmethod
def from_xml(element: ET.Element, is_root: bool = False): def from_xml(
element: ET.Element, is_root: bool = False, validate_property_names: bool = True
) -> "Node":
"""Convert an :class:`xml.etree.ElementTree.Element` to a :class:`Node`. """Convert an :class:`xml.etree.ElementTree.Element` to a :class:`Node`.
The element must be valid DBus introspection XML for a ``node``. The element must be valid DBus introspection XML for a ``node``.
@@ -418,6 +423,8 @@ class Node:
:type element: :class:`xml.etree.ElementTree.Element` :type element: :class:`xml.etree.ElementTree.Element`
:param is_root: Whether this is the root node :param is_root: Whether this is the root node
:type is_root: bool :type is_root: bool
:param validate_property_names: Whether to validate property names or not
:type validate_property_names: bool
:raises: :raises:
- :class:`InvalidIntrospectionError <dbus_fast.InvalidIntrospectionError>` - If the XML tree is not valid introspection data. - :class:`InvalidIntrospectionError <dbus_fast.InvalidIntrospectionError>` - If the XML tree is not valid introspection data.
@@ -426,20 +433,30 @@ class Node:
for child in element: for child in element:
if child.tag == "interface": if child.tag == "interface":
node.interfaces.append(Interface.from_xml(child)) node.interfaces.append(
Interface.from_xml(
child, validate_property_names=validate_property_names
)
)
elif child.tag == "node": elif child.tag == "node":
node.nodes.append(Node.from_xml(child)) node.nodes.append(
Node.from_xml(
child, validate_property_names=validate_property_names
)
)
return node return node
@staticmethod @staticmethod
def parse(data: str) -> "Node": def parse(data: str, validate_property_names: bool = True) -> "Node":
"""Parse XML data as a string into a :class:`Node`. """Parse XML data as a string into a :class:`Node`.
The string must be valid DBus introspection XML. The string must be valid DBus introspection XML.
:param data: The XMl string. :param data: The XMl string.
:type data: str :type data: str
:param validate_property_names: Whether to validate property names or not
:type validate_property_names: bool
:raises: :raises:
- :class:`InvalidIntrospectionError <dbus_fast.InvalidIntrospectionError>` - If the string is not valid introspection data. - :class:`InvalidIntrospectionError <dbus_fast.InvalidIntrospectionError>` - If the string is not valid introspection data.
@@ -450,7 +467,9 @@ class Node:
'introspection data must have a "node" for the root element' 'introspection data must have a "node" for the root element'
) )
return Node.from_xml(element, is_root=True) return Node.from_xml(
element, is_root=True, validate_property_names=validate_property_names
)
def to_xml(self) -> ET.Element: def to_xml(self) -> ET.Element:
"""Convert this :class:`Node` into an :class:`xml.etree.ElementTree.Element`.""" """Convert this :class:`Node` into an :class:`xml.etree.ElementTree.Element`."""

View File

@@ -262,6 +262,7 @@ class BaseMessageBus:
path: str, path: str,
callback: Callable[[Optional[intr.Node], Optional[Exception]], None], callback: Callable[[Optional[intr.Node], Optional[Exception]], None],
check_callback_type: bool = True, check_callback_type: bool = True,
validate_property_names: bool = True,
) -> None: ) -> None:
"""Get introspection data for the node at the given path from the given """Get introspection data for the node at the given path from the given
bus name. bus name.
@@ -276,6 +277,10 @@ class BaseMessageBus:
:param callback: A callback that will be called with the introspection :param callback: A callback that will be called with the introspection
data as a :class:`Node <dbus_fast.introspection.Node>`. data as a :class:`Node <dbus_fast.introspection.Node>`.
:type callback: :class:`Callable` :type callback: :class:`Callable`
:param check_callback_type: Whether to check callback type or not.
:type check_callback_type: bool
:param validate_property_names: Whether to validate property names or not.
:type validate_property_names: bool
:raises: :raises:
- :class:`InvalidObjectPathError <dbus_fast.InvalidObjectPathError>` - If the given object path is not valid. - :class:`InvalidObjectPathError <dbus_fast.InvalidObjectPathError>` - If the given object path is not valid.
@@ -287,7 +292,9 @@ class BaseMessageBus:
def reply_notify(reply: Optional[Message], err: Optional[Exception]) -> None: def reply_notify(reply: Optional[Message], err: Optional[Exception]) -> None:
try: try:
BaseMessageBus._check_method_return(reply, err, "s") BaseMessageBus._check_method_return(reply, err, "s")
result = intr.Node.parse(reply.body[0]) # type: ignore[union-attr] result = intr.Node.parse(
reply.body[0], validate_property_names=validate_property_names
) # type: ignore[union-attr]
except Exception as e: except Exception as e:
callback(None, e) callback(None, e)
return return

View File

@@ -0,0 +1,10 @@
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node name="/com/example/sample_object0">
<interface name="com.example.SampleInterface0">
<method name="Frobate">
<arg name="0-foo-bar" type="i" direction="in"/>
</method>
<property name="0-baz-qux" type="y" access="write"/>
</interface>
</node>

View File

@@ -5,7 +5,7 @@
<method name="Frobate"> <method name="Frobate">
<arg name="foo" type="i" direction="in"/> <arg name="foo" type="i" direction="in"/>
<arg name="bar" type="s" direction="out"/> <arg name="bar" type="s" direction="out"/>
<arg name="baz" type="a{us}" direction="out"/> <arg name="0-baz" type="a{us}" direction="out"/>
<annotation name="org.freedesktop.DBus.Deprecated" value="true"/> <annotation name="org.freedesktop.DBus.Deprecated" value="true"/>
</method> </method>
<method name="Bazify"> <method name="Bazify">
@@ -16,11 +16,11 @@
<arg name="bar" type="(iiav)" direction="in"/> <arg name="bar" type="(iiav)" direction="in"/>
</method> </method>
<signal name="Changed"> <signal name="Changed">
<arg name="new_value" type="b"/> <arg name="0-new_value" type="b"/>
</signal> </signal>
<signal name="ChangedMulti"> <signal name="ChangedMulti">
<arg name="new_value1" type="b"/> <arg name="new_value1" type="b"/>
<arg name="new_value2" type="y"/> <arg name="0-new_value2" type="y"/>
</signal> </signal>
<property name="Bar" type="y" access="write"/> <property name="Bar" type="y" access="write"/>
</interface> </interface>

View File

@@ -1,14 +1,33 @@
import os import os
from dbus_fast import ArgDirection, PropertyAccess, SignatureType from dbus_fast import (
ArgDirection,
PropertyAccess,
SignatureType,
InvalidMemberNameError,
)
from dbus_fast import introspection as intr from dbus_fast import introspection as intr
with open(f"{os.path.dirname(__file__)}/data/introspection.xml") as f: with open(f"{os.path.dirname(__file__)}/data/strict-introspection.xml") as f:
example_data = f.read() strict_data = f.read()
with open(f"{os.path.dirname(__file__)}/data/sloppy-introspection.xml") as f:
sloppy_data = f.read()
def test_example_introspection_from_xml(): def test_introspection_from_xml_sloppy():
node = intr.Node.parse(example_data) intr.Node.parse(sloppy_data, validate_property_names=False)
def test_introspection_from_xml_strict():
try:
node = intr.Node.parse(sloppy_data)
except InvalidMemberNameError:
pass
else:
assert False, "Expected an AssertionError"
node = intr.Node.parse(strict_data)
assert len(node.interfaces) == 1 assert len(node.interfaces) == 1
interface = node.interfaces[0] interface = node.interfaces[0]
@@ -61,12 +80,12 @@ def test_example_introspection_from_xml():
assert len(changed.args) == 1 assert len(changed.args) == 1
new_value = changed.args[0] new_value = changed.args[0]
assert type(new_value) is intr.Arg assert type(new_value) is intr.Arg
assert new_value.name == "new_value" assert new_value.name == "0-new_value"
assert new_value.signature == "b" assert new_value.signature == "b"
def test_example_introspection_to_xml(): def test_example_introspection_to_xml():
node = intr.Node.parse(example_data) node = intr.Node.parse(strict_data)
tree = node.to_xml() tree = node.to_xml()
assert tree.tag == "node" assert tree.tag == "node"
assert tree.attrib.get("name") == "/com/example/sample_object0" assert tree.attrib.get("name") == "/com/example/sample_object0"
@@ -94,7 +113,7 @@ def test_example_introspection_to_xml():
arg = signal[0] arg = signal[0]
assert arg.tag == "arg" assert arg.tag == "arg"
assert arg.attrib.get("name") == "new_value" assert arg.attrib.get("name") == "0-new_value"
assert arg.attrib.get("type") == "b" assert arg.attrib.get("type") == "b"
signal = interface[4] signal = interface[4]
@@ -109,7 +128,7 @@ def test_example_introspection_to_xml():
arg = signal[1] arg = signal[1]
assert arg.tag == "arg" assert arg.tag == "arg"
assert arg.attrib.get("name") == "new_value2" assert arg.attrib.get("name") == "0-new_value2"
assert arg.attrib.get("type") == "y" assert arg.attrib.get("type") == "y"
prop = interface[5] prop = interface[5]