From 907ee6de6a2ac6a2b5ada992ca89efa164459417 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Klinkovsk=C3=BD?= <1289205+lahwaacz@users.noreply.github.com> Date: Sun, 11 May 2025 16:58:19 +0200 Subject: [PATCH] Add type hints: mwparserfromhell.nodes.template --- src/mwparserfromhell/nodes/template.py | 93 +++++++++++++++++++++------------- 1 file changed, 59 insertions(+), 34 deletions(-) diff --git a/src/mwparserfromhell/nodes/template.py b/src/mwparserfromhell/nodes/template.py index b4c89a2..50f4193 100644 --- a/src/mwparserfromhell/nodes/template.py +++ b/src/mwparserfromhell/nodes/template.py @@ -19,6 +19,18 @@ # SOFTWARE. from collections import defaultdict import re +from typing import ( + TYPE_CHECKING, + Any, + Optional, + Union, + List, + Dict, + Callable, + Generator, + Mapping, + cast, +) from ._base import Node from .html_entity import HTMLEntity @@ -26,6 +38,9 @@ from .text import Text from .extras import Parameter from ..utils import parse_anything +if TYPE_CHECKING: + from ..wikicode import Wikicode + __all__ = ["Template"] FLAGS = re.DOTALL | re.UNICODE @@ -36,34 +51,36 @@ _UNSET = object() class Template(Node): """Represents a template in wikicode, like ``{{foo}}``.""" - def __init__(self, name, params=None): + def __init__(self, name: Any, params: Optional[List[Parameter]] = None): super().__init__() self.name = name - if params: - self._params = params - else: - self._params = [] + self._params: List[Parameter] = params or [] - def __str__(self): + def __str__(self) -> str: if self.params: params = "|".join([str(param) for param in self.params]) return "{{" + str(self.name) + "|" + params + "}}" return "{{" + str(self.name) + "}}" - def __children__(self): + def __children__(self) -> Generator["Wikicode"]: yield self.name for param in self.params: if param.showkey: yield param.name yield param.value - def __strip__(self, **kwargs): + def __strip__(self, **kwargs: Any) -> Optional[str]: if kwargs.get("keep_template_params"): parts = [param.value.strip_code(**kwargs) for param in self.params] return " ".join(part for part in parts if part) return None - def __showtree__(self, write, get, mark): + def __showtree__( + self, + write: Callable[[str], None], + get: Callable[["Wikicode"], None], + mark: Callable[[], None], + ) -> None: write("{{") get(self.name) for param in self.params: @@ -76,7 +93,7 @@ class Template(Node): write("}}") @staticmethod - def _surface_escape(code, char): + def _surface_escape(code: "Wikicode", char: str) -> None: """Return *code* with *char* escaped as an HTML entity. The main use of this is to escape pipes (``|``) or equal signs (``=``) @@ -89,7 +106,7 @@ class Template(Node): code.replace(node, node.replace(char, replacement), False) @staticmethod - def _select_theory(theories): + def _select_theory(theories: Dict[str, int]) -> Optional[str]: """Return the most likely spacing convention given different options. Given a dictionary of convention options as keys and their occurrence @@ -105,7 +122,7 @@ class Template(Node): return None @staticmethod - def _blank_param_value(value): + def _blank_param_value(value: "Wikicode") -> None: """Remove the content from *value* while keeping its whitespace. Replace *value*\\ 's nodes with two text nodes, the first containing @@ -120,15 +137,17 @@ class Template(Node): before, after = match.group(1), match.group(2) value.nodes = [Text(before), Text(after)] - def _get_spacing_conventions(self, use_names): + def _get_spacing_conventions( + self, use_names: bool + ) -> tuple[Optional[str], Optional[str]]: """Try to determine the whitespace conventions for parameters. This will examine the existing parameters and use :meth:`_select_theory` to determine if there are any preferred styles for how much whitespace to put before or after the value. """ - before_theories = defaultdict(lambda: 0) - after_theories = defaultdict(lambda: 0) + before_theories: defaultdict[str, int] = defaultdict(lambda: 0) + after_theories: defaultdict[str, int] = defaultdict(lambda: 0) for param in self.params: if not param.showkey: continue @@ -150,14 +169,14 @@ class Template(Node): after = self._select_theory(after_theories) return before, after - def _fix_dependendent_params(self, i): + def _fix_dependendent_params(self, i: int) -> None: """Unhide keys if necessary after removing the param at index *i*.""" if not self.params[i].showkey: for param in self.params[i + 1 :]: if not param.showkey: param.showkey = True - def _remove_exact(self, needle, keep_field): + def _remove_exact(self, needle: Parameter, keep_field: bool) -> None: """Remove a specific parameter, *needle*, from the template.""" for i, param in enumerate(self.params): if param is needle: @@ -169,7 +188,7 @@ class Template(Node): return raise ValueError(needle) - def _should_remove(self, i, name): + def _should_remove(self, i: int, name: str) -> bool: """Look ahead for a parameter with the same name, but hidden. If one exists, we should remove the given one rather than blanking it. @@ -183,20 +202,20 @@ class Template(Node): return False @property - def name(self): + def name(self) -> "Wikicode": """The name of the template, as a :class:`.Wikicode` object.""" return self._name + @name.setter + def name(self, value: Any) -> None: + self._name = parse_anything(value) + @property - def params(self): + def params(self) -> List[Parameter]: """The list of parameters contained within the template.""" return self._params - @name.setter - def name(self, value): - self._name = parse_anything(value) - - def has(self, name, ignore_empty=False): + def has(self, name: Union[str, Any], ignore_empty: bool = False) -> bool: """Return ``True`` if any parameter in the template is named *name*. With *ignore_empty*, ``False`` will be returned even if the template @@ -212,11 +231,11 @@ class Template(Node): return True return False - def has_param(self, name, ignore_empty=False): + def has_param(self, name: Union[str, Any], ignore_empty: bool = False) -> bool: """Alias for :meth:`has`.""" return self.has(name, ignore_empty) - def get(self, name, default=_UNSET): + def get(self, name: Union[str, Any], default: Any = _UNSET) -> Union[Parameter, Any]: """Get the parameter whose name is *name*. The returned object is a :class:`.Parameter` instance. Raises @@ -233,12 +252,18 @@ class Template(Node): raise ValueError(name) return default - def __getitem__(self, name): + def __getitem__(self, name: Union[str, Any]) -> Parameter: return self.get(name) def add( - self, name, value, showkey=None, before=None, after=None, preserve_spacing=True - ): + self, + name: Any, + value: Any, + showkey: Optional[bool] = None, + before: Optional[Union[Parameter, str]] = None, + after: Optional[Union[Parameter, str]] = None, + preserve_spacing: bool = True, + ) -> Parameter: """Add a parameter to the template with a given *name* and *value*. *name* and *value* can be anything parsable by @@ -329,7 +354,7 @@ class Template(Node): self.params.append(param) return param - def update(self, params, **kwargs): + def update(self, params: Mapping[Any, Any], **kwargs: Any) -> None: """Update the template with multiple parameters at once. Args: params: A dictionary mapping parameter names to values. @@ -340,10 +365,10 @@ class Template(Node): for name, value in params.items(): self.add(name, value, **kwargs) - def __setitem__(self, name, value): + def __setitem__(self, name: Any, value: Any) -> Parameter: return self.add(name, value) - def remove(self, param, keep_field=False): + def remove(self, param: Union[Parameter, str], keep_field: bool = False) -> None: """Remove a parameter from the template, identified by *param*. If *param* is a :class:`.Parameter` object, it will be matched exactly, @@ -390,5 +415,5 @@ class Template(Node): for i in reversed(to_remove): self.params.pop(i) - def __delitem__(self, param): + def __delitem__(self, param: Union[Parameter, str]) -> None: return self.remove(param)