Browse Source

Document SmartList, StringMixIn, utils; some cleanup.

tags/v0.1
Ben Kurtovic 11 years ago
parent
commit
d9805d409b
4 changed files with 106 additions and 4 deletions
  1. +64
    -0
      mwparserfromhell/smart_list.py
  2. +20
    -0
      mwparserfromhell/string_mixin.py
  3. +21
    -2
      mwparserfromhell/utils.py
  4. +1
    -2
      mwparserfromhell/wikicode.py

+ 64
- 0
mwparserfromhell/smart_list.py View File

@@ -20,13 +20,54 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE. # SOFTWARE.


"""
This module contains the :py:class:`~mwparserfromhell.smart_list.SmartList`
type, as well as its :py:class:`~mwparserfromhell.smart_list._ListProxy` child,
which together implement a list whose sublists reflect changes made to the main
list, and vice-versa.
"""

from __future__ import unicode_literals from __future__ import unicode_literals


from .compat import maxsize, py3k from .compat import maxsize, py3k


__all__ = ["SmartList"] __all__ = ["SmartList"]


def inheritdoc(method):
"""Set __doc__ of *method* to __doc__ of *method* in its parent class.

Since this is used on
:py:class:`~mwparserfromhell.smart_list.SmartList`, the "parent class" used
is ``list``. This function can be used as a decorator.
"""
method.__doc__ = getattr(list, method.__name__).__doc__
return method


class SmartList(list): class SmartList(list):
"""Implements the ``list`` interface with special handling of sublists.

When a sublist is created (through list[i:j]), any changes made to this
list (such as the addition, removal, or replacement of elements) will be
reflected in the sublist, or vice-versa, to the greatest degree possible.
This is implemented by having sublists - instances of the
:py:class:`~mwparserfromhell.smart_list._ListProxy` type - dynamically
determine their elements by storing their slice info and retrieving that
slice from the parent. Methods that change the size of the list also change
the slice info. For example::

>>> parent = SmartList([0, 1, 2, 3])
>>> parent
[0, 1, 2, 3]
>>> child = parent[2:]
>>> child
[2, 3]
>>> child.append(4)
>>> child
[2, 3, 4]
>>> parent
[0, 1, 2, 3, 4]
"""
def __init__(self, iterable=None): def __init__(self, iterable=None):
if iterable: if iterable:
super(SmartList, self).__init__(iterable) super(SmartList, self).__init__(iterable)
@@ -88,17 +129,21 @@ class SmartList(list):
self.extend(other) self.extend(other)
return self return self


@inheritdoc
def append(self, item): def append(self, item):
head = len(self) head = len(self)
self[head:head] = [item] self[head:head] = [item]


@inheritdoc
def extend(self, item): def extend(self, item):
head = len(self) head = len(self)
self[head:head] = item self[head:head] = item


@inheritdoc
def insert(self, index, item): def insert(self, index, item):
self[index:index] = [item] self[index:index] = [item]


@inheritdoc
def pop(self, index=None): def pop(self, index=None):
if index is None: if index is None:
index = len(self) - 1 index = len(self) - 1
@@ -106,15 +151,18 @@ class SmartList(list):
del self[index] del self[index]
return item return item


@inheritdoc
def remove(self, item): def remove(self, item):
del self[self.index(item)] del self[self.index(item)]


@inheritdoc
def reverse(self): def reverse(self):
copy = list(self) copy = list(self)
for child in self._children: for child in self._children:
child._parent = copy child._parent = copy
super(SmartList, self).reverse() super(SmartList, self).reverse()


@inheritdoc
def sort(self, cmp=None, key=None, reverse=None): def sort(self, cmp=None, key=None, reverse=None):
copy = list(self) copy = list(self)
for child in self._children: for child in self._children:
@@ -132,6 +180,13 @@ class SmartList(list):




class _ListProxy(list): class _ListProxy(list):
"""Implement the ``list`` interface by getting elements from a parent.

This is created by a :py:class:`~mwparserfromhell.smart_list.SmartList`
object when slicing. It does not actually store the list at any time;
instead, whenever the list is needed, it builds it dynamically using the
:py:meth:`_render` method.
"""
def __init__(self, parent, sliceinfo): def __init__(self, parent, sliceinfo):
super(_ListProxy, self).__init__() super(_ListProxy, self).__init__()
self._parent = parent self._parent = parent
@@ -249,12 +304,15 @@ class _ListProxy(list):
def _render(self): def _render(self):
return list(self._parent)[self._start:self._stop:self._step] return list(self._parent)[self._start:self._stop:self._step]


@inheritdoc
def append(self, item): def append(self, item):
self._parent.insert(self._stop, item) self._parent.insert(self._stop, item)


@inheritdoc
def count(self, item): def count(self, item):
return self._render().count(item) return self._render().count(item)


@inheritdoc
def index(self, item, start=None, stop=None): def index(self, item, start=None, stop=None):
if start is not None: if start is not None:
if stop is not None: if stop is not None:
@@ -262,26 +320,32 @@ class _ListProxy(list):
return self._render().index(item, start) return self._render().index(item, start)
return self._render().index(item) return self._render().index(item)


@inheritdoc
def extend(self, item): def extend(self, item):
self._parent[self._stop:self._stop] = item self._parent[self._stop:self._stop] = item


