예제 #1
0
파일: context.py 프로젝트: subokita/Aglyph
    def _parse_dict(self, dict_element):
        """Return a :obj:`dict` evaluator object parsed from
        *dict_element*.

        :arg xml.etree.ElementTree.Element dict_element:
           a ``<dict>`` element
        :rtype:
           :class:`aglyph.component.Evaluator`

        .. note::
           The evaluator returned by this method produces a new
           :obj:`dict` object each time it is called.

        """
        # a list of 2-tuples, (key, value), used to initialize a dictionary
        items = []
        for element in list(dict_element):
            if element.tag != "item":
                raise AglyphError("unexpected element: dict/%s" % element.tag)

            children = list(element)
            child_tags = [child.tag for child in children]
            if child_tags == ["key", "value"]:
                key_element, value_element = children
            else:
                raise AglyphError("expected item/key, item/value; found %s" %
                                  ", ".join("item/%s" % ctag
                                            for ctag in child_tags))

            items.append((self._unserialize_element_value(key_element),
                          self._unserialize_element_value(value_element)))

        return evaluate(dict, items)
예제 #2
0
파일: context.py 프로젝트: subokita/Aglyph
    def _process_value_element(self, value_element, parent_tag):
        """Create a usable Python object from *value_element*.

        :arg xml.etree.ElementTree.Element value_element:
           an element that describes a value
        :arg str parent_tag:
           the name of the *value_element* parent element
        :return:
           a Python object that is the value of *value_element*

        *value_element* must be a ``<False />``, ``<True />``,
        ``<None />``, ``<bytes>``, ``<str>``, ``<unicode>``, ``<int>``,
        ``<float>``, ``<tuple>``, ``<list>``, ``<dict>``, or ``<set>``
        element.

        This method will return one of the following types, dependent
        upon the element:

        * an object of a Python built-in type
        * a Python built-in constant
        * an Aglyph :class:`Reference`
        * an Aglyph :class:`Evaluator`

        """
        parse_value = getattr(self, "_parse_%s" % value_element.tag, None)
        if parse_value is None:
            raise AglyphError("unexpected element: %s/%s" %
                              (parent_tag, value_element.tag))
        return parse_value(value_element)
예제 #3
0
파일: context.py 프로젝트: subokita/Aglyph
    def _unserialize_element_value(self, valuecontainer_element):
        """Return the appropriate object, value, Aglyph reference, or
        Aglyph evaluator for *element*.

        :arg xml.etree.ElementTree.Element valuecontainer_element:
           an element with a single child element that describes a value
        :return:
           the runtime object that is the result of processing
           *valuecontainer_element*
        :rtype:
           an object of a Python built-in type, a Python built-in
           constant, a :class:`Reference`, or an :class:`Evaluator`

        *valuecontainer_element* must be an ``<arg>``, ``<attribute>``,
        ``<key>``, or ``<value>`` element.

        """
        component_id = valuecontainer_element.get("reference")
        if component_id is not None:
            return ref(component_id)

        children = list(valuecontainer_element)
        if len(children) != 1:
            vtag = valuecontainer_element.tag
            raise AglyphError(
                "<%s> must contain exactly one child element; found %s" %
                (vtag, ", ".join(
                    "%s/%s" % (vtag, c.tag)
                    for c in children) if children else "no children"))

        return self._process_value_element(children[0],
                                           valuecontainer_element.tag)
예제 #4
0
 def test_register_fails_on_nonimportable_object(self):
     builder = self._builder_type(
         _MockContext(), dummy.ModuleClass.NestedClass)
     e_expected = AglyphError(
         "%r does not have an importable dotted name" %
             dummy.ModuleClass.NestedClass)
     assertRaisesWithMessage(self, e_expected, builder.register)
예제 #5
0
 def test_factory_name_and_member_name_are_mutually_exclusive(self):
     e_expected = AglyphError(
         "only one of factory_name or member_name may be specified")
     assertRaisesWithMessage(self,
                             e_expected,
                             Component,
                             "test",
                             factory_name="factory",
                             member_name="member")
