from __future__ import annotations from collections.abc import Iterable from mypy_extensions import trait from mypy.types import ( AnyType, CallableArgument, CallableType, DeletedType, EllipsisType, ErasedType, Instance, LiteralType, NoneType, Overloaded, Parameters, ParamSpecType, PartialType, PlaceholderType, RawExpressionType, SyntheticTypeVisitor, TupleType, Type, TypeAliasType, TypedDictType, TypeList, TypeType, TypeVarTupleType, TypeVarType, UnboundType, UninhabitedType, UnionType, UnpackType, ) @trait class TypeTraverserVisitor(SyntheticTypeVisitor[None]): """Visitor that traverses all components of a type""" # Atomic types def visit_any(self, t: AnyType, /) -> None: pass def visit_uninhabited_type(self, t: UninhabitedType, /) -> None: pass def visit_none_type(self, t: NoneType, /) -> None: pass def visit_erased_type(self, t: ErasedType, /) -> None: pass def visit_deleted_type(self, t: DeletedType, /) -> None: pass def visit_type_var(self, t: TypeVarType, /) -> None: # Note that type variable values and upper bound aren't treated as # components, since they are components of the type variable # definition. We want to traverse everything just once. t.default.accept(self) def visit_param_spec(self, t: ParamSpecType, /) -> None: t.default.accept(self) def visit_parameters(self, t: Parameters, /) -> None: self.traverse_type_list(t.arg_types) def visit_type_var_tuple(self, t: TypeVarTupleType, /) -> None: t.default.accept(self) def visit_literal_type(self, t: LiteralType, /) -> None: t.fallback.accept(self) # Composite types def visit_instance(self, t: Instance, /) -> None: self.traverse_type_tuple(t.args) def visit_callable_type(self, t: CallableType, /) -> None: # FIX generics self.traverse_type_list(t.arg_types) t.ret_type.accept(self) t.fallback.accept(self) if t.type_guard is not None: t.type_guard.accept(self) if t.type_is is not None: t.type_is.accept(self) def visit_tuple_type(self, t: TupleType, /) -> None: self.traverse_type_list(t.items) t.partial_fallback.accept(self) def visit_typeddict_type(self, t: TypedDictType, /) -> None: self.traverse_types(t.items.values()) t.fallback.accept(self) def visit_union_type(self, t: UnionType, /) -> None: self.traverse_type_list(t.items) def visit_overloaded(self, t: Overloaded, /) -> None: self.traverse_types(t.items) def visit_type_type(self, t: TypeType, /) -> None: t.item.accept(self) # Special types (not real types) def visit_callable_argument(self, t: CallableArgument, /) -> None: t.typ.accept(self) def visit_unbound_type(self, t: UnboundType, /) -> None: self.traverse_type_tuple(t.args) def visit_type_list(self, t: TypeList, /) -> None: self.traverse_type_list(t.items) def visit_ellipsis_type(self, t: EllipsisType, /) -> None: pass def visit_placeholder_type(self, t: PlaceholderType, /) -> None: self.traverse_type_list(t.args) def visit_partial_type(self, t: PartialType, /) -> None: pass def visit_raw_expression_type(self, t: RawExpressionType, /) -> None: pass def visit_type_alias_type(self, t: TypeAliasType, /) -> None: # TODO: sometimes we want to traverse target as well # We need to find a way to indicate explicitly the intent, # maybe make this method abstract (like for TypeTranslator)? self.traverse_type_list(t.args) def visit_unpack_type(self, t: UnpackType, /) -> None: t.type.accept(self) # Helpers def traverse_types(self, types: Iterable[Type], /) -> None: for typ in types: typ.accept(self) def traverse_type_list(self, types: list[Type], /) -> None: # Micro-optimization: Specialized for lists for typ in types: typ.accept(self) def traverse_type_tuple(self, types: tuple[Type, ...], /) -> None: # Micro-optimization: Specialized for tuples for typ in types: typ.accept(self)