Procházet zdrojové kódy

Take proper advantage of Sphinx's default domains.

tags/v0.4
Ben Kurtovic před 10 roky
rodič
revize
87e0079512
19 změnil soubory, kde provedl 248 přidání a 256 odebrání
  1. +4
    -4
      mwparserfromhell/compat.py
  2. +13
    -13
      mwparserfromhell/nodes/__init__.py
  3. +2
    -2
      mwparserfromhell/nodes/external_link.py
  4. +2
    -3
      mwparserfromhell/nodes/extras/__init__.py
  5. +3
    -3
      mwparserfromhell/nodes/extras/attribute.py
  6. +2
    -2
      mwparserfromhell/nodes/extras/parameter.py
  7. +1
    -1
      mwparserfromhell/nodes/heading.py
  8. +13
    -13
      mwparserfromhell/nodes/tag.py
  9. +16
    -16
      mwparserfromhell/nodes/template.py
  10. +2
    -2
      mwparserfromhell/nodes/wikilink.py
  11. +18
    -18
      mwparserfromhell/parser/__init__.py
  12. +5
    -5
      mwparserfromhell/parser/builder.py
  13. +44
    -44
      mwparserfromhell/parser/contexts.py
  14. +6
    -6
      mwparserfromhell/parser/tokenizer.py
  15. +2
    -2
      mwparserfromhell/parser/tokens.py
  16. +11
    -11
      mwparserfromhell/smart_list.py
  17. +5
    -6
      mwparserfromhell/string_mixin.py
  18. +10
    -10
      mwparserfromhell/utils.py
  19. +89
    -95
      mwparserfromhell/wikicode.py

+ 4
- 4
mwparserfromhell/compat.py Zobrazit soubor

@@ -2,10 +2,10 @@

"""
Implements support for both Python 2 and Python 3 by defining common types in
terms of their Python 2/3 variants. For example, :py:class:`str` is set to
:py:class:`unicode` on Python 2 but :py:class:`str` on Python 3; likewise,
:py:class:`bytes` is :py:class:`str` on 2 but :py:class:`bytes` on 3. These
types are meant to be imported directly from within the parser's modules.
terms of their Python 2/3 variants. For example, :class:`str` is set to
:class:`unicode` on Python 2 but :class:`str` on Python 3; likewise,
:class:`bytes` is :class:`str` on 2 but :class:`bytes` on 3. These types are
meant to be imported directly from within the parser's modules.
"""

import sys


+ 13
- 13
mwparserfromhell/nodes/__init__.py Zobrazit soubor

@@ -21,12 +21,12 @@
# SOFTWARE.

"""
This package contains :py:class:`~.Wikicode` "nodes", which represent a single
unit of wikitext, such as a Template, an HTML tag, a Heading, or plain text.
The node "tree" is far from flat, as most types can contain additional
:py:class:`~.Wikicode` types within them - and with that, more nodes. For
example, the name of a :py:class:`~.Template` is a :py:class:`~.Wikicode`
object that can contain text or more templates.
This package contains :class:`.Wikicode` "nodes", which represent a single unit
of wikitext, such as a Template, an HTML tag, a Heading, or plain text. The
node "tree" is far from flat, as most types can contain additional
:class:`.Wikicode` types within them - and with that, more nodes. For example,
the name of a :class:`.Template` is a :class:`.Wikicode` object that can
contain text or more templates.
"""

