diff --git a/.github/workflows/ci-tests.yml b/.github/workflows/ci-tests.yml index 148b2ef12..4b270dcb8 100644 --- a/.github/workflows/ci-tests.yml +++ b/.github/workflows/ci-tests.yml @@ -20,7 +20,7 @@ jobs: strategy: matrix: py-ver-major: [3] - py-ver-minor: [9, 10, 11, 12, 13, 14] + py-ver-minor: [10, 11, 12, 13, 14] fail-fast: false env: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 66ecb487c..98ef8c753 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,5 +1,5 @@ Style guide: -- Python 3.9+ compatible code +- Python 3.10+ compatible code - PEP-484 type hints - Prefer new style format strings https://pyformat.info/ - Use ``make format`` to format your code diff --git a/Makefile b/Makefile index 649f855f2..4d5ac5cf7 100644 --- a/Makefile +++ b/Makefile @@ -205,7 +205,7 @@ bandit: bandit --recursive --exclude schema_salad/tests/ schema_salad pyupgrade: $(filter-out schema_salad/metaschema.py,$(PYSOURCES)) - pyupgrade --exit-zero-even-if-changed --py39-plus $^ + pyupgrade --exit-zero-even-if-changed --py310-plus $^ auto-walrus $^ release-test: FORCE diff --git a/README.rst b/README.rst index 6b51da033..d0ba8f45e 100644 --- a/README.rst +++ b/README.rst @@ -23,7 +23,7 @@ generation, and transformation to RDF_. Salad provides a bridge between document and record oriented data modeling and the Semantic Web. -The Schema Salad library is Python 3.9+ only. +The Schema Salad library is Python 3.10+ only. Installation ------------ diff --git a/mypy-stubs/mistune/__init__.pyi b/mypy-stubs/mistune/__init__.pyi index e8799f3a3..c34bfb731 100644 --- a/mypy-stubs/mistune/__init__.pyi +++ b/mypy-stubs/mistune/__init__.pyi @@ -1,6 +1,5 @@ -from typing import Any, Dict, Iterable, List, Optional, Union - -from typing_extensions import Literal +from collections.abc import Iterable +from typing import Any, Literal, TypeAlias from .block_parser import BlockParser as BlockParser from .core import BaseRenderer as BaseRenderer @@ -32,13 +31,13 @@ __all__ = [ "markdown", ] -RendererRef = Union[Literal["html", "ast"], BaseRenderer] +RendererRef: TypeAlias = Literal["html", "ast"] | BaseRenderer def create_markdown( escape: bool = True, hard_wrap: bool = False, - renderer: Optional[RendererRef] = "html", - plugins: Optional[Iterable[PluginRef]] = None, + renderer: RendererRef | None = "html", + plugins: Iterable[PluginRef] | None = None, ) -> Markdown: ... html: Markdown @@ -46,6 +45,6 @@ html: Markdown def markdown( text: str, escape: bool = True, - renderer: Optional[RendererRef] = "html", - plugins: Optional[Iterable[Any]] = None, -) -> Union[str, List[Dict[str, Any]]]: ... + renderer: RendererRef | None = "html", + plugins: Iterable[Any] | None = None, +) -> str | list[dict[str, Any]]: ... diff --git a/mypy-stubs/mistune/block_parser.pyi b/mypy-stubs/mistune/block_parser.pyi index 63a839fd5..12c889c6d 100644 --- a/mypy-stubs/mistune/block_parser.pyi +++ b/mypy-stubs/mistune/block_parser.pyi @@ -1,4 +1,6 @@ -from typing import ClassVar, Dict, Iterable, List, Match, Optional, Pattern, Tuple +from collections.abc import Iterable +from re import Match, Pattern +from typing import ClassVar from .core import BlockState as BlockState from .core import Parser as Parser @@ -22,29 +24,27 @@ class BlockParser(Parser[BlockState]): BLANK_LINE: Pattern[str] RAW_HTML: str BLOCK_HTML: str - SPECIFICATION: ClassVar[Dict[str, str]] + SPECIFICATION: ClassVar[dict[str, str]] DEFAULT_RULES: ClassVar[Iterable[str]] - block_quote_rules: List[str] - list_rules: List[str] + block_quote_rules: list[str] + list_rules: list[str] max_nested_level: int def __init__( self, - block_quote_rules: Optional[List[str]] = None, - list_rules: Optional[List[str]] = None, + block_quote_rules: list[str] | None = None, + list_rules: list[str] | None = None, max_nested_level: int = 6, ) -> None: ... def parse_blank_line(self, m: Match[str], state: BlockState) -> int: ... def parse_thematic_break(self, m: Match[str], state: BlockState) -> int: ... def parse_indent_code(self, m: Match[str], state: BlockState) -> int: ... - def parse_fenced_code(self, m: Match[str], state: BlockState) -> Optional[int]: ... + def parse_fenced_code(self, m: Match[str], state: BlockState) -> int | None: ... def parse_atx_heading(self, m: Match[str], state: BlockState) -> int: ... - def parse_setex_heading(self, m: Match[str], state: BlockState) -> Optional[int]: ... - def parse_ref_link(self, m: Match[str], state: BlockState) -> Optional[int]: ... - def extract_block_quote( - self, m: Match[str], state: BlockState - ) -> Tuple[str, Optional[int]]: ... + def parse_setex_heading(self, m: Match[str], state: BlockState) -> int | None: ... + def parse_ref_link(self, m: Match[str], state: BlockState) -> int | None: ... + def extract_block_quote(self, m: Match[str], state: BlockState) -> tuple[str, int | None]: ... def parse_block_quote(self, m: Match[str], state: BlockState) -> int: ... def parse_list(self, m: Match[str], state: BlockState) -> int: ... - def parse_block_html(self, m: Match[str], state: BlockState) -> Optional[int]: ... - def parse_raw_html(self, m: Match[str], state: BlockState) -> Optional[int]: ... - def parse(self, state: BlockState, rules: Optional[List[str]] = None) -> None: ... + def parse_block_html(self, m: Match[str], state: BlockState) -> int | None: ... + def parse_raw_html(self, m: Match[str], state: BlockState) -> int | None: ... + def parse(self, state: BlockState, rules: list[str] | None = None) -> None: ... diff --git a/mypy-stubs/mistune/core.pyi b/mypy-stubs/mistune/core.pyi index 4c32592fc..01637a1fc 100644 --- a/mypy-stubs/mistune/core.pyi +++ b/mypy-stubs/mistune/core.pyi @@ -1,85 +1,71 @@ import re -from collections.abc import Generator as Generator -from typing import ( - Any, - Callable, - ClassVar, - Dict, - Generic, - Iterable, - List, - Match, - MutableMapping, - Optional, - Pattern, - Type, - TypeVar, - Union, -) +from collections.abc import Callable, Iterable, MutableMapping +from re import Match, Pattern +from typing import Any, ClassVar, Generic, TypeVar from typing_extensions import Self class BlockState: src: str - tokens: List[Dict[str, Any]] + tokens: list[dict[str, Any]] cursor: int cursor_max: int list_tight: bool parent: Any env: MutableMapping[str, Any] - def __init__(self, parent: Optional[Any] = None) -> None: ... + def __init__(self, parent: Any | None = None) -> None: ... def child_state(self, src: str) -> BlockState: ... def process(self, src: str) -> None: ... def find_line_end(self) -> int: ... def get_text(self, end_pos: int) -> str: ... def last_token(self) -> Any: ... - def prepend_token(self, token: Dict[str, Any]) -> None: ... - def append_token(self, token: Dict[str, Any]) -> None: ... + def prepend_token(self, token: dict[str, Any]) -> None: ... + def append_token(self, token: dict[str, Any]) -> None: ... def add_paragraph(self, text: str) -> None: ... - def append_paragraph(self) -> Optional[int]: ... + def append_paragraph(self) -> int | None: ... def depth(self) -> int: ... class InlineState: env: MutableMapping[str, Any] src: str - tokens: List[Dict[str, Any]] + tokens: list[dict[str, Any]] in_image: bool in_link: bool in_emphasis: bool in_strong: bool def __init__(self, env: MutableMapping[str, Any]) -> None: ... - def prepend_token(self, token: Dict[str, Any]) -> None: ... - def append_token(self, token: Dict[str, Any]) -> None: ... + def prepend_token(self, token: dict[str, Any]) -> None: ... + def append_token(self, token: dict[str, Any]) -> None: ... def copy(self) -> InlineState: ... ST = TypeVar("ST", InlineState, BlockState) class Parser(Generic[ST]): sc_flag: re._FlagsType - state_cls: Type[ST] - SPECIFICATION: ClassVar[Dict[str, str]] + state_cls: type[ST] + SPECIFICATION: ClassVar[dict[str, str]] DEFAULT_RULES: ClassVar[Iterable[str]] - specification: Dict[str, str] + specification: dict[str, str] rules: Iterable[str] def __init__(self) -> None: ... - def compile_sc(self, rules: Optional[List[str]] = None) -> Pattern[str]: ... + def compile_sc(self, rules: list[str] | None = None) -> Pattern[str]: ... def register( self, name: str, - pattern: Union[str, None], - func: Callable[[Self, Match[str], ST], Optional[int]], - before: Optional[str] = None, + pattern: str | None, + func: Callable[[Self, Match[str], ST], int | None], + before: str | None = None, ) -> None: ... def register_rule(self, name: str, pattern: str, func: Any) -> None: ... @staticmethod - def insert_rule(rules: List[str], name: str, before: Optional[str] = None) -> None: ... - def parse_method(self, m: Match[str], state: ST) -> Optional[int]: ... + def insert_rule(rules: list[str], name: str, before: str | None = None) -> None: ... + def parse_method(self, m: Match[str], state: ST) -> int | None: ... class BaseRenderer: NAME: ClassVar[str] def __init__(self) -> None: ... def register(self, name: str, method: Callable[..., str]) -> None: ... - def render_token(self, token: Dict[str, Any], state: BlockState) -> str: ... - def iter_tokens(self, tokens: Iterable[Dict[str, Any]], state: BlockState) -> Iterable[str]: ... - def render_tokens(self, tokens: Iterable[Dict[str, Any]], state: BlockState) -> str: ... - def __call__(self, tokens: Iterable[Dict[str, Any]], state: BlockState) -> str: ... + def render_token(self, token: dict[str, Any], state: BlockState) -> str: ... + def iter_tokens(self, tokens: Iterable[dict[str, Any]], state: BlockState) -> Iterable[str]: ... + def render_tokens(self, tokens: Iterable[dict[str, Any]], state: BlockState) -> str: ... + def __call__(self, tokens: Iterable[dict[str, Any]], state: BlockState) -> str: ... diff --git a/mypy-stubs/mistune/directives/__init__.pyi b/mypy-stubs/mistune/directives/__init__.pyi index fd1a3d919..a0653478e 100644 --- a/mypy-stubs/mistune/directives/__init__.pyi +++ b/mypy-stubs/mistune/directives/__init__.pyi @@ -1,5 +1,3 @@ -from typing import List - from ._base import BaseDirective as BaseDirective from ._base import DirectiveParser as DirectiveParser from ._base import DirectivePlugin as DirectivePlugin @@ -25,4 +23,4 @@ __all__ = [ ] class RstDirective(RSTDirective): - def __init__(self, plugins: List[DirectivePlugin]) -> None: ... + def __init__(self, plugins: list[DirectivePlugin]) -> None: ... diff --git a/mypy-stubs/mistune/directives/_base.pyi b/mypy-stubs/mistune/directives/_base.pyi index 891651614..e59c0213f 100644 --- a/mypy-stubs/mistune/directives/_base.pyi +++ b/mypy-stubs/mistune/directives/_base.pyi @@ -1,17 +1,8 @@ import abc from abc import ABCMeta, abstractmethod -from typing import ( - Any, - Callable, - Dict, - Iterable, - List, - Match, - Optional, - Tuple, - Type, - Union, -) +from collections.abc import Callable, Iterable +from re import Match +from typing import Any from ..block_parser import BlockParser as BlockParser from ..core import BlockState as BlockState @@ -31,42 +22,40 @@ class DirectiveParser(ABCMeta, metaclass=abc.ABCMeta): @classmethod def parse_tokens( cls, block: BlockParser, text: str, state: BlockState - ) -> Iterable[Dict[str, Any]]: ... + ) -> Iterable[dict[str, Any]]: ... @staticmethod - def parse_options(m: Match[str]) -> List[Tuple[str, str]]: ... + def parse_options(m: Match[str]) -> list[tuple[str, str]]: ... class BaseDirective(metaclass=ABCMeta): - parser: Type[DirectiveParser] - directive_pattern: Optional[str] - def __init__(self, plugins: List["DirectivePlugin"]) -> None: ... + parser: type[DirectiveParser] + directive_pattern: str | None + def __init__(self, plugins: list["DirectivePlugin"]) -> None: ... def register( self, name: str, - fn: Callable[ - [BlockParser, Match[str], BlockState], Union[Dict[str, Any], List[Dict[str, Any]]] - ], + fn: Callable[[BlockParser, Match[str], BlockState], dict[str, Any] | list[dict[str, Any]]], ) -> None: ... def parse_method( self, block: BlockParser, m: Match[str], state: BlockState - ) -> Union[Dict[str, Any], List[Dict[str, Any]]]: ... + ) -> dict[str, Any] | list[dict[str, Any]]: ... @abstractmethod def parse_directive( self, block: BlockParser, m: Match[str], state: BlockState - ) -> Optional[int]: ... - def register_block_parser(self, md: Markdown, before: Optional[str] = None) -> None: ... + ) -> int | None: ... + def register_block_parser(self, md: Markdown, before: str | None = None) -> None: ... def __call__(self, markdown: Markdown) -> None: ... class DirectivePlugin: - parser: Type[DirectiveParser] + parser: type[DirectiveParser] def __init__(self) -> None: ... - def parse_options(self, m: Match[str]) -> List[Tuple[str, str]]: ... + def parse_options(self, m: Match[str]) -> list[tuple[str, str]]: ... def parse_type(self, m: Match[str]) -> str: ... def parse_title(self, m: Match[str]) -> str: ... def parse_content(self, m: Match[str]) -> str: ... def parse_tokens( self, block: BlockParser, text: str, state: BlockState - ) -> Iterable[Dict[str, Any]]: ... + ) -> Iterable[dict[str, Any]]: ... def parse( self, block: BlockParser, m: Match[str], state: BlockState - ) -> Union[Dict[str, Any], List[Dict[str, Any]]]: ... + ) -> dict[str, Any] | list[dict[str, Any]]: ... def __call__(self, directive: BaseDirective, md: Markdown) -> None: ... diff --git a/mypy-stubs/mistune/directives/_fenced.pyi b/mypy-stubs/mistune/directives/_fenced.pyi index 99ee838a2..e83186032 100644 --- a/mypy-stubs/mistune/directives/_fenced.pyi +++ b/mypy-stubs/mistune/directives/_fenced.pyi @@ -1,4 +1,4 @@ -from typing import List, Match, Optional +from re import Match from _typeshed import Incomplete @@ -22,11 +22,11 @@ class FencedDirective(BaseDirective): parser = FencedParser markers: Incomplete directive_pattern: Incomplete - def __init__(self, plugins: List[DirectivePlugin], markers: str = "`~") -> None: ... + def __init__(self, plugins: list[DirectivePlugin], markers: str = "`~") -> None: ... def parse_directive( self, block: BlockParser, m: Match[str], state: BlockState - ) -> Optional[int]: ... + ) -> int | None: ... def parse_fenced_code( self, block: BlockParser, m: Match[str], state: BlockState - ) -> Optional[int]: ... + ) -> int | None: ... def __call__(self, md: Markdown) -> None: ... diff --git a/mypy-stubs/mistune/directives/_rst.pyi b/mypy-stubs/mistune/directives/_rst.pyi index c8d07c619..3e110496a 100644 --- a/mypy-stubs/mistune/directives/_rst.pyi +++ b/mypy-stubs/mistune/directives/_rst.pyi @@ -1,4 +1,4 @@ -from typing import Match, Optional +from re import Match from ..block_parser import BlockParser from ..core import BlockState @@ -21,5 +21,5 @@ class RSTDirective(BaseDirective): directive_pattern: str def parse_directive( self, block: BlockParser, m: Match[str], state: BlockState - ) -> Optional[int]: ... + ) -> int | None: ... def __call__(self, markdown: Markdown) -> None: ... diff --git a/mypy-stubs/mistune/directives/admonition.pyi b/mypy-stubs/mistune/directives/admonition.pyi index a0bbfbbc3..e69830e32 100644 --- a/mypy-stubs/mistune/directives/admonition.pyi +++ b/mypy-stubs/mistune/directives/admonition.pyi @@ -1,4 +1,5 @@ -from typing import Any, Dict, Match +from re import Match +from typing import Any from _typeshed import Incomplete @@ -10,7 +11,7 @@ from ._base import DirectivePlugin as DirectivePlugin class Admonition(DirectivePlugin): SUPPORTED_NAMES: Incomplete - def parse(self, block: BlockParser, m: Match[str], state: BlockState) -> Dict[str, Any]: ... + def parse(self, block: BlockParser, m: Match[str], state: BlockState) -> dict[str, Any]: ... def __call__(self, directive: BaseDirective, md: Markdown) -> None: ... def render_admonition(self, text: str, name: str, **attrs: Any) -> str: ... diff --git a/mypy-stubs/mistune/directives/image.pyi b/mypy-stubs/mistune/directives/image.pyi index 703424bba..d0ab3bfaa 100644 --- a/mypy-stubs/mistune/directives/image.pyi +++ b/mypy-stubs/mistune/directives/image.pyi @@ -1,4 +1,5 @@ -from typing import Any, Dict, List, Match, Optional +from re import Match +from typing import Any from ..block_parser import BlockParser from ..core import BlockState @@ -9,13 +10,13 @@ __all__ = ["Image", "Figure"] class Image(DirectivePlugin): NAME: str - def parse(self, block: BlockParser, m: Match[str], state: BlockState) -> Dict[str, Any]: ... + def parse(self, block: BlockParser, m: Match[str], state: BlockState) -> dict[str, Any]: ... def __call__(self, directive: BaseDirective, md: Markdown) -> None: ... class Figure(DirectivePlugin): NAME: str def parse_directive_content( self, block: BlockParser, m: Match[str], state: BlockState - ) -> Optional[List[Dict[str, Any]]]: ... - def parse(self, block: BlockParser, m: Match[str], state: BlockState) -> Dict[str, Any]: ... + ) -> list[dict[str, Any]] | None: ... + def parse(self, block: BlockParser, m: Match[str], state: BlockState) -> dict[str, Any]: ... def __call__(self, directive: BaseDirective, md: Markdown) -> None: ... diff --git a/mypy-stubs/mistune/directives/include.pyi b/mypy-stubs/mistune/directives/include.pyi index 802e73dbf..0792f6b1f 100644 --- a/mypy-stubs/mistune/directives/include.pyi +++ b/mypy-stubs/mistune/directives/include.pyi @@ -1,4 +1,5 @@ -from typing import Any, Dict, List, Match, Union +from re import Match +from typing import Any from ..block_parser import BlockParser as BlockParser from ..core import BaseRenderer as BaseRenderer @@ -10,7 +11,7 @@ from ._base import DirectivePlugin as DirectivePlugin class Include(DirectivePlugin): def parse( self, block: BlockParser, m: Match[str], state: BlockState - ) -> Union[Dict[str, Any], List[Dict[str, Any]]]: ... + ) -> dict[str, Any] | list[dict[str, Any]]: ... def __call__(self, directive: BaseDirective, md: Markdown) -> None: ... def render_html_include(renderer: BaseRenderer, text: str, **attrs: Any) -> str: ... diff --git a/mypy-stubs/mistune/directives/toc.pyi b/mypy-stubs/mistune/directives/toc.pyi index f7712179a..9e8435746 100644 --- a/mypy-stubs/mistune/directives/toc.pyi +++ b/mypy-stubs/mistune/directives/toc.pyi @@ -1,4 +1,5 @@ -from typing import Any, Dict, Match +from re import Match +from typing import Any from _typeshed import Incomplete @@ -15,8 +16,8 @@ class TableOfContents(DirectivePlugin): min_level: Incomplete max_level: Incomplete def __init__(self, min_level: int = 1, max_level: int = 3) -> None: ... - def generate_heading_id(self, token: Dict[str, Any], index: int) -> str: ... - def parse(self, block: BlockParser, m: Match[str], state: BlockState) -> Dict[str, Any]: ... + def generate_heading_id(self, token: dict[str, Any], index: int) -> str: ... + def parse(self, block: BlockParser, m: Match[str], state: BlockState) -> dict[str, Any]: ... def toc_hook(self, md: Markdown, state: BlockState) -> None: ... def __call__(self, directive: BaseDirective, md: Markdown) -> None: ... diff --git a/mypy-stubs/mistune/helpers.pyi b/mypy-stubs/mistune/helpers.pyi index 678fa28df..5e7fa265a 100644 --- a/mypy-stubs/mistune/helpers.pyi +++ b/mypy-stubs/mistune/helpers.pyi @@ -1,4 +1,4 @@ -from typing import Any, Dict, Tuple, Union +from typing import Any from _typeshed import Incomplete @@ -19,12 +19,12 @@ BLOCK_TAGS: Incomplete PRE_TAGS: Incomplete def unescape_char(text: str) -> str: ... -def parse_link_text(src: str, pos: int) -> Union[Tuple[str, int], Tuple[None, None]]: ... -def parse_link_label(src: str, start_pos: int) -> Union[Tuple[str, int], Tuple[None, None]]: ... +def parse_link_text(src: str, pos: int) -> tuple[str, int] | tuple[None, None]: ... +def parse_link_label(src: str, start_pos: int) -> tuple[str, int] | tuple[None, None]: ... def parse_link_href( src: str, start_pos: int, block: bool = False -) -> Union[Tuple[str, int], Tuple[None, None]]: ... +) -> tuple[str, int] | tuple[None, None]: ... def parse_link_title( src: str, start_pos: int, max_pos: int -) -> Union[Tuple[str, int], Tuple[None, None]]: ... -def parse_link(src: str, pos: int) -> Union[Tuple[Dict[str, Any], int], Tuple[None, None]]: ... +) -> tuple[str, int] | tuple[None, None]: ... +def parse_link(src: str, pos: int) -> tuple[dict[str, Any], int] | tuple[None, None]: ... diff --git a/mypy-stubs/mistune/inline_parser.pyi b/mypy-stubs/mistune/inline_parser.pyi index 80295e66e..d6a2d6479 100644 --- a/mypy-stubs/mistune/inline_parser.pyi +++ b/mypy-stubs/mistune/inline_parser.pyi @@ -1,14 +1,6 @@ -from typing import ( - Any, - ClassVar, - Dict, - Iterable, - List, - Match, - MutableMapping, - Optional, - Pattern, -) +from collections.abc import Iterable, MutableMapping +from re import Match, Pattern +from typing import Any, ClassVar from .core import InlineState as InlineState from .core import Parser as Parser @@ -27,19 +19,19 @@ from .util import unikey as unikey PAREN_END_RE: Pattern[str] AUTO_EMAIL: str INLINE_HTML: str -EMPHASIS_END_RE: Dict[str, Pattern[str]] +EMPHASIS_END_RE: dict[str, Pattern[str]] class InlineParser(Parser[InlineState]): sc_flag: int state_cls = InlineState STD_LINEBREAK: str HARD_LINEBREAK: str - SPECIFICATION: ClassVar[Dict[str, str]] + SPECIFICATION: ClassVar[dict[str, str]] DEFAULT_RULES: ClassVar[Iterable[str]] hard_wrap: bool def __init__(self, hard_wrap: bool = False) -> None: ... def parse_escape(self, m: Match[str], state: InlineState) -> int: ... - def parse_link(self, m: Match[str], state: InlineState) -> Optional[int]: ... + def parse_link(self, m: Match[str], state: InlineState) -> int | None: ... def parse_auto_link(self, m: Match[str], state: InlineState) -> int: ... def parse_auto_email(self, m: Match[str], state: InlineState) -> int: ... def parse_emphasis(self, m: Match[str], state: InlineState) -> int: ... @@ -48,9 +40,9 @@ class InlineParser(Parser[InlineState]): def parse_softbreak(self, m: Match[str], state: InlineState) -> int: ... def parse_inline_html(self, m: Match[str], state: InlineState) -> int: ... def process_text(self, text: str, state: InlineState) -> None: ... - def parse(self, state: InlineState) -> List[Dict[str, Any]]: ... + def parse(self, state: InlineState) -> list[dict[str, Any]]: ... def precedence_scan( - self, m: Match[str], state: InlineState, end_pos: int, rules: Optional[List[str]] = None - ) -> Optional[int]: ... - def render(self, state: InlineState) -> List[Dict[str, Any]]: ... - def __call__(self, s: str, env: MutableMapping[str, Any]) -> List[Dict[str, Any]]: ... + self, m: Match[str], state: InlineState, end_pos: int, rules: list[str] | None = None + ) -> int | None: ... + def render(self, state: InlineState) -> list[dict[str, Any]]: ... + def __call__(self, s: str, env: MutableMapping[str, Any]) -> list[dict[str, Any]]: ... diff --git a/mypy-stubs/mistune/list_parser.pyi b/mypy-stubs/mistune/list_parser.pyi index 6fff2298b..96a5e1333 100644 --- a/mypy-stubs/mistune/list_parser.pyi +++ b/mypy-stubs/mistune/list_parser.pyi @@ -1,4 +1,4 @@ -from typing import Match +from re import Match from .block_parser import BlockParser as BlockParser from .core import BlockState as BlockState diff --git a/mypy-stubs/mistune/markdown.pyi b/mypy-stubs/mistune/markdown.pyi index 7a8fe03f1..7a7712a9b 100644 --- a/mypy-stubs/mistune/markdown.pyi +++ b/mypy-stubs/mistune/markdown.pyi @@ -1,4 +1,5 @@ -from typing import Any, Dict, Iterable, List, Optional, Tuple, Union +from collections.abc import Iterable +from typing import Any from _typeshed import Incomplete @@ -17,17 +18,17 @@ class Markdown: after_render_hooks: Incomplete def __init__( self, - renderer: Optional[BaseRenderer] = None, - block: Optional[BlockParser] = None, - inline: Optional[InlineParser] = None, - plugins: Optional[Iterable[Plugin]] = None, + renderer: BaseRenderer | None = None, + block: BlockParser | None = None, + inline: InlineParser | None = None, + plugins: Iterable[Plugin] | None = None, ) -> None: ... def use(self, plugin: Plugin) -> None: ... - def render_state(self, state: BlockState) -> Union[str, List[Dict[str, Any]]]: ... + def render_state(self, state: BlockState) -> str | list[dict[str, Any]]: ... def parse( - self, s: str, state: Optional[BlockState] = None - ) -> Tuple[Union[str, List[Dict[str, Any]]], BlockState]: ... + self, s: str, state: BlockState | None = None + ) -> tuple[str | list[dict[str, Any]], BlockState]: ... def read( - self, filepath: str, encoding: str = "utf-8", state: Optional[BlockState] = None - ) -> Tuple[Union[str, List[Dict[str, Any]]], BlockState]: ... - def __call__(self, s: str) -> Union[str, List[Dict[str, Any]]]: ... + self, filepath: str, encoding: str = "utf-8", state: BlockState | None = None + ) -> tuple[str | list[dict[str, Any]], BlockState]: ... + def __call__(self, s: str) -> str | list[dict[str, Any]]: ... diff --git a/mypy-stubs/mistune/plugins/__init__.pyi b/mypy-stubs/mistune/plugins/__init__.pyi index 251062a34..1e8c66d12 100644 --- a/mypy-stubs/mistune/plugins/__init__.pyi +++ b/mypy-stubs/mistune/plugins/__init__.pyi @@ -1,10 +1,10 @@ -from typing import Protocol, Union +from typing import Protocol, TypeAlias from ..markdown import Markdown class Plugin(Protocol): def __call__(self, markdown: Markdown) -> None: ... -PluginRef = Union[str, Plugin] +PluginRef: TypeAlias = str | Plugin def import_plugin(name: PluginRef) -> Plugin: ... diff --git a/mypy-stubs/mistune/plugins/ruby.pyi b/mypy-stubs/mistune/plugins/ruby.pyi index a2ef28635..6a911d0b0 100644 --- a/mypy-stubs/mistune/plugins/ruby.pyi +++ b/mypy-stubs/mistune/plugins/ruby.pyi @@ -1,4 +1,4 @@ -from typing import Match +from re import Match from ..block_parser import BlockParser as BlockParser from ..core import BaseRenderer as BaseRenderer diff --git a/mypy-stubs/mistune/renderers/_list.pyi b/mypy-stubs/mistune/renderers/_list.pyi index 37a8b5c18..2f53f2a3d 100644 --- a/mypy-stubs/mistune/renderers/_list.pyi +++ b/mypy-stubs/mistune/renderers/_list.pyi @@ -1,7 +1,7 @@ -from typing import Any, Dict +from typing import Any from ..core import BaseRenderer as BaseRenderer from ..core import BlockState as BlockState from ..util import strip_end as strip_end -def render_list(renderer: BaseRenderer, token: Dict[str, Any], state: BlockState) -> str: ... +def render_list(renderer: BaseRenderer, token: dict[str, Any], state: BlockState) -> str: ... diff --git a/mypy-stubs/mistune/renderers/html.pyi b/mypy-stubs/mistune/renderers/html.pyi index c001e70f0..2246367c6 100644 --- a/mypy-stubs/mistune/renderers/html.pyi +++ b/mypy-stubs/mistune/renderers/html.pyi @@ -1,6 +1,4 @@ -from typing import Any, ClassVar, Dict, Optional, Tuple - -from typing_extensions import Literal +from typing import Any, ClassVar, Literal from ..core import BaseRenderer as BaseRenderer from ..core import BlockState as BlockState @@ -9,19 +7,19 @@ from ..util import striptags as striptags class HTMLRenderer(BaseRenderer): NAME: ClassVar[Literal["html"]] - HARMFUL_PROTOCOLS: ClassVar[Tuple[str, ...]] - GOOD_DATA_PROTOCOLS: ClassVar[Tuple[str, ...]] + HARMFUL_PROTOCOLS: ClassVar[tuple[str, ...]] + GOOD_DATA_PROTOCOLS: ClassVar[tuple[str, ...]] _escape: bool def __init__( - self, escape: bool = True, allow_harmful_protocols: Optional[bool] = None + self, escape: bool = True, allow_harmful_protocols: bool | None = None ) -> None: ... - def render_token(self, token: Dict[str, Any], state: BlockState) -> str: ... + def render_token(self, token: dict[str, Any], state: BlockState) -> str: ... def safe_url(self, url: str) -> str: ... def text(self, text: str) -> str: ... def emphasis(self, text: str) -> str: ... def strong(self, text: str) -> str: ... - def link(self, text: str, url: str, title: Optional[str] = None) -> str: ... - def image(self, text: str, url: str, title: Optional[str] = None) -> str: ... + def link(self, text: str, url: str, title: str | None = None) -> str: ... + def image(self, text: str, url: str, title: str | None = None) -> str: ... def codespan(self, text: str) -> str: ... def linebreak(self) -> str: ... def softbreak(self) -> str: ... @@ -31,7 +29,7 @@ class HTMLRenderer(BaseRenderer): def blank_line(self) -> str: ... def thematic_break(self) -> str: ... def block_text(self, text: str) -> str: ... - def block_code(self, code: str, info: Optional[str] = None) -> str: ... + def block_code(self, code: str, info: str | None = None) -> str: ... def block_quote(self, text: str) -> str: ... def block_html(self, html: str) -> str: ... def block_error(self, text: str) -> str: ... diff --git a/mypy-stubs/mistune/renderers/markdown.pyi b/mypy-stubs/mistune/renderers/markdown.pyi index 1d8a5d9b8..64cccd1e3 100644 --- a/mypy-stubs/mistune/renderers/markdown.pyi +++ b/mypy-stubs/mistune/renderers/markdown.pyi @@ -1,4 +1,5 @@ -from typing import Any, Dict, Iterable +from collections.abc import Iterable +from typing import Any from _typeshed import Incomplete @@ -11,25 +12,25 @@ fenced_re: Incomplete class MarkdownRenderer(BaseRenderer): NAME: str - def __call__(self, tokens: Iterable[Dict[str, Any]], state: BlockState) -> str: ... + def __call__(self, tokens: Iterable[dict[str, Any]], state: BlockState) -> str: ... def render_referrences(self, state: BlockState) -> Iterable[str]: ... - def render_children(self, token: Dict[str, Any], state: BlockState) -> str: ... - def text(self, token: Dict[str, Any], state: BlockState) -> str: ... - def emphasis(self, token: Dict[str, Any], state: BlockState) -> str: ... - def strong(self, token: Dict[str, Any], state: BlockState) -> str: ... - def link(self, token: Dict[str, Any], state: BlockState) -> str: ... - def image(self, token: Dict[str, Any], state: BlockState) -> str: ... - def codespan(self, token: Dict[str, Any], state: BlockState) -> str: ... - def linebreak(self, token: Dict[str, Any], state: BlockState) -> str: ... - def softbreak(self, token: Dict[str, Any], state: BlockState) -> str: ... - def blank_line(self, token: Dict[str, Any], state: BlockState) -> str: ... - def inline_html(self, token: Dict[str, Any], state: BlockState) -> str: ... - def paragraph(self, token: Dict[str, Any], state: BlockState) -> str: ... - def heading(self, token: Dict[str, Any], state: BlockState) -> str: ... - def thematic_break(self, token: Dict[str, Any], state: BlockState) -> str: ... - def block_text(self, token: Dict[str, Any], state: BlockState) -> str: ... - def block_code(self, token: Dict[str, Any], state: BlockState) -> str: ... - def block_quote(self, token: Dict[str, Any], state: BlockState) -> str: ... - def block_html(self, token: Dict[str, Any], state: BlockState) -> str: ... - def block_error(self, token: Dict[str, Any], state: BlockState) -> str: ... - def list(self, token: Dict[str, Any], state: BlockState) -> str: ... + def render_children(self, token: dict[str, Any], state: BlockState) -> str: ... + def text(self, token: dict[str, Any], state: BlockState) -> str: ... + def emphasis(self, token: dict[str, Any], state: BlockState) -> str: ... + def strong(self, token: dict[str, Any], state: BlockState) -> str: ... + def link(self, token: dict[str, Any], state: BlockState) -> str: ... + def image(self, token: dict[str, Any], state: BlockState) -> str: ... + def codespan(self, token: dict[str, Any], state: BlockState) -> str: ... + def linebreak(self, token: dict[str, Any], state: BlockState) -> str: ... + def softbreak(self, token: dict[str, Any], state: BlockState) -> str: ... + def blank_line(self, token: dict[str, Any], state: BlockState) -> str: ... + def inline_html(self, token: dict[str, Any], state: BlockState) -> str: ... + def paragraph(self, token: dict[str, Any], state: BlockState) -> str: ... + def heading(self, token: dict[str, Any], state: BlockState) -> str: ... + def thematic_break(self, token: dict[str, Any], state: BlockState) -> str: ... + def block_text(self, token: dict[str, Any], state: BlockState) -> str: ... + def block_code(self, token: dict[str, Any], state: BlockState) -> str: ... + def block_quote(self, token: dict[str, Any], state: BlockState) -> str: ... + def block_html(self, token: dict[str, Any], state: BlockState) -> str: ... + def block_error(self, token: dict[str, Any], state: BlockState) -> str: ... + def list(self, token: dict[str, Any], state: BlockState) -> str: ... diff --git a/mypy-stubs/mistune/renderers/rst.pyi b/mypy-stubs/mistune/renderers/rst.pyi index 51a9aaac2..200ecdd58 100644 --- a/mypy-stubs/mistune/renderers/rst.pyi +++ b/mypy-stubs/mistune/renderers/rst.pyi @@ -1,4 +1,5 @@ -from typing import Any, Dict, Iterable +from collections.abc import Iterable +from typing import Any from _typeshed import Incomplete @@ -11,25 +12,25 @@ class RSTRenderer(BaseRenderer): NAME: str HEADING_MARKERS: Incomplete INLINE_IMAGE_PREFIX: str - def iter_tokens(self, tokens: Iterable[Dict[str, Any]], state: BlockState) -> Iterable[str]: ... - def __call__(self, tokens: Iterable[Dict[str, Any]], state: BlockState) -> str: ... + def iter_tokens(self, tokens: Iterable[dict[str, Any]], state: BlockState) -> Iterable[str]: ... + def __call__(self, tokens: Iterable[dict[str, Any]], state: BlockState) -> str: ... def render_referrences(self, state: BlockState) -> Iterable[str]: ... - def render_children(self, token: Dict[str, Any], state: BlockState) -> str: ... - def text(self, token: Dict[str, Any], state: BlockState) -> str: ... - def emphasis(self, token: Dict[str, Any], state: BlockState) -> str: ... - def strong(self, token: Dict[str, Any], state: BlockState) -> str: ... - def link(self, token: Dict[str, Any], state: BlockState) -> str: ... - def image(self, token: Dict[str, Any], state: BlockState) -> str: ... - def codespan(self, token: Dict[str, Any], state: BlockState) -> str: ... - def linebreak(self, token: Dict[str, Any], state: BlockState) -> str: ... - def softbreak(self, token: Dict[str, Any], state: BlockState) -> str: ... - def inline_html(self, token: Dict[str, Any], state: BlockState) -> str: ... - def paragraph(self, token: Dict[str, Any], state: BlockState) -> str: ... - def heading(self, token: Dict[str, Any], state: BlockState) -> str: ... - def thematic_break(self, token: Dict[str, Any], state: BlockState) -> str: ... - def block_text(self, token: Dict[str, Any], state: BlockState) -> str: ... - def block_code(self, token: Dict[str, Any], state: BlockState) -> str: ... - def block_quote(self, token: Dict[str, Any], state: BlockState) -> str: ... - def block_html(self, token: Dict[str, Any], state: BlockState) -> str: ... - def block_error(self, token: Dict[str, Any], state: BlockState) -> str: ... - def list(self, token: Dict[str, Any], state: BlockState) -> str: ... + def render_children(self, token: dict[str, Any], state: BlockState) -> str: ... + def text(self, token: dict[str, Any], state: BlockState) -> str: ... + def emphasis(self, token: dict[str, Any], state: BlockState) -> str: ... + def strong(self, token: dict[str, Any], state: BlockState) -> str: ... + def link(self, token: dict[str, Any], state: BlockState) -> str: ... + def image(self, token: dict[str, Any], state: BlockState) -> str: ... + def codespan(self, token: dict[str, Any], state: BlockState) -> str: ... + def linebreak(self, token: dict[str, Any], state: BlockState) -> str: ... + def softbreak(self, token: dict[str, Any], state: BlockState) -> str: ... + def inline_html(self, token: dict[str, Any], state: BlockState) -> str: ... + def paragraph(self, token: dict[str, Any], state: BlockState) -> str: ... + def heading(self, token: dict[str, Any], state: BlockState) -> str: ... + def thematic_break(self, token: dict[str, Any], state: BlockState) -> str: ... + def block_text(self, token: dict[str, Any], state: BlockState) -> str: ... + def block_code(self, token: dict[str, Any], state: BlockState) -> str: ... + def block_quote(self, token: dict[str, Any], state: BlockState) -> str: ... + def block_html(self, token: dict[str, Any], state: BlockState) -> str: ... + def block_error(self, token: dict[str, Any], state: BlockState) -> str: ... + def list(self, token: dict[str, Any], state: BlockState) -> str: ... diff --git a/mypy-stubs/mistune/toc.pyi b/mypy-stubs/mistune/toc.pyi index 9f91d20c6..9ba29b9f5 100644 --- a/mypy-stubs/mistune/toc.pyi +++ b/mypy-stubs/mistune/toc.pyi @@ -1,4 +1,5 @@ -from typing import Any, Callable, Dict, Iterable, Optional, Tuple +from collections.abc import Callable, Iterable +from typing import Any from .core import BlockState as BlockState from .markdown import Markdown as Markdown @@ -8,7 +9,7 @@ def add_toc_hook( md: Markdown, min_level: int = 1, max_level: int = 3, - heading_id: Optional[Callable[[Dict[str, Any], int], str]] = None, + heading_id: Callable[[dict[str, Any], int], str] | None = None, ) -> None: ... -def normalize_toc_item(md: Markdown, token: Dict[str, Any]) -> Tuple[int, str, str]: ... -def render_toc_ul(toc: Iterable[Tuple[int, str, str]]) -> str: ... +def normalize_toc_item(md: Markdown, token: dict[str, Any]) -> tuple[int, str, str]: ... +def render_toc_ul(toc: Iterable[tuple[int, str, str]]) -> str: ... diff --git a/mypy-stubs/rdflib/collection.pyi b/mypy-stubs/rdflib/collection.pyi index 0bee98429..a04649b6a 100644 --- a/mypy-stubs/rdflib/collection.pyi +++ b/mypy-stubs/rdflib/collection.pyi @@ -1,4 +1,5 @@ -from typing import Any, Iterator +from collections.abc import Iterator +from typing import Any from rdflib.graph import Graph from rdflib.term import Node diff --git a/mypy-stubs/rdflib/compare.pyi b/mypy-stubs/rdflib/compare.pyi index 6451292ab..795d1059d 100644 --- a/mypy-stubs/rdflib/compare.pyi +++ b/mypy-stubs/rdflib/compare.pyi @@ -1,8 +1,8 @@ -from typing import Dict, Union +from typing import TypeAlias from rdflib.graph import ConjunctiveGraph, Graph -Stats = Dict[str, Union[int, str]] +Stats: TypeAlias = dict[str, int | str] class IsomorphicGraph(ConjunctiveGraph): pass diff --git a/mypy-stubs/rdflib/graph.pyi b/mypy-stubs/rdflib/graph.pyi index d3e6f2f54..ba4285561 100644 --- a/mypy-stubs/rdflib/graph.pyi +++ b/mypy-stubs/rdflib/graph.pyi @@ -1,22 +1,13 @@ import pathlib -from typing import ( - IO, - Any, - Iterable, - Iterator, - List, - Optional, - Set, - Tuple, - Union, - overload, -) +from builtins import set as _set +from collections.abc import Iterable, Iterator +from typing import IO, Any, overload from rdflib import query from rdflib.collection import Collection from rdflib.paths import Path from rdflib.resource import Resource -from rdflib.term import BNode, Identifier, Node +from rdflib.term import Identifier, Node class Graph(Node): base: Any = ... @@ -26,9 +17,9 @@ class Graph(Node): def __init__( self, store: str = ..., - identifier: Optional[Any] = ..., - namespace_manager: Optional[Any] = ..., - base: Optional[Any] = ..., + identifier: Any | None = ..., + namespace_manager: Any | None = ..., + base: Any | None = ..., ) -> None: ... store: Any = ... identifier: Any = ... @@ -44,59 +35,53 @@ class Graph(Node): def remove(self, triple: Any) -> None: ... def triples( self, - triple: Tuple[ - Optional[Union[str, Identifier]], - Optional[Union[str, Identifier]], - Optional[Identifier], + triple: tuple[ + str | Identifier | None, + str | Identifier | None, + Identifier | None, ], - ) -> Iterator[Tuple[Identifier, Identifier, Identifier]]: ... + ) -> Iterator[tuple[Identifier, Identifier, Identifier]]: ... def __getitem__( self, item: slice | Path | Node ) -> Iterator[ - Tuple[Identifier, Identifier, Identifier] | Tuple[Identifier, identifier] | Node + tuple[Identifier, Identifier, Identifier] | tuple[Identifier, identifier] | Node ]: ... def __contains__(self, triple: Any) -> bool: ... def __add__(self, other: Any) -> Graph: ... def set(self, triple: Any) -> None: ... - def subjects( - self, predicate: Optional[Any] = ..., object: Optional[Any] = ... - ) -> Iterable[Node]: ... - def predicates( - self, subject: Optional[Any] = ..., object: Optional[Any] = ... - ) -> Iterable[Node]: ... + def subjects(self, predicate: Any | None = ..., object: Any | None = ...) -> Iterable[Node]: ... + def predicates(self, subject: Any | None = ..., object: Any | None = ...) -> Iterable[Node]: ... def objects( - self, subject: Optional[Any] = ..., predicate: Optional[Any] = ... + self, subject: Any | None = ..., predicate: Any | None = ... ) -> Iterable[Identifier]: ... - def subject_predicates(self, object: Optional[Any] = ...) -> None: ... - def subject_objects(self, predicate: Optional[Any] = ...) -> None: ... - def predicate_objects(self, subject: Optional[Any] = ...) -> None: ... - def triples_choices(self, triple: Any, context: Optional[Any] = ...) -> None: ... + def subject_predicates(self, object: Any | None = ...) -> None: ... + def subject_objects(self, predicate: Any | None = ...) -> None: ... + def predicate_objects(self, subject: Any | None = ...) -> None: ... + def triples_choices(self, triple: Any, context: Any | None = ...) -> None: ... def value( self, - subject: Optional[Any] = ..., + subject: Any | None = ..., predicate: Any = ..., - object: Optional[Any] = ..., - default: Optional[Any] = ..., + object: Any | None = ..., + default: Any | None = ..., any: bool = ..., ) -> Any: ... def label(self, subject: Any, default: str = ...) -> Any: ... def preferredLabel( self, subject: Any, - lang: Optional[Any] = ..., - default: Optional[Any] = ..., + lang: Any | None = ..., + default: Any | None = ..., labelProperties: Any = ..., - ) -> List[Tuple[Any, Any]]: ... + ) -> list[tuple[Any, Any]]: ... def comment(self, subject: Any, default: str = ...) -> Any: ... def items(self, list: Any) -> Iterator[Any]: ... - def transitiveClosure( - self, func: Any, arg: Any, seen: Optional[Any] = ... - ) -> Iterator[Any]: ... + def transitiveClosure(self, func: Any, arg: Any, seen: Any | None = ...) -> Iterator[Any]: ... def transitive_objects( - self, subject: Any, property: Any, remember: Optional[Any] = ... + self, subject: Any, property: Any, remember: Any | None = ... ) -> Iterator[Any]: ... def transitive_subjects( - self, predicate: Any, object: Any, remember: Optional[Any] = ... + self, predicate: Any, object: Any, remember: Any | None = ... ) -> Iterator[Any]: ... def seq(self, subject: Any) -> Seq | None: ... def qname(self, uri: Any) -> Any: ... @@ -104,7 +89,7 @@ class Graph(Node): def bind( self, prefix: Any, namespace: Any, override: bool = ..., replace: bool = ... ) -> Any: ... - def namespaces(self) -> Iterator[Tuple[Any, Any]]: ... + def namespaces(self) -> Iterator[tuple[Any, Any]]: ... def absolutize(self, uri: Any, defrag: int = ...) -> Any: ... # no destination and non-None positional encoding @@ -113,7 +98,7 @@ class Graph(Node): self, destination: None, format: str, - base: Optional[str], + base: str | None, encoding: str, **args: Any, ) -> bytes: ... @@ -124,7 +109,7 @@ class Graph(Node): self, destination: None = ..., format: str = ..., - base: Optional[str] = ..., + base: str | None = ..., *, encoding: str, **args: Any, @@ -136,7 +121,7 @@ class Graph(Node): self, destination: None = ..., format: str = ..., - base: Optional[str] = ..., + base: str | None = ..., encoding: None = ..., **args: Any, ) -> str: ... @@ -145,10 +130,10 @@ class Graph(Node): @overload def serialize( self, - destination: Union[str, pathlib.PurePath, IO[bytes]], + destination: str | pathlib.PurePath | IO[bytes], format: str = ..., - base: Optional[str] = ..., - encoding: Optional[str] = ..., + base: str | None = ..., + encoding: str | None = ..., **args: Any, ) -> "Graph": ... @@ -156,30 +141,30 @@ class Graph(Node): @overload def serialize( self, - destination: Optional[Union[str, pathlib.PurePath, IO[bytes]]] = ..., + destination: str | pathlib.PurePath | IO[bytes] | None = ..., format: str = ..., - base: Optional[str] = ..., - encoding: Optional[str] = ..., + base: str | None = ..., + encoding: str | None = ..., **args: Any, - ) -> Union[bytes, str, "Graph"]: ... + ) -> bytes | str | "Graph": ... def parse( self, - source: Optional[Any] = ..., - publicID: Optional[Any] = ..., - format: Optional[str] = ..., - location: Optional[Any] = ..., - file: Optional[Any] = ..., - data: Optional[Any] = ..., + source: Any | None = ..., + publicID: Any | None = ..., + format: str | None = ..., + location: Any | None = ..., + file: Any | None = ..., + data: Any | None = ..., **args: Any, ) -> "Graph": ... - def load(self, source: Any, publicID: Optional[Any] = ..., format: str = ...) -> "Graph": ... + def load(self, source: Any, publicID: Any | None = ..., format: str = ...) -> "Graph": ... def query( self, query_object: Any, processor: str = ..., result: str = ..., - initNs: Optional[Any] = ..., - initBindings: Optional[Any] = ..., + initNs: Any | None = ..., + initBindings: Any | None = ..., use_store_provided: bool = ..., **kwargs: Any, ) -> query.Result: ... @@ -187,27 +172,25 @@ class Graph(Node): self, update_object: Any, processor: str = ..., - initNs: Optional[Any] = ..., - initBindings: Optional[Any] = ..., + initNs: Any | None = ..., + initBindings: Any | None = ..., use_store_provided: bool = ..., **kwargs: Any, ) -> Any: ... def n3(self) -> str: ... def isomorphic(self, other: Any) -> bool: ... def connected(self) -> bool: ... - def all_nodes(self) -> Set[Any]: ... + def all_nodes(self) -> _set[Node]: ... def collection(self, identifier: Any) -> Collection: ... def resource(self, identifier: Any) -> Resource: ... def skolemize( self, - new_graph: Optional[Any] = ..., - bnode: Optional[Any] = ..., - authority: Optional[Any] = ..., - basepath: Optional[Any] = ..., - ) -> Graph: ... - def de_skolemize( - self, new_graph: Optional[Any] = ..., uriref: Optional[Any] = ... + new_graph: Any | None = ..., + bnode: Any | None = ..., + authority: Any | None = ..., + basepath: Any | None = ..., ) -> Graph: ... + def de_skolemize(self, new_graph: Any | None = ..., uriref: Any | None = ...) -> Graph: ... class ConjunctiveGraph(Graph): context_aware: bool = ... @@ -216,32 +199,32 @@ class ConjunctiveGraph(Graph): def __init__( self, store: str = ..., - identifier: Optional[Any] = ..., - default_graph_base: Optional[Any] = ..., + identifier: Any | None = ..., + default_graph_base: Any | None = ..., ) -> None: ... def add(self, triple_or_quad: Any) -> None: ... def addN(self, quads: Any) -> None: ... def remove(self, triple_or_quad: Any) -> None: ... # def triples(self, triple_or_quad: Tuple[Optional[Union[str, BNode]], Optional[Union[str, BNode]], Optional[BNode]], context: Tuple[Optional[Union[str, BNode]], Optional[Union[str, BNode]], Optional[BNode]]) -> Iterator[Tuple[Identifier, Identifier, Identifier]]: ... - def quads(self, triple_or_quad: Optional[Any] = ...) -> None: ... - def triples_choices(self, triple: Any, context: Optional[Any] = ...) -> None: ... - def contexts(self, triple: Optional[Any] = ...) -> None: ... + def quads(self, triple_or_quad: Any | None = ...) -> None: ... + def triples_choices(self, triple: Any, context: Any | None = ...) -> None: ... + def contexts(self, triple: Any | None = ...) -> None: ... def get_context( self, identifier: Node | str | None, quoted: bool = ..., - base: Optional[str] = ..., + base: str | None = ..., ) -> Graph: ... def remove_context(self, context: Any) -> None: ... - def context_id(self, uri: Any, context_id: Optional[Any] = ...) -> Any: ... + def context_id(self, uri: Any, context_id: Any | None = ...) -> Any: ... def parse( self, - source: Optional[Any] = ..., - publicID: Optional[Any] = ..., - format: Optional[str] = ..., - location: Optional[Any] = ..., - file: Optional[Any] = ..., - data: Optional[Any] = ..., + source: Any | None = ..., + publicID: Any | None = ..., + format: str | None = ..., + location: Any | None = ..., + file: Any | None = ..., + data: Any | None = ..., **args: Any, ) -> Graph: ... diff --git a/mypy-stubs/rdflib/namespace/__init__.pyi b/mypy-stubs/rdflib/namespace/__init__.pyi index 63af92977..1b1636dc2 100644 --- a/mypy-stubs/rdflib/namespace/__init__.pyi +++ b/mypy-stubs/rdflib/namespace/__init__.pyi @@ -1,4 +1,4 @@ -from typing import Any, Tuple +from typing import Any from rdflib.term import URIRef @@ -59,7 +59,7 @@ SPLIT_START_CATEGORIES = NAME_START_CATEGORIES + ["Nd"] XMLNS = "http://www.w3.org/XML/1998/namespace" -def split_uri(uri: Any, split_start: Any = ...) -> Tuple[str, str]: ... +def split_uri(uri: Any, split_start: Any = ...) -> tuple[str, str]: ... from rdflib.namespace._CSVW import CSVW from rdflib.namespace._DC import DC diff --git a/mypy-stubs/rdflib/paths.pyi b/mypy-stubs/rdflib/paths.pyi index 9bea17956..c41e414bb 100644 --- a/mypy-stubs/rdflib/paths.pyi +++ b/mypy-stubs/rdflib/paths.pyi @@ -1,7 +1,6 @@ -from collections.abc import Generator -from typing import Any, Callable, Union +from collections.abc import Callable +from typing import Any -from rdflib.term import Node as Node from rdflib.term import URIRef as URIRef ZeroOrMore: str @@ -9,10 +8,10 @@ OneOrMore: str ZeroOrOne: str class Path: - __or__: Callable[[Path, Union["URIRef", "Path"]], "AlternativePath"] + __or__: Callable[[Path, "URIRef" | "Path"], "AlternativePath"] __invert__: Callable[[Path], "InvPath"] __neg__: Callable[[Path], "NegatedPath"] - __truediv__: Callable[[Path, Union["URIRef", "Path"]], "SequencePath"] + __truediv__: Callable[[Path, "URIRef" | "Path"], "SequencePath"] __mul__: Callable[[Path, str], "MulPath"] def __hash__(self) -> int: ... def __lt__(self, other: Any) -> bool: ... diff --git a/mypy-stubs/rdflib/plugin.pyi b/mypy-stubs/rdflib/plugin.pyi index b5f237135..06a9cd590 100644 --- a/mypy-stubs/rdflib/plugin.pyi +++ b/mypy-stubs/rdflib/plugin.pyi @@ -1,4 +1,5 @@ -from typing import Any, Generic, Iterator, Optional, Type, TypeVar, overload +from collections.abc import Iterator +from typing import Any, Generic, TypeVar, overload from rdflib.exceptions import Error @@ -10,19 +11,19 @@ PluginT = TypeVar("PluginT") class Plugin(Generic[PluginT]): name: str - kind: Type[PluginT] + kind: type[PluginT] module_path: str class_name: str - _class: Optional[Type[PluginT]] + _class: type[PluginT] | None def __init__( - self, name: str, kind: Type[PluginT], module_path: str, class_name: str + self, name: str, kind: type[PluginT], module_path: str, class_name: str ) -> None: ... - def getClass(self) -> Type[PluginT]: ... + def getClass(self) -> type[PluginT]: ... -def register(name: str, kind: Type[Any], module_path: str, class_name: str) -> None: ... -def get(name: str, kind: Type[PluginT]) -> Type[PluginT]: ... +def register(name: str, kind: type[Any], module_path: str, class_name: str) -> None: ... +def get(name: str, kind: type[PluginT]) -> type[PluginT]: ... @overload -def plugins(name: Optional[str] = ..., kind: Type[PluginT] = ...) -> Iterator[Plugin[PluginT]]: ... +def plugins(name: str | None = ..., kind: type[PluginT] = ...) -> Iterator[Plugin[PluginT]]: ... @overload -def plugins(name: Optional[str] = ..., kind: None = ...) -> Iterator[Plugin[Any]]: ... +def plugins(name: str | None = ..., kind: None = ...) -> Iterator[Plugin[Any]]: ... diff --git a/mypy-stubs/rdflib/query.pyi b/mypy-stubs/rdflib/query.pyi index 981fe12d2..28d34b72d 100644 --- a/mypy-stubs/rdflib/query.pyi +++ b/mypy-stubs/rdflib/query.pyi @@ -1,12 +1,12 @@ -from typing import IO, Any, Dict, Iterator, List, Mapping, Optional, Tuple, overload +from collections.abc import Iterator, Mapping +from typing import IO, Any, overload -from rdflib import URIRef, Variable -from rdflib.term import Identifier +from rdflib.term import Identifier, Variable from typing_extensions import SupportsIndex -class ResultRow(Tuple["Identifier", ...]): +class ResultRow(tuple["Identifier", ...]): def __new__( - cls, values: Mapping[Variable, Identifier], labels: List[Variable] + cls, values: Mapping[Variable, Identifier], labels: list[Variable] ) -> ResultRow: ... def __getattr__(self, name: str) -> Identifier: ... @overload @@ -14,9 +14,9 @@ class ResultRow(Tuple["Identifier", ...]): @overload def __getitem__(self, __x: SupportsIndex) -> Identifier: ... @overload - def __getitem__(self, __x: slice) -> Tuple[Identifier, ...]: ... + def __getitem__(self, __x: slice) -> tuple[Identifier, ...]: ... def get(self, name: str, default: Any | None = ...) -> Identifier: ... - def asdict(self) -> Dict[str, Identifier]: ... + def asdict(self) -> dict[str, Identifier]: ... class Result: type: Any @@ -39,4 +39,4 @@ class Result: encoding: str = ..., format: str = ..., **args: Any, - ) -> Optional[bytes]: ... + ) -> bytes | None: ... diff --git a/mypy-stubs/rdflib/resource.pyi b/mypy-stubs/rdflib/resource.pyi index 0dd3b988e..773bca9b3 100644 --- a/mypy-stubs/rdflib/resource.pyi +++ b/mypy-stubs/rdflib/resource.pyi @@ -1,4 +1,5 @@ -from typing import Any, Iterable, Iterator, Tuple +from collections.abc import Iterable, Iterator +from typing import Any from _typeshed import Incomplete from rdflib.graph import Graph, Seq @@ -14,9 +15,9 @@ class Resource: def subjects(self, predicate: Any | None = ...) -> Iterable[Node]: ... def predicates(self, o: Incomplete | None = ...) -> Iterable[Node]: ... def objects(self, predicate: Any | None = ...) -> Iterable[Node]: ... - def subject_predicates(self) -> Iterator[Tuple[Node, Node]]: ... - def subject_objects(self) -> Iterator[Tuple[Node, Node]]: ... - def predicate_objects(self) -> Iterator[Tuple[Node, Node]]: ... + def subject_predicates(self) -> Iterator[tuple[Node, Node]]: ... + def subject_objects(self) -> Iterator[tuple[Node, Node]]: ... + def predicate_objects(self) -> Iterator[tuple[Node, Node]]: ... def value( self, p: Node, o: Node | None = ..., default: Any | None = ..., any: bool = ... ) -> Any: ... diff --git a/mypy-stubs/rdflib/term.pyi b/mypy-stubs/rdflib/term.pyi index 0830bdc29..7f3ac9e8f 100644 --- a/mypy-stubs/rdflib/term.pyi +++ b/mypy-stubs/rdflib/term.pyi @@ -1,9 +1,9 @@ -from typing import Any, Callable, Union +from typing import Any class Node: ... class Identifier(Node, str): - def __new__(cls, value: Union[Any, str, None]) -> "Identifier": ... + def __new__(cls, value: Any | str | None) -> "Identifier": ... def eq(self, other: Any) -> bool: ... def neq(self, other: Any) -> bool: ... diff --git a/mypy-stubs/rdflib/util.pyi b/mypy-stubs/rdflib/util.pyi index 93c14b9ce..39dc9299c 100644 --- a/mypy-stubs/rdflib/util.pyi +++ b/mypy-stubs/rdflib/util.pyi @@ -1,3 +1 @@ -from typing import Optional - -def guess_format(fpath: str, fmap: dict[str, str] | None = ...) -> Optional[str]: ... +def guess_format(fpath: str, fmap: dict[str, str] | None = ...) -> str | None: ... diff --git a/pyproject.toml b/pyproject.toml index c7354e00a..9e1b7a0e8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,4 +30,4 @@ before-all = "apk add ccache || yum install -y ccache || dnf --nogpg -y install [tool.black] line-length = 100 -target-version = [ "py39" ] +target-version = [ "py310" ] diff --git a/schema_salad/avro/schema.py b/schema_salad/avro/schema.py index c9012c2fd..ff3758ec5 100644 --- a/schema_salad/avro/schema.py +++ b/schema_salad/avro/schema.py @@ -31,7 +31,7 @@ A boolean; or Null. """ -from typing import Any, Optional, Union, cast +from typing import Any, Optional, TypeAlias, Union, cast from mypy_extensions import mypyc_attr @@ -62,14 +62,10 @@ # need recursion support in mypy/mypyc for a comprehensive JSON type # MappingDataType = Dict[str, Union[PropType, List[PropsType]]] # was: Union[str, MappingDataType, List[MappingDataType]] -JsonDataType = Any -AtomicPropType = Union[None, str, int, float, bool, "Schema", list[str], list["Field"]] -PropType = Union[ - AtomicPropType, - dict[str, AtomicPropType], - list[dict[str, AtomicPropType]], -] -PropsType = dict[str, PropType] +JsonDataType: TypeAlias = Any +AtomicPropType: TypeAlias = Union[None, str, int, float, bool, "Schema", list[str], list["Field"]] +PropType: TypeAlias = AtomicPropType | dict[str, AtomicPropType] | list[dict[str, AtomicPropType]] +PropsType: TypeAlias = dict[str, PropType | None] FIELD_RESERVED_PROPS = ("default", "name", "doc", "order", "type") @@ -98,7 +94,7 @@ class SchemaParseException(AvroException): class Schema: """Base class for all Schema classes.""" - def __init__(self, atype: str, other_props: Optional[PropsType] = None) -> None: + def __init__(self, atype: str, other_props: PropsType | None = None) -> None: """Avro Schema initializer.""" # Ensure valid ctor args if not isinstance(atype, str): @@ -123,11 +119,11 @@ def props(self) -> PropsType: return self._props # utility functions to manipulate properties dict - def get_prop(self, key: str) -> Optional[PropType]: + def get_prop(self, key: str) -> PropType | None: """Retrieve a property from the Schema.""" return self._props.get(key) - def set_prop(self, key: str, value: Optional[PropType]) -> None: + def set_prop(self, key: str, value: PropType | None) -> None: """Set a Schema property.""" self._props[key] = value @@ -137,9 +133,9 @@ class Name: def __init__( self, - name_attr: Optional[str] = None, - space_attr: Optional[str] = None, - default_space: Optional[str] = None, + name_attr: str | None = None, + space_attr: str | None = None, + default_space: str | None = None, ) -> None: """ Formulate full name according to the specification. @@ -151,7 +147,7 @@ def __init__( # Ensure valid ctor args - def validate(val: Optional[str], name: str) -> None: + def validate(val: str | None, name: str) -> None: if (isinstance(val, str) and val != "") or val is None: # OK return @@ -162,7 +158,7 @@ def validate(val: Optional[str], name: str) -> None: validate(space_attr, "Space") validate(default_space, "Default space") - self._full: Optional[str] = name_attr + self._full: str | None = name_attr if name_attr is None or name_attr == "": return @@ -175,11 +171,11 @@ def validate(val: Optional[str], name: str) -> None: self._full = f"{default_space}.{name_attr}" @property - def fullname(self) -> Optional[str]: + def fullname(self) -> str | None: """Retrieve the computed full name.""" return self._full - def get_space(self) -> Optional[str]: + def get_space(self) -> str | None: """Back out a namespace from full name.""" if self._full is None: return None @@ -192,26 +188,24 @@ def get_space(self) -> Optional[str]: class Names: """Track name set and default namespace during parsing.""" - def __init__(self, default_namespace: Optional[str] = None) -> None: + def __init__(self, default_namespace: str | None = None) -> None: """Create a namespace tracker.""" self.names: dict[str, NamedSchema] = {} self.default_namespace = default_namespace - def has_name(self, name_attr: str, space_attr: Optional[str]) -> bool: + def has_name(self, name_attr: str, space_attr: str | None) -> bool: """Test if the given namespace is stored.""" test = Name(name_attr, space_attr, self.default_namespace).fullname return test in self.names - def get_name(self, name_attr: str, space_attr: Optional[str]) -> Optional["NamedSchema"]: + def get_name(self, name_attr: str, space_attr: str | None) -> Optional["NamedSchema"]: """Fetch the stored schema for the given namespace.""" test = Name(name_attr, space_attr, self.default_namespace).fullname if test not in self.names: return None return self.names[test] - def add_name( - self, name_attr: str, space_attr: Optional[str], new_schema: "NamedSchema" - ) -> Name: + def add_name(self, name_attr: str, space_attr: str | None, new_schema: "NamedSchema") -> Name: """ Add a new schema object to the name set. @@ -243,9 +237,9 @@ def __init__( self, atype: str, name: str, - namespace: Optional[str] = None, - names: Optional[Names] = None, - other_props: Optional[PropsType] = None, + namespace: str | None = None, + names: Names | None = None, + other_props: PropsType | None = None, ) -> None: # Ensure valid ctor args if not name: @@ -284,11 +278,11 @@ def __init__( atype: JsonDataType, name: str, has_default: bool, - default: Optional[Any] = None, - order: Optional[str] = None, - names: Optional[Names] = None, - doc: Optional[Union[str, list[str]]] = None, - other_props: Optional[PropsType] = None, + default: Any | None = None, + order: str | None = None, + names: Names | None = None, + doc: str | list[str] | None = None, + other_props: PropsType | None = None, ) -> None: # Ensure valid ctor args if not name: @@ -329,16 +323,16 @@ def __init__( # read-only properties @property - def default(self) -> Optional[Any]: + def default(self) -> Any | None: """Return the default value, if any.""" return self.get_prop("default") # utility functions to manipulate properties dict - def get_prop(self, key: str) -> Optional[PropType]: + def get_prop(self, key: str) -> PropType | None: """Retrieve a property from the Field.""" return self._props.get(key) - def set_prop(self, key: str, value: Optional[PropType]) -> None: + def set_prop(self, key: str, value: PropType | None) -> None: """Set a Field property.""" self._props[key] = value @@ -349,7 +343,7 @@ def set_prop(self, key: str, value: Optional[PropType]) -> None: class PrimitiveSchema(Schema): """Valid primitive types are in PRIMITIVE_TYPES.""" - def __init__(self, atype: str, other_props: Optional[PropsType] = None) -> None: + def __init__(self, atype: str, other_props: PropsType | None = None) -> None: """Create a PrimitiveSchema.""" # Ensure valid ctor args if atype not in PRIMITIVE_TYPES: @@ -370,11 +364,11 @@ class EnumSchema(NamedSchema): def __init__( self, name: str, - namespace: Optional[str], + namespace: str | None, symbols: list[str], - names: Optional[Names] = None, - doc: Optional[Union[str, list[str]]] = None, - other_props: Optional[PropsType] = None, + names: Names | None = None, + doc: str | list[str] | None = None, + other_props: PropsType | None = None, ) -> None: # Ensure valid ctor args if not isinstance(symbols, list): @@ -411,7 +405,7 @@ def __init__( self, items: JsonDataType, names: Names, - other_props: Optional[PropsType] = None, + other_props: PropsType | None = None, ) -> None: # Call parent ctor Schema.__init__(self, "array", other_props) @@ -446,7 +440,7 @@ def __init__( self, values: JsonDataType, names: Names, - other_props: Optional[PropsType] = None, + other_props: PropsType | None = None, ) -> None: """Create a MapSchema object.""" # Call parent ctor @@ -483,9 +477,9 @@ def __init__( values: JsonDataType, names: Names, name: str, - namespace: Optional[str] = None, - doc: Optional[Union[str, list[str]]] = None, - other_props: Optional[PropsType] = None, + namespace: str | None = None, + doc: str | list[str] | None = None, + other_props: PropsType | None = None, ) -> None: """Create a NamedMapSchema object.""" # Call parent ctor @@ -582,8 +576,8 @@ def __init__( schemas: list[JsonDataType], names: Names, name: str, - namespace: Optional[str] = None, - doc: Optional[Union[str, list[str]]] = None, + namespace: str | None = None, + doc: str | list[str] | None = None, ): """ Initialize a new NamedUnionSchema. @@ -634,7 +628,7 @@ def make_field_objects(field_data: list[PropsType], names: Names) -> list[Field] doc = field.get("doc") if not (doc is None or isinstance(doc, (list, str))): raise SchemaParseException('"doc" must be a string, list of strings, or None') - doc = cast(Union[str, list[str], None], doc) + doc = cast(str | list[str] | None, doc) other_props = get_other_props(field, FIELD_RESERVED_PROPS) new_field = Field(atype, name, has_default, default, order, names, doc, other_props) parsed_fields[new_field.name] = field @@ -646,12 +640,12 @@ def make_field_objects(field_data: list[PropsType], names: Names) -> list[Field] def __init__( self, name: str, - namespace: Optional[str], + namespace: str | None, fields: list[PropsType], names: Names, schema_type: str = "record", - doc: Optional[Union[str, list[str]]] = None, - other_props: Optional[PropsType] = None, + doc: str | list[str] | None = None, + other_props: PropsType | None = None, ) -> None: # Ensure valid ctor args if not isinstance(fields, list): @@ -683,7 +677,7 @@ def fields(self) -> list[Field]: # # Module Methods # -def get_other_props(all_props: PropsType, reserved_props: tuple[str, ...]) -> Optional[PropsType]: +def get_other_props(all_props: PropsType, reserved_props: tuple[str, ...]) -> PropsType | None: """ Retrieve the non-reserved properties from a dictionary of properties. @@ -694,7 +688,7 @@ def get_other_props(all_props: PropsType, reserved_props: tuple[str, ...]) -> Op return None -def make_avsc_object(json_data: JsonDataType, names: Optional[Names] = None) -> Schema: +def make_avsc_object(json_data: JsonDataType, names: Names | None = None) -> Schema: """ Build Avro Schema from data parsed out of JSON string. diff --git a/schema_salad/codegen.py b/schema_salad/codegen.py index f6d2651bc..c1e6dcb97 100644 --- a/schema_salad/codegen.py +++ b/schema_salad/codegen.py @@ -3,7 +3,7 @@ import sys from collections.abc import MutableMapping, MutableSequence from io import TextIOWrapper -from typing import Any, Final, Optional, TextIO, Union +from typing import Any, Final, TextIO from urllib.parse import urlsplit from . import schema @@ -27,18 +27,18 @@ def codegen( i: list[dict[str, str]], schema_metadata: dict[str, Any], loader: Loader, - target: Optional[str] = None, - examples: Optional[str] = None, - package: Optional[str] = None, - copyright: Optional[str] = None, - spdx_copyright_text: Optional[list[str]] = None, - spdx_license_identifier: Optional[str] = None, - parser_info: Optional[str] = None, + target: str | None = None, + examples: str | None = None, + package: str | None = None, + copyright: str | None = None, + spdx_copyright_text: list[str] | None = None, + spdx_license_identifier: str | None = None, + parser_info: str | None = None, ) -> None: """Generate classes with loaders for the given Schema Salad description.""" j = schema.extend_and_specialize(i, loader) - gen: Optional[CodeGenBase] = None + gen: CodeGenBase | None = None base = schema_metadata.get("$base", schema_metadata.get("id")) # ``urlsplit`` decides whether to return an encoded result based # on the object type. To ensure the code behaves the same for Py @@ -56,7 +56,7 @@ def codegen( if lang in {"python", "cpp", "dlang"}: if target: - dest: Union[TextIOWrapper, TextIO] = open(target, mode="w", encoding="utf-8") + dest: TextIOWrapper | TextIO = open(target, mode="w", encoding="utf-8") else: dest = sys.stdout if lang == "cpp": diff --git a/schema_salad/codegen_base.py b/schema_salad/codegen_base.py index 5fd82be67..2f285c52e 100644 --- a/schema_salad/codegen_base.py +++ b/schema_salad/codegen_base.py @@ -2,63 +2,27 @@ from collections import OrderedDict from collections.abc import MutableSequence -from typing import Any, Final, Optional, Union +from typing import Any, NamedTuple -class TypeDef: # pylint: disable=too-few-public-methods +class TypeDef(NamedTuple): """Schema Salad type description.""" - __slots__: Final = [ - "name", - "init", - "is_uri", - "scoped_id", - "ref_scope", - "loader_type", - "instance_type", - "abstract", - ] - - # switch to class-style typing.NamedTuple once support for Python < 3.6 - # is dropped - def __init__( - self, # pylint: disable=too-many-arguments - name: str, - init: str, - is_uri: bool = False, - scoped_id: bool = False, - ref_scope: Optional[int] = 0, - loader_type: Optional[str] = None, - instance_type: Optional[str] = None, - abstract: bool = False, - ) -> None: - self.name: Final = name - self.init: Final = init - self.is_uri: Final = is_uri - self.scoped_id: Final = scoped_id - self.ref_scope: Final = ref_scope - self.abstract: Final = abstract - # Follow attributes used by Java but not Python. - self.loader_type: Final = loader_type - self.instance_type: Final = instance_type - - -class LazyInitDef: - """Lazy initialization logic.""" + name: str + init: str + is_uri: bool = False + scoped_id: bool = False + ref_scope: int | None = 0 + loader_type: str | None = None + instance_type: str | None = None + abstract: bool = False - __slots__: Final = ( - "name", - "init", - ) - def __init__( - self, - name: str, - init: str, - ) -> None: - """Create a LazyInitDef object.""" - self.name: Final = name - self.init: Final = init +class LazyInitDef(NamedTuple): + """Lazy initialization logic.""" + + name: str + init: str class CodeGenBase: @@ -111,9 +75,9 @@ def end_class(self, classname: str, field_names: list[str]) -> None: def type_loader( self, - type_declaration: Union[list[Any], dict[str, Any]], - container: Optional[str] = None, - no_link_check: Optional[bool] = None, + type_declaration: list[Any] | dict[str, Any], + container: str | None = None, + no_link_check: bool | None = None, ) -> TypeDef: """Parse the given type declaration and declare its components.""" raise NotImplementedError() @@ -122,9 +86,9 @@ def declare_field( self, name: str, fieldtype: TypeDef, - doc: Optional[str], + doc: str | None, optional: bool, - subscope: Optional[str], + subscope: str | None, ) -> None: """Output the code to load the given field.""" raise NotImplementedError() @@ -133,7 +97,7 @@ def declare_id_field( self, name: str, fieldtype: TypeDef, - doc: Optional[str], + doc: str | None, optional: bool, ) -> None: """Output the code to handle the given ID field.""" @@ -144,19 +108,19 @@ def uri_loader( inner: TypeDef, scoped_id: bool, vocab_term: bool, - ref_scope: Optional[int], - no_link_check: Optional[bool] = None, + ref_scope: int | None, + no_link_check: bool | None = None, ) -> TypeDef: """Construct the TypeDef for the given URI loader.""" raise NotImplementedError() def idmap_loader( - self, field: str, inner: TypeDef, map_subject: str, map_predicate: Optional[str] + self, field: str, inner: TypeDef, map_subject: str, map_predicate: str | None ) -> TypeDef: """Construct the TypeDef for the given mapped ID loader.""" raise NotImplementedError() - def typedsl_loader(self, inner: TypeDef, ref_scope: Optional[int]) -> TypeDef: + def typedsl_loader(self, inner: TypeDef, ref_scope: int | None) -> TypeDef: """Construct the TypeDef for the given DSL loader.""" raise NotImplementedError() diff --git a/schema_salad/cpp_codegen.py b/schema_salad/cpp_codegen.py index 7cf635142..754f591ca 100644 --- a/schema_salad/cpp_codegen.py +++ b/schema_salad/cpp_codegen.py @@ -21,7 +21,7 @@ import os import re -from typing import IO, Any, Optional, Union, cast +from typing import IO, Any, cast from . import _logger from .codegen_base import CodeGenBase, TypeDef @@ -585,11 +585,11 @@ def __init__( self, base: str, target: IO[str], - examples: Optional[str], + examples: str | None, package: str, - copyright: Optional[str], - spdx_copyright_text: Optional[list[str]], - spdx_license_identifier: Optional[str], + copyright: str | None, + spdx_copyright_text: list[str] | None, + spdx_license_identifier: str | None, ) -> None: """Initialize the C++ code generator.""" super().__init__() @@ -607,113 +607,79 @@ def __init__( self.unionDefinitions: dict[str, UnionDefinition] = {} self.documentRootTypes: list[ClassDefinition] = [] - def convertTypeToCpp(self, type_declaration: Union[list[Any], dict[str, Any], str]) -> str: + def convertTypeToCpp(self, type_declaration: list[Any] | dict[str, Any] | str) -> str: """Convert a Schema Salad type to a C++ type.""" if not isinstance(type_declaration, list): return self.convertTypeToCpp([type_declaration]) - if len(type_declaration) == 1: - if type_declaration[0] in ("null", "https://w3id.org/cwl/salad#null"): + if len(type_declaration) > 1: + type_declaration = list(map(self.convertTypeToCpp, type_declaration)) + type_declaration = ", ".join(type_declaration) + return f"std::variant<{type_declaration}>" + + match type_declaration[0]: + case "null" | "https://w3id.org/cwl/salad#null": return "std::monostate" - elif type_declaration[0] in ( - "string", - "http://www.w3.org/2001/XMLSchema#string", - ): + case "string" | "http://www.w3.org/2001/XMLSchema#string": return "std::string" - elif type_declaration[0] in ("int", "http://www.w3.org/2001/XMLSchema#int"): + case "int" | "http://www.w3.org/2001/XMLSchema#int": return "int32_t" - elif type_declaration[0] in ( - "long", - "http://www.w3.org/2001/XMLSchema#long", - ): + case "long" | "http://www.w3.org/2001/XMLSchema#long": return "int64_t" - elif type_declaration[0] in ( - "float", - "http://www.w3.org/2001/XMLSchema#float", - ): + case "float" | "http://www.w3.org/2001/XMLSchema#float": return "float" - elif type_declaration[0] in ( - "double", - "http://www.w3.org/2001/XMLSchema#double", - ): + case "double" | "http://www.w3.org/2001/XMLSchema#double": return "double" - elif type_declaration[0] in ( - "boolean", - "http://www.w3.org/2001/XMLSchema#boolean", - ): + case "boolean" | "http://www.w3.org/2001/XMLSchema#boolean": return "bool" - elif type_declaration[0] == "https://w3id.org/cwl/salad#Any": + case "https://w3id.org/cwl/salad#Any": return "std::any" - elif type_declaration[0] == "https://w3id.org/cwl/cwl#Expression": + case "https://w3id.org/cwl/cwl#Expression": return "cwl_expression_string" - elif type_declaration[0] in ( - "PrimitiveType", - "https://w3id.org/cwl/salad#PrimitiveType", - ): + case "PrimitiveType" | "https://w3id.org/cwl/salad#PrimitiveType": return "std::variant" - elif isinstance(type_declaration[0], dict): - if "type" in type_declaration[0] and type_declaration[0]["type"] in ( - "enum", - "https://w3id.org/cwl/salad#enum", - ): - name = type_declaration[0]["name"] - if name not in self.enumDefinitions: - self.enumDefinitions[name] = EnumDefinition( - type_declaration[0]["name"], - list(map(shortname, type_declaration[0]["symbols"])), - ) - if len(name.split("#")) != 2: - return safename(name) - (namespace, classname) = name.split("#") - return safenamespacename(namespace) + "::" + safename(classname) - elif "type" in type_declaration[0] and type_declaration[0]["type"] in ( - "array", - "https://w3id.org/cwl/salad#array", - ): - items = type_declaration[0]["items"] - if isinstance(items, list): - ts = [self.convertTypeToCpp(i) for i in items] - name = ", ".join(ts) - return f"std::vector>" - else: - i = self.convertTypeToCpp(items) - return f"std::vector<{i}>" - elif "type" in type_declaration[0] and type_declaration[0]["type"] in ( - "map", - "https://w3id.org/cwl/salad#map", - ): - values = type_declaration[0]["values"] - if isinstance(values, list): - ts = [self.convertTypeToCpp(i) for i in values] - name = ", ".join(ts) - return f"std::map>" - else: - i = self.convertTypeToCpp(values) - return f"std::map" - elif "type" in type_declaration[0] and type_declaration[0]["type"] in ( - "record", - "https://w3id.org/cwl/salad#record", - ): - n = type_declaration[0]["name"] - (namespace, classname) = split_name(n) - return safenamespacename(namespace) + "::" + safename(classname) - + case {"type": "enum" | "https://w3id.org/cwl/salad#enum"}: + name = type_declaration[0]["name"] + if name not in self.enumDefinitions: + self.enumDefinitions[name] = EnumDefinition( + type_declaration[0]["name"], + list(map(shortname, type_declaration[0]["symbols"])), + ) + if len(name.split("#")) != 2: + return safename(name) + (namespace, classname) = name.split("#") + return safenamespacename(namespace) + "::" + safename(classname) + case {"type": "array" | "https://w3id.org/cwl/salad#array", "items": list(items)}: + ts = [self.convertTypeToCpp(i) for i in items] + name = ", ".join(ts) + return f"std::vector>" + case {"type": "array" | "https://w3id.org/cwl/salad#array", "items": items}: + i = self.convertTypeToCpp(items) + return f"std::vector<{i}>" + case {"type": "map" | "https://w3id.org/cwl/salad#map", "values": list(values)}: + ts = [self.convertTypeToCpp(i) for i in values] + name = ", ".join(ts) + return f"std::map>" + case {"type": "map" | "https://w3id.org/cwl/salad#map", "values": values}: + i = self.convertTypeToCpp(values) + return f"std::map" + case {"type": "record" | "https://w3id.org/cwl/salad#record"}: + n = type_declaration[0]["name"] + (namespace, classname) = split_name(n) + return safenamespacename(namespace) + "::" + safename(classname) + case dict(): n = type_declaration[0]["type"] (namespace, classname) = split_name(n) return safenamespacename(namespace) + "::" + safename(classname) - if len(type_declaration[0].split("#")) != 2: - _logger.debug(f"// something weird2 about {type_declaration[0]}") - return cast(str, type_declaration[0]) - - (namespace, classname) = split_name(type_declaration[0]) - return safenamespacename(namespace) + "::" + safename(classname) + if len(type_declaration[0].split("#")) != 2: + _logger.debug(f"// something weird2 about {type_declaration[0]}") + return cast(str, type_declaration[0]) - type_declaration = list(map(self.convertTypeToCpp, type_declaration)) - type_declaration = ", ".join(type_declaration) - return f"std::variant<{type_declaration}>" + (namespace, classname) = split_name(type_declaration[0]) + return safenamespacename(namespace) + "::" + safename(classname) - def epilogue(self, root_loader: Optional[TypeDef]) -> None: + def epilogue(self, root_loader: TypeDef | None) -> None: """Trigger to generate the epilouge code.""" # find common namespace diff --git a/schema_salad/dlang_codegen.py b/schema_salad/dlang_codegen.py index 28f44e830..268a46e32 100644 --- a/schema_salad/dlang_codegen.py +++ b/schema_salad/dlang_codegen.py @@ -4,7 +4,7 @@ import functools import json import textwrap -from typing import IO, Any, Optional, Union, cast +from typing import IO, Any, cast from . import _logger, schema from .codegen_base import CodeGenBase, TypeDef @@ -27,10 +27,10 @@ def __init__( self, base: str, target: IO[str], - examples: Optional[str], + examples: str | None, package: str, - copyright_: Optional[str], - parser_info: Optional[str], + copyright_: str | None, + parser_info: str | None, salad_version: str, ) -> None: """Initialize the D codegen.""" @@ -148,7 +148,7 @@ def safe_name(name: str) -> str: avn = avn[5:] return avn - def to_doc_comment(self, doc: Union[None, str, list[str]]) -> str: + def to_doc_comment(self, doc: None | str | list[str]) -> str: """Return an embedded documentation comments for a given string.""" if doc is None: return "///\n" @@ -172,7 +172,7 @@ def to_doc_comment(self, doc: Union[None, str, list[str]]) -> str: def parse_record_field_type( self, type_: Any, - jsonld_pred: Union[None, str, dict[str, Any]], + jsonld_pred: None | str | dict[str, Any], parent_has_idmap: bool = False, has_default: bool = False, ) -> tuple[str, str]: @@ -205,44 +205,48 @@ def parse_record_field_type( else: annotate_str = "" - if isinstance(type_, str): - stype = shortname(type_) - if stype == "boolean": - type_str = "bool" - elif stype == "null": - type_str = "None" - else: - type_str = stype - elif isinstance(type_, list): - t_str = [ - self.parse_record_field_type(t, None, parent_has_idmap=has_idmap)[1] for t in type_ - ] - if has_default: - t_str = [t for t in t_str if t != "None"] - if len(t_str) == 1: - type_str = t_str[0] - else: - if are_dispatchable(type_, has_idmap): - t_str += ["Any"] - union_types = ", ".join(t_str) - type_str = f"Union!({union_types})" - elif shortname(type_["type"]) == "array": - item_type = self.parse_record_field_type( - type_["items"], None, parent_has_idmap=has_idmap - )[1] - type_str = f"{item_type}[]" - elif shortname(type_["type"]) == "record": - return annotate_str, shortname(type_.get("name", "record")) - elif shortname(type_["type"]) == "enum": - return annotate_str, "'not yet implemented'" - elif shortname(type_["type"]) == "map": - value_type = self.parse_record_field_type( - type_["values"], None, parent_has_idmap=has_idmap, has_default=True - )[1] - type_str = f"{value_type}[string]" + match type_: + case str(): + match shortname(type_): + case "boolean": + type_str = "bool" + case "null": + type_str = "None" + case str(stype): + type_str = stype + case list(): + t_str = [ + self.parse_record_field_type(t, None, parent_has_idmap=has_idmap)[1] + for t in type_ + ] + if has_default: + t_str = [t for t in t_str if t != "None"] + if len(t_str) == 1: + type_str = t_str[0] + else: + if are_dispatchable(type_, has_idmap): + t_str += ["Any"] + union_types = ", ".join(t_str) + type_str = f"Union!({union_types})" + case dict(): + match shortname(type_["type"]): + case "array": + item_type = self.parse_record_field_type( + type_["items"], None, parent_has_idmap=has_idmap + )[1] + type_str = f"{item_type}[]" + case "record": + return annotate_str, shortname(type_.get("name", "record")) + case "enum": + return annotate_str, "'not yet implemented'" + case "map": + value_type = self.parse_record_field_type( + type_["values"], None, parent_has_idmap=has_idmap, has_default=True + )[1] + type_str = f"{value_type}[string]" return annotate_str, type_str - def parse_record_field(self, field: dict[str, Any], parent_name: Optional[str] = None) -> str: + def parse_record_field(self, field: dict[str, Any], parent_name: str | None = None) -> str: """Return a declaration string for a given record field.""" fname = shortname(field["name"]) + "_" jsonld_pred = field.get("jsonldPredicate", None) diff --git a/schema_salad/dotnet_codegen.py b/schema_salad/dotnet_codegen.py index 471e27ada..40bfc0031 100644 --- a/schema_salad/dotnet_codegen.py +++ b/schema_salad/dotnet_codegen.py @@ -7,7 +7,7 @@ from importlib.resources import files from io import StringIO from pathlib import Path -from typing import Any, Optional, Union +from typing import Any, cast from xml.sax.saxutils import escape # nosec from . import _logger, schema @@ -18,7 +18,7 @@ from .utils import Traversable -def doc_to_doc_string(doc: Optional[str], indent_level: int = 0) -> str: +def doc_to_doc_string(doc: str | None, indent_level: int = 0) -> str: """Generate a documentation string from a schema salad doc field.""" lead = "" + " " * indent_level + "/// " if doc: @@ -100,9 +100,7 @@ def doc_to_doc_string(doc: Optional[str], indent_level: int = 0) -> str: class DotNetCodeGen(CodeGenBase): """Generation of TypeScript code for a given Schema Salad definition.""" - def __init__( - self, base: str, examples: Optional[str], target: Optional[str], package: str - ) -> None: + def __init__(self, base: str, examples: str | None, target: str | None, package: str) -> None: """Initialize the TypeScript codegen.""" super().__init__() self.target_dir = Path(target or ".").resolve() @@ -389,31 +387,32 @@ def end_class(self, classname: str, field_names: list[str]) -> None: def type_loader( self, - type_declaration: Union[list[Any], dict[str, Any], str], - container: Optional[str] = None, - no_link_check: Optional[bool] = None, + type_declaration: list[Any] | dict[str, Any] | str, + container: str | None = None, + no_link_check: bool | None = None, ) -> TypeDef: """Parse the given type declaration and declare its components.""" - if isinstance(type_declaration, MutableSequence): - sub_types = [self.type_loader(i) for i in type_declaration] - sub_names: list[str] = list(dict.fromkeys([i.name for i in sub_types])) - sub_instance_types: list[str] = list( - dict.fromkeys([i.instance_type for i in sub_types if i.instance_type is not None]) - ) - return self.declare_type( - TypeDef( - name="union_of_{}".format("_or_".join(sub_names)), - init="new UnionLoader(new List {{ {} }})".format(", ".join(sub_names)), - instance_type="OneOf<" + ", ".join(sub_instance_types) + ">", - loader_type="ILoader", + match type_declaration: + case MutableSequence(): + sub_types = [self.type_loader(i) for i in type_declaration] + sub_names: list[str] = list(dict.fromkeys([i.name for i in sub_types])) + sub_instance_types: list[str] = list( + dict.fromkeys( + [i.instance_type for i in sub_types if i.instance_type is not None] + ) ) - ) - if isinstance(type_declaration, MutableMapping): - if type_declaration["type"] in ( - "array", - "https://w3id.org/cwl/salad#array", - ): - i = self.type_loader(type_declaration["items"]) + return self.declare_type( + TypeDef( + name="union_of_{}".format("_or_".join(sub_names)), + init="new UnionLoader(new List {{ {} }})".format( + ", ".join(sub_names) + ), + instance_type="OneOf<" + ", ".join(sub_instance_types) + ">", + loader_type="ILoader", + ) + ) + case {"type": "array" | "https://w3id.org/cwl/salad#array", "items": items}: + i = self.type_loader(items) return self.declare_type( TypeDef( instance_type=f"List<{i.instance_type}>", @@ -422,11 +421,8 @@ def type_loader( init=f"new ArrayLoader<{i.instance_type}>({i.name})", ) ) - if type_declaration["type"] in ( - "map", - "https://w3id.org/cwl/salad#map", - ): - i = self.type_loader(type_declaration["values"]) + case {"type": "map" | "https://w3id.org/cwl/salad#map", "values": values}: + i = self.type_loader(values) return self.declare_type( TypeDef( instance_type=f"Dictionary", @@ -442,33 +438,35 @@ def type_loader( ), ) ) - if type_declaration["type"] in ("enum", "https://w3id.org/cwl/salad#enum"): - return self.type_loader_enum(type_declaration) - if type_declaration["type"] in ( - "record", - "https://w3id.org/cwl/salad#record", - ): + case {"type": "enum" | "https://w3id.org/cwl/salad#enum"}: + return self.type_loader_enum(type_declaration) # type: ignore[arg-type] + case { + "type": "record" | "https://w3id.org/cwl/salad#record", + "name": str(name), + **rest, + }: return self.declare_type( TypeDef( - instance_type=self.safe_name(type_declaration["name"]), - name=self.safe_name(type_declaration["name"]) + "Loader", + instance_type=self.safe_name(name), + name=self.safe_name(name) + "Loader", init="new RecordLoader<{}>({}, {})".format( - self.safe_name(type_declaration["name"]), + self.safe_name(name), ( f"'{container}'" if container is not None else self.to_dotnet(None) ), # noqa: B907 self.to_dotnet(no_link_check), ), - loader_type="ILoader<{}>".format(self.safe_name(type_declaration["name"])), - abstract=type_declaration.get("abstract", False), + loader_type=f"ILoader<{self.safe_name(name)}>", + abstract=cast(bool, rest.get("abstract", False)), ) ) - if type_declaration["type"] in ( - "union", - "https://w3id.org/cwl/salad#union", - ): + case { + "type": "union" | "https://w3id.org/cwl/salad#union", + "name": name, + "names": names, + }: # Declare the named loader to handle recursive union definitions - loader_name = self.safe_name(type_declaration["name"]) + "Loader" + loader_name = self.safe_name(name) + "Loader" loader_type = TypeDef( name=loader_name, init="new UnionLoader(new List())", @@ -477,7 +475,7 @@ def type_loader( ) self.declare_type(loader_type) # Parse inner types - sub_types = [self.type_loader(i) for i in type_declaration["names"]] + sub_types = [self.type_loader(i) for i in names] # Register lazy initialization for the loader self.add_lazy_init( LazyInitDef( @@ -488,19 +486,23 @@ def type_loader( ) ) return loader_type - raise SchemaException("wft {}".format(type_declaration["type"])) - if type_declaration in prims: - return prims[type_declaration] - if type_declaration in ("Expression", "https://w3id.org/cwl/cwl#Expression"): - return self.declare_type( - TypeDef( - name=self.safe_name(type_declaration) + "Loader", - init="new ExpressionLoader()", - loader_type="ILoader", - instance_type="string", + case {"type": type_decl}: + raise SchemaException(f"wft {type_decl}") + case str(decl) if decl in prims: + return prims[decl] + case "Expression" | "https://w3id.org/cwl/cwl#Expression" as decl: + return self.declare_type( + TypeDef( + name=self.safe_name(decl) + "Loader", + init="new ExpressionLoader()", + loader_type="ILoader", + instance_type="string", + ) ) - ) - return self.collected_types[self.safe_name(type_declaration) + "Loader"] + case str(decl): + return self.collected_types[self.safe_name(decl) + "Loader"] + case _ as decl: + raise SchemaException(f"wft {decl}") def type_loader_enum(self, type_declaration: dict[str, Any]) -> TypeDef: """Build an enum type loader for the given declaration.""" @@ -604,9 +606,9 @@ def declare_field( self, name: str, fieldtype: TypeDef, - doc: Optional[str], + doc: str | None, optional: bool, - subscope: Optional[str], + subscope: str | None, ) -> None: """Output the code to load the given field.""" if self.current_class_is_abstract: @@ -787,7 +789,7 @@ def declare_id_field( self, name: str, fieldtype: TypeDef, - doc: Optional[str], + doc: str | None, optional: bool, ) -> None: """Output the code to handle the given ID field.""" @@ -839,8 +841,8 @@ def uri_loader( inner: TypeDef, scoped_id: bool, vocab_term: bool, - ref_scope: Optional[int], - no_link_check: Optional[bool] = None, + ref_scope: int | None, + no_link_check: bool | None = None, ) -> TypeDef: """Construct the TypeDef for the given URI loader.""" instance_type = inner.instance_type or "object" @@ -863,7 +865,7 @@ def uri_loader( ) def idmap_loader( - self, field: str, inner: TypeDef, map_subject: str, map_predicate: Optional[str] + self, field: str, inner: TypeDef, map_subject: str, map_predicate: str | None ) -> TypeDef: """Construct the TypeDef for the given mapped ID loader.""" instance_type = inner.instance_type or "object" @@ -878,7 +880,7 @@ def idmap_loader( ) ) - def typedsl_loader(self, inner: TypeDef, ref_scope: Optional[int]) -> TypeDef: + def typedsl_loader(self, inner: TypeDef, ref_scope: int | None) -> TypeDef: """Construct the TypeDef for the given DSL loader.""" instance_type = inner.instance_type or "object" return self.declare_type( diff --git a/schema_salad/exceptions.py b/schema_salad/exceptions.py index 4bd13c744..e4d7574ad 100644 --- a/schema_salad/exceptions.py +++ b/schema_salad/exceptions.py @@ -1,7 +1,7 @@ """Shared Exception classes.""" from collections.abc import Sequence -from typing import Final, Optional, Union +from typing import Final from .sourceline import SourceLine, reflow_all, strip_duplicated_lineno @@ -22,17 +22,17 @@ class SchemaSaladException(Exception): def __init__( self, msg: str, - sl: Optional[SourceLine] = None, - children: Optional[Sequence["SchemaSaladException"]] = None, + sl: SourceLine | None = None, + children: Sequence["SchemaSaladException"] | None = None, bullet_for_children: str = "", - detailed_message: Optional[str] = None, + detailed_message: str | None = None, ) -> None: super().__init__(msg) self.message: Final = self.args[0] self.detailed_message: Final = detailed_message - self.file: Optional[str] = None - self.start: Optional[tuple[int, int]] = None - self.end: Optional[tuple[int, int]] = None + self.file: str | None = None + self.start: tuple[int, int] | None = None + self.end: tuple[int, int] | None = None self.is_warning: bool = False @@ -67,7 +67,7 @@ def as_warning(self) -> "SchemaSaladException": c.as_warning() return self - def with_sourceline(self, sl: Optional[SourceLine]) -> "SchemaSaladException": + def with_sourceline(self, sl: SourceLine | None) -> "SchemaSaladException": """Use the provided SourceLine to set the causal location.""" if sl and sl.file(): self.file = sl.file() @@ -90,8 +90,8 @@ def leaves(self) -> list["SchemaSaladException"]: def prefix(self) -> str: pre: str = "" if self.file: - linecol0: Union[int, str] = "" - linecol1: Union[int, str] = "" + linecol0: int | str = "" + linecol1: int | str = "" if self.start: linecol0, linecol1 = self.start pre = f"{self.file}:{linecol0}:{linecol1}: " diff --git a/schema_salad/fetcher.py b/schema_salad/fetcher.py index 6ec1e49c9..0cfd66bc5 100644 --- a/schema_salad/fetcher.py +++ b/schema_salad/fetcher.py @@ -7,7 +7,7 @@ import urllib.parse import urllib.request from abc import ABC, abstractmethod -from typing import Final, Optional +from typing import Final import requests from mypy_extensions import mypyc_attr @@ -24,7 +24,7 @@ class Fetcher(ABC): """Fetch resources from URIs.""" @abstractmethod - def fetch_text(self, url: str, content_types: Optional[list[str]] = None) -> str: + def fetch_text(self, url: str, content_types: list[str] | None = None) -> str: """Retrieve the given resource as a string.""" @abstractmethod @@ -58,13 +58,13 @@ class DefaultFetcher(MemoryCachingFetcher): def __init__( self, cache: CacheType, - session: Optional[requests.sessions.Session], + session: requests.sessions.Session | None, ) -> None: """Create a DefaultFetcher object.""" super().__init__(cache) self.session: Final = session - def fetch_text(self, url: str, content_types: Optional[list[str]] = None) -> str: + def fetch_text(self, url: str, content_types: list[str] | None = None) -> str: """Retrieve the given resource as a string.""" result: Final = self.cache.get(url, None) if isinstance(result, str): @@ -74,44 +74,46 @@ def fetch_text(self, url: str, content_types: Optional[list[str]] = None) -> str scheme: Final = split.scheme path = split.path - if scheme in ["http", "https"] and self.session is not None: - try: - headers = {} - if content_types: - headers["Accept"] = ", ".join(content_types) + ", */*;q=0.8" - resp: Final = self.session.get(url, headers=headers) - resp.raise_for_status() - except Exception as e: - raise ValidationException(f"Error fetching {url}: {e}") from e - if content_types and "content-type" in resp.headers: - received_content_types = set( - resp.headers["content-type"].split(";")[:1][0].split(",") - ) - if set(content_types).isdisjoint(received_content_types): - _logger.warning( - "While fetching %s, got content-type of %r. Expected one of %s.", - url, - resp.headers["content-type"].split(";")[:1][0], - content_types, + match scheme: + case "http" | "https" if self.session is not None: + try: + headers = {} + if content_types: + headers["Accept"] = ", ".join(content_types) + ", */*;q=0.8" + resp: Final = self.session.get(url, headers=headers) + resp.raise_for_status() + except Exception as e: + raise ValidationException(f"Error fetching {url}: {e}") from e + if content_types and "content-type" in resp.headers: + received_content_types = set( + resp.headers["content-type"].split(";")[:1][0].split(",") ) - return resp.text - if scheme == "file": - try: - # On Windows, url.path will be /drive:/path ; on Unix systems, - # /path. As we want drive:/path instead of /drive:/path on Windows, - # remove the leading /. - if os.path.isabs( - path[1:] - ): # checking if pathis valid after removing front / or not - path = path[1:] - with open(urllib.request.url2pathname(str(path)), encoding="utf-8") as fp: - return str(fp.read()) - - except OSError as err: - if err.filename == path: - raise ValidationException(str(err)) from err - raise ValidationException(f"Error reading {url}: {err}") from err - raise ValidationException(f"Unsupported scheme in url: {url}") + if set(content_types).isdisjoint(received_content_types): + _logger.warning( + "While fetching %s, got content-type of %r. Expected one of %s.", + url, + resp.headers["content-type"].split(";")[:1][0], + content_types, + ) + return resp.text + case "file": + try: + # On Windows, url.path will be /drive:/path ; on Unix systems, + # /path. As we want drive:/path instead of /drive:/path on Windows, + # remove the leading /. + if os.path.isabs( + path[1:] + ): # checking if pathis valid after removing front / or not + path = path[1:] + with open(urllib.request.url2pathname(str(path)), encoding="utf-8") as fp: + return str(fp.read()) + + except OSError as err: + if err.filename == path: + raise ValidationException(str(err)) from err + raise ValidationException(f"Error reading {url}: {err}") from err + case _: + raise ValidationException(f"Unsupported scheme in url: {url}") def check_exists(self, url: str) -> bool: if url in self.cache: @@ -121,21 +123,23 @@ def check_exists(self, url: str) -> bool: scheme: Final = split.scheme path: Final = split.path - if scheme in ["http", "https"]: - if self.session is None: - raise ValidationException(f"Can't check {scheme} URL, session is None") - try: - resp: Final = self.session.head(url, allow_redirects=True) - resp.raise_for_status() - except Exception: - return False - self.cache[url] = True - return True - if scheme == "file": - return os.path.exists(urllib.request.url2pathname(str(path))) - if scheme == "mailto": - return True - raise ValidationException(f"Unsupported scheme {scheme!r} in url: {url}") + match scheme: + case "http" | "https": + if self.session is None: + raise ValidationException(f"Can't check {scheme} URL, session is None") + try: + resp: Final = self.session.head(url, allow_redirects=True) + resp.raise_for_status() + except Exception: + return False + self.cache[url] = True + return True + case "file": + return os.path.exists(urllib.request.url2pathname(str(path))) + case "mailto": + return True + case _ as sch: + raise ValidationException(f"Unsupported scheme {sch!r} in url: {url}") def urljoin(self, base_url: str, url: str) -> str: if url.startswith("_:"): diff --git a/schema_salad/java_codegen.py b/schema_salad/java_codegen.py index eef108642..207f83a58 100644 --- a/schema_salad/java_codegen.py +++ b/schema_salad/java_codegen.py @@ -8,7 +8,7 @@ from importlib.resources import files from io import StringIO from pathlib import Path -from typing import Any, Final, Optional, Union +from typing import Any, Final from . import _logger, schema from .codegen_base import CodeGenBase, LazyInitDef, TypeDef @@ -32,7 +32,7 @@ def _ensure_directory_and_write(path: Path, contents: str) -> None: f.write(contents) -def _doc_to_doc_string(doc: Optional[str], indent_level: int = 0) -> str: +def _doc_to_doc_string(doc: str | None, indent_level: int = 0) -> str: lead: Final = " " + " " * indent_level + "* " * indent_level if doc: doc_str = f"{lead}
\n" @@ -122,10 +122,10 @@ class JavaCodeGen(CodeGenBase): def __init__( self, base: str, - target: Optional[str], - examples: Optional[str], + target: str | None, + examples: str | None, package: str, - copyright: Optional[str], + copyright: str | None, ) -> None: super().__init__() self.base_uri: Final = base @@ -392,74 +392,71 @@ def end_class(self, classname: str, field_names: list[str]) -> None: def type_loader( self, - type_declaration: Union[list[Any], dict[str, Any], str], - container: Optional[str] = None, - no_link_check: Optional[bool] = None, + type_declaration: list[Any] | dict[str, Any] | str, + container: str | None = None, + no_link_check: bool | None = None, ) -> TypeDef: """Parse the given type declaration and declare its components.""" - if isinstance(type_declaration, MutableSequence): - sub = [self.type_loader(i) for i in type_declaration] - if len(sub) < 2: - return sub[0] - - if len(sub) == 2: - type_1 = sub[0] - type_2 = sub[1] - type_1_name = type_1.name - type_2_name = type_2.name - if type_1_name == "NullInstance" or type_2_name == "NullInstance": - non_null_type = type_1 if type_1.name != "NullInstance" else type_2 - return self.declare_type( - TypeDef( - instance_type="java.util.Optional<{}>".format( - non_null_type.instance_type - ), - init=f"new OptionalLoader({non_null_type.name})", - name=f"optional_{non_null_type.name}", - loader_type="Loader>".format( - non_null_type.instance_type - ), + match type_declaration: + case MutableSequence() as decl: + sub = [self.type_loader(i) for i in decl] + if len(sub) < 2: + return sub[0] + + if len(sub) == 2: + type_1 = sub[0] + type_2 = sub[1] + type_1_name = type_1.name + type_2_name = type_2.name + if type_1_name == "NullInstance" or type_2_name == "NullInstance": + non_null_type = type_1 if type_1.name != "NullInstance" else type_2 + return self.declare_type( + TypeDef( + instance_type="java.util.Optional<{}>".format( + non_null_type.instance_type + ), + init=f"new OptionalLoader({non_null_type.name})", + name=f"optional_{non_null_type.name}", + loader_type="Loader>".format( + non_null_type.instance_type + ), + ) ) - ) - if ( - type_1_name == f"array_of_{type_2_name}" - or type_2_name == f"array_of_{type_1_name}" - ) and USE_ONE_OR_LIST_OF_TYPES: - if type_1_name == f"array_of_{type_2_name}": - single_type = type_2 - array_type = type_1 - else: - single_type = type_1 - array_type = type_2 - fqclass = f"{self.package}.{single_type.instance_type}" - return self.declare_type( - TypeDef( - instance_type=f"{self.package}.utils.OneOrListOf<{fqclass}>", - init="new OneOrListOfLoader<{}>({}, {})".format( - fqclass, single_type.name, array_type.name - ), - name=f"one_or_array_of_{single_type.name}", - loader_type="Loader<{}.utils.OneOrListOf<{}>>".format( - self.package, fqclass - ), + if ( + type_1_name == f"array_of_{type_2_name}" + or type_2_name == f"array_of_{type_1_name}" + ) and USE_ONE_OR_LIST_OF_TYPES: + if type_1_name == f"array_of_{type_2_name}": + single_type = type_2 + array_type = type_1 + else: + single_type = type_1 + array_type = type_2 + fqclass = f"{self.package}.{single_type.instance_type}" + return self.declare_type( + TypeDef( + instance_type=f"{self.package}.utils.OneOrListOf<{fqclass}>", + init="new OneOrListOfLoader<{}>({}, {})".format( + fqclass, single_type.name, array_type.name + ), + name=f"one_or_array_of_{single_type.name}", + loader_type="Loader<{}.utils.OneOrListOf<{}>>".format( + self.package, fqclass + ), + ) ) + return self.declare_type( + TypeDef( + instance_type="Object", + init="new UnionLoader(new Loader[] {{ {} }})".format( + ", ".join(s.name for s in sub) + ), + name="union_of_{}".format("_or_".join(s.name for s in sub)), + loader_type="Loader", ) - return self.declare_type( - TypeDef( - instance_type="Object", - init="new UnionLoader(new Loader[] {{ {} }})".format( - ", ".join(s.name for s in sub) - ), - name="union_of_{}".format("_or_".join(s.name for s in sub)), - loader_type="Loader", ) - ) - if isinstance(type_declaration, MutableMapping): - if type_declaration["type"] in ( - "array", - "https://w3id.org/cwl/salad#array", - ): - i = self.type_loader(type_declaration["items"]) + case {"type": "array" | "https://w3id.org/cwl/salad#array", "items": items}: + i = self.type_loader(items) instance_type = ( "java.util.List" if i.instance_type == "String" @@ -475,11 +472,8 @@ def type_loader( loader_type=f"Loader>", ) ) - if type_declaration["type"] in ( - "map", - "https://w3id.org/cwl/salad#map", - ): - i = self.type_loader(type_declaration["values"]) + case {"type": "map" | "https://w3id.org/cwl/salad#map", "values": values}: + i = self.type_loader(values) return self.declare_type( TypeDef( # special doesn't work out with subclassing, gotta be more clever @@ -496,18 +490,21 @@ def type_loader( loader_type=f"Loader>", ) ) - if type_declaration["type"] in ("enum", "https://w3id.org/cwl/salad#enum"): - return self.type_loader_enum(type_declaration) - if type_declaration["type"] in ( - "record", - "https://w3id.org/cwl/salad#record", - ): - is_abstract = type_declaration.get("abstract", False) - fqclass = "{}.{}".format(self.package, self.safe_name(type_declaration["name"])) + case { + "type": "enum" | "https://w3id.org/cwl/salad#enum", + }: + return self.type_loader_enum(type_declaration) # type: ignore[arg-type] + case { + "type": "record" | "https://w3id.org/cwl/salad#record", + "name": name, + **rest, + }: + is_abstract = rest.get("abstract", False) + fqclass = f"{self.package}.{self.safe_name(name)}" return self.declare_type( TypeDef( - instance_type=self.safe_name(type_declaration["name"]), - name=self.safe_name(type_declaration["name"]), + instance_type=self.safe_name(name), + name=self.safe_name(name), init="new RecordLoader<{clazz}>({clazz}{ext}.class, " "{container}, {no_link_check})".format( clazz=fqclass, @@ -522,12 +519,13 @@ def type_loader( loader_type=f"Loader<{fqclass}>", ) ) - if type_declaration["type"] in ( - "union", - "https://w3id.org/cwl/salad#union", - ): + case { + "type": "union" | "https://w3id.org/cwl/salad#union", + "name": name, + "names": names, + }: # Declare the named loader to handle recursive union definitions - loader_name = self.safe_name(type_declaration["name"]) + loader_name = self.safe_name(name) loader_type = TypeDef( instance_type="Object", init="new UnionLoader(new Loader[] {})", @@ -536,7 +534,7 @@ def type_loader( ) self.declare_type(loader_type) # Parse inner types - sub = [self.type_loader(i) for i in type_declaration["names"]] + sub = [self.type_loader(i) for i in names] if len(sub) == 2: type_1 = sub[0] @@ -590,19 +588,20 @@ def type_loader( ) ) return loader_type - raise SchemaException("wft {}".format(type_declaration["type"])) - if type_declaration in prims: - return prims[type_declaration] - if type_declaration in ("Expression", "https://w3id.org/cwl/cwl#Expression"): - return self.declare_type( - TypeDef( - name=self.safe_name(type_declaration) + "Loader", - init="new ExpressionLoader()", - loader_type="Loader", - instance_type="String", + case {"type": type_decl}: + raise SchemaException(f"wft {type_decl}") + case str() as decl if decl in prims: + return prims[decl] + case "Expression" | "https://w3id.org/cwl/cwl#Expression" as decl: + return self.declare_type( + TypeDef( + name=self.safe_name(decl) + "Loader", + init="new ExpressionLoader()", + loader_type="Loader", + instance_type="String", + ) ) - ) - return self.collected_types[self.safe_name(type_declaration)] + return self.collected_types[self.safe_name(type_declaration)] # type: ignore[arg-type] def type_loader_enum(self, type_declaration: dict[str, Any]) -> TypeDef: """Build an enum type loader for the given declaration.""" @@ -687,9 +686,9 @@ def declare_field( self, name: str, fieldtype: TypeDef, - doc: Optional[str], + doc: str | None, optional: bool, - subscope: Optional[str], + subscope: str | None, ) -> None: fieldname = name property_name = self.property_name(fieldname) @@ -789,7 +788,7 @@ def declare_id_field( self, name: str, fieldtype: TypeDef, - doc: Optional[str], + doc: str | None, optional: bool, ) -> None: if self.current_class_is_abstract: @@ -833,8 +832,8 @@ def uri_loader( inner: TypeDef, scoped_id: bool, vocab_term: bool, - ref_scope: Optional[int], - no_link_check: Optional[bool] = None, + ref_scope: int | None, + no_link_check: bool | None = None, ) -> TypeDef: instance_type = inner.instance_type or "Object" return self.declare_type( @@ -856,7 +855,7 @@ def uri_loader( ) def idmap_loader( - self, field: str, inner: TypeDef, map_subject: str, map_predicate: Optional[str] + self, field: str, inner: TypeDef, map_subject: str, map_predicate: str | None ) -> TypeDef: instance_type: Final = inner.instance_type or "Object" return self.declare_type( @@ -870,7 +869,7 @@ def idmap_loader( ) ) - def typedsl_loader(self, inner: TypeDef, ref_scope: Union[int, None]) -> TypeDef: + def typedsl_loader(self, inner: TypeDef, ref_scope: int | None) -> TypeDef: """Construct the TypeDef for the given DSL loader.""" instance_type = inner.instance_type or "Object" return self.declare_type( diff --git a/schema_salad/jsonld_context.py b/schema_salad/jsonld_context.py index 28f308f8d..c58dc30bf 100755 --- a/schema_salad/jsonld_context.py +++ b/schema_salad/jsonld_context.py @@ -1,7 +1,7 @@ import logging import unicodedata from collections.abc import Iterable, MutableMapping, MutableSequence -from typing import Any, Final, Optional, Union, cast +from typing import Any, Final, cast from urllib.parse import urldefrag, urlsplit import rdflib @@ -17,16 +17,16 @@ def pred( - datatype: MutableMapping[str, Union[dict[str, str], str]], - field: Optional[dict[str, Any]], + datatype: MutableMapping[str, dict[str, str] | str], + field: dict[str, Any] | None, name: str, context: ContextType, defaultBase: str, namespaces: dict[str, rdflib.namespace.Namespace], -) -> Union[dict[str, Optional[str]], str]: +) -> dict[str, str | None] | str: split: Final = urlsplit(name) - vee: Optional[str] = None + vee: str | None = None if split.scheme != "": vee = name @@ -36,7 +36,7 @@ def pred( vee = str(namespaces[ns[0:-1]][ln]) _logger.debug("name, v %s %s", name, vee) - v: Optional[Union[dict[str, Optional[str]], str]] = None + v: dict[str, str | None] | str | None = None if field is not None and "jsonldPredicate" in field: if isinstance(field["jsonldPredicate"], MutableMapping): @@ -122,7 +122,7 @@ def process_type( _logger.debug("Processing field %s", i) - v: Union[dict[Any, Any], str, None] = pred( + v: dict[Any, Any] | str | None = pred( t, i, fieldname, context, defaultPrefix, namespaces ) @@ -203,7 +203,7 @@ def salad_to_jsonld_context( return (context, g) -def fix_jsonld_ids(obj: Union[CommentedMap, float, str, CommentedSeq], ids: list[str]) -> None: +def fix_jsonld_ids(obj: CommentedMap | float | str | CommentedSeq, ids: list[str]) -> None: """Add missing identity entries.""" if isinstance(obj, MutableMapping): for i in ids: @@ -217,10 +217,10 @@ def fix_jsonld_ids(obj: Union[CommentedMap, float, str, CommentedSeq], ids: list def makerdf( - workflow: Optional[str], - wf: Union[CommentedMap, float, str, CommentedSeq], + workflow: str | None, + wf: CommentedMap | float | str | CommentedSeq, ctx: ContextType, - graph: Optional[Graph] = None, + graph: Graph | None = None, ) -> Graph: prefixes: Final = {} idfields: Final = [] diff --git a/schema_salad/main.py b/schema_salad/main.py index 42a9dc93f..20236f788 100644 --- a/schema_salad/main.py +++ b/schema_salad/main.py @@ -5,7 +5,7 @@ import os import sys from collections.abc import Mapping, MutableSequence -from typing import Any, Final, Optional, Union, cast +from typing import Any, Final, cast from urllib.parse import urlparse from rdflib import __version__ as rdflib_version @@ -30,7 +30,7 @@ def printrdf( workflow: str, - wf: Union[CommentedMap, CommentedSeq], + wf: CommentedMap | CommentedSeq, ctx: dict[str, Any], sr: str, ) -> None: @@ -226,7 +226,7 @@ def arg_parser() -> argparse.ArgumentParser: return parser -def main(argsl: Optional[list[str]] = None) -> int: +def main(argsl: list[str] | None = None) -> int: """Run the schema-salad-tool.""" if argsl is None: argsl = sys.argv[1:] diff --git a/schema_salad/makedoc.py b/schema_salad/makedoc.py index 348f9957d..25970baf0 100644 --- a/schema_salad/makedoc.py +++ b/schema_salad/makedoc.py @@ -7,14 +7,14 @@ import sys from collections.abc import MutableMapping, MutableSequence from io import StringIO, TextIOWrapper -from typing import IO, Any, Final, Literal, Optional, Union, cast +from typing import IO, Any, Final, Literal, cast from urllib.parse import urldefrag from mistune import create_markdown from mistune.markdown import Markdown from mistune.renderers.html import HTMLRenderer -from .exceptions import SchemaSaladException, ValidationException +from .exceptions import ValidationException from .schema import avro_field_name, extend_and_specialize, get_metaschema from .utils import add_dictlist, aslist from .validate import avro_type_name @@ -99,7 +99,7 @@ def block_html(self, html: str) -> str: """Don't escape characters nor wrap predefined HTML within paragraph tags.""" return html + "\n" - def block_code(self, code: str, info: Optional[str] = None) -> str: + def block_code(self, code: str, info: str | None = None) -> str: """Don't escape quotation marks.""" text = "
 str:
     return "\n".join(mdlines)
 
 
-def fix_doc(doc: Union[list[str], str]) -> str:
+def fix_doc(doc: list[str] | str) -> str:
     """Concatenate doc strings, replacing email addresses with mailto links."""
     docstr: Final = "".join(doc) if isinstance(doc, MutableSequence) else doc
     return "\n".join(
@@ -258,7 +258,7 @@ def __init__(
         self.docAfter: Final[dict[str, list[str]]] = {}
         self.rendered: Final[set[str]] = set()
         self.redirects: Final = redirects
-        self.title: Optional[str] = None
+        self.title: str | None = None
         self.primitiveType: Final = primitiveType
 
         for t in j:
@@ -289,7 +289,7 @@ def __init__(
             try:
                 if entry["type"] == "record":
                     self.record_refs[entry["name"]] = []
-                    fields: Union[str, list[dict[str, str]]] = entry.get("fields", [])
+                    fields: str | list[dict[str, str]] = entry.get("fields", [])
                     if isinstance(fields, str):
                         raise KeyError("record fields must be a list of mappings")
                     else:
@@ -326,19 +326,19 @@ def typefmt(
         tp: Any,
         redirects: dict[str, str],
         nbsp: bool = False,
-        jsonldPredicate: Optional[Union[dict[str, str], str]] = None,
+        jsonldPredicate: dict[str, str] | str | None = None,
     ) -> str:
-        if isinstance(tp, MutableSequence):
-            if nbsp and len(tp) <= 3:
-                return " | ".join(
-                    [self.typefmt(n, redirects, jsonldPredicate=jsonldPredicate) for n in tp]
+        match tp:
+            case MutableSequence() as seq:
+                if nbsp and len(seq) <= 3:
+                    return " | ".join(
+                        [self.typefmt(n, redirects, jsonldPredicate=jsonldPredicate) for n in seq]
+                    )
+                return " | ".join(
+                    [self.typefmt(n, redirects, jsonldPredicate=jsonldPredicate) for n in seq]
                 )
-            return " | ".join(
-                [self.typefmt(n, redirects, jsonldPredicate=jsonldPredicate) for n in tp]
-            )
-        if isinstance(tp, MutableMapping):
-            if tp["type"] == "https://w3id.org/cwl/salad#array":
-                ar = "array<{}>".format(self.typefmt(tp["items"], redirects, nbsp=True))
+            case {"type": "https://w3id.org/cwl/salad#array", "items": items}:
+                ar = f"array<{self.typefmt(items, redirects, nbsp=True)}>"
                 if isinstance(jsonldPredicate, dict) and "mapSubject" in jsonldPredicate:
                     if "mapPredicate" in jsonldPredicate:
                         ar += " | "
@@ -350,7 +350,7 @@ def typefmt(
                             ", {} | {}>".format(
                                 jsonldPredicate["mapSubject"],
                                 jsonldPredicate["mapPredicate"],
-                                self.typefmt(tp["items"], redirects),
+                                self.typefmt(items, redirects),
                             )
                         )
                     else:
@@ -359,37 +359,36 @@ def typefmt(
                             ar += "
" ar += "map<{}, {}>".format( jsonldPredicate["mapSubject"], - self.typefmt(tp["items"], redirects), + self.typefmt(items, redirects), ) return ar - if tp["type"] in ( - "https://w3id.org/cwl/salad#record", - "https://w3id.org/cwl/salad#enum", - ): - frg: Final = vocab_type_name(tp["name"]) - if tp["name"] in redirects: - return """{}""".format(redirects[tp["name"]], frg) - if tp["name"] in self.typemap: - return f"""{frg}""" - if tp["type"] == "https://w3id.org/cwl/salad#enum" and len(tp["symbols"]) == 1: - return "constant value {}".format( - avro_field_name(tp["symbols"][0]) - ) - return frg - if isinstance(tp["type"], MutableMapping): - return self.typefmt(tp["type"], redirects) - else: - if str(tp) in redirects: + case {"type": "https://w3id.org/cwl/salad#record", "name": name}: + frg1: Final = vocab_type_name(name) + if name in redirects: + return f"""{frg1}""" + if name in self.typemap: + return f"""{frg1}""" + return frg1 + case {"type": "https://w3id.org/cwl/salad#enum", "name": name, "symbols": symbols}: + frg2: Final = vocab_type_name(name) + if name in redirects: + return f"""{frg2}""" + if name in self.typemap: + return f"""{frg2}""" + if len(symbols) == 1: + return f"constant value {avro_field_name(symbols[0])}" + return frg2 + case {"type": type_decl}: + return self.typefmt(type_decl, redirects) + case str(tp) if tp in redirects: return f"""{redirects[tp]}""" # noqa: B907 - if str(tp) in basicTypes: - return """{}""".format( - self.primitiveType, vocab_type_name(str(tp)) - ) - frg2: Final = urldefrag(tp)[1] - if frg2 != "": - tp = frg2 - return f"""{tp}""" - raise SchemaSaladException("We should not be here!") + case str(tp) if tp in basicTypes: + return f"""{vocab_type_name(tp)}""" + case _ as tp: + frg3: Final = urldefrag(tp)[1] + if frg3 != "": + tp = frg3 + return f"""{tp}""" def render_type(self, f: dict[str, Any], depth: int) -> None: """Render a type declaration.""" @@ -548,8 +547,8 @@ def avrold_doc( brand: str, brandlink: str, primtype: str, - brandstyle: Optional[str] = None, - brandinverse: Optional[bool] = False, + brandstyle: str | None = None, + brandinverse: bool | None = False, ) -> None: toc: Final = ToC() toc.start_numbering = False @@ -784,13 +783,13 @@ def main() -> None: def makedoc( stdout: IO[Any], schema: str, - redirects: Optional[list[str]] = None, - only: Optional[list[str]] = None, - brand: Optional[str] = None, - brandlink: Optional[str] = None, - primtype: Optional[str] = None, - brandstyle: Optional[str] = None, - brandinverse: Optional[bool] = False, + redirects: list[str] | None = None, + only: list[str] | None = None, + brand: str | None = None, + brandlink: str | None = None, + primtype: str | None = None, + brandstyle: str | None = None, + brandinverse: bool | None = False, ) -> None: """Emit HTML representation of a given schema.""" s: Final[list[dict[str, Any]]] = [] diff --git a/schema_salad/metaschema.py b/schema_salad/metaschema.py index 149302a4a..b29812004 100644 --- a/schema_salad/metaschema.py +++ b/schema_salad/metaschema.py @@ -14,7 +14,7 @@ from collections.abc import MutableMapping, MutableSequence, Sequence from io import StringIO from itertools import chain -from typing import Any, Final, Optional, Union, cast +from typing import Any, Final, Optional, TypeAlias, cast from urllib.parse import quote, urldefrag, urlparse, urlsplit, urlunsplit from urllib.request import pathname2url @@ -38,11 +38,11 @@ class LoadingOptions: idx: Final[IdxType] - fileuri: Final[Optional[str]] + fileuri: Final[str | None] baseuri: Final[str] namespaces: Final[MutableMapping[str, str]] schemas: Final[MutableSequence[str]] - original_doc: Final[Optional[Any]] + original_doc: Final[Any | None] addl_metadata: Final[MutableMapping[str, Any]] fetcher: Final[Fetcher] vocab: Final[dict[str, str]] @@ -50,24 +50,24 @@ class LoadingOptions: cache: Final[CacheType] imports: Final[list[str]] includes: Final[list[str]] - no_link_check: Final[Optional[bool]] - container: Final[Optional[str]] + no_link_check: Final[bool | None] + container: Final[str | None] def __init__( self, - fetcher: Optional[Fetcher] = None, - namespaces: Optional[dict[str, str]] = None, - schemas: Optional[list[str]] = None, - fileuri: Optional[str] = None, + fetcher: Fetcher | None = None, + namespaces: dict[str, str] | None = None, + schemas: list[str] | None = None, + fileuri: str | None = None, copyfrom: Optional["LoadingOptions"] = None, - original_doc: Optional[Any] = None, - addl_metadata: Optional[dict[str, str]] = None, - baseuri: Optional[str] = None, - idx: Optional[IdxType] = None, - imports: Optional[list[str]] = None, - includes: Optional[list[str]] = None, - no_link_check: Optional[bool] = None, - container: Optional[str] = None, + original_doc: Any | None = None, + addl_metadata: dict[str, str] | None = None, + baseuri: str | None = None, + idx: IdxType | None = None, + imports: list[str] | None = None, + includes: list[str] | None = None, + no_link_check: bool | None = None, + container: str | None = None, ) -> None: """Create a LoadingOptions object.""" self.original_doc = original_doc @@ -79,7 +79,7 @@ def __init__( self.idx = temp_idx if fileuri is not None: - temp_fileuri: Optional[str] = fileuri + temp_fileuri: str | None = fileuri else: temp_fileuri = copyfrom.fileuri if copyfrom is not None else None self.fileuri = temp_fileuri @@ -121,13 +121,13 @@ def __init__( self.includes = temp_includes if no_link_check is not None: - temp_no_link_check: Optional[bool] = no_link_check + temp_no_link_check: bool | None = no_link_check else: temp_no_link_check = copyfrom.no_link_check if copyfrom is not None else False self.no_link_check = temp_no_link_check if container is not None: - temp_container: Optional[str] = container + temp_container: str | None = container else: temp_container = copyfrom.container if copyfrom is not None else None self.container = temp_container @@ -211,7 +211,7 @@ def fromDoc( _doc: Any, baseuri: str, loadingOptions: LoadingOptions, - docRoot: Optional[str] = None, + docRoot: str | None = None, ) -> "Saveable": """Construct this object from the result of yaml.load().""" @@ -223,11 +223,11 @@ def save( def load_field( - val: Union[str, dict[str, str]], + val: str | dict[str, str], fieldtype: "_Loader", baseuri: str, loadingOptions: LoadingOptions, - lc: Optional[list[Any]] = None, + lc: list[Any] | None = None, ) -> Any: """Load field.""" if isinstance(val, MutableMapping): @@ -251,7 +251,9 @@ def load_field( return fieldtype.load(val, baseuri, loadingOptions, lc=lc) -save_type = Optional[Union[MutableMapping[str, Any], MutableSequence[Any], int, float, bool, str]] +save_type: TypeAlias = ( + None | MutableMapping[str, Any] | MutableSequence[Any] | int | float | bool | str +) def extract_type(val_type: type[Any]) -> str: @@ -367,7 +369,7 @@ def expand_url( loadingOptions: LoadingOptions, scoped_id: bool = False, vocab_term: bool = False, - scoped_ref: Optional[int] = None, + scoped_ref: int | None = None, ) -> str: if url in ("@id", "@type"): return url @@ -434,8 +436,8 @@ def load( doc: Any, baseuri: str, loadingOptions: LoadingOptions, - docRoot: Optional[str] = None, - lc: Optional[list[Any]] = None, + docRoot: str | None = None, + lc: list[Any] | None = None, ) -> Any: pass @@ -446,8 +448,8 @@ def load( doc: Any, baseuri: str, loadingOptions: LoadingOptions, - docRoot: Optional[str] = None, - lc: Optional[list[Any]] = None, + docRoot: str | None = None, + lc: list[Any] | None = None, ) -> Any: if doc is not None: return doc @@ -455,7 +457,7 @@ def load( class _PrimitiveLoader(_Loader): - def __init__(self, tp: Union[type, tuple[type[str], type[str]]]) -> None: + def __init__(self, tp: type | tuple[type[str], type[str]]) -> None: self.tp: Final = tp def load( @@ -463,8 +465,8 @@ def load( doc: Any, baseuri: str, loadingOptions: LoadingOptions, - docRoot: Optional[str] = None, - lc: Optional[list[Any]] = None, + docRoot: str | None = None, + lc: list[Any] | None = None, ) -> Any: if not isinstance(doc, self.tp): raise ValidationException(f"Expected a {self.tp} but got {doc.__class__.__name__}") @@ -483,8 +485,8 @@ def load( doc: Any, baseuri: str, loadingOptions: LoadingOptions, - docRoot: Optional[str] = None, - lc: Optional[list[Any]] = None, + docRoot: str | None = None, + lc: list[Any] | None = None, ) -> Any: if not isinstance(doc, MutableSequence): raise ValidationException( @@ -535,9 +537,9 @@ class _MapLoader(_Loader): def __init__( self, values: _Loader, - name: Optional[str] = None, - container: Optional[str] = None, - no_link_check: Optional[bool] = None, + name: str | None = None, + container: str | None = None, + no_link_check: bool | None = None, ) -> None: self.values: Final = values self.name: Final = name @@ -549,8 +551,8 @@ def load( doc: Any, baseuri: str, loadingOptions: LoadingOptions, - docRoot: Optional[str] = None, - lc: Optional[list[Any]] = None, + docRoot: str | None = None, + lc: list[Any] | None = None, ) -> Any: if not isinstance(doc, MutableMapping): raise ValidationException(f"Expected a map, was {type(doc)}") @@ -584,8 +586,8 @@ def load( doc: Any, baseuri: str, loadingOptions: LoadingOptions, - docRoot: Optional[str] = None, - lc: Optional[list[Any]] = None, + docRoot: str | None = None, + lc: list[Any] | None = None, ) -> Any: if doc in self.symbols: return doc @@ -604,66 +606,67 @@ def load( doc: Any, baseuri: str, loadingOptions: LoadingOptions, - docRoot: Optional[str] = None, - lc: Optional[list[Any]] = None, + docRoot: str | None = None, + lc: list[Any] | None = None, ) -> Any: r: Final[list[dict[str, Any]]] = [] - if isinstance(doc, MutableSequence): - for d in doc: - if isinstance(d, str): - if d.endswith("?"): - r.append({"pattern": d[:-1], "required": False}) - else: - r.append({"pattern": d}) - elif isinstance(d, dict): - new_dict1: dict[str, Any] = {} - dict_copy = copy.deepcopy(d) - if "pattern" in dict_copy: - new_dict1["pattern"] = dict_copy.pop("pattern") - else: - raise ValidationException( - f"Missing pattern in secondaryFiles specification entry: {d}" + match doc: + case MutableSequence() as dlist: + for d in dlist: + if isinstance(d, str): + if d.endswith("?"): + r.append({"pattern": d[:-1], "required": False}) + else: + r.append({"pattern": d}) + elif isinstance(d, dict): + new_dict1: dict[str, Any] = {} + dict_copy = copy.deepcopy(d) + if "pattern" in dict_copy: + new_dict1["pattern"] = dict_copy.pop("pattern") + else: + raise ValidationException( + f"Missing pattern in secondaryFiles specification entry: {d}" + ) + new_dict1["required"] = ( + dict_copy.pop("required") if "required" in dict_copy else None ) - new_dict1["required"] = ( - dict_copy.pop("required") if "required" in dict_copy else None - ) - if len(dict_copy): - raise ValidationException( - "Unallowed values in secondaryFiles specification entry: {}".format( - dict_copy + if len(dict_copy): + raise ValidationException( + "Unallowed values in secondaryFiles specification entry: {}".format( + dict_copy + ) ) - ) - r.append(new_dict1) + r.append(new_dict1) + else: + raise ValidationException( + "Expected a string or sequence of (strings or mappings)." + ) + case MutableMapping() as decl: + new_dict2 = {} + doc_copy = copy.deepcopy(decl) + if "pattern" in doc_copy: + new_dict2["pattern"] = doc_copy.pop("pattern") else: raise ValidationException( - "Expected a string or sequence of (strings or mappings)." + f"Missing pattern in secondaryFiles specification entry: {decl}" ) - elif isinstance(doc, MutableMapping): - new_dict2: Final = {} - doc_copy: Final = copy.deepcopy(doc) - if "pattern" in doc_copy: - new_dict2["pattern"] = doc_copy.pop("pattern") - else: - raise ValidationException( - f"Missing pattern in secondaryFiles specification entry: {doc}" - ) - new_dict2["required"] = doc_copy.pop("required") if "required" in doc_copy else None + new_dict2["required"] = doc_copy.pop("required") if "required" in doc_copy else None - if len(doc_copy): - raise ValidationException( - f"Unallowed values in secondaryFiles specification entry: {doc_copy}" - ) - r.append(new_dict2) + if len(doc_copy): + raise ValidationException( + f"Unallowed values in secondaryFiles specification entry: {doc_copy}" + ) + r.append(new_dict2) - elif isinstance(doc, str): - if doc.endswith("?"): - r.append({"pattern": doc[:-1], "required": False}) - else: - r.append({"pattern": doc}) - else: - raise ValidationException("Expected str or sequence of str") + case str(decl): + if decl.endswith("?"): + r.append({"pattern": decl[:-1], "required": False}) + else: + r.append({"pattern": decl}) + case _: + raise ValidationException("Expected str or sequence of str") return self.inner.load(r, baseuri, loadingOptions, docRoot, lc=lc) @@ -671,8 +674,8 @@ class _RecordLoader(_Loader): def __init__( self, classtype: type[Saveable], - container: Optional[str] = None, - no_link_check: Optional[bool] = None, + container: str | None = None, + no_link_check: bool | None = None, ) -> None: self.classtype: Final = classtype self.container: Final = container @@ -683,8 +686,8 @@ def load( doc: Any, baseuri: str, loadingOptions: LoadingOptions, - docRoot: Optional[str] = None, - lc: Optional[list[Any]] = None, + docRoot: str | None = None, + lc: list[Any] | None = None, ) -> Any: if not isinstance(doc, MutableMapping): raise ValidationException( @@ -710,8 +713,8 @@ def load( doc: Any, baseuri: str, loadingOptions: LoadingOptions, - docRoot: Optional[str] = None, - lc: Optional[list[Any]] = None, + docRoot: str | None = None, + lc: list[Any] | None = None, ) -> Any: if not isinstance(doc, str): raise ValidationException( @@ -722,7 +725,7 @@ def load( class _UnionLoader(_Loader): - def __init__(self, alternates: Sequence[_Loader], name: Optional[str] = None) -> None: + def __init__(self, alternates: Sequence[_Loader], name: str | None = None) -> None: self.alternates = alternates self.name: Final = name @@ -734,8 +737,8 @@ def load( doc: Any, baseuri: str, loadingOptions: LoadingOptions, - docRoot: Optional[str] = None, - lc: Optional[list[Any]] = None, + docRoot: str | None = None, + lc: list[Any] | None = None, ) -> Any: errors: Final = [] @@ -817,8 +820,8 @@ def __init__( inner: _Loader, scoped_id: bool, vocab_term: bool, - scoped_ref: Optional[int], - no_link_check: Optional[bool], + scoped_ref: int | None, + no_link_check: bool | None, ) -> None: self.inner: Final = inner self.scoped_id: Final = scoped_id @@ -831,39 +834,40 @@ def load( doc: Any, baseuri: str, loadingOptions: LoadingOptions, - docRoot: Optional[str] = None, - lc: Optional[list[Any]] = None, + docRoot: str | None = None, + lc: list[Any] | None = None, ) -> Any: if self.no_link_check is not None: loadingOptions = LoadingOptions( copyfrom=loadingOptions, no_link_check=self.no_link_check ) - if isinstance(doc, MutableSequence): - newdoc: Final = [] - for i in doc: - if isinstance(i, str): - newdoc.append( - expand_url( - i, - baseuri, - loadingOptions, - self.scoped_id, - self.vocab_term, - self.scoped_ref, + match doc: + case MutableSequence() as decl: + newdoc: Final = [] + for i in decl: + if isinstance(i, str): + newdoc.append( + expand_url( + i, + baseuri, + loadingOptions, + self.scoped_id, + self.vocab_term, + self.scoped_ref, + ) ) - ) - else: - newdoc.append(i) - doc = newdoc - elif isinstance(doc, str): - doc = expand_url( - doc, - baseuri, - loadingOptions, - self.scoped_id, - self.vocab_term, - self.scoped_ref, - ) + else: + newdoc.append(i) + doc = newdoc + case str(decl): + doc = expand_url( + decl, + baseuri, + loadingOptions, + self.scoped_id, + self.vocab_term, + self.scoped_ref, + ) if isinstance(doc, str): if not loadingOptions.no_link_check: errors: Final = [] @@ -880,7 +884,7 @@ def load( class _TypeDSLLoader(_Loader): - def __init__(self, inner: _Loader, refScope: Optional[int], salad_version: str) -> None: + def __init__(self, inner: _Loader, refScope: int | None, salad_version: str) -> None: self.inner: Final = inner self.refScope: Final = refScope self.salad_version: Final = salad_version @@ -890,7 +894,7 @@ def resolve( doc: str, baseuri: str, loadingOptions: LoadingOptions, - ) -> Union[list[Union[dict[str, Any], str]], dict[str, Any], str]: + ) -> list[dict[str, Any] | str] | dict[str, Any] | str: doc_ = doc optional = False if doc_.endswith("?"): @@ -899,7 +903,7 @@ def resolve( if doc_.endswith("[]"): salad_versions: Final = [int(v) for v in self.salad_version[1:].split(".")] - items: Union[list[Union[dict[str, Any], str]], dict[str, Any], str] = "" + items: list[dict[str, Any] | str] | dict[str, Any] | str = "" rest: Final = doc_[0:-2] if salad_versions < [1, 3]: if rest.endswith("[]"): @@ -911,7 +915,7 @@ def resolve( items = self.resolve(rest, baseuri, loadingOptions) if isinstance(items, str): items = expand_url(items, baseuri, loadingOptions, False, True, self.refScope) - expanded: Union[dict[str, Any], str] = {"type": "array", "items": items} + expanded: dict[str, Any] | str = {"type": "array", "items": items} else: expanded = expand_url(doc_, baseuri, loadingOptions, False, True, self.refScope) @@ -925,8 +929,8 @@ def load( doc: Any, baseuri: str, loadingOptions: LoadingOptions, - docRoot: Optional[str] = None, - lc: Optional[list[Any]] = None, + docRoot: str | None = None, + lc: list[Any] | None = None, ) -> Any: if isinstance(doc, MutableSequence): r: Final[list[Any]] = [] @@ -950,7 +954,7 @@ def load( class _IdMapLoader(_Loader): - def __init__(self, inner: _Loader, mapSubject: str, mapPredicate: Optional[str]) -> None: + def __init__(self, inner: _Loader, mapSubject: str, mapPredicate: str | None) -> None: self.inner: Final = inner self.mapSubject: Final = mapSubject self.mapPredicate: Final = mapPredicate @@ -960,8 +964,8 @@ def load( doc: Any, baseuri: str, loadingOptions: LoadingOptions, - docRoot: Optional[str] = None, - lc: Optional[list[Any]] = None, + docRoot: str | None = None, + lc: list[Any] | None = None, ) -> Any: if isinstance(doc, MutableMapping): r: Final[list[Any]] = [] @@ -990,10 +994,10 @@ def load( def _document_load( loader: _Loader, - doc: Union[str, MutableMapping[str, Any], MutableSequence[Any]], + doc: str | MutableMapping[str, Any] | MutableSequence[Any], baseuri: str, loadingOptions: LoadingOptions, - addl_metadata_fields: Optional[MutableSequence[str]] = None, + addl_metadata_fields: MutableSequence[str] | None = None, ) -> tuple[Any, LoadingOptions]: if isinstance(doc, str): return _document_load_by_url( @@ -1062,7 +1066,7 @@ def _document_load_by_url( loader: _Loader, url: str, loadingOptions: LoadingOptions, - addl_metadata_fields: Optional[MutableSequence[str]] = None, + addl_metadata_fields: MutableSequence[str] | None = None, ) -> tuple[Any, LoadingOptions]: if url in loadingOptions.idx: return loadingOptions.idx[url] @@ -1117,7 +1121,7 @@ def save_relative_uri( uri: Any, base_url: str, scoped_id: bool, - ref_scope: Optional[int], + ref_scope: int | None, relative_uris: bool, ) -> Any: """Convert any URI to a relative one, obeying the scoping rules.""" diff --git a/schema_salad/python_codegen.py b/schema_salad/python_codegen.py index d6cb84dad..d3451de1e 100644 --- a/schema_salad/python_codegen.py +++ b/schema_salad/python_codegen.py @@ -1,9 +1,9 @@ """Python code generator for a given schema salad definition.""" import textwrap -from collections.abc import MutableMapping, MutableSequence +from collections.abc import MutableSequence from io import StringIO -from typing import IO, Any, Final, Optional, Union +from typing import IO, Any, Final try: import black @@ -72,7 +72,7 @@ class PythonCodeGen(CodeGenBase): def __init__( self, out: IO[str], - copyright: Optional[str], + copyright: str | None, parser_info: str, salad_version: str, ) -> None: @@ -372,42 +372,34 @@ def end_class(self, classname: str, field_names: list[str]) -> None: def type_loader( self, - type_declaration: Union[list[Any], dict[str, Any], str], - container: Optional[str] = None, - no_link_check: Optional[bool] = None, + type_declaration: list[Any] | dict[str, Any] | str, + container: str | None = None, + no_link_check: bool | None = None, ) -> TypeDef: """Parse the given type declaration and declare its components.""" - if isinstance(type_declaration, MutableSequence): - sub_names1: Final[list[str]] = list( - dict.fromkeys([self.type_loader(i).name for i in type_declaration]) - ) - return self.declare_type( - TypeDef( - "union_of_{}".format("_or_".join(sub_names1)), - "_UnionLoader(({},))".format(", ".join(sub_names1)), + match type_declaration: + case MutableSequence(): + sub_names1: Final = list( + dict.fromkeys([self.type_loader(i).name for i in type_declaration]) ) - ) - if isinstance(type_declaration, MutableMapping): - if type_declaration["type"] in ( - "array", - "https://w3id.org/cwl/salad#array", - ): - i1: Final = self.type_loader(type_declaration["items"]) + return self.declare_type( + TypeDef( + "union_of_{}".format("_or_".join(sub_names1)), + "_UnionLoader(({},))".format(", ".join(sub_names1)), + ) + ) + case {"type": "array" | "https://w3id.org/cwl/salad#array", "items": items}: + i1: Final = self.type_loader(items) return self.declare_type( TypeDef( f"array_of_{i1.name}", f"_ArrayLoader({i1.name})", ) ) - if type_declaration["type"] in ( - "map", - "https://w3id.org/cwl/salad#map", - ): - i2: Final = self.type_loader(type_declaration["values"]) - name = ( - self.safe_name(type_declaration["name"]) if "name" in type_declaration else None - ) - anon_type: Final = self.declare_type( + case {"type": "map" | "https://w3id.org/cwl/salad#map", "values": values, **rest}: + i2: Final = self.type_loader(values) + name = self.safe_name(str(rest["name"])) if "name" in rest else None + anon_type = self.declare_type( TypeDef( f"map_of_{i2.name}", "_MapLoader({}, {}, {}, {})".format( @@ -418,65 +410,64 @@ def type_loader( ), ) ) - if "name" in type_declaration: + if "name" in rest: return self.declare_type( - TypeDef(self.safe_name(type_declaration["name"]) + "Loader", anon_type.name) + TypeDef(self.safe_name(str(rest["name"])) + "Loader", anon_type.name) ) else: return anon_type - if type_declaration["type"] in ("enum", "https://w3id.org/cwl/salad#enum"): - for sym in type_declaration["symbols"]: + case { + "type": "enum" | "https://w3id.org/cwl/salad#enum", + "symbols": symbols, + "name": name, + **rest, + }: + for sym in symbols: self.add_vocab(shortname(sym), sym) - if "doc" in type_declaration: - doc: Final = type_declaration["doc"] + if "doc" in rest: + doc: Final = rest["doc"] if isinstance(doc, MutableSequence): formated_doc = "\n".join(doc) else: - formated_doc = doc.strip() + formated_doc = str(doc).strip() docstring = f'\n"""\n{formated_doc}\n"""' else: docstring = "" return self.declare_type( TypeDef( - self.safe_name(type_declaration["name"]) + "Loader", + self.safe_name(name) + "Loader", '_EnumLoader(("{}",), "{}"){}'.format( - '", "'.join( - schema.avro_field_name(sym) for sym in type_declaration["symbols"] - ), - self.safe_name(type_declaration["name"]), + '", "'.join(schema.avro_field_name(sym) for sym in symbols), + self.safe_name(name), docstring, ), ) ) - if type_declaration["type"] in ( - "record", - "https://w3id.org/cwl/salad#record", - ): + case {"type": "record" | "https://w3id.org/cwl/salad#record", "name": name, **rest}: return self.declare_type( TypeDef( - self.safe_name(type_declaration["name"]) + "Loader", + self.safe_name(name) + "Loader", "_RecordLoader({}, {}, {})".format( - self.safe_name(type_declaration["name"]), + self.safe_name(name), f"'{container}'" if container is not None else None, # noqa: B907 no_link_check, ), - abstract=type_declaration.get("abstract", False), + abstract=bool(rest.get("abstract", False)), ) ) - if type_declaration["type"] in ( - "union", - "https://w3id.org/cwl/salad#union", - ): + case { + "type": "union" | "https://w3id.org/cwl/salad#union", + "name": name, + "names": list(names), + }: # Declare the named loader to handle recursive union definitions - loader_name = self.safe_name(type_declaration["name"]) + "Loader" + loader_name = self.safe_name(name) + "Loader" loader_type = TypeDef(loader_name, f"_UnionLoader((), '{loader_name}')") self.declare_type(loader_type) # Parse inner types - sub_names2: Final[list[str]] = list( - dict.fromkeys([self.type_loader(i).name for i in type_declaration["names"]]) - ) + sub_names2: Final = list(dict.fromkeys([self.type_loader(i).name for i in names])) # Register lazy initialization for the loader self.add_lazy_init( LazyInitDef( @@ -485,25 +476,29 @@ def type_loader( ) ) return loader_type - raise SchemaException("wft {}".format(type_declaration["type"])) + case {"type": decl}: + raise SchemaException(f"wft {decl}") - if type_declaration in prims: - return prims[type_declaration] + case str(decl) if decl in prims: + return prims[decl] - if type_declaration in ("Expression", "https://w3id.org/cwl/cwl#Expression"): - return self.declare_type( - TypeDef( - self.safe_name(type_declaration) + "Loader", - "_ExpressionLoader(str)", + case "Expression" | "https://w3id.org/cwl/cwl#Expression" as decl: + return self.declare_type( + TypeDef( + self.safe_name(decl) + "Loader", + "_ExpressionLoader(str)", + ) ) - ) - return self.collected_types[self.safe_name(type_declaration) + "Loader"] + case str(decl): + return self.collected_types[self.safe_name(decl) + "Loader"] + case _ as decl: + raise SchemaException(f"wtf {decl}") def declare_id_field( self, name: str, fieldtype: TypeDef, - doc: Optional[str], + doc: str | None, optional: bool, ) -> None: if self.current_class_is_abstract: @@ -539,9 +534,9 @@ def declare_field( self, name: str, fieldtype: TypeDef, - doc: Optional[str], + doc: str | None, optional: bool, - subscope: Optional[str], + subscope: str | None, ) -> None: if self.current_class_is_abstract: return @@ -722,8 +717,8 @@ def uri_loader( inner: TypeDef, scoped_id: bool, vocab_term: bool, - ref_scope: Optional[int], - no_link_check: Optional[bool] = None, + ref_scope: int | None, + no_link_check: bool | None = None, ) -> TypeDef: """Construct the TypeDef for the given URI loader.""" return self.declare_type( @@ -737,7 +732,7 @@ def uri_loader( ) def idmap_loader( - self, field: str, inner: TypeDef, map_subject: str, map_predicate: Optional[str] + self, field: str, inner: TypeDef, map_subject: str, map_predicate: str | None ) -> TypeDef: """Construct the TypeDef for the given mapped ID loader.""" return self.declare_type( @@ -747,7 +742,7 @@ def idmap_loader( ) ) - def typedsl_loader(self, inner: TypeDef, ref_scope: Optional[int]) -> TypeDef: + def typedsl_loader(self, inner: TypeDef, ref_scope: int | None) -> TypeDef: """Construct the TypeDef for the given DSL loader.""" return self.declare_type( TypeDef( diff --git a/schema_salad/python_codegen_support.py b/schema_salad/python_codegen_support.py index b59eee11c..cd240b0d2 100644 --- a/schema_salad/python_codegen_support.py +++ b/schema_salad/python_codegen_support.py @@ -11,7 +11,7 @@ from collections.abc import MutableMapping, MutableSequence, Sequence from io import StringIO from itertools import chain -from typing import Any, Final, Optional, Union, cast +from typing import Any, Final, Optional, TypeAlias, cast from urllib.parse import quote, urldefrag, urlparse, urlsplit, urlunsplit from urllib.request import pathname2url @@ -35,11 +35,11 @@ class LoadingOptions: idx: Final[IdxType] - fileuri: Final[Optional[str]] + fileuri: Final[str | None] baseuri: Final[str] namespaces: Final[MutableMapping[str, str]] schemas: Final[MutableSequence[str]] - original_doc: Final[Optional[Any]] + original_doc: Final[Any | None] addl_metadata: Final[MutableMapping[str, Any]] fetcher: Final[Fetcher] vocab: Final[dict[str, str]] @@ -47,24 +47,24 @@ class LoadingOptions: cache: Final[CacheType] imports: Final[list[str]] includes: Final[list[str]] - no_link_check: Final[Optional[bool]] - container: Final[Optional[str]] + no_link_check: Final[bool | None] + container: Final[str | None] def __init__( self, - fetcher: Optional[Fetcher] = None, - namespaces: Optional[dict[str, str]] = None, - schemas: Optional[list[str]] = None, - fileuri: Optional[str] = None, + fetcher: Fetcher | None = None, + namespaces: dict[str, str] | None = None, + schemas: list[str] | None = None, + fileuri: str | None = None, copyfrom: Optional["LoadingOptions"] = None, - original_doc: Optional[Any] = None, - addl_metadata: Optional[dict[str, str]] = None, - baseuri: Optional[str] = None, - idx: Optional[IdxType] = None, - imports: Optional[list[str]] = None, - includes: Optional[list[str]] = None, - no_link_check: Optional[bool] = None, - container: Optional[str] = None, + original_doc: Any | None = None, + addl_metadata: dict[str, str] | None = None, + baseuri: str | None = None, + idx: IdxType | None = None, + imports: list[str] | None = None, + includes: list[str] | None = None, + no_link_check: bool | None = None, + container: str | None = None, ) -> None: """Create a LoadingOptions object.""" self.original_doc = original_doc @@ -76,7 +76,7 @@ def __init__( self.idx = temp_idx if fileuri is not None: - temp_fileuri: Optional[str] = fileuri + temp_fileuri: str | None = fileuri else: temp_fileuri = copyfrom.fileuri if copyfrom is not None else None self.fileuri = temp_fileuri @@ -118,13 +118,13 @@ def __init__( self.includes = temp_includes if no_link_check is not None: - temp_no_link_check: Optional[bool] = no_link_check + temp_no_link_check: bool | None = no_link_check else: temp_no_link_check = copyfrom.no_link_check if copyfrom is not None else False self.no_link_check = temp_no_link_check if container is not None: - temp_container: Optional[str] = container + temp_container: str | None = container else: temp_container = copyfrom.container if copyfrom is not None else None self.container = temp_container @@ -208,7 +208,7 @@ def fromDoc( _doc: Any, baseuri: str, loadingOptions: LoadingOptions, - docRoot: Optional[str] = None, + docRoot: str | None = None, ) -> "Saveable": """Construct this object from the result of yaml.load().""" @@ -220,11 +220,11 @@ def save( def load_field( - val: Union[str, dict[str, str]], + val: str | dict[str, str], fieldtype: "_Loader", baseuri: str, loadingOptions: LoadingOptions, - lc: Optional[list[Any]] = None, + lc: list[Any] | None = None, ) -> Any: """Load field.""" if isinstance(val, MutableMapping): @@ -248,7 +248,9 @@ def load_field( return fieldtype.load(val, baseuri, loadingOptions, lc=lc) -save_type = Optional[Union[MutableMapping[str, Any], MutableSequence[Any], int, float, bool, str]] +save_type: TypeAlias = ( + None | MutableMapping[str, Any] | MutableSequence[Any] | int | float | bool | str +) def extract_type(val_type: type[Any]) -> str: @@ -364,7 +366,7 @@ def expand_url( loadingOptions: LoadingOptions, scoped_id: bool = False, vocab_term: bool = False, - scoped_ref: Optional[int] = None, + scoped_ref: int | None = None, ) -> str: if url in ("@id", "@type"): return url @@ -431,8 +433,8 @@ def load( doc: Any, baseuri: str, loadingOptions: LoadingOptions, - docRoot: Optional[str] = None, - lc: Optional[list[Any]] = None, + docRoot: str | None = None, + lc: list[Any] | None = None, ) -> Any: pass @@ -443,8 +445,8 @@ def load( doc: Any, baseuri: str, loadingOptions: LoadingOptions, - docRoot: Optional[str] = None, - lc: Optional[list[Any]] = None, + docRoot: str | None = None, + lc: list[Any] | None = None, ) -> Any: if doc is not None: return doc @@ -452,7 +454,7 @@ def load( class _PrimitiveLoader(_Loader): - def __init__(self, tp: Union[type, tuple[type[str], type[str]]]) -> None: + def __init__(self, tp: type | tuple[type[str], type[str]]) -> None: self.tp: Final = tp def load( @@ -460,8 +462,8 @@ def load( doc: Any, baseuri: str, loadingOptions: LoadingOptions, - docRoot: Optional[str] = None, - lc: Optional[list[Any]] = None, + docRoot: str | None = None, + lc: list[Any] | None = None, ) -> Any: if not isinstance(doc, self.tp): raise ValidationException(f"Expected a {self.tp} but got {doc.__class__.__name__}") @@ -480,8 +482,8 @@ def load( doc: Any, baseuri: str, loadingOptions: LoadingOptions, - docRoot: Optional[str] = None, - lc: Optional[list[Any]] = None, + docRoot: str | None = None, + lc: list[Any] | None = None, ) -> Any: if not isinstance(doc, MutableSequence): raise ValidationException( @@ -532,9 +534,9 @@ class _MapLoader(_Loader): def __init__( self, values: _Loader, - name: Optional[str] = None, - container: Optional[str] = None, - no_link_check: Optional[bool] = None, + name: str | None = None, + container: str | None = None, + no_link_check: bool | None = None, ) -> None: self.values: Final = values self.name: Final = name @@ -546,8 +548,8 @@ def load( doc: Any, baseuri: str, loadingOptions: LoadingOptions, - docRoot: Optional[str] = None, - lc: Optional[list[Any]] = None, + docRoot: str | None = None, + lc: list[Any] | None = None, ) -> Any: if not isinstance(doc, MutableMapping): raise ValidationException(f"Expected a map, was {type(doc)}") @@ -581,8 +583,8 @@ def load( doc: Any, baseuri: str, loadingOptions: LoadingOptions, - docRoot: Optional[str] = None, - lc: Optional[list[Any]] = None, + docRoot: str | None = None, + lc: list[Any] | None = None, ) -> Any: if doc in self.symbols: return doc @@ -601,66 +603,67 @@ def load( doc: Any, baseuri: str, loadingOptions: LoadingOptions, - docRoot: Optional[str] = None, - lc: Optional[list[Any]] = None, + docRoot: str | None = None, + lc: list[Any] | None = None, ) -> Any: r: Final[list[dict[str, Any]]] = [] - if isinstance(doc, MutableSequence): - for d in doc: - if isinstance(d, str): - if d.endswith("?"): - r.append({"pattern": d[:-1], "required": False}) - else: - r.append({"pattern": d}) - elif isinstance(d, dict): - new_dict1: dict[str, Any] = {} - dict_copy = copy.deepcopy(d) - if "pattern" in dict_copy: - new_dict1["pattern"] = dict_copy.pop("pattern") - else: - raise ValidationException( - f"Missing pattern in secondaryFiles specification entry: {d}" + match doc: + case MutableSequence() as dlist: + for d in dlist: + if isinstance(d, str): + if d.endswith("?"): + r.append({"pattern": d[:-1], "required": False}) + else: + r.append({"pattern": d}) + elif isinstance(d, dict): + new_dict1: dict[str, Any] = {} + dict_copy = copy.deepcopy(d) + if "pattern" in dict_copy: + new_dict1["pattern"] = dict_copy.pop("pattern") + else: + raise ValidationException( + f"Missing pattern in secondaryFiles specification entry: {d}" + ) + new_dict1["required"] = ( + dict_copy.pop("required") if "required" in dict_copy else None ) - new_dict1["required"] = ( - dict_copy.pop("required") if "required" in dict_copy else None - ) - if len(dict_copy): - raise ValidationException( - "Unallowed values in secondaryFiles specification entry: {}".format( - dict_copy + if len(dict_copy): + raise ValidationException( + "Unallowed values in secondaryFiles specification entry: {}".format( + dict_copy + ) ) - ) - r.append(new_dict1) + r.append(new_dict1) + else: + raise ValidationException( + "Expected a string or sequence of (strings or mappings)." + ) + case MutableMapping() as decl: + new_dict2 = {} + doc_copy = copy.deepcopy(decl) + if "pattern" in doc_copy: + new_dict2["pattern"] = doc_copy.pop("pattern") else: raise ValidationException( - "Expected a string or sequence of (strings or mappings)." + f"Missing pattern in secondaryFiles specification entry: {decl}" ) - elif isinstance(doc, MutableMapping): - new_dict2: Final = {} - doc_copy: Final = copy.deepcopy(doc) - if "pattern" in doc_copy: - new_dict2["pattern"] = doc_copy.pop("pattern") - else: - raise ValidationException( - f"Missing pattern in secondaryFiles specification entry: {doc}" - ) - new_dict2["required"] = doc_copy.pop("required") if "required" in doc_copy else None + new_dict2["required"] = doc_copy.pop("required") if "required" in doc_copy else None - if len(doc_copy): - raise ValidationException( - f"Unallowed values in secondaryFiles specification entry: {doc_copy}" - ) - r.append(new_dict2) + if len(doc_copy): + raise ValidationException( + f"Unallowed values in secondaryFiles specification entry: {doc_copy}" + ) + r.append(new_dict2) - elif isinstance(doc, str): - if doc.endswith("?"): - r.append({"pattern": doc[:-1], "required": False}) - else: - r.append({"pattern": doc}) - else: - raise ValidationException("Expected str or sequence of str") + case str(decl): + if decl.endswith("?"): + r.append({"pattern": decl[:-1], "required": False}) + else: + r.append({"pattern": decl}) + case _: + raise ValidationException("Expected str or sequence of str") return self.inner.load(r, baseuri, loadingOptions, docRoot, lc=lc) @@ -668,8 +671,8 @@ class _RecordLoader(_Loader): def __init__( self, classtype: type[Saveable], - container: Optional[str] = None, - no_link_check: Optional[bool] = None, + container: str | None = None, + no_link_check: bool | None = None, ) -> None: self.classtype: Final = classtype self.container: Final = container @@ -680,8 +683,8 @@ def load( doc: Any, baseuri: str, loadingOptions: LoadingOptions, - docRoot: Optional[str] = None, - lc: Optional[list[Any]] = None, + docRoot: str | None = None, + lc: list[Any] | None = None, ) -> Any: if not isinstance(doc, MutableMapping): raise ValidationException( @@ -707,8 +710,8 @@ def load( doc: Any, baseuri: str, loadingOptions: LoadingOptions, - docRoot: Optional[str] = None, - lc: Optional[list[Any]] = None, + docRoot: str | None = None, + lc: list[Any] | None = None, ) -> Any: if not isinstance(doc, str): raise ValidationException( @@ -719,7 +722,7 @@ def load( class _UnionLoader(_Loader): - def __init__(self, alternates: Sequence[_Loader], name: Optional[str] = None) -> None: + def __init__(self, alternates: Sequence[_Loader], name: str | None = None) -> None: self.alternates = alternates self.name: Final = name @@ -731,8 +734,8 @@ def load( doc: Any, baseuri: str, loadingOptions: LoadingOptions, - docRoot: Optional[str] = None, - lc: Optional[list[Any]] = None, + docRoot: str | None = None, + lc: list[Any] | None = None, ) -> Any: errors: Final = [] @@ -814,8 +817,8 @@ def __init__( inner: _Loader, scoped_id: bool, vocab_term: bool, - scoped_ref: Optional[int], - no_link_check: Optional[bool], + scoped_ref: int | None, + no_link_check: bool | None, ) -> None: self.inner: Final = inner self.scoped_id: Final = scoped_id @@ -828,39 +831,40 @@ def load( doc: Any, baseuri: str, loadingOptions: LoadingOptions, - docRoot: Optional[str] = None, - lc: Optional[list[Any]] = None, + docRoot: str | None = None, + lc: list[Any] | None = None, ) -> Any: if self.no_link_check is not None: loadingOptions = LoadingOptions( copyfrom=loadingOptions, no_link_check=self.no_link_check ) - if isinstance(doc, MutableSequence): - newdoc: Final = [] - for i in doc: - if isinstance(i, str): - newdoc.append( - expand_url( - i, - baseuri, - loadingOptions, - self.scoped_id, - self.vocab_term, - self.scoped_ref, + match doc: + case MutableSequence() as decl: + newdoc: Final = [] + for i in decl: + if isinstance(i, str): + newdoc.append( + expand_url( + i, + baseuri, + loadingOptions, + self.scoped_id, + self.vocab_term, + self.scoped_ref, + ) ) - ) - else: - newdoc.append(i) - doc = newdoc - elif isinstance(doc, str): - doc = expand_url( - doc, - baseuri, - loadingOptions, - self.scoped_id, - self.vocab_term, - self.scoped_ref, - ) + else: + newdoc.append(i) + doc = newdoc + case str(decl): + doc = expand_url( + decl, + baseuri, + loadingOptions, + self.scoped_id, + self.vocab_term, + self.scoped_ref, + ) if isinstance(doc, str): if not loadingOptions.no_link_check: errors: Final = [] @@ -877,7 +881,7 @@ def load( class _TypeDSLLoader(_Loader): - def __init__(self, inner: _Loader, refScope: Optional[int], salad_version: str) -> None: + def __init__(self, inner: _Loader, refScope: int | None, salad_version: str) -> None: self.inner: Final = inner self.refScope: Final = refScope self.salad_version: Final = salad_version @@ -887,7 +891,7 @@ def resolve( doc: str, baseuri: str, loadingOptions: LoadingOptions, - ) -> Union[list[Union[dict[str, Any], str]], dict[str, Any], str]: + ) -> list[dict[str, Any] | str] | dict[str, Any] | str: doc_ = doc optional = False if doc_.endswith("?"): @@ -896,7 +900,7 @@ def resolve( if doc_.endswith("[]"): salad_versions: Final = [int(v) for v in self.salad_version[1:].split(".")] - items: Union[list[Union[dict[str, Any], str]], dict[str, Any], str] = "" + items: list[dict[str, Any] | str] | dict[str, Any] | str = "" rest: Final = doc_[0:-2] if salad_versions < [1, 3]: if rest.endswith("[]"): @@ -908,7 +912,7 @@ def resolve( items = self.resolve(rest, baseuri, loadingOptions) if isinstance(items, str): items = expand_url(items, baseuri, loadingOptions, False, True, self.refScope) - expanded: Union[dict[str, Any], str] = {"type": "array", "items": items} + expanded: dict[str, Any] | str = {"type": "array", "items": items} else: expanded = expand_url(doc_, baseuri, loadingOptions, False, True, self.refScope) @@ -922,8 +926,8 @@ def load( doc: Any, baseuri: str, loadingOptions: LoadingOptions, - docRoot: Optional[str] = None, - lc: Optional[list[Any]] = None, + docRoot: str | None = None, + lc: list[Any] | None = None, ) -> Any: if isinstance(doc, MutableSequence): r: Final[list[Any]] = [] @@ -947,7 +951,7 @@ def load( class _IdMapLoader(_Loader): - def __init__(self, inner: _Loader, mapSubject: str, mapPredicate: Optional[str]) -> None: + def __init__(self, inner: _Loader, mapSubject: str, mapPredicate: str | None) -> None: self.inner: Final = inner self.mapSubject: Final = mapSubject self.mapPredicate: Final = mapPredicate @@ -957,8 +961,8 @@ def load( doc: Any, baseuri: str, loadingOptions: LoadingOptions, - docRoot: Optional[str] = None, - lc: Optional[list[Any]] = None, + docRoot: str | None = None, + lc: list[Any] | None = None, ) -> Any: if isinstance(doc, MutableMapping): r: Final[list[Any]] = [] @@ -987,10 +991,10 @@ def load( def _document_load( loader: _Loader, - doc: Union[str, MutableMapping[str, Any], MutableSequence[Any]], + doc: str | MutableMapping[str, Any] | MutableSequence[Any], baseuri: str, loadingOptions: LoadingOptions, - addl_metadata_fields: Optional[MutableSequence[str]] = None, + addl_metadata_fields: MutableSequence[str] | None = None, ) -> tuple[Any, LoadingOptions]: if isinstance(doc, str): return _document_load_by_url( @@ -1059,7 +1063,7 @@ def _document_load_by_url( loader: _Loader, url: str, loadingOptions: LoadingOptions, - addl_metadata_fields: Optional[MutableSequence[str]] = None, + addl_metadata_fields: MutableSequence[str] | None = None, ) -> tuple[Any, LoadingOptions]: if url in loadingOptions.idx: return loadingOptions.idx[url] @@ -1114,7 +1118,7 @@ def save_relative_uri( uri: Any, base_url: str, scoped_id: bool, - ref_scope: Optional[int], + ref_scope: int | None, relative_uris: bool, ) -> Any: """Convert any URI to a relative one, obeying the scoping rules.""" diff --git a/schema_salad/ref_resolver.py b/schema_salad/ref_resolver.py index f516bd21f..74c64e3d4 100644 --- a/schema_salad/ref_resolver.py +++ b/schema_salad/ref_resolver.py @@ -7,9 +7,9 @@ import traceback import urllib import xml.sax # nosec -from collections.abc import MutableMapping, MutableSequence +from collections.abc import Callable, MutableMapping, MutableSequence from io import StringIO -from typing import Any, Callable, Final, Optional, Union, cast +from typing import Any, Final, Optional, cast import requests from cachecontrol.caches import SeparateBodyFileCache @@ -93,7 +93,7 @@ def to_validation_exception(e: MarkedYAMLError) -> ValidationException: return exc -class NormDict(dict[str, Union[CommentedMap, CommentedSeq, str, None]]): +class NormDict(dict[str, CommentedMap | CommentedSeq | str | None]): """A Dict where all keys are normalized using the provided function.""" def __init__(self, normalize: Callable[[str], str] = str) -> None: @@ -144,17 +144,17 @@ class Loader: def __init__( self, ctx: ContextType, - schemagraph: Optional[Graph] = None, - foreign_properties: Optional[set[str]] = None, - idx: Optional[IdxType] = None, - cache: Optional[CacheType] = None, - session: Optional[requests.sessions.Session] = None, - fetcher_constructor: Optional[FetcherCallableType] = None, - skip_schemas: Optional[bool] = None, - url_fields: Optional[set[str]] = None, - allow_attachments: Optional[AttachmentsType] = None, - doc_cache: Union[str, bool] = True, - salad_version: Optional[str] = None, + schemagraph: Graph | None = None, + foreign_properties: set[str] | None = None, + idx: IdxType | None = None, + cache: CacheType | None = None, + session: requests.sessions.Session | None = None, + fetcher_constructor: FetcherCallableType | None = None, + skip_schemas: bool | None = None, + url_fields: set[str] | None = None, + allow_attachments: AttachmentsType | None = None, + doc_cache: str | bool = True, + salad_version: str | None = None, ) -> None: self.idx: Final[IdxType] = NormDict(_url_norm) if idx is None else idx @@ -194,7 +194,7 @@ def __init__( self.vocab_fields: Final[set[str]] = set() self.identifiers: Final[list[str]] = [] self.identity_links: Final[set[str]] = set() - self.standalone: Optional[set[str]] = None + self.standalone: set[str] | None = None self.nolinkcheck: Final[set[str]] = set() self.vocab: Final[dict[str, str]] = {} self.rvocab: Final[dict[str, str]] = {} @@ -219,7 +219,7 @@ def expand_url( base_url: str, scoped_id: bool = False, vocab_term: bool = False, - scoped_ref: Optional[int] = None, + scoped_ref: int | None = None, ) -> str: if url in ("@id", "@type"): return url @@ -288,7 +288,7 @@ def add_namespaces(self, ns: dict[str, str]) -> None: """Add the given namespace to our vocab list.""" self.vocab.update(ns) - def add_schemas(self, ns: Union[list[str], str], base_url: str) -> None: + def add_schemas(self, ns: list[str] | str, base_url: str) -> None: """Fetch external schemas and add them to the graph.""" if self.skip_schemas: return @@ -419,17 +419,17 @@ def add_context(self, newcontext: ContextType) -> None: def resolve_ref( self, ref: ResolveType, - base_url: Optional[str] = None, + base_url: str | None = None, checklinks: bool = True, strict_foreign_properties: bool = False, - content_types: Optional[list[str]] = None, # Expected content-types + content_types: list[str] | None = None, # Expected content-types ) -> ResolvedRefType: lref = ref - obj: Optional[CommentedMap] = None + obj: CommentedMap | None = None resolved_obj: ResolveType = None imp = False inc = False - mixin: Optional[MutableMapping[str, str]] = None + mixin: MutableMapping[str, str] | None = None if not base_url: base_url = file_uri(os.getcwd()) + "/" @@ -487,7 +487,7 @@ def resolve_ref( if url in self.idx and (not mixin): resolved_obj = self.idx[url] if isinstance(resolved_obj, MutableMapping): - metadata: Union[CommentedMap, CommentedSeq, str, None] = self.idx.get( + metadata: CommentedMap | CommentedSeq | str | None = self.idx.get( urllib.parse.urldefrag(url)[0], CommentedMap() ) if isinstance(metadata, MutableMapping): @@ -599,7 +599,7 @@ def _resolve_idmap( ls = CommentedSeq() for k in sorted(idmapFieldValue.keys()): val = idmapFieldValue[k] - v: Optional[CommentedMap] = None + v: CommentedMap | None = None if not isinstance(val, CommentedMap): if idmapField in loader.mapPredicate: v = CommentedMap(((loader.mapPredicate[idmapField], val),)) @@ -632,10 +632,10 @@ def _resolve_idmap( def _type_dsl( self, - t: Union[str, CommentedMap, CommentedSeq], + t: str | CommentedMap | CommentedSeq, lc: LineCol, filename: str, - ) -> Union[str, CommentedMap, CommentedSeq]: + ) -> str | CommentedMap | CommentedSeq: if not isinstance(t, str): return t @@ -660,7 +660,7 @@ def _type_dsl( cmap.lc.add_kv_line_col("type", lc) cmap.lc.add_kv_line_col("items", lc) cmap.lc.filename = filename - expanded: Union[str, CommentedMap, CommentedSeq] = cmap + expanded: str | CommentedMap | CommentedSeq = cmap else: expanded = t_ @@ -669,7 +669,7 @@ def _type_dsl( cs.lc.add_kv_line_col(0, lc) cs.lc.add_kv_line_col(1, lc) cs.lc.filename = filename - ret: Union[str, CommentedMap, CommentedSeq] = cs + ret: str | CommentedMap | CommentedSeq = cs else: ret = expanded @@ -677,14 +677,14 @@ def _type_dsl( def _secondaryFile_dsl( self, - t: Union[str, CommentedMap, CommentedSeq], + t: str | CommentedMap | CommentedSeq, lc: LineCol, filename: str, - ) -> Union[str, CommentedMap, CommentedSeq]: + ) -> str | CommentedMap | CommentedSeq: if not isinstance(t, str): return t pat: Final = t[0:-1] if t.endswith("?") else t - req: Final[Optional[bool]] = False if t.endswith("?") else None + req: Final[bool | None] = False if t.endswith("?") else None second: Final = CommentedMap((("pattern", pat), ("required", req))) second.lc.add_kv_line_col("pattern", lc) @@ -694,12 +694,12 @@ def _secondaryFile_dsl( def _apply_dsl( self, - datum: Union[str, CommentedMap, CommentedSeq], + datum: str | CommentedMap | CommentedSeq, d: str, loader: "Loader", lc: LineCol, filename: str, - ) -> Union[str, CommentedMap, CommentedSeq]: + ) -> str | CommentedMap | CommentedSeq: if d in loader.type_dsl_fields: return self._type_dsl(datum, lc, filename) if d in loader.secondaryFile_dsl_fields: @@ -778,7 +778,7 @@ def _resolve_identifier(self, document: CommentedMap, loader: "Loader", base_url def _resolve_identity( self, - document: dict[str, Union[str, MutableSequence[Union[str, CommentedMap]]]], + document: dict[str, str | MutableSequence[str | CommentedMap]], loader: "Loader", base_url: str, ) -> None: @@ -806,7 +806,7 @@ def _normalize_fields(self, document: CommentedMap, loader: "Loader") -> None: def _resolve_uris( self, - document: dict[str, Union[str, MutableSequence[Union[str, CommentedMap]]]], + document: dict[str, str | MutableSequence[str | CommentedMap]], loader: "Loader", base_url: str, ) -> None: @@ -837,7 +837,7 @@ def resolve_all( self, document: ResolveType, base_url: str, - file_base: Optional[str] = None, + file_base: str | None = None, checklinks: bool = True, strict_foreign_properties: bool = False, ) -> ResolvedRefType: @@ -1002,7 +1002,7 @@ def fetch( self, url: str, inject_ids: bool = True, - content_types: Optional[list[str]] = None, + content_types: list[str] | None = None, ) -> IdxResultType: if url in self.idx: return self.idx[url] @@ -1012,7 +1012,7 @@ def fetch( textIO.name = str(url) yaml: Final = yaml_no_ts() attachments: Final = yaml.load_all(textIO) - result: Final = cast(Union[CommentedSeq, CommentedMap], next(attachments)) + result: Final = cast(CommentedSeq | CommentedMap, next(attachments)) if self.allow_attachments is not None and self.allow_attachments(result): i = 1 @@ -1062,12 +1062,12 @@ def validate_scoped(self, field: str, link: str, docid: str) -> str: def validate_link( self, field: str, - link: Union[str, CommentedSeq, CommentedMap], + link: str | CommentedSeq | CommentedMap, # link also can be None, but that results in # mypyc "error: Local variable "link" has inferred type None; add an annotation" docid: str, all_doc_ids: dict[str, str], - ) -> Union[str, CommentedSeq, CommentedMap]: + ) -> str | CommentedSeq | CommentedMap: if field in self.nolinkcheck: return link if isinstance(link, str): @@ -1105,7 +1105,7 @@ def validate_link( ) return link - def getid(self, d: Any) -> Optional[str]: + def getid(self, d: Any) -> str | None: """Use our identifiers to extract the first match from the document.""" if isinstance(d, MutableMapping): for i in self.identifiers: @@ -1201,7 +1201,7 @@ def validate_links( def _copy_dict_without_key( - from_dict: Union[CommentedMap, ContextType], filtered_key: str + from_dict: CommentedMap | ContextType, filtered_key: str ) -> CommentedMap: new_dict = CommentedMap(from_dict.items()) if filtered_key in new_dict: diff --git a/schema_salad/schema.py b/schema_salad/schema.py index 44aafc0b4..986fedf61 100644 --- a/schema_salad/schema.py +++ b/schema_salad/schema.py @@ -4,7 +4,7 @@ import hashlib from collections.abc import Mapping, MutableMapping, MutableSequence from importlib.resources import files -from typing import IO, Any, Final, Optional, Union, cast +from typing import IO, Any, Final, TypeAlias, cast from urllib.parse import urlparse from ruamel.yaml.comments import CommentedMap, CommentedSeq @@ -79,8 +79,7 @@ saladp + "Any", } - -cached_metaschema: Optional[tuple[Names, list[dict[str, str]], Loader]] = None +cached_metaschema: tuple[Names, list[dict[str, str]], Loader] | None = None def get_metaschema() -> tuple[Names, list[dict[str, str]], Loader]: @@ -239,12 +238,12 @@ def collect_namespaces(metadata: Mapping[str, Any]) -> dict[str, str]: return namespaces -schema_type = tuple[Loader, Union[Names, SchemaParseException], dict[str, Any], Loader] +schema_type: TypeAlias = tuple[Loader, Names | SchemaParseException, dict[str, Any], Loader] def load_schema( schema_ref: ResolveType, - cache: Optional[CacheType] = None, + cache: CacheType | None = None, ) -> schema_type: """ Load a schema that can be used to validate documents using load_and_validate. @@ -284,7 +283,7 @@ def load_schema( def load_and_validate( document_loader: Loader, avsc_names: Names, - document: Union[CommentedMap, str], + document: CommentedMap | str, strict: bool, strict_foreign_properties: bool = False, ) -> tuple[Any, dict[str, Any]]: @@ -417,7 +416,7 @@ def validate_doc( raise ValidationException("", None, anyerrors, "*") -def get_anon_name(rec: MutableMapping[str, Union[str, dict[str, str], list[str]]]) -> str: +def get_anon_name(rec: MutableMapping[str, str | dict[str, str] | list[str]]) -> str: """Calculate a reproducible name for anonymous types.""" if "name" in rec: name: Final = rec["name"] @@ -518,7 +517,7 @@ def avro_field_name(url: str) -> str: return d.path.split("/")[-1] -Avro = Union[MutableMapping[str, Any], MutableSequence[Any], str] +Avro: TypeAlias = MutableMapping[str, Any] | MutableSequence[Any] | str def make_valid_avro( @@ -527,8 +526,8 @@ def make_valid_avro( found: set[str], union: bool = False, fielddef: bool = False, - vocab: Optional[dict[str, str]] = None, -) -> Union[Avro, MutableMapping[str, str], str, list[Union[Any, MutableMapping[str, str], str]]]: + vocab: dict[str, str] | None = None, +) -> Avro | MutableMapping[str, str] | str | list[Any | MutableMapping[str, str] | str]: """Convert our schema to be more avro like.""" if vocab is None: _, _, metaschema_loader = get_metaschema() @@ -729,7 +728,7 @@ def extend_and_specialize(items: list[dict[str, Any]], loader: Loader) -> list[d def make_avro( i: list[dict[str, Any]], loader: Loader, - metaschema_vocab: Optional[dict[str, str]] = None, + metaschema_vocab: dict[str, str] | None = None, ) -> list[Any]: j: Final = extend_and_specialize(i, loader) @@ -749,7 +748,7 @@ def make_avro( def make_avro_schema( - i: list[Any], loader: Loader, metaschema_vocab: Optional[dict[str, str]] = None + i: list[Any], loader: Loader, metaschema_vocab: dict[str, str] | None = None ) -> Names: """ All in one convenience function. @@ -763,7 +762,7 @@ def make_avro_schema( return names -def make_avro_schema_from_avro(avro: list[Union[Avro, dict[str, str], str]]) -> Names: +def make_avro_schema_from_avro(avro: list[Avro | dict[str, str] | str]) -> Names: """Create avro.schema.Names from the given definitions.""" names: Final = Names() make_avsc_object(convert_to_dict(avro), names) diff --git a/schema_salad/sourceline.py b/schema_salad/sourceline.py index 23d9ede0e..3aecdd6d5 100644 --- a/schema_salad/sourceline.py +++ b/schema_salad/sourceline.py @@ -1,7 +1,7 @@ import os import re -from collections.abc import MutableMapping, MutableSequence -from typing import Any, AnyStr, Callable, Final, Optional, Union +from collections.abc import Callable, MutableMapping, MutableSequence +from typing import Any, AnyStr, Final import ruamel.yaml from ruamel.yaml.comments import CommentedBase, CommentedMap, CommentedSeq @@ -31,7 +31,7 @@ def add_lc_filename(r: ruamel.yaml.comments.CommentedBase, source: str) -> None: _add_lc_filename(r, relname(source)) -def reflow_all(text: str, maxline: Optional[int] = None) -> str: +def reflow_all(text: str, maxline: int | None = None) -> str: """Reflow text respecting a common prefix with line & col info.""" if maxline is None: maxline = int(os.environ.get("COLUMNS", "100")) @@ -43,8 +43,8 @@ def reflow_all(text: str, maxline: Optional[int] = None) -> str: group = g.group(1) assert group is not None # nosec maxno = max(maxno, len(group)) - maxno_text: Final = maxline - maxno - msg: Final[list[str]] = [] + maxno_text = maxline - maxno + msg: list[str] = [] for line in text.splitlines(): g = lineno_re.match(line) if not g: @@ -59,7 +59,7 @@ def reflow_all(text: str, maxline: Optional[int] = None) -> str: return "\n".join(msg) -def reflow(text: str, maxline: int, shift: Optional[str] = "") -> str: +def reflow(text: str, maxline: int, shift: str | None = "") -> str: """Reflow a single line of text.""" maxline2: Final = max(maxline, 20) if len(text) > maxline2: @@ -103,7 +103,7 @@ def strip_duplicated_lineno(text: str) -> str: Same as :py:meth:`strip_dup_lineno` but without reflow. """ - pre: Optional[str] = None + pre: str | None = None msg: Final = [] for line in text.splitlines(): g = lineno_re.match(line) @@ -122,11 +122,11 @@ def strip_duplicated_lineno(text: str) -> str: return "\n".join(msg) -def strip_dup_lineno(text: str, maxline: Optional[int] = None) -> str: +def strip_dup_lineno(text: str, maxline: int | None = None) -> str: """Strip duplicated line numbers.""" if maxline is None: maxline = int(os.environ.get("COLUMNS", "100")) - pre: Optional[str] = None + pre: str | None = None msg: Final = [] maxno = 0 for line in text.splitlines(): @@ -163,10 +163,10 @@ def strip_dup_lineno(text: str, maxline: Optional[int] = None) -> str: def cmap( - d: Union[int, float, str, MutableMapping[str, Any], MutableSequence[Any], None], - lc: Optional[list[int]] = None, - fn: Optional[str] = None, -) -> Union[int, float, str, CommentedMap, CommentedSeq, None]: + d: int | float | str | MutableMapping[str, Any] | MutableSequence[Any] | None, + lc: list[int] | None = None, + fn: str | None = None, +) -> int | float | str | CommentedMap | CommentedSeq | None: """ Apply line+column & filename data through to the provided data. @@ -232,7 +232,7 @@ class SourceLine: def __init__( self, item: Any, - key: Optional[Any] = None, + key: Any | None = None, raise_type: Callable[[str], Any] = str, include_traceback: bool = False, ) -> None: @@ -254,13 +254,13 @@ def __exit__( return raise self.makeError(str(exc_value)) from exc_value - def file(self) -> Optional[str]: + def file(self) -> str | None: """Return the embedded filename.""" if hasattr(self.item, "lc") and hasattr(self.item.lc, "filename"): return str(self.item.lc.filename) return None - def start(self) -> Optional[tuple[int, int]]: + def start(self) -> tuple[int, int] | None: """Determine the starting location.""" if self.file() is None: return None @@ -271,7 +271,7 @@ def start(self) -> Optional[tuple[int, int]]: (self.item.lc.data[self.key][1] or 0) + 1, ) - def end(self) -> Optional[tuple[int, int]]: + def end(self) -> tuple[int, int] | None: """Empty, for now.""" return None diff --git a/schema_salad/tests/test_codegen_errors.py b/schema_salad/tests/test_codegen_errors.py index 00b496bb4..f71a45db3 100644 --- a/schema_salad/tests/test_codegen_errors.py +++ b/schema_salad/tests/test_codegen_errors.py @@ -203,8 +203,8 @@ def load_document_by_uri(tmp_path: Path, path: Path) -> Any: def python_codegen( file_uri: str, target: Path, - parser_info: Optional[str] = None, - package: Optional[str] = None, + parser_info: str | None = None, + package: str | None = None, ) -> None: document_loader, avsc_names, schema_metadata, metaschema_loader = load_schema(file_uri) assert isinstance(avsc_names, Names) diff --git a/schema_salad/tests/test_cpp_codegen.py b/schema_salad/tests/test_cpp_codegen.py index db27e5803..df1c7961e 100644 --- a/schema_salad/tests/test_cpp_codegen.py +++ b/schema_salad/tests/test_cpp_codegen.py @@ -3,7 +3,7 @@ import filecmp import os from pathlib import Path -from typing import Any, Optional, cast +from typing import Any, cast import pytest @@ -107,8 +107,8 @@ def test_cwl_cpp_generations_with_spdx(tmp_path: Path) -> None: def cpp_codegen( file_uri: str, target: Path, - spdx_copyright_text: Optional[list[str]] = None, - spdx_license_identifier: Optional[str] = None, + spdx_copyright_text: list[str] | None = None, + spdx_license_identifier: str | None = None, ) -> None: """Help using the C++ code generation function.""" document_loader, avsc_names, schema_metadata, metaschema_loader = load_schema(file_uri) diff --git a/schema_salad/tests/test_dotnet_codegen.py b/schema_salad/tests/test_dotnet_codegen.py index 90d07f0b8..d76f40637 100644 --- a/schema_salad/tests/test_dotnet_codegen.py +++ b/schema_salad/tests/test_dotnet_codegen.py @@ -1,6 +1,6 @@ import shutil from pathlib import Path -from typing import Any, Optional, cast +from typing import Any, cast from schema_salad import codegen from schema_salad.schema import load_schema @@ -70,7 +70,7 @@ def test_class_field(tmp_path: Path) -> None: ) -def dotnet_codegen(file_uri: str, target: Path, examples: Optional[Path] = None) -> None: +def dotnet_codegen(file_uri: str, target: Path, examples: Path | None = None) -> None: document_loader, avsc_names, schema_metadata, metaschema_loader = load_schema(file_uri) schema_raw_doc = metaschema_loader.fetch(file_uri) schema_doc, schema_metadata = metaschema_loader.resolve_all(schema_raw_doc, file_uri) diff --git a/schema_salad/tests/test_fetch.py b/schema_salad/tests/test_fetch.py index cfce9e85f..d13be24a1 100644 --- a/schema_salad/tests/test_fetch.py +++ b/schema_salad/tests/test_fetch.py @@ -1,5 +1,4 @@ import os -from typing import Optional from urllib.parse import urljoin, urlsplit import pytest @@ -14,11 +13,11 @@ class testFetcher(Fetcher): def __init__( self, cache: CacheType, - session: Optional[requests.sessions.Session], + session: requests.sessions.Session | None, ) -> None: pass - def fetch_text(self, url: str, content_types: Optional[list[str]] = None) -> str: + def fetch_text(self, url: str, content_types: list[str] | None = None) -> str: if url == "keep:abc+123/foo.txt": return "hello: keepfoo" if url.endswith("foo.txt"): @@ -47,11 +46,11 @@ class CWLTestFetcher(Fetcher): def __init__( self, cache: CacheType, - session: Optional[requests.sessions.Session], + session: requests.sessions.Session | None, ) -> None: pass - def fetch_text(self, url: str, content_types: Optional[list[str]] = None) -> str: + def fetch_text(self, url: str, content_types: list[str] | None = None) -> str: if url == "baz:bar/foo.cwl": return """ cwlVersion: v1.0 diff --git a/schema_salad/tests/test_java_codegen.py b/schema_salad/tests/test_java_codegen.py index 09dcb0380..eaf686eab 100644 --- a/schema_salad/tests/test_java_codegen.py +++ b/schema_salad/tests/test_java_codegen.py @@ -1,6 +1,6 @@ import shutil from pathlib import Path -from typing import Any, Optional, cast +from typing import Any, cast from schema_salad import codegen from schema_salad.schema import load_schema @@ -36,7 +36,7 @@ def test_meta_schema_gen(tmp_path: Path) -> None: assert src_dir.exists() -def java_codegen(file_uri: str, target: Path, examples: Optional[Path] = None) -> None: +def java_codegen(file_uri: str, target: Path, examples: Path | None = None) -> None: document_loader, avsc_names, schema_metadata, metaschema_loader = load_schema(file_uri) schema_raw_doc = metaschema_loader.fetch(file_uri) schema_doc, schema_metadata = metaschema_loader.resolve_all(schema_raw_doc, file_uri) diff --git a/schema_salad/tests/test_makedoc.py b/schema_salad/tests/test_makedoc.py index abc6ffae4..887b00868 100644 --- a/schema_salad/tests/test_makedoc.py +++ b/schema_salad/tests/test_makedoc.py @@ -18,7 +18,6 @@ import tempfile from io import StringIO from pathlib import Path -from typing import Optional import pytest @@ -37,7 +36,7 @@ def test_schema_salad_inherit_docs() -> None: assert 1 == stdout.getvalue().count("Parent ID") -def generate_doc(schema_data: Optional[str] = None) -> str: +def generate_doc(schema_data: str | None = None) -> str: """Avoid error when calling fixture directly.""" stdout = StringIO() if schema_data: diff --git a/schema_salad/tests/test_misc.py b/schema_salad/tests/test_misc.py index ad958c727..69ace9e00 100644 --- a/schema_salad/tests/test_misc.py +++ b/schema_salad/tests/test_misc.py @@ -1,5 +1,3 @@ -from typing import Optional, Union - from rdflib.graph import Graph from schema_salad.avro.schema import Names @@ -20,7 +18,7 @@ def test_load_schema_cache() -> None: path1 = get_path("tests/test_schema/misc_schema_v1.yml") with path1.open() as f: - cache1: Optional[dict[str, Union[str, Graph, bool]]] = {schemaid: f.read()} + cache1: dict[str, str | Graph | bool] | None = {schemaid: f.read()} document_loader, avsc_names, schema_metadata, metaschema_loader = load_schema( schemaid, cache=cache1 @@ -31,7 +29,7 @@ def test_load_schema_cache() -> None: path2 = get_path("tests/test_schema/misc_schema_v2.yml") with path2.open() as f: - cache2: Optional[dict[str, Union[str, Graph, bool]]] = {schemaid: f.read()} + cache2: dict[str, str | Graph | bool] | None = {schemaid: f.read()} document_loader, avsc_names, schema_metadata, metaschema_loader = load_schema( schemaid, cache=cache2 diff --git a/schema_salad/tests/test_python_codegen.py b/schema_salad/tests/test_python_codegen.py index 889828663..e93fe53e0 100644 --- a/schema_salad/tests/test_python_codegen.py +++ b/schema_salad/tests/test_python_codegen.py @@ -1,7 +1,7 @@ import inspect import os from pathlib import Path -from typing import Any, Optional, cast +from typing import Any, cast from rdflib import Graph from rdflib.compare import to_isomorphic @@ -58,8 +58,8 @@ def test_meta_schema_gen_no_base(tmp_path: Path) -> None: def python_codegen( file_uri: str, target: Path, - parser_info: Optional[str] = None, - package: Optional[str] = None, + parser_info: str | None = None, + package: str | None = None, ) -> None: document_loader, avsc_names, schema_metadata, metaschema_loader = load_schema(file_uri) assert isinstance(avsc_names, Names) diff --git a/schema_salad/tests/test_real_cwl.py b/schema_salad/tests/test_real_cwl.py index 014040a7e..95de4e0dd 100644 --- a/schema_salad/tests/test_real_cwl.py +++ b/schema_salad/tests/test_real_cwl.py @@ -4,7 +4,7 @@ run individually as py.test -k tests/test_real_cwl.py """ -from typing import Any, Optional, Union +from typing import Any import pytest @@ -20,9 +20,9 @@ class TestRealWorldCWL: document_loader: Loader - avsc_names: Union[Names, SchemaParseException, None] = None - schema_metadata: Optional[dict[str, Any]] = None - metaschema_loader: Optional[Loader] = None + avsc_names: Names | SchemaParseException | None = None + schema_metadata: dict[str, Any] | None = None + metaschema_loader: Loader | None = None @classmethod def setup_class(cls) -> None: diff --git a/schema_salad/tests/test_ref_resolver.py b/schema_salad/tests/test_ref_resolver.py index ffc625e72..dfe3ca480 100644 --- a/schema_salad/tests/test_ref_resolver.py +++ b/schema_salad/tests/test_ref_resolver.py @@ -5,7 +5,7 @@ import sys import tempfile from pathlib import Path -from typing import Any, Union +from typing import Any import pytest from _pytest.fixtures import FixtureRequest @@ -218,7 +218,7 @@ def test_attachments() -> None: content = f1.read() assert {"foo": "bar", "baz": content, "quux": content} == r1 - def aa1(item: Union[CommentedMap, CommentedSeq]) -> bool: + def aa1(item: CommentedMap | CommentedSeq) -> bool: return bool(item["foo"] == "bar") l2 = Loader({}, allow_attachments=aa1) @@ -229,7 +229,7 @@ def aa1(item: Union[CommentedMap, CommentedSeq]) -> bool: "quux": "This is the [second attachment].", } == r2 - def aa2(item: Union[CommentedMap, CommentedSeq]) -> bool: + def aa2(item: CommentedMap | CommentedSeq) -> bool: return bool(item["foo"] == "baz") l3 = Loader({}, allow_attachments=aa2) diff --git a/schema_salad/tests/test_schemas_directive.py b/schema_salad/tests/test_schemas_directive.py index 6ddd82de6..ab1a84575 100644 --- a/schema_salad/tests/test_schemas_directive.py +++ b/schema_salad/tests/test_schemas_directive.py @@ -5,7 +5,7 @@ """ import os -from typing import Any, Optional, Union +from typing import Any from schema_salad.avro.schema import Names, SchemaParseException from schema_salad.ref_resolver import Loader @@ -20,9 +20,9 @@ class TestSchemasDirective: """Ensure codegen-produced parsers accept $schemas directives""" document_loader: Loader - avsc_names: Union[Names, SchemaParseException, None] = None - schema_metadata: Optional[dict[str, Any]] = None - metaschema_loader: Optional[Loader] = None + avsc_names: Names | SchemaParseException | None = None + schema_metadata: dict[str, Any] | None = None + metaschema_loader: Loader | None = None @classmethod def setup_class(cls) -> None: diff --git a/schema_salad/tests/test_typescript_codegen.py b/schema_salad/tests/test_typescript_codegen.py index 49f4cb2ab..26c69a2bc 100644 --- a/schema_salad/tests/test_typescript_codegen.py +++ b/schema_salad/tests/test_typescript_codegen.py @@ -1,6 +1,6 @@ import shutil from pathlib import Path -from typing import Any, Optional, cast +from typing import Any, cast from schema_salad import codegen from schema_salad.schema import load_schema @@ -72,7 +72,7 @@ def test_class_field(tmp_path: Path) -> None: ) -def typescript_codegen(file_uri: str, target: Path, examples: Optional[Path] = None) -> None: +def typescript_codegen(file_uri: str, target: Path, examples: Path | None = None) -> None: document_loader, avsc_names, schema_metadata, metaschema_loader = load_schema(file_uri) schema_raw_doc = metaschema_loader.fetch(file_uri) schema_doc, schema_metadata = metaschema_loader.resolve_all(schema_raw_doc, file_uri) diff --git a/schema_salad/typescript_codegen.py b/schema_salad/typescript_codegen.py index e81f46406..48ab365f2 100644 --- a/schema_salad/typescript_codegen.py +++ b/schema_salad/typescript_codegen.py @@ -7,7 +7,7 @@ from importlib.resources import files from io import StringIO from pathlib import Path -from typing import Any, Final, Optional, Union +from typing import Any, Final from . import _logger, schema from .codegen_base import CodeGenBase, LazyInitDef, TypeDef @@ -17,7 +17,7 @@ from .utils import Traversable -def doc_to_doc_string(doc: Optional[str], indent_level: int = 0) -> str: +def doc_to_doc_string(doc: str | None, indent_level: int = 0) -> str: """Generate a documentation string from a schema salad doc field.""" lead: Final = " " + " " * indent_level + "* " if doc: @@ -79,9 +79,7 @@ def doc_to_doc_string(doc: Optional[str], indent_level: int = 0) -> str: class TypeScriptCodeGen(CodeGenBase): """Generation of TypeScript code for a given Schema Salad definition.""" - def __init__( - self, base: str, examples: Optional[str], target: Optional[str], package: str - ) -> None: + def __init__(self, base: str, examples: str | None, target: str | None, package: str) -> None: """Initialize the TypeScript codegen.""" super().__init__() self.target_dir: Final = Path(target or ".").resolve() @@ -357,30 +355,29 @@ def end_class(self, classname: str, field_names: list[str]) -> None: def type_loader( self, - type_declaration: Union[list[Any], dict[str, Any], str], - container: Optional[str] = None, - no_link_check: Optional[bool] = None, + type_declaration: list[Any] | dict[str, Any] | str, + container: str | None = None, + no_link_check: bool | None = None, ) -> TypeDef: """Parse the given type declaration and declare its components.""" - if isinstance(type_declaration, MutableSequence): - sub_types1: Final = [self.type_loader(i) for i in type_declaration] - sub_names: Final[list[str]] = list(dict.fromkeys([i.name for i in sub_types1])) - sub_instance_types: Final[list[str]] = list( - dict.fromkeys([i.instance_type for i in sub_types1 if i.instance_type is not None]) - ) - return self.declare_type( - TypeDef( - "unionOf{}".format("Or".join(sub_names)), - "new _UnionLoader([{}])".format(", ".join(sub_names)), - instance_type=" | ".join(sub_instance_types), + match type_declaration: + case MutableSequence(): + sub_types1: Final = [self.type_loader(i) for i in type_declaration] + sub_names: Final[list[str]] = list(dict.fromkeys([i.name for i in sub_types1])) + sub_instance_types: Final[list[str]] = list( + dict.fromkeys( + [i.instance_type for i in sub_types1 if i.instance_type is not None] + ) ) - ) - if isinstance(type_declaration, MutableMapping): - if type_declaration["type"] in ( - "array", - "https://w3id.org/cwl/salad#array", - ): - i1: Final = self.type_loader(type_declaration["items"]) + return self.declare_type( + TypeDef( + "unionOf{}".format("Or".join(sub_names)), + "new _UnionLoader([{}])".format(", ".join(sub_names)), + instance_type=" | ".join(sub_instance_types), + ) + ) + case {"type": "array" | "https://w3id.org/cwl/salad#array", "items": items}: + i1: Final = self.type_loader(items) return self.declare_type( TypeDef( f"arrayOf{i1.name}", @@ -388,11 +385,8 @@ def type_loader( instance_type=f"Array<{i1.instance_type}>", ) ) - if type_declaration["type"] in ( - "map", - "https://w3id.org/cwl/salad#map", - ): - i2: Final = self.type_loader(type_declaration["values"]) + case {"type": "map" | "https://w3id.org/cwl/salad#map", "values": values}: + i2: Final = self.type_loader(values) return self.declare_type( TypeDef( f"mapOf{i2.name}", @@ -408,18 +402,17 @@ def type_loader( instance_type=f"Dictionary<{i2.instance_type}>", ) ) - if type_declaration["type"] in ("enum", "https://w3id.org/cwl/salad#enum"): - return self.type_loader_enum(type_declaration) + case { + "type": "enum" | "https://w3id.org/cwl/salad#enum", + }: + return self.type_loader_enum(type_declaration) # type: ignore[arg-type] - if type_declaration["type"] in ( - "record", - "https://w3id.org/cwl/salad#record", - ): + case {"type": "record" | "https://w3id.org/cwl/salad#record", "name": name, **rest}: return self.declare_type( TypeDef( - self.safe_name(type_declaration["name"]) + "Loader", + self.safe_name(name) + "Loader", "new _RecordLoader({}.fromDoc, {}, {})".format( - self.safe_name(type_declaration["name"]), + self.safe_name(name), ( f"'{container}'" if container is not None @@ -427,16 +420,17 @@ def type_loader( ), # noqa: B907 self.to_typescript(no_link_check), ), - instance_type="Internal." + self.safe_name(type_declaration["name"]), - abstract=type_declaration.get("abstract", False), + instance_type="Internal." + self.safe_name(name), + abstract=rest.get("abstract", False), # type: ignore[arg-type] ) ) - if type_declaration["type"] in ( - "union", - "https://w3id.org/cwl/salad#union", - ): + case { + "type": "union" | "https://w3id.org/cwl/salad#union", + "name": name, + "names": names, + }: # Declare the named loader to handle recursive union definitions - loader_name: Final = self.safe_name(type_declaration["name"]) + "Loader" + loader_name: Final = self.safe_name(name) + "Loader" loader_type: Final = TypeDef( loader_name, "new _UnionLoader([])", @@ -444,7 +438,7 @@ def type_loader( ) self.declare_type(loader_type) # Parse inner types - sub_types2: Final = [self.type_loader(i) for i in type_declaration["names"]] + sub_types2: Final = [self.type_loader(i) for i in names] # Register lazy initialization for the loader self.add_lazy_init( @@ -456,20 +450,22 @@ def type_loader( ) ) return loader_type - raise SchemaException("wft {}".format(type_declaration["type"])) - - if type_declaration in prims: - return prims[type_declaration] - - if type_declaration in ("Expression", "https://w3id.org/cwl/cwl#Expression"): - return self.declare_type( - TypeDef( - self.safe_name(type_declaration) + "Loader", - "new _ExpressionLoader()", - instance_type="string", + case {"type": decl}: + raise SchemaException(f"wtf {decl}") + case str(decl) if decl in prims: + return prims[decl] + case "Expression" | "https://w3id.org/cwl/cwl#Expression" as decl: + return self.declare_type( + TypeDef( + self.safe_name(decl) + "Loader", + "new _ExpressionLoader()", + instance_type="string", + ) ) - ) - return self.collected_types[self.safe_name(type_declaration) + "Loader"] + case str(decl): + return self.collected_types[self.safe_name(decl) + "Loader"] + case _ as decl: + raise SchemaException(f"wtf {decl}") def type_loader_enum(self, type_declaration: dict[str, Any]) -> TypeDef: """Build an enum type loader for the given declaration.""" @@ -510,9 +506,9 @@ def declare_field( self, name: str, fieldtype: TypeDef, - doc: Optional[str], + doc: str | None, optional: bool, - subscope: Optional[str], + subscope: str | None, ) -> None: """Output the code to load the given field.""" safename: Final = self.safe_name(name) @@ -675,7 +671,7 @@ def declare_id_field( self, name: str, fieldtype: TypeDef, - doc: Optional[str], + doc: str | None, optional: bool, ) -> None: """Output the code to handle the given ID field.""" @@ -719,8 +715,8 @@ def uri_loader( inner: TypeDef, scoped_id: bool, vocab_term: bool, - ref_scope: Optional[int], - no_link_check: Optional[bool] = None, + ref_scope: int | None, + no_link_check: bool | None = None, ) -> TypeDef: """Construct the TypeDef for the given URI loader.""" instance_type = inner.instance_type or "any" @@ -742,7 +738,7 @@ def uri_loader( ) def idmap_loader( - self, field: str, inner: TypeDef, map_subject: str, map_predicate: Optional[str] + self, field: str, inner: TypeDef, map_subject: str, map_predicate: str | None ) -> TypeDef: """Construct the TypeDef for the given mapped ID loader.""" instance_type = inner.instance_type or "any" @@ -754,7 +750,7 @@ def idmap_loader( ) ) - def typedsl_loader(self, inner: TypeDef, ref_scope: Optional[int]) -> TypeDef: + def typedsl_loader(self, inner: TypeDef, ref_scope: int | None) -> TypeDef: """Construct the TypeDef for the given DSL loader.""" instance_type = inner.instance_type or "any" return self.declare_type( diff --git a/schema_salad/utils.py b/schema_salad/utils.py index 37718300c..30c909cb2 100644 --- a/schema_salad/utils.py +++ b/schema_salad/utils.py @@ -1,9 +1,9 @@ import json import os import sys -from collections.abc import Iterable, Mapping, MutableSequence +from collections.abc import Callable, Iterable, Mapping, MutableSequence from io import BufferedWriter -from typing import IO, TYPE_CHECKING, Any, Callable, Final, Optional, TypeVar, Union +from typing import IO, TYPE_CHECKING, Any, Final, Optional, TypeAlias, TypeVar, Union import requests from rdflib.graph import Graph @@ -21,18 +21,18 @@ __all__: Final = ["Traversable"] -ContextType = dict[str, Union[dict[str, Any], str, Iterable[str]]] +ContextType: TypeAlias = dict[str, Union[dict[str, Any], str, Iterable[str]]] DocumentType = TypeVar("DocumentType", CommentedSeq, CommentedMap) DocumentOrStrType = TypeVar("DocumentOrStrType", CommentedSeq, CommentedMap, str) FieldType = TypeVar("FieldType", str, CommentedSeq, CommentedMap) -MandatoryResolveType = Union[int, float, str, CommentedMap, CommentedSeq] -ResolveType = Optional[MandatoryResolveType] -ResolvedRefType = tuple[ResolveType, CommentedMap] -IdxResultType = Union[CommentedMap, CommentedSeq, str, None] -IdxType = dict[str, IdxResultType] -CacheType = dict[str, Union[str, Graph, bool]] -FetcherCallableType = Callable[[CacheType, requests.sessions.Session], "Fetcher"] -AttachmentsType = Callable[[Union[CommentedMap, CommentedSeq]], bool] +MandatoryResolveType: TypeAlias = Union[int, float, str, CommentedMap, CommentedSeq] +ResolveType: TypeAlias = Optional[MandatoryResolveType] +ResolvedRefType: TypeAlias = tuple[ResolveType, CommentedMap] +IdxResultType: TypeAlias = Union[CommentedMap, CommentedSeq, str, None] +IdxType: TypeAlias = dict[str, IdxResultType] +CacheType: TypeAlias = dict[str, Union[str, Graph, bool]] +FetcherCallableType: TypeAlias = Callable[[CacheType, requests.sessions.Session], "Fetcher"] +AttachmentsType: TypeAlias = Callable[[Union[CommentedMap, CommentedSeq]], bool] def add_dictlist(di: dict[Any, Any], key: Any, val: Any) -> None: diff --git a/schema_salad/validate.py b/schema_salad/validate.py index 71574ad39..6cc6c26d1 100644 --- a/schema_salad/validate.py +++ b/schema_salad/validate.py @@ -1,7 +1,7 @@ import logging import pprint from collections.abc import Mapping, MutableMapping, MutableSequence -from typing import Any, Final, Optional +from typing import Any, Final from urllib.parse import urlsplit from . import avro @@ -19,10 +19,10 @@ def validate( expected_schema: Schema, datum: Any, - identifiers: Optional[list[str]] = None, + identifiers: list[str] | None = None, strict: bool = False, - foreign_properties: Optional[set[str]] = None, - vocab: Optional[Mapping[str, str]] = None, + foreign_properties: set[str] | None = None, + vocab: Mapping[str, str] | None = None, ) -> bool: if not identifiers: identifiers = [] @@ -114,14 +114,14 @@ def vpformat(datum: Any) -> str: def validate_ex( expected_schema: Schema, datum: Any, - identifiers: Optional[list[str]] = None, + identifiers: list[str] | None = None, strict: bool = False, - foreign_properties: Optional[set[str]] = None, + foreign_properties: set[str] | None = None, raise_ex: bool = True, strict_foreign_properties: bool = False, logger: logging.Logger = _logger, skip_foreign_properties: bool = False, - vocab: Optional[Mapping[str, str]] = None, + vocab: Mapping[str, str] | None = None, ) -> bool: """Determine if a python datum is an instance of a schema.""" debug: Final = _logger.isEnabledFor(logging.DEBUG) diff --git a/setup.py b/setup.py index fce2c5eac..554f66bbe 100644 --- a/setup.py +++ b/setup.py @@ -114,7 +114,7 @@ download_url="https://github.com/common-workflow-language/schema_salad/releases", ext_modules=ext_modules, license="Apache 2.0", - python_requires=">=3.9,<3.15", + python_requires=">=3.10,<3.15", use_scm_version=True, setup_requires=pytest_runner + ["setuptools_scm>=8.0.4,<10"], packages=["schema_salad", "schema_salad.tests", "schema_salad.avro"], @@ -162,11 +162,11 @@ "Operating System :: POSIX", "Operating System :: MacOS :: MacOS X", "Development Status :: 5 - Production/Stable", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", "Typing :: Typed", ], ) diff --git a/tox.ini b/tox.ini index 7f89a7a02..ab1526de9 100644 --- a/tox.ini +++ b/tox.ini @@ -1,9 +1,9 @@ [tox] envlist = - py3{9,10,11,12,13,14}-lint, - py3{9,10,11,12,13,14}-unit, - py3{9,10,11,12,13,14}-mypy, - py3{9,10,11,12,13,14}-memleak, + py3{10,11,12,13,14}-lint, + py3{10,11,12,13,14}-unit, + py3{10,11,12,13,14}-mypy, + py3{10,11,12,13,14}-memleak, lintreadme, pydocstyle @@ -15,7 +15,6 @@ testpaths=schema_salad/tests [gh-actions] python = - 3.9: py39 3.10: py310 3.11: py311 3.12: py312 @@ -24,10 +23,10 @@ python = [testenv] description = - py3{9,10,11,12,13,14}-unit: Run the unit tests - py3{9,10,11,12,13,14}-lint: Lint the Python code and search for common security issues - py3{9,10,11,12,13,14}-mypy: Check for type safety - py3{9,10,11,12,13,14}-memleak: Simple test for memory leaks with mypyc + py3{10,11,12,13,14}-unit: Run the unit tests + py3{10,11,12,13,14}-lint: Lint the Python code and search for common security issues + py3{10,11,12,13,14}-mypy: Check for type safety + py3{10,11,12,13,14}-memleak: Simple test for memory leaks with mypyc pydocstyle: docstring style checker lintreadme: Lint the README.rst->.md conversion @@ -35,36 +34,36 @@ passenv = CI GITHUB_* deps = - py3{9,10,11,12,13,14}-{unit,mypy}: -rrequirements.txt - py3{9,10,11,12,13,14}-{unit,mypy}: -rtest-requirements.txt - py3{9,10,11,12,13,14}-lint: -rlint-requirements.txt - py3{9,10,11,12,13,14}-{mypy,memleak,lint}: -rmypy-requirements.txt - py3{9,10,11,12,13,14}-memleak: cwl-utils - py3{9,10,11,12,13,14}-memleak: objgraph + py3{10,11,12,13,14}-{unit,mypy}: -rrequirements.txt + py3{10,11,12,13,14}-{unit,mypy}: -rtest-requirements.txt + py3{10,11,12,13,14}-lint: -rlint-requirements.txt + py3{10,11,12,13,14}-{mypy,memleak,lint}: -rmypy-requirements.txt + py3{10,11,12,13,14}-memleak: cwl-utils + py3{10,11,12,13,14}-memleak: objgraph # don't forget to update dev-requirements.txt as well setenv = py3{8,9,10,11,12,13}-unit: LC_ALL = C.UTF-8 commands = - py3{9,10,11,12,13,14}-unit: python -m pip install -U pip setuptools wheel - py3{9,10,11,12,13,14}-unit: make --always-make coverage-report coverage.xml PYTEST_EXTRA="{posargs}" - py3{9,10,11,12,13,14}-lint: make bandit flake8 format-check diff_pylint_report diff_pydocstyle_report - py3{9,10,11,12,13,14}-mypy: make mypy mypyc - py3{9,10,11,12,13,14}-memleak: make mypyi - py3{9,10,11,12,13,14}-memleak: python schema_salad/tests/memory-leak-check.py schema_salad/tests/test_real_cwl/ICGC-TCGA-PanCancer/complete/preprocess_vcf.cwl + py3{10,11,12,13,14}-unit: python -m pip install -U pip setuptools wheel + py3{10,11,12,13,14}-unit: make --always-make coverage-report coverage.xml PYTEST_EXTRA="{posargs}" + py3{10,11,12,13,14}-lint: make bandit flake8 format-check diff_pylint_report diff_pydocstyle_report + py3{10,11,12,13,14}-mypy: make mypy mypyc + py3{10,11,12,13,14}-memleak: make mypyi + py3{10,11,12,13,14}-memleak: python schema_salad/tests/memory-leak-check.py schema_salad/tests/test_real_cwl/ICGC-TCGA-PanCancer/complete/preprocess_vcf.cwl allowlist_externals = - py3{9,10,11,12,13,14}-lint: flake8 - py3{9,10,11,12,13,14}-lint: black - py3{9,10,11,12,13,14}-{mypy,memleak,shellcheck,lint,unit}: make + py3{10,11,12,13,14}-lint: flake8 + py3{10,11,12,13,14}-lint: black + py3{10,11,12,13,14}-{mypy,memleak,shellcheck,lint,unit}: make lintreadme: make skip_install = - py3{9,10,11,12,13,14}-lint: true + py3{10,11,12,13,14}-lint: true extras = - py3{9,10,11,12,13,14}-unit: pycodegen + py3{10,11,12,13,14}-unit: pycodegen [testenv:pydocstyle] allowlist_externals = make