code: def assert_equal(
      actual,
      expected,
      ignore_list_order=False,
      rel=1e-5,
      dict_path="",
      ignore_keys=None,
      **kwargs,
  ):
      """Generic equality function that raises an ``AssertionError`` if the objects are not equal.
  
      Notes
      -----
      Works with pandas.DataFrame, pandas.Series, numpy.ndarray, str, int, float,
      bool, None, or a dictionary or list of such items, with arbitrary nesting
      of dictionaries and lists.
  
      Does not check equivalence of functions, or work with nested numpy arrays.
  
      Parameters
      ----------
      actual : `pandas.DataFrame`, `pandas.Series`, `numpy.array`, `str`, `int`,
       `float`, `bool`, `None`, or a dictionary or list of such items
          Actual value.
      expected : `pandas.DataFrame`, `pandas.Series`, `numpy.array`, `str`, `int`,
       `float`, `bool`, `None`, or a dictionary or list of such items
          Expected value to compare against.
      ignore_list_order : `bool`, optional, default False
          If True, lists are considered equal if they contain the same elements. This option is valid
          only if the list can be sorted (all elements can be compared to each other).
          If False, lists are considered equal if they contain the same elements in the same order.
      rel : `float`, optional, default 1e-5
          To check int and float, passed to ``rel`` argument of `pytest.approx`.
          To check numpy arrays, passed to ``rtol`` argument of
          `numpy.testing.assert_allclose`.
          To check pandas dataframe, series, and index, passed to ``rtol`` argument of
          `pandas.testing.assert_frame_equal`, `pandas.testing.assert_series_equal`,
          `pandas.testing.assert_index_equal`.
      dict_path : `str`, optional, default ""
          Location within nested dictionary of the original call to this function.
          User should not set this parameter.
      ignore_keys : `dict`, optional, default None
          Keys to ignore in equality comparison. This only applies if
          `expected` is a dictionary.
          Does not compare the values of these keys. However,
          still returns false if the key is not present.
  
          Can be a nested dictionary. Terminal keys are those whose values should
          not be compared.
  
  
          If the expected value is an nested dictionary, this dictionary can
          also be nested, with the same structure.
          For example, if expected:
          expected = {
              "k1": {
                  "k1": 1,
                  "k2": [1, 2, 3],
              }
              "k2": {
                  "k1": "abc"
              }
          }
          Then the following ``ignore_keys`` will ignore
          dict["k1"]["k1"] and dict["k2"]["k1"] in the comparison.
          ignore_keys = {
              "k1": {
                  "k1": False  # The value can be anything, the keys determine what's ignored
              },
              "k2": {
                  "k1": "skip"  # The value can be anything, the keys determine what's ignored
              }
          }
          This ``ignore_keys`` will ignore
          dict["k1"] and dict["k2"]["k1"] in the comparison. "k1" is
          ignored entirely because its value is not a dictionary.
          ignore_keys = {
              "k1": None  # The value can be anything, the keys determine what's ignored
              "k2": {
                  "k1": None
              }
          }
      kwargs : keyword args, optional
          Keyword args to pass to `pandas.testing.assert_frame_equal`,
          `pandas.testing.assert_series_equal`.
  
      Raises
      ------
      AssertionError
          If actual does not match expected.
      """
      # a message to add to all error messages
      location = f"dictionary location: {dict_path}"
      message = "" if dict_path == "" else f"Error at {location}.\n"
  
      if expected is None:
          if actual is not None:
              raise AssertionError(
                  f"{message}Actual should be None, found {actual}."
              )
      elif isinstance(expected, pd.DataFrame):
          if not isinstance(actual, pd.DataFrame):
              raise AssertionError(
                  f"{message}Actual should be a pandas DataFrame, found {actual}."
              )
          # leverages pandas assert function and add `message` to the error
          try:
              assert_frame_equal(actual, expected, rtol=rel, **kwargs)
          except AssertionError as e:
              import sys
  
              raise type(e)(f"{e}{message}").with_traceback(
                  sys.exc_info()[2]
              )
      elif isinstance(expected, pd.Series):
          if not isinstance(actual, pd.Series):
              raise AssertionError(
                  f"{message}Actual should be a pandas Series, found {actual}."
              )
          try:
              assert_series_equal(actual, expected, rtol=rel, **kwargs)
          except AssertionError as e:
              import sys
  
              raise type(e)(f"{message}{e}").with_traceback(
                  sys.exc_info()[2]
              )
      elif isinstance(expected, pd.Index):
          if not isinstance(actual, pd.Index):
              raise AssertionError(
                  f"{message}Actual should be a pandas Index, found {actual}."
              )
          try:
              assert_index_equal(actual, expected, rtol=rel, **kwargs)
          except AssertionError as e:
              import sys
  
              raise type(e)(f"{message}{e}").with_traceback(
                  sys.exc_info()[2]
              )
      elif isinstance(expected, np.ndarray):
          if not isinstance(actual, np.ndarray):
              raise AssertionError(
                  f"{message}Actual should be a numpy array, found {actual}."
              )
          np.testing.assert_allclose(
              actual, expected, rtol=rel, err_msg=message
          )
      elif isinstance(expected, (list, tuple)):
          if not isinstance(actual, (list, tuple)):
              raise AssertionError(
                  f"{message}Actual should be a list or tuple, found {actual}."
              )
          if not len(actual) == len(expected):
              raise AssertionError(
                  f"{message}Lists have different length. "
                  f"Actual: {actual}. Expected: {expected}."
              )
          if ignore_list_order:
              # order doesn't matter
              actual = sorted(actual)
              expected = sorted(expected)
          # element-wise comparison
          if ignore_keys is not None:
              warnings.warn(
                  f"At {location}. `ignore_keys` is {ignore_keys}, but found a list. "
                  f"No keys will be ignored"
              )
          for item_actual, item_expected in zip(actual, expected):
              assert_equal(
                  item_actual,
                  item_expected,
                  ignore_list_order=ignore_list_order,
                  rel=rel,
                  dict_path=dict_path,
                  ignore_keys=None,
                  **kwargs,
              )
      elif isinstance(expected, dict):
          # dictionaries are equal if their keys and values are equal
          if not isinstance(actual, dict):
              raise AssertionError(
                  f"{message}Actual should be a dict, found {actual}."
              )
          # checks the keys
          if not actual.keys() == expected.keys():
              raise AssertionError(
                  f"{message}Dict keys do not match. "
                  f"Actual: {actual.keys()}. Expected: {expected.keys()}."
              )
          # check the next level of nesting, if not ignored by `ignore_keys`
          for k, expected_item in expected.items():
              if ignore_keys is not None and k in ignore_keys.keys():
                  if isinstance(ignore_keys[k], dict):
                      # specific keys within the value are ignored
                      new_ignore_keys = ignore_keys[k]
                  else:
                      # the entire value is ignored
                      continue
              else:
                  # the key is not ignored, so its value should be fully compared
                  new_ignore_keys = None
  
              # appends the key to the path
              new_path = (
                  f"{dict_path}['{k}']"
                  if dict_path != ""
                  else f"dict['{k}']"
              )
              assert_equal(
                  actual[k],
                  expected_item,
                  ignore_list_order=ignore_list_order,
                  rel=rel,
                  dict_path=new_path,
                  ignore_keys=new_ignore_keys,
                  **kwargs,
              )
      elif isinstance(expected, (int, float)):
          if not isinstance(actual, (int, float)):
              raise AssertionError(
                  f"{message}Actual should be numeric, found {actual}."
              )
          if not math.isclose(
              actual, expected, rel_tol=rel, abs_tol=0.0
          ):
              raise AssertionError(
                  f"{message}Actual does not match expected. "
                  f"Actual: {actual}. Expected: {expected}."
              )
      else:
          if actual != expected:
              raise AssertionError(
                  f"{message}Actual does not match expected. "
                  f"Actual: {actual}. Expected: {expected}."
              )
  
imports: import copy
  import dataclasses
  import functools
  import math
  import re
  import warnings
  from dataclasses import field
  from typing import List
  import numpy
  import pandas
  from pandas.testing import assert_frame_equal
  from pandas.testing import assert_index_equal
  from pandas.testing import assert_series_equal
label_desc: Use image node_id 6 for calling a global function with example usage: assert_equal(actual, expected, ignore_list_order, rel, dict_path, ignore_keys) without return types