예제 #6
0
파일: context.py 프로젝트: subokita/Aglyph
    def _process_attributes(self, attributes_element):
        """Yield attributes (fields, setter methods, or properties)
        parsed from *attributes_element*.

        :arg xml.etree.ElementTree.Element attributes_element:
           an ``<attributes>`` element
        :return:
           an iterator that yields the 2-tuple ``(name, value)``

        """
        for element in list(attributes_element):
            if element.tag != "attribute":
                raise AglyphError("unexpected element: attributes/%s" %
                                  element.tag)
            name = element.get("name")
            if not name:
                raise AglyphError(
                    "attribute/@name is required and cannot be empty")
            value = self._unserialize_element_value(element)
            yield (name, value)
예제 #7
0
파일: context.py 프로젝트: subokita/Aglyph
    def _process_init(self, init_element):
        """Yield initialization arguments (positional and keyword)
        parsed from *init_element*.

        :arg xml.etree.ElementTree.Element init_element:
           an ``<init>`` element
        :return:
           an iterator that yields the 2-tuple ``(keyword, value)``

        .. note::
           Both positional and keyword arguments are yielded by this
           method as a 2-tuple ``(keyword, value)``. For positional
           arguments, ``keyword`` will be ``None``.

        """
        for element in list(init_element):
            if element.tag != "arg":
                raise AglyphError("unexpected element: init/%s" % element.tag)
            keyword = element.get("keyword")
            if keyword == "":
                raise AglyphError("arg/@keyword cannot be empty")
            value = self._unserialize_element_value(element)
            yield (keyword, value)
예제 #8
0
파일: context.py 프로젝트: subokita/Aglyph
    def _process_dependencies(self, depsupport, depsupport_element):
        """Parse the child elements of *depsupport_element* to populate
        the *depsupport* initialization arguments and attributess.

        :arg depsupport:
           a :class:`Template` or :class:`Component`
        :arg xml.etree.ElementTree.Element depsupport_element:
           the ``<template>`` or ``<component>`` that was parsed to
           create *depsupport*

        """
        children = list(depsupport_element)
        child_tags = [elem.tag for elem in children]
        if child_tags == ["init"]:
            init_element = children[0]
            attributes_element = None
        elif child_tags == ["init", "attributes"]:
            init_element, attributes_element = children
        elif child_tags == ["attributes"]:
            init_element = None
            attributes_element = children[0]
        elif not child_tags:
            init_element = None
            attributes_element = None
        else:
            dtag = depsupport_element.tag
            raise AglyphError("unexpected element: %s/%s" %
                              (depsupport_element.tag, child_tags[0]))

        if init_element is not None:
            for (keyword, value) in self._process_init(init_element):
                if keyword is None:
                    depsupport.args.append(value)
                else:
                    depsupport.keywords[keyword] = value

        if attributes_element is not None:
            for (name, value) in self._process_attributes(attributes_element):
                depsupport.attributes[name] = value

        self.__log.debug("%r has args=%r, keywords=%r, attributess=%r",
                         depsupport, depsupport.args, depsupport.keywords,
                         depsupport.attributes)
예제 #9
0
파일: context.py 프로젝트: subokita/Aglyph
    def register(self, definition):
        """Add a component or template *definition* to this context.

        :arg definition:
           a :class:`Component` or :class:`Template` object
        :raise AglyphError:
           if a component or template with the same unique ID is already
           registered in this context

        .. note::
           To **replace** an already-registered component or template
           with the same unique ID, use :meth:`dict.__setitem__`
           directly.

        """
        if definition.unique_id in self:
            raise AglyphError(
                "%s with ID %r already mapped in %s" %
                (name_of(definition.__class__), definition.unique_id, self))
        self[definition.unique_id] = definition
