class_docstr: A `RuntimeGroup` is the representation of logically related `Runtimes`.
  
  The group provides convenient methods for managing those related `Runtimes`.
  Most methods are wrappers around their counterparts in the `Runtime` class.
  Typical usage examples are exposing a port (i.e. a service such as a DB) in
  the `RuntimeGroup`, transfer files, or execute  a `RuntimeTask` on the contained
  `Runtimes`. Additionally, all concrete RuntimeCluster (e.g. HyperoptCluster)
  implementations rely on `RuntimeGroups` for example.
  
  Examples:
  Execute a `RuntimeTask` in a `RuntimeGroup`
  
  ```python
  # Create instances
  group = RuntimeGroup([Runtime('host-1'), Runtime('host-2')])
  # group = RuntimeGroup(hosts=['host-1', 'host-2'])
  my_task = RuntimeTask('group-demo').run_command('echo Hello Group!')
  
  # Execute a RuntimeTask in a single Runtime
  single_task = group.execute_task(my_task)
  print(single_task.execution_log[0])
  
  # Execute a RuntimeTask in the whole RuntimeGroup
  task_list = group.execute_task(my_task, broadcast=True)
  
  # Execute a RuntimeTask on a single Runtime contained in the RuntimeGroup
  task = group.execute_task(my_task)
  ```
  
  A DB is running on localhost on port `local_port` and the DB is only accessible
  from localhost. But you also want to access the service on the other `Runtimes` on port
  `runtime_port`. Then you can use this method to expose the service which is running on the
  local machine to the remote machines.
  
  ```python
  # Expose a port to all Runtimes contained in the Runtime. If a port list is given the next free port is
  # chosen and returned.
  group_port = group.expose_port_to_runtimes(local_port=60000, runtime_port=list(range(60000,60010)))
  print('Local port 60000 is now exposed to port ' + str(group_port) + ' in the RuntimeGroup!')
  ```
  
  A DB is running on a remote host on port `runtime_port` and the DB is only accessible from the remote
  machine itself. But you also want to access the service to other `Runtimes` in the group. Then you can use
  this method to expose the service which is running on one `Runtime` to the whole group.
  
  ```python
  # Expose a port from a Runtime to all other ones in the RuntimeGroup. If a port list is given the next
  # free port is chosen and returned.
  group_port = group.expose_port_from_runtime_to_group(host='host-1', runtime_port=60000,
                                                          group_port=list(range(60000,60010)))
  print('Port 60000 of `host-1` is now exposed to port ' + str(group_port) + ' in the RuntimeGroup!')
  ```
