Browse Source

Merge branch 'develop'

tags/v0.4
Ben Kurtovic 10 years ago
parent
commit
38957be574
11 changed files with 138 additions and 29 deletions
  1. +1
    -0
      .travis.yml
  2. +6
    -0
      CHANGELOG
  3. +11
    -0
      docs/changelog.rst
  4. +1
    -1
      mwparserfromhell/__init__.py
  5. +2
    -1
      mwparserfromhell/compat.py
  6. +29
    -12
      mwparserfromhell/nodes/template.py
  7. +4
    -4
      mwparserfromhell/string_mixin.py
  8. +1
    -0
      setup.py
  9. +6
    -4
      tests/test_string_mixin.py
  10. +76
    -6
      tests/test_template.py
  11. +1
    -1
      tests/tokenizer/tags.mwtest

+ 1
- 0
.travis.yml View File

@@ -1,6 +1,7 @@
language: python
python:
- "2.7"
- "3.2"
- "3.3"
install: python setup.py build
script: python setup.py test -q

+ 6
- 0
CHANGELOG View File

@@ -1,3 +1,9 @@
v0.3.2 (released September 1, 2013):

- Added support for Python 3.2 (along with current support for 3.3 and 2.7).
- Renamed Template.remove()'s first argument from 'name' to 'param', which now
accepts Parameter objects in addition to parameter name strings.

v0.3.1 (released August 29, 2013):

- Fixed a parser bug involving URLs nested inside other markup.


+ 11
- 0
docs/changelog.rst View File

@@ -1,6 +1,17 @@
Changelog
=========

v0.3.2
------

`Released September 1, 2013 <https://github.com/earwig/mwparserfromhell/tree/v0.3.2>`_
(`changes <https://github.com/earwig/mwparserfromhell/compare/v0.3.1...v0.3.2>`__):

- Added support for Python 3.2 (along with current support for 3.3 and 2.7).
- Renamed :py:meth:`.Template.remove`\ 's first argument from *name* to
*param*, which now accepts :py:class:`.Parameter` objects in addition to
parameter name strings.

v0.3.1
------



+ 1
- 1
mwparserfromhell/__init__.py View File

@@ -31,7 +31,7 @@ from __future__ import unicode_literals
__author__ = "Ben Kurtovic"
__copyright__ = "Copyright (C) 2012, 2013 Ben Kurtovic"
__license__ = "MIT License"
__version__ = "0.3.1"
__version__ = "0.3.2"
__email__ = "ben.kurtovic@verizon.net"

