from __future__ import annotations
from ._argument import Argument, SubWikiTextWithArgs
from ._comment_bold_italic import COMMENT_PATTERN
from ._wikitext import WS, rc
COMMENT_SUB = rc(COMMENT_PATTERN).sub
TL_NAME_ARGS_FULLMATCH = rc(rb'[^|}]*+(?#name)(?<arg>\|[^|]*+)*+').fullmatch
[docs]
class Template(SubWikiTextWithArgs):
"""Convert strings to Template objects.
The string should start with {{ and end with }}.
"""
__slots__ = ()
_name_args_matcher = TL_NAME_ARGS_FULLMATCH
_first_arg_sep = 124
[docs]
def normal_name(
self,
rm_namespaces=('Template',),
*,
code: str | None = None,
capitalize=False,
) -> str:
"""Return normal form of self.name.
- Remove comments.
- Remove language code.
- Remove namespace ("template:" or any of `localized_namespaces`.
- Use space instead of underscore.
- Remove consecutive spaces.
- Use uppercase for the first letter if `capitalize`.
- Remove #anchor.
:param rm_namespaces: is used to provide additional localized
namespaces for the template namespace. They will be removed from
the result. Default is ('Template',).
:param capitalize: If True, convert the first letter of the
template's name to a capital letter. See
[[mw:Manual:$wgCapitalLinks]] for more info.
:param code: is the language code.
Example:
>>> Template(
... '{{ eN : tEmPlAtE : <!-- c --> t_1 # b | a }}'
... ).normal_name(code='en')
'T 1'
"""
# Remove comments
name = COMMENT_SUB('', self.name).strip(WS)
# Remove code
if code:
head, sep, tail = name.partition(':')
if not head and sep:
name = tail.strip(' ')
head, sep, tail = name.partition(':')
if code.lower() == head.strip(' ').lower():
name = tail.strip(' ')
# Remove namespace
head, sep, tail = name.partition(':')
if not head and sep:
name = tail.strip(' ')
head, sep, tail = name.partition(':')
if head:
ns = head.strip(' ').lower()
for namespace in rm_namespaces:
if namespace.lower() == ns:
name = tail.strip(' ')
break
# Use space instead of underscore
name = name.replace('_', ' ')
if capitalize:
# Use uppercase for the first letter
name = name[:1].upper() + name[1:]
# Remove #anchor
name, sep, tail = name.partition('#')
return ' '.join(name.split())
[docs]
def get_arg(self, name: str) -> Argument | None:
"""Return the last argument with the given name.
Return None if no argument with that name is found.
"""
return super()._get_arg(name, ignore_equals=False)
[docs]
def has_arg(self, name: str, value: str | None = None) -> bool:
"""Return true if there is an arg named `name`.
Also check equality of values if `value` is provided.
Note: If you just need to get an argument and you want to LBYL, it's
better to get_arg directly and then check if the returned value
is None.
"""
return super()._has_arg(name, value, ignore_equals=False)
[docs]
def set_arg(
self,
name: str | None,
value: str,
positional: bool | None = None,
before: str | None = None,
after: str | None = None,
preserve_spacing: bool = False,
) -> None:
"""Set the value for `name` argument. Add it if it doesn't exist.
- Use `positional`, `before` and `after` keyword arguments only when
adding a new argument.
- If `before` is given, ignore `after`.
- If neither `before` nor `after` are given and it's needed to add a
new argument, then append the new argument to the end.
- If `positional` is True, add the given value as a positional
argument and ignore `preserve_spacing`.
- If positional is None (the default), mirror the kind of the last
existing argument when the name is the next positional index
(positional if the last arg is positional, keyword otherwise).
"""
super()._set_arg(
name,
value,
positional,
before,
after,
preserve_spacing,
ignore_equals=False,
)
[docs]
def del_arg(self, name: str) -> None:
"""Delete all arguments with the given name."""
super()._del_arg(name, ignore_equals=False)
@property
def templates(self) -> list[Template]:
return super().templates[1:]