Source code for ska_ser_skallop.connectors.remoting.tangobridge.parsing

"""Parse gql rest based results."""
from typing import Any, Dict

# TODO replace this module with python 3.9's typed dict and the util/dictionary_validation


[docs]class ParseError(Exception): """Signal an error occurred in parsing the data returned from gql query.""" pass
[docs]class Parser: """Object use for parsing input data in a piece wise and recursive fashion. The :py:meth:`parse` method results in the object traversing in a chain like fashion within the dictionary structure until the data being pointed to is of type string (meaning it has reached the end of the path). For example: .. code-block:: python data = { 'this': { 'is': { 'a': { 'very': { 'long': { 'chain': 'This is the end of the chain' } } } } } } parser = Parser(data) assert_that( parser.parse('this').parse('is').parse('a').parse('very').parse('long').parse('chain').value )is_equal_to('This is the end of the chain') Each parse method has the option of a given default which means if there does not exist a given key, the default object will be used. Otherwise a ParseError will be raised. """ def __init__(self, data: Dict) -> None: """Initialise the object. :param data: The input data to be parsed :type data: Dict """ self.data = data self.value: Any = "" @property def value_as_singleton(self) -> Any: """Return the first item in the parsed result (assumes result is a list). This is usefull in cases whereby it is known beforehand that the result will always be a list with only one element in it. :return: The inner value from the list """ assert isinstance(self.value, list) if self.value == []: return None return self.value[0]
[docs] def parse(self, key: str, default=None) -> "Parser": """Parse the data by traversing to the next item returned from the key. If the result is of type string then the value is set to that result, otherwise the data is updated to point to the returned result for parsing further. :param key: The key to lookup the value in the dictionary :type key: str :param default: The default value to used if key does not exists if none is given then a missing key will result in a parse error, defaults to None :type default: Any :raises ParseError: When the given key does not exist and no default value was given. :return: Itself so as to allow for chaining a parse execution """ if key in self.data.keys(): result = self.data[key] else: if default is None: parse_message = f"Unable to load {key} from {self.data}" raise ParseError(parse_message) # else set the result to given default result = default if not isinstance(result, dict): self.value = result else: self.data = result return self
[docs]def parse(data: Dict, key: str, default=None) -> Parser: """Parse a given input dictionary. E.g. : .. code-block:: python data = { 'this': { 'is': { 'a': { 'very': { 'long': { 'chain': 'This is the end of the chain' } } } } } } assert_that( parse('this').parse('is').parse('a').parse('very').parse('long').parse('chain').value )is_equal_to('This is the end of the chain') :param data: The input data :type data: Dict :param key: The key pointing to the first item for traversing the parsing. :type key: str :param default: Whether to return a default value if the key does not exist, defaults to None :type default: Any, optional :return: A Parser object that can be traversed further of read to get the actual value from """ return Parser(data).parse(key, default)