class_docstr: A base class that specifies the shape and dtype of a tensor.
  
  Args:
      shape: A string representing einops notation of the shape of the tensor.
          For example, "B, C" represents a tensor with two dimensions, the first
          of which has size B and the second of which has size C. shape must
          consist of unique dimension names. For example having "B B" is invalid.
      dtype: The dtype of the tensor. If None, the dtype is not checked during
          validation. Also during Sampling the dtype is set the default dtype of
          the backend.
      framework: The framework of the tensor. If None, the framework is not
          checked during validation.
      shape_vals: An optional dictionary mapping some dimension names to their
          values. For example, if shape is "B, C" and shape_vals is {"C": 3}, then
          the shape of the tensor is (B, 3). B is to be determined during
          run-time but C is fixed to 3.
  
  .. testcode::
      :skipif: True
  
      spec = TensorSpec("b, d", d=128, dtype=tf.float32)
      spec.shape  # ('b', 128)
      spec.validate(torch.rand(32, 128, dtype=torch.float32))  # passes
      spec.validate(torch.rand(32, 64, dtype=torch.float32))   # raises ValueError
      spec.validate(torch.rand(32, 128, dtype=torch.float64))  # raises ValueError
  
  Public Methods:
      validate: Checks if the shape and dtype of the tensor matches the
          specification.
      fill: creates a tensor with the specified value that is an
          example of a tensor that matches the specification. This can only be
          called if `framework` is specified.
docstr: Checks if the shape_vals is valid.
  
  Valid means that shape consist of unique dimension names and shape_vals only
  consists of keys that are in shape. Also shape_vals can only contain postive
  integers.
code: def _validate_shape_vals(
      self, d_names: List[str], shape_vals: Dict[str, int]
  ) -> None:
      """Checks if the shape_vals is valid.
  
      Valid means that shape consist of unique dimension names and shape_vals only
      consists of keys that are in shape. Also shape_vals can only contain postive
      integers.
      """
      d_names_set = set(d_names)
      if len(d_names_set) != len(d_names):
          raise ValueError(
              _INVALID_INPUT_DUP_DIM.format(",".join(d_names))
          )
  
      for d_name in shape_vals:
          if d_name not in d_names_set:
              raise ValueError(
                  _INVALID_INPUT_UNKNOWN_DIM.format(
                      d_name, ",".join(d_names)
                  )
              )
  
          d_value = shape_vals.get(d_name, None)
          if d_value is not None:
              if not isinstance(d_value, int):
                  raise ValueError(
                      _INVALID_INPUT_INT_DIM.format(
                          d_name, ",".join(d_names), type(d_value)
                      )
                  )
              if d_value <= 0:
                  raise ValueError(
                      _INVALID_INPUT_POSITIVE.format(
                          d_name, ",".join(d_names), d_value
                      )
                  )
  
imports: import abc
  from copy import deepcopy
  import numpy
  from typing import Any, Optional, Dict, List, Tuple, Union, Type
  from ray.rllib.utils import try_import_jax, try_import_tf, try_import_torch
  from ray.rllib.utils.annotations import OverrideToImplementCustomLogic
  from ray.rllib.utils.annotations import DeveloperAPI, override
  from ray.rllib.utils.typing import TensorType
label_desc: Use image node_id 15 for calling the TensorSpec obj's underlying member method code with example usage: obj._validate_shape_vals(d_names, shape_vals) without return types