예제 #10
0
파일: context.py 프로젝트: subokita/Aglyph
    def _parse_eval(self, eval_element):
        """Return a partial object that will evaluate an expression
        parsed from *eval_element*.

        :arg xml.etree.ElementTree.Element eval_element:\
           an ``<eval>`` element
        :rtype: :obj:`functools.partial`

        ..versionadded:: 3.0.0
           The partial object will use Python's :func:`ast.literal_eval`
           function to evaluate the expression when it is called. (Prior
           versions of Aglyph used the builtin :obj:`eval` function.)

        .. seealso::
           `Eval really is dangerous
           <http://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html>`_
              Ned Batchelder's insanely thorough discussion of :obj:`eval`

        """
        if eval_element.text is None:
            raise AglyphError("<eval> cannot be an empty element")
        return partial(literal_eval, eval_element.text)
예제 #11
0
    def _initialize(self, component):
        """Create a new *component* object initialized with its
        dependencies.

        :arg aglyph.component.Component component:
           a component definition
        :return:
           an initialized object of *component*

        This method performs **type 3 (constructor)** dependency
        injection.

        .. versionchanged:: 2.1.0
           If *component* specifies a :attr:`Component.member_name`
           **and** either :attr:`Component.args`
           or :attr:`Component.keywords`, then a :class:`RuntimeWarning`
           is issued.

        """
        initializer = self._resolve_initializer(component)
        if component.member_name is None:
            (args, keywords) = self._resolve_args_and_keywords(component)
            try:
                # issues/2: always use the __call__ protocol to initialize
                obj = initializer(*args, **keywords)
            except Exception as e:
                raise AglyphError(
                    "failed to initialize object of component %r" %
                    component.unique_id, e)
        else:
            obj = initializer
            if component.args or component.keywords:
                msg = ("ignoring args and keywords for component %r "
                       "(uses member_name assembly)")
                self.__log.warning(msg, component.unique_id)
                warnings.warn(msg % component.unique_id, RuntimeWarning)
        return obj
예제 #12
0
파일: context.py 프로젝트: subokita/Aglyph
    def __init__(self, context_id, after_inject=None, before_clear=None):
        """
        :arg str context_id:
           an identifier for this context
        :keyword str after_inject:
           specifies the name of the method that will be called (if it
           exists) on **all** component objects after all of their
           dependencies have been injected
        :keyword str before_clear:
           specifies the name of the method that will be called (if it
           exists) on **all** singleton, borg, and weakref objects
           immediately before they are cleared from cache

        """
        #PYVER: arguments to super() are implicit under Python 3
        super(Context, self).__init__()

        if not context_id:
            raise AglyphError("%s ID must not be None or empty" %
                              name_of(self.__class__))

        self._context_id = context_id
        self._after_inject = after_inject
        self._before_clear = before_clear
예제 #13
0
 def test_nonimportable_class(self):
     e_expected = AglyphError("%r does not have an importable dotted name" %
                              dummy.ModuleClass.NestedClass)
     assertRaisesWithMessage(self, e_expected, Reference,
                             dummy.ModuleClass.NestedClass)
예제 #14
0
 def test_bound_method(self):
     bound_method = dummy.ModuleClass(None).method
     e_expected = AglyphError("%r does not have an importable dotted name" %
                              bound_method)
     assertRaisesWithMessage(self, e_expected, Reference, bound_method)
예제 #15
0
 def test_context_id_cannot_be_empty(self):
     e_expected = AglyphError(
         "%s ID must not be None or empty" %
             name_of(self._context.__class__))
     assertRaisesWithMessage(self, e_expected, self._context.__class__, "")
예제 #16
0
 def test_nonimportable_function(self):
     nested_function = dummy.outer_function()
     e_expected = AglyphError("%r does not have an importable dotted name" %
                              nested_function)
     assertRaisesWithMessage(self, e_expected, Reference, nested_function)
