code: def generate_diff(api, other):
      api_classes = set(api["classes"].keys())
      other_classes = set(other["classes"].keys())
      new_classes = api_classes.difference(other_classes)
      removed_classes = set(other_classes).difference(api_classes)
      new_methods = {}
      removed_methods = {}
      changed_methods = {}
      expanded_methods = {}
      expanded_funcs = {}
      changed_funcs = {}
      common = api_classes.intersection(other_classes)
      for key in common:
          current_class = api["classes"][key]
          other_class = other["classes"][key]
          new = set(current_class.keys()).difference(other_class.keys())
          for meth in new:
              new_methods[meth] = current_class[meth]
          removed = set(other_class.keys()).difference(
              current_class.keys()
          )
          for meth in removed:
              removed_methods[meth] = tuple(other_class[meth])
          common_methods = set(other_class.keys()).intersection(
              current_class.keys()
          )
          for meth in common_methods:
              if current_class[meth] != tuple(other_class[meth]):
                  if set(current_class[meth]).issuperset(
                      other_class[meth]
                  ):
                      expanded_methods[key] = set(
                          current_class[meth]
                      ).difference(other_class[meth])
                  else:
                      changed_methods[key] = {
                          "current": current_class[meth],
                          "other": tuple(other_class[meth]),
                      }
  
      api_funcs = set(api["functions"].keys())
      other_funcs = set(other["functions"].keys())
      new_funcs = api_funcs.difference(other_funcs)
      removed_funcs = set(other_funcs).difference(api_funcs)
      common_funcs = api_funcs.intersection(other_funcs)
      for key in common_funcs:
          current_func = api["functions"][key]
          other_func = other["functions"][key]
          if current_func == tuple(other_func):
              continue
          elif set(current_func).issuperset(other_func):
              expanded_funcs[key] = set(current_func).difference(
                  other_func
              )
          else:
              changed_funcs[key] = {
                  "current": current_func,
                  "other": tuple(other_func),
              }
  
      def header(v, first=False):
          return "\n\n" * (not first) + f"\n{v}\n" + "-" * len(v) + "\n"
  
      with open("api-differences.rst", "w", encoding="utf-8") as rst:
          rst.write(header("New Classes", first=True))
          for val in sorted(new_classes):
              rst.write(f"* :class:`{val}`\n")
          rst.write(header("Removed Classes"))
          for val in sorted(removed_classes):
              rst.write(f"* ``{val}``\n")
  
          rst.write(header("New Methods"))
          for val in sorted(new_methods):
              rst.write(f"* :meth:`{val}`\n")
  
          rst.write(header("Removed Methods"))
          for val in sorted(removed_methods):
              rst.write(f"* ``{val}``\n")
  
          rst.write(header("Methods with New Arguments"))
          for val in sorted(expanded_methods):
              args = map(lambda v: f"``{v}``", expanded_methods[val])
              rst.write(f"* :meth:`{val}`: " + ", ".join(args) + "\n")
  
          rst.write(header("Methods with Changed Arguments"))
          for val in sorted(changed_methods):
              rst.write(f"* :meth:`{val}`\n")
              name = val.split(".")[-1]
              args = ", ".join(changed_methods[val]["current"])
              if args.startswith("self"):
                  args = args[4:]
                  if args.startswith(", "):
                      args = args[2:]
              rst.write(f"   * New: ``{name}({args})``\n")
              args = ", ".join(changed_methods[val]["other"])
              if args.startswith("self"):
                  args = args[4:]
                  if args.startswith(", "):
                      args = args[2:]
              rst.write(f"   * Old: ``{name}({args})``\n")
  
          rst.write(header("New Functions"))
          for val in sorted(new_funcs):
              rst.write(f"* :func:`{val}`\n")
          rst.write(header("Removed Functions"))
          for val in sorted(removed_funcs):
              rst.write(f"* ``{val}``\n")
  
          rst.write(header("Functions with New Arguments"))
          for val in sorted(expanded_funcs):
              args = map(lambda v: f"``{v}``", expanded_funcs[val])
              rst.write(f"* :func:`{val}`: " + ", ".join(args) + "\n")
  
          rst.write(header("Functions with Changed Arguments"))
          for val in sorted(changed_funcs):
              rst.write(f"* :func:`{val}`\n")
              name = val.split(".")[-1]
              args = ", ".join(changed_funcs[val]["current"])
              rst.write(f"   * New: ``{name}({args})``\n")
              args = ", ".join(changed_funcs[val]["other"])
              rst.write(f"   * Old: ``{name}({args})``\n")
  
file_path: statsmodels/tools/releasing/enumerate-api.py
imports: from setuptools import find_packages
  import argparse
  import importlib
  import inspect
  import json
  import logging
  import os
  from pkgutil import iter_modules
  import sys
label_desc: Use image node_id 4 for calling a global function with example usage: generate_diff(api, other) and returns: unknown