from . import (compat, definitions, nodes, parser, smart_list, string_mixin,


+ 2
- 1
mwparserfromhell/compat.py View File

@@ -10,7 +10,8 @@ types are meant to be imported directly from within the parser's modules.

import sys

py3k = sys.version_info[0] == 3
py3k = sys.version_info.major == 3
py32 = py3k and sys.version_info.minor == 2

if py3k:
bytes = bytes


+ 29
- 12
mwparserfromhell/nodes/template.py View File

@@ -150,6 +150,16 @@ class Template(Node):
return False
return True

def _remove_exact(self, needle, keep_field):
"""Remove a specific parameter, *needle*, from the template."""
for i, param in enumerate(self.params):
if param is needle:
if keep_field or not self._remove_without_field(param, i):
self._blank_param_value(param.value)
else:
self.params.pop(i)
return

@property
def name(self):
"""The name of the template, as a :py:class:`~.Wikicode` object."""
@@ -180,7 +190,8 @@ class Template(Node):
return True
return False

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

def get(self, name):
@@ -280,8 +291,12 @@ class Template(Node):
self.params.append(param)
return param

def remove(self, name, keep_field=False):
"""Remove a parameter from the template whose name is *name*.
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 *keep_field* is ``True``, we will keep the parameter's name, but
blank its value. Otherwise, we will remove the parameter completely
@@ -289,12 +304,14 @@ class Template(Node):
from ``{{foo|bar|baz}}`` is unsafe because ``{{foo|baz}}`` is not what
we expected, so ``{{foo||baz}}`` will be produced instead).

If the parameter shows up multiple times in the template, we will
remove all instances of it (and keep one if *keep_field* is ``True`` -
the first instance if none have dependents, otherwise the one with
dependents will be kept).
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
(and keep only one if *keep_field* is ``True`` - the first instance if
none have dependents, otherwise the one with dependents will be kept).
"""
name = str(name).strip()
if isinstance(param, Parameter):
return self._remove_exact(param, keep_field)
name = str(param).strip()
removed = False
to_remove = []
for i, param in enumerate(self.params):
@@ -304,15 +321,15 @@ class Template(Node):
self._blank_param_value(param.value)
keep_field = False
else:
to_remove.append(param)
to_remove.append(i)
else:
if self._remove_without_field(param, i):
to_remove.append(param)
to_remove.append(i)
else:
self._blank_param_value(param.value)
if not removed:
removed = True
if not removed:
raise ValueError(name)
for param in to_remove:
self.params.remove(param)
for i in reversed(to_remove):
self.params.pop(i)

+ 4
- 4
mwparserfromhell/string_mixin.py View File

@@ -27,7 +27,7 @@ interface for the ``unicode`` type (``str`` on py3k) in a dynamic manner.

from __future__ import unicode_literals

from .compat import py3k, str
from .compat import py3k, py32, str

__all__ = ["StringMixIn"]

@@ -125,7 +125,7 @@ class StringMixIn(object):
def capitalize(self):
return self.__unicode__().capitalize()

if py3k:
if py3k and not py32:
@inheritdoc
def casefold(self):
return self.__unicode__().casefold()
@@ -288,7 +288,7 @@ class StringMixIn(object):
def rpartition(self, sep):
return self.__unicode__().rpartition(sep)

if py3k:
if py3k and not py32:
@inheritdoc
def rsplit(self, sep=None, maxsplit=None):
kwargs = {}
@@ -310,7 +310,7 @@ class StringMixIn(object):
def rstrip(self, chars=None):
return self.__unicode__().rstrip(chars)

if py3k:
if py3k and not py32:
@inheritdoc
def split(self, sep=None, maxsplit=None):
kwargs = {}


+ 1
- 0
setup.py View File

@@ -54,6 +54,7 @@ setup(
"Operating System :: OS Independent",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.2",
"Programming Language :: Python :: 3.3",
"Topic :: Text Processing :: Markup"
],


+ 6
- 4
tests/test_string_mixin.py View File

@@ -25,7 +25,7 @@ from sys import getdefaultencoding
from types import GeneratorType
import unittest

from mwparserfromhell.compat import bytes, py3k, str
from mwparserfromhell.compat import bytes, py3k, py32, str
from mwparserfromhell.string_mixin import StringMixIn

from .compat import range
@@ -52,8 +52,10 @@ class TestStringMixIn(unittest.TestCase):
"rsplit", "rstrip", "split", "splitlines", "startswith", "strip",
"swapcase", "title", "translate", "upper", "zfill"]
if py3k:
methods.extend(["casefold", "format_map", "isidentifier",
"isprintable", "maketrans"])
if not py32:
methods.append("casefold")
methods.extend(["format_map", "isidentifier", "isprintable",
"maketrans"])
else:
methods.append("decode")
for meth in methods:
@@ -325,7 +327,7 @@ class TestStringMixIn(unittest.TestCase):
self.assertEqual("", str15.lower())
self.assertEqual("foobar", str16.lower())
self.assertEqual("ß", str22.lower())
if py3k:
if py3k and not py32:
self.assertEqual("", str15.casefold())
self.assertEqual("foobar", str16.casefold())
self.assertEqual("ss", str22.casefold())


+ 76
- 6
tests/test_template.py View File

