Преглед на файлове

Update type annotations and rerun ruff

tags/v0.7.0
Ben Kurtovic преди 1 година
родител
ревизия
d4f6bffd23
променени са 68 файла, в които са добавени 353 реда и са изтрити 245 реда
  1. +1
    -3
      docs/conf.py
  2. +13
    -0
      pyproject.toml
  3. +7
    -5
      scripts/memtest.py
  4. +3
    -1
      src/mwparserfromhell/definitions.py
  5. +4
    -2
      src/mwparserfromhell/nodes/__init__.py
  6. +8
    -5
      src/mwparserfromhell/nodes/_base.py
  7. +10
    -8
      src/mwparserfromhell/nodes/argument.py
  8. +2
    -1
      src/mwparserfromhell/nodes/comment.py
  9. +10
    -8
      src/mwparserfromhell/nodes/external_link.py
  10. +3
    -1
      src/mwparserfromhell/nodes/extras/__init__.py
  11. +11
    -10
      src/mwparserfromhell/nodes/extras/attribute.py
  12. +7
    -6
      src/mwparserfromhell/nodes/extras/parameter.py
  13. +9
    -7
      src/mwparserfromhell/nodes/heading.py
  14. +14
    -16
      src/mwparserfromhell/nodes/html_entity.py
  15. +27
    -25
      src/mwparserfromhell/nodes/tag.py
  16. +31
    -33
      src/mwparserfromhell/nodes/template.py
  17. +3
    -2
      src/mwparserfromhell/nodes/text.py
  18. +10
    -8
      src/mwparserfromhell/nodes/wikilink.py
  19. +3
    -1
      src/mwparserfromhell/parser/__init__.py
  20. +4
    -3
      src/mwparserfromhell/parser/builder.py
  21. +3
    -1
      src/mwparserfromhell/parser/contexts.py
  22. +1
    -1
      src/mwparserfromhell/parser/ctokenizer/common.h
  23. +1
    -1
      src/mwparserfromhell/parser/ctokenizer/contexts.h
  24. +1
    -1
      src/mwparserfromhell/parser/ctokenizer/definitions.c
  25. +1
    -1
      src/mwparserfromhell/parser/ctokenizer/definitions.h
  26. +1
    -1
      src/mwparserfromhell/parser/ctokenizer/tag_data.c
  27. +1
    -1
      src/mwparserfromhell/parser/ctokenizer/tag_data.h
  28. +1
    -1
      src/mwparserfromhell/parser/ctokenizer/textbuffer.c
  29. +1
    -1
      src/mwparserfromhell/parser/ctokenizer/textbuffer.h
  30. +1
    -1
      src/mwparserfromhell/parser/ctokenizer/tok_parse.c
  31. +1
    -1
      src/mwparserfromhell/parser/ctokenizer/tok_parse.h
  32. +1
    -1
      src/mwparserfromhell/parser/ctokenizer/tok_support.c
  33. +1
    -1
      src/mwparserfromhell/parser/ctokenizer/tok_support.h
  34. +1
    -1
      src/mwparserfromhell/parser/ctokenizer/tokenizer.c
  35. +1
    -1
      src/mwparserfromhell/parser/ctokenizer/tokenizer.h
  36. +1
    -1
      src/mwparserfromhell/parser/ctokenizer/tokens.c
  37. +1
    -1
      src/mwparserfromhell/parser/ctokenizer/tokens.h
  38. +4
    -2
      src/mwparserfromhell/parser/errors.py
  39. +7
    -5
      src/mwparserfromhell/parser/tokenizer.py
  40. +3
    -1
      src/mwparserfromhell/parser/tokens.py
  41. +3
    -1
      src/mwparserfromhell/smart_list/__init__.py
  42. +3
    -1
      src/mwparserfromhell/smart_list/list_proxy.py
  43. +3
    -1
      src/mwparserfromhell/smart_list/smart_list.py
  44. +3
    -1
      src/mwparserfromhell/smart_list/utils.py
  45. +4
    -2
      src/mwparserfromhell/string_mixin.py
  46. +1
    -1
      src/mwparserfromhell/utils.py
  47. +41
    -40
      src/mwparserfromhell/wikicode.py
  48. +3
    -1
      tests/conftest.py
  49. +4
    -1
      tests/test_argument.py
  50. +4
    -1
      tests/test_attribute.py
  51. +5
    -2
      tests/test_builder.py
  52. +3
    -1
      tests/test_comment.py
  53. +4
    -2
      tests/test_docs.py
  54. +4
    -1
      tests/test_external_link.py
  55. +4
    -1
      tests/test_heading.py
  56. +3
    -1
      tests/test_html_entity.py
  57. +4
    -1
      tests/test_parameter.py
  58. +4
    -1
      tests/test_parser.py
  59. +3
    -1
      tests/test_smart_list.py
  60. +3
    -1
      tests/test_string_mixin.py
  61. +4
    -1
      tests/test_tag.py
  62. +5
    -2
      tests/test_template.py
  63. +3
    -1
      tests/test_text.py
  64. +4
    -2
      tests/test_tokenizer.py
  65. +3
    -1
      tests/test_tokens.py
  66. +4
    -1
      tests/test_utils.py
  67. +7
    -4
      tests/test_wikicode.py
  68. +4
    -1
      tests/test_wikilink.py

+ 1
- 3
docs/conf.py Целия файл

@@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
#
# mwparserfromhell documentation build configuration file, created by
# sphinx-quickstart on Tue Aug 21 20:47:26 2012.
#
@@ -11,8 +9,8 @@
# All configuration values have a default; values that are commented out
# serve to show the default.

import sys
import os
import sys

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the


+ 13
- 0
pyproject.toml Целия файл

@@ -0,0 +1,13 @@
[tool.pyright]
pythonVersion = "3.9"
venvPath = "."
venv = ".venv"

[tool.ruff]
target-version = "py39"

[tool.ruff.lint]
extend-select = [
"I",
"UP",
]

+ 7
- 5
scripts/memtest.py Целия файл

@@ -1,4 +1,4 @@
# Copyright (C) 2012-2020 Ben Kurtovic <ben.kurtovic@gmail.com>
# Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -30,10 +30,12 @@ Actual memory leaks typically leak very large amounts of memory (megabytes)
and scale with the number of loops.
"""

from __future__ import annotations

import sys
from locale import LC_ALL, setlocale
from multiprocessing import Process, Pipe
from multiprocessing import Pipe, Process
from os import listdir, path
import sys

import psutil

@@ -80,7 +82,7 @@ class MemoryTest:

def _load(self):
def load_file(filename):
with open(filename, "rU") as fp:
with open(filename) as fp:
text = fp.read()
name = path.split(filename)[1][: 0 - len(extension)]
self._parse_file(name, text)
@@ -109,7 +111,7 @@ class MemoryTest:
tmpl = "{0}LEAKING{1}: {2:n} bytes, {3:.2%} inc ({4:n} bytes/loop)"
sys.stdout.write(tmpl.format(Color.YELLOW, Color.RESET, d, p, bpt))
else:
sys.stdout.write("{}OK{}".format(Color.GREEN, Color.RESET))
sys.stdout.write(f"{Color.GREEN}OK{Color.RESET}")

def run(self):
"""Run the memory test suite."""


+ 3
- 1
src/mwparserfromhell/definitions.py Целия файл

@@ -1,4 +1,4 @@
# Copyright (C) 2012-2020 Ben Kurtovic <ben.kurtovic@gmail.com>
# Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -26,6 +26,8 @@ When updating this file, please also update the the C tokenizer version:
- mwparserfromhell/parser/ctokenizer/definitions.h
"""

from __future__ import annotations

