# Path: stagehand/toolbox/utils.py
# def tobytes(value, encoding=None, desperate=True, coerce=False, fs=False):
#     """
#     Convert (if necessary) the given value to a "string of bytes", agnostic to
#     any character encoding.
# 
#     :param value: the value to be converted to a string of bytes
#     :param encoding: the character set to first try to encode to; if None, will
#                      use the system default (from the locale).
#     :type encoding: str
#     :param desperate: if True and encoding to the given (or default) charset
#                       fails, will also try utf-8 and latin-1 (in that order),
#                       and if those fail, will encode to the preferred charset,
#                       replacing unknown characters with \\\\uFFFD.
#     :type desperate: bool
#     :param coerce: if True, will coerce numeric types to a bytes object; if
#                    False, such values will be returned untouched.
#     :type coerce: bool
#     :param fs: indicates value is a file name or other environment string; if True,
#                the encoding (if not explicitly specified) will be the encoding
#                given by ``sys.getfilesystemencoding()`` and the error handler
#                used will be ``surrogateescape`` if supported.
#     :type fs: bool
#     :returns: the value as a bytes object, or the original value if coerce is
#               False and the value was not a bytes or string.
#     """
#     if isinstance(value, bytes):
#         # Nothing to do.
#         return value
#     elif isinstance(value, (int, float)):
#         return bytes(str(value), 'ascii') if coerce else value
#     elif not isinstance(value, str):
#         # Need to coerce to a unicode before converting to bytes.  We can't just
#         # feed it to bytes() in case the default character set can't encode it.
#         value = tostr(value, coerce=coerce)
# 
#     errors = 'strict'
#     if fs:
#         if not encoding:
#             encoding = sys.getfilesystemencoding()
#         errors = 'surrogateescape'
# 
#     for c in (encoding or ENCODING, 'utf-8', 'latin-1'):
#         try:
#             return value.encode(c, errors)
#         except UnicodeError:
#             pass
#         if not desperate:
#             raise UnicodeError("Couldn't encode value to bytes (and not desperate enough to keep trying)")
# 
#     return value.encode(encoding or ENCODING, 'replace')
# 
# Path: stagehand/toolbox/core.py
# class Signals(dict):
#     """
#     A collection of one or more Signal objects, which behaves like a dictionary
#     (with key order preserved).
# 
#     The initializer takes zero or more arguments, where each argument can be a:
#         * dict (of name=Signal() pairs) or other Signals object
#         * tuple/list of (name, Signal) tuples
#         * str representing the name of the signal
#     """
#     def __init__(self, *signals):
#         super().__init__()
#         # Preserve order of keys.
#         self._keys = []
#         for s in signals:
#             if isinstance(s, dict):
#                 # parameter is a dict/Signals object
#                 self.update(s)
#                 self._keys.extend(s.keys())
#             elif isinstance(s, str):
#                 # parameter is a string
#                 self[s] = Signal()
#                 self._keys.append(s)
#             elif isinstance(s, (tuple, list)) and len(s) == 2:
#                 # In form (key, value)
#                 if isinstance(s[0], basestring) and isinstance(s[1], Signal):
#                     self[s[0]] = s[1]
#                     self._keys.append(s[0])
#                 else:
#                     raise TypeError('With form (k, v), key must be string and v must be Signal')
# 
#             else:
#                 # parameter is something else, bad
#                 raise TypeError('signal key must be string')
# 
# 
#     def __delitem__(self, key):
#         super().__delitem__(key)
#         self._keys.remove(key)
# 
# 
#     def keys(self):
#         """
#         List of signal names (strings).
#         """
#         return self._keys
# 
# 
#     def values(self):
#         """
#         List of Signal objects.
#         """
#         return [self[k] for k in self._keys]
# 
# 
#     def __add__(self, signals):
#         return Signals(self, *signals)
# 
# 
#     def add(self, *signals):
#         """
#         Creates a new Signals object by merging all signals defined in
#         self and the signals specified in the arguments.
# 
#         The same types of arguments accepted by the initializer are allowed
#         here.
#         """
#         return Signals(self, *signals)
# 
# 
#     def subset(self, *names):
#         """
#         Returns a new Signals object by taking a subset of the supplied
#         signal names.
# 
#         The keys of the new Signals object are ordered as specified in the
#         names parameter.
# 
#             >>> yield signals.subset('pass', 'fail').any()
#         """
#         return Signals(*[(k, self[k]) for k in names])
# 
# 
#     def futures(self):
#         return [self[k].future() for k in self._keys]
# 
# 
#     def any(self):
#         return asyncio.wait(self.futures(), return_when=asyncio.FIRST_COMPLETED)
# 
# 
#     def all(self, return_exceptions=True):
#         return asyncio.gather(*self.futures(), return_exceptions=return_exceptions)


