| ################################################################################ |
| # 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) |