Source code for stenotype.backend.typing

from functools import singledispatch
from typing import Union, TypeVar

from . import elements as ste


[docs]@singledispatch def normalize( element: ste.Steno ) -> Union[ste.Dots, ste.Identifier, ste.Generic, ste.Callable]: """Normalize any element representation to the subset supported by typing""" raise NotImplementedError( f"{element.__class__.__name__!r} cannot be represented via typing yet" )
ID = TypeVar("ID", ste.Dots, ste.Identifier)
[docs]@normalize.register(ste.Dots) @normalize.register(ste.Identifier) def normalize_identity(element: ID) -> ID: return element
# typing expressions # ==================
[docs]@normalize.register(ste.Generic) def normalize_generic(element: ste.Generic) -> ste.Generic: return ste.Generic( base=element.base, parameters=tuple(map(normalize, element.parameters)) )
[docs]@normalize.register(ste.Callable) def normalize_callable(element: ste.Callable) -> ste.Callable: if isinstance(element.positional, ste.Dots): return element return ste.Callable( positional=tuple(map(normalize, element.positional)), returns=element.returns )
# stenotype expressions # ===================== # Special Forms # -------------
[docs]@normalize.register(ste.Any) def normalize_any(element: ste.Any) -> ste.Identifier: return ste.Identifier("typing", "Any")
[docs]@normalize.register(ste.Optional) def normalize_optional(element: ste.Optional) -> ste.Generic: return ste.Generic( base=ste.Identifier("typing", "Optional"), parameters=(normalize(element.base),) )
[docs]@normalize.register(ste.Union) def normalize_union(element: ste.Union) -> ste.Generic: return ste.Generic( base=ste.Identifier("typing", "Union"), parameters=tuple(map(normalize, element)), )
# Containers # ----------
[docs]@normalize.register(ste.Tuple) def normalize_tuple(element: ste.Tuple) -> ste.Generic: return ste.Generic( base=ste.Identifier("typing", "Tuple"), parameters=tuple(map(normalize, element.elements)), )
[docs]@normalize.register(ste.List) def normalize_list(element: ste.List) -> ste.Generic: return ste.Generic( base=ste.Identifier("typing", "List"), parameters=(normalize(element.values),) )
[docs]@normalize.register(ste.Dict) def normalize_dict(element: ste.Dict) -> ste.Generic: return ste.Generic( base=ste.Identifier("typing", "Dict"), parameters=(normalize(element.keys), normalize(element.values)), )
[docs]@normalize.register(ste.Set) def normalize_set(element: ste.Set) -> ste.Generic: return ste.Generic( base=ste.Identifier("typing", "Set"), parameters=(normalize(element.values),) )
# Literals # --------
[docs]@normalize.register(ste.Literal) def normalize_literal(element: ste.Literal) -> ste.Generic: return ste.Generic(base=ste.Identifier("typing", "Literal"), parameters=(element,))
# Shorthands # ---------- SHORTHAND = { ste.Iterable: ste.Identifier("typing", "Iterable"), ste.Context: ste.Identifier("typing", "ContextManager"), ste.Awaitable: ste.Identifier("typing", "Awaitable"), ste.AsyncIterable: ste.Identifier("typing", "AsyncIterable"), ste.AsyncContext: ste.Identifier("typing", "AsyncContextManager"), }
[docs]@normalize.register(ste.Iterable) @normalize.register(ste.Context) @normalize.register(ste.Awaitable) @normalize.register(ste.AsyncIterable) @normalize.register(ste.AsyncContext) def normalize_shorthand( element: Union[ ste.Iterable, ste.Context, ste.Awaitable, ste.AsyncIterable, ste.AsyncContext ] ) -> ste.Generic: return ste.Generic( base=SHORTHAND[type(element)], parameters=(normalize(element.base),) )
# Callables # ---------
[docs]@normalize.register(ste.Signature) def normalize_signature(element: ste.Signature): # TODO: declare a ``Protocol`` if ``Callable`` is not enough return _normalize_callable(element)
def _normalize_callable(element: ste.Signature) -> ste.Callable: if element.keywords or element.kwargs: raise ValueError("'typing.Callable' does not support keyword arguments") if element.args: # args may have a name, but it is inconsequential to the call if not isinstance(element.args.base, ste.Any): raise ValueError( "'typing.Callable' does not support typed variadic arguments" ) if element.positional or element.mixed: raise ValueError( "'typing.Callable' does not support explicit and variadic arguments" ) return ste.Callable(positional=ste.Dots(), returns=element.returns) else: # names of arguments may be relevant, do not discard if any(arg.name is not None for arg in element.positional + element.mixed): raise ValueError("'typing.Callable' does not support named arguments") return ste.Callable( positional=tuple(arg.base for arg in element.positional + element.mixed), returns=element.returns, )