| @@ -32,5 +32,11 @@ __license__ = "MIT License" | |||
| __version__ = "0.1.dev" | |||
| __email__ = "ben.kurtovic@verizon.net" | |||
| from mwparserfromhell import parameter, parser, template | |||
| from mwparserfromhell.node import Node | |||
| from mwparserfromhell.parameter import Parameter | |||
| from mwparserfromhell.parser import Parser | |||
| from mwparserfromhell.template import Template | |||
| from mwparserfromhell.text import Text | |||
| from mwparserfromhell.wikicode import Wikicode | |||
| parse = Parser().parse | |||
| @@ -0,0 +1,26 @@ | |||
| # -*- coding: utf-8 -*- | |||
| # | |||
| # Copyright (C) 2012 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||
| # | |||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | |||
| # of this software and associated documentation files (the "Software"), to deal | |||
| # in the Software without restriction, including without limitation the rights | |||
| # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
| # copies of the Software, and to permit persons to whom the Software is | |||
| # furnished to do so, subject to the following conditions: | |||
| # | |||
| # The above copyright notice and this permission notice shall be included in | |||
| # all copies or substantial portions of the Software. | |||
| # | |||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
| # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||
| # SOFTWARE. | |||
| __all__ = ["Node"] | |||
| class Node(object): | |||
| pass | |||
| @@ -20,108 +20,20 @@ | |||
| # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||
| # SOFTWARE. | |||
| from mwparserfromhell.template import Template | |||
| from mwparserfromhell.string_mixin import StringMixin | |||
| __all__ = ["Parameter"] | |||
| class Parameter(object): | |||
| def __init__(self, name, value, templates=None): | |||
| class Parameter(StringMixin): | |||
| def __init__(self, name, value=None, showkey=True): | |||
| self._name = name | |||
| self._value = value | |||
| if templates: | |||
| self._templates = templates | |||
| else: | |||
| self._templates = [] | |||
| self._showkey = showkey | |||
| def __repr__(self): | |||
| return repr(self.value) | |||
| def __str__(self): | |||
| return self.value | |||
| def __lt__(self, other): | |||
| if isinstance(other, Parameter): | |||
| return self.value < other.value | |||
| return self.value < other | |||
| def __le__(self, other): | |||
| if isinstance(other, Parameter): | |||
| return self.value <= other.value | |||
| return self.value <= other | |||
| def __eq__(self, other): | |||
| if isinstance(other, Parameter): | |||
| return (self.name == other.name and self.value == other.value and | |||
| self.templates == other.templates) | |||
| return self.value == other | |||
| def __ne__(self, other): | |||
| if isinstance(other, Parameter): | |||
| return (self.name != other.name or self.value != other.value or | |||
| self.templates != other.templates) | |||
| return self.value != other | |||
| def __gt__(self, other): | |||
| if isinstance(other, Parameter): | |||
| return self.value > other.value | |||
| return self.value > other | |||
| def __ge__(self, other): | |||
| if isinstance(other, Parameter): | |||
| return self.value >= other.value | |||
| return self.value >= other | |||
| def __nonzero__(self): | |||
| return bool(self.value) | |||
| def __len__(self): | |||
| return len(self.value) | |||
| def __iter__(self): | |||
| for char in self.value: | |||
| yield char | |||
| def __getitem__(self, key): | |||
| return self.value[key] | |||
| def __contains__(self, item): | |||
| return item in self.value or item in self.templates | |||
| def __add__(self, other): | |||
| if isinstance(other, Parameter): | |||
| return Parameter(self.name, self.value + other.value, | |||
| self.templates + other.templates) | |||
| if isinstance(other, Template): | |||
| return Parameter(self.name, self.value + other.render(), | |||
| self.templates + [other]) | |||
| return self.value + other | |||
| def __radd__(self, other): | |||
| if isinstance(other, Template): | |||
| return Template(other.name, other.params + [self]) | |||
| return other + self.value | |||
| def __iadd__(self, other): | |||
| if isinstance(other, Parameter): | |||
| self.value += other.value | |||
| self.templates += other.templates | |||
| elif isinstance(other, Template): | |||
| self.value += other.render() | |||
| self.templates.append(other) | |||
| else: | |||
| self.value += other | |||
| return self | |||
| def __mul__(self, other): | |||
| return Parameter(self.name, self.value * other, self.templates * other) | |||
| def __rmul__(self, other): | |||
| return Parameter(self.name, other * self.value, other * self.templates) | |||
| def __imul__(self, other): | |||
| self.value *= other | |||
| self.templates *= other | |||
| return self | |||
| def __unicode__(self): | |||
| if self.showkey: | |||
| return unicode(self.name) + "=" + unicode(self.value) | |||
| return unicode(self.value) | |||
| @property | |||
| def name(self): | |||
| @@ -132,5 +44,5 @@ class Parameter(object): | |||
| return self._value | |||
| @property | |||
| def templates(self): | |||
| return self._templates | |||
| def showkey(self): | |||
| return self._showkey | |||
| @@ -22,15 +22,28 @@ | |||
| from mwparserfromhell.parameter import Parameter | |||
| from mwparserfromhell.template import Template | |||
| from mwparserfromhell.text import Text | |||
| from mwparserfromhell.wikicode import Wikicode | |||
| __all__ = ["Parser"] | |||
| class Parser(object): | |||
| def _tokenize(self, text): | |||
| return text | |||
| def parse(self, text): | |||
| tokens = self._tokenize(text) | |||
| params = [Parameter("1", "bar"), Parameter("2", "baz")] | |||
| templates = [Template(name="foo", params=params)] | |||
| return templates | |||
| text = u"This is a {{test}} message with a {{template|with|foo={{params}}}}." | |||
| node1 = Text(u"This is a ") | |||
| node2 = Template(Wikicode([Text(u"test")])) | |||
| node3 = Text(u" message with a ") | |||
| node4_param1_name = Wikicode([Text(u"1")]) | |||
| node4_param1_value = Wikicode([Text(u"with")]) | |||
| node4_param1 = Parameter(node4_param1_name, node4_param1_value, showkey=False) | |||
| node4_param2_name = Wikicode([Text(u"foo")]) | |||
| node4_param2_value = Wikicode([Template(Wikicode([Text(u"params")]))]) | |||
| node4_param2 = Parameter(node4_param2_name, node4_param2_value, showkey=True) | |||
| node4 = Template(Wikicode([Text(u"template")]), [node4_param1, node4_param2]) | |||
| node5 = Text(u".") | |||
| parsed = Wikicode([node1, node2, node3, node4, node5]) | |||
| return parsed | |||
| @@ -0,0 +1,63 @@ | |||
| # -*- coding: utf-8 -*- | |||
| # | |||
| # Copyright (C) 2012 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||
| # | |||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | |||
| # of this software and associated documentation files (the "Software"), to deal | |||
| # in the Software without reunicodeiction, including without limitation the rights | |||
| # to use, copy, modify, merge, publish, diunicodeibute, sublicense, and/or sell | |||
| # copies of the Software, and to permit persons to whom the Software is | |||
| # furnished to do so, subject to the following conditions: | |||
| # | |||
| # The above copyright notice and this permission notice shall be included in | |||
| # all copies or substantial portions of the Software. | |||
| # | |||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
| # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||
| # SOFTWARE. | |||
| __all__ = ["StringMixin"] | |||
| class StringMixin(object): | |||
| def __str__(self): | |||
| return str(unicode(self)) | |||
| def __repr__(self): | |||
| return repr(unicode(self)) | |||
| def __lt__(self, other): | |||
| if isinstance(other, unicodeingMixin): | |||
| return unicode(self) < unicode(other) | |||
| return unicode(self) < other | |||
| def __le__(self, other): | |||
| if isinstance(other, unicodeingMixin): | |||
| return unicode(self) <= unicode(other) | |||
| return unicode(self) <= other | |||
| def __eq__(self, other): | |||
| if isinstance(other, unicodeingMixin): | |||
| return unicode(self) == unicode(other) | |||
| return unicode(self) == other | |||
| def __ne__(self, other): | |||
| if isinstance(other, unicodeingMixin): | |||
| return unicode(self) != unicode(other) | |||
| return unicode(self) != other | |||
| def __gt__(self, other): | |||
| if isinstance(other, unicodeingMixin): | |||
| return unicode(self) > unicode(other) | |||
| return unicode(self) > other | |||
| def __ge__(self, other): | |||
| if isinstance(other, unicodeingMixin): | |||
| return unicode(self) >= unicode(other) | |||
| return unicode(self) >= other | |||
| def __getitem__(self, index): | |||
| return unicode(self)[index] | |||
| @@ -20,51 +20,25 @@ | |||
| # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||
| # SOFTWARE. | |||
| from collections import OrderedDict | |||
| from mwparserfromhell.node import Node | |||
| from mwparserfromhell.string_mixin import StringMixin | |||
| __all__ = ["Template"] | |||
| class Template(object): | |||
| class Template(Node, StringMixin): | |||
| def __init__(self, name, params=None): | |||
| self._name = name | |||
| self._params = OrderedDict() | |||
| if params: | |||
| for param in params: | |||
| self._params[param.name] = param | |||
| def __repr__(self): | |||
| paramlist = [] | |||
| for name, param in self._params.iteritems(): | |||
| paramlist.append('"{0}": "{1}"'.format(name, str(param))) | |||
| params = "{" + ", ".join(paramlist) + "}" | |||
| return "Template(name={0}, params={1})".format(self.name, params) | |||
| def __eq__(self, other): | |||
| if isinstance(other, Template): | |||
| return self.name == other.name and self.params == other.params | |||
| return self.render() == other | |||
| def __ne__(self, other): | |||
| if isinstance(other, Template): | |||
| return self.name != other.name or self.params != other.params | |||
| return self.render() != other | |||
| def __getitem__(self, key): | |||
| try: | |||
| return self._params[key] | |||
| except KeyError: # Try lookup by order in param list | |||
| return self._params.values()[key] | |||
| self._params = params | |||
| else: | |||
| self._params = [] | |||
| def __setitem__(self, key, value): | |||
| if isinstance(key, int): | |||
| if key > len(self._params): | |||
| raise IndexError("Index is too large") | |||
| elif key == len(self._params): # Simple addition to the end | |||
| self._params[key] = value | |||
| else: # We'll need to rebuild the OrderedDict | |||
| self._params | |||
| def __unicode__(self): | |||
| if self.params: | |||
| params = u"|".join([unicode(param) for param in self.params]) | |||
| return "{{" + unicode(self.name) + "|" + params + "}}" | |||
| else: | |||
| self._params[key] = value | |||
| return "{{" + unicode(self.name) + "}}" | |||
| @property | |||
| def name(self): | |||
| @@ -72,13 +46,16 @@ class Template(object): | |||
| @property | |||
| def params(self): | |||
| return self._params.values() | |||
| return self._params | |||
| def has_param(self): | |||
| pass | |||
| def get_param(self): | |||
| pass | |||
| def add_param(self): | |||
| pass | |||
| def render(self): | |||
| params = "" | |||
| for param in self.params: | |||
| if param.name.isdigit() and "=" not in param.value: | |||
| params += "|" + param.value | |||
| else: | |||
| params += "|" + param.name + "=" + param.value | |||
| return "{{" + self.name + params + "}}" | |||
| def remove_param(self): | |||
| pass | |||
| @@ -0,0 +1,37 @@ | |||
| # -*- coding: utf-8 -*- | |||
| # | |||
| # Copyright (C) 2012 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||
| # | |||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | |||
| # of this software and associated documentation files (the "Software"), to deal | |||
| # in the Software without restriction, including without limitation the rights | |||
| # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
| # copies of the Software, and to permit persons to whom the Software is | |||
| # furnished to do so, subject to the following conditions: | |||
| # | |||
| # The above copyright notice and this permission notice shall be included in | |||
| # all copies or substantial portions of the Software. | |||
| # | |||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
| # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||
| # SOFTWARE. | |||
| from mwparserfromhell.node import Node | |||
| from mwparserfromhell.string_mixin import StringMixin | |||
| __all__ = ["Text"] | |||
| class Text(Node, StringMixin): | |||
| def __init__(self, value): | |||
| self._value = value | |||
| def __unicode__(self): | |||
| return unicode(self.value) | |||
| @property | |||
| def value(self): | |||
| return self._value | |||
| @@ -0,0 +1,171 @@ | |||
| # -*- coding: utf-8 -*- | |||
| # | |||
| # Copyright (C) 2012 by Ben Kurtovic <ben.kurtovic@verizon.net> | |||
| # | |||
| # Permission is hereby granted, free of charge, to any person obtaining a copy | |||
| # of this software and associated documentation files (the "Software"), to deal | |||
| # in the Software without restriction, including without limitation the rights | |||
| # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
| # copies of the Software, and to permit persons to whom the Software is | |||
| # furnished to do so, subject to the following conditions: | |||
| # | |||
| # The above copyright notice and this permission notice shall be included in | |||
| # all copies or substantial portions of the Software. | |||
| # | |||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
| # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||
| # SOFTWARE. | |||
| import re | |||
| import mwparserfromhell | |||
| from mwparserfromhell.node import Node | |||
| from mwparserfromhell.string_mixin import StringMixin | |||
| from mwparserfromhell.template import Template | |||
| from mwparserfromhell.text import Text | |||
| __all__ = ["Wikicode"] | |||
| FLAGS = re.I | re.S | re.U | |||
| class Wikicode(StringMixin): | |||
| def __init__(self, nodes): | |||
| self._nodes = nodes | |||
| def __unicode__(self): | |||
| return "".join([unicode(node) for node in self.nodes]) | |||
| def _nodify(self, value): | |||
| if isinstance(value, Wikicode): | |||
| return value.nodes | |||
| if isinstance(value, Node): | |||
| return [value] | |||
| if isinstance(value, str) or isinstance(value, unicode): | |||
| return mwparserfromhell.parse(value).nodes | |||
| error = "Needs string, Node, or Wikicode object, but got {0}: {1}" | |||
| raise ValueError(error.format(type(value), value)) | |||
| def _get_all_nodes(self, code): | |||
| for node in code.nodes: | |||
| yield node | |||
| if isinstance(node, Template): | |||
| for child in self._get_all_nodes(node.name): | |||
| yield child | |||
| for param in node.params: | |||
| if param.showkey: | |||
| for child in self._get_all_nodes(param.name): | |||
| yield child | |||
| for child in self._get_all_nodes(param.value): | |||
| yield child | |||
| def _show_tree(self, code, lines, marker=None, indent=0): | |||
| def write(*args): | |||
| if lines and lines[-1] is marker: # Continue from the last line | |||
| lines.pop() # Remove the marker | |||
| last = lines.pop() | |||
| lines.append(last + " ".join(args)) | |||
| else: | |||
| lines.append(" " * indent + " ".join(args)) | |||
| for node in code.nodes: | |||
| if isinstance(node, Template): | |||
| write("{{", ) | |||
| self._show_tree(node.name, lines, marker, indent + 1) | |||
| for param in node.params: | |||
| write(" | ") | |||
| lines.append(marker) # Continue from this line | |||
| self._show_tree(param.name, lines, marker, indent + 1) | |||
| write(" = ") | |||
| lines.append(marker) # Continue from this line | |||
| self._show_tree(param.value, lines, marker, indent + 1) | |||
| write("}}") | |||
| elif isinstance(node, Text): | |||
| write(unicode(node)) | |||
| else: | |||
| raise NotImplementedError(node) | |||
| return lines | |||
| @property | |||
| def nodes(self): | |||
| return self._nodes | |||
| def get(self, index): | |||
| return self.nodes[index] | |||
| def set(self, index, value): | |||
| nodes = self._nodify(value) | |||
| if len(nodes) > 1: | |||
| raise ValueError("Cannot coerce multiple nodes into one index") | |||
| if index >= len(self.nodes) or -1 * index > len(self.nodes): | |||
| raise IndexError("List assignment index out of range") | |||
| self.nodex.pop(index) | |||
| if nodes: | |||
| self.nodes[index] = nodes[0] | |||
| def index(self, obj): | |||
| if obj not in self.nodes: | |||
| raise ValueError(obj) | |||
| if isinstance(obj, Node): | |||
| for i, node in enumerate(self.nodes): | |||
| if node is obj: | |||
| return i | |||
| raise ValueError(obj) | |||
| return self.nodes.index(obj) | |||
| def insert(self, index, value, recursive=True): | |||
| nodes = self._nodify(value) | |||
| for node in reversed(nodes): | |||
| self.nodes.insert(index, node) | |||
| def insert_before(self, obj, value): | |||
| if obj not in self.nodes: | |||
| raise KeyError(obj) | |||
| self.insert(self.index(obj), value) | |||
| def insert_after(self, obj, value): | |||
| if obj not in self.nodes: | |||
| raise KeyError(obj) | |||
| self.insert(self.index(obj) + 1, value) | |||
| def append(self, value): | |||
| nodes = self._nodify(value) | |||
| for node in nodes: | |||
| self.nodes.append(node) | |||
| def remove(self, node): | |||
| self.nodes.pop(self.index(node)) | |||
| def ifilter(self, recursive=False, matches=None, flags=FLAGS, | |||
| forcetype=None): | |||
| if recursive: | |||
| nodes = self._get_all_nodes(self) | |||
| else: | |||
| nodes = self.nodes | |||
| for node in nodes: | |||
| if not forcetype or isinstance(node, forcetype): | |||
| if not matches or re.search(matches, unicode(node), flags): | |||
| yield node | |||
| def ifilter_templates(self, recursive=False, matches=None, flags=FLAGS): | |||
| return self.filter(recursive, matches, flags, forcetype=Template) | |||
| def ifilter_text(self, recursive=False, matches=None, flags=FLAGS): | |||
| return self.filter(recursive, matches, flags, forcetype=Text) | |||
| def filter(self, recursive=False, matches=None, flags=FLAGS, | |||
| forcetype=None): | |||
| return list(self.ifilter(recursive, matches, flags, forcetype)) | |||
| def filter_templates(self, recursive=False, matches=None, flags=FLAGS): | |||
| return list(self.ifilter_templates(recursive, matches, flags)) | |||
| def filter_text(self, recursive=False, matches=None, flags=FLAGS): | |||
| return list(self.ifilter_text(recursive, matches, flags)) | |||
| def show_tree(self): | |||
| marker = object() # Random object we can find with certainty in a list | |||
| print "\n".join(self._show_tree(self, [], marker)) | |||