class_docstr: Maintains a stack of applied transforms to data.
  
  Data can be one of two types:
      1. A `MetaTensor` (this is the preferred data type).
      2. A dictionary of data containing arrays/tensors and auxiliary metadata. In
          this case, a key must be supplied (this dictionary-based approach is deprecated).
  
  If `data` is of type `MetaTensor`, then the applied transform will be added to ``data.applied_operations``.
  
  If `data` is a dictionary, then one of two things can happen:
      1. If data[key] is a `MetaTensor`, the applied transform will be added to ``data[key].applied_operations``.
      2. Else, the applied transform will be appended to an adjacent list using
          `trace_key`. If, for example, the key is `image`, then the transform
          will be appended to `image_transforms` (this dictionary-based approach is deprecated).
  
  Hopefully it is clear that there are three total possibilities:
      1. data is `MetaTensor`
      2. data is dictionary, data[key] is `MetaTensor`
      3. data is dictionary, data[key] is not `MetaTensor` (this is a deprecated approach).
  
  The ``__call__`` method of this transform class must be implemented so
  that the transformation information is stored during the data transformation.
  
  The information in the stack of applied transforms must be compatible with the
  default collate, by only storing strings, numbers and arrays.
  
  `tracing` could be enabled by `self.set_tracing` or setting
  `MONAI_TRACE_TRANSFORM` when initializing the class.
docstr: Update a stack of applied/pending transforms metadata of ``data``.
  
  Args:
      data: dictionary of data or `MetaTensor`.
      key: if data is a dictionary, data[key] will be modified.
      sp_size: the expected output spatial size when the transform is applied.
          it can be tensor or numpy, but will be converted to a list of integers.
      affine: the affine representation of the (spatial) transform in the image space.
          When the transform is applied, meta_tensor.affine will be updated to ``meta_tensor.affine @ affine``.
      extra_info: if desired, any extra information pertaining to the applied
          transform can be stored in this dictionary. These are often needed for
          computing the inverse transformation.
      orig_size: sometimes during the inverse it is useful to know what the size
          of the original image was, in which case it can be supplied here.
      transform_info: info from self.get_transform_info().
      lazy: whether to push the transform to pending_operations or applied_operations.
  
  Returns:
  
      For backward compatibility, if ``data`` is a dictionary, it returns the dictionary with
      updated ``data[key]``. Otherwise, this function returns a MetaObj with updated transform metadata.