__all__ = [
"get_html_tag",
"is_parsable",


+ 4
- 2
src/mwparserfromhell/nodes/__init__.py Целия файл

@@ -1,4 +1,4 @@
# Copyright (C) 2012-2020 Ben Kurtovic <ben.kurtovic@gmail.com>
# Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -27,9 +27,10 @@ the name of a :class:`.Template` is a :class:`.Wikicode` object that can
contain text or more templates.
"""

from __future__ import annotations

from . import extras
from ._base import Node
from .text import Text
from .argument import Argument
from .comment import Comment
from .external_link import ExternalLink
@@ -37,6 +38,7 @@ from .heading import Heading
from .html_entity import HTMLEntity
from .tag import Tag
from .template import Template
from .text import Text
from .wikilink import Wikilink

__all__ = [


+ 8
- 5
src/mwparserfromhell/nodes/_base.py Целия файл

@@ -1,4 +1,4 @@
# Copyright (C) 2012-2020 Ben Kurtovic <ben.kurtovic@gmail.com>
# Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -18,7 +18,10 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

from typing import TYPE_CHECKING, Any, Generator, Optional, Callable
from __future__ import annotations

from collections.abc import Generator
from typing import TYPE_CHECKING, Any, Callable

from ..string_mixin import StringMixIn

@@ -45,18 +48,18 @@ class Node(StringMixIn):
def __str__(self) -> str:
raise NotImplementedError()

def __children__(self) -> Generator["Wikicode", None, None]:
def __children__(self) -> Generator[Wikicode, None, None]:
return
# pylint: disable=unreachable
yield # pragma: no cover (this is a generator that yields nothing)

def __strip__(self, **kwargs: Any) -> Optional[str]:
def __strip__(self, **kwargs: Any) -> str | None:
return None

def __showtree__(
self,
write: Callable[[str], None],
get: Callable[["Wikicode"], None],
get: Callable[[Wikicode], None],
mark: Callable[[], None],
) -> None:
write(str(self))

+ 10
- 8
src/mwparserfromhell/nodes/argument.py Целия файл

@@ -1,4 +1,4 @@
# Copyright (C) 2012-2020 Ben Kurtovic <ben.kurtovic@gmail.com>
# Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -18,11 +18,13 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

from __future__ import annotations

from typing import TYPE_CHECKING, Any, Generator, Optional, Callable
from collections.abc import Generator
from typing import TYPE_CHECKING, Any, Callable

from ._base import Node
from ..utils import parse_anything
from ._base import Node

if TYPE_CHECKING:
from ..wikicode import Wikicode
@@ -44,12 +46,12 @@ class Argument(Node):
return start + "|" + str(self.default) + "}}}"
return start + "}}}"

def __children__(self) -> Generator["Wikicode", None, None]:
def __children__(self) -> Generator[Wikicode, None, None]:
yield self.name
if self.default is not None:
yield self.default

def __strip__(self, **kwargs: Any) -> Optional[str]:
def __strip__(self, **kwargs: Any) -> str | None:
if self.default is not None:
return self.default.strip_code(**kwargs)
return None
@@ -57,7 +59,7 @@ class Argument(Node):
def __showtree__(
self,
write: Callable[[str], None],
get: Callable[["Wikicode"], None],
get: Callable[[Wikicode], None],
mark: Callable[[], None],
) -> None:
write("{{{")
@@ -69,7 +71,7 @@ class Argument(Node):
write("}}}")

@property
def name(self) -> "Wikicode":
def name(self) -> Wikicode:
"""The name of the argument to substitute."""
return self._name

@@ -78,7 +80,7 @@ class Argument(Node):
self._name = parse_anything(value)

@property
def default(self) -> Optional["Wikicode"]:
def default(self) -> Wikicode | None:
"""The default value to substitute if none is passed.

This will be ``None`` if the argument wasn't defined with one. The


+ 2
- 1
src/mwparserfromhell/nodes/comment.py Целия файл

@@ -1,4 +1,4 @@
# Copyright (C) 2012-2020 Ben Kurtovic <ben.kurtovic@gmail.com>
# Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -18,6 +18,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

from __future__ import annotations

from typing import Any



+ 10
- 8
src/mwparserfromhell/nodes/external_link.py Целия файл

@@ -1,4 +1,4 @@
# Copyright (C) 2012-2020 Ben Kurtovic <ben.kurtovic@gmail.com>
# Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -18,11 +18,13 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

from __future__ import annotations

from typing import TYPE_CHECKING, Any, Optional, Generator, Callable
from collections.abc import Generator
from typing import TYPE_CHECKING, Any, Callable

from ._base import Node
from ..utils import parse_anything
from ._base import Node

if TYPE_CHECKING:
from ..wikicode import Wikicode
@@ -55,12 +57,12 @@ class ExternalLink(Node):
return "[" + str(self.url) + "]"
return str(self.url)

def __children__(self) -> Generator["Wikicode", None, None]:
def __children__(self) -> Generator[Wikicode, None, None]:
yield self.url
if self.title is not None:
yield self.title

def __strip__(self, **kwargs: Any) -> Optional[str]:
def __strip__(self, **kwargs: Any) -> str | None:
if self.brackets:
if self.title:
return self.title.strip_code(**kwargs)
@@ -70,7 +72,7 @@ class ExternalLink(Node):
def __showtree__(
self,
write: Callable[[str], None],
get: Callable[["Wikicode"], None],
get: Callable[[Wikicode], None],
mark: Callable[[], None],
) -> None:
if self.brackets:
@@ -82,7 +84,7 @@ class ExternalLink(Node):
write("]")

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

@@ -94,7 +96,7 @@ class ExternalLink(Node):
self._url = parse_anything(value, contexts.EXT_LINK_URI)

@property
def title(self) -> Optional["Wikicode"]:
def title(self) -> Wikicode | None:
"""The link title (if given), as a :class:`.Wikicode` object."""
return self._title



+ 3
- 1
src/mwparserfromhell/nodes/extras/__init__.py Целия файл

@@ -1,4 +1,4 @@
# Copyright (C) 2012-2016 Ben Kurtovic <ben.kurtovic@gmail.com>
# Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -23,6 +23,8 @@ This package contains objects used by :class:`.Node`\\ s, but that are not
nodes themselves. This includes template parameters and HTML tag attributes.
"""

from __future__ import annotations

__all__ = [
"Attribute",
"Parameter",


+ 11
- 10
src/mwparserfromhell/nodes/extras/attribute.py Целия файл

@@ -1,4 +1,4 @@
# Copyright (C) 2012-2020 Ben Kurtovic <ben.kurtovic@gmail.com>
# Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -18,8 +18,9 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

from __future__ import annotations

from typing import TYPE_CHECKING, Any, Optional
from typing import TYPE_CHECKING, Any

from ...string_mixin import StringMixIn
from ...utils import parse_anything
@@ -42,7 +43,7 @@ class Attribute(StringMixIn):
self,
name: Any,
value: Any = None,
quotes: Optional[str] = '"',
quotes: str | None = '"',
pad_first: str = " ",
pad_before_eq: str = "",
pad_after_eq: str = "",
@@ -54,7 +55,7 @@ class Attribute(StringMixIn):
self._pad_after_eq: str

self.name = name
self._quotes: Optional[str] = None
self._quotes: str | None = None
self.value = value
self.quotes = quotes
self.pad_first = pad_first
@@ -71,7 +72,7 @@ class Attribute(StringMixIn):
return result

@staticmethod
def _value_needs_quotes(value: "Wikicode") -> Optional[str]:
def _value_needs_quotes(value: Wikicode) -> str | None:
"""Return valid quotes for the given value, or None if unneeded."""
if not value:
return None
@@ -95,15 +96,15 @@ class Attribute(StringMixIn):
setattr(self, attr, value)

@staticmethod
def coerce_quotes(quotes: Any) -> Optional[str]:
def coerce_quotes(quotes: Any) -> str | None:
"""Coerce a quote type into an acceptable value, or raise an error."""
coerced_quotes = str(quotes) if quotes else None
if coerced_quotes not in [None, '"', "'"]:
raise ValueError("{!r} is not a valid quote type".format(quotes))
raise ValueError(f"{quotes!r} is not a valid quote type")
return coerced_quotes

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

@@ -112,7 +113,7 @@ class Attribute(StringMixIn):
self._name = parse_anything(value)

@property
def value(self) -> Optional["Wikicode"]:
def value(self) -> Wikicode | None:
"""The value of the attribute as a :class:`.Wikicode` object."""
return self._value

@@ -128,7 +129,7 @@ class Attribute(StringMixIn):
self._value = code

@property
def quotes(self) -> Optional[str]:
def quotes(self) -> str | None:
"""How to enclose the attribute value. ``"``, ``'``, or ``None``."""
return self._quotes



+ 7
- 6
src/mwparserfromhell/nodes/extras/parameter.py Целия файл

@@ -1,4 +1,4 @@
# Copyright (C) 2012-2020 Ben Kurtovic <ben.kurtovic@gmail.com>
# Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -18,9 +18,10 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

from typing import TYPE_CHECKING, Any, Union
from __future__ import annotations

import re
from typing import TYPE_CHECKING, Any

from ...string_mixin import StringMixIn
from ...utils import parse_anything
@@ -52,12 +53,12 @@ class Parameter(StringMixIn):
return str(self.value)

@staticmethod
def can_hide_key(key: Any) -> Union[re.Match, None]:
def can_hide_key(key: Any) -> re.Match | None:
"""Return whether or not the given key can be hidden."""
return re.match(r"[1-9][0-9]*$", str(key).strip())

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

@@ -66,7 +67,7 @@ class Parameter(StringMixIn):
self._name = parse_anything(newval)

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

@@ -83,5 +84,5 @@ class Parameter(StringMixIn):
def showkey(self, newval: Any) -> None:
newval = bool(newval)
if not newval and not self.can_hide_key(self.name):
raise ValueError("parameter key {!r} cannot be hidden".format(self.name))
raise ValueError(f"parameter key {self.name!r} cannot be hidden")
self._showkey = newval

+ 9
- 7
src/mwparserfromhell/nodes/heading.py Целия файл

@@ -1,4 +1,4 @@
# Copyright (C) 2012-2020 Ben Kurtovic <ben.kurtovic@gmail.com>
# Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -18,11 +18,13 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

from __future__ import annotations

from typing import TYPE_CHECKING, Any, Generator, Callable, Optional
from collections.abc import Generator
from typing import TYPE_CHECKING, Any, Callable

from ._base import Node
from ..utils import parse_anything
from ._base import Node

if TYPE_CHECKING:
from ..wikicode import Wikicode
@@ -39,16 +41,16 @@ class Heading(Node):
def __str__(self) -> str:
return ("=" * self.level) + str(self.title) + ("=" * self.level)

def __children__(self) -> Generator["Wikicode", None, None]:
def __children__(self) -> Generator[Wikicode, None, None]:
yield self.title

def __strip__(self, **kwargs: Any) -> Optional[str]:
def __strip__(self, **kwargs: Any) -> str | None:
return self.title.strip_code(**kwargs)

def __showtree__(
self,
write: Callable[[str], None],
get: Callable[["Wikicode"], None],
get: Callable[[Wikicode], None],
mark: Callable[[], None],
) -> None:
write("=" * self.level)
@@ -56,7 +58,7 @@ class Heading(Node):
write("=" * self.level)

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



+ 14
- 16
src/mwparserfromhell/nodes/html_entity.py Целия файл

@@ -1,4 +1,4 @@
# Copyright (C) 2012-2020 Ben Kurtovic <ben.kurtovic@gmail.com>
# Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -18,8 +18,10 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

from typing import Any, Optional
from __future__ import annotations

import html.entities as htmlentities
from typing import Any

from ._base import Node

@@ -32,7 +34,7 @@ class HTMLEntity(Node):
def __init__(
self,
value: Any,
named: Optional[bool] = None,
named: bool | None = None,
hexadecimal: bool = False,
hex_char: str = "x",
):
@@ -58,12 +60,12 @@ class HTMLEntity(Node):

def __str__(self) -> str:
if self.named:
return "&{};".format(self.value)
return f"&{self.value};"
if self.hexadecimal:
return "&#{}{};".format(self.hex_char, self.value)
return "&#{};".format(self.value)
return f"&#{self.hex_char}{self.value};"
return f"&#{self.value};"

def __strip__(self, **kwargs: Any) -> Optional[str]:
def __strip__(self, **kwargs: Any) -> str | None:
if kwargs.get("normalize"):
return self.normalize()
return str(self)
@@ -84,23 +86,21 @@ class HTMLEntity(Node):
except ValueError:
if newval not in htmlentities.entitydefs:
raise ValueError(
"entity value {!r} is not a valid name".format(newval)
f"entity value {newval!r} is not a valid name"
) from None
self._named = True
self._hexadecimal = False
else:
if intval < 0 or intval > 0x10FFFF:
raise ValueError(
"entity value 0x{:x} is not in range(0x110000)".format(intval)
f"entity value 0x{intval:x} is not in range(0x110000)"
) from None
self._named = False
self._hexadecimal = True
else:
test = int(newval, 16 if self.hexadecimal else 10)
if test < 0 or test > 0x10FFFF:
raise ValueError(
"entity value {} is not in range(0x110000)".format(test)
)
raise ValueError(f"entity value {test} is not in range(0x110000)")
self._named = False
self._value = newval

@@ -118,16 +118,14 @@ class HTMLEntity(Node):
def named(self, newval: bool) -> None:
newval = bool(newval)
if newval and self.value not in htmlentities.entitydefs:
raise ValueError("entity value {!r} is not a valid name".format(self.value))
raise ValueError(f"entity value {self.value!r} is not a valid name")

if not newval:
try:
int(self.value, 16)
except ValueError as exc:
raise ValueError(
"current entity value {!r} is not a valid Unicode codepoint".format(
self.value
)
f"current entity value {self.value!r} is not a valid Unicode codepoint"
) from exc
self._named = newval



+ 27
- 25
src/mwparserfromhell/nodes/tag.py Целия файл

@@ -1,4 +1,4 @@
# Copyright (C) 2012-2020 Ben Kurtovic <ben.kurtovic@gmail.com>
# Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -18,13 +18,15 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

from __future__ import annotations

from typing import TYPE_CHECKING, Any, Callable, Optional, List, Generator
from collections.abc import Generator
from typing import TYPE_CHECKING, Any, Callable

from ._base import Node
from .extras import Attribute
from ..definitions import is_visible
from ..utils import parse_anything
from ._base import Node
from .extras import Attribute

if TYPE_CHECKING:
from ..wikicode import Wikicode
@@ -39,19 +41,19 @@ class Tag(Node):
self,
tag: Any,
contents: Any = None,
attrs: Optional[List[Attribute]] = None,
wiki_markup: Optional[str] = None,
attrs: list[Attribute] | None = None,
wiki_markup: str | None = None,
self_closing: bool = False,
invalid: bool = False,
implicit: bool = False,
padding: str = "",
closing_tag: Optional["Wikicode"] = None,
wiki_style_separator: Optional[str] = None,
closing_wiki_markup: Optional[str] = None,
closing_tag: Wikicode | None = None,
wiki_style_separator: str | None = None,
closing_wiki_markup: str | None = None,
):
super().__init__()
self._attrs: List[Attribute]
self._closing_wiki_markup: Optional[str]
self._attrs: list[Attribute]
self._closing_wiki_markup: str | None

