class_docstr: Generates and executes code that performs prefix sums ("scans") on
  arbitrary types, with many possible tweaks.
  
  Usage example::
  
      from pyopencl.scan import GenericScanKernel
      knl = GenericScanKernel(
              context, np.int32,
              arguments="__global int *ary",
              input_expr="ary[i]",
              scan_expr="a+b", neutral="0",
              output_statement="ary[i+1] = item;")
  
      a = cl.array.arange(queue, 10000, dtype=np.int32)
      knl(a, queue=queue)
  
  .. automethod:: __init__
  .. automethod:: __call__
class_docstr_tok: ["Generates","and","executes","code","that","performs","prefix","sums","(","``","scans","''",")","on","arbitrary","types",",","with","many","possible","tweaks",".","Usage","example",":",":","from","pyopencl.scan","import","GenericScanKernel","knl","=","GenericScanKernel","(","context",",","np.int32",",","arguments=","''","__global","int","*","ary","''",",","input_expr=","''","ary","[","i","]","''",",","scan_expr=","''","a+b","''",",","neutral=","''","0","''",",","output_statement=","''","ary","[","i+1","]","=","item",";","''",")","a","=","cl.array.arange","(","queue",",","10000",",","dtype=np.int32",")","knl","(","a",",","queue=queue",")","..","automethod",":",":","__init__","..","automethod",":",":","__call__"]
code: def finish_setup(self) -> None:
      # Before generating the kernel, see if it's cached.
      from pyopencl.cache import get_device_cache_id
  
      devices_key = tuple(
          get_device_cache_id(device) for device in self.devices
      )
  
      cache_key = (self.kernel_key, devices_key)
      from_cache = False
  
      if not cl._PYOPENCL_NO_CACHE:
          try:
              result = generic_scan_kernel_cache[cache_key]
              from_cache = True
              logger.debug(
                  "cache hit for generated scan kernel '%s'"
                  % self.name_prefix
              )
              (
                  self.first_level_scan_gen_info,
                  self.second_level_scan_gen_info,
                  self.final_update_gen_info,
              ) = result
          except KeyError:
              pass
  
      if not from_cache:
          logger.debug(
              "cache miss for generated scan kernel '%s'"
              % self.name_prefix
          )
          self._finish_setup_impl()
  
          result = (
              self.first_level_scan_gen_info,
              self.second_level_scan_gen_info,
              self.final_update_gen_info,
          )
  
          generic_scan_kernel_cache.store_if_not_present(
              cache_key, result
          )
  
      # Build the kernels.
      self.first_level_scan_info = self.first_level_scan_gen_info.build(
          self.context, self.options
      )
      del self.first_level_scan_gen_info
  
      self.second_level_scan_info = (
          self.second_level_scan_gen_info.build(
              self.context, self.options
          )
      )
      del self.second_level_scan_gen_info
  
      self.final_update_info = self.final_update_gen_info.build(
          self.context, self.options
      )
      del self.final_update_gen_info
  
imports: from abc import ABC, abstractmethod
  from dataclasses import dataclass
  from typing import Any, Dict, List, Optional, Set, Tuple, Union
  import numpy
  import pyopencl
  import pyopencl.array
  from pyopencl.tools import KernelTemplateBase, DtypedArgument, bitlog2, context_dependent_memoize, dtype_to_ctype, get_arg_list_scalar_arg_dtypes, get_arg_offset_adjuster_code, _process_code_for_macro, _NumpyTypesKeyBuilder
  import pyopencl._mymako
  from pyopencl._cluda import CLUDA_PREAMBLE
  from pytools.persistent_dict import WriteOncePersistentDict
  import logging
label_desc: Use image node_id 1 for calling the GenericScanKernel obj's underlying member method code with example usage: obj.finish_setup() without return types