@@ -316,12 +316,12 @@ class TestTemplate(TreeEqualityTestCase):
def test_remove(self):
"""test Template.remove()"""
node1 = Template(wraptext("foobar"))
node2 = Template(wraptext("foo"), [pgenh("1", "bar"),
pgens("abc", "def")])
node3 = Template(wraptext("foo"), [pgenh("1", "bar"),
pgens("abc", "def")])
node4 = Template(wraptext("foo"), [pgenh("1", "bar"),
pgenh("2", "baz")])
node2 = Template(wraptext("foo"),
[pgenh("1", "bar"), pgens("abc", "def")])
node3 = Template(wraptext("foo"),
[pgenh("1", "bar"), pgens("abc", "def")])
node4 = Template(wraptext("foo"),
[pgenh("1", "bar"), pgenh("2", "baz")])
node5 = Template(wraptext("foo"), [
pgens(" a", "b"), pgens("b", "c"), pgens("a ", "d")])
node6 = Template(wraptext("foo"), [
@@ -334,6 +334,44 @@ class TestTemplate(TreeEqualityTestCase):
pgens("1 ", "a"), pgenh("1", "b"), pgenh("2", "c")])
node10 = Template(wraptext("foo"), [
pgens("1 ", "a"), pgenh("1", "b"), pgenh("2", "c")])
node11 = Template(wraptext("foo"), [
pgens(" a", "b"), pgens("b", "c"), pgens("a ", "d")])
node12 = Template(wraptext("foo"), [
pgens(" a", "b"), pgens("b", "c"), pgens("a ", "d")])
node13 = Template(wraptext("foo"), [
pgens(" a", "b"), pgens("b", "c"), pgens("a ", "d")])
node14 = Template(wraptext("foo"), [
pgens(" a", "b"), pgens("b", "c"), pgens("a ", "d")])
node15 = Template(wraptext("foo"), [
pgens(" a", "b"), pgens("b", "c"), pgens("a ", "d")])
node16 = Template(wraptext("foo"), [
pgens(" a", "b"), pgens("b", "c"), pgens("a ", "d")])
node17 = Template(wraptext("foo"), [
pgens("1 ", "a"), pgenh("1", "b"), pgenh("2", "c")])
node18 = Template(wraptext("foo"), [
pgens("1 ", "a"), pgenh("1", "b"), pgenh("2", "c")])
node19 = Template(wraptext("foo"), [
pgens("1 ", "a"), pgenh("1", "b"), pgenh("2", "c")])
node20 = Template(wraptext("foo"), [
pgens("1 ", "a"), pgenh("1", "b"), pgenh("2", "c")])
node21 = Template(wraptext("foo"), [
pgens("a", "b"), pgens("c", "d"), pgens("e", "f"), pgens("a", "b"),
pgens("a", "b")])
node22 = Template(wraptext("foo"), [
pgens("a", "b"), pgens("c", "d"), pgens("e", "f"), pgens("a", "b"),
pgens("a", "b")])
node23 = Template(wraptext("foo"), [
pgens("a", "b"), pgens("c", "d"), pgens("e", "f"), pgens("a", "b"),
pgens("a", "b")])
node24 = Template(wraptext("foo"), [
pgens("a", "b"), pgens("c", "d"), pgens("e", "f"), pgens("a", "b"),
pgens("a", "b")])
node25 = Template(wraptext("foo"), [
pgens("a", "b"), pgens("c", "d"), pgens("e", "f"), pgens("a", "b"),
pgens("a", "b")])
node26 = Template(wraptext("foo"), [
pgens("a", "b"), pgens("c", "d"), pgens("e", "f"), pgens("a", "b"),
pgens("a", "b")])

node2.remove("1")
node2.remove("abc")
@@ -346,6 +384,22 @@ class TestTemplate(TreeEqualityTestCase):
node8.remove(1, keep_field=False)
node9.remove(1, keep_field=True)
node10.remove(1, keep_field=False)
node11.remove(node11.params[0], keep_field=False)
node12.remove(node12.params[0], keep_field=True)
node13.remove(node13.params[1], keep_field=False)
node14.remove(node14.params[1], keep_field=True)
node15.remove(node15.params[2], keep_field=False)
node16.remove(node16.params[2], keep_field=True)
node17.remove(node17.params[0], keep_field=False)
node18.remove(node18.params[0], keep_field=True)
node19.remove(node19.params[1], keep_field=False)
node20.remove(node20.params[1], keep_field=True)
node21.remove("a", keep_field=False)
node22.remove("a", keep_field=True)
node23.remove(node23.params[0], keep_field=False)
node24.remove(node24.params[0], keep_field=True)
node25.remove(node25.params[3], keep_field=False)
node26.remove(node26.params[3], keep_field=True)

