Ejemplo n.º 1
0
 def __init__(self, *args: Any, **kwargs: Any) -> None:
     super().__init__(*args, **kwargs)
     self.__recognizer = Recognizer(self._registered_classes)
Ejemplo n.º 2
0
 def __init__(self, *args: Any, **kwargs: Any) -> None:
     """Create a Loader."""
     super().__init__(*args, **kwargs)
     self.__recognizer = Recognizer(self._registered_classes,
                                    self._additional_classes)
Ejemplo n.º 3
0
class Loader(yaml.RoundTripLoader):
    def __init__(self, *args: Any, **kwargs: Any) -> None:
        super().__init__(*args, **kwargs)
        self.__recognizer = Recognizer(self._registered_classes)

    def get_single_node(self) -> yaml.Node:
        """Hook used when loading a single document.

        This is the hook we use to hook yatiml into ruamel.yaml. It is \
        called by the yaml libray when the user uses load() to load a \
        YAML document.

        Returns:
            A processed node representing the document.
        """
        node = super().get_single_node()
        if node is not None:
            node = self.__process_node(node, type(self).document_type)
        return node

    def get_node(self) -> yaml.Node:
        """Hook used when reading a multi-document stream.

        This is the hook we use to hook yatiml into ruamel.yaml. It is \
        called by the yaml library when the user uses load_all() to \
        load multiple documents from a stream.

        Returns:
            A processed node representing the document.
        """
        node = super().get_node()
        if node is not None:
            node = self.__process_node(node, type(self).document_type)
        return node

    def __type_to_tag(self, type_: Type) -> str:
        """Convert a type to the corresponding YAML tag.

        Args:
            type_: The type to convert

        Returns:
            A string containing the YAML tag.
        """
        if type_ in scalar_type_to_tag:
            return scalar_type_to_tag[type_]

        if is_generic_list(type_):
            return 'tag:yaml.org,2002:seq'

        if is_generic_dict(type_):
            return 'tag:yaml.org,2002:map'

        if type_ in self._registered_classes.values():
            return '!{}'.format(type_.__name__)

        raise RuntimeError((
            'Unknown type {} in type_to_tag,'  # pragma: no cover
            ' please report a YAtiML bug.').format(type_))

    def __savorize(self, node: yaml.Node, expected_type: Type) -> yaml.Node:
        """Removes syntactic sugar from the node.

        This calls yatiml_savorize(), first on the class's base \
        classes, then on the class itself.

        Args:
            node: The node to modify.
            expected_type: The type to assume this type is.
        """
        logger.debug('Savorizing node assuming type {}'.format(
            expected_type.__name__))

        for base_class in expected_type.__bases__:
            if base_class in self._registered_classes.values():
                node = self.__savorize(node, base_class)

        if hasattr(expected_type, 'yatiml_savorize'):
            logger.debug('Calling {}.yatiml_savorize()'.format(
                expected_type.__name__))
            cnode = Node(node)
            expected_type.yatiml_savorize(cnode)
            node = cnode.yaml_node
        return node

    def __process_node(self, node: yaml.Node,
                       expected_type: Type) -> yaml.Node:
        """Processes a node.

        This is the main function that implements yatiml's \
        functionality. It figures out how to interpret this node \
        (recognition), then applies syntactic sugar, and finally \
        recurses to the subnodes, if any.

        Args:
            node: The node to process.
            expected_type: The type we expect this node to be.

        Returns:
            The transformed node, or a transformed copy.
        """
        logger.info('Processing node {} expecting type {}'.format(
            node, expected_type))

        # figure out how to interpret this node
        recognized_types, message = self.__recognizer.recognize(
            node, expected_type)

        if len(recognized_types) != 1:
            raise RecognitionError(message)

        recognized_type = recognized_types[0]

        # remove syntactic sugar
        logger.debug('Savorizing node {}'.format(node))
        if recognized_type in self._registered_classes.values():
            node = self.__savorize(node, recognized_type)
        logger.debug('Savorized, now {}'.format(node))

        # process subnodes
        logger.debug('Recursing into subnodes')
        if is_generic_list(recognized_type):
            if node.tag != 'tag:yaml.org,2002:seq':
                raise RecognitionError('{}{}Expected a {} here'.format(
                    node.start_mark, os.linesep, type_to_desc(expected_type)))
            for item in node.value:
                self.__process_node(item,
                                    generic_type_args(recognized_type)[0])
        elif is_generic_dict(recognized_type):
            if node.tag != 'tag:yaml.org,2002:map':
                raise RecognitionError('{}{}Expected a {} here'.format(
                    node.start_mark, os.linesep, type_to_desc(expected_type)))
            for _, value_node in node.value:
                self.__process_node(value_node,
                                    generic_type_args(recognized_type)[1])

        elif recognized_type in self._registered_classes.values():
            if (not issubclass(recognized_type, enum.Enum)
                    and not issubclass(recognized_type, str)
                    and not issubclass(recognized_type, UserString)):
                for attr_name, type_, _ in class_subobjects(recognized_type):
                    cnode = Node(node)
                    if cnode.has_attribute(attr_name):
                        subnode = cnode.get_attribute(attr_name)
                        new_subnode = self.__process_node(
                            subnode.yaml_node, type_)
                        cnode.set_attribute(attr_name, new_subnode)
        else:
            logger.debug('Not a generic class or a user-defined class, not'
                         ' recursing')

        node.tag = self.__type_to_tag(recognized_type)
        logger.debug('Finished processing node {}'.format(node))
        return node
