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: Loads the C code to perform the `Op`.
docstr_tok: ["Loads","the","C","code","to","perform","the","`","Op","`","."]
code: def load_c_code(self, func_files: list[str]) -> None:
      """Loads the C code to perform the `Op`."""
      func_files = [self.get_path(f) for f in func_files]
      for func_file in func_files:
          with open(func_file) as f:
              self.func_codes.append(f.read())
  
      # If both the old section markers and the new section markers are
      # present, raise an error because we don't know which ones to follow.
      old_markers_present = False
      new_markers_present = False
      for code in self.func_codes:
          if self.backward_re.search(code):
              old_markers_present = True
          if self.section_re.search(code):
              new_markers_present = True
  
      if old_markers_present and new_markers_present:
          raise ValueError(
              "Both the new and the old syntax for "
              "identifying code sections are present in the "
              "provided C code. These two syntaxes should not "
              "be used at the same time."
          )
  
      for i, code in enumerate(self.func_codes):
          if self.backward_re.search(code):
              # This is backward compat code that will go away in a while
  
              # Separate the code into the proper sections
              split = self.backward_re.split(code)
              n = 1
              while n < len(split):
                  if split[n] == "APPLY":
                      self.code_sections["support_code_apply"] = split[
                          n + 1
                      ]
                  elif split[n] == "SUPPORT":
                      self.code_sections["support_code"] = split[n + 1]
                  n += 2
              continue
  
          elif self.section_re.search(code):
              # Check for code outside of the supported sections
              split = self.section_re.split(code)
              if split[0].strip() != "":
                  raise ValueError(
                      "Stray code before first #section "
                      f"statement (in file {func_files[i]}): {split[0]}"
                  )
  
              # Separate the code into the proper sections
              n = 1
              while n < len(split):
                  if split[n] not in self.SECTIONS:
                      raise ValueError(
                          f"Unknown section type (in file {func_files[i]}): {split[n]}"
                      )
                  if split[n] not in self.code_sections:
                      self.code_sections[split[n]] = ""
                  self.code_sections[split[n]] += split[n + 1]
                  n += 2
  
          else:
              raise ValueError(
                  f"No valid section marker was found in file {func_files[i]}"
              )
  
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 3 for calling the ExternalCOp obj's underlying member method code with example usage: obj.load_c_code(func_files) without return types