self.assertRaises(ValueError, node1.remove, 1)
self.assertRaises(ValueError, node1.remove, "a")
@@ -359,6 +413,22 @@ class TestTemplate(TreeEqualityTestCase):
self.assertEqual("{{foo|2=c}}", node8)
self.assertEqual("{{foo||c}}", node9)
self.assertEqual("{{foo||c}}", node10)
self.assertEqual("{{foo|b=c|a =d}}", node11)
self.assertEqual("{{foo| a=|b=c|a =d}}", node12)
self.assertEqual("{{foo| a=b|a =d}}", node13)
self.assertEqual("{{foo| a=b|b=|a =d}}", node14)
self.assertEqual("{{foo| a=b|b=c}}", node15)
self.assertEqual("{{foo| a=b|b=c|a =}}", node16)
self.assertEqual("{{foo|b|c}}", node17)
self.assertEqual("{{foo|1 =|b|c}}", node18)
self.assertEqual("{{foo|1 =a||c}}", node19)
self.assertEqual("{{foo|1 =a||c}}", node20)
self.assertEqual("{{foo|c=d|e=f}}", node21)
self.assertEqual("{{foo|a=|c=d|e=f}}", node22)
self.assertEqual("{{foo|c=d|e=f|a=b|a=b}}", node23)
self.assertEqual("{{foo|a=|c=d|e=f|a=b|a=b}}", node24)
self.assertEqual("{{foo|a=b|c=d|e=f|a=b}}", node25)
self.assertEqual("{{foo|a=b|c=d|e=f|a=|a=b}}", node26)

if __name__ == "__main__":
unittest.main(verbosity=2)

+ 1
- 1
tests/tokenizer/tags.mwtest View File

@@ -470,7 +470,7 @@ output: [TemplateOpen(), Text(text="t1"), TemplateClose(), TagOpenOpen(), Text(t
name: unparsable_attributed
label: a tag that should not be put through the normal parser; parsed attributes
input: "{{t1}}<nowiki attr=val attr2="{{val2}}">{{t2}}</nowiki>{{t3}}"
output: [TemplateOpen(), Text(text=u't1'), TemplateClose(), TagOpenOpen(), Text(text="nowiki"), TagAttrStart(pad_first=" ", pad_before_eq="", pad_after_eq=""), Text(text="attr"), TagAttrEquals(), Text(text="val"), TagAttrStart(pad_first=" ", pad_before_eq="", pad_after_eq=""), Text(text="attr2"), TagAttrEquals(), TagAttrQuote(), TemplateOpen(), Text(text="val2"), TemplateClose(), TagCloseOpen(padding=""), Text(text="{{t2}}"), TagOpenClose(), Text(text="nowiki"), TagCloseClose(), TemplateOpen(), Text(text="t3"), TemplateClose()]
output: [TemplateOpen(), Text(text="t1"), TemplateClose(), TagOpenOpen(), Text(text="nowiki"), TagAttrStart(pad_first=" ", pad_before_eq="", pad_after_eq=""), Text(text="attr"), TagAttrEquals(), Text(text="val"), TagAttrStart(pad_first=" ", pad_before_eq="", pad_after_eq=""), Text(text="attr2"), TagAttrEquals(), TagAttrQuote(), TemplateOpen(), Text(text="val2"), TemplateClose(), TagCloseOpen(padding=""), Text(text="{{t2}}"), TagOpenClose(), Text(text="nowiki"), TagCloseClose(), TemplateOpen(), Text(text="t3"), TemplateClose()]

---



Loading…
Cancel
Save