self.tag = tag
self.contents = contents
@@ -98,7 +100,7 @@ class Tag(Node):
result += "</" + str(self.closing_tag) + ">"
return result

def __children__(self) -> Generator["Wikicode", None, None]:
def __children__(self) -> Generator[Wikicode, None, None]:
if not self.wiki_markup:
yield self.tag
for attr in self.attributes:
@@ -110,7 +112,7 @@ class Tag(Node):
if not self.wiki_markup and self.closing_tag:
yield self.closing_tag

def __strip__(self, **kwargs: Any) -> Optional[str]:
def __strip__(self, **kwargs: Any) -> str | None:
if self.contents and is_visible(str(self.tag)):
return self.contents.strip_code(**kwargs)
return None
@@ -118,7 +120,7 @@ class Tag(Node):
def __showtree__(
self,
write: Callable[[str], None],
get: Callable[["Wikicode"], None],
get: Callable[[Wikicode], None],
mark: Callable[[], None],
) -> None:
write("</" if self.invalid else "<")
@@ -140,7 +142,7 @@ class Tag(Node):
write(">")

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

@@ -149,7 +151,7 @@ class Tag(Node):
self._tag = self._closing_tag = parse_anything(value)

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

@@ -158,7 +160,7 @@ class Tag(Node):
self._contents = parse_anything(value)

@property
def attributes(self) -> List[Attribute]:
def attributes(self) -> list[Attribute]:
"""The list of attributes affecting the tag.

Each attribute is an instance of :class:`.Attribute`.
@@ -166,7 +168,7 @@ class Tag(Node):
return self._attrs

@property
def wiki_markup(self) -> Optional[str]:
def wiki_markup(self) -> str | None:
"""The wikified version of a tag to show instead of HTML.

