Просмотр исходного кода

Update type annotations and rerun ruff

tags/v0.7.0
Ben Kurtovic 1 год назад
Родитель
Сommit
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 # mwparserfromhell documentation build configuration file, created by
# sphinx-quickstart on Tue Aug 21 20:47:26 2012. # 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 # All configuration values have a default; values that are commented out
# serve to show the default. # serve to show the default.


import sys
import os import os
import sys


# If extensions (or modules to document with autodoc) are in another directory, # 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 # 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 # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # 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. and scale with the number of loops.
""" """


from __future__ import annotations

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


import psutil import psutil


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


def _load(self): def _load(self):
def load_file(filename): def load_file(filename):
with open(filename, "rU") as fp:
with open(filename) as fp:
text = fp.read() text = fp.read()
name = path.split(filename)[1][: 0 - len(extension)] name = path.split(filename)[1][: 0 - len(extension)]
self._parse_file(name, text) 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)" 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)) sys.stdout.write(tmpl.format(Color.YELLOW, Color.RESET, d, p, bpt))
else: else:
sys.stdout.write("{}OK{}".format(Color.GREEN, Color.RESET))
sys.stdout.write(f"{Color.GREEN}OK{Color.RESET}")


def run(self): def run(self):
"""Run the memory test suite.""" """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 # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # 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 - mwparserfromhell/parser/ctokenizer/definitions.h
""" """


from __future__ import annotations

__all__ = [ __all__ = [
"get_html_tag", "get_html_tag",
"is_parsable", "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 # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # 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. contain text or more templates.
""" """


from __future__ import annotations

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


__all__ = [ __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 # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # 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 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE. # 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 from ..string_mixin import StringMixIn


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


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


def __showtree__( def __showtree__(
self, self,
write: Callable[[str], None], write: Callable[[str], None],
get: Callable[["Wikicode"], None],
get: Callable[[Wikicode], None],
mark: Callable[[], None], mark: Callable[[], None],
) -> None: ) -> None:
write(str(self)) 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 # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # 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 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE. # 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 ..utils import parse_anything
from ._base import Node


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


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


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


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


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


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


This will be ``None`` if the argument wasn't defined with one. The 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 # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # 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 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE. # SOFTWARE.


from __future__ import annotations


from typing import Any 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 # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # 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 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE. # 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 ..utils import parse_anything
from ._base import Node


if TYPE_CHECKING: if TYPE_CHECKING:
from ..wikicode import Wikicode from ..wikicode import Wikicode
@@ -55,12 +57,12 @@ class ExternalLink(Node):
return "[" + str(self.url) + "]" return "[" + str(self.url) + "]"
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 yield self.url
if self.title is not None: if self.title is not None:
yield self.title yield self.title


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


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


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


@property @property
def title(self) -> Optional["Wikicode"]:
def title(self) -> Wikicode | None:
"""The link title (if given), as a :class:`.Wikicode` object.""" """The link title (if given), as a :class:`.Wikicode` object."""
return self._title 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 # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # 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. nodes themselves. This includes template parameters and HTML tag attributes.
""" """


from __future__ import annotations

__all__ = [ __all__ = [
"Attribute", "Attribute",
"Parameter", "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 # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # 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 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE. # SOFTWARE.


from __future__ import annotations


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


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


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


@staticmethod @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.""" """Return valid quotes for the given value, or None if unneeded."""
if not value: if not value:
return None return None
@@ -95,15 +96,15 @@ class Attribute(StringMixIn):
setattr(self, attr, value) setattr(self, attr, value)


@staticmethod @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.""" """Coerce a quote type into an acceptable value, or raise an error."""
coerced_quotes = str(quotes) if quotes else None coerced_quotes = str(quotes) if quotes else None
if coerced_quotes not in [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 return coerced_quotes


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


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


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


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


@property @property
def quotes(self) -> Optional[str]:
def quotes(self) -> str | None:
"""How to enclose the attribute value. ``"``, ``'``, or ``None``.""" """How to enclose the attribute value. ``"``, ``'``, or ``None``."""
return self._quotes 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 # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # 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 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE. # SOFTWARE.


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


