@@ -41,8 +41,23 @@ def inheritdoc(method):
method.__doc__ = getattr(list, method.__name__).__doc__
method.__doc__ = getattr(list, method.__name__).__doc__
return method
return method
class _SliceNormalizerMixIn(object):
"""MixIn that provides a private method to normalize slices."""
class SmartList(list):
def _normalize_slice(self, key):
"""Return a slice equivalent to the input *key*, standardized."""
if key.start is not None:
start = (len(self) + key.start) if key.start < 0 else key.start
else:
start = 0
if key.stop is not None:
stop = (len(self) + key.stop) if key.stop < 0 else key.stop
else:
stop = maxsize
return slice(start, stop, key.step or 1)
class SmartList(_SliceNormalizerMixIn, list):
"""Implements the ``list`` interface with special handling of sublists.
"""Implements the ``list`` interface with special handling of sublists.
When a sublist is created (by ``list[i:j]``), any changes made to this
When a sublist is created (by ``list[i:j]``), any changes made to this
@@ -76,8 +91,8 @@ class SmartList(list):
def __getitem__(self, key):
def __getitem__(self, key):
if not isinstance(key, slice):
if not isinstance(key, slice):
return super(SmartList, self).__getitem__(key)
return super(SmartList, self).__getitem__(key)
keystop = maxsize if key.stop is None else key.stop
sliceinfo = [key.start or 0 , keystop, key.step or 1 ]
key = self._normalize_slice(key)
sliceinfo = [key.start, key. stop, key.step]
child = _ListProxy(self, sliceinfo)
child = _ListProxy(self, sliceinfo)
self._children[id(child)] = (child, sliceinfo)
self._children[id(child)] = (child, sliceinfo)
return child
return child
@@ -87,9 +102,8 @@ class SmartList(list):
return super(SmartList, self).__setitem__(key, item)
return super(SmartList, self).__setitem__(key, item)
item = list(item)
item = list(item)
super(SmartList, self).__setitem__(key, item)
super(SmartList, self).__setitem__(key, item)
keystop = maxsize if key.stop is None else key.stop
key = slice(key.start or 0, keystop, key.step or 1)
diff = len(item) + (key.start - key.stop) / key.step
key = self._normalize_slice(key)
diff = len(item) + (key.start - key.stop) // key.step
values = self._children.values if py3k else self._children.itervalues
values = self._children.values if py3k else self._children.itervalues
if diff:
if diff:
for child, (start, stop, step) in values():
for child, (start, stop, step) in values():
@@ -101,11 +115,10 @@ class SmartList(list):
def __delitem__(self, key):
def __delitem__(self, key):
super(SmartList, self).__delitem__(key)
super(SmartList, self).__delitem__(key)
if isinstance(key, slice):
if isinstance(key, slice):
keystop = maxsize if key.stop is None else key.stop
key = slice(key.start or 0, keystop, key.step or 1)
key = self._normalize_slice(key)
else:
else:
key = slice(key, key + 1, 1)
key = slice(key, key + 1, 1)
diff = (key.stop - key.start) / key.step
diff = (key.stop - key.start) // key.step
values = self._children.values if py3k else self._children.itervalues
values = self._children.values if py3k else self._children.itervalues
for child, (start, stop, step) in values():
for child, (start, stop, step) in values():
if start > key.start:
if start > key.start:
@@ -166,22 +179,35 @@ class SmartList(list):
child._parent = copy
child._parent = copy
super(SmartList, self).reverse()
super(SmartList, self).reverse()
@inheritdoc
def sort(self, cmp=None, key=None, reverse=None):
copy = list(self)
for child in self._children:
child._parent = copy
kwargs = {}
if cmp is not None:
kwargs["cmp"] = cmp
if key is not None:
kwargs["key"] = key
if reverse is not None:
kwargs["reverse"] = reverse
super(SmartList, self).sort(**kwargs)
class _ListProxy(list):
if py3k:
@inheritdoc
def sort(self, key=None, reverse=None):
copy = list(self)
for child in self._children:
child._parent = copy
kwargs = {}
if key is not None:
kwargs["key"] = key
if reverse is not None:
kwargs["reverse"] = reverse
super(SmartList, self).sort(**kwargs)
else:
@inheritdoc
def sort(self, cmp=None, key=None, reverse=None):
copy = list(self)
for child in self._children:
child._parent = copy
kwargs = {}
if cmp is not None:
kwargs["cmp"] = cmp
if key is not None:
kwargs["key"] = key
if reverse is not None:
kwargs["reverse"] = reverse
super(SmartList, self).sort(**kwargs)
class _ListProxy(_SliceNormalizerMixIn, list):
"""Implement the ``list`` interface by getting elements from a parent.
"""Implement the ``list`` interface by getting elements from a parent.
This is created by a :py:class:`~.SmartList` object when slicing. It does
This is created by a :py:class:`~.SmartList` object when slicing. It does
@@ -235,19 +261,28 @@ class _ListProxy(list):
return bool(self._render())
return bool(self._render())
def __len__(self):
def __len__(self):
return (self._stop - self._start) / self._step
return (self._stop - self._start) // self._step
def __getitem__(self, key):
def __getitem__(self, key):
return self._render()[key]
if isinstance(key, slice):
key = self._normalize_slice(key)
if key.stop == maxsize:
keystop = self._stop
else:
keystop = key.stop + self._start
adjusted = slice(key.start + self._start, keystop, key.step)
return self._parent[adjusted]
else:
return self._render()[key]
def __setitem__(self, key, item):
def __setitem__(self, key, item):
if isinstance(key, slice):
if isinstance(key, slice):
keystart = (key.start or 0) + self._start
if key.stop is None or key.stop == maxsize:
key = self._normalize_slice(key)
if key.stop == maxsize:
keystop = self._stop
keystop = self._stop
else:
else:
keystop = key.stop + self._start
keystop = key.stop + self._start
adjusted = slice(keystart, keystop, key.step)
adjusted = slice(key.start + self._ start, keystop, key.step)
self._parent[adjusted] = item
self._parent[adjusted] = item
else:
else:
length = len(self)
length = len(self)
@@ -259,12 +294,12 @@ class _ListProxy(list):
def __delitem__(self, key):
def __delitem__(self, key):
if isinstance(key, slice):
if isinstance(key, slice):
keystart = (key.start or 0) + self._start
if key.stop is None or key.stop == maxsize:
key = self._normalize_slice(key)
if key.stop == maxsize:
keystop = self._stop
keystop = self._stop
else:
else:
keystop = key.stop + self._start
keystop = key.stop + self._start
adjusted = slice(keystart, keystop, key.step)
adjusted = slice(key.start + self._ start, keystop, key.step)
del self._parent[adjusted]
del self._parent[adjusted]
else:
else:
length = len(self)
length = len(self)
@@ -388,18 +423,30 @@ class _ListProxy(list):
item.reverse()
item.reverse()
self._parent[self._start:self._stop:self._step] = item
self._parent[self._start:self._stop:self._step] = item
@inheritdoc
def sort(self, cmp=None, key=None, reverse=None):
item = self._render()
kwargs = {}
if cmp is not None:
kwargs["cmp"] = cmp
if key is not None:
kwargs["key"] = key
if reverse is not None:
kwargs["reverse"] = reverse
item.sort(**kwargs)
self._parent[self._start:self._stop:self._step] = item
if py3k:
@inheritdoc
def sort(self, key=None, reverse=None):
item = self._render()
kwargs = {}
if key is not None:
kwargs["key"] = key
if reverse is not None:
kwargs["reverse"] = reverse
item.sort(**kwargs)
self._parent[self._start:self._stop:self._step] = item
else:
@inheritdoc
def sort(self, cmp=None, key=None, reverse=None):
item = self._render()
kwargs = {}
if cmp is not None:
kwargs["cmp"] = cmp
if key is not None:
kwargs["key"] = key
if reverse is not None:
kwargs["reverse"] = reverse
item.sort(**kwargs)
self._parent[self._start:self._stop:self._step] = item
del inheritdoc
del inheritdoc