예제 #17
0
 def test_fails_to_identify_instance(self):
     # instance doesn't have __qualname__ or __name__
     instance = dummy.ModuleClass(None)
     e_expected = AglyphError("%r does not have an importable dotted name" %
                              instance)
     assertRaisesWithMessage(self, e_expected, _identify, instance)
예제 #18
0
 def test_fails_to_identify_classmethod(self):
     e_expected = AglyphError("%r does not have an importable dotted name" %
                              dummy.ModuleClass.classmethod_factory)
     assertRaisesWithMessage(self, e_expected, _identify,
                             dummy.ModuleClass.classmethod_factory)
예제 #19
0
 def test_fails_to_identify_property(self):
     # property doesn't have __module__, __qualname__, or __name__
     e_expected = AglyphError("%r does not have an importable dotted name" %
                              dummy.ModuleClass.prop)
     assertRaisesWithMessage(self, e_expected, _identify,
                             dummy.ModuleClass.prop)
예제 #20
0
 def test_builtin_object(self):
     e_expected = AglyphError("79 does not have an importable dotted name")
     assertRaisesWithMessage(self, e_expected, Reference, 79)
예제 #21
0
 def test_user_object(self):
     obj = dummy.ModuleClass(None)
     e_expected = AglyphError("%r does not have an importable dotted name" %
                              obj)
     assertRaisesWithMessage(self, e_expected, Reference, obj)
예제 #22
0
 def test_process_attributes_unexpected_children(self):
     stream = bytebuf(self._uresource.encode("utf-8"))
     e_expected = AglyphError("unexpected element: attributes/unexpected")
     assertRaisesWithMessage(self, e_expected, XMLContext, stream)
예제 #23
0
 def test_staticmethod(self):
     e_expected = AglyphError("%r does not have an importable dotted name" %
                              dummy.ModuleClass.staticmethod_factory)
     assertRaisesWithMessage(self, e_expected, Reference,
                             dummy.ModuleClass.staticmethod_factory)
예제 #24
0
 def test_attribute_name_cannot_be_empty(self):
     stream = bytebuf(self._uresource.encode("utf-8"))
     e_expected = AglyphError(
         "attribute/@name is required and cannot be empty")
     assertRaisesWithMessage(self, e_expected, XMLContext, stream)
예제 #25
0
 def test_fails_to_identify_none(self):
     # None doesn't have __module__, __qualname__, or __name__
     e_expected = AglyphError("%r does not have an importable dotted name" %
                              None)
     assertRaisesWithMessage(self, e_expected, _identify, None)
예제 #26
0
 def test_attribute_cannot_be_empty(self):
     stream = bytebuf(self._uresource.encode("utf-8"))
     e_expected = AglyphError(
         "<attribute> must contain exactly one child element; "
         "found no children")
     assertRaisesWithMessage(self, e_expected, XMLContext, stream)
예제 #27
0
 def test_fails_to_identify_nested_function(self):
     nested_function = dummy.outer_function()
     e_expected = AglyphError("%r does not have an importable dotted name" %
                              nested_function)
     assertRaisesWithMessage(self, e_expected, _identify, nested_function)
예제 #28
0
 def test_attribute_rejects_multiple_children(self):
     stream = bytebuf(self._uresource.encode("utf-8"))
     e_expected = AglyphError(
         "<attribute> must contain exactly one child element; "
         "found attribute/str, attribute/int")
     assertRaisesWithMessage(self, e_expected, XMLContext, stream)
예제 #29
0
 def test_fails_to_identify_bound_function(self):
     bound_function = dummy.MODULE_MEMBER.method
     e_expected = AglyphError("%r does not have an importable dotted name" %
                              bound_function)
     assertRaisesWithMessage(self, e_expected, _identify, bound_function)
예제 #30
0
 def test_context_id_cannot_be_empty(self):
     stream = bytebuf(self._uresource.encode("utf-8"))
     e_expected = AglyphError("XMLContext ID must not be None or empty")
     assertRaisesWithMessage(self, e_expected, XMLContext, stream)