import re import re
from typing import TYPE_CHECKING, Any


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


@staticmethod @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 whether or not the given key can be hidden."""
return re.match(r"[1-9][0-9]*$", str(key).strip()) return re.match(r"[1-9][0-9]*$", str(key).strip())


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


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


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


@@ -83,5 +84,5 @@ class Parameter(StringMixIn):
def showkey(self, newval: Any) -> None: def showkey(self, newval: Any) -> None:
newval = bool(newval) newval = bool(newval)
if not newval and not self.can_hide_key(self.name): 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 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 # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # 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 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE. # 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 ..utils import parse_anything
from ._base import Node


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


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


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


@property @property
def title(self) -> "Wikicode":
def title(self) -> Wikicode:
"""The title of the heading, as a :class:`.Wikicode` object.""" """The title of the heading, as a :class:`.Wikicode` object."""
return self._title 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 # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # 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 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE. # SOFTWARE.


from typing import Any, Optional
from __future__ import annotations

import html.entities as htmlentities import html.entities as htmlentities
from typing import Any


from ._base import Node from ._base import Node


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


def __str__(self) -> str: def __str__(self) -> str:
if self.named: if self.named:
return "&{};".format(self.value)
return f"&{self.value};"
if self.hexadecimal: 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"): if kwargs.get("normalize"):
return self.normalize() return self.normalize()
return str(self) return str(self)
@@ -84,23 +86,21 @@ class HTMLEntity(Node):
except ValueError: except ValueError:
if newval not in htmlentities.entitydefs: if newval not in htmlentities.entitydefs:
raise ValueError( raise ValueError(
"entity value {!r} is not a valid name".format(newval)
f"entity value {newval!r} is not a valid name"
) from None ) from None
self._named = True self._named = True
self._hexadecimal = False self._hexadecimal = False
else: else:
if intval < 0 or intval > 0x10FFFF: if intval < 0 or intval > 0x10FFFF:
raise ValueError( 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 ) from None
self._named = False self._named = False
self._hexadecimal = True self._hexadecimal = True
else: else:
test = int(newval, 16 if self.hexadecimal else 10) test = int(newval, 16 if self.hexadecimal else 10)
if test < 0 or test > 0x10FFFF: 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._named = False
self._value = newval self._value = newval


@@ -118,16 +118,14 @@ class HTMLEntity(Node):
def named(self, newval: bool) -> None: def named(self, newval: bool) -> None:
newval = bool(newval) newval = bool(newval)
if newval and self.value not in htmlentities.entitydefs: 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: if not newval:
try: try:
int(self.value, 16) int(self.value, 16)
except ValueError as exc: except ValueError as exc:
raise ValueError( 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 ) from exc
self._named = newval 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 # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # 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 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE. # 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 ..definitions import is_visible
from ..utils import parse_anything from ..utils import parse_anything
from ._base import Node
from .extras import Attribute


if TYPE_CHECKING: if TYPE_CHECKING:
from ..wikicode import Wikicode from ..wikicode import Wikicode
@@ -39,19 +41,19 @@ class Tag(Node):
self, self,
tag: Any, tag: Any,
contents: Any = None, 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, self_closing: bool = False,
invalid: bool = False, invalid: bool = False,
implicit: bool = False, implicit: bool = False,
padding: str = "", 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__() 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.tag = tag
self.contents = contents self.contents = contents
@@ -98,7 +100,7 @@ class Tag(Node):
result += "</" + str(self.closing_tag) + ">" result += "</" + str(self.closing_tag) + ">"
return result return result


def __children__(self) -> Generator["Wikicode", None, None]:
def __children__(self) -> Generator[Wikicode, None, None]:
if not self.wiki_markup: if not self.wiki_markup:
yield self.tag yield self.tag
for attr in self.attributes: for attr in self.attributes:
@@ -110,7 +112,7 @@ class Tag(Node):
if not self.wiki_markup and self.closing_tag: if not self.wiki_markup and self.closing_tag:
yield 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)): if self.contents and is_visible(str(self.tag)):
return self.contents.strip_code(**kwargs) return self.contents.strip_code(**kwargs)
return None return None
@@ -118,7 +120,7 @@ class Tag(Node):
def __showtree__( def __showtree__(
self, self,
write: Callable[[str], None], write: Callable[[str], None],
get: Callable[["Wikicode"], None],
get: Callable[[Wikicode], None],
mark: Callable[[], None], mark: Callable[[], None],
) -> None: ) -> None:
write("</" if self.invalid else "<") write("</" if self.invalid else "<")
@@ -140,7 +142,7 @@ class Tag(Node):
write(">") write(">")


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


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


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


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


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


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


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


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


@wiki_markup.setter @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 self._wiki_markup = str(value) if value else None
if not value or not self.closing_wiki_markup: if not value or not self.closing_wiki_markup:
self._closing_wiki_markup = self._wiki_markup self._closing_wiki_markup = self._wiki_markup
@@ -235,7 +237,7 @@ class Tag(Node):
self._padding = value self._padding = value


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


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


@property @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. """The separator between the padding and content in a wiki markup tag.


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