Ejemplo n.º 4
0
class Loader(yaml.RoundTripLoader):
    """The YAtiML Loader class.

    Derive your own Loader class from this one, then add classes to it
    using :func:`add_to_loader`.

    .. warning::

        This class is **deprecated**, and will be removed in a
        future version. You should use :meth:`load_function`
        instead.
    """
    _registered_classes = None  # type: ClassVar[Dict[str, Type]]
    _additional_classes = None  # type: ClassVar[Dict[Type, str]]
    document_type = type(None)  # type: ClassVar[Type]

    def __init__(self, *args: Any, **kwargs: Any) -> None:
        """Create a Loader."""
        super().__init__(*args, **kwargs)
        self.__recognizer = Recognizer(self._registered_classes,
                                       self._additional_classes)

    def get_single_node(self) -> yaml.Node:
        """Hook used when loading a single document.

        This is the hook we use to hook yatiml into ruamel.yaml. It is
        called by the yaml libray when the user uses load() to load a
        YAML document.

        Returns:
            A processed node representing the document.
        """
        node = cast(yaml.Node, super().get_single_node())
        if node is not None:
            node = self.__process_node(node, type(self).document_type)
        return node

    def get_node(self) -> yaml.Node:
        """Hook used when reading a multi-document stream.

        This is the hook we use to hook yatiml into ruamel.yaml. It is
        called by the yaml library when the user uses load_all() to
        load multiple documents from a stream.

        Returns:
            A processed node representing the document.
        """
        node = cast(yaml.Node, super().get_node())
        if node is not None:
            node = self.__process_node(node, type(self).document_type)
        return node

    def __type_to_tag(self, type_: Type) -> str:
        """Convert a type to the corresponding YAML tag.

        Args:
            type_: The type to convert

        Returns:
            A string containing the YAML tag.
        """
        if type_ in scalar_type_to_tag:
            return scalar_type_to_tag[type_]

        if is_generic_sequence(type_):
            return 'tag:yaml.org,2002:seq'

        if is_generic_mapping(type_):
            return 'tag:yaml.org,2002:map'

        if type_ in self._registered_classes.values():
            return '!{}'.format(type_.__name__)

        if type_ in self._additional_classes:
            return self._additional_classes[type_]

        raise RuntimeError((
            'Unknown type {} in type_to_tag,'  # pragma: no cover
            ' please report a YAtiML bug.').format(type_))

    def __savorize(self, node: yaml.Node, expected_type: Type) -> yaml.Node:
        """Removes syntactic sugar from the node.

        This calls _yatiml_savorize(), first on the class's base
        classes, then on the class itself.

        Args:
            node: The node to modify.
            expected_type: The type to assume this type is.
        """
        logger.debug('Savorizing node assuming type {}'.format(
            expected_type.__name__))

        for base_class in expected_type.__bases__:
            if base_class in self._registered_classes.values():
                node = self.__savorize(node, base_class)

        if '_yatiml_savorize' in expected_type.__dict__:
            logger.debug('Calling {}._yatiml_savorize()'.format(
                expected_type.__name__))
            cnode = Node(node)
            expected_type._yatiml_savorize(cnode)
            node = cnode.yaml_node
        return node

    def __process_node(self, node: yaml.Node,
                       expected_type: Type) -> yaml.Node:
        """Processes a node.

        This is the main function that implements yatiml's
        functionality. It figures out how to interpret this node
        (recognition), then applies syntactic sugar, and finally
        recurses to the subnodes, if any.

        Args:
            node: The node to process.
            expected_type: The type we expect this node to be.

        Returns:
            The transformed node, or a transformed copy.
        """
        logger.info('Processing node {} expecting type {}'.format(
            node, expected_type))

        # figure out how to interpret this node
        recognized_types, message = self.__recognizer.recognize(
            node, expected_type)

        if len(recognized_types) != 1:
            raise RecognitionError(message)

        recognized_type = next(iter(recognized_types))

        # remove syntactic sugar
        logger.debug('Savorizing node {}'.format(node))
        if recognized_type in self._registered_classes.values():
            node = self.__savorize(node, recognized_type)
        logger.debug('Savorized, now {}'.format(node))

        # process subnodes
        logger.debug('Recursing into subnodes')
        if is_generic_sequence(recognized_type):
            if node.tag != 'tag:yaml.org,2002:seq':
                raise RecognitionError('{}{}Expected a {} here'.format(
                    node.start_mark, os.linesep, type_to_desc(expected_type)))
            node.value = [
                self.__process_node(item,
                                    generic_type_args(recognized_type)[0])
                for item in node.value
            ]

        elif is_generic_mapping(recognized_type):
            if node.tag != 'tag:yaml.org,2002:map':
                raise RecognitionError('{}{}Expected a {} here'.format(
                    node.start_mark, os.linesep, type_to_desc(expected_type)))
            node.value = [
                (self.__process_node(key_node,
                                     generic_type_args(recognized_type)[0]),
                 self.__process_node(value_node,
                                     generic_type_args(recognized_type)[1]))
                for key_node, value_node in node.value
            ]

        elif recognized_type in self._registered_classes.values():
            if (not issubclass(recognized_type, enum.Enum)
                    and not is_string_like(recognized_type)):
                for attr_name, type_, _ in class_subobjects(recognized_type):
                    cnode = Node(node)
                    if cnode.has_attribute(attr_name):
                        subnode = cnode.get_attribute(attr_name)
                        new_subnode = self.__process_node(
                            subnode.yaml_node, type_)
                        cnode.set_attribute(attr_name, new_subnode)
        else:
            logger.debug('Not a generic class or a user-defined class, not'
                         ' recursing')

        if recognized_type is Any:
            strip_tags(self, node)
        else:
            node.tag = self.__type_to_tag(recognized_type)
        logger.debug('Finished processing node {}'.format(node))
        return node
Ejemplo n.º 5
0
def unknown_sequence_node():
    ynode = yaml.SequenceNode('tag:yaml.org,2002:seq', [])
    return yatiml.UnknownNode(Recognizer({}), ynode)
Ejemplo n.º 6
0
def unknown_scalar_node():
    ynode = yaml.ScalarNode('tag:yaml.org,2002:int', '23')
    return yatiml.UnknownNode(Recognizer({}), ynode)
Ejemplo n.º 7
0
def unknown_node(yaml_node):
    return yatiml.UnknownNode(Recognizer({}), yaml_node)
Ejemplo n.º 8
0
def recognizer() -> Recognizer:
    return Recognizer({}, {})