code: def _add_functionality():
      def generic_get_cl_version(self):
          import re
  
          version_string = self.version
          match = re.match(
              r"^OpenCL ([0-9]+)\.([0-9]+) .*$", version_string
          )
          if match is None:
              raise RuntimeError(
                  "%s %s returned non-conformant "
                  "platform version string '%s'"
                  % (type(self).__name__, self, version_string)
              )
  
          return int(match.group(1)), int(match.group(2))
  
      # {{{ Platform
  
      def platform_repr(self):
          return (
              f"<pyopencl.Platform '{self.name}' at 0x{self.int_ptr:x}>"
          )
  
      Platform.__repr__ = platform_repr
      Platform._get_cl_version = generic_get_cl_version
  
      # }}}
  
      # {{{ Device
  
      def device_repr(self):
          return "<pyopencl.Device '{}' on '{}' at 0x{:x}>".format(
              self.name.strip(),
              self.platform.name.strip(),
              self.int_ptr,
          )
  
      def device_hashable_model_and_version_identifier(self):
          return (
              "v1",
              self.vendor,
              self.vendor_id,
              self.name,
              self.version,
          )
  
      def device_persistent_unique_id(self):
          warn(
              "Device.persistent_unique_id is deprecated. "
              "Use Device.hashable_model_and_version_identifier instead.",
              DeprecationWarning,
              stacklevel=2,
          )
          return device_hashable_model_and_version_identifier(self)
  
      Device.__repr__ = device_repr
  
      # undocumented for now:
      Device._get_cl_version = generic_get_cl_version
      Device.hashable_model_and_version_identifier = property(
          device_hashable_model_and_version_identifier
      )
      Device.persistent_unique_id = property(
          device_persistent_unique_id
      )
  
      # }}}
  
      # {{{ Context
  
      context_old_init = Context.__init__
  
      def context_init(
          self, devices, properties, dev_type, cache_dir=None
      ):
          if cache_dir is not None:
              warn(
                  "The 'cache_dir' argument to the Context constructor "
                  "is deprecated and no longer has an effect. "
                  "It was removed because it only applied to the wrapper "
                  "object and not the context itself, leading to inconsistencies.",
                  DeprecationWarning,
                  stacklevel=2,
              )
  
          context_old_init(self, devices, properties, dev_type)
  
      def context_repr(self):
          return "<pyopencl.Context at 0x{:x} on {}>".format(
              self.int_ptr, ", ".join(repr(dev) for dev in self.devices)
          )
  
      def context_get_cl_version(self):
          return self.devices[0].platform._get_cl_version()
  
      Context.__repr__ = context_repr
      from pytools import memoize_method
  
      Context._get_cl_version = memoize_method(context_get_cl_version)
  
      # }}}
  
      # {{{ CommandQueue
  
      def command_queue_enter(self):
          return self
  
      def command_queue_exit(self, exc_type, exc_val, exc_tb):
          self.finish()
          self._finalize()
  
      def command_queue_get_cl_version(self):
          return self.device._get_cl_version()
  
      CommandQueue.__enter__ = command_queue_enter
      CommandQueue.__exit__ = command_queue_exit
      CommandQueue._get_cl_version = memoize_method(
          command_queue_get_cl_version
      )
  
      # }}}
  
      # {{{ _Program (the internal, non-caching version)
  
      def program_get_build_logs(self):
          build_logs = []
          for dev in self.get_info(_cl.program_info.DEVICES):
              try:
                  log = self.get_build_info(dev, program_build_info.LOG)
              except Exception:
                  log = "<error retrieving log>"
  
              build_logs.append((dev, log))
  
          return build_logs
  
      def program_build(self, options_bytes, devices=None):
          err = None
          try:
              self._build(options=options_bytes, devices=devices)
          except Error as e:
              msg = (
                  str(e)
                  + "\n\n"
                  + (75 * "=" + "\n").join(
                      f"Build on {dev}:\n\n{log}"
                      for dev, log in self._get_build_logs()
                  )
              )
              code = e.code
              routine = e.routine
  
              err = _cl.RuntimeError(
                  _cl._ErrorRecord(msg=msg, code=code, routine=routine)
              )
  
          if err is not None:
              # Python 3.2 outputs the whole list of currently active exceptions
              # This serves to remove one (redundant) level from that nesting.
              raise err
  
          message = (75 * "=" + "\n").join(
              f"Build on {dev} succeeded, but said:\n\n{log}"
              for dev, log in self._get_build_logs()
              if log is not None and log.strip()
          )
  
          if message:
              if self.kind() == program_kind.SOURCE:
                  build_type = "From-source build"
              elif self.kind() == program_kind.BINARY:
                  build_type = "From-binary build"
              elif self.kind() == program_kind.IL:
                  build_type = "From-IL build"
              else:
                  build_type = "Build"
  
              compiler_output(
                  "%s succeeded, but resulted in non-empty logs:\n%s"
                  % (build_type, message)
              )
  
          return self
  
      _cl._Program._get_build_logs = program_get_build_logs
      _cl._Program.build = program_build
  
      # }}}
  
      # {{{ Event
      class ProfilingInfoGetter:
          def __init__(self, event):
              self.event = event
  
          def __getattr__(self, name):
              info_cls = _cl.profiling_info
  
              try:
                  inf_attr = getattr(info_cls, name.upper())
              except AttributeError:
                  raise AttributeError(
                      "%s has no attribute '%s'" % (type(self), name)
                  )
              else:
                  return self.event.get_profiling_info(inf_attr)
  
      _cl.Event.profile = property(ProfilingInfoGetter)
  
      # }}}
  
      # {{{ Kernel
  
      kernel_old_init = Kernel.__init__
      kernel_old_get_info = Kernel.get_info
      kernel_old_get_work_group_info = Kernel.get_work_group_info
  
      def kernel_init(self, prg, name):
          if not isinstance(prg, _cl._Program):
              prg = prg._get_prg()
  
          kernel_old_init(self, prg, name)
  
          self._setup(prg)
  
      def kernel__setup(self, prg):
          self._source = getattr(prg, "_source", None)
  
          from pyopencl.invoker import generate_enqueue_and_set_args
  
          self._enqueue, self._set_args = generate_enqueue_and_set_args(
              self.function_name,
              self.num_args,
              self.num_args,
              None,
              warn_about_arg_count_bug=None,
              work_around_arg_count_bug=None,
              devs=self.context.devices,
          )
  
          self._wg_info_cache = {}
          return self
  
      def kernel_set_arg_types(self, arg_types):
          arg_types = tuple(arg_types)
  
          # {{{ arg counting bug handling
  
          # For example:
          # https://github.com/pocl/pocl/issues/197
          # (but Apple CPU has a similar bug)
  
          work_around_arg_count_bug = False
          warn_about_arg_count_bug = False
  
          from pyopencl.characterize import has_struct_arg_count_bug
  
          count_bug_per_dev = [
              has_struct_arg_count_bug(dev, self.context)
              for dev in self.context.devices
          ]
  
          from pytools import single_valued
  
          if any(count_bug_per_dev):
              if all(count_bug_per_dev):
                  work_around_arg_count_bug = single_valued(
                      count_bug_per_dev
                  )
              else:
                  warn_about_arg_count_bug = True
  
          # }}}
  
          from pyopencl.invoker import generate_enqueue_and_set_args
  
          enqueue, my_set_args = generate_enqueue_and_set_args(
              self.function_name,
              len(arg_types),
              self.num_args,
              arg_types,
              warn_about_arg_count_bug=warn_about_arg_count_bug,
              work_around_arg_count_bug=work_around_arg_count_bug,
              devs=self.context.devices,
          )
  
          # Make ourselves a kernel-specific class, so that we're able to override
          # __call__. Inspired by https://stackoverflow.com/a/38541437
          class KernelWithCustomEnqueue(type(self)):
              __call__ = enqueue
              set_args = my_set_args
  
          try:
              self.__class__ = KernelWithCustomEnqueue
          except TypeError:
              # __class__ assignment may not work in all cases, due to differing
              # object layouts. Fall back to bouncing through kernel_call below.
              self._enqueue = enqueue
              self.set_args = my_set_args
  
      def kernel_get_work_group_info(self, param, device):
          cache_key = (param, device.int_ptr)
          try:
              return self._wg_info_cache[cache_key]
          except KeyError:
              pass
  
          result = kernel_old_get_work_group_info(self, param, device)
          self._wg_info_cache[cache_key] = result
          return result
  
      def kernel_set_args(self, *args, **kwargs):
          # Need to duplicate the 'self' argument for dynamically generated  method
          return self._set_args(self, *args, **kwargs)
  
      def kernel_call(
          self, queue, global_size, local_size, *args, **kwargs
      ):
          # __call__ can't be overridden directly, so we need this
          # trampoline hack.
  
          # Note: This is only used for the generic __call__, before
          # kernel_set_scalar_arg_dtypes is called.
          return self._enqueue(
              self, queue, global_size, local_size, *args, **kwargs
          )
  
      def kernel_capture_call(
          self,
          output_file,
          queue,
          global_size,
          local_size,
          *args,
          **kwargs,
      ):
          from pyopencl.capture_call import capture_kernel_call
  
          capture_kernel_call(
              self,
              output_file,
              queue,
              global_size,
              local_size,
              *args,
              **kwargs,
          )
  
      def kernel_get_info(self, param_name):
          val = kernel_old_get_info(self, param_name)
  
          if isinstance(val, _Program):
              return Program(val)
          else:
              return val
  
      Kernel.__init__ = kernel_init
      Kernel._setup = kernel__setup
      Kernel.get_work_group_info = kernel_get_work_group_info
  
      # FIXME: Possibly deprecate this version
      Kernel.set_scalar_arg_dtypes = kernel_set_arg_types
      Kernel.set_arg_types = kernel_set_arg_types
  
      Kernel.set_args = kernel_set_args
      Kernel.__call__ = kernel_call
      Kernel.capture_call = kernel_capture_call
      Kernel.get_info = kernel_get_info
  
      # }}}
  
      # {{{ ImageFormat
  
      def image_format_repr(self):
          return "ImageFormat({}, {})".format(
              channel_order.to_string(
                  self.channel_order, "<unknown channel order 0x%x>"
              ),
              channel_type.to_string(
                  self.channel_data_type,
                  "<unknown channel data type 0x%x>",
              ),
          )
  
      def image_format_eq(self, other):
          return (
              self.channel_order == other.channel_order
              and self.channel_data_type == other.channel_data_type
          )
  
      def image_format_ne(self, other):
          return not image_format_eq(self, other)
  
      def image_format_hash(self):
          return hash(
              (type(self), self.channel_order, self.channel_data_type)
          )
  
      ImageFormat.__repr__ = image_format_repr
      ImageFormat.__eq__ = image_format_eq
      ImageFormat.__ne__ = image_format_ne
      ImageFormat.__hash__ = image_format_hash
  
      # }}}
  
      # {{{ Image
  
      image_old_init = Image.__init__
  
      def image_init(
          self,
          context,
          flags,
          format,
          shape=None,
          pitches=None,
          hostbuf=None,
          is_array=False,
          buffer=None,
      ):
          if shape is None and hostbuf is None:
              raise Error(
                  "'shape' must be passed if 'hostbuf' is not given"
              )
  
          if shape is None and hostbuf is not None:
              shape = hostbuf.shape
  
          if hostbuf is not None and not (
              flags & (mem_flags.USE_HOST_PTR | mem_flags.COPY_HOST_PTR)
          ):
              warn(
                  "'hostbuf' was passed, but no memory flags to make use of it.",
                  stacklevel=2,
              )
  
          if hostbuf is None and pitches is not None:
              raise Error(
                  "'pitches' may only be given if 'hostbuf' is given"
              )
  
          if context._get_cl_version() >= (
              1,
              2,
          ) and get_cl_header_version() >= (1, 2):
              if buffer is not None and is_array:
                  raise ValueError(
                      "'buffer' and 'is_array' are mutually exclusive"
                  )
  
              if len(shape) == 3:
                  if buffer is not None:
                      raise TypeError(
                          "'buffer' argument is not supported for 3D arrays"
                      )
                  elif is_array:
                      image_type = mem_object_type.IMAGE2D_ARRAY
                  else:
                      image_type = mem_object_type.IMAGE3D
  
              elif len(shape) == 2:
                  if buffer is not None:
                      raise TypeError(
                          "'buffer' argument is not supported for 2D arrays"
                      )
                  elif is_array:
                      image_type = mem_object_type.IMAGE1D_ARRAY
                  else:
                      image_type = mem_object_type.IMAGE2D
  
              elif len(shape) == 1:
                  if buffer is not None:
                      image_type = mem_object_type.IMAGE1D_BUFFER
                  elif is_array:
                      raise TypeError(
                          "array of zero-dimensional images not supported"
                      )
                  else:
                      image_type = mem_object_type.IMAGE1D
  
              else:
                  raise ValueError(
                      "images cannot have more than three dimensions"
                  )
  
              desc = ImageDescriptor()
  
              desc.image_type = image_type
              desc.shape = shape  # also sets desc.array_size
  
              if pitches is None:
                  desc.pitches = (0, 0)
              else:
                  desc.pitches = pitches
  
              desc.num_mip_levels = 0  # per CL 1.2 spec
              desc.num_samples = 0  # per CL 1.2 spec
              desc.buffer = buffer
  
              image_old_init(
                  self, context, flags, format, desc, hostbuf
              )
          else:
              # legacy init for CL 1.1 and older
              if is_array:
                  raise TypeError(
                      "'is_array=True' is not supported for CL < 1.2"
                  )
              # if num_mip_levels is not None:
              # raise TypeError(
              #       "'num_mip_levels' argument is not supported for CL < 1.2")
              # if num_samples is not None:
              # raise TypeError(
              #        "'num_samples' argument is not supported for CL < 1.2")
              if buffer is not None:
                  raise TypeError(
                      "'buffer' argument is not supported for CL < 1.2"
                  )
  
              image_old_init(
                  self, context, flags, format, shape, pitches, hostbuf
              )
  
      class _ImageInfoGetter:
          def __init__(self, event):
              warn(
                  "Image.image.attr is deprecated and will go away in 2021. "
                  "Use Image.attr directly, instead.",
                  stacklevel=2,
              )
  
              self.event = event
  
          def __getattr__(self, name):
              try:
                  inf_attr = getattr(_cl.image_info, name.upper())
              except AttributeError:
                  raise AttributeError(
                      "%s has no attribute '%s'" % (type(self), name)
                  )
              else:
                  return self.event.get_image_info(inf_attr)
  
      def image_shape(self):
          if self.type == mem_object_type.IMAGE2D:
              return (self.width, self.height)
          elif self.type == mem_object_type.IMAGE3D:
              return (self.width, self.height, self.depth)
          else:
              raise LogicError("only images have shapes")
  
      Image.__init__ = image_init
      Image.image = property(_ImageInfoGetter)
      Image.shape = property(image_shape)
  
      # }}}
  
      # {{{ Error
  
      def error_str(self):
          val = self.what
          try:
              val.routine
          except AttributeError:
              return str(val)
          else:
              result = ""
              if val.code() != status_code.SUCCESS:
                  result = status_code.to_string(
                      val.code(), "<unknown error %d>"
                  )
              routine = val.routine()
              if routine:
                  result = f"{routine} failed: {result}"
              what = val.what()
              if what:
                  if result:
                      result += " - "
                  result += what
              return result
  
      def error_code(self):
          return self.args[0].code()
  
      def error_routine(self):
          return self.args[0].routine()
  
      def error_what(self):
          return self.args[0]
  
      Error.__str__ = error_str
      Error.code = property(error_code)
      Error.routine = property(error_routine)
      Error.what = property(error_what)
  
      # }}}
  
      # {{{ MemoryMap
  
      def memory_map_enter(self):
          return self
  
      def memory_map_exit(self, exc_type, exc_val, exc_tb):
          self.release()
  
      MemoryMap.__doc__ = """
          This class may also be used as a context manager in a ``with`` statement.
          The memory corresponding to this object will be unmapped when
          this object is deleted or :meth:`release` is called.
  
          .. automethod:: release
          """
      MemoryMap.__enter__ = memory_map_enter
      MemoryMap.__exit__ = memory_map_exit
  
      # }}}
  
      # {{{ SVMPointer
  
      if get_cl_header_version() >= (2, 0):
          SVMPointer.__doc__ = """A base class for things that can be passed to
              functions that allow an SVM pointer, e.g. kernel enqueues and memory
              copies.
  
              Objects of this type cannot currently be directly created or
              implemented in Python.  To obtain objects implementing this type,
              consider its subtypes :class:`SVMAllocation` and :class:`SVM`.
  
  
              .. property:: svm_ptr
  
                  Gives the SVM pointer as an :class:`int`.
  
              .. property:: size
  
                  An :class:`int` denoting the size in bytes, or *None*, if the size
                  of the SVM pointed to is not known.
  
                  *Most* objects of this type (e.g. instances of
                  :class:`SVMAllocation` and :class:`SVM` know their size, so that,
                  for example :class:`enqueue_copy` will automatically copy an entire
                  :class:`SVMAllocation` when a size is not explicitly specified.
  
              .. automethod:: map
              .. automethod:: map_ro
              .. automethod:: map_rw
              .. automethod:: as_buffer
              .. property:: buf
  
                  An opaque object implementing the :c:func:`Python buffer protocol
                  <PyObject_GetBuffer>`. It exposes the pointed-to memory as
                  a one-dimensional buffer of bytes, with the size matching
                  :attr:`size`.
  
                  No guarantee is provided that two references to this attribute
                  result in the same object.
              """
  
      def svmptr_map(
          self,
          queue: CommandQueue,
          *,
          flags: int,
          is_blocking: bool = True,
          wait_for: Optional[Sequence[Event]] = None,
          size: Optional[Event] = None,
      ) -> "SVMMap":
          """
          :arg is_blocking: If *False*, subsequent code must wait on
              :attr:`SVMMap.event` in the returned object before accessing the
              mapped memory.
          :arg flags: a combination of :class:`pyopencl.map_flags`.
          :arg size: The size of the map in bytes. If not provided, defaults to
              :attr:`size`.
  
          |std-enqueue-blurb|
          """
          return SVMMap(
              self,
              np.asarray(self.buf),
              queue,
              _cl._enqueue_svm_map(
                  queue, is_blocking, flags, self, wait_for, size=size
              ),
          )
  
      def svmptr_map_ro(
          self,
          queue: CommandQueue,
          *,
          is_blocking: bool = True,
          wait_for: Optional[Sequence[Event]] = None,
          size: Optional[int] = None,
      ) -> "SVMMap":
          """Like :meth:`map`, but with *flags* set for a read-only map."""
  
          return self.map(
              queue,
              flags=map_flags.READ,
              is_blocking=is_blocking,
              wait_for=wait_for,
              size=size,
          )
  
      def svmptr_map_rw(
          self,
          queue: CommandQueue,
          *,
          is_blocking: bool = True,
          wait_for: Optional[Sequence[Event]] = None,
          size: Optional[int] = None,
      ) -> "SVMMap":
          """Like :meth:`map`, but with *flags* set for a read-only map."""
  
          return self.map(
              queue,
              flags=map_flags.READ | map_flags.WRITE,
              is_blocking=is_blocking,
              wait_for=wait_for,
              size=size,
          )
  
      def svmptr__enqueue_unmap(self, queue, wait_for=None):
          return _cl._enqueue_svm_unmap(queue, self, wait_for)
  
      def svmptr_as_buffer(
          self,
          ctx: Context,
          *,
          flags: Optional[int] = None,
          size: Optional[int] = None,
      ) -> Buffer:
          """
          :arg ctx: a :class:`Context`
          :arg flags: a combination of :class:`pyopencl.map_flags`, defaults to
              read-write.
          :arg size: The size of the map in bytes. If not provided, defaults to
              :attr:`size`.
          :returns: a :class:`Buffer` corresponding to *self*.
  
          The memory referred to by this object must not be freed before
          the returned :class:`Buffer` is released.
          """
  
          if flags is None:
              flags = mem_flags.READ_WRITE | mem_flags.USE_HOST_PTR
  
          if size is None:
              size = self.size
  
          return Buffer(ctx, flags, size=size, hostbuf=self.buf)
  
      if get_cl_header_version() >= (2, 0):
          SVMPointer.map = svmptr_map
          SVMPointer.map_ro = svmptr_map_ro
          SVMPointer.map_rw = svmptr_map_rw
          SVMPointer._enqueue_unmap = svmptr__enqueue_unmap
          SVMPointer.as_buffer = svmptr_as_buffer
  
      # }}}
  
      # {{{ SVMAllocation
  
      if get_cl_header_version() >= (2, 0):
          SVMAllocation.__doc__ = """
              Is a :class:`SVMPointer`.
  
              .. versionadded:: 2016.2
  
              .. automethod:: __init__
  
                  :arg flags: See :class:`svm_mem_flags`.
                  :arg queue: If not specified, the allocation will be freed
                      eagerly, irrespective of whether pending/enqueued operations
                      are still using this memory.
  
                      If specified, deallocation of the memory will be enqueued
                      with the given queue, and will only be performed
                      after previously-enqueue operations in the queue have
                      completed.
  
                      It is an error to specify an out-of-order queue.
  
                      .. warning::
  
                          Not specifying a queue will typically lead to undesired
                          behavior, including crashes and memory corruption.
                          See the warning in :ref:`svm`.
  
              .. automethod:: enqueue_release
  
                  Enqueue the release of this allocation into *queue*.
                  If *queue* is not specified, enqueue the deallocation
                  into the queue provided at allocation time or via
                  :class:`bind_to_queue`.
  
              .. automethod:: bind_to_queue
  
                  Change the queue used for implicit enqueue of deallocation
                  to *queue*. Sufficient synchronization is ensured by
                  enqueuing a marker into the old queue and waiting on this
                  marker in the new queue.
  
              .. automethod:: unbind_from_queue
  
                  Configure the allocation to no longer implicitly enqueue
                  memory allocation. If such a queue was previously provided,
                  :meth:`~CommandQueue.finish` is automatically called on it.
              """
  
      # }}}
  
      # {{{ SVM
  
      if get_cl_header_version() >= (2, 0):
          SVM.__doc__ = """Tags an object exhibiting the Python buffer interface
              (such as a :class:`numpy.ndarray`) as referring to shared virtual
              memory.
  
              Is a :class:`SVMPointer`, hence objects of this type may be passed
              to kernel calls and :func:`enqueue_copy`, and all methods declared
              there are also available there. Note that :meth:`map` differs
              slightly from :meth:`SVMPointer.map`.
  
              Depending on the features of the OpenCL implementation, the following
              types of objects may be passed to/wrapped in this type:
  
              *   fine-grain shared memory as returned by (e.g.) :func:`fsvm_empty`,
                  if the implementation supports fine-grained shared virtual memory.
                  This memory may directly be passed to a kernel::
  
                      ary = cl.fsvm_empty(ctx, 1000, np.float32)
                      assert isinstance(ary, np.ndarray)
  
                      prg.twice(queue, ary.shape, None, cl.SVM(ary))
                      queue.finish() # synchronize
                      print(ary) # access from host
  
                  Observe how mapping (as needed in coarse-grain SVM) is no longer
                  necessary.
  
              *   any :class:`numpy.ndarray` (or other Python object with a buffer
                  interface) if the implementation supports fine-grained *system*
                  shared virtual memory.
  
                  This is how plain :mod:`numpy` arrays may directly be passed to a
                  kernel::
  
                      ary = np.zeros(1000, np.float32)
                      prg.twice(queue, ary.shape, None, cl.SVM(ary))
                      queue.finish() # synchronize
                      print(ary) # access from host
  
              *   coarse-grain shared memory as returned by (e.g.) :func:`csvm_empty`
                  for any implementation of OpenCL 2.0.
  
                  .. note::
  
                      Applications making use of coarse-grain SVM may be better
                      served by opaque-style SVM. See :ref:`opaque-svm`.
  
                  This is how coarse-grain SVM may be used from both host and device::
  
                      svm_ary = cl.SVM(
                          cl.csvm_empty(ctx, 1000, np.float32, alignment=64))
                      assert isinstance(svm_ary.mem, np.ndarray)
  
                      with svm_ary.map_rw(queue) as ary:
                          ary.fill(17)  # use from host
  
                      prg.twice(queue, svm_ary.mem.shape, None, svm_ary)
  
              Coarse-grain shared-memory *must* be mapped into host address space
              using :meth:`~SVMPointer.map` before being accessed through the
              :mod:`numpy` interface.
  
              .. note::
  
                  This object merely serves as a 'tag' that changes the behavior
                  of functions to which it is passed. It has no special management
                  relationship to the memory it tags. For example, it is permissible
                  to grab a :class:`numpy.ndarray` out of :attr:`SVM.mem` of one
                  :class:`SVM` instance and use the array to construct another.
                  Neither of the tags need to be kept alive.
  
              .. versionadded:: 2016.2
  
              .. attribute:: mem
  
                  The wrapped object.
  
              .. automethod:: __init__
              .. automethod:: map
              .. automethod:: map_ro
              .. automethod:: map_rw
              """
  
      # }}}
  
      if get_cl_header_version() >= (2, 0):
          svm_old_init = SVM.__init__
  
      def svm_init(self, mem):
          svm_old_init(self, mem)
  
          self.mem = mem
  
      def svm_map(self, queue, flags, is_blocking=True, wait_for=None):
          """
          :arg is_blocking: If *False*, subsequent code must wait on
              :attr:`SVMMap.event` in the returned object before accessing the
              mapped memory.
          :arg flags: a combination of :class:`pyopencl.map_flags`.
          :returns: an :class:`SVMMap` instance
  
          This differs from the inherited :class:`SVMPointer.map` in that no size
          can be specified, and that :attr:`mem` is the exact array produced
          when the :class:`SVMMap` is used as a context manager.
  
          |std-enqueue-blurb|
          """
          return SVMMap(
              self,
              self.mem,
              queue,
              _cl._enqueue_svm_map(
                  queue, is_blocking, flags, self, wait_for
              ),
          )
  
      def svm_map_ro(self, queue, is_blocking=True, wait_for=None):
          """Like :meth:`map`, but with *flags* set for a read-only map."""
  
          return self.map(
              queue,
              map_flags.READ,
              is_blocking=is_blocking,
              wait_for=wait_for,
          )
  
      def svm_map_rw(self, queue, is_blocking=True, wait_for=None):
          """Like :meth:`map`, but with *flags* set for a read-only map."""
  
          return self.map(
              queue,
              map_flags.READ | map_flags.WRITE,
              is_blocking=is_blocking,
              wait_for=wait_for,
          )
  
      def svm__enqueue_unmap(self, queue, wait_for=None):
          return _cl._enqueue_svm_unmap(queue, self, wait_for)
  
      if get_cl_header_version() >= (2, 0):
          SVM.__init__ = svm_init
          SVM.map = svm_map
          SVM.map_ro = svm_map_ro
          SVM.map_rw = svm_map_rw
          SVM._enqueue_unmap = svm__enqueue_unmap
  
      # }}}
  
      # ORDER DEPENDENCY: Some of the above may override get_info, the effect needs
      # to be visible through the attributes. So get_info attr creation needs to happen
      # after the overriding is complete.
      cls_to_info_cls = {
          _cl.Platform: (_cl.Platform.get_info, _cl.platform_info, []),
          _cl.Device: (
              _cl.Device.get_info,
              _cl.device_info,
              ["PLATFORM", "MAX_WORK_GROUP_SIZE", "MAX_COMPUTE_UNITS"],
          ),
          _cl.Context: (_cl.Context.get_info, _cl.context_info, []),
          _cl.CommandQueue: (
              _cl.CommandQueue.get_info,
              _cl.command_queue_info,
              ["CONTEXT", "DEVICE"],
          ),
          _cl.Event: (_cl.Event.get_info, _cl.event_info, []),
          _cl.MemoryObjectHolder: (
              MemoryObjectHolder.get_info,
              _cl.mem_info,
              [],
          ),
          Image: (_cl.Image.get_image_info, _cl.image_info, []),
          Pipe: (_cl.Pipe.get_pipe_info, _cl.pipe_info, []),
          Program: (Program.get_info, _cl.program_info, []),
          Kernel: (Kernel.get_info, _cl.kernel_info, []),
          _cl.Sampler: (Sampler.get_info, _cl.sampler_info, []),
      }
  
      def to_string(cls, value, default_format=None):
          if cls._is_bitfield:
              names = []
              for name in dir(cls):
                  attr = getattr(cls, name)
                  if not isinstance(attr, int):
                      continue
                  if attr == value or attr & value:
                      names.append(name)
              if names:
                  return " | ".join(names)
          else:
              for name in dir(cls):
                  if (
                      not name.startswith("_")
                      and getattr(cls, name) == value
                  ):
                      return name
  
          if default_format is None:
              raise ValueError(
                  "a name for value %d was not found in %s"
                  % (value, cls.__name__)
              )
          else:
              return default_format % value
  
      for cls in CONSTANT_CLASSES:
          cls._is_bitfield = cls in BITFIELD_CONSTANT_CLASSES
          cls.to_string = classmethod(to_string)
  
      # {{{ get_info attributes -------------------------------------------------
  
      def make_getinfo(info_method, info_name, info_attr):
          def result(self):
              return info_method(self, info_attr)
  
          return property(result)
  
      def make_cacheable_getinfo(
          info_method, info_name, cache_attr, info_attr
      ):
          def result(self):
              try:
                  return getattr(self, cache_attr)
              except AttributeError:
                  pass
  
              result = info_method(self, info_attr)
              setattr(self, cache_attr, result)
              return result
  
          return property(result)
  
      for cls, (
          info_method,
          info_class,
          cacheable_attrs,
      ) in cls_to_info_cls.items():
          for info_name, _info_value in info_class.__dict__.items():
              if info_name == "to_string" or info_name.startswith("_"):
                  continue
  
              info_lower = info_name.lower()
              info_constant = getattr(info_class, info_name)
              if info_name in cacheable_attrs:
                  cache_attr = intern("_info_cache_" + info_lower)
                  setattr(
                      cls,
                      info_lower,
                      make_cacheable_getinfo(
                          info_method,
                          info_lower,
                          cache_attr,
                          info_constant,
                      ),
                  )
              else:
                  setattr(
                      cls,
                      info_lower,
                      make_getinfo(
                          info_method, info_name, info_constant
                      ),
                  )
  
      # }}}
  
      if _cl.have_gl():
  
          def gl_object_get_gl_object(self):
              return self.get_gl_object_info()[1]
  
          GLBuffer.gl_object = property(gl_object_get_gl_object)
          GLTexture.gl_object = property(gl_object_get_gl_object)
  
