class_docstr: Class for an `Op` with an external C implementation.
  
  One can inherit from this class, provide its constructor with a path to
  an external C source file and the name of a function within it, and define
  an `Op` for said function.
docstr: Construct name, value pairs that will be turned into macros for use within the `Op`'s code.
  
  The names must be strings that are not a C keyword and the
  values must be strings of literal C representations.
  
  If op uses a :class:`pytensor.graph.params_type.ParamsType` as ``params_type``,
  it returns:
   - a default macro ``PARAMS_TYPE`` which defines the class name of the
     corresponding C struct.
   - a macro ``DTYPE_PARAM_key`` for every ``key`` in the :class:`ParamsType` for which associated
     type implements the method :func:`pytensor.graph.type.CLinkerType.c_element_type`.
     ``DTYPE_PARAM_key`` defines the primitive C type name of an item in a variable
     associated to ``key``.
docstr_tok: ["Construct","name",",","value","pairs","that","will","be","turned","into","macros","for","use","within","the","`","Op","`","'s","code",".","The","names","must","be","strings","that","are","not","a","C","keyword","and","the","values","must","be","strings","of","literal","C","representations",".","If","op","uses","a",":","class",":","`","pytensor.graph.params_type.ParamsType","`","as","``","params_type","``",",","it","returns",":","-","a","default","macro","``","PARAMS_TYPE","``","which","defines","the","class","name","of","the","corresponding","C","struct",".","-","a","macro","``","DTYPE_PARAM_key","``","for","every","``","key","``","in","the",":","class",":","`","ParamsType","`","for","which","associated","type","implements","the","method",":","func",":","`","pytensor.graph.type.CLinkerType.c_element_type","`",".","``","DTYPE_PARAM_key","``","defines","the","primitive","C","type","name","of","an","item","in","a","variable","associated","to","``","key","``","."]
code: def __get_op_params(self) -> list[tuple[str, Any]]:
      """Construct name, value pairs that will be turned into macros for use within the `Op`'s code.
  
      The names must be strings that are not a C keyword and the
      values must be strings of literal C representations.
  
      If op uses a :class:`pytensor.graph.params_type.ParamsType` as ``params_type``,
      it returns:
       - a default macro ``PARAMS_TYPE`` which defines the class name of the
         corresponding C struct.
       - a macro ``DTYPE_PARAM_key`` for every ``key`` in the :class:`ParamsType` for which associated
         type implements the method :func:`pytensor.graph.type.CLinkerType.c_element_type`.
         ``DTYPE_PARAM_key`` defines the primitive C type name of an item in a variable
         associated to ``key``.
  
      """
      params: list[tuple[str, Any]] = []
      if isinstance(self.params_type, ParamsType):
          wrapper = self.params_type
          params.append(("PARAMS_TYPE", wrapper.name))
          for i in range(wrapper.length):
              c_type = wrapper.types[i].c_element_type()
              if c_type:
                  # NB (reminder): These macros are currently used only in ParamsType example test
                  # (`pytensor/graph/tests/test_quadratic_function.c`), to demonstrate how we can
                  # access params dtypes when dtypes may change (e.g. if based on config.floatX).
                  # But in practice, params types generally have fixed types per op.
                  params.append(
                      (
                          "DTYPE_PARAM_" + wrapper.fields[i],
                          c_type,
                      )
                  )
      return params
  
imports: import inspect
  import os
  import re
  import warnings
  from collections.abc import Collection
  from re import Pattern
  from typing import TYPE_CHECKING, Any, Callable, ClassVar, Optional, Union, cast
  import numpy
  from pytensor.configdefaults import config
  from pytensor.graph.basic import Apply, Variable
  from pytensor.graph.op import ComputeMapType, Op, StorageMapType, ThunkType
  from pytensor.graph.type import HasDataType
  from pytensor.graph.utils import MethodNotDefined
  from pytensor.link.c.interface import CLinkerOp
  from pytensor.link.c.params_type import ParamsType
  from pytensor.utils import hash_from_code
label_desc: Use image node_id 4 for calling the ExternalCOp obj's underlying member method code with example usage: obj.__get_op_params() and returns: params