Esempio n. 1
0
class Element(object):
    """A base class representing an NLG element.
        Aside for providing a base class for other kinds of NLG elements,
        the class also implements basic functionality for elements.

    """

    category = category.ELEMENT

    def __init__(self, features=None, parent=None, id=None):
        self.features = FeatureSet()
        self.features.update(features)
        self.parent = parent
        self.id = id
        self.hash = -1

    def __copy__(self):
        rv = self.__class__(features=self.features,
                            parent=self.parent,
                            id=self.id)
        return rv

    # noinspection PyArgumentList
    def __deepcopy__(self, memo):
        rv = self.__class__(features=None, parent=None, id=self.id)
        memo[id(self)] = rv
        rv.features = deepcopy(self.features, memo=memo)
        rv.parent = memo.get(id(self.parent), None)
        return rv

    def __bool__(self):
        """Because Element is abstract, it will evaluate to false. """
        return False

    def __eq__(self, other):
        return (isinstance(other, Element) and self.id == other.id
                and self.category == other.category and comparable_features(
                    self.features) == comparable_features(other.features))

    def __hash__(self):
        if self.hash == -1:
            self.hash = hash(str(self))
        return self.hash

    def __repr__(self):
        from . import visitors
        v = visitors.ReprVisitor()
        self.accept(v)
        return str(v)

    def __str__(self):
        from . import visitors
        v = visitors.SimpleStrVisitor()
        self.accept(v)
        return str(v)

    def __contains__(self, feature_name):
        """Check if the argument feature name is contained in the element."""
        return feature_name in self.features

    def __setitem__(self, feature_name, feature_value):
        """Set the feature name/value in the element feature set."""
        self.features[feature_name] = feature_value

    def __getitem__(self, feature_name):
        """Return the value associated with the feature name, from the
        element feature set.

        If the feature name is not found in the feature dict, return None.

        """
        return self.features.get(feature_name)

    def __delitem__(self, feature_name):
        """Remove the argument feature name and its associated value from
        the element feature set.

        """
        self.features.discard(feature_name)

    def __add__(self, other):
        """Add two elements resulting in a coordination 
        if both elements are not "False" else return the "True" element.
        
        """
        if not self:
            return other
        if not other:
            return self
        return Coordination(self, other)

    @classmethod
    def from_dict(cls, dct):
        o = cls(None, None, None)
        o.__dict__.update(dct)
        return o

    @classmethod
    def from_json(cls, s):
        return json.loads(s, cls=ElementDecoder)

    def to_json(self):
        return json.dumps(self, cls=ElementEncoder)

    def to_xml(self, depth=0, headers=False):
        from . import visitors
        visitor = visitors.XmlVisitor(depth=depth)
        self.accept(visitor)
        if headers:
            return str(visitor)
        else:
            return str(visitor.xml)

    def accept(self, visitor, **kwargs):
        """Implementation of the Visitor pattern."""
        visitor_method_name = self.category.lower()
        # get the appropriate method of the visitor instance
        m = getattr(visitor, visitor_method_name)
        # ensure that the method is callable
        if not hasattr(m, '__call__'):
            msg = 'Error: cannot call undefined method: %s on visitor'
            raise ValueError(msg % visitor_method_name)
        # and finally call the callback
        return m(self, **kwargs)

    def elements(self, recursive=False, itself=None):
        """Return a generator yielding elements contained in the element

        :param bool recursive: also include sub-elements of the contained elements
        :param str itself: yield `self` as one of the elements; values in (None, 'first', 'last')

        """
        if itself or recursive and self.category != category.ELEMENT:
            yield self

    def arguments(self):
        """Return any arguments (vars) from the element as a list. """
        return [
            x for x in self.elements(recursive=True)
            if x.category == category.VAR
        ]

    def replace(self, one, another, key=lambda x: x):
        """Replace the first occurrence of `one` by `another`.

        :param one: a constituent to replace; will be raised to element
        :param another: a replacement element; will be raised to element
        :param key: a key function for comparison; default is identity
        :returns: True if replacement occurred; False otherwise
        
        """
        return False  # basic implementation does nothing

    def replace_argument(self, id, replacement):
        """Replace an argument with given `id` by `replacement`
        if such argument exists.

        """
        for a in self.arguments():
            if a.id == id:
                return self.replace(a, replacement)
        return False

    def replace_arguments(self, **kwargs):
        """Replace arguments with ids in the kwargs 
        by the corresponding values. 
        
        """
        for k, v in kwargs.items():
            self.replace_argument(k, v)

    @property
    def string(self):
        """Return the string inside the value. """
        return ''

    def update_parents(self, parent=_sentinel):
        """Re-set the `parent` attribute of nested elements."""
        if parent is not _sentinel:
            self.parent = parent
Esempio n. 2
0
 def test_get(self):
     fs = FeatureSet([self.number.singular, self.person.first])
     self.assertEqual(self.number.singular, fs.get(self.number))
     self.assertEqual(None, fs.get(self.tense))
     self.assertEqual(self.tense.future,
                      fs.get(self.tense, self.tense.future))