class_docstr: Base class for P2P RPC servicers (e.g. DHT, averager, MoE server). The interface mimics gRPC servicers.
  
  - ``add_p2p_handlers(self, p2p)`` registers all rpc_* methods of the derived class as P2P handlers, allowing
    other peers to call them. It uses type annotations for the ``request`` parameter and the return value
    to infer protobufs the methods operate with.
  
  - ``get_stub(self, p2p, peer)`` creates a stub with all rpc_* methods. Calls to the stub methods are translated
    to calls to the remote peer.
class_docstr_tok: ["Base","class","for","P2P","RPC","servicers","(","e.g",".","DHT",",","averager",",","MoE","server",")",".","The","interface","mimics","gRPC","servicers",".","-","``","add_p2p_handlers","(","self",",","p2p",")","``","registers","all","rpc_","*","methods","of","the","derived","class","as","P2P","handlers",",","allowing","other","peers","to","call","them",".","It","uses","type","annotations","for","the","``","request","``","parameter","and","the","return","value","to","infer","protobufs","the","methods","operate","with",".","-","``","get_stub","(","self",",","p2p",",","peer",")","``","creates","a","stub","with","all","rpc_","*","methods",".","Calls","to","the","stub","methods","are","translated","to","calls","to","the","remote","peer","."]
code: def _make_rpc_caller(cls, handler: RPCHandler):
      input_type = (
          AsyncIterator[handler.request_type]
          if handler.stream_input
          else handler.request_type
      )
      output_type = (
          AsyncIterator[handler.response_type]
          if handler.stream_output
          else handler.response_type
      )
  
      # This method will be added to a new Stub type (a subclass of StubBase)
      async def caller(
          self: StubBase,
          input: input_type,
          timeout: Optional[float] = None,
      ) -> output_type:
          handle_name = cls._get_handle_name(
              self._namespace, handler.method_name
          )
          if not handler.stream_output:
              return await asyncio.wait_for(
                  self._p2p.call_protobuf_handler(
                      self._peer,
                      handle_name,
                      input,
                      handler.response_type,
                  ),
                  timeout=timeout,
              )
  
          if timeout is not None:
              raise ValueError(
                  "Timeouts for handlers returning streams are not supported"
              )
          return await self._p2p.iterate_protobuf_handler(
              self._peer, handle_name, input, handler.response_type
          )
  
      caller.__name__ = handler.method_name
      return caller
  
file_path: hivemind/hivemind/p2p/servicer.py
imports: import asyncio
  import inspect
  from dataclasses import dataclass
  from typing import Any, AsyncIterator, List, Optional, Tuple, Type, get_type_hints
  from hivemind.p2p.p2p_daemon import P2P
  from hivemind.p2p.p2p_daemon_bindings.datastructures import PeerID
label_desc: Use image node_id 2 for calling the ServicerBase obj's underlying member method code with example usage: obj._make_rpc_caller(cls, handler) and returns: caller, unknown, unknown