#
# This file is part of Dragonfly.
# (c) Copyright 2007, 2008 by Christo Butcher
# Licensed under the LGPL.
#
# Dragonfly is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Dragonfly is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with Dragonfly. If not, see
# <http://www.gnu.org/licenses/>.
#
# The overridden methods in the Dragonfly.list.List class were
# automatically generated using the commented-out code immediately below.
#
#def construct_skeleton():
# instance = list()
# ignored_attributes = ('__class__', '__contains__', '__doc__',
# '__eq__', '__ge__', '__getattribute__', '__getitem__',
# '__getslice__', '__gt__', '__hash__', '__init__', '__iter__',
# '__le__', '__len__', '__lt__', '__ne__', '__new__', '__repr__',
# '__str__', 'count', 'index', '__delattr__', '__setattr__')
# output = []
# for name in dir(instance):
# attribute = getattr(instance, name)
# if not callable(attribute): continue
# if name in ignored_attributes: continue
# output.append("""\
# def %(function)s(self, *args, **kwargs):
# result = %(class)s.%(function)s(self, *args, **kwargs)
# self._update(); return result
#""" % {"class": "list", "function": name})
# return "".join(output)
#print construct_skeleton()
from six import string_types
#===========================================================================
# Base class for dragonfly list objects.
[docs]
class ListBase(object):
""" Base class for dragonfly list objects. """
def __init__(self, name):
self._name = name
self._grammar = None
self._batch_mode = False
self._batch_updates = False
#-----------------------------------------------------------------------
# Protected attribute access.
valid_types = property(
lambda self: string_types,
doc="The types of object at a Dragonfly list can contain.")
name = property(lambda self: self._name,
doc="Read-only access to a list's name.")
def _get_grammar(self):
return self._grammar
def _set_grammar(self, grammar):
self._grammar = grammar
# if self._grammar is None:
# self._grammar = grammar
# else:
# raise TypeError("The grammar object a Dragonfly list is bound "
# "to cannot be changed after it has been set.")
grammar = property(_get_grammar, _set_grammar,
doc="Set-once access to a list's grammar object.")
#-----------------------------------------------------------------------
# Context manager methods for optimizing update_list() calls.
def __enter__(self):
self._batch_mode = True
def __exit__(self, exc_type, exc_value, exc_tb):
self._batch_mode = False
if self._batch_updates:
self._update()
self._batch_updates = False
#-----------------------------------------------------------------------
# Notify the grammar of a list modification.
def _update(self):
"""
Internal method that notifies the engine of list updates.
This method should be called internally by :class:`ListBase`sub-
classes when the list is modified.
"""
# Return early for batch mode. A single update_list() call will
# occur in __exit__(), after a 'with' block.
if self._batch_mode:
self._batch_updates = True
return
# Validate list items.
self._validate_items()
# If this list is part of a grammar, then notify it of the list
# changes.
if self._grammar:
self._grammar.update_list(self)
def _validate_items(self):
valid_types = self.valid_types
invalid = [i for i in self if not isinstance(i, valid_types)]
if invalid:
raise TypeError("Dragonfly lists can only contain"
" string objects; received: %r" % invalid)
#-----------------------------------------------------------------------
# Accessor for the grammar to retrieve the list items.
def get_list_items(self):
raise NotImplementedError("Call to virtual method list_items()")
#===========================================================================
# Wrapper for Python's built-in list type.
[docs]
class List(ListBase, list):
"""
Wrapper for Python's built-in list that supports automatic
engine notification of changes.
Use :class:`~dragonfly.grammar.elements_basic.ListRef` elements
in a grammar rule to allow matching speech to list items.
"""
def __init__(self, name, *args, **kwargs):
ListBase.__init__(self, name)
list.__init__(self, *args, **kwargs)
#-----------------------------------------------------------------------
# Accessor for the grammar to retrieve the list items.
def get_list_items(self):
return self
#-----------------------------------------------------------------------
# Custom methods.
[docs]
def set(self, other):
"""Set the contents of this list to the contents of another."""
with self:
self[:] = other
#-----------------------------------------------------------------------
# Overridden list methods.
def __add__(self, *args, **kwargs):
result = list.__add__(self, *args, **kwargs)
self._update(); return result
def __delitem__(self, *args, **kwargs):
result = list.__delitem__(self, *args, **kwargs)
self._update(); return result
def __delslice__(self, *args, **kwargs):
# pylint: disable=no-member
result = list.__delslice__(self, *args, **kwargs)
self._update(); return result
def __iadd__(self, *args, **kwargs):
result = list.__iadd__(self, *args, **kwargs)
self._update(); return result
def __imul__(self, *args, **kwargs):
result = list.__imul__(self, *args, **kwargs)
self._update(); return result
def __mul__(self, *args, **kwargs):
result = list.__mul__(self, *args, **kwargs)
self._update(); return result
def __reduce__(self, *args, **kwargs):
result = list.__reduce__(self, *args, **kwargs)
self._update(); return result
def __reduce_ex__(self, *args, **kwargs):
result = list.__reduce_ex__(self, *args, **kwargs)
self._update(); return result
def __rmul__(self, *args, **kwargs):
result = list.__rmul__(self, *args, **kwargs)
self._update(); return result
def __setitem__(self, *args, **kwargs):
result = list.__setitem__(self, *args, **kwargs)
self._update(); return result
def __setslice__(self, *args, **kwargs):
# pylint: disable=no-member
result = list.__setslice__(self, *args, **kwargs)
self._update(); return result
[docs]
def append(self, *args, **kwargs):
result = list.append(self, *args, **kwargs)
self._update(); return result
[docs]
def extend(self, *args, **kwargs):
result = list.extend(self, *args, **kwargs)
self._update(); return result
[docs]
def insert(self, *args, **kwargs):
result = list.insert(self, *args, **kwargs)
self._update(); return result
[docs]
def pop(self, *args, **kwargs):
result = list.pop(self, *args, **kwargs)
self._update(); return result
[docs]
def remove(self, *args, **kwargs):
result = list.remove(self, *args, **kwargs)
self._update(); return result
[docs]
def reverse(self, *args, **kwargs):
result = list.reverse(self, *args, **kwargs)
self._update(); return result
[docs]
def sort(self, *args, **kwargs):
result = list.sort(self, *args, **kwargs)
self._update(); return result
[docs]
def clear(self):
del self[:]
#===========================================================================
# Wrapper for Python's built-in dict type.
[docs]
class DictList(ListBase, dict):
"""
Wrapper for Python's built-in dict that supports automatic
engine notification of changes. The object's keys are used
as the elements of the engine list, while use of the associated
values is left to the user.
Use :class:`~dragonfly.grammar.elements_basic.DictListRef` elements
in a grammar rule to allow matching speech to dictionary keys.
"""
def __init__(self, name, *args, **kwargs):
ListBase.__init__(self, name)
dict.__init__(self, *args, **kwargs)
#-----------------------------------------------------------------------
# Accessor for the grammar to retrieve the list items.
def get_list_items(self):
return list(self.keys())
#-----------------------------------------------------------------------
# Custom methods.
[docs]
def set(self, other):
"""Set the contents of this dict to the contents of another."""
with self:
self.clear()
self.update(other)
#-----------------------------------------------------------------------
# Overridden dict methods.
def __delitem__(self, *args, **kwargs):
result = dict.__delitem__(self, *args, **kwargs)
self._update(); return result
def __reduce__(self, *args, **kwargs):
result = dict.__reduce__(self, *args, **kwargs)
self._update(); return result
def __reduce_ex__(self, *args, **kwargs):
result = dict.__reduce_ex__(self, *args, **kwargs)
self._update(); return result
def __setitem__(self, *args, **kwargs):
result = dict.__setitem__(self, *args, **kwargs)
self._update(); return result
[docs]
def clear(self, *args, **kwargs):
result = dict.clear(self, *args, **kwargs)
self._update(); return result
[docs]
def fromkeys(self, *args, **kwargs):
result = dict.fromkeys(self, *args, **kwargs)
self._update(); return result
[docs]
def pop(self, *args, **kwargs):
result = dict.pop(self, *args, **kwargs)
self._update(); return result
[docs]
def popitem(self, *args, **kwargs):
result = dict.popitem(self, *args, **kwargs)
self._update(); return result
[docs]
def setdefault(self, *args, **kwargs):
result = dict.setdefault(self, *args, **kwargs)
self._update(); return result
[docs]
def update(self, *args, **kwargs):
result = dict.update(self, *args, **kwargs)
self._update(); return result