If set to a value, this will be displayed instead of the brackets.
@@ -176,7 +178,7 @@ class Tag(Node):
return self._wiki_markup

@wiki_markup.setter
def wiki_markup(self, value: Optional[str]) -> None:
def wiki_markup(self, value: str | None) -> None:
self._wiki_markup = str(value) if value else None
if not value or not self.closing_wiki_markup:
self._closing_wiki_markup = self._wiki_markup
@@ -235,7 +237,7 @@ class Tag(Node):
self._padding = value

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

This will usually equal :attr:`tag`, unless there is additional
@@ -248,7 +250,7 @@ class Tag(Node):
self._closing_tag = parse_anything(value)

@property
def wiki_style_separator(self) -> Optional[str]:
def wiki_style_separator(self) -> str | None:
"""The separator between the padding and content in a wiki markup tag.

Essentially the wiki equivalent of the TagCloseOpen.
@@ -256,11 +258,11 @@ class Tag(Node):
return self._wiki_style_separator

@wiki_style_separator.setter
def wiki_style_separator(self, value: Optional[str]) -> None:
def wiki_style_separator(self, value: str | None) -> None:
self._wiki_style_separator = str(value) if value else None

@property
def closing_wiki_markup(self) -> Optional[str]:
def closing_wiki_markup(self) -> str | None:
"""The wikified version of the closing tag to show instead of HTML.

If set to a value, this will be displayed instead of the close tag
@@ -273,7 +275,7 @@ class Tag(Node):
return self._closing_wiki_markup

@closing_wiki_markup.setter
def closing_wiki_markup(self, value: Optional[str]) -> None:
def closing_wiki_markup(self, value: str | None) -> None:
self._closing_wiki_markup = str(value) if value else None

def has(self, name: str) -> bool:
@@ -304,7 +306,7 @@ class Tag(Node):
self,
name: Any,
value: Any = None,
quotes: Optional[str] = '"',
quotes: str | None = '"',
pad_first: str = " ",
pad_before_eq: str = "",
pad_after_eq: str = "",


+ 31
- 33
src/mwparserfromhell/nodes/template.py Целия файл

@@ -1,4 +1,4 @@
# Copyright (C) 2012-2020 Ben Kurtovic <ben.kurtovic@gmail.com>
# Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -17,27 +17,25 @@
# 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 __future__ import annotations

import re
from collections import defaultdict
from collections.abc import Generator, Mapping
from typing import (
TYPE_CHECKING,
Any,
Callable,
Dict,
Generator,
List,
Mapping,
Optional,
TypeVar,
Union,
overload,
)

from ..utils import parse_anything
from ._base import Node
from .extras import Parameter
from .html_entity import HTMLEntity
from .text import Text
from .extras import Parameter
from ..utils import parse_anything

if TYPE_CHECKING:
from ..wikicode import Wikicode
@@ -54,10 +52,10 @@ T = TypeVar("T")
class Template(Node):
"""Represents a template in wikicode, like ``{{foo}}``."""

def __init__(self, name: Any, params: Optional[List[Parameter]] = None):
def __init__(self, name: Any, params: list[Parameter] | None = None):
super().__init__()
self.name = name
self._params: List[Parameter] = params or []
self._params: list[Parameter] = params or []

def __str__(self) -> str:
if self.params:
@@ -65,14 +63,14 @@ class Template(Node):
return "{{" + str(self.name) + "|" + params + "}}"
return "{{" + str(self.name) + "}}"

def __children__(self) -> Generator["Wikicode", None, None]:
def __children__(self) -> Generator[Wikicode]:
yield self.name
for param in self.params:
if param.showkey:
yield param.name
yield param.value

def __strip__(self, **kwargs: Any) -> Optional[str]:
def __strip__(self, **kwargs: Any) -> str | None:
if kwargs.get("keep_template_params"):
parts = [param.value.strip_code(**kwargs) for param in self.params]
return " ".join(part for part in parts if part)
@@ -81,7 +79,7 @@ class Template(Node):
def __showtree__(
self,
write: Callable[[str], None],
get: Callable[["Wikicode"], None],
get: Callable[[Wikicode], None],
mark: Callable[[], None],
) -> None:
write("{{")
@@ -96,7 +94,7 @@ class Template(Node):
write("}}")

@staticmethod
def _surface_escape(code: "Wikicode", char: str) -> None:
def _surface_escape(code: Wikicode, char: str) -> None:
"""Return *code* with *char* escaped as an HTML entity.

The main use of this is to escape pipes (``|``) or equal signs (``=``)
@@ -109,7 +107,7 @@ class Template(Node):
code.replace(node, node.replace(char, replacement), False)

@staticmethod
def _select_theory(theories: Dict[str, int]) -> Optional[str]:
def _select_theory(theories: dict[str, int]) -> str | None:
"""Return the most likely spacing convention given different options.

Given a dictionary of convention options as keys and their occurrence
@@ -125,7 +123,7 @@ class Template(Node):
return None

@staticmethod
def _blank_param_value(value: "Wikicode") -> None:
def _blank_param_value(value: Wikicode) -> None:
"""Remove the content from *value* while keeping its whitespace.

Replace *value*\\ 's nodes with two text nodes, the first containing
@@ -142,15 +140,15 @@ class Template(Node):

def _get_spacing_conventions(
self, use_names: bool
) -> tuple[Optional[str], Optional[str]]:
) -> tuple[str | None, str | None]:
"""Try to determine the whitespace conventions for parameters.

This will examine the existing parameters and use
: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[str, int] = defaultdict(lambda: 0)
after_theories: defaultdict[str, int] = defaultdict(lambda: 0)
before_theories: defaultdict[str, int] = defaultdict(int)
after_theories: defaultdict[str, int] = defaultdict(int)
for param in self.params:
if not param.showkey:
continue
@@ -205,7 +203,7 @@ class Template(Node):
return False

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

@@ -214,11 +212,11 @@ class Template(Node):
self._name = parse_anything(value)

@property
def params(self) -> List[Parameter]:
def params(self) -> list[Parameter]:
"""The list of parameters contained within the template."""
return self._params

def has(self, name: Union[str, Any], ignore_empty: bool = False) -> bool:
def has(self, name: str | Any, ignore_empty: bool = False) -> bool:
"""Return ``True`` if any parameter in the template is named *name*.

With *ignore_empty*, ``False`` will be returned even if the template
@@ -234,17 +232,17 @@ class Template(Node):
return True
return False

def has_param(self, name: Union[str, Any], ignore_empty: bool = False) -> bool:
def has_param(self, name: str | Any, ignore_empty: bool = False) -> bool:
"""Alias for :meth:`has`."""
return self.has(name, ignore_empty)

@overload
def get(self, name: Union[str, Any]) -> Parameter: ...
def get(self, name: str | Any) -> Parameter: ...

@overload
def get(self, name: Union[str, Any], default: T) -> Union[Parameter, T]: ...
def get(self, name: str | Any, default: T) -> Parameter | T: ...

def get(self, name: Union[str, Any], default: T = _UNSET) -> Union[Parameter, T]:
def get(self, name: str | Any, default: T = _UNSET) -> Parameter | T:
"""Get the parameter whose name is *name*.

The returned object is a :class:`.Parameter` instance. Raises
@@ -261,16 +259,16 @@ class Template(Node):
raise ValueError(name)
return default

def __getitem__(self, name: Union[str, Any]) -> Parameter:
def __getitem__(self, name: str | Any) -> Parameter:
return self.get(name)

def add(
self,
name: Any,
value: Any,
showkey: Optional[bool] = None,
before: Optional[Union[Parameter, str]] = None,
after: Optional[Union[Parameter, str]] = None,
showkey: bool | None = None,
before: Parameter | str | None = None,
after: Parameter | str | None = None,
preserve_spacing: bool = True,
) -> Parameter:
"""Add a parameter to the template with a given *name* and *value*.
@@ -377,7 +375,7 @@ class Template(Node):
def __setitem__(self, name: Any, value: Any) -> Parameter:
return self.add(name, value)

def remove(self, param: Union[Parameter, str], keep_field: bool = False) -> None:
def remove(self, param: Parameter | str, keep_field: bool = False) -> None:
"""Remove a parameter from the template, identified by *param*.