import types
import socket
import logging
import pickle
import struct
import sys
import hashlib
import time
import traceback
import os
import asyncio
from .utils import tobytes
from .core import Signals
        connecting to this RPC Server.

        :param obj: callable(s) to be accessible to connected clients.
        :type obj: callable, module, or class instance

        If a module is given, all exposed callables.
        """
        self.objects.append(obj)


    def disconnect(self, obj):
        """
        Disconnects a previously connected object.
        """
        try:
            self.objects.remove(obj)
        except ValueError:
            pass


class Channel(asyncio.Protocol):
    """
    Channel object for two point communication, implementing the custom rpc
    protocol. The server creates a Channel object for each incoming client
    connection.  Client itself is also a Channel.
    """
    channel_type = 'client'

    def __init__(self, auth_secret=b'', challenge=False, *, loop=None):
        super().__init__()

# Path: stagehand/toolbox/utils.py
# def tobytes(value, encoding=None, desperate=True, coerce=False, fs=False):
#     """
#     Convert (if necessary) the given value to a "string of bytes", agnostic to
#     any character encoding.
# 
#     :param value: the value to be converted to a string of bytes
#     :param encoding: the character set to first try to encode to; if None, will
#                      use the system default (from the locale).
#     :type encoding: str
#     :param desperate: if True and encoding to the given (or default) charset
#                       fails, will also try utf-8 and latin-1 (in that order),
#                       and if those fail, will encode to the preferred charset,
#                       replacing unknown characters with \\\\uFFFD.
#     :type desperate: bool
#     :param coerce: if True, will coerce numeric types to a bytes object; if
#                    False, such values will be returned untouched.
#     :type coerce: bool
#     :param fs: indicates value is a file name or other environment string; if True,
#                the encoding (if not explicitly specified) will be the encoding
#                given by ``sys.getfilesystemencoding()`` and the error handler
#                used will be ``surrogateescape`` if supported.
#     :type fs: bool
#     :returns: the value as a bytes object, or the original value if coerce is
#               False and the value was not a bytes or string.
#     """
#     if isinstance(value, bytes):
#         # Nothing to do.
#         return value
#     elif isinstance(value, (int, float)):
#         return bytes(str(value), 'ascii') if coerce else value
#     elif not isinstance(value, str):
#         # Need to coerce to a unicode before converting to bytes.  We can't just
#         # feed it to bytes() in case the default character set can't encode it.
#         value = tostr(value, coerce=coerce)
# 
#     errors = 'strict'
#     if fs:
#         if not encoding:
#             encoding = sys.getfilesystemencoding()
#         errors = 'surrogateescape'
# 
#     for c in (encoding or ENCODING, 'utf-8', 'latin-1'):
#         try:
#             return value.encode(c, errors)
#         except UnicodeError:
#             pass
#         if not desperate:
#             raise UnicodeError("Couldn't encode value to bytes (and not desperate enough to keep trying)")
# 
#     return value.encode(encoding or ENCODING, 'replace')
# 
# Path: stagehand/toolbox/core.py
# class Signals(dict):
#     """
#     A collection of one or more Signal objects, which behaves like a dictionary
#     (with key order preserved).
# 
#     The initializer takes zero or more arguments, where each argument can be a:
#         * dict (of name=Signal() pairs) or other Signals object
#         * tuple/list of (name, Signal) tuples
#         * str representing the name of the signal
#     """
#     def __init__(self, *signals):
#         super().__init__()
#         # Preserve order of keys.
#         self._keys = []
#         for s in signals:
#             if isinstance(s, dict):
#                 # parameter is a dict/Signals object
#                 self.update(s)
#                 self._keys.extend(s.keys())
#             elif isinstance(s, str):
#                 # parameter is a string
#                 self[s] = Signal()
#                 self._keys.append(s)
#             elif isinstance(s, (tuple, list)) and len(s) == 2:
#                 # In form (key, value)
#                 if isinstance(s[0], basestring) and isinstance(s[1], Signal):
#                     self[s[0]] = s[1]
#                     self._keys.append(s[0])
#                 else:
#                     raise TypeError('With form (k, v), key must be string and v must be Signal')
# 
#             else:
#                 # parameter is something else, bad
#                 raise TypeError('signal key must be string')
# 
# 
#     def __delitem__(self, key):
#         super().__delitem__(key)
#         self._keys.remove(key)
# 
# 
#     def keys(self):
#         """
#         List of signal names (strings).
#         """
#         return self._keys
# 
# 
#     def values(self):
#         """
#         List of Signal objects.
#         """
#         return [self[k] for k in self._keys]
# 
# 
#     def __add__(self, signals):
#         return Signals(self, *signals)
# 
# 
#     def add(self, *signals):
#         """
#         Creates a new Signals object by merging all signals defined in
#         self and the signals specified in the arguments.
# 
#         The same types of arguments accepted by the initializer are allowed
#         here.
#         """
#         return Signals(self, *signals)
# 
# 
#     def subset(self, *names):
#         """
#         Returns a new Signals object by taking a subset of the supplied
#         signal names.
# 
#         The keys of the new Signals object are ordered as specified in the
#         names parameter.
# 
#             >>> yield signals.subset('pass', 'fail').any()
#         """
#         return Signals(*[(k, self[k]) for k in names])
# 
# 
#     def futures(self):
#         return [self[k].future() for k in self._keys]
# 
# 
#     def any(self):
#         return asyncio.wait(self.futures(), return_when=asyncio.FIRST_COMPLETED)
# 
# 
#     def all(self, return_exceptions=True):
#         return asyncio.gather(*self.futures(), return_exceptions=return_exceptions)


# Path: stagehand/toolbox/rpc.py
import types
import socket
import logging
import pickle
import struct
import sys
import hashlib
import time
import traceback
import os
import asyncio
from .utils import tobytes
from .core import Signals

        connecting to this RPC Server.

        :param obj: callable(s) to be accessible to connected clients.
        :type obj: callable, module, or class instance

        If a module is given, all exposed callables.
        """
        self.objects.append(obj)


    def disconnect(self, obj):
        """
        Disconnects a previously connected object.
        """
        try:
            self.objects.remove(obj)
        except ValueError:
            pass


class Channel(asyncio.Protocol):
    """
    Channel object for two point communication, implementing the custom rpc
    protocol. The server creates a Channel object for each incoming client
    connection.  Client itself is also a Channel.
    """
    channel_type = 'client'

    def __init__(self, auth_secret=b'', challenge=False, *, loop=None):
        super().__init__()

        self.signals = Signals('authenticated', 'closed')