blob: 00a02af823a43ac5aa81cbccc92d05855bf5db24 [file]
################################################################################
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#################################################################################
import typing
from typing import Any
def check_type_match(actual: Any, expect: Any) -> None:
"""Check if the actual type can match the expect type.
Parameters
----------
actual : class or generic type
The actual type to check.
expect : class or generic type
The expect type to satisfy.
"""
# if expect type is Any, all actual type is legal.
if expect is Any:
return
# Handle string annotations from __future__ import annotations
if isinstance(actual, str):
# For string annotations, we need to compare the string representation
# Convert expect type to string for comparison
if hasattr(expect, "__name__"):
expect_str = expect.__name__
else:
expect_str = str(expect)
# Simple string comparison for basic types
if actual == expect_str or actual == str(expect):
return
# For complex types, try to extract the base type name
if "[" in actual:
actual_base = actual.split("[")[0]
if hasattr(expect, "__name__"):
expect_base = expect.__name__
elif hasattr(expect, "_name"):
expect_base = expect._name
else:
expect_base = str(expect).split("[")[0]
if actual_base == expect_base:
return
# If string comparison fails, raise TypeError
raise TypeError()
actual_class = actual
expect_class = expect
# get origin class from generic type.
if typing.get_origin(actual) is not None:
actual_class = typing.get_origin(actual)
if typing.get_origin(expect) is not None:
expect_class = typing.get_origin(expect)
# check Ellipsis(...)
if expect is Ellipsis and actual is Ellipsis:
return
if not issubclass(actual_class, expect_class):
raise TypeError()
# Check type arguments when expect type has.
expect_args = typing.get_args(expect)
if expect_args is not None and len(expect_args) != 0:
if len(typing.get_args(actual)) != len(typing.get_args(expect)):
raise TypeError()
for actual_arg, expect_arg in zip(
typing.get_args(actual), typing.get_args(expect), strict=False
):
check_type_match(actual_arg, expect_arg)