class_docstr: A torch.Tensor input or an intermediate value in the FX graph
class_docstr_tok: ["A","torch.Tensor","input","or","an","intermediate","value","in","the","FX","graph"]
code: def var_getattr(self, tx, name):
      from . import ConstantVariable, UserDefinedClassVariable
  
      if tx.strict_checks_enabled:
          if name in self._strict_mode_banned_ops():
              unimplemented(
                  f"Illegal getattr invocation {name} in strict mode"
              )
  
      result = None
      if name == "ndim" and self.ndim is not None:
          result = ConstantVariable.create(self.ndim)
      elif name == "dtype" and self.dtype is not None:
          result = ConstantVariable.create(self.dtype)
      elif name == "device" and self.device is not None:
          result = ConstantVariable.create(self.device)
      elif name == "layout" and self.layout is not None:
          result = ConstantVariable.create(self.layout)
      elif name == "is_cuda" and self.device is not None:
          result = ConstantVariable.create(self.device.type == "cuda")
      elif name == "shape" and self.size is not None:
          sizes = [
              variables.ConstantVariable.create(x) for x in self.size
          ]
          result = SizeVariable(sizes)
      elif name == "requires_grad" and self.requires_grad is not None:
          result = ConstantVariable.create(self.requires_grad)
      elif name == "is_quantized" and self.is_quantized is not None:
          result = ConstantVariable.create(self.is_quantized)
      elif name == "is_sparse" and self.is_sparse is not None:
          result = ConstantVariable.create(self.is_sparse)
      elif name == "shape" and self.size is None:
          result = self.call_method(tx, "size", [], {})
      elif name == "ndim" and self.ndim is None:
          result = self.call_method(tx, "dim", [], {})
      elif name == "data":
          result = self.call_method(tx, "detach", [], {})
      if name == "__class__":
          return UserDefinedClassVariable(self.python_type())
  
      # Add a guard for type matching, these guards are checked before tensor guards
      # In some cases, a <tensor>.<attr> guard can be evaluated first, and break if
      # <tensor> is later changed to another type
      if result is not None and self.source is not None:
          install_guard(self.make_guard(GuardBuilder.TYPE_MATCH))
          result.source = AttrSource(self.source, name)
  
      # It's hard to get inplace view (metadata mutation) on graph input work properly across
      # dynamo/aot/inductor, just fall back.
      if self.source is not None and hasattr(torch.ops.aten, name):
          fn = getattr(torch.ops.aten, name)
          if (
              hasattr(fn, "overloads")
              and hasattr(fn, fn.overloads()[0])
              and torch.Tag.inplace_view
              in getattr(fn, fn.overloads()[0]).tags
          ):
              # Delay the graph break to the actual call of unsqueeze_/resize_/resize_as_ etc.
              return variables.misc.DelayGraphBreakVariable(
                  source=AttrSource(self.source, name)
              )
  
      # For attributes (not methods) that were not caught in the special handling above,
      # (e.g. tensor.real), we handle these generically, assuming that the output type is
      # a tensor.
      if result is None:
  
          def try_generic_attr_handling():
              from .builder import wrap_fx_proxy
              from .misc import GetAttrVariable
  
              try:
                  static_attr = inspect.getattr_static(
                      torch.Tensor, name
                  )
              except AttributeError:
                  return None
  
              # Make sure this is an attribute, not a method.
              # type(torch.Tensor.H) should be "getset_descriptor"
              # This is a because of CPython implementation, see THPVariableType:
              # these attributes are implemented under tp_getset, which appear
              # as `getset_descriptor`s, (compared to, say, methods which appear
              # as `method_descriptor`s)
              if type(static_attr) != types.GetSetDescriptorType:
                  return None
  
              proxy = GetAttrVariable.create_getattr_proxy(
                  self.as_proxy(), name
              )
              if self.source is not None:
                  return wrap_fx_proxy(
                      tx=tx,
                      proxy=proxy,
                      source=AttrSource(self.source, name),
                  )
              else:
                  return wrap_fx_proxy(tx=tx, proxy=proxy)
  
          result = try_generic_attr_handling()
  
      if result is None:
          result = self.dynamic_getattr(tx, name)
  
      if result is None:
          raise NotImplementedError()
      return result
  
imports: import functools
  import inspect
  import operator
  import types
  from typing import Dict, List
  import sympy
  import torch._numpy
  import torch.fx
  import torch.random
  from torch._dynamo import compiled_autograd
  from torch.fx.experimental.symbolic_shapes import guard_scalar, GuardOnDataDependentSymNode, has_free_symbols, is_symbolic, SymTypes
  from ..None import config, variables
  from .._trace_wrapped_higher_order_op import trace_wrapped
  from ..exc import unimplemented, UserError, UserErrorType
  from ..guards import GuardBuilder, install_guard
  from ..source import AttrSource
  from ..utils import fqn, get_custom_getattr, get_fake_value, get_real_value, guard_if_dyn, object_has_getattribute, product, proxy_args_kwargs, tensortype_to_dtype
  from .base import VariableTracker
  from .constant import ConstantVariable
  from .lists import SizeVariable
label_desc: Use image node_id 7 for calling the TensorVariable obj's underlying member method code with example usage: obj.var_getattr(tx, name) and returns: result, UserDefinedClassVariable, variables, None, wrap_fx_proxy, wrap_fx_proxy, None