docstr: Find one single possible connection between two lists of sockets.
docstr_tok: ["Find","one","single","possible","connection","between","two","lists","of","sockets","."]
code: def from_list_of_sockets(
      sender_node: str,
      sender_sockets: List[OutputSocket],
      receiver_node: str,
      receiver_sockets: List[InputSocket],
  ) -> "Connection":
      """
      Find one single possible connection between two lists of sockets.
      """
      # List all sender/receiver combinations of sockets that match by type
      possible_connections = [
          (sender_sock, receiver_sock)
          for sender_sock, receiver_sock in itertools.product(
              sender_sockets, receiver_sockets
          )
          if _types_are_compatible(sender_sock.type, receiver_sock.type)
      ]
  
      # No connections seem to be possible
      if not possible_connections:
          connections_status_str = _connections_status(
              sender_node=sender_node,
              sender_sockets=sender_sockets,
              receiver_node=receiver_node,
              receiver_sockets=receiver_sockets,
          )
  
          # Both sockets were specified: explain why the types don't match
          if (
              len(sender_sockets) == len(receiver_sockets)
              and len(sender_sockets) == 1
          ):
              raise PipelineConnectError(
                  f"Cannot connect '{sender_node}.{sender_sockets[0].name}' with '{receiver_node}.{receiver_sockets[0].name}': "
                  f"their declared input and output types do not match.\n{connections_status_str}"
              )
  
          # Not both sockets were specified: explain there's no possible match on any pair
          connections_status_str = _connections_status(
              sender_node=sender_node,
              sender_sockets=sender_sockets,
              receiver_node=receiver_node,
              receiver_sockets=receiver_sockets,
          )
          raise PipelineConnectError(
              f"Cannot connect '{sender_node}' with '{receiver_node}': "
              f"no matching connections available.\n{connections_status_str}"
          )
  
      # There's more than one possible connection
      if len(possible_connections) > 1:
          # Try to match by name
          name_matches = [
              (out_sock, in_sock)
              for out_sock, in_sock in possible_connections
              if in_sock.name == out_sock.name
          ]
          if len(name_matches) == 1:
              # Sockets match by type and name, let's use this
              return Connection(
                  sender_node,
                  name_matches[0][0],
                  receiver_node,
                  name_matches[0][1],
              )
  
          # TODO allow for multiple connections at once if there is no ambiguity?
          # TODO give priority to sockets that have no default values?
          connections_status_str = _connections_status(
              sender_node=sender_node,
              sender_sockets=sender_sockets,
              receiver_node=receiver_node,
              receiver_sockets=receiver_sockets,
          )
          raise PipelineConnectError(
              f"Cannot connect '{sender_node}' with '{receiver_node}': more than one connection is possible "
              "between these components. Please specify the connection name, like: "
              f"pipeline.connect('{sender_node}.{possible_connections[0][0].name}', "
              f"'{receiver_node}.{possible_connections[0][1].name}').\n{connections_status_str}"
          )
  
      match = possible_connections[0]
      return Connection(sender_node, match[0], receiver_node, match[1])
  
file_path: haystack/haystack/core/component/connection.py
imports: import itertools
  from typing import Optional, List, Tuple
  from dataclasses import dataclass
  from haystack.core.component.sockets import InputSocket, OutputSocket
  from haystack.core.type_utils import _type_name, _types_are_compatible
  from haystack.core.errors import PipelineConnectError
label_desc: Use image node_id 5 for calling the Connection obj's underlying member method code with example usage: obj.from_list_of_sockets(sender_node, sender_sockets, receiver_node, receiver_sockets) and returns: Connection, Connection