docstr: Deals with caching logic
docstr_tok: ["Deals","with","caching","logic"]
code: def __getattribute__(self, func_name):
      """Deals with caching logic"""
      if func_name not in TupleProvider.TRACEABLE_FUNCTIONS:
          return object.__getattribute__(self, func_name)
  
      # Trace requests while tracing
      if self.tracing:
  
          def func_with_trace(*args, **kwargs):
              request = (func_name, args, kwargs)
              self.request_cache.append(request)
              return object.__getattribute__(self, func_name)(
                  *args, **kwargs
              )
  
          return func_with_trace
  
      # If the cache is empty, call function directly
      if len(self.tuple_cache) == 0:
          return object.__getattribute__(self, func_name)
  
      # Return results from cache if available
      def func_from_cache(*args, **kwargs):
          hashable_kwargs = frozenset(kwargs.items())
          request = (func_name, args, hashable_kwargs)
          # Read from cache
          if request in self.tuple_cache.keys():
              return self.tuple_cache[request].pop()
          # Cache miss
          return object.__getattribute__(self, func_name)(
              *args, **kwargs
          )
  
      return func_from_cache
  
imports: import os
  import crypten
  import crypten.communicator
  import torch
label_desc: Use image node_id 13 for calling the TupleProvider obj's underlying member method code with example usage: obj.__getattribute__(func_name) and returns: func_from_cache, object, func_with_trace, object, object, object, self