@wiki_style_separator.setter @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 self._wiki_style_separator = str(value) if value else None


@property @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. """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 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 return self._closing_wiki_markup


@closing_wiki_markup.setter @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 self._closing_wiki_markup = str(value) if value else None


def has(self, name: str) -> bool: def has(self, name: str) -> bool:
@@ -304,7 +306,7 @@ class Tag(Node):
self, self,
name: Any, name: Any,
value: Any = None, value: Any = None,
quotes: Optional[str] = '"',
quotes: str | None = '"',
pad_first: str = " ", pad_first: str = " ",
pad_before_eq: str = "", pad_before_eq: str = "",
pad_after_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 # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # 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, # 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 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE. # SOFTWARE.

from __future__ import annotations

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


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


if TYPE_CHECKING: if TYPE_CHECKING:
from ..wikicode import Wikicode from ..wikicode import Wikicode
@@ -54,10 +52,10 @@ T = TypeVar("T")
class Template(Node): class Template(Node):
"""Represents a template in wikicode, like ``{{foo}}``.""" """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__() super().__init__()
self.name = name self.name = name
self._params: List[Parameter] = params or []
self._params: list[Parameter] = params or []


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


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


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


@staticmethod @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. """Return *code* with *char* escaped as an HTML entity.


The main use of this is to escape pipes (``|``) or equal signs (``=``) 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) code.replace(node, node.replace(char, replacement), False)


@staticmethod @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. """Return the most likely spacing convention given different options.


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


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


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


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


This will examine the existing parameters and use This will examine the existing parameters and use
:meth:`_select_theory` to determine if there are any preferred styles :meth:`_select_theory` to determine if there are any preferred styles
for how much whitespace to put before or after the value. 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: for param in self.params:
if not param.showkey: if not param.showkey:
continue continue
@@ -205,7 +203,7 @@ class Template(Node):
return False return False


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


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


@property @property
def params(self) -> List[Parameter]:
def params(self) -> list[Parameter]:
"""The list of parameters contained within the template.""" """The list of parameters contained within the template."""
return self._params 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*. """Return ``True`` if any parameter in the template is named *name*.


With *ignore_empty*, ``False`` will be returned even if the template With *ignore_empty*, ``False`` will be returned even if the template
@@ -234,17 +232,17 @@ class Template(Node):
return True return True
return False 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`.""" """Alias for :meth:`has`."""
return self.has(name, ignore_empty) return self.has(name, ignore_empty)


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


@overload @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*. """Get the parameter whose name is *name*.


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


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


def add( def add(
self, self,
name: Any, name: Any,
value: 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, preserve_spacing: bool = True,
) -> Parameter: ) -> Parameter:
"""Add a parameter to the template with a given *name* and *value*. """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: def __setitem__(self, name: Any, value: Any) -> Parameter:
return self.add(name, value) 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*. """Remove a parameter from the template, identified by *param*.


If *param* is a :class:`.Parameter` object, it will be matched exactly, 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): for i in reversed(to_remove):
self.params.pop(i) self.params.pop(i)


