Source code for dcar.signature
"""D-Bus type signature."""
from collections import Counter
from . import marshal
from .const import MAX_SIGNATURE_LEN, MAX_NESTING_DEPTH
from .errors import SignatureError
__all__ = ['Signature']
[docs]class Signature:
"""A signature.
The signature string will be parsed into a list of complete types.
Each complete type is a tuple with the fist element being its
signature. The second element is ``None`` for all basic
(i.e. fixed and string-like) types, an empty list for variants,
and a list of complete types for arrays, structs, and dict entries.
A :class:`Signature` object can be used as an iterator which yields
tuples of complete types.
:param str sig: D-Bus type signature
:raises ~dcar.SignatureError: if there is a problem with the signature
"""
def __init__(self, sig):
if not isinstance(sig, str):
raise SignatureError('must be of type str, not %s' %
sig.__class__.__name__)
if len(sig) > MAX_SIGNATURE_LEN:
raise SignatureError('too long: %d > %d' %
(len(sig), MAX_SIGNATURE_LEN))
self._string = sig
counter = Counter()
self._data = _parse_signature(list(sig), counter)
if counter['r'] or counter['e']:
raise SignatureError('unclosed: struct %d, dict entry %d' %
(counter['r'], counter['e']))
def __len__(self):
return len(self._data)
def __iter__(self):
return iter(self._data)
def __str__(self):
return self._string
def __repr__(self):
return repr(self._data)
def _parse_signature(sig, counter, container=None):
if counter['a'] > MAX_NESTING_DEPTH or counter['r'] > MAX_NESTING_DEPTH:
raise SignatureError('depth: array %d, struct %d' %
(counter['a'], counter['r']))
ct_lst = [] # list of complete types
while sig:
token = sig.pop(0)
if token == '(':
counter['r'] += 1
lst = _parse_signature(sig, counter, 'r')
if not lst:
raise SignatureError('struct must have at least 1 element')
ct_lst.append(('r', lst))
elif token == '{':
if container != 'a':
raise SignatureError('dict entry outside array')
counter['e'] += 1
lst = _parse_signature(sig, counter, 'e')
if len(lst) != 2:
raise SignatureError('dict entry must have 2 elements')
if lst[0][1] is not None:
raise SignatureError('dict entry key must be basic type')
ct_lst.append(('e', lst))
elif token == 'a':
counter['a'] += 1
lst = _parse_signature(sig, counter, 'a')
if not lst:
raise SignatureError('array without element type')
ct_lst.append((token, lst))
elif token == 'v':
ct_lst.append((token, []))
elif token in marshal.type_codes:
ct_lst.append((token, None))
elif container == 'r' and token == ')':
counter['r'] -= 1
break
elif container == 'e' and token == '}':
counter['e'] -= 1
break
else:
raise SignatureError('unexpected token: %r (%r %r)' %
(token, sig, ct_lst))
if container == 'a':
counter['a'] -= 1
break
return ct_lst