code: def cleanup(self):
      if (
          self.process.poll() is None
      ):  # check if process is still running:
          # the sigterm handler in the daemon process waits for any in-progress uploads etc. to finish.
          # the sigterm handler also deletes the socket file
          self.process.terminate()
          self.process.wait()
  
          # need to join these threads to avoid "could not acquire lock" error
          self.stdout_thread.join()
          self.stderr_thread.join()
      self.channel.close()
  
imports: import functools
  import tempfile
  import os
  from typing import Optional, Dict, Any, List
  import subprocess
  import atexit
  import sys
  import threading
  import grpc
  from google.rpc import status_pb2, error_details_pb2
  from .servicepb.keepsake_pb2_grpc import DaemonStub
  from .servicepb import keepsake_pb2
  from .None import pb_convert
  from .experiment import Experiment
  from .checkpoint import Checkpoint, PrimaryMetric
  from .None import exceptions
  from .None import console
label_desc: Use image node_id 2 for calling the Daemon obj's underlying member method code with example usage: obj.cleanup() without return types