from __future__ import unicode_literals
@@ -40,16 +40,16 @@ __all__ = ["Node", "Text", "Argument", "Heading", "HTMLEntity", "Tag",
class Node(StringMixIn):
"""Represents the base Node type, demonstrating the methods to override.

:py:meth:`__unicode__` must be overridden. It should return a ``unicode``
or (``str`` in py3k) representation of the node. If the node contains
:py:class:`~.Wikicode` objects inside of it, :py:meth:`__children__`
should be a generator that iterates over them. If the node is printable
(shown when the page is rendered), :py:meth:`__strip__` should return its
:meth:`__unicode__` must be overridden. It should return a ``unicode`` or
(``str`` in py3k) representation of the node. If the node contains
:class:`.Wikicode` objects inside of it, :meth:`__children__` should be a
generator that iterates over them. If the node is printable
(shown when the page is rendered), :meth:`__strip__` should return its
printable version, stripping out any formatting marks. It does not have to
return a string, but something that can be converted to a string with
``str()``. Finally, :py:meth:`__showtree__` can be overridden to build a
``str()``. Finally, :meth:`__showtree__` can be overridden to build a
nice tree representation of the node, if desired, for
:py:meth:`~.Wikicode.get_tree`.
:meth:`~.Wikicode.get_tree`.
"""
def __unicode__(self):
raise NotImplementedError()


+ 2
- 2
mwparserfromhell/nodes/external_link.py Zobrazit soubor

@@ -67,12 +67,12 @@ class ExternalLink(Node):

@property
def url(self):
"""The URL of the link target, as a :py:class:`~.Wikicode` object."""
"""The URL of the link target, as a :class:`.Wikicode` object."""
return self._url

@property
def title(self):
"""The link title (if given), as a :py:class:`~.Wikicode` object."""
"""The link title (if given), as a :class:`.Wikicode` object."""
return self._title

@property


+ 2
- 3
mwparserfromhell/nodes/extras/__init__.py Zobrazit soubor

@@ -21,9 +21,8 @@
# SOFTWARE.

"""
This package contains objects used by
:py:class:`~.Node`\ s, but are not nodes themselves. This includes the
parameters of Templates or the attributes of HTML tags.
This package contains objects used by :class:`.Node`\ s, but that are not nodes
themselves. This includes template parameters and HTML tag attributes.
"""

from .attribute import Attribute


+ 3
- 3
mwparserfromhell/nodes/extras/attribute.py Zobrazit soubor

@@ -31,7 +31,7 @@ __all__ = ["Attribute"]
class Attribute(StringMixIn):
"""Represents an attribute of an HTML tag.

This is used by :py:class:`~.Tag` objects. For example, the tag
This is used by :class:`.Tag` objects. For example, the tag
``<ref name="foo">`` contains an Attribute whose name is ``"name"`` and
whose value is ``"foo"``.
"""
@@ -84,12 +84,12 @@ class Attribute(StringMixIn):

@property
def name(self):
"""The name of the attribute as a :py:class:`~.Wikicode` object."""
"""The name of the attribute as a :class:`.Wikicode` object."""
return self._name

@property
def value(self):
"""The value of the attribute as a :py:class:`~.Wikicode` object."""
"""The value of the attribute as a :class:`.Wikicode` object."""
return self._value

@property


+ 2
- 2
mwparserfromhell/nodes/extras/parameter.py Zobrazit soubor

@@ -58,12 +58,12 @@ class Parameter(StringMixIn):

@property
def name(self):
"""The name of the parameter as a :py:class:`~.Wikicode` object."""
"""The name of the parameter as a :class:`.Wikicode` object."""
return self._name

@property
def value(self):
"""The value of the parameter as a :py:class:`~.Wikicode` object."""
"""The value of the parameter as a :class:`.Wikicode` object."""
return self._value

@property


+ 1
- 1
mwparserfromhell/nodes/heading.py Zobrazit soubor

@@ -52,7 +52,7 @@ class Heading(Node):

@property
def title(self):
"""The title of the heading, as a :py:class:`~.Wikicode` object."""
"""The title of the heading, as a :class:`.Wikicode` object."""
return self._title

@property


+ 13
- 13
mwparserfromhell/nodes/tag.py Zobrazit soubor

@@ -108,19 +108,19 @@ class Tag(Node):

@property
def tag(self):
"""The tag itself, as a :py:class:`~.Wikicode` object."""
"""The tag itself, as a :class:`.Wikicode` object."""
return self._tag

@property
def contents(self):
"""The contents of the tag, as a :py:class:`~.Wikicode` object."""
"""The contents of the tag, as a :class:`.Wikicode` object."""
return self._contents

@property
def attributes(self):
"""The list of attributes affecting the tag.

Each attribute is an instance of :py:class:`~.Attribute`.
Each attribute is an instance of :class:`.Attribute`.
"""
return self._attrs

@@ -146,7 +146,7 @@ class Tag(Node):
This makes the tag look like a lone close tag. It is technically
invalid and is only parsable Wikicode when the tag itself is
single-only, like ``<br>`` and ``<img>``. See
:py:func:`.definitions.is_single_only`.
:func:`.definitions.is_single_only`.
"""
return self._invalid

@@ -155,8 +155,8 @@ class Tag(Node):
"""Whether the tag is implicitly self-closing, with no ending slash.

This is only possible for specific "single" tags like ``<br>`` and
``<li>``. See :py:func:`.definitions.is_single`. This field only has an
effect if :py:attr:`self_closing` is also ``True``.
``<li>``. See :func:`.definitions.is_single`. This field only has an
effect if :attr:`self_closing` is also ``True``.
"""
return self._implicit

@@ -167,9 +167,9 @@ class Tag(Node):

@property
def closing_tag(self):
"""The closing tag, as a :py:class:`~.Wikicode` object.
"""The closing tag, as a :class:`.Wikicode` object.

This will usually equal :py:attr:`tag`, unless there is additional
This will usually equal :attr:`tag`, unless there is additional
spacing, comments, or the like.
"""
return self._closing_tag
@@ -226,8 +226,8 @@ class Tag(Node):
def get(self, name):
"""Get the attribute with the given *name*.

The returned object is a :py:class:`~.Attribute` instance. Raises
:py:exc:`ValueError` if no attribute has this name. Since multiple
The returned object is a :class:`.Attribute` instance. Raises
:exc:`ValueError` if no attribute has this name. Since multiple
attributes can have the same name, we'll return the last match, since
all but the last are ignored by the MediaWiki parser.
"""
@@ -241,9 +241,9 @@ class Tag(Node):
"""Add an attribute with the given *name* and *value*.

*name* and *value* can be anything parsable by
:py:func:`.utils.parse_anything`; *value* can be omitted if the
attribute is valueless. If *quotes* is not ``None``, it should be a
string (either ``"`` or ``'``) that *value* will be wrapped in (this is
:func:`.utils.parse_anything`; *value* can be omitted if the attribute
is valueless. If *quotes* is not ``None``, it should be a string
(either ``"`` or ``'``) that *value* will be wrapped in (this is
recommended). ``None`` is only legal if *value* contains no spacing.

*pad_first*, *pad_before_eq*, and *pad_after_eq* are whitespace used as


+ 16
- 16
mwparserfromhell/nodes/template.py Zobrazit soubor

@@ -110,8 +110,8 @@ class Template(Node):
"""Try to determine the whitespace conventions for parameters.

This will examine the existing parameters and use
:py:meth:`_select_theory` to determine if there are any preferred
styles for how much whitespace to put before or after the value.
:meth:`_select_theory` to determine if there are any preferred styles
for how much whitespace to put before or after the value.
"""
before_theories = defaultdict(lambda: 0)
after_theories = defaultdict(lambda: 0)
@@ -159,7 +159,7 @@ class Template(Node):

@property
def name(self):
"""The name of the template, as a :py:class:`~.Wikicode` object."""
"""The name of the template, as a :class:`.Wikicode` object."""
return self._name

@property
@@ -189,13 +189,13 @@ class Template(Node):

has_param = lambda self, name, ignore_empty=False: \
self.has(name, ignore_empty)
has_param.__doc__ = "Alias for :py:meth:`has`."
has_param.__doc__ = "Alias for :meth:`has`."

def get(self, name):
"""Get the parameter whose name is *name*.

The returned object is a :py:class:`~.Parameter` instance. Raises
:py:exc:`ValueError` if no parameter has this name. Since multiple
The returned object is a :class:`.Parameter` instance. Raises
:exc:`ValueError` if no parameter has this name. Since multiple
parameters can have the same name, we'll return the last match, since
the last parameter is the only one read by the MediaWiki parser.
"""
@@ -210,8 +210,8 @@ class Template(Node):
"""Add a parameter to the template with a given *name* and *value*.

*name* and *value* can be anything parsable by
:py:func:`.utils.parse_anything`; pipes and equal signs are
automatically escaped from *value* when appropriate.
:func:`.utils.parse_anything`; pipes and equal signs are automatically
escaped from *value* when appropriate.

If *showkey* is given, this will determine whether or not to show the
parameter's name (e.g., ``{{foo|bar}}``'s parameter has a name of
@@ -221,13 +221,13 @@ class Template(Node):
If *name* is already a parameter in the template, we'll replace its
value while keeping the same whitespace around it. We will also try to
guess the dominant spacing convention when adding a new parameter using
:py:meth:`_get_spacing_conventions`.
:meth:`_get_spacing_conventions`.

If *before* is given (either a :py:class:`~.Parameter` object or a
name), then we will place the parameter immediately before this one.
If *before* is given (either a :class:`.Parameter` object or a name),
then we will place the parameter immediately before this one.
Otherwise, it will be added at the end. If *before* is a name and
exists multiple times in the template, we will place it before the last
occurrence. If *before* is not in the template, :py:exc:`ValueError` is
occurrence. If *before* is not in the template, :exc:`ValueError` is
raised. The argument is ignored if the new parameter already exists.

If *preserve_spacing* is ``False``, we will avoid preserving spacing
@@ -289,9 +289,9 @@ class Template(Node):
def remove(self, param, keep_field=False):
"""Remove a parameter from the template, identified by *param*.

If *param* is a :py:class:`.Parameter` object, it will be matched
exactly, otherwise it will be treated like the *name* argument to
:py:meth:`has` and :py:meth:`get`.
If *param* is a :class:`.Parameter` object, it will be matched exactly,
otherwise it will be treated like the *name* argument to :meth:`has`
and :meth:`get`.

If *keep_field* is ``True``, we will keep the parameter's name, but
blank its value. Otherwise, we will remove the parameter completely
@@ -300,7 +300,7 @@ class Template(Node):
we expected, so ``{{foo||baz}}`` will be produced instead).

If the parameter shows up multiple times in the template and *param* is
not a :py:class:`.Parameter` object, we will remove all instances of it
not a :class:`.Parameter` object, we will remove all instances of it
(and keep only one if *keep_field* is ``True`` - the first instance if
none have dependents, otherwise the one with dependents will be kept).
"""


+ 2
- 2
mwparserfromhell/nodes/wikilink.py Zobrazit soubor

@@ -62,12 +62,12 @@ class Wikilink(Node):

@property
def title(self):
"""The title of the linked page, as a :py:class:`~.Wikicode` object."""
"""The title of the linked page, as a :class:`.Wikicode` object."""
return self._title

@property
def text(self):
"""The text to display (if any), as a :py:class:`~.Wikicode` object."""
"""The text to display (if any), as a :class:`.Wikicode` object."""
return self._text

@title.setter


+ 18
- 18
mwparserfromhell/parser/__init__.py Zobrazit soubor

@@ -22,8 +22,8 @@

"""
This package contains the actual wikicode parser, split up into two main
modules: the :py:mod:`~.tokenizer` and the :py:mod:`~.builder`. This module
joins them together under one interface.
modules: the :mod:`.tokenizer` and the :mod:`.builder`. This module joins them
together into one interface.
"""

class ParserError(Exception):
@@ -54,16 +54,16 @@ class Parser(object):
"""Represents a parser for wikicode.

Actual parsing is a two-step process: first, the text is split up into a
series of tokens by the :py:class:`.Tokenizer`, and then the tokens are
converted into trees of :py:class:`.Wikicode` objects and
:py:class:`.Node`\ s by the :py:class:`.Builder`.
series of tokens by the :class:`.Tokenizer`, and then the tokens are
converted into trees of :class:`.Wikicode` objects and :class:`.Node`\ s by
the :class:`.Builder`.

Instances of this class or its dependents (:py:class:`.Tokenizer` and
:py:class:`.Builder`) should not be shared between threads.
:py:meth:`parse` can be called multiple times as long as it is not done
concurrently. In general, there is no need to do this because parsing
should be done through :py:func:`mwparserfromhell.parse`, which creates a
new :py:class:`.Parser` object as necessary.
Instances of this class or its dependents (:class:`.Tokenizer` and
:class:`.Builder`) should not be shared between threads. :meth:`parse` can
be called multiple times as long as it is not done concurrently. In
general, there is no need to do this because parsing should be done through
:func:`mwparserfromhell.parse`, which creates a new :class:`.Parser` object
as necessary.
"""

def __init__(self):
@@ -74,20 +74,20 @@ class Parser(object):
self._builder = Builder()

def parse(self, text, context=0, skip_style_tags=False):
"""Parse *text*, returning a :py:class:`~.Wikicode` object tree.
"""Parse *text*, returning a :class:`.Wikicode` object tree.

If given, *context* will be passed as a starting context to the parser.
This is helpful when this function is used inside node attribute
setters. For example, :py:class:`~.ExternalLink`\ 's
:py:attr:`~.ExternalLink.url` setter sets *context* to
:py:mod:`contexts.EXT_LINK_URI <.contexts>` to prevent the URL itself
from becoming an :py:class:`~.ExternalLink`.
setters. For example, :class:`.ExternalLink`\ 's
:attr:`~.ExternalLink.url` setter sets *context* to
:mod:`contexts.EXT_LINK_URI <.contexts>` to prevent the URL itself
from becoming an :class:`.ExternalLink`.

If *skip_style_tags* is ``True``, then ``''`` and ``'''`` will not be
parsed, but instead will be treated as plain text.

If there is an internal error while parsing, :py:exc:`.ParserError`
will be raised.
If there is an internal error while parsing, :exc:`.ParserError` will
be raised.
"""
tokens = self._tokenizer.tokenize(text, context, skip_style_tags)
code = self._builder.build(tokens)


+ 5
- 5
mwparserfromhell/parser/builder.py Zobrazit soubor

@@ -48,9 +48,9 @@ def _add_handler(token_type):
class Builder(object):
"""Builds a tree of nodes out of a sequence of tokens.

To use, pass a list of :py:class:`~.Token`\ s to the :py:meth:`build`
method. The list will be exhausted as it is parsed and a
:py:class:`.Wikicode` object containing the node tree will be returned.
To use, pass a list of :class:`.Token`\ s to the :meth:`build` method. The
list will be exhausted as it is parsed and a :class:`.Wikicode` object
containing the node tree will be returned.
"""

def __init__(self):
@@ -64,8 +64,8 @@ class Builder(object):
def _pop(self):
"""Pop the current node list off of the stack.

The raw node list is wrapped in a :py:class:`.SmartList` and then in a
:py:class:`.Wikicode` object.
The raw node list is wrapped in a :class:`.SmartList` and then in a
:class:`.Wikicode` object.
"""
return Wikicode(SmartList(self._stacks.pop()))



+ 44
- 44
mwparserfromhell/parser/contexts.py Zobrazit soubor

@@ -35,72 +35,72 @@ will cover ``BAR == 0b10`` and ``BAZ == 0b01``).

Local (stack-specific) contexts:

* :py:const:`TEMPLATE`
* :const:`TEMPLATE`

* :py:const:`TEMPLATE_NAME`
* :py:const:`TEMPLATE_PARAM_KEY`
* :py:const:`TEMPLATE_PARAM_VALUE`
* :const:`TEMPLATE_NAME`
* :const:`TEMPLATE_PARAM_KEY`
* :const:`TEMPLATE_PARAM_VALUE`

* :py:const:`ARGUMENT`
* :const:`ARGUMENT`

* :py:const:`ARGUMENT_NAME`
* :py:const:`ARGUMENT_DEFAULT`
* :const:`ARGUMENT_NAME`
* :const:`ARGUMENT_DEFAULT`

* :py:const:`WIKILINK`
* :const:`WIKILINK`

* :py:const:`WIKILINK_TITLE`
* :py:const:`WIKILINK_TEXT`
* :const:`WIKILINK_TITLE`
* :const:`WIKILINK_TEXT`

* :py:const:`EXT_LINK`
* :const:`EXT_LINK`

* :py:const:`EXT_LINK_URI`
* :py:const:`EXT_LINK_TITLE`
* :const:`EXT_LINK_URI`
* :const:`EXT_LINK_TITLE`

* :py:const:`HEADING`
* :const:`HEADING`

* :py:const:`HEADING_LEVEL_1`
* :py:const:`HEADING_LEVEL_2`
* :py:const:`HEADING_LEVEL_3`
* :py:const:`HEADING_LEVEL_4`
* :py:const:`HEADING_LEVEL_5`
* :py:const:`HEADING_LEVEL_6`
* :const:`HEADING_LEVEL_1`
* :const:`HEADING_LEVEL_2`
* :const:`HEADING_LEVEL_3`
* :const:`HEADING_LEVEL_4`
* :const:`HEADING_LEVEL_5`
* :const:`HEADING_LEVEL_6`

* :py:const:`TAG`
* :const:`TAG`

* :py:const:`TAG_OPEN`
* :py:const:`TAG_ATTR`
* :py:const:`TAG_BODY`
* :py:const:`TAG_CLOSE`
* :const:`TAG_OPEN`
* :const:`TAG_ATTR`
* :const:`TAG_BODY`
* :const:`TAG_CLOSE`

* :py:const:`STYLE`
* :const:`STYLE`

* :py:const:`STYLE_ITALICS`
* :py:const:`STYLE_BOLD`
* :py:const:`STYLE_PASS_AGAIN`
* :py:const:`STYLE_SECOND_PASS`
* :const:`STYLE_ITALICS`
* :const:`STYLE_BOLD`
* :const:`STYLE_PASS_AGAIN`
* :const:`STYLE_SECOND_PASS`

* :py:const:`DL_TERM`
* :const:`DL_TERM`

* :py:const:`SAFETY_CHECK`
* :const:`SAFETY_CHECK`

* :py:const:`HAS_TEXT`
* :py:const:`FAIL_ON_TEXT`
* :py:const:`FAIL_NEXT`
* :py:const:`FAIL_ON_LBRACE`
* :py:const:`FAIL_ON_RBRACE`
* :py:const:`FAIL_ON_EQUALS`
* :const:`HAS_TEXT`
* :const:`FAIL_ON_TEXT`
* :const:`FAIL_NEXT`
* :const:`FAIL_ON_LBRACE`
* :const:`FAIL_ON_RBRACE`
* :const:`FAIL_ON_EQUALS`

Global contexts:

* :py:const:`GL_HEADING`
* :const:`GL_HEADING`

Aggregate contexts:

* :py:const:`FAIL`
* :py:const:`UNSAFE`
* :py:const:`DOUBLE`
* :py:const:`NO_WIKILINKS`
* :py:const:`NO_EXT_LINKS`
* :const:`FAIL`
* :const:`UNSAFE`
* :const:`DOUBLE`
* :const:`NO_WIKILINKS`
* :const:`NO_EXT_LINKS`

"""



+ 6
- 6
mwparserfromhell/parser/tokenizer.py Zobrazit soubor

@@ -135,7 +135,7 @@ class Tokenizer(object):
"""Fail the current tokenization route.

Discards the current stack/context/textbuffer and raises
:py:exc:`~.BadRoute`.
:exc:`.BadRoute`.
"""
context = self._context
self._pop()
@@ -173,14 +173,14 @@ class Tokenizer(object):
def _read(self, delta=0, wrap=False, strict=False):
"""Read the value at a relative point in the wikicode.

The value is read from :py:attr:`self._head <_head>` plus the value of
The value is read from :attr:`self._head <_head>` plus the value of
*delta* (which can be negative). If *wrap* is ``False``, we will not
allow attempts to read from the end of the string if ``self._head +
delta`` is negative. If *strict* is ``True``, the route will be failed
(with :py:meth:`_fail_route`) if we try to read from past the end of
the string; otherwise, :py:attr:`self.END <END>` is returned. If we try
to read from before the start of the string, :py:attr:`self.START
<START>` is returned.
(with :meth:`_fail_route`) if we try to read from past the end of the
string; otherwise, :attr:`self.END <END>` is returned. If we try to
read from before the start of the string, :attr:`self.START <START>` is
returned.
"""
index = self._head + delta
if index < 0 and (not wrap or abs(index) > len(self._text)):


+ 2
- 2
mwparserfromhell/parser/tokens.py Zobrazit soubor

@@ -24,8 +24,8 @@
This module contains the token definitions that are used as an intermediate
parsing data type - they are stored in a flat list, with each token being
identified by its type and optional attributes. The token list is generated in
a syntactically valid form by the :py:class:`~.Tokenizer`, and then converted
into the :py:class`~.Wikicode` tree by the :py:class:`~.Builder`.
a syntactically valid form by the :class:`.Tokenizer`, and then converted into
the :class`.Wikicode` tree by the :class:`.Builder`.
"""

from __future__ import unicode_literals


+ 11
- 11
mwparserfromhell/smart_list.py Zobrazit soubor

@@ -21,8 +21,8 @@
# SOFTWARE.

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

@@ -35,7 +35,7 @@ __all__ = ["SmartList"]
def inheritdoc(method):
"""Set __doc__ of *method* to __doc__ of *method* in its parent class.

Since this is used on :py:class:`~.SmartList`, the "parent class" used is
Since this is used on :class:`.SmartList`, the "parent class" used is
``list``. This function can be used as a decorator.
"""
method.__doc__ = getattr(list, method.__name__).__doc__
@@ -65,9 +65,9 @@ class SmartList(_SliceNormalizerMixIn, list):
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:`~._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::
:class:`._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
@@ -84,8 +84,8 @@ class SmartList(_SliceNormalizerMixIn, list):
The parent needs to keep a list of its children in order to update them,
which prevents them from being garbage-collected. If you are keeping the
parent around for a while but creating many children, it is advisable to
call :py:meth:`~._ListProxy.detach` when you're finished with them. Certain
parent methods, like :py:meth:`reverse` and :py:meth:`sort`, will do this
call :meth:`._ListProxy.detach` when you're finished with them. Certain
parent methods, like :meth:`reverse` and :meth:`sort`, will do this
automatically.
"""

@@ -217,9 +217,9 @@ class SmartList(_SliceNormalizerMixIn, list):
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
not actually store the list at any time; instead, whenever the list is
needed, it builds it dynamically using the :py:meth:`_render` method.
This is created by a :class:`.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 :meth:`_render` method.
"""

def __init__(self, parent, sliceinfo):


+ 5
- 6
mwparserfromhell/string_mixin.py Zobrazit soubor

@@ -21,7 +21,7 @@
# SOFTWARE.

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

@@ -35,7 +35,7 @@ __all__ = ["StringMixIn"]
def inheritdoc(method):
"""Set __doc__ of *method* to __doc__ of *method* in its parent class.

Since this is used on :py:class:`~.StringMixIn`, the "parent class" used is
Since this is used on :class:`.StringMixIn`, the "parent class" used is
``str``. This function can be used as a decorator.
"""
method.__doc__ = getattr(str, method.__name__).__doc__
@@ -44,11 +44,10 @@ def inheritdoc(method):
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__`
To use this class, inherit from it and override the :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.
The various string methods will operate on the value of :meth:`__unicode__`
instead of the immutable ``self`` like the regular ``str`` type.
"""

if py3k:


+ 10
- 10
mwparserfromhell/utils.py Zobrazit soubor

@@ -34,18 +34,18 @@ from .smart_list import SmartList
__all__ = ["parse_anything"]

def parse_anything(value, context=0, skip_style_tags=False):
"""Return a :py:class:`~.Wikicode` for *value*, allowing multiple types.
"""Return a :class:`.Wikicode` for *value*, allowing multiple types.

This differs from :py:meth:`.Parser.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:`~.Node` or :py:class:`~.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:`~.Wikicode` and others like
:py:class:`~.Template`, such as :py:meth:`wikicode.insert()
<.Wikicode.insert>` or setting :py:meth:`template.name <.Template.name>`.
This differs from :meth:`.Parser.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 :class:`.Node`
or :class:`.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
:class:`.Wikicode` and others like :class:`.Template`, such as
:meth:`wikicode.insert() <.Wikicode.insert>` or setting
:meth:`template.name <.Template.name>`.

Additional arguments are passed directly to :py:meth:`.Parser.parse`.
Additional arguments are passed directly to :meth:`.Parser.parse`.
"""
from .parser import Parser
from .wikicode import Wikicode


+ 89
- 95
mwparserfromhell/wikicode.py Zobrazit soubor

@@ -39,8 +39,8 @@ class Wikicode(StringMixIn):

Additionally, it contains methods that can be used to extract data from or
modify the nodes, implemented in an interface similar to a list. For
example, :py:meth:`index` can get the index of a node in the list, and
:py:meth:`insert` can add a new node at that index. The :py:meth:`filter()
example, :meth:`index` can get the index of a node in the list, and
:meth:`insert` can add a new node at that index. The :meth:`filter()
<ifilter>` series of functions is very useful for extracting and iterating
over, for example, all of the templates in the object.
"""
@@ -55,7 +55,7 @@ class Wikicode(StringMixIn):

@staticmethod
def _get_children(node, contexts=False, restrict=None, parent=None):
"""Iterate over all child :py:class:`.Node`\ s of a given *node*."""
"""Iterate over all child :class:`.Node`\ s of a given *node*."""
yield (parent, node) if contexts else node
if restrict and isinstance(node, restrict):
return
@@ -74,7 +74,7 @@ class Wikicode(StringMixIn):

@staticmethod
def _build_matcher(matches, flags):
"""Helper for :py:meth:`_indexed_ifilter` and others.
"""Helper for :meth:`_indexed_ifilter` and others.

If *matches* is a function, return it. If it's a regex, return a
wrapper around it that can be called with a node to do a search. If
@@ -90,7 +90,7 @@ class Wikicode(StringMixIn):
forcetype=None):
"""Iterate over nodes and their corresponding indices in the node list.

The arguments are interpreted as for :py:meth:`ifilter`. For each tuple
The arguments are interpreted as for :meth:`ifilter`. For each tuple
``(i, node)`` yielded by this method, ``self.index(node) == i``. Note
that if *recursive* is ``True``, ``self.nodes[i]`` might not be the
node itself, but will still contain it.
@@ -111,14 +111,14 @@ class Wikicode(StringMixIn):
def _do_strong_search(self, obj, recursive=True):
"""Search for the specific element *obj* within the node list.

*obj* can be either a :py:class:`.Node` or a :py:class:`.Wikicode`
object. If found, we return a tuple (*context*, *index*) where
*context* is the :py:class:`.Wikicode` that contains *obj* and *index*
is its index there, as a :py:class:`slice`. Note that if *recursive* is
``False``, *context* will always be ``self`` (since we only look for
*obj* among immediate descendants), but if *recursive* is ``True``,
then it could be any :py:class:`.Wikicode` contained by a node within
``self``. If *obj* is not found, :py:exc:`ValueError` is raised.
*obj* can be either a :class:`.Node` or a :class:`.Wikicode` object. If
found, we return a tuple (*context*, *index*) where *context* is the
:class:`.Wikicode` that contains *obj* and *index* is its index there,
as a :class:`slice`. Note that if *recursive* is ``False``, *context*
will always be ``self`` (since we only look for *obj* among immediate
descendants), but if *recursive* is ``True``, then it could be any
:class:`.Wikicode` contained by a node within ``self``. If *obj* is not
found, :exc:`ValueError` is raised.
"""
if isinstance(obj, Node):
mkslice = lambda i: slice(i, i + 1)
@@ -141,14 +141,14 @@ class Wikicode(StringMixIn):
def _do_weak_search(self, obj, recursive):
"""Search for an element that looks like *obj* within the node list.

This follows the same rules as :py:meth:`_do_strong_search` with some
This follows the same rules as :meth:`_do_strong_search` with some
differences. *obj* is treated as a string that might represent any
:py:class:`.Node`, :py:class:`.Wikicode`, or combination of the two
present in the node list. Thus, matching is weak (using string
comparisons) rather than strong (using ``is``). Because multiple nodes
can match *obj*, the result is a list of tuples instead of just one
(however, :py:exc:`ValueError` is still raised if nothing is found).
Individual matches will never overlap.
:class:`.Node`, :class:`.Wikicode`, or combination of the two present
in the node list. Thus, matching is weak (using string comparisons)
rather than strong (using ``is``). Because multiple nodes can match
*obj*, the result is a list of tuples instead of just one (however,
:exc:`ValueError` is still raised if nothing is found). Individual
matches will never overlap.

The tuples contain a new first element, *exact*, which is ``True`` if
we were able to match *obj* exactly to one or more adjacent nodes, or
@@ -212,19 +212,19 @@ class Wikicode(StringMixIn):
def _build_filter_methods(cls, **meths):
"""Given Node types, build the corresponding i?filter shortcuts.

The should be given as keys storing the method's base name paired
with values storing the corresponding :py:class:`~.Node` type. For
example, the dict may contain the pair ``("templates", Template)``,
which will produce the methods :py:meth:`ifilter_templates` and
:py:meth:`filter_templates`, which are shortcuts for
:py:meth:`ifilter(forcetype=Template) <ifilter>` and
:py:meth:`filter(forcetype=Template) <filter>`, respectively. These
The should be given as keys storing the method's base name paired with
values storing the corresponding :class:`.Node` type. For example, the
dict may contain the pair ``("templates", Template)``, which will
produce the methods :meth:`ifilter_templates` and
:meth:`filter_templates`, which are shortcuts for
:meth:`ifilter(forcetype=Template) <ifilter>` and
:meth:`filter(forcetype=Template) <filter>`, respectively. These
shortcuts are added to the class itself, with an appropriate docstring.
"""
doc = """Iterate over {0}.

This is equivalent to :py:meth:`{1}` with *forcetype* set to
:py:class:`~{2.__module__}.{2.__name__}`.
This is equivalent to :meth:`{1}` with *forcetype* set to
:class:`~{2.__module__}.{2.__name__}`.
"""
make_ifilter = lambda ftype: (lambda self, *a, **kw:
self.ifilter(forcetype=ftype, *a, **kw))
@@ -240,10 +240,10 @@ class Wikicode(StringMixIn):

@property
def nodes(self):
"""A list of :py:class:`~.Node` objects.
"""A list of :class:`.Node` objects.

This is the internal data actually stored within a
:py:class:`~.Wikicode` object.
This is the internal data actually stored within a :class:`.Wikicode`
object.
"""
return self._nodes

@@ -260,11 +260,10 @@ class Wikicode(StringMixIn):
def set(self, index, value):
"""Set the ``Node`` at *index* to *value*.

Raises :py:exc:`IndexError` if *index* is out of range, or
:py:exc:`ValueError` if *value* cannot be coerced into one
:py:class:`~.Node`. To insert multiple nodes at an index, use
:py:meth:`get` with either :py:meth:`remove` and :py:meth:`insert` or
:py:meth:`replace`.
Raises :exc:`IndexError` if *index* is out of range, or
:exc:`ValueError` if *value* cannot be coerced into one :class:`.Node`.
To insert multiple nodes at an index, use :meth:`get` with either
:meth:`remove` and :meth:`insert` or :meth:`replace`.
"""
nodes = parse_anything(value).nodes
if len(nodes) > 1:
@@ -279,7 +278,7 @@ class Wikicode(StringMixIn):
def index(self, obj, recursive=False):
"""Return the index of *obj* in the list of nodes.

Raises :py:exc:`ValueError` if *obj* is not found. If *recursive* is
Raises :exc:`ValueError` if *obj* is not found. If *recursive* is
``True``, we will look in all nodes of ours and their descendants, and
return the index of our direct descendant node within *our* list of
nodes. Otherwise, the lookup is done only on direct descendants.
@@ -298,9 +297,8 @@ class Wikicode(StringMixIn):
def insert(self, index, value):
"""Insert *value* at *index* in the list of nodes.

*value* can be anything parsable by :py:func:`.parse_anything`, which
includes strings or other :py:class:`~.Wikicode` or :py:class:`~.Node`
objects.
*value* can be anything parsable by :func:`.parse_anything`, which
includes strings or other :class:`.Wikicode` or :class:`.Node` objects.
"""
nodes = parse_anything(value).nodes
for node in reversed(nodes):
@@ -309,15 +307,14 @@ class Wikicode(StringMixIn):
def insert_before(self, obj, value, recursive=True):
"""Insert *value* immediately before *obj*.

*obj* can be either a string, a :py:class:`~.Node`, or another
:py:class:`~.Wikicode` object (as created by :py:meth:`get_sections`,
for example). If *obj* is a string, we will operate on all instances
of that string within the code, otherwise only on the specific instance
given. *value* can be anything parsable by :py:func:`.parse_anything`.
If *recursive* is ``True``, we will try to find *obj* within our child
nodes even if it is not a direct descendant of this
:py:class:`~.Wikicode` object. If *obj* is not found,
:py:exc:`ValueError` is raised.
*obj* can be either a string, a :class:`.Node`, or another
:class:`.Wikicode` object (as created by :meth:`get_sections`, for
example). If *obj* is a string, we will operate on all instances of
that string within the code, otherwise only on the specific instance
given. *value* can be anything parsable by :func:`.parse_anything`. If
*recursive* is ``True``, we will try to find *obj* within our child
nodes even if it is not a direct descendant of this :class:`.Wikicode`
object. If *obj* is not found, :exc:`ValueError` is raised.
"""
if isinstance(obj, (Node, Wikicode)):
context, index = self._do_strong_search(obj, recursive)
@@ -333,15 +330,14 @@ class Wikicode(StringMixIn):
def insert_after(self, obj, value, recursive=True):
"""Insert *value* immediately after *obj*.

*obj* can be either a string, a :py:class:`~.Node`, or another
:py:class:`~.Wikicode` object (as created by :py:meth:`get_sections`,
for example). If *obj* is a string, we will operate on all instances
of that string within the code, otherwise only on the specific instance
given. *value* can be anything parsable by :py:func:`.parse_anything`.
If *recursive* is ``True``, we will try to find *obj* within our child
nodes even if it is not a direct descendant of this
:py:class:`~.Wikicode` object. If *obj* is not found,
:py:exc:`ValueError` is raised.
*obj* can be either a string, a :class:`.Node`, or another
:class:`.Wikicode` object (as created by :meth:`get_sections`, for
example). If *obj* is a string, we will operate on all instances of
that string within the code, otherwise only on the specific instance
given. *value* can be anything parsable by :func:`.parse_anything`. If
*recursive* is ``True``, we will try to find *obj* within our child
nodes even if it is not a direct descendant of this :class:`.Wikicode`
object. If *obj* is not found, :exc:`ValueError` is raised.
"""
if isinstance(obj, (Node, Wikicode)):
context, index = self._do_strong_search(obj, recursive)
@@ -357,15 +353,14 @@ class Wikicode(StringMixIn):
def replace(self, obj, value, recursive=True):
"""Replace *obj* with *value*.

*obj* can be either a string, a :py:class:`~.Node`, or another
:py:class:`~.Wikicode` object (as created by :py:meth:`get_sections`,
for example). If *obj* is a string, we will operate on all instances
of that string within the code, otherwise only on the specific instance
given. *value* can be anything parsable by :py:func:`.parse_anything`.
*obj* can be either a string, a :class:`.Node`, or another
:class:`.Wikicode` object (as created by :meth:`get_sections`, for
example). If *obj* is a string, we will operate on all instances of
that string within the code, otherwise only on the specific instance
given. *value* can be anything parsable by :func:`.parse_anything`.
If *recursive* is ``True``, we will try to find *obj* within our child
nodes even if it is not a direct descendant of this
:py:class:`~.Wikicode` object. If *obj* is not found,
:py:exc:`ValueError` is raised.
nodes even if it is not a direct descendant of this :class:`.Wikicode`
object. If *obj* is not found, :exc:`ValueError` is raised.
"""
if isinstance(obj, (Node, Wikicode)):
context, index = self._do_strong_search(obj, recursive)
@@ -384,7 +379,7 @@ class Wikicode(StringMixIn):
def append(self, value):
"""Insert *value* at the end of the list of nodes.

*value* can be anything parsable by :py:func:`.parse_anything`.
*value* can be anything parsable by :func:`.parse_anything`.
"""
nodes = parse_anything(value).nodes
for node in nodes:
@@ -393,14 +388,14 @@ class Wikicode(StringMixIn):
def remove(self, obj, recursive=True):
"""Remove *obj* from the list of nodes.

*obj* can be either a string, a :py:class:`~.Node`, or another
:py:class:`~.Wikicode` object (as created by :py:meth:`get_sections`,
for example). If *obj* is a string, we will operate on all instances
of that string within the code, otherwise only on the specific instance
*obj* can be either a string, a :class:`.Node`, or another
:class:`.Wikicode` object (as created by :meth:`get_sections`, for
example). If *obj* is a string, we will operate on all instances of
that string within the code, otherwise only on the specific instance
given. If *recursive* is ``True``, we will try to find *obj* within our
child nodes even if it is not a direct descendant of this
:py:class:`~.Wikicode` object. If *obj* is not found,
:py:exc:`ValueError` is raised.
:class:`.Wikicode` object. If *obj* is not found, :exc:`ValueError` is
raised.
"""
if isinstance(obj, (Node, Wikicode)):
context, index = self._do_strong_search(obj, recursive)
@@ -417,10 +412,10 @@ class Wikicode(StringMixIn):
def matches(self, other):
"""Do a loose equivalency test suitable for comparing page names.

*other* can be any string-like object, including
:py:class:`~.Wikicode`, or a tuple of these. This operation is
symmetric; both sides are adjusted. Specifically, whitespace and markup
is stripped and the first letter's case is normalized. Typical usage is
*other* can be any string-like object, including :class:`.Wikicode`, or
a tuple of these. This operation is symmetric; both sides are adjusted.
Specifically, whitespace and markup is stripped and the first letter's
case is normalized. Typical usage is
``if template.name.matches("stub"): ...``.
"""
cmp = lambda a, b: (a[0].upper() + a[1:] == b[0].upper() + b[1:]
@@ -453,12 +448,12 @@ class Wikicode(StringMixIn):
["{{foo}}", "{{foo|{{bar}}}}"]

*matches* can be used to further restrict the nodes, either as a
function (taking a single :py:class:`.Node` and returning a boolean) or
a regular expression (matched against the node's string representation
with :py:func:`re.search`). If *matches* is a regex, the flags passed
to :py:func:`re.search` are :py:const:`re.IGNORECASE`,
:py:const:`re.DOTALL`, and :py:const:`re.UNICODE`, but custom flags can
be specified by passing *flags*.
function (taking a single :class:`.Node` and returning a boolean) or a
regular expression (matched against the node's string representation
with :func:`re.search`). If *matches* is a regex, the flags passed to
:func:`re.search` are :const:`re.IGNORECASE`, :const:`re.DOTALL`, and
:const:`re.UNICODE`, but custom flags can be specified by passing
*flags*.
"""
gen = self._indexed_ifilter(recursive, matches, flags, forcetype)
return (node for i, node in gen)
@@ -466,7 +461,7 @@ class Wikicode(StringMixIn):
def filter(self, *args, **kwargs):
"""Return a list of nodes within our list matching certain conditions.

This is equivalent to calling :py:func:`list` on :py:meth:`ifilter`.
This is equivalent to calling :func:`list` on :meth:`ifilter`.
"""
return list(self.ifilter(*args, **kwargs))

@@ -474,9 +469,9 @@ class Wikicode(StringMixIn):
include_lead=None, include_headings=True):
"""Return a list of sections within the page.

Sections are returned as :py:class:`~.Wikicode` objects with a shared
node list (implemented using :py:class:`~.SmartList`) so that changes
to sections are reflected in the parent Wikicode object.
Sections are returned as :class:`.Wikicode` objects with a shared node
list (implemented using :class:`.SmartList`) so that changes to
sections are reflected in the parent Wikicode object.

Each section contains all of its subsections, unless *flat* is
``True``. If *levels* is given, it should be a iterable of integers;
@@ -484,14 +479,13 @@ class Wikicode(StringMixIn):
*matches* is given, it should be either a function or a regex; only
sections whose headings match it (without the surrounding equal signs)
will be included. *flags* can be used to override the default regex
flags (see :py:meth:`ifilter`) if a regex *matches* is used.
flags (see :meth:`ifilter`) if a regex *matches* is used.

If *include_lead* is ``True``, the first, lead section (without a
heading) will be included in the list; ``False`` will not include it;
the default will include it only if no specific *levels* were given. If
*include_headings* is ``True``, the section's beginning
:py:class:`~.Heading` object will be included; otherwise, this is
skipped.
:class:`.Heading` object will be included; otherwise, this is skipped.
"""
title_matcher = self._build_matcher(matches, flags)
matcher = lambda heading: (title_matcher(heading.title) and
@@ -540,7 +534,7 @@ class Wikicode(StringMixIn):
"""Return a rendered string without unprintable code such as templates.

The way a node is stripped is handled by the
:py:meth:`~.Node.__strip__` method of :py:class:`~.Node` objects, which
:meth:`~.Node.__strip__` method of :class:`.Node` objects, which
generally return a subset of their nodes or ``None``. For example,
templates and tags are removed completely, links are stripped to just
their display part, headings are stripped to just their title. If
@@ -568,9 +562,9 @@ class Wikicode(StringMixIn):
"""Return a hierarchical tree representation of the object.

The representation is a string makes the most sense printed. It is
built by calling :py:meth:`_get_tree` on the
:py:class:`~.Wikicode` object and its children recursively. The end
result may look something like the following::
built by calling :meth:`_get_tree` on the :class:`.Wikicode` object and
its children recursively. The end result may look something like the
following::

>>> text = "Lorem ipsum {{foo|bar|{{baz}}|spam=eggs}}"
>>> print mwparserfromhell.parse(text).get_tree()


Načítá se…
Zrušit
Uložit