code: def assert_dtype_consistent(expected_dtype, real_dtype):
      cate_dtypes = [pd.CategoricalDtype]
      if cudf:
          cate_dtypes.append(cudf.CategoricalDtype)
      cate_dtypes = tuple(cate_dtypes)
  
      if isinstance(real_dtype, pd.DatetimeTZDtype):
          real_dtype = real_dtype.base
      if expected_dtype != real_dtype:
          if (
              expected_dtype == np.dtype("O")
              and real_dtype.type is np.str_
          ):
              # real dtype is string, this matches expectation
              return
          if expected_dtype is None:
              raise AssertionError("Expected dtype cannot be None")
          if isinstance(real_dtype, cate_dtypes) and isinstance(
              expected_dtype, cate_dtypes
          ):
              return
          if not np.can_cast(
              real_dtype, expected_dtype
          ) and not np.can_cast(expected_dtype, real_dtype):
              raise AssertionError(
                  f"cannot cast between dtype of real dtype {real_dtype} "
                  f"and dtype {expected_dtype} defined in metadata"
              )
  
imports: import fnmatch
  import functools
  import inspect
  import itertools
  import logging
  import os
  import sys
  import time
  import types
  from typing import Dict
  import numpy
  import pandas
  import pytest
  from ..core.operand import OperandStage
  from ..utils import lazy_import
label_desc: Use image node_id 3 for calling the ObjectCheckMixin obj's underlying member method code with example usage: obj.assert_dtype_consistent(expected_dtype, real_dtype) without return types