class_docstr_tok: ["A","`","RuntimeGroup","`","is","the","representation","of","logically","related","`","Runtimes","`",".","The","group","provides","convenient","methods","for","managing","those","related","`","Runtimes","`",".","Most","methods","are","wrappers","around","their","counterparts","in","the","`","Runtime","`","class",".","Typical","usage","examples","are","exposing","a","port","(","i.e",".","a","service","such","as","a","DB",")","in","the","`","RuntimeGroup","`",",","transfer","files",",","or","execute","a","`","RuntimeTask","`","on","the","contained","`","Runtimes","`",".","Additionally",",","all","concrete","RuntimeCluster","(","e.g",".","HyperoptCluster",")","implementations","rely","on","`","RuntimeGroups","`","for","example",".","Examples",":","Execute","a","`","RuntimeTask","`","in","a","`","RuntimeGroup","`","``","`","python","#","Create","instances","group","=","RuntimeGroup","(","[","Runtime","(","'host-1","'",")",",","Runtime","(","'host-2","'",")","]",")","#","group","=","RuntimeGroup","(","hosts=","[","'host-1","'",",","'host-2","'","]",")","my_task","=","RuntimeTask","(","'group-demo","'",")",".run_command","(","'echo","Hello","Group","!","'",")","#","Execute","a","RuntimeTask","in","a","single","Runtime","single_task","=","group.execute_task","(","my_task",")","print","(","single_task.execution_log","[","0","]",")","#","Execute","a","RuntimeTask","in","the","whole","RuntimeGroup","task_list","=","group.execute_task","(","my_task",",","broadcast=True",")","#","Execute","a","RuntimeTask","on","a","single","Runtime","contained","in","the","RuntimeGroup","task","=","group.execute_task","(","my_task",")","``","`","A","DB","is","running","on","localhost","on","port","`","local_port","`","and","the","DB","is","only","accessible","from","localhost",".","But","you","also","want","to","access","the","service","on","the","other","`","Runtimes","`","on","port","`","runtime_port","`",".","Then","you","can","use","this","method","to","expose","the","service","which","is","running","on","the","local","machine","to","the","remote","machines",".","``","`","python","#","Expose","a","port","to","all","Runtimes","contained","in","the","Runtime",".","If","a","port","list","is","given","the","next","free","port","is","#","chosen","and","returned",".","group_port","=","group.expose_port_to_runtimes","(","local_port=60000",",","runtime_port=list","(","range","(","60000,60010",")",")",")","print","(","'Local","port","60000","is","now","exposed","to","port","'","+","str","(","group_port",")","+","'","in","the","RuntimeGroup","!","'",")","``","`","A","DB","is","running","on","a","remote","host","on","port","`","runtime_port","`","and","the","DB","is","only","accessible","from","the","remote","machine","itself",".","But","you","also","want","to","access","the","service","to","other","`","Runtimes","`","in","the","group",".","Then","you","can","use","this","method","to","expose","the","service","which","is","running","on","one","`","Runtime","`","to","the","whole","group",".","``","`","python","#","Expose","a","port","from","a","Runtime","to","all","other","ones","in","the","RuntimeGroup",".","If","a","port","list","is","given","the","next","#","free","port","is","chosen","and","returned",".","group_port","=","group.expose_port_from_runtime_to_group","(","host='host-1","'",",","runtime_port=60000",",","group_port=list","(","range","(","60000,60010",")",")",")","print","(","'Port","60000","of","`","host-1","`","is","now","exposed","to","port","'","+","str","(","group_port",")","+","'","in","the","RuntimeGroup","!","'",")","``","`"]
docstr: Trigger the reading of runtime information asynchronously and buffer the result.
  
  Note:
      The actual reading of `Runtime.info data takes place when requesting the attribute the first time.
      Consequently, the result gets buffered in the respective Runtime instance. The actual reading of the data
      takes places on the remote host and takes some seconds. This method enables you to read the information in a
      separate processes so that the execution time stays more or less the same independent of the actual amount
      of Runtimes used.
docstr_tok: ["Trigger","the","reading","of","runtime","information","asynchronously","and","buffer","the","result",".","Note",":","The","actual","reading","of","`","Runtime.info","data","takes","place","when","requesting","the","attribute","the","first","time",".","Consequently",",","the","result","gets","buffered","in","the","respective","Runtime","instance",".","The","actual","reading","of","the","data","takes","places","on","the","remote","host","and","takes","some","seconds",".","This","method","enables","you","to","read","the","information","in","a","separate","processes","so","that","the","execution","time","stays","more","or","less","the","same","independent","of","the","actual","amount","of","Runtimes","used","."]
code: def fill_runtime_info_buffers_async(self) -> None:
      """Trigger the reading of runtime information asynchronously and buffer the result.
  
      Note:
          The actual reading of `Runtime.info data takes place when requesting the attribute the first time.
          Consequently, the result gets buffered in the respective Runtime instance. The actual reading of the data
          takes places on the remote host and takes some seconds. This method enables you to read the information in a
          separate processes so that the execution time stays more or less the same independent of the actual amount
          of Runtimes used.
  
      """
      self.log.debug(
          "Start filling the buffers Runtime.info asynchronously."
      )
      with Pool(self.runtime_count) as pool:
          results = []
          for runtime in self._runtimes.values():
              # We need to check the private member because otherwise the reading will be triggered synchronously
              if runtime._info:
                  # If the runtime info is already present, we do not need to re-read it
                  continue
              self.log.debug(
                  f"Start reading runtime info asynchronously of Runtime {runtime.host}"
              )
              results.append(
                  pool.apply_async(
                      _utils.read_host_info, (runtime.host,)
                  )
              )
  
          index = 1
          for result in results:
              self.log.debug(
                  f"Waiting fo result {index} of {self.runtime_count}."
              )
              index += 1
              runtime_info = result.get()
              self.log.debug(
                  f'Result for host {runtime_info["host"]} retrieved'
              )
              if not runtime_info["host"] or not isinstance(
                  runtime_info["host"], str
              ):
                  continue
              self.get_runtime(
                  runtime_info["host"]
              )._info = runtime_info
  
imports: import atexit
  import logging
  import warnings
  from copy import deepcopy
  from multiprocessing import Pool, Process
  from typing import Dict, Generator, List, Optional, Union
  from storm import Storm
  from lazycluster import _utils
  from lazycluster.exceptions import InvalidRuntimeError, NoPortsLeftError, NoRuntimesDetectedError, PortInUseError
  from lazycluster.runtimes import Runtime, RuntimeTask
label_desc: Use image node_id 13 for calling the RuntimeGroup obj's underlying member method code with example usage: obj.fill_runtime_info_buffers_async() without return types