@inheritdoc
def insert(self, index, item): def insert(self, index, item):
self._parent.insert(self._start + index, item) self._parent.insert(self._start + index, item)


@inheritdoc
def pop(self, index=None): def pop(self, index=None):
if index is None: if index is None:
index = len(self) - 1 index = len(self) - 1
return self._parent.pop(self._start + index) return self._parent.pop(self._start + index)


@inheritdoc
def remove(self, item): def remove(self, item):
index = self.index(item) index = self.index(item)
del self._parent[index] del self._parent[index]


@inheritdoc
def reverse(self): def reverse(self):
item = self._render() item = self._render()
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): def sort(self, cmp=None, key=None, reverse=None):
item = self._render() item = self._render()
if cmp is not None: if cmp is not None:


+ 20
- 0
mwparserfromhell/string_mixin.py View File

@@ -20,6 +20,12 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE. # SOFTWARE.


"""
This module contains the :py:class:`~mwparserfromhell.string_mixin.StringMixIn`
type, which implements the interface for the ``unicode`` type (``str`` on py3k)
in a dynamic manner.
"""

from __future__ import unicode_literals from __future__ import unicode_literals


from .compat import py3k, str from .compat import py3k, str
@@ -27,11 +33,25 @@ from .compat import py3k, str
__all__ = ["StringMixIn"] __all__ = ["StringMixIn"]


def inheritdoc(method): def inheritdoc(method):
"""Set __doc__ of *method* to __doc__ of *method* in its parent class.

Since this is used on
:py:class:`~mwparserfromhell.string_mixin.StringMixIn`, the "parent class"
used is ``str``. This function can be used as a decorator.
"""
method.__doc__ = getattr(str, method.__name__).__doc__ method.__doc__ = getattr(str, method.__name__).__doc__
return method return method




class StringMixIn(object): class StringMixIn(object):
"""Implement the interface for ``unicode``/``str`` in a dynamic manner.

To use this class, inherit from it and override the :py:meth:`__unicode__`
method (same on py3k) to return the string representation of the object.
The various string methods will operate on the value of
:py:meth:`__unicode__` instead of the immutable ``self`` like the regular
``str`` type.
"""
if py3k: if py3k:
def __str__(self): def __str__(self):
return self.__unicode__() return self.__unicode__()


+ 21
- 2
mwparserfromhell/utils.py View File

@@ -20,6 +20,11 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE. # SOFTWARE.


"""
This module contains accessory functions that wrap around existing ones to
provide additional functionality.
"""

from __future__ import unicode_literals from __future__ import unicode_literals


import mwparserfromhell import mwparserfromhell
@@ -28,15 +33,29 @@ from .nodes import Node
from .smart_list import SmartList from .smart_list import SmartList


def parse_anything(value): def parse_anything(value):
"""Return a :py:class:`~mwparserfromhell.wikicode.Wikicode` for *value*.

This differs from :py:func:`mwparserfromhell.parse` in that we accept more
than just a string to be parsed. Unicode objects (strings in py3k), strings
(bytes in py3k), integers (converted to strings), ``None``, existing
:py:class:`~mwparserfromhell.nodes.Node` or
:py:class:`~mwparserfromhell.wikicode.Wikicode` objects, as well as an
iterable of these types, are supported. This is used to parse input
on-the-fly by various methods of
:py:class:`~mwparserfromhell.wikicode.Wikicode` and others like
:py:class:`~mwparserfromhell.nodes.template.Template`, such as
:py:meth:`wikicode.insert() <mwparserfromhell.wikicode.Wikicode.insert>`
or setting :py:meth:`template.name
<mwparserfromhell.nodes.template.Template.name>`.
"""
wikicode = mwparserfromhell.wikicode.Wikicode wikicode = mwparserfromhell.wikicode.Wikicode
if isinstance(value, wikicode): if isinstance(value, wikicode):
return value return value
elif isinstance(value, Node): elif isinstance(value, Node):
return wikicode(SmartList([value])) return wikicode(SmartList([value]))
elif isinstance(value, basestring):
elif isinstance(value, str):
return mwparserfromhell.parse(value) return mwparserfromhell.parse(value)
elif isinstance(value, bytes): elif isinstance(value, bytes):
# This should only happen in py3k when bytes is not in basestring:
return mwparserfromhell.parse(value.decode("utf8")) return mwparserfromhell.parse(value.decode("utf8"))
elif isinstance(value, int): elif isinstance(value, int):
return mwparserfromhell.parse(str(value)) return mwparserfromhell.parse(str(value))


+ 1
- 2
mwparserfromhell/wikicode.py View File

@@ -316,8 +316,7 @@ class Wikicode(StringMixIn):
"""Iterate over template nodes. """Iterate over template nodes.


This is equivalent to :py:meth:`ifilter` with *forcetype* set to This is equivalent to :py:meth:`ifilter` with *forcetype* set to
:py:class:`~mwparserfromhell.nodes.template.Template`. It takes all
other arguments passable to :py:meth:`ifilter`.
:py:class:`~mwparserfromhell.nodes.template.Template`.
""" """
return self.filter(recursive, matches, flags, forcetype=Template) return self.filter(recursive, matches, flags, forcetype=Template)




Loading…
Cancel
Save