| @@ -19,11 +19,16 @@ | |||||
| # SOFTWARE. | # SOFTWARE. | ||||
| from typing import TYPE_CHECKING, Any, Callable, Optional, List, Generator | |||||
| from ._base import Node | from ._base import Node | ||||
| from .extras import Attribute | from .extras import Attribute | ||||
| from ..definitions import is_visible | from ..definitions import is_visible | ||||
| from ..utils import parse_anything | from ..utils import parse_anything | ||||
| if TYPE_CHECKING: | |||||
| from ..wikicode import Wikicode | |||||
| __all__ = ["Tag"] | __all__ = ["Tag"] | ||||
| @@ -32,19 +37,22 @@ class Tag(Node): | |||||
| def __init__( | def __init__( | ||||
| self, | self, | ||||
| tag, | |||||
| contents=None, | |||||
| attrs=None, | |||||
| wiki_markup=None, | |||||
| self_closing=False, | |||||
| invalid=False, | |||||
| implicit=False, | |||||
| padding="", | |||||
| closing_tag=None, | |||||
| wiki_style_separator=None, | |||||
| closing_wiki_markup=None, | |||||
| tag: Any, | |||||
| contents: Any = None, | |||||
| attrs: Optional[List[Attribute]] = None, | |||||
| wiki_markup: Optional[str] = None, | |||||
| self_closing: bool = False, | |||||
| invalid: bool = False, | |||||
| implicit: bool = False, | |||||
| padding: str = "", | |||||
| closing_tag: Optional["Wikicode"] = None, | |||||
| wiki_style_separator: Optional[str] = None, | |||||
| closing_wiki_markup: Optional[str] = None, | |||||
| ): | ): | ||||
| super().__init__() | super().__init__() | ||||
| self._attrs: List[Attribute] | |||||
| self._closing_wiki_markup: Optional[str] | |||||
| self.tag = tag | self.tag = tag | ||||
| self.contents = contents | self.contents = contents | ||||
| self._attrs = attrs if attrs else [] | self._attrs = attrs if attrs else [] | ||||
| @@ -60,7 +68,7 @@ class Tag(Node): | |||||
| if closing_wiki_markup is not None: | if closing_wiki_markup is not None: | ||||
| self.closing_wiki_markup = closing_wiki_markup | self.closing_wiki_markup = closing_wiki_markup | ||||
| def __str__(self): | |||||
| def __str__(self) -> str: | |||||
| if self.wiki_markup: | if self.wiki_markup: | ||||
| if self.attributes: | if self.attributes: | ||||
| attrs = "".join([str(attr) for attr in self.attributes]) | attrs = "".join([str(attr) for attr in self.attributes]) | ||||
| @@ -90,7 +98,7 @@ class Tag(Node): | |||||
| result += "</" + str(self.closing_tag) + ">" | result += "</" + str(self.closing_tag) + ">" | ||||
| return result | return result | ||||
| def __children__(self): | |||||
| def __children__(self) -> Generator["Wikicode"]: | |||||
| 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: | ||||
| @@ -102,12 +110,17 @@ 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): | |||||
| if self.contents and is_visible(self.tag): | |||||
| def __strip__(self, **kwargs: Any) -> Optional[str]: | |||||
| 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 | ||||
| def __showtree__(self, write, get, mark): | |||||
| def __showtree__( | |||||
| self, | |||||
| write: Callable[[str], None], | |||||
| get: Callable[["Wikicode"], None], | |||||
| mark: Callable[[], None], | |||||
| ) -> None: | |||||
| write("</" if self.invalid else "<") | write("</" if self.invalid else "<") | ||||
| get(self.tag) | get(self.tag) | ||||
| for attr in self.attributes: | for attr in self.attributes: | ||||
| @@ -127,17 +140,25 @@ class Tag(Node): | |||||
| write(">") | write(">") | ||||
| @property | @property | ||||
| def tag(self): | |||||
| 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 | ||||
| @tag.setter | |||||
| def tag(self, value: Any) -> None: | |||||
| self._tag = self._closing_tag = parse_anything(value) | |||||
| @property | @property | ||||
| def contents(self): | |||||
| 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 | ||||
| @contents.setter | |||||
| def contents(self, value: Any) -> None: | |||||
| self._contents = parse_anything(value) | |||||
| @property | @property | ||||
| def attributes(self): | |||||
| 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`. | ||||
| @@ -145,7 +166,7 @@ class Tag(Node): | |||||
| return self._attrs | return self._attrs | ||||
| @property | @property | ||||
| def wiki_markup(self): | |||||
| def wiki_markup(self) -> Optional[str]: | |||||
| """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. | ||||
| @@ -154,13 +175,23 @@ class Tag(Node): | |||||
| """ | """ | ||||
| return self._wiki_markup | return self._wiki_markup | ||||
| @wiki_markup.setter | |||||
| def wiki_markup(self, value: Optional[str]) -> None: | |||||
| self._wiki_markup = str(value) if value else None | |||||
| if not value or not self.closing_wiki_markup: | |||||
| self._closing_wiki_markup = self._wiki_markup | |||||
| @property | @property | ||||
| def self_closing(self): | |||||
| def self_closing(self) -> bool: | |||||
| """Whether the tag is self-closing with no content (like ``<br/>``).""" | """Whether the tag is self-closing with no content (like ``<br/>``).""" | ||||
| return self._self_closing | return self._self_closing | ||||
| @self_closing.setter | |||||
| def self_closing(self, value: bool) -> None: | |||||
| self._self_closing = bool(value) | |||||
| @property | @property | ||||
| def invalid(self): | |||||
| def invalid(self) -> bool: | |||||
| """Whether the tag starts with a backslash after the opening bracket. | """Whether the tag starts with a backslash after the opening bracket. | ||||
| This makes the tag look like a lone close tag. It is technically | This makes the tag look like a lone close tag. It is technically | ||||
| @@ -170,8 +201,12 @@ class Tag(Node): | |||||
| """ | """ | ||||
| return self._invalid | return self._invalid | ||||
| @invalid.setter | |||||
| def invalid(self, value: bool) -> None: | |||||
| self._invalid = bool(value) | |||||
| @property | @property | ||||
| def implicit(self): | |||||
| def implicit(self) -> bool: | |||||
| """Whether the tag is implicitly self-closing, with no ending slash. | """Whether the tag is implicitly self-closing, with no ending slash. | ||||
| This is only possible for specific "single" tags like ``<br>`` and | This is only possible for specific "single" tags like ``<br>`` and | ||||
| @@ -180,13 +215,27 @@ class Tag(Node): | |||||
| """ | """ | ||||
| return self._implicit | return self._implicit | ||||
| @implicit.setter | |||||
| def implicit(self, value: bool) -> None: | |||||
| self._implicit = bool(value) | |||||
| @property | @property | ||||
| def padding(self): | |||||
| def padding(self) -> str: | |||||
| """Spacing to insert before the first closing ``>``.""" | """Spacing to insert before the first closing ``>``.""" | ||||
| return self._padding | return self._padding | ||||
| @padding.setter | |||||
| def padding(self, value: str) -> None: | |||||
| if not value: | |||||
| self._padding = "" | |||||
| else: | |||||
| value = str(value) | |||||
| if not value.isspace(): | |||||
| raise ValueError("padding must be entirely whitespace") | |||||
| self._padding = value | |||||
| @property | @property | ||||
| def closing_tag(self): | |||||
| 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 | ||||
| @@ -194,76 +243,40 @@ class Tag(Node): | |||||
| """ | """ | ||||
| return self._closing_tag | return self._closing_tag | ||||
| @closing_tag.setter | |||||
| def closing_tag(self, value: Any) -> None: | |||||
| self._closing_tag = parse_anything(value) | |||||
| @property | @property | ||||
| def wiki_style_separator(self): | |||||
| def wiki_style_separator(self) -> Optional[str]: | |||||
| """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. | ||||
| """ | """ | ||||
| return self._wiki_style_separator | return self._wiki_style_separator | ||||
| @wiki_style_separator.setter | |||||
| def wiki_style_separator(self, value: Optional[str]) -> None: | |||||
| self._wiki_style_separator = str(value) if value else None | |||||
| @property | @property | ||||
| def closing_wiki_markup(self): | |||||
| def closing_wiki_markup(self) -> Optional[str]: | |||||
| """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 | ||||
| brackets. If tag is :attr:`self_closing` is ``True`` then this is not | |||||
| displayed. If :attr:`wiki_markup` is set and this has not been set, this | |||||
| is set to the value of :attr:`wiki_markup`. If this has been set and | |||||
| brackets. If the tag is :attr:`self_closing`, this is not displayed. | |||||
| If :attr:`wiki_markup` is set and this has not been set, this is set | |||||
| to the value of :attr:`wiki_markup`. If this has been set and | |||||
| :attr:`wiki_markup` is set to a ``False`` value, this is set to | :attr:`wiki_markup` is set to a ``False`` value, this is set to | ||||
| ``None``. | ``None``. | ||||
| """ | """ | ||||
| return self._closing_wiki_markup | return self._closing_wiki_markup | ||||
| @tag.setter | |||||
| def tag(self, value): | |||||
| self._tag = self._closing_tag = parse_anything(value) | |||||
| @contents.setter | |||||
| def contents(self, value): | |||||
| self._contents = parse_anything(value) | |||||
| @wiki_markup.setter | |||||
| def wiki_markup(self, value): | |||||
| self._wiki_markup = str(value) if value else None | |||||
| if not value or not self.closing_wiki_markup: | |||||
| self._closing_wiki_markup = self._wiki_markup | |||||
| @self_closing.setter | |||||
| def self_closing(self, value): | |||||
| self._self_closing = bool(value) | |||||
| @invalid.setter | |||||
| def invalid(self, value): | |||||
| self._invalid = bool(value) | |||||
| @implicit.setter | |||||
| def implicit(self, value): | |||||
| self._implicit = bool(value) | |||||
| @padding.setter | |||||
| def padding(self, value): | |||||
| if not value: | |||||
| self._padding = "" | |||||
| else: | |||||
| value = str(value) | |||||
| if not value.isspace(): | |||||
| raise ValueError("padding must be entirely whitespace") | |||||
| self._padding = value | |||||
| @closing_tag.setter | |||||
| def closing_tag(self, value): | |||||
| self._closing_tag = parse_anything(value) | |||||
| @wiki_style_separator.setter | |||||
| def wiki_style_separator(self, value): | |||||
| self._wiki_style_separator = str(value) if value else None | |||||
| @closing_wiki_markup.setter | @closing_wiki_markup.setter | ||||
| def closing_wiki_markup(self, value): | |||||
| def closing_wiki_markup(self, value: Optional[str]) -> 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): | |||||
| def has(self, name: str) -> bool: | |||||
| """Return whether any attribute in the tag has the given *name*. | """Return whether any attribute in the tag has the given *name*. | ||||
| Note that a tag may have multiple attributes with the same name, but | Note that a tag may have multiple attributes with the same name, but | ||||
| @@ -274,7 +287,7 @@ class Tag(Node): | |||||
| return True | return True | ||||
| return False | return False | ||||
| def get(self, name): | |||||
| def get(self, name: str) -> Attribute: | |||||
| """Get the attribute with the given *name*. | """Get the attribute with the given *name*. | ||||
| The returned object is a :class:`.Attribute` instance. Raises | The returned object is a :class:`.Attribute` instance. Raises | ||||
| @@ -289,13 +302,13 @@ class Tag(Node): | |||||
| def add( | def add( | ||||
| self, | self, | ||||
| name, | |||||
| value=None, | |||||
| quotes='"', | |||||
| pad_first=" ", | |||||
| pad_before_eq="", | |||||
| pad_after_eq="", | |||||
| ): | |||||
| name: Any, | |||||
| value: Any = None, | |||||
| quotes: Optional[str] = '"', | |||||
| pad_first: str = " ", | |||||
| pad_before_eq: str = "", | |||||
| pad_after_eq: str = "", | |||||
| ) -> Attribute: | |||||
| """Add an attribute with the given *name* and *value*. | """Add an attribute with the given *name* and *value*. | ||||
| *name* and *value* can be anything parsable by | *name* and *value* can be anything parsable by | ||||
| @@ -318,7 +331,7 @@ class Tag(Node): | |||||
| self.attributes.append(attr) | self.attributes.append(attr) | ||||
| return attr | return attr | ||||
| def remove(self, name): | |||||
| def remove(self, name: str) -> None: | |||||
| """Remove all attributes with the given *name*. | """Remove all attributes with the given *name*. | ||||
| Raises :exc:`ValueError` if none were found. | Raises :exc:`ValueError` if none were found. | ||||