If *param* is a :class:`.Parameter` object, it will be matched exactly,
@@ -424,5 +422,5 @@ class Template(Node):
for i in reversed(to_remove):
self.params.pop(i)

def __delitem__(self, param: Union[Parameter, str]) -> None:
def __delitem__(self, param: Parameter | str) -> None:
return self.remove(param)

+ 3
- 2
src/mwparserfromhell/nodes/text.py Целия файл

@@ -1,4 +1,4 @@
# Copyright (C) 2012-2020 Ben Kurtovic <ben.kurtovic@gmail.com>
# Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -18,6 +18,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

from __future__ import annotations

from typing import TYPE_CHECKING, Any, Callable

@@ -45,7 +46,7 @@ class Text(Node):
def __showtree__(
self,
write: Callable[[str], None],
get: Callable[["Wikicode"], None],
get: Callable[[Wikicode], None],
mark: Callable[[], None],
) -> None:
write(str(self).encode("unicode_escape").decode("utf8"))


+ 10
- 8
src/mwparserfromhell/nodes/wikilink.py Целия файл

@@ -1,4 +1,4 @@
# Copyright (C) 2012-2020 Ben Kurtovic <ben.kurtovic@gmail.com>
# Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -18,11 +18,13 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

from __future__ import annotations

from typing import TYPE_CHECKING, Any, Optional, Generator, Callable
from collections.abc import Generator
from typing import TYPE_CHECKING, Any, Callable

from ._base import Node
from ..utils import parse_anything
from ._base import Node

if TYPE_CHECKING:
from ..wikicode import Wikicode
@@ -43,12 +45,12 @@ class Wikilink(Node):
return "[[" + str(self.title) + "|" + str(self.text) + "]]"
return "[[" + str(self.title) + "]]"

def __children__(self) -> Generator["Wikicode", None, None]:
def __children__(self) -> Generator[Wikicode, None, None]:
yield self.title
if self.text is not None:
yield self.text

def __strip__(self, **kwargs: Any) -> Optional[str]:
def __strip__(self, **kwargs: Any) -> str | None:
if self.text is not None:
return self.text.strip_code(**kwargs)
return self.title.strip_code(**kwargs)
@@ -56,7 +58,7 @@ class Wikilink(Node):
def __showtree__(
self,
write: Callable[[str], None],
get: Callable[["Wikicode"], None],
get: Callable[[Wikicode], None],
mark: Callable[[], None],
) -> None:
write("[[")
@@ -68,7 +70,7 @@ class Wikilink(Node):
write("]]")

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

@@ -77,7 +79,7 @@ class Wikilink(Node):
self._title = parse_anything(value)

@property
def text(self) -> Optional["Wikicode"]:
def text(self) -> Wikicode | None:
"""The text to display (if any), as a :class:`.Wikicode` object."""
return self._text



+ 3
- 1
src/mwparserfromhell/parser/__init__.py Целия файл

@@ -1,4 +1,4 @@
# Copyright (C) 2012-2020 Ben Kurtovic <ben.kurtovic@gmail.com>
# Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -24,6 +24,8 @@ modules: the :mod:`.tokenizer` and the :mod:`.builder`. This module joins them
together into one interface.
"""

from __future__ import annotations

from .builder import Builder
from .errors import ParserError



+ 4
- 3
src/mwparserfromhell/parser/builder.py Целия файл

@@ -1,4 +1,4 @@
# Copyright (C) 2012-2020 Ben Kurtovic <ben.kurtovic@gmail.com>
# Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -18,9 +18,8 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

from __future__ import annotations

from . import tokens
from .errors import ParserError
from ..nodes import (
Argument,
Comment,
@@ -35,6 +34,8 @@ from ..nodes import (
from ..nodes.extras import Attribute, Parameter
from ..smart_list import SmartList
from ..wikicode import Wikicode
from . import tokens
from .errors import ParserError

__all__ = ["Builder"]



+ 3
- 1
src/mwparserfromhell/parser/contexts.py Целия файл

@@ -1,4 +1,4 @@
# Copyright (C) 2012-2019 Ben Kurtovic <ben.kurtovic@gmail.com>
# Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -114,6 +114,8 @@ Aggregate contexts:

"""

from __future__ import annotations

# Local contexts:

TEMPLATE_NAME = 1 << 0


+ 1
- 1
src/mwparserfromhell/parser/ctokenizer/common.h Целия файл