docstr_tok: ["Update","a","stack","of","applied\/pending","transforms","metadata","of","``","data","``",".","Args",":","data",":","dictionary","of","data","or","`","MetaTensor","`",".","key",":","if","data","is","a","dictionary",",","data","[","key","]","will","be","modified",".","sp_size",":","the","expected","output","spatial","size","when","the","transform","is","applied",".","it","can","be","tensor","or","numpy",",","but","will","be","converted","to","a","list","of","integers",".","affine",":","the","affine","representation","of","the","(","spatial",")","transform","in","the","image","space",".","When","the","transform","is","applied",",","meta_tensor.affine","will","be","updated","to","``","meta_tensor.affine","@","affine","``",".","extra_info",":","if","desired",",","any","extra","information","pertaining","to","the","applied","transform","can","be","stored","in","this","dictionary",".","These","are","often","needed","for","computing","the","inverse","transformation",".","orig_size",":","sometimes","during","the","inverse","it","is","useful","to","know","what","the","size","of","the","original","image","was",",","in","which","case","it","can","be","supplied","here",".","transform_info",":","info","from","self.get_transform_info","(",")",".","lazy",":","whether","to","push","the","transform","to","pending_operations","or","applied_operations",".","Returns",":","For","backward","compatibility",",","if","``","data","``","is","a","dictionary",",","it","returns","the","dictionary","with","updated","``","data","[","key","]","``",".","Otherwise",",","this","function","returns","a","MetaObj","with","updated","transform","metadata","."]
code: def track_transform_meta(
      cls,
      data,
      key: Hashable = None,
      sp_size=None,
      affine=None,
      extra_info: dict | None = None,
      orig_size: tuple | None = None,
      transform_info=None,
      lazy=False,
  ):
      """
      Update a stack of applied/pending transforms metadata of ``data``.
  
      Args:
          data: dictionary of data or `MetaTensor`.
          key: if data is a dictionary, data[key] will be modified.
          sp_size: the expected output spatial size when the transform is applied.
              it can be tensor or numpy, but will be converted to a list of integers.
          affine: the affine representation of the (spatial) transform in the image space.
              When the transform is applied, meta_tensor.affine will be updated to ``meta_tensor.affine @ affine``.
          extra_info: if desired, any extra information pertaining to the applied
              transform can be stored in this dictionary. These are often needed for
              computing the inverse transformation.
          orig_size: sometimes during the inverse it is useful to know what the size
              of the original image was, in which case it can be supplied here.
          transform_info: info from self.get_transform_info().
          lazy: whether to push the transform to pending_operations or applied_operations.
  
      Returns:
  
          For backward compatibility, if ``data`` is a dictionary, it returns the dictionary with
          updated ``data[key]``. Otherwise, this function returns a MetaObj with updated transform metadata.
      """
      data_t = (
          data[key] if key is not None else data
      )  # compatible with the dict data representation
      out_obj = MetaObj()
      # after deprecating metadict, we should always convert data_t to metatensor here
      if isinstance(data_t, MetaTensor):
          out_obj.copy_meta_from(data_t, keys=out_obj.__dict__.keys())
  
      if lazy and (not get_track_meta()):
          warnings.warn(
              "metadata is not tracked, please call 'set_track_meta(True)' if doing lazy evaluation."
          )
  
      if (
          not lazy
          and affine is not None
          and isinstance(data_t, MetaTensor)
      ):
          # not lazy evaluation, directly update the metatensor affine (don't push to the stack)
          orig_affine = data_t.peek_pending_affine()
          orig_affine = convert_to_dst_type(
              orig_affine, affine, dtype=torch.float64
          )[0]
          try:
              affine = orig_affine @ to_affine_nd(
                  len(orig_affine) - 1, affine, dtype=torch.float64
              )
          except RuntimeError as e:
              if orig_affine.ndim > 2:
                  if data_t.is_batch:
                      msg = "Transform applied to batched tensor, should be applied to instances only"
                  else:
                      msg = "Mismatch affine matrix, ensured that the batch dimension is not included in the calculation."
                  raise RuntimeError(msg) from e
              else:
                  raise
          out_obj.meta[MetaKeys.AFFINE] = convert_to_tensor(
              affine, device=torch.device("cpu"), dtype=torch.float64
          )
  
      if not (
          get_track_meta()
          and transform_info
          and transform_info.get(TraceKeys.TRACING)
      ):
          if isinstance(data, Mapping):
              if not isinstance(data, dict):
                  data = dict(data)
              data[key] = (
                  data_t.copy_meta_from(out_obj)
                  if isinstance(data_t, MetaTensor)
                  else data_t
              )
              return data
          return out_obj  # return with data_t as tensor if get_track_meta() is False
  
      info = transform_info.copy()
      # track the current spatial shape
      if orig_size is not None:
          info[TraceKeys.ORIG_SIZE] = orig_size
      elif isinstance(data_t, MetaTensor):
          info[TraceKeys.ORIG_SIZE] = data_t.peek_pending_shape()
      elif hasattr(data_t, "shape"):
          info[TraceKeys.ORIG_SIZE] = data_t.shape[1:]
  
      # add lazy status to the transform info
      info[TraceKeys.LAZY] = lazy
  
      # include extra_info
      if extra_info is not None:
          extra_info.pop(LazyAttr.SHAPE, None)
          extra_info.pop(LazyAttr.AFFINE, None)
          info[TraceKeys.EXTRA_INFO] = extra_info
  
      # push the transform info to the applied_operation or pending_operation stack
      if lazy:
          if sp_size is None:
              if LazyAttr.SHAPE not in info:
                  info[LazyAttr.SHAPE] = info.get(
                      TraceKeys.ORIG_SIZE, []
                  )
          else:
              info[LazyAttr.SHAPE] = sp_size
          info[LazyAttr.SHAPE] = tuple(
              convert_to_numpy(
                  info[LazyAttr.SHAPE], wrap_sequence=True
              ).tolist()
          )
          if affine is None:
              if LazyAttr.AFFINE not in info:
                  info[
                      LazyAttr.AFFINE
                  ] = MetaTensor.get_default_affine()
          else:
              info[LazyAttr.AFFINE] = affine
          info[LazyAttr.AFFINE] = convert_to_tensor(
              info[LazyAttr.AFFINE], device=torch.device("cpu")
          )
          out_obj.push_pending_operation(info)
      else:
          if out_obj.pending_operations:
              transform_name = (
                  info.get(TraceKeys.CLASS_NAME, "")
                  if isinstance(info, dict)
                  else ""
              )
              msg = (
                  f"Transform {transform_name} has been applied to a MetaTensor with pending operations: "
                  f"{[x.get(TraceKeys.CLASS_NAME) for x in out_obj.pending_operations]}"
              )
              if key is not None:
                  msg += f" for key {key}"
  
              pend = out_obj.pending_operations[-1]
              statuses = pend.get(TraceKeys.STATUSES, dict())
              messages = statuses.get(
                  TraceStatusKeys.PENDING_DURING_APPLY, list()
              )
              messages.append(msg)
              statuses[TraceStatusKeys.PENDING_DURING_APPLY] = messages
              info[TraceKeys.STATUSES] = statuses
          out_obj.push_applied_operation(info)
      if isinstance(data, Mapping):
          if not isinstance(data, dict):
              data = dict(data)
          if isinstance(data_t, MetaTensor):
              data[key] = data_t.copy_meta_from(out_obj)
          else:
              x_k = TraceableTransform.trace_key(key)
              if x_k not in data:
                  data[x_k] = []  # If this is the first, create list
              data[x_k].append(info)
          return data
      return out_obj
  
imports: from __future__ import annotations
  import warnings
  from collections.abc import Hashable, Mapping
  from contextlib import contextmanager
  from typing import Any
  import torch
  from monai import transforms
  from monai.data.meta_obj import MetaObj, get_track_meta
  from monai.data.meta_tensor import MetaTensor
  from monai.data.utils import to_affine_nd
  from monai.transforms.traits import InvertibleTrait
  from monai.transforms.transform import Transform
  from monai.utils import LazyAttr, MetaKeys, TraceKeys, TraceStatusKeys, convert_to_dst_type, convert_to_numpy, convert_to_tensor
  from monai.utils.misc import MONAIEnvVars
label_desc: Use image node_id 6 for calling the TraceableTransform obj's underlying member method code with example usage: obj.track_transform_meta(cls, data, key, sp_size, affine, extra_info, orig_size, transform_info, lazy) and returns: out_obj, out_obj, data, data