imports: from sys import intern
  from warnings import warn
  from typing import Any, Dict, List, Optional, Sequence, Tuple, Union
  from pyopencl.version import VERSION, VERSION_STATUS, VERSION_TEXT
  import pyopencl.cltypes
  import logging
  import os
  import numpy
  import sys
  from pyopencl._cl import get_cl_header_version, program_kind, status_code, platform_info, device_type, device_info, device_topology_type_amd, device_fp_config, device_mem_cache_type, device_local_mem_type, device_exec_capabilities, device_svm_capabilities, command_queue_properties, context_info, gl_context_info, context_properties, command_queue_info, queue_properties, mem_flags, svm_mem_flags, channel_order, channel_type, mem_object_type, mem_info, image_info, pipe_info, pipe_properties, addressing_mode, filter_mode, sampler_info, sampler_properties, map_flags, program_info, program_build_info, program_binary_type, kernel_info, kernel_arg_info, kernel_arg_address_qualifier, kernel_arg_access_qualifier, kernel_arg_type_qualifier, kernel_work_group_info, kernel_sub_group_info, event_info, command_type, command_execution_status, profiling_info, mem_migration_flags, device_partition_property, device_affinity_domain, device_atomic_capabilities, device_device_enqueue_capabilities, version_bits, khronos_vendor_id, Error, MemoryError, LogicError, RuntimeError, Platform, get_platforms, Device, Context, CommandQueue, LocalMemory, MemoryObjectHolder, MemoryObject, MemoryMap, Buffer, _Program, Kernel, Event, wait_for_events, NannyEvent, enqueue_nd_range_kernel, _enqueue_marker, _enqueue_read_buffer, _enqueue_write_buffer, _enqueue_copy_buffer, _enqueue_read_buffer_rect, _enqueue_write_buffer_rect, _enqueue_copy_buffer_rect, _enqueue_read_image, _enqueue_copy_image, _enqueue_write_image, _enqueue_copy_image_to_buffer, _enqueue_copy_buffer_to_image, have_gl, ImageFormat, get_supported_image_formats, Image, Sampler, Pipe
  import inspect
label_desc: Use image node_id 9 for calling a global function with example usage: _add_functionality() and returns: int,  int, str, self, str, self, str, str, str,  self,  self,  self,  self, device_hashable_model_and_version_identifier, str, self, self, self, build_logs, self, self, result, self, self, str, unknown, unknown, hash, self, self, self, self, SVMMap, self, self, _cl, Buffer, SVMMap, self, self, _cl, property, property, self, Program, val, self,  self, result, unknown, info_method, result, self, self, self, self,  self,  self, str, str, getattr, name