@@ -1,5 +1,5 @@
/*
Copyright (C) 2012-2017 Ben Kurtovic <ben.kurtovic@gmail.com>
Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>

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


+ 1
- 1
src/mwparserfromhell/parser/ctokenizer/contexts.h Целия файл

@@ -1,5 +1,5 @@
/*
Copyright (C) 2012-2017 Ben Kurtovic <ben.kurtovic@gmail.com>
Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>

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


+ 1
- 1
src/mwparserfromhell/parser/ctokenizer/definitions.c Целия файл

@@ -1,5 +1,5 @@
/*
Copyright (C) 2012-2020 Ben Kurtovic <ben.kurtovic@gmail.com>
Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>

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


+ 1
- 1
src/mwparserfromhell/parser/ctokenizer/definitions.h Целия файл

@@ -1,5 +1,5 @@
/*
Copyright (C) 2012-2016 Ben Kurtovic <ben.kurtovic@gmail.com>
Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>

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


+ 1
- 1
src/mwparserfromhell/parser/ctokenizer/tag_data.c Целия файл

@@ -1,5 +1,5 @@
/*
Copyright (C) 2012-2016 Ben Kurtovic <ben.kurtovic@gmail.com>
Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>

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


+ 1
- 1
src/mwparserfromhell/parser/ctokenizer/tag_data.h Целия файл

@@ -1,5 +1,5 @@
/*
Copyright (C) 2012-2016 Ben Kurtovic <ben.kurtovic@gmail.com>
Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>

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


+ 1
- 1
src/mwparserfromhell/parser/ctokenizer/textbuffer.c Целия файл

@@ -1,5 +1,5 @@
/*
Copyright (C) 2012-2016 Ben Kurtovic <ben.kurtovic@gmail.com>
Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>

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


+ 1
- 1
src/mwparserfromhell/parser/ctokenizer/textbuffer.h Целия файл

@@ -1,5 +1,5 @@
/*
Copyright (C) 2012-2016 Ben Kurtovic <ben.kurtovic@gmail.com>
Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>

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


+ 1
- 1
src/mwparserfromhell/parser/ctokenizer/tok_parse.c Целия файл

@@ -1,5 +1,5 @@
/*
Copyright (C) 2012-2021 Ben Kurtovic <ben.kurtovic@gmail.com>
Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>

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


+ 1
- 1
src/mwparserfromhell/parser/ctokenizer/tok_parse.h Целия файл

@@ -1,5 +1,5 @@
/*
Copyright (C) 2012-2016 Ben Kurtovic <ben.kurtovic@gmail.com>
Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>

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


+ 1
- 1
src/mwparserfromhell/parser/ctokenizer/tok_support.c Целия файл

@@ -1,5 +1,5 @@
/*
Copyright (C) 2012-2018 Ben Kurtovic <ben.kurtovic@gmail.com>
Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>

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


+ 1
- 1
src/mwparserfromhell/parser/ctokenizer/tok_support.h Целия файл

@@ -1,5 +1,5 @@
/*
Copyright (C) 2012-2018 Ben Kurtovic <ben.kurtovic@gmail.com>
Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>

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


+ 1
- 1
src/mwparserfromhell/parser/ctokenizer/tokenizer.c Целия файл

@@ -1,5 +1,5 @@
/*
Copyright (C) 2012-2017 Ben Kurtovic <ben.kurtovic@gmail.com>
Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>

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


+ 1
- 1
src/mwparserfromhell/parser/ctokenizer/tokenizer.h Целия файл

@@ -1,5 +1,5 @@
/*
Copyright (C) 2012-2016 Ben Kurtovic <ben.kurtovic@gmail.com>
Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>

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


+ 1
- 1
src/mwparserfromhell/parser/ctokenizer/tokens.c Целия файл

@@ -1,5 +1,5 @@
/*
Copyright (C) 2012-2016 Ben Kurtovic <ben.kurtovic@gmail.com>
Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>

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


+ 1
- 1
src/mwparserfromhell/parser/ctokenizer/tokens.h Целия файл

@@ -1,5 +1,5 @@
/*
Copyright (C) 2012-2016 Ben Kurtovic <ben.kurtovic@gmail.com>
Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>

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


+ 4
- 2
src/mwparserfromhell/parser/errors.py Целия файл

@@ -1,4 +1,4 @@
# Copyright (C) 2012-2020 Ben Kurtovic <ben.kurtovic@gmail.com>
# Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -18,6 +18,8 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

from __future__ import annotations

__all__ = ["ParserError"]


@@ -31,5 +33,5 @@ class ParserError(Exception):
"""

def __init__(self, extra):
msg = "This is a bug and should be reported. Info: {}.".format(extra)
msg = f"This is a bug and should be reported. Info: {extra}."
super().__init__(msg)

+ 7
- 5
src/mwparserfromhell/parser/tokenizer.py Целия файл

@@ -1,4 +1,4 @@
# Copyright (C) 2012-2021 Ben Kurtovic <ben.kurtovic@gmail.com>
# Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -18,19 +18,21 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

from __future__ import annotations

import html.entities as htmlentities
from math import log
import re
from math import log

from . import contexts, tokens
from .errors import ParserError
from ..definitions import (
get_html_tag,
is_parsable,
is_scheme,
is_single,
is_single_only,
is_scheme,
)
from . import contexts, tokens
from .errors import ParserError

__all__ = ["Tokenizer"]



+ 3
- 1
src/mwparserfromhell/parser/tokens.py Целия файл

@@ -1,4 +1,4 @@
# Copyright (C) 2012-2020 Ben Kurtovic <ben.kurtovic@gmail.com>
# Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -26,6 +26,8 @@ a syntactically valid form by the :class:`.Tokenizer`, and then converted into
the :class`.Wikicode` tree by the :class:`.Builder`.
"""

from __future__ import annotations

__all__ = ["Token"]




+ 3
- 1
src/mwparserfromhell/smart_list/__init__.py Целия файл

@@ -1,4 +1,4 @@
# Copyright (C) 2012-2020 Ben Kurtovic <ben.kurtovic@gmail.com>
# Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>
# Copyright (C) 2019-2020 Yuri Astrakhan <YuriAstrakhan@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -25,6 +25,8 @@ This module contains the :class:`.SmartList` type, as well as its
reflect changes made to the main list, and vice-versa.
"""

from __future__ import annotations

__all__ = ["SmartList"]

from .smart_list import SmartList

+ 3
- 1
src/mwparserfromhell/smart_list/list_proxy.py Целия файл

@@ -1,4 +1,4 @@
# Copyright (C) 2012-2023 Ben Kurtovic <ben.kurtovic@gmail.com>
# Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>
# Copyright (C) 2019-2020 Yuri Astrakhan <YuriAstrakhan@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -19,6 +19,8 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

from __future__ import annotations

import weakref

from .utils import _SliceNormalizerMixIn, inheritdoc


+ 3
- 1
src/mwparserfromhell/smart_list/smart_list.py Целия файл

@@ -1,4 +1,4 @@
# Copyright (C) 2012-2023 Ben Kurtovic <ben.kurtovic@gmail.com>
# Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>
# Copyright (C) 2019-2020 Yuri Astrakhan <YuriAstrakhan@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -19,6 +19,8 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

from __future__ import annotations

from weakref import ref

from .list_proxy import ListProxy


+ 3
- 1
src/mwparserfromhell/smart_list/utils.py Целия файл

@@ -1,4 +1,4 @@
# Copyright (C) 2012-2023 Ben Kurtovic <ben.kurtovic@gmail.com>
# Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>
# Copyright (C) 2019-2020 Yuri Astrakhan <YuriAstrakhan@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -19,6 +19,8 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

from __future__ import annotations

from sys import maxsize

__all__ = []


+ 4
- 2
src/mwparserfromhell/string_mixin.py Целия файл

@@ -1,4 +1,4 @@
# Copyright (C) 2012-2020 Ben Kurtovic <ben.kurtovic@gmail.com>
# Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -23,6 +23,8 @@ This module contains the :class:`.StringMixIn` type, which implements the
interface for the ``str`` type in a dynamic manner.
"""

from __future__ import annotations

from sys import getdefaultencoding

__all__ = ["StringMixIn"]
@@ -95,7 +97,7 @@ class StringMixIn:
def __getattr__(self, attr):
if not hasattr(str, attr):
raise AttributeError(
"{!r} object has no attribute {!r}".format(type(self).__name__, attr)
f"{type(self).__name__!r} object has no attribute {attr!r}"
)
return getattr(self.__str__(), attr)



+ 1
- 1
src/mwparserfromhell/utils.py Целия файл

@@ -1,4 +1,4 @@
# Copyright (C) 2012-2023 Ben Kurtovic <ben.kurtovic@gmail.com>
# Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal


+ 41
- 40
src/mwparserfromhell/wikicode.py Целия файл

@@ -1,4 +1,4 @@
# Copyright (C) 2012-2020 Ben Kurtovic <ben.kurtovic@gmail.com>
# Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -18,9 +18,12 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

from __future__ import annotations

import re
from collections.abc import Generator, Iterable
from itertools import chain
from typing import Any, Callable, Generator, Iterable, Optional, Union, cast, overload
from typing import Any, Callable, cast, overload

from .nodes import (
Argument,
@@ -67,9 +70,9 @@ class Wikicode(StringMixIn):
def _get_children(
node: Node,
contexts: bool = False,
restrict: Optional[type] = None,
parent: Optional["Wikicode"] = None,
) -> Generator[Union[tuple[Optional["Wikicode"], Node], Node], None, None]:
restrict: type | None = None,
parent: Wikicode | None = None,
) -> Generator[tuple[Wikicode | None, Node] | 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):
@@ -80,7 +83,7 @@ class Wikicode(StringMixIn):
yield from sub

@staticmethod
def _slice_replace(code: "Wikicode", index: slice, old: str, new: str) -> None:
def _slice_replace(code: Wikicode, index: slice, old: str, new: str) -> None:
"""Replace the string *old* with *new* across *index* in *code*."""
nodes = [str(node) for node in code.get(index)]
substring = "".join(nodes).replace(old, new)
@@ -88,11 +91,11 @@ class Wikicode(StringMixIn):

@staticmethod
def _build_matcher(
matches: Union[
Callable[[Node], Union[bool, re.Match[str], None]], re.Pattern, str, None
],
matches: (
Callable[[Node], bool | re.Match[str] | None] | re.Pattern | str | None
),
flags: int,
) -> Callable[[Node], Union[bool, re.Match[str], None]]:
) -> Callable[[Node], bool | re.Match[str] | None]:
"""Helper for :meth:`_indexed_ifilter` and others.

If *matches* is a function, return it. If it's a regex, return a
@@ -108,10 +111,10 @@ class Wikicode(StringMixIn):
def _indexed_ifilter(
self,
recursive: bool = True,
matches: Union[Callable[[Node], bool], re.Pattern, str, None] = None,
matches: Callable[[Node], bool] | re.Pattern | str | None = None,
flags: int = FLAGS,
forcetype: Optional[type] = None,
) -> Generator[tuple[int, Node], None, None]:
forcetype: type | None = None,
) -> Generator[tuple[int, Node]]:
"""Iterate over nodes and their corresponding indices in the node list.

The arguments are interpreted as for :meth:`ifilter`. For each tuple
@@ -124,7 +127,7 @@ class Wikicode(StringMixIn):
if recursive:
restrict = forcetype if recursive == self.RECURSE_OTHERS else None

def getter(i: int, node: Node) -> Generator[tuple[int, Node], None, None]:
def getter(i: int, node: Node) -> Generator[tuple[int, Node]]:
for ch in self._get_children(node, restrict=restrict):
yield (i, cast(Node, ch))

@@ -135,7 +138,7 @@ class Wikicode(StringMixIn):
if (not forcetype or isinstance(node, forcetype)) and match(node):
yield (i, node)

def _is_child_wikicode(self, obj: "Wikicode", recursive: bool = True) -> bool:
def _is_child_wikicode(self, obj: Wikicode, recursive: bool = True) -> bool:
"""Return whether the given :class:`.Wikicode` is a descendant."""

def deref(nodes):
@@ -157,8 +160,8 @@ class Wikicode(StringMixIn):
return False

def _do_strong_search(
self, obj: Union[Node, "Wikicode"], recursive: bool = True
) -> tuple["Wikicode", slice]:
self, obj: Node | Wikicode, recursive: bool = True
) -> tuple[Wikicode, slice]:
"""Search for the specific element *obj* within the node list.

*obj* can be either a :class:`.Node` or a :class:`.Wikicode` object. If
@@ -194,7 +197,7 @@ class Wikicode(StringMixIn):

def _do_weak_search(
self, obj: Any, recursive: bool
) -> list[tuple[bool, "Wikicode", slice]]:
) -> list[tuple[bool, Wikicode, slice]]:
"""Search for an element that looks like *obj* within the node list.

This follows the same rules as :meth:`_do_strong_search` with some
@@ -215,7 +218,7 @@ class Wikicode(StringMixIn):
if not obj or obj not in self:
raise ValueError(obj)
results = []
contexts: list["Wikicode"] = [self]
contexts: list[Wikicode] = [self]
while contexts:
context = contexts.pop()
i = len(context.nodes) - 1
@@ -239,7 +242,7 @@ class Wikicode(StringMixIn):
return results

def _get_tree(
self, code: "Wikicode", lines: list[str], marker: Any, indent: int
self, code: Wikicode, lines: list[str], marker: Any, indent: int
) -> list[str]:
"""Build a tree to illustrate the way the Wikicode object was parsed.

@@ -314,7 +317,7 @@ class Wikicode(StringMixIn):
return self._nodes

@nodes.setter
def nodes(self, value: Union[list[Node], Any]) -> None:
def nodes(self, value: list[Node] | Any) -> None:
if not isinstance(value, list):
value = parse_anything(value).nodes
self._nodes = value
@@ -347,7 +350,7 @@ class Wikicode(StringMixIn):
else:
self.nodes.pop(index)

def contains(self, obj: Union[Node, "Wikicode", str]) -> bool:
def contains(self, obj: Node | Wikicode | str) -> bool:
"""Return whether this Wikicode object contains *obj*.

If *obj* is a :class:`.Node` or :class:`.Wikicode` object, then we
@@ -362,7 +365,7 @@ class Wikicode(StringMixIn):
return False
return True

def index(self, obj: Union[Node, "Wikicode", str], recursive: bool = False) -> int:
def index(self, obj: Node | Wikicode | str, recursive: bool = False) -> int:
"""Return the index of *obj* in the list of nodes.

Raises :exc:`ValueError` if *obj* is not found. If *recursive* is
@@ -381,7 +384,7 @@ class Wikicode(StringMixIn):
return i
raise ValueError(obj)

def get_ancestors(self, obj: Union[Node, "Wikicode"]) -> list[Node]:
def get_ancestors(self, obj: Node | Wikicode) -> list[Node]:
"""Return a list of all ancestor nodes of the :class:`.Node` *obj*.

The list is ordered from the most shallow ancestor (greatest great-
@@ -398,7 +401,7 @@ class Wikicode(StringMixIn):
object. Will raise :exc:`ValueError` if it wasn't found.
"""

def _get_ancestors(code: "Wikicode", needle: Node) -> Union[list[Node], None]:
def _get_ancestors(code: Wikicode, needle: Node) -> list[Node] | None:
for node in code.nodes:
if node is needle:
return []
@@ -418,7 +421,7 @@ class Wikicode(StringMixIn):
raise ValueError(obj)
return ancestors

def get_parent(self, obj: Union[Node, "Wikicode"]) -> Union[Node, None]:
def get_parent(self, obj: Node | Wikicode) -> Node | None:
"""Return the direct parent node of the :class:`.Node` *obj*.

This function is equivalent to calling :meth:`.get_ancestors` and
@@ -440,7 +443,7 @@ class Wikicode(StringMixIn):
self.nodes.insert(index, node)

def insert_before(
self, obj: Union[Node, "Wikicode", str], value: Any, recursive: bool = True
self, obj: Node | Wikicode | str, value: Any, recursive: bool = True
) -> None:
"""Insert *value* immediately before *obj*.

@@ -465,7 +468,7 @@ class Wikicode(StringMixIn):
self._slice_replace(context, index, obj, str(value) + obj)

def insert_after(
self, obj: Union[Node, "Wikicode", str], value: Any, recursive: bool = True
self, obj: Node | Wikicode | str, value: Any, recursive: bool = True
) -> None:
"""Insert *value* immediately after *obj*.

@@ -490,7 +493,7 @@ class Wikicode(StringMixIn):
self._slice_replace(context, index, obj, obj + str(value))

def replace(
self, obj: Union[Node, "Wikicode", str], value: Any, recursive: bool = True
self, obj: Node | Wikicode | str, value: Any, recursive: bool = True
) -> None:
"""Replace *obj* with *value*.

@@ -526,7 +529,7 @@ class Wikicode(StringMixIn):
for node in nodes:
self.nodes.append(node)

def remove(self, obj: Union[Node, "Wikicode", str], recursive: bool = True) -> None:
def remove(self, obj: Node | Wikicode | str, recursive: bool = True) -> None:
"""Remove *obj* from the list of nodes.

*obj* can be either a string, a :class:`.Node`, or another
@@ -552,9 +555,7 @@ class Wikicode(StringMixIn):

def matches(
self,
other: Union[
Node, "Wikicode", str, bytes, Iterable[Union[Node, "Wikicode", str, bytes]]
],
other: Node | Wikicode | str | bytes | Iterable[Node | Wikicode | str | bytes],
) -> bool:
"""Do a loose equivalency test suitable for comparing page names.

@@ -583,10 +584,10 @@ class Wikicode(StringMixIn):
def ifilter(
self,
recursive: bool = True,
matches: Union[Callable[[Node], bool], re.Pattern, str, None] = None,
matches: Callable[[Node], bool] | re.Pattern | str | None = None,
flags: int = FLAGS,
forcetype: Optional[type] = None,
) -> Generator[Node, None, None]:
forcetype: type | None = None,
) -> Generator[Node]:
"""Iterate over nodes in our list matching certain conditions.

If *forcetype* is given, only nodes that are instances of this type (or
@@ -622,13 +623,13 @@ class Wikicode(StringMixIn):

def get_sections(
self,
levels: Optional[Iterable[int]] = None,
matches: Union[Callable[[Node], bool], re.Pattern, str, None] = None,
levels: Iterable[int] | None = None,
matches: Callable[[Node], bool] | re.Pattern | str | None = None,
flags: int = FLAGS,
flat: bool = False,
include_lead: Optional[bool] = None,
include_lead: bool | None = None,
include_headings: bool = True,
) -> list["Wikicode"]:
) -> list[Wikicode]:
"""Return a list of sections within the page.

Sections are returned as :class:`.Wikicode` objects with a shared node


+ 3
- 1
tests/conftest.py Целия файл

@@ -1,4 +1,4 @@
# Copyright (C) 2012-2021 Ben Kurtovic <ben.kurtovic@gmail.com>
# Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -18,6 +18,8 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

from __future__ import annotations

from mwparserfromhell.nodes import (
Argument,
Comment,


+ 4
- 1
tests/test_argument.py Целия файл

@@ -1,4 +1,4 @@
# Copyright (C) 2012-2020 Ben Kurtovic <ben.kurtovic@gmail.com>
# Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -22,9 +22,12 @@
Test cases for the Argument node.
"""

from __future__ import annotations

import pytest

from mwparserfromhell.nodes import Argument, Text

from .conftest import assert_wikicode_equal, wrap, wraptext




+ 4
- 1
tests/test_attribute.py Целия файл

@@ -1,4 +1,4 @@
# Copyright (C) 2012-2020 Ben Kurtovic <ben.kurtovic@gmail.com>
# Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -22,10 +22,13 @@
Test cases for the Attribute node extra.
"""

from __future__ import annotations

import pytest

from mwparserfromhell.nodes import Template
from mwparserfromhell.nodes.extras import Attribute

from .conftest import assert_wikicode_equal, wrap, wraptext




+ 5
- 2
tests/test_builder.py Целия файл

@@ -1,4 +1,4 @@
# Copyright (C) 2012-2019 Ben Kurtovic <ben.kurtovic@gmail.com>
# Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -22,6 +22,8 @@
Tests for the builder, which turns tokens into Wikicode objects.
"""

from __future__ import annotations

import pytest

from mwparserfromhell.nodes import (
@@ -36,8 +38,9 @@ from mwparserfromhell.nodes import (
Wikilink,
)
from mwparserfromhell.nodes.extras import Attribute, Parameter
from mwparserfromhell.parser import tokens, ParserError
from mwparserfromhell.parser import ParserError, tokens
from mwparserfromhell.parser.builder import Builder

from .conftest import assert_wikicode_equal, wrap, wraptext




+ 3
- 1
tests/test_comment.py Целия файл

@@ -1,4 +1,4 @@
# Copyright (C) 2012-2020 Ben Kurtovic <ben.kurtovic@gmail.com>
# Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -22,6 +22,8 @@
Test cases for the Comment node.
"""

from __future__ import annotations

import pytest

from mwparserfromhell.nodes import Comment


+ 4
- 2
tests/test_docs.py Целия файл

@@ -1,4 +1,4 @@
# Copyright (C) 2012-2020 Ben Kurtovic <ben.kurtovic@gmail.com>
# Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -22,9 +22,11 @@
Integration test cases for mwparserfromhell's documentation.
"""

from __future__ import annotations

import json
from io import StringIO
import os
from io import StringIO
from urllib.parse import urlencode
from urllib.request import urlopen



+ 4
- 1
tests/test_external_link.py Целия файл

@@ -1,4 +1,4 @@
# Copyright (C) 2012-2020 Ben Kurtovic <ben.kurtovic@gmail.com>
# Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -22,9 +22,12 @@
Test cases for the ExternalLink node.
"""

from __future__ import annotations

import pytest

from mwparserfromhell.nodes import ExternalLink, Text

from .conftest import assert_wikicode_equal, wrap, wraptext




+ 4
- 1
tests/test_heading.py Целия файл

@@ -1,4 +1,4 @@
# Copyright (C) 2012-2020 Ben Kurtovic <ben.kurtovic@gmail.com>
# Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -22,9 +22,12 @@
Test cases for the Heading node.
"""

from __future__ import annotations

import pytest

from mwparserfromhell.nodes import Heading, Text

from .conftest import assert_wikicode_equal, wrap, wraptext




+ 3
- 1
tests/test_html_entity.py Целия файл

@@ -1,4 +1,4 @@
# Copyright (C) 2012-2020 Ben Kurtovic <ben.kurtovic@gmail.com>
# Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -22,6 +22,8 @@
Test cases for the HTMLEntity node.
"""

from __future__ import annotations

import pytest

from mwparserfromhell.nodes import HTMLEntity


+ 4
- 1
tests/test_parameter.py Целия файл

@@ -1,4 +1,4 @@
# Copyright (C) 2012-2020 Ben Kurtovic <ben.kurtovic@gmail.com>
# Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -22,9 +22,12 @@
Test cases for the Parameter node extra.
"""

from __future__ import annotations

import pytest

from mwparserfromhell.nodes.extras import Parameter

from .conftest import assert_wikicode_equal, wraptext




+ 4
- 1
tests/test_parser.py Целия файл

@@ -1,4 +1,4 @@
# Copyright (C) 2012-2016 Ben Kurtovic <ben.kurtovic@gmail.com>
# Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -22,11 +22,14 @@
Tests for the Parser class itself, which tokenizes and builds nodes.
"""

from __future__ import annotations

import pytest

from mwparserfromhell import parser
from mwparserfromhell.nodes import Tag, Template, Text, Wikilink
from mwparserfromhell.nodes.extras import Parameter

from .conftest import assert_wikicode_equal, wrap, wraptext




+ 3
- 1
tests/test_smart_list.py Целия файл

@@ -1,4 +1,4 @@
# Copyright (C) 2012-2023 Ben Kurtovic <ben.kurtovic@gmail.com>
# Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -22,6 +22,8 @@
Test cases for the SmartList class and its child, ListProxy.
"""

from __future__ import annotations

import pickle

import pytest


+ 3
- 1
tests/test_string_mixin.py Целия файл

@@ -1,4 +1,4 @@
# Copyright (C) 2012-2020 Ben Kurtovic <ben.kurtovic@gmail.com>
# Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -22,6 +22,8 @@
Test cases for the StringMixIn class.
"""

from __future__ import annotations

import sys
from types import GeneratorType



+ 4
- 1
tests/test_tag.py Целия файл

@@ -1,4 +1,4 @@
# Copyright (C) 2012-2020 Ben Kurtovic <ben.kurtovic@gmail.com>
# Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -22,10 +22,13 @@
Test cases for the Tag node.
"""

from __future__ import annotations

import pytest

from mwparserfromhell.nodes import Tag, Template, Text
from mwparserfromhell.nodes.extras import Attribute

from .conftest import assert_wikicode_equal, wrap, wraptext




+ 5
- 2
tests/test_template.py Целия файл

@@ -1,4 +1,4 @@
# Copyright (C) 2012-2020 Ben Kurtovic <ben.kurtovic@gmail.com>
# Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -22,13 +22,16 @@
Test cases for the Template node.
"""

from __future__ import annotations

from difflib import unified_diff

import pytest

from mwparserfromhell import parse
from mwparserfromhell.nodes import HTMLEntity, Template, Text
from mwparserfromhell.nodes.extras import Parameter
from mwparserfromhell import parse
from .conftest import assert_wikicode_equal, wrap, wraptext




+ 3
- 1
tests/test_text.py Целия файл

@@ -1,4 +1,4 @@
# Copyright (C) 2012-2020 Ben Kurtovic <ben.kurtovic@gmail.com>
# Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -22,6 +22,8 @@
Test cases for the Text node.
"""

from __future__ import annotations

import pytest

from mwparserfromhell.nodes import Text


+ 4
- 2
tests/test_tokenizer.py Целия файл

@@ -1,4 +1,4 @@
# Copyright (C) 2012-2016 Ben Kurtovic <ben.kurtovic@gmail.com>
# Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -18,9 +18,11 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

from __future__ import annotations

import codecs
from os import listdir, path
import warnings
from os import listdir, path

import pytest



+ 3
- 1
tests/test_tokens.py Целия файл

@@ -1,4 +1,4 @@
# Copyright (C) 2012-2020 Ben Kurtovic <ben.kurtovic@gmail.com>
# Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -22,6 +22,8 @@
Test cases for the Token class and its subclasses.
"""

from __future__ import annotations

import pytest

from mwparserfromhell.parser import tokens


+ 4
- 1
tests/test_utils.py Целия файл

@@ -1,4 +1,4 @@
# Copyright (C) 2012-2016 Ben Kurtovic <ben.kurtovic@gmail.com>
# Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -22,10 +22,13 @@
Tests for the utils module, which provides parse_anything().
"""

from __future__ import annotations

import pytest

from mwparserfromhell.nodes import Template, Text
from mwparserfromhell.utils import parse_anything

from .conftest import assert_wikicode_equal, wrap, wraptext




+ 7
- 4
tests/test_wikicode.py Целия файл

@@ -1,4 +1,4 @@
# Copyright (C) 2012-2023 Ben Kurtovic <ben.kurtovic@gmail.com>
# Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -22,17 +22,20 @@
Tests for the Wikicode class, which manages a list of nodes.
"""

from functools import partial
import re
from __future__ import annotations
import pickle
import re
from functools import partial
from types import GeneratorType

import pytest

from mwparserfromhell import parse
from mwparserfromhell.nodes import Argument, Heading, Template, Text
from mwparserfromhell.smart_list import SmartList
from mwparserfromhell.wikicode import Wikicode
from mwparserfromhell import parse
from .conftest import wrap, wraptext




+ 4
- 1
tests/test_wikilink.py Целия файл

@@ -1,4 +1,4 @@
# Copyright (C) 2012-2020 Ben Kurtovic <ben.kurtovic@gmail.com>
# Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -22,9 +22,12 @@
Test cases for the Wikilink node.
"""

from __future__ import annotations

import pytest

from mwparserfromhell.nodes import Text, Wikilink

from .conftest import assert_wikicode_equal, wrap, wraptext




Зареждане…
Отказ
Запис