# Path: txtorcon/interface.py
# class IStreamAttacher(Interface):
#     """
#     Used by :class:`txtorcon.TorState` to map streams to circuits (see
#     :meth:`txtorcon.TorState.set_attacher`).
# 
#     Each time a new :class:`txtorcon.Stream` is created, this
#     interface will be queried to find out which
#     :class:`txtorcon.Circuit` it should be attached to.
# 
#     Only advanced use-cases should need to use this directly; for most
#     users, using the :func:`txtorcon.Circuit.stream_via` interface
#     should be preferred.
#     """
# 
#     def attach_stream_failure(stream, fail):
#         """
#         :param stream:
#             The stream we were trying to attach.
# 
#         :param fail:
#             A Failure instance.
# 
#         A failure has occurred while trying to attach the stream.
#         """
# 
#     def attach_stream(stream, circuits):
#         """
#         :param stream:
#             The stream to attach, which will be in NEW or NEWRESOLVE
#             state.
# 
#         :param circuits:
#             all currently available :class:`txtorcon.Circuit` objects
#             in the :class:`txtorcon.TorState` in a dict indexed by id.
#             Note they are *not* limited to BUILT circuits.
# 
#         You should return a :class:`txtorcon.Circuit` instance which
#         should be at state BUILT in the currently running Tor. You may
#         also return a Deferred which will callback with the desired
#         circuit. In this case, you will probably need to be aware that
#         the callback from :meth:`txtorcon.TorState.build_circuit` does
#         not wait for the circuit to be in BUILT state.
# 
#         Alternatively, you may return None in which case the Tor
#         controller will be told to choose a circuit itself.
# 
#         Note that Tor will refuse to attach to any circuit not in
#         BUILT state; see ATTACHSTREAM in control-spec.txt
# 
#         Note also that although you get a request to attach a stream
#         that ends in .onion Tor doesn't currently let you specify how
#         to attach .onion addresses and will always give a 551 error.
#         """


import itertools
import heapq
from zope.interface import implementer
from .interface import IStreamAttacher

        @implementer(IStreamAttacher)
        class MyAttacher(object):
            def __init__(self, interesting_host, circuit):
                self._host = interesting_host
                self._circuit = circuit

            def attach_stream(self, stream, circuits):
                if stream.target_host == self._host:
                    return self._circuit
                return None

        attachers.add_attacher(MyAttacher('torproject.org', circ1))
        attachers.add_attacher(MyAttacher('meejah.ca', circ2))
    """

    def __init__(self):
        # use only heapq.* to modify this; 0th item is "smallest"
        # item. contains 3-tuples of (priority, number, attacher)
        self._attacher_heap = []
        # need to keep a map so we can delete from the priority-queue :(
        self._attacher_to_entry = dict()
        # need to keep a counter so the sorting has a tie-breaker
        self._counter = itertools.count(0, 1)

    def add_attacher(self, attacher, priority=0):
        """
        Add a new IStreamAttacher at a certain priortiy; lower priority
        values mean more important (that is, 0 is the most important).
        """

# Path: txtorcon/interface.py
# class IStreamAttacher(Interface):
#     """
#     Used by :class:`txtorcon.TorState` to map streams to circuits (see
#     :meth:`txtorcon.TorState.set_attacher`).
# 
#     Each time a new :class:`txtorcon.Stream` is created, this
#     interface will be queried to find out which
#     :class:`txtorcon.Circuit` it should be attached to.
# 
#     Only advanced use-cases should need to use this directly; for most
#     users, using the :func:`txtorcon.Circuit.stream_via` interface
#     should be preferred.
#     """
# 
#     def attach_stream_failure(stream, fail):
#         """
#         :param stream:
#             The stream we were trying to attach.
# 
#         :param fail:
#             A Failure instance.
# 
#         A failure has occurred while trying to attach the stream.
#         """
# 
#     def attach_stream(stream, circuits):
#         """
#         :param stream:
#             The stream to attach, which will be in NEW or NEWRESOLVE
#             state.
# 
#         :param circuits:
#             all currently available :class:`txtorcon.Circuit` objects
#             in the :class:`txtorcon.TorState` in a dict indexed by id.
#             Note they are *not* limited to BUILT circuits.
# 
#         You should return a :class:`txtorcon.Circuit` instance which
#         should be at state BUILT in the currently running Tor. You may
#         also return a Deferred which will callback with the desired
#         circuit. In this case, you will probably need to be aware that
#         the callback from :meth:`txtorcon.TorState.build_circuit` does
#         not wait for the circuit to be in BUILT state.
# 
#         Alternatively, you may return None in which case the Tor
#         controller will be told to choose a circuit itself.
# 
#         Note that Tor will refuse to attach to any circuit not in
#         BUILT state; see ATTACHSTREAM in control-spec.txt
# 
#         Note also that although you get a request to attach a stream
#         that ends in .onion Tor doesn't currently let you specify how
#         to attach .onion addresses and will always give a 551 error.
#         """


# Path: txtorcon/attacher.py
import itertools
import heapq
from zope.interface import implementer
from .interface import IStreamAttacher


        @implementer(IStreamAttacher)
        class MyAttacher(object):
            def __init__(self, interesting_host, circuit):
                self._host = interesting_host
                self._circuit = circuit

            def attach_stream(self, stream, circuits):
                if stream.target_host == self._host:
                    return self._circuit
                return None

        attachers.add_attacher(MyAttacher('torproject.org', circ1))
        attachers.add_attacher(MyAttacher('meejah.ca', circ2))
    """

    def __init__(self):
        # use only heapq.* to modify this; 0th item is "smallest"
        # item. contains 3-tuples of (priority, number, attacher)
        self._attacher_heap = []
        # need to keep a map so we can delete from the priority-queue :(
        self._attacher_to_entry = dict()
        # need to keep a counter so the sorting has a tie-breaker
        self._counter = itertools.count(0, 1)

    def add_attacher(self, attacher, priority=0):
        """
        Add a new IStreamAttacher at a certain priortiy; lower priority
        values mean more important (that is, 0 is the most important).
        """

        item = [priority, next(self._counter), IStreamAttacher(attacher)]