瀏覽代碼

Apply some bugfixes to SmartList to fix tests on Python 3.

- Add a _SliceNormalizerMixIn to properly handle slices.
- Use floor division when applying key.step.
- Implement sort() without 'cmp' parameter.
- Fix bytes(list) behavior.
- Children of _ListProxies are now _ListProxies, not regular lists.
tags/v0.2
Ben Kurtovic 11 年之前
父節點
當前提交
32ac6958e1
共有 2 個檔案被更改,包括 99 行新增50 行删除
  1. +92
    -45
      mwparserfromhell/smart_list.py
  2. +7
    -5
      tests/test_smart_list.py

+ 92
- 45
mwparserfromhell/smart_list.py 查看文件

@@ -41,8 +41,23 @@ def inheritdoc(method):
method.__doc__ = getattr(list, method.__name__).__doc__
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.

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):
if not isinstance(key, slice):
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)
self._children[id(child)] = (child, sliceinfo)
return child
@@ -87,9 +102,8 @@ class SmartList(list):
return super(SmartList, self).__setitem__(key, item)
item = list(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
if diff:
for child, (start, stop, step) in values():
@@ -101,11 +115,10 @@ class SmartList(list):
def __delitem__(self, key):
super(SmartList, self).__delitem__(key)
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:
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
for child, (start, stop, step) in values():
if start > key.start:
@@ -166,22 +179,35 @@ class SmartList(list):
child._parent = copy
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.

This is created by a :py:class:`~.SmartList` object when slicing. It does
@@ -235,19 +261,28 @@ class _ListProxy(list):
return bool(self._render())

def __len__(self):
return (self._stop - self._start) / self._step
return (self._stop - self._start) // self._step

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):
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
else:
keystop = key.stop + self._start
adjusted = slice(keystart, keystop, key.step)
adjusted = slice(key.start + self._start, keystop, key.step)
self._parent[adjusted] = item
else:
length = len(self)
@@ -259,12 +294,12 @@ class _ListProxy(list):

def __delitem__(self, key):
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
else:
keystop = key.stop + self._start
adjusted = slice(keystart, keystop, key.step)
adjusted = slice(key.start + self._start, keystop, key.step)
del self._parent[adjusted]
else:
length = len(self)
@@ -388,18 +423,30 @@ class _ListProxy(list):
item.reverse()
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

+ 7
- 5
tests/test_smart_list.py 查看文件

@@ -123,7 +123,7 @@ class TestSmartList(unittest.TestCase):

if py3k:
self.assertEqual("[0, 1, 2, 3, 'one', 'two']", str(list1))
self.assertEqual(b"[0, 1, 2, 3, 'one', 'two']", bytes(list1))
self.assertEqual(b"\x00\x01\x02", bytes(list4))
self.assertEqual("[0, 1, 2, 3, 'one', 'two']", repr(list1))
else:
self.assertEqual("[0, 1, 2, 3, u'one', u'two']", unicode(list1))
@@ -256,10 +256,12 @@ class TestSmartList(unittest.TestCase):
self.assertEqual([0, 2, 2, 3, 4, 5], list1)
list1.sort(reverse=True)
self.assertEqual([5, 4, 3, 2, 2, 0], list1)
list1.sort(cmp=lambda x, y: abs(3 - x) - abs(3 - y)) # Distance from 3
self.assertEqual([3, 4, 2, 2, 5, 0], list1)
list1.sort(cmp=lambda x, y: abs(3 - x) - abs(3 - y), reverse=True)
self.assertEqual([0, 5, 4, 2, 2, 3], list1)
if not py3k:
func = lambda x, y: abs(3 - x) - abs(3 - y) # Distance from 3
list1.sort(cmp=func)
self.assertEqual([3, 4, 2, 2, 5, 0], list1)
list1.sort(cmp=func, reverse=True)
self.assertEqual([0, 5, 4, 2, 2, 3], list1)
list3.sort(key=lambda i: i[1])
self.assertEqual([("d", 2), ("c", 3), ("a", 5), ("b", 8)], list3)
list3.sort(key=lambda i: i[1], reverse=True)


Loading…
取消
儲存