def __delitem__(self, param: Union[Parameter, str]) -> None:
def __delitem__(self, param: Parameter | str) -> None:
return self.remove(param) 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 # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # 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 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE. # SOFTWARE.


from __future__ import annotations


from typing import TYPE_CHECKING, Any, Callable from typing import TYPE_CHECKING, Any, Callable


@@ -45,7 +46,7 @@ class Text(Node):
def __showtree__( def __showtree__(
self, self,
write: Callable[[str], None], write: Callable[[str], None],
get: Callable[["Wikicode"], None],
get: Callable[[Wikicode], None],
mark: Callable[[], None], mark: Callable[[], None],
) -> None: ) -> None:
write(str(self).encode("unicode_escape").decode("utf8")) 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 # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # 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 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE. # 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 ..utils import parse_anything
from ._base import Node


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


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


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


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


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


@property @property
def text(self) -> Optional["Wikicode"]:
def text(self) -> Wikicode | None:
"""The text to display (if any), as a :class:`.Wikicode` object.""" """The text to display (if any), as a :class:`.Wikicode` object."""
return self._text 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 # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # 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. together into one interface.
""" """


from __future__ import annotations

from .builder import Builder from .builder import Builder
from .errors import ParserError 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 # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # 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 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE. # SOFTWARE.


from __future__ import annotations


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


__all__ = ["Builder"] __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 # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # of this software and associated documentation files (the "Software"), to deal
@@ -114,6 +114,8 @@ Aggregate contexts:


""" """


from __future__ import annotations

# Local contexts: # Local contexts:


TEMPLATE_NAME = 1 << 0 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # 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 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE. # SOFTWARE.


from __future__ import annotations

__all__ = ["ParserError"] __all__ = ["ParserError"]




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


def __init__(self, extra): 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) 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 # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # 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 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE. # SOFTWARE.


from __future__ import annotations

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


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


__all__ = ["Tokenizer"] __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 # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # 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`. the :class`.Wikicode` tree by the :class:`.Builder`.
""" """


from __future__ import annotations

__all__ = ["Token"] __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> # Copyright (C) 2019-2020 Yuri Astrakhan <YuriAstrakhan@gmail.com>
# #
# Permission is hereby granted, free of charge, to any person obtaining a copy # 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. reflect changes made to the main list, and vice-versa.
""" """


from __future__ import annotations

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


from .smart_list import 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> # Copyright (C) 2019-2020 Yuri Astrakhan <YuriAstrakhan@gmail.com>
# #
# Permission is hereby granted, free of charge, to any person obtaining a copy # 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 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE. # SOFTWARE.


from __future__ import annotations

import weakref import weakref


from .utils import _SliceNormalizerMixIn, inheritdoc 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> # Copyright (C) 2019-2020 Yuri Astrakhan <YuriAstrakhan@gmail.com>
# #
# Permission is hereby granted, free of charge, to any person obtaining a copy # 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 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE. # SOFTWARE.


from __future__ import annotations

from weakref import ref from weakref import ref


from .list_proxy import ListProxy 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> # Copyright (C) 2019-2020 Yuri Astrakhan <YuriAstrakhan@gmail.com>
# #
# Permission is hereby granted, free of charge, to any person obtaining a copy # 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 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE. # SOFTWARE.


from __future__ import annotations

from sys import maxsize from sys import maxsize


__all__ = [] __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 # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # 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. interface for the ``str`` type in a dynamic manner.
""" """


from __future__ import annotations

from sys import getdefaultencoding from sys import getdefaultencoding


__all__ = ["StringMixIn"] __all__ = ["StringMixIn"]
@@ -95,7 +97,7 @@ class StringMixIn:
def __getattr__(self, attr): def __getattr__(self, attr):
if not hasattr(str, attr): if not hasattr(str, attr):
raise AttributeError( 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) 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 # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # 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 # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # 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 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE. # SOFTWARE.


from __future__ import annotations

