From 2553dbde19341100e1a1ce6f984a23cb2b3827aa Mon Sep 17 00:00:00 2001 From: facelessuser Date: Fri, 17 Feb 2023 06:57:35 -0700 Subject: [PATCH] Replace use of Optional in type annotations Since we use `from __future__ import annotations` type annotations will be ignored as strings. We can use `type | None` even on older Python versions. --- soupsieve/__init__.py | 30 +++++++++++------------ soupsieve/css_match.py | 54 ++++++++++++++++++++--------------------- soupsieve/css_parser.py | 20 +++++++-------- soupsieve/css_types.py | 24 +++++++++--------- soupsieve/util.py | 6 ++--- 5 files changed, 67 insertions(+), 67 deletions(-) diff --git a/soupsieve/__init__.py b/soupsieve/__init__.py index 591a4f4..0640363 100644 --- a/soupsieve/__init__.py +++ b/soupsieve/__init__.py @@ -32,7 +32,7 @@ from . import css_types as ct from .util import DEBUG, SelectorSyntaxError # noqa: F401 import bs4 # type: ignore[import] -from typing import Optional, Any, Iterator, Iterable +from typing import Any, Iterator, Iterable __all__ = ( 'DEBUG', 'SelectorSyntaxError', 'SoupSieve', @@ -45,10 +45,10 @@ def compile( # noqa: A001 pattern: str, - namespaces: Optional[dict[str, str]] = None, + namespaces: dict[str, str] | None = None, flags: int = 0, *, - custom: Optional[dict[str, str]] = None, + custom: dict[str, str] | None = None, **kwargs: Any ) -> cm.SoupSieve: """Compile CSS pattern.""" @@ -79,10 +79,10 @@ def purge() -> None: def closest( select: str, tag: 'bs4.Tag', - namespaces: Optional[dict[str, str]] = None, + namespaces: dict[str, str] | None = None, flags: int = 0, *, - custom: Optional[dict[str, str]] = None, + custom: dict[str, str] | None = None, **kwargs: Any ) -> 'bs4.Tag': """Match closest ancestor.""" @@ -93,10 +93,10 @@ def closest( def match( select: str, tag: 'bs4.Tag', - namespaces: Optional[dict[str, str]] = None, + namespaces: dict[str, str] | None = None, flags: int = 0, *, - custom: Optional[dict[str, str]] = None, + custom: dict[str, str] | None = None, **kwargs: Any ) -> bool: """Match node.""" @@ -107,10 +107,10 @@ def match( def filter( # noqa: A001 select: str, iterable: Iterable['bs4.Tag'], - namespaces: Optional[dict[str, str]] = None, + namespaces: dict[str, str] | None = None, flags: int = 0, *, - custom: Optional[dict[str, str]] = None, + custom: dict[str, str] | None = None, **kwargs: Any ) -> list['bs4.Tag']: """Filter list of nodes.""" @@ -121,10 +121,10 @@ def filter( # noqa: A001 def select_one( select: str, tag: 'bs4.Tag', - namespaces: Optional[dict[str, str]] = None, + namespaces: dict[str, str] | None = None, flags: int = 0, *, - custom: Optional[dict[str, str]] = None, + custom: dict[str, str] | None = None, **kwargs: Any ) -> 'bs4.Tag': """Select a single tag.""" @@ -135,11 +135,11 @@ def select_one( def select( select: str, tag: 'bs4.Tag', - namespaces: Optional[dict[str, str]] = None, + namespaces: dict[str, str] | None = None, limit: int = 0, flags: int = 0, *, - custom: Optional[dict[str, str]] = None, + custom: dict[str, str] | None = None, **kwargs: Any ) -> list['bs4.Tag']: """Select the specified tags.""" @@ -150,11 +150,11 @@ def select( def iselect( select: str, tag: 'bs4.Tag', - namespaces: Optional[dict[str, str]] = None, + namespaces: dict[str, str] | None = None, limit: int = 0, flags: int = 0, *, - custom: Optional[dict[str, str]] = None, + custom: dict[str, str] | None = None, **kwargs: Any ) -> Iterator['bs4.Tag']: """Iterate the specified tags.""" diff --git a/soupsieve/css_match.py b/soupsieve/css_match.py index 6575282..168e4be 100644 --- a/soupsieve/css_match.py +++ b/soupsieve/css_match.py @@ -6,7 +6,7 @@ from . import css_types as ct import unicodedata import bs4 # type: ignore[import] -from typing import Iterator, Iterable, Any, Optional, Callable, Sequence, cast # noqa: F401 +from typing import Iterator, Iterable, Any, Callable, Sequence, cast # noqa: F401 # Empty tag pattern (whitespace okay) RE_NOT_EMPTY = re.compile('[^ \t\r\n\f]') @@ -171,7 +171,7 @@ def get_contents(self, el: bs4.Tag, no_iframe: bool = False) -> Iterator[bs4.Pag def get_children( self, el: bs4.Tag, - start: Optional[int] = None, + start: int | None = None, reverse: bool = False, tags: bool = True, no_iframe: bool = False @@ -239,22 +239,22 @@ def get_parent(self, el: bs4.Tag, no_iframe: bool = False) -> bs4.Tag: return parent @staticmethod - def get_tag_name(el: bs4.Tag) -> Optional[str]: + def get_tag_name(el: bs4.Tag) -> str | None: """Get tag.""" - return cast(Optional[str], el.name) + return cast('str | None', el.name) @staticmethod - def get_prefix_name(el: bs4.Tag) -> Optional[str]: + def get_prefix_name(el: bs4.Tag) -> str | None: """Get prefix.""" - return cast(Optional[str], el.prefix) + return cast('str | None', el.prefix) @staticmethod - def get_uri(el: bs4.Tag) -> Optional[str]: + def get_uri(el: bs4.Tag) -> str | None: """Get namespace `URI`.""" - return cast(Optional[str], el.namespace) + return cast('str | None', el.namespace) @classmethod def get_next(cls, el: bs4.Tag, tags: bool = True) -> bs4.PageElement: @@ -287,7 +287,7 @@ def has_html_ns(el: bs4.Tag) -> bool: return bool(ns and ns == NS_XHTML) @staticmethod - def split_namespace(el: bs4.Tag, attr_name: str) -> tuple[Optional[str], Optional[str]]: + def split_namespace(el: bs4.Tag, attr_name: str) -> tuple[str | None, str | None]: """Return namespace and attribute name without the prefix.""" return getattr(attr_name, 'namespace', None), getattr(attr_name, 'name', None) @@ -330,8 +330,8 @@ def get_attribute_by_name( cls, el: bs4.Tag, name: str, - default: Optional[str | Sequence[str]] = None - ) -> Optional[str | Sequence[str]]: + default: str | Sequence[str] | None = None + ) -> str | Sequence[str] | None: """Get attribute by name.""" value = default @@ -348,7 +348,7 @@ def get_attribute_by_name( return value @classmethod - def iter_attributes(cls, el: bs4.Tag) -> Iterator[tuple[str, Optional[str | Sequence[str]]]]: + def iter_attributes(cls, el: bs4.Tag) -> Iterator[tuple[str, str | Sequence[str] | None]]: """Iterate attributes.""" for k, v in el.attrs.items(): @@ -424,10 +424,10 @@ def validate_minutes(minutes: int) -> bool: return 0 <= minutes <= 59 @classmethod - def parse_value(cls, itype: str, value: Optional[str]) -> Optional[tuple[float, ...]]: + def parse_value(cls, itype: str, value: str | None) -> tuple[float, ...] | None: """Parse the input value.""" - parsed = None # type: Optional[tuple[float, ...]] + parsed = None # type: tuple[float, ...] | None if value is None: return value if itype == "date": @@ -486,7 +486,7 @@ def __init__( self, selectors: ct.SelectorList, scope: bs4.Tag, - namespaces: Optional[ct.Namespaces], + namespaces: ct.Namespaces | None, flags: int ) -> None: """Initialize.""" @@ -545,19 +545,19 @@ def is_html_tag(self, el: bs4.Tag) -> bool: return self.get_tag_ns(el) == NS_XHTML - def get_tag(self, el: bs4.Tag) -> Optional[str]: + def get_tag(self, el: bs4.Tag) -> str | None: """Get tag.""" name = self.get_tag_name(el) return util.lower(name) if name is not None and not self.is_xml else name - def get_prefix(self, el: bs4.Tag) -> Optional[str]: + def get_prefix(self, el: bs4.Tag) -> str | None: """Get prefix.""" prefix = self.get_prefix_name(el) return util.lower(prefix) if prefix is not None and not self.is_xml else prefix - def find_bidi(self, el: bs4.Tag) -> Optional[int]: + def find_bidi(self, el: bs4.Tag) -> int | None: """Get directionality from element text.""" for node in self.get_children(el, tags=False): @@ -653,8 +653,8 @@ def match_attribute_name( self, el: bs4.Tag, attr: str, - prefix: Optional[str] - ) -> Optional[str | Sequence[str]]: + prefix: str | None + ) -> str | Sequence[str] | None: """Match attribute name and return value if it exists.""" value = None @@ -751,7 +751,7 @@ def match_tagname(self, el: bs4.Tag, tag: ct.SelectorTag) -> bool: name not in (self.get_tag(el), '*') ) - def match_tag(self, el: bs4.Tag, tag: Optional[ct.SelectorTag]) -> bool: + def match_tag(self, el: bs4.Tag, tag: ct.SelectorTag | None) -> bool: """Match the tag.""" match = True @@ -1030,7 +1030,7 @@ def match_contains(self, el: bs4.Tag, contains: tuple[ct.SelectorContains, ...]) """Match element if it contains text.""" match = True - content = None # type: Optional[str | Sequence[str]] + content = None # type: str | Sequence[str] | None for contain_list in contains: if content is None: if contain_list.own: @@ -1099,7 +1099,7 @@ def match_indeterminate(self, el: bs4.Tag) -> bool: match = False name = cast(str, self.get_attribute_by_name(el, 'name')) - def get_parent_form(el: bs4.Tag) -> Optional[bs4.Tag]: + def get_parent_form(el: bs4.Tag) -> bs4.Tag | None: """Find this input's form.""" form = None parent = self.get_parent(el, no_iframe=True) @@ -1478,7 +1478,7 @@ def select(self, limit: int = 0) -> Iterator[bs4.Tag]: if lim < 1: break - def closest(self) -> Optional[bs4.Tag]: + def closest(self) -> bs4.Tag | None: """Match closest ancestor.""" current = self.tag @@ -1506,7 +1506,7 @@ class SoupSieve(ct.Immutable): pattern: str selectors: ct.SelectorList - namespaces: Optional[ct.Namespaces] + namespaces: ct.Namespaces | None custom: dict[str, str] flags: int @@ -1516,8 +1516,8 @@ def __init__( self, pattern: str, selectors: ct.SelectorList, - namespaces: Optional[ct.Namespaces], - custom: Optional[ct.CustomSelectors], + namespaces: ct.Namespaces | None, + custom: ct.CustomSelectors | None, flags: int ): """Initialize.""" diff --git a/soupsieve/css_parser.py b/soupsieve/css_parser.py index 4b8db18..3698f2e 100644 --- a/soupsieve/css_parser.py +++ b/soupsieve/css_parser.py @@ -7,7 +7,7 @@ from . import css_types as ct from .util import SelectorSyntaxError import warnings -from typing import Optional, Match, Any, Iterator, cast +from typing import Match, Any, Iterator, cast UNICODE_REPLACEMENT_CHAR = 0xFFFD @@ -207,8 +207,8 @@ @lru_cache(maxsize=_MAXCACHE) def _cached_css_compile( pattern: str, - namespaces: Optional[ct.Namespaces], - custom: Optional[ct.CustomSelectors], + namespaces: ct.Namespaces | None, + custom: ct.CustomSelectors | None, flags: int ) -> cm.SoupSieve: """Cached CSS compile.""" @@ -233,7 +233,7 @@ def _purge_cache() -> None: _cached_css_compile.cache_clear() -def process_custom(custom: Optional[ct.CustomSelectors]) -> dict[str, str | ct.SelectorList]: +def process_custom(custom: ct.CustomSelectors | None) -> dict[str, str | ct.SelectorList]: """Process custom.""" custom_selectors = {} @@ -317,7 +317,7 @@ def get_name(self) -> str: return self.name - def match(self, selector: str, index: int, flags: int) -> Optional[Match[str]]: + def match(self, selector: str, index: int, flags: int) -> Match[str] | None: """Match the selector.""" return self.re_pattern.match(selector, index) @@ -336,7 +336,7 @@ def __init__(self, patterns: tuple[tuple[str, tuple[str, ...], str, type[Selecto for pseudo in p[1]: self.patterns[pseudo] = pattern - self.matched_name = None # type: Optional[SelectorPattern] + self.matched_name = None # type: SelectorPattern | None self.re_pseudo_name = re.compile(PAT_PSEUDO_CLASS_SPECIAL, re.I | re.X | re.U) def get_name(self) -> str: @@ -344,7 +344,7 @@ def get_name(self) -> str: return '' if self.matched_name is None else self.matched_name.get_name() - def match(self, selector: str, index: int, flags: int) -> Optional[Match[str]]: + def match(self, selector: str, index: int, flags: int) -> Match[str] | None: """Match the selector.""" pseudo = None @@ -372,14 +372,14 @@ class _Selector: def __init__(self, **kwargs: Any) -> None: """Initialize.""" - self.tag = kwargs.get('tag', None) # type: Optional[ct.SelectorTag] + self.tag = kwargs.get('tag', None) # type: ct.SelectorTag | None self.ids = kwargs.get('ids', []) # type: list[str] self.classes = kwargs.get('classes', []) # type: list[str] self.attributes = kwargs.get('attributes', []) # type: list[ct.SelectorAttribute] self.nth = kwargs.get('nth', []) # type: list[ct.SelectorNth] self.selectors = kwargs.get('selectors', []) # type: list[ct.SelectorList] self.relations = kwargs.get('relations', []) # type: list[_Selector] - self.rel_type = kwargs.get('rel_type', None) # type: Optional[str] + self.rel_type = kwargs.get('rel_type', None) # type: str | None self.contains = kwargs.get('contains', []) # type: list[ct.SelectorContains] self.lang = kwargs.get('lang', []) # type: list[ct.SelectorLang] self.flags = kwargs.get('flags', 0) # type: int @@ -462,7 +462,7 @@ class CSSParser: def __init__( self, selector: str, - custom: Optional[dict[str, str | ct.SelectorList]] = None, + custom: dict[str, str | ct.SelectorList] | None = None, flags: int = 0 ) -> None: """Initialize.""" diff --git a/soupsieve/css_types.py b/soupsieve/css_types.py index a97d5f4..c263bb0 100644 --- a/soupsieve/css_types.py +++ b/soupsieve/css_types.py @@ -2,7 +2,7 @@ from __future__ import annotations import copyreg from .pretty import pretty -from typing import Any, Iterator, Hashable, Optional, Pattern, Iterable, Mapping +from typing import Any, Iterator, Hashable, Pattern, Iterable, Mapping __all__ = ( 'Selector', @@ -189,28 +189,28 @@ class Selector(Immutable): 'relation', 'rel_type', 'contains', 'lang', 'flags', '_hash' ) - tag: Optional[SelectorTag] + tag: SelectorTag | None ids: tuple[str, ...] classes: tuple[str, ...] attributes: tuple[SelectorAttribute, ...] nth: tuple[SelectorNth, ...] selectors: tuple[SelectorList, ...] relation: SelectorList - rel_type: Optional[str] + rel_type: str | None contains: tuple[SelectorContains, ...] lang: tuple[SelectorLang, ...] flags: int def __init__( self, - tag: Optional[SelectorTag], + tag: SelectorTag | None, ids: tuple[str, ...], classes: tuple[str, ...], attributes: tuple[SelectorAttribute, ...], nth: tuple[SelectorNth, ...], selectors: tuple[SelectorList, ...], relation: SelectorList, - rel_type: Optional[str], + rel_type: str | None, contains: tuple[SelectorContains, ...], lang: tuple[SelectorLang, ...], flags: int @@ -247,9 +247,9 @@ class SelectorTag(Immutable): __slots__ = ("name", "prefix", "_hash") name: str - prefix: Optional[str] + prefix: str | None - def __init__(self, name: str, prefix: Optional[str]) -> None: + def __init__(self, name: str, prefix: str | None) -> None: """Initialize.""" super().__init__(name=name, prefix=prefix) @@ -262,15 +262,15 @@ class SelectorAttribute(Immutable): attribute: str prefix: str - pattern: Optional[Pattern[str]] - xml_type_pattern: Optional[Pattern[str]] + pattern: Pattern[str] | None + xml_type_pattern: Pattern[str] | None def __init__( self, attribute: str, prefix: str, - pattern: Optional[Pattern[str]], - xml_type_pattern: Optional[Pattern[str]] + pattern: Pattern[str] | None, + xml_type_pattern: Pattern[str] | None ) -> None: """Initialize.""" @@ -360,7 +360,7 @@ class SelectorList(Immutable): def __init__( self, - selectors: Optional[Iterable[Selector | SelectorNull]] = None, + selectors: Iterable[Selector | SelectorNull] | None = None, is_not: bool = False, is_html: bool = False ) -> None: diff --git a/soupsieve/util.py b/soupsieve/util.py index cf4dc5c..9f91233 100644 --- a/soupsieve/util.py +++ b/soupsieve/util.py @@ -3,7 +3,7 @@ from functools import wraps, lru_cache import warnings import re -from typing import Callable, Any, Optional +from typing import Callable, Any DEBUG = 0x00001 @@ -27,7 +27,7 @@ def lower(string: str) -> str: class SelectorSyntaxError(Exception): """Syntax error in a CSS selector.""" - def __init__(self, msg: str, pattern: Optional[str] = None, index: Optional[int] = None) -> None: + def __init__(self, msg: str, pattern: str | None = None, index: int | None = None) -> None: """Initialize.""" self.line = None @@ -84,7 +84,7 @@ def get_pattern_context(pattern: str, index: int) -> tuple[str, int, int]: col = 1 text = [] # type: list[str] line = 1 - offset = None # type: Optional[int] + offset = None # type: int | None # Split pattern by newline and handle the text before the newline for m in RE_PATTERN_LINE_SPLIT.finditer(pattern):