Source code for dbusapi.typeparser

# -*- coding: utf-8 -*-
# vim: ai ts=4 sts=4 et sw=4
#
# Copyright © 2016 Kaloyan Tenchov
# Copyright © 2017 Philip Withnall
#
# This library is free software; you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License as published by the Free
# Software Foundation; either version 2.1 of the License, or (at your option)
# any later version.
#
# This library is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
# details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this library.  If not, see <http://www.gnu.org/licenses/>.


"""
Module providing a `TypeParser` object for parsing D-Bus type strings into
abstract syntax trees (ASTs) representing the nested type structure.
"""


from dbusapi import types
from dbusapi.log import Log


[docs]class TypeParsingLog(Log): """Specialized Log subclass for type parsing messages""" def __init__(self): """Construct a new TypeParsingLog""" super(TypeParsingLog, self).__init__() self.register_issue_code('invalid-type') self.register_issue_code('reserved-type') self.register_issue_code('unknown-type') self.domain = 'types'
[docs]class TypeParser(object): """ Parse and validate a D-Bus type string. The D-Bus type system and type strings are explained in detail in the D-Bus specification: https://dbus.freedesktop.org/doc/dbus-specification.html#type-system and in the GVariant documentation for GLib: https://developer.gnome.org/glib/stable/glib-GVariantType.html#id-1.6.18.6.9 """ def __init__(self, signature): """ Construct a new TypeParser. Args: signature: a D-Bus type string """ self.signature = signature self._log = TypeParsingLog() self._index = 0 @staticmethod
[docs] def get_output_codes(): """Return a list of all possible output codes.""" return TypeParsingLog().issue_codes
[docs] def get_output(self): """Return a list of all logged parser messages.""" return self._log.issues
def _get_next_character(self): """Return the next character from the signature.""" if self._index < len(self.signature): character = self.signature[self._index] self._index += 1 else: character = None return character # pylint: disable=too-many-return-statements,too-many-branches def _parse_one_type(self, character): """Parse one complete type from the signature.""" basic_types = { 'y': types.Byte, 'b': types.Boolean, 'n': types.Int16, 'q': types.UInt16, 'i': types.Int32, 'u': types.UInt32, 'x': types.Int64, 't': types.UInt64, 'd': types.Double, 's': types.String, 'o': types.ObjectPath, 'g': types.Signature, 'v': types.Variant, 'h': types.UnixFD, } if character in basic_types: return basic_types[character]() elif character == "a": out_array = types.Array() character = self._get_next_character() if not character: self._log.log_issue('invalid-type', 'Incomplete array declaration.') return None one_type = self._parse_one_type(character) if not one_type: # Invalid member type - error has already been logged. return None out_array.members.append(one_type) return out_array elif character == "(": out_struct = types.Struct() while True: character = self._get_next_character() if not character or \ (character == ')' and len(out_struct.members) == 0): self._log.log_issue('invalid-type', 'Incomplete structure declaration.') return None elif character == ")": break one_type = self._parse_one_type(character) if not one_type: # Invalid member type - error has already been logged. return None out_struct.members.append(one_type) return out_struct elif character == "{": out_dict = types.DictEntry() while True: character = self._get_next_character() if not character or \ (character == '}' and len(out_dict.members) != 2): self._log.log_issue('invalid-type', 'Incomplete dictionary declaration.') return None elif character == "}": break one_type = self._parse_one_type(character) if not one_type: # Invalid member type - error has already been logged. return None if len(out_dict.members) >= 2: self._log.log_issue('invalid-type', 'Invalid dictionary declaration.') return None out_dict.members.append(one_type) return out_dict elif character in ['r', 'e', 'm', '*', '?', '@', '&', '^']: # https://dbus.freedesktop.org/doc/dbus-specification.html#idm399 self._log.log_issue('reserved-type', 'Reserved type ‘%s’ must not be used in ' 'signatures on D-Bus.' % character) return None else: self._log.log_issue('unknown-type', 'Unknown type ‘%s’.' % character) return None
[docs] def parse(self): """ Parse the type string and build an AST of the type Returns: A non-empty list of types. If parsing fails, or if the input string is empty, None is returned. """ self._log.clear() self._index = 0 if len(self.signature) == 0: self._log.log_issue('invalid-type', 'Empty type string.') out = types.TypeSignature() while True: character = self._get_next_character() if not character: break one_type = self._parse_one_type(character) if not one_type: # Invalid type break out.members.append(one_type) if self._log.issues: return None return out