import re import re
from collections.abc import Generator, Iterable
from itertools import chain 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 ( from .nodes import (
Argument, Argument,
@@ -67,9 +70,9 @@ class Wikicode(StringMixIn):
def _get_children( def _get_children(
node: Node, node: Node,
contexts: bool = False, 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*.""" """Iterate over all child :class:`.Node`\\ s of a given *node*."""
yield (parent, node) if contexts else node yield (parent, node) if contexts else node
if restrict and isinstance(node, restrict): if restrict and isinstance(node, restrict):
@@ -80,7 +83,7 @@ class Wikicode(StringMixIn):
yield from sub yield from sub


@staticmethod @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*.""" """Replace the string *old* with *new* across *index* in *code*."""
nodes = [str(node) for node in code.get(index)] nodes = [str(node) for node in code.get(index)]
substring = "".join(nodes).replace(old, new) substring = "".join(nodes).replace(old, new)
@@ -88,11 +91,11 @@ class Wikicode(StringMixIn):


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


If *matches* is a function, return it. If it's a regex, return a If *matches* is a function, return it. If it's a regex, return a
@@ -108,10 +111,10 @@ class Wikicode(StringMixIn):
def _indexed_ifilter( def _indexed_ifilter(
self, self,
recursive: bool = True, 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, 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. """Iterate over nodes and their corresponding indices in the node list.


The arguments are interpreted as for :meth:`ifilter`. For each tuple The arguments are interpreted as for :meth:`ifilter`. For each tuple
@@ -124,7 +127,7 @@ class Wikicode(StringMixIn):
if recursive: if recursive:
restrict = forcetype if recursive == self.RECURSE_OTHERS else None 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): for ch in self._get_children(node, restrict=restrict):
yield (i, cast(Node, ch)) yield (i, cast(Node, ch))


@@ -135,7 +138,7 @@ class Wikicode(StringMixIn):
if (not forcetype or isinstance(node, forcetype)) and match(node): if (not forcetype or isinstance(node, forcetype)) and match(node):
yield (i, 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.""" """Return whether the given :class:`.Wikicode` is a descendant."""


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


def _do_strong_search( 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. """Search for the specific element *obj* within the node list.


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


def _do_weak_search( def _do_weak_search(
self, obj: Any, recursive: bool 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. """Search for an element that looks like *obj* within the node list.


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


def _get_tree( 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]: ) -> list[str]:
"""Build a tree to illustrate the way the Wikicode object was parsed. """Build a tree to illustrate the way the Wikicode object was parsed.


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


@nodes.setter @nodes.setter
def nodes(self, value: Union[list[Node], Any]) -> None:
def nodes(self, value: list[Node] | Any) -> None:
if not isinstance(value, list): if not isinstance(value, list):
value = parse_anything(value).nodes value = parse_anything(value).nodes
self._nodes = value self._nodes = value
@@ -347,7 +350,7 @@ class Wikicode(StringMixIn):
else: else:
self.nodes.pop(index) 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*. """Return whether this Wikicode object contains *obj*.


If *obj* is a :class:`.Node` or :class:`.Wikicode` object, then we If *obj* is a :class:`.Node` or :class:`.Wikicode` object, then we
@@ -362,7 +365,7 @@ class Wikicode(StringMixIn):
return False return False
return True 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. """Return the index of *obj* in the list of nodes.


Raises :exc:`ValueError` if *obj* is not found. If *recursive* is Raises :exc:`ValueError` if *obj* is not found. If *recursive* is
@@ -381,7 +384,7 @@ class Wikicode(StringMixIn):
return i return i
raise ValueError(obj) 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*. """Return a list of all ancestor nodes of the :class:`.Node` *obj*.


The list is ordered from the most shallow ancestor (greatest great- 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. 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: for node in code.nodes:
if node is needle: if node is needle:
return [] return []
@@ -418,7 +421,7 @@ class Wikicode(StringMixIn):
raise ValueError(obj) raise ValueError(obj)
return ancestors 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*. """Return the direct parent node of the :class:`.Node` *obj*.


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


def insert_before( 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: ) -> None:
"""Insert *value* immediately before *obj*. """Insert *value* immediately before *obj*.


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


def insert_after( 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: ) -> None:
"""Insert *value* immediately after *obj*. """Insert *value* immediately after *obj*.


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


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


@@ -526,7 +529,7 @@ class Wikicode(StringMixIn):
for node in nodes: for node in nodes:
self.nodes.append(node) 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. """Remove *obj* from the list of nodes.


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


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


@@ -583,10 +584,10 @@ class Wikicode(StringMixIn):
def ifilter( def ifilter(
self, self,
recursive: bool = True, 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, 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. """Iterate over nodes in our list matching certain conditions.


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


def get_sections( def get_sections(
self, 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, flags: int = FLAGS,
flat: bool = False, flat: bool = False,
include_lead: Optional[bool] = None,
include_lead: bool | None = None,
include_headings: bool = True, include_headings: bool = True,
) -> list["Wikicode"]:
) -> list[Wikicode]:
"""Return a list of sections within the page. """Return a list of sections within the page.


Sections are returned as :class:`.Wikicode` objects with a shared node 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 # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # 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 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE. # SOFTWARE.


from __future__ import annotations

from mwparserfromhell.nodes import ( from mwparserfromhell.nodes import (
Argument, Argument,
Comment, 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 # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # of this software and associated documentation files (the "Software"), to deal
@@ -22,9 +22,12 @@
Test cases for the Argument node. Test cases for the Argument node.
""" """


from __future__ import annotations

import pytest import pytest


from mwparserfromhell.nodes import Argument, Text from mwparserfromhell.nodes import Argument, Text

from .conftest import assert_wikicode_equal, wrap, wraptext 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 # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # of this software and associated documentation files (the "Software"), to deal
@@ -22,10 +22,13 @@
Test cases for the Attribute node extra. Test cases for the Attribute node extra.
""" """


from __future__ import annotations

import pytest import pytest


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

from .conftest import assert_wikicode_equal, wrap, wraptext 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 # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # 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. Tests for the builder, which turns tokens into Wikicode objects.
""" """


from __future__ import annotations

import pytest import pytest


from mwparserfromhell.nodes import ( from mwparserfromhell.nodes import (
@@ -36,8 +38,9 @@ from mwparserfromhell.nodes import (
Wikilink, Wikilink,
) )
from mwparserfromhell.nodes.extras import Attribute, Parameter 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 mwparserfromhell.parser.builder import Builder

from .conftest import assert_wikicode_equal, wrap, wraptext 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 # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # of this software and associated documentation files (the "Software"), to deal
@@ -22,6 +22,8 @@
Test cases for the Comment node. Test cases for the Comment node.
""" """


from __future__ import annotations

import pytest import pytest


from mwparserfromhell.nodes import Comment 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 # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # of this software and associated documentation files (the "Software"), to deal
@@ -22,9 +22,11 @@
Integration test cases for mwparserfromhell's documentation. Integration test cases for mwparserfromhell's documentation.
""" """


from __future__ import annotations

import json import json
from io import StringIO
import os import os
from io import StringIO
from urllib.parse import urlencode from urllib.parse import urlencode
from urllib.request import urlopen 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 # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # of this software and associated documentation files (the "Software"), to deal
@@ -22,9 +22,12 @@
Test cases for the ExternalLink node. Test cases for the ExternalLink node.
""" """


from __future__ import annotations

import pytest import pytest


from mwparserfromhell.nodes import ExternalLink, Text from mwparserfromhell.nodes import ExternalLink, Text

from .conftest import assert_wikicode_equal, wrap, wraptext 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 # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # of this software and associated documentation files (the "Software"), to deal
@@ -22,9 +22,12 @@
Test cases for the Heading node. Test cases for the Heading node.
""" """


from __future__ import annotations

import pytest import pytest


from mwparserfromhell.nodes import Heading, Text from mwparserfromhell.nodes import Heading, Text

from .conftest import assert_wikicode_equal, wrap, wraptext 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 # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # of this software and associated documentation files (the "Software"), to deal
@@ -22,6 +22,8 @@
Test cases for the HTMLEntity node. Test cases for the HTMLEntity node.
""" """


from __future__ import annotations

import pytest import pytest


from mwparserfromhell.nodes import HTMLEntity 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 # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # of this software and associated documentation files (the "Software"), to deal
@@ -22,9 +22,12 @@
Test cases for the Parameter node extra. Test cases for the Parameter node extra.
""" """


from __future__ import annotations

import pytest import pytest


from mwparserfromhell.nodes.extras import Parameter from mwparserfromhell.nodes.extras import Parameter

from .conftest import assert_wikicode_equal, wraptext 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 # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # 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. Tests for the Parser class itself, which tokenizes and builds nodes.
""" """


from __future__ import annotations

import pytest import pytest


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

from .conftest import assert_wikicode_equal, wrap, wraptext 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 # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # 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. Test cases for the SmartList class and its child, ListProxy.
""" """


from __future__ import annotations

import pickle import pickle


import pytest 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 # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # of this software and associated documentation files (the "Software"), to deal
@@ -22,6 +22,8 @@
Test cases for the StringMixIn class. Test cases for the StringMixIn class.
""" """


from __future__ import annotations

import sys import sys
from types import GeneratorType 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 # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # of this software and associated documentation files (the "Software"), to deal
@@ -22,10 +22,13 @@
Test cases for the Tag node. Test cases for the Tag node.
""" """


from __future__ import annotations

import pytest import pytest


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

from .conftest import assert_wikicode_equal, wrap, wraptext 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 # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # of this software and associated documentation files (the "Software"), to deal
@@ -22,13 +22,16 @@
Test cases for the Template node. Test cases for the Template node.
""" """


from __future__ import annotations

from difflib import unified_diff from difflib import unified_diff


import pytest import pytest


from mwparserfromhell import parse
from mwparserfromhell.nodes import HTMLEntity, Template, Text from mwparserfromhell.nodes import HTMLEntity, Template, Text
from mwparserfromhell.nodes.extras import Parameter from mwparserfromhell.nodes.extras import Parameter
from mwparserfromhell import parse
from .conftest import assert_wikicode_equal, wrap, wraptext 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 # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # of this software and associated documentation files (the "Software"), to deal
@@ -22,6 +22,8 @@
Test cases for the Text node. Test cases for the Text node.
""" """


from __future__ import annotations

import pytest import pytest


from mwparserfromhell.nodes import Text 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 # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # 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 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE. # SOFTWARE.


from __future__ import annotations

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


import pytest 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 # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # of this software and associated documentation files (the "Software"), to deal
@@ -22,6 +22,8 @@
Test cases for the Token class and its subclasses. Test cases for the Token class and its subclasses.
""" """


from __future__ import annotations

import pytest import pytest


from mwparserfromhell.parser import tokens 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 # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # of this software and associated documentation files (the "Software"), to deal
@@ -22,10 +22,13 @@
Tests for the utils module, which provides parse_anything(). Tests for the utils module, which provides parse_anything().
""" """


from __future__ import annotations

import pytest import pytest


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

from .conftest import assert_wikicode_equal, wrap, wraptext 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 # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # 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. Tests for the Wikicode class, which manages a list of nodes.
""" """


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


import pytest import pytest


from mwparserfromhell import parse
from mwparserfromhell.nodes import Argument, Heading, Template, Text from mwparserfromhell.nodes import Argument, Heading, Template, Text
from mwparserfromhell.smart_list import SmartList from mwparserfromhell.smart_list import SmartList
from mwparserfromhell.wikicode import Wikicode from mwparserfromhell.wikicode import Wikicode
from mwparserfromhell import parse
from .conftest import wrap, wraptext 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 # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # of this software and associated documentation files (the "Software"), to deal
@@ -22,9 +22,12 @@
Test cases for the Wikilink node. Test cases for the Wikilink node.
""" """


from __future__ import annotations

import pytest import pytest


from mwparserfromhell.nodes import Text, Wikilink from mwparserfromhell.nodes import Text, Wikilink

from .conftest import assert_wikicode_equal, wrap, wraptext from .conftest import assert_wikicode_equal, wrap, wraptext






Загрузка…
Отмена
Сохранить