class API(type):
    ''' Metaclass for defining an interfaceable API.
    '''
    
    def __new__(mcls, clsname, bases, namespace, *args, _short_circuit=False,
                **kwargs):
        ''' Modify the existing namespace:
        1.  remove any @fixture_api methods.
        2.  extract any @public_api.fixture methods
        3.  extract any @public_api.interface methods
        4.  create cls.__fixture__ class object
        5.  create cls.__interface__ class object
        '''
        # Dead-end for fixtures and interfaces
        if _short_circuit:
            cls = super().__new__(mcls, clsname, bases, namespace, *args,
                                  **kwargs)
        
        # For the parent/public class...
        else:
            public_name = clsname
            fixture_name = clsname + 'Fixture'
            interface_name = clsname + 'Interface'
                
            public_namespace = {}
            fixture_namespace = {}
            interface_namespace = {}
        
            # No need to modify bases, either for the actual type or the
            # fixture/interface
            
            # Iterate over the entire defined namespace.
            for name, obj in namespace.items():
                is_fixture = getattr(obj, '__is_fixture__', False)
                is_interface = getattr(obj, '__is_interface__', False)
                
                # If the .fixture magic attr was defined, use it; else, just
                # fall back to inheritance
                fixture = getattr(obj, '__fixture__', None)
                interface = getattr(obj, '__interface__', None)
                
                # __is_fixture__ get sent only to the fixture.
                if is_fixture:
                    fixture_namespace[name] = obj
                    
                elif is_interface:
                    interface_namespace[name] = obj
                
                # All other objects pass via inheritance.
                else:
                    public_namespace[name] = obj
                
                # If we have an actual fixture defined, then it supercedes
                # public_namespace
                if fixture is not None:
                    fixture_namespace[name] = fixture
                    
                # Same goes with the interface.
                if interface is not None:
                    interface_namespace[name] = interface
            
            # Create the class
            cls = super().__new__(
                mcls,
                public_name,
                bases,
                public_namespace,
                *args,
                **kwargs
            )
            
            # Figure out the fixture inheritance tree. Fixtures always take
            # precedent over parents, but also always inherit them.
            fixture_bases = (cls, *(
                base.__fixture__ if hasattr(base, '__fixture__') else base
                for base in bases
            ))
            interface_bases = (cls, *(
                base.__interface__ if hasattr(base, '__interface__') else base
                for base in bases
            ))
            
            # NOTE:
            # See Python docs on super() with zero args: "the compiler fills in
            # the necessary details to correctly retrieve the class being
            # defined". Therefore, this is necessary when using fixture_bases:
            # super(Class, self).__init__(*args, **kwargs)
            
            # Now add in the types for both the fixture and the interface.
            # Reuse same bases for both, but use the REAL metaclass for it.
            # TODO: make the fixture inherit from the cls above, so that it can
            # have access to the original methods using super()
            cls.__fixture__ = mcls(
                fixture_name,
                fixture_bases,
                fixture_namespace,
                *args,
                _short_circuit = True,
                **kwargs
            )
            cls.__interface__ = mcls(
                interface_name,
                interface_bases,
                interface_namespace,
                *args,
                _short_circuit = True,
                **kwargs
            )
        
        # And don't forget to return the final cls object.
        return cls
        
    def __init__(cls, *args, _short_circuit=False, **kwargs):
        # Just strip out _short_circuit
        super().__init__(*args, **kwargs) def public_api(func):
    ''' Decorator to automatically mark the object as the normal thing.
    '''
    
    def fixture_closure(fixture_func, public_func=func):
        ''' Defines the decorator for @method.fixture.
        '''
        # This is the actual __fixture__ method, to be defined via decorator
        public_func.__fixture__ = fixture_func
        return public_func
    
    def interface_closure(interface_func, public_func=func):
        ''' Defines the decorator for @method.fixture.
        '''
        # This is the actual __interface method, to be defined via decorator
        public_func.__interface__ = interface_func
        return public_func
        
    func.fixture = fixture_closure
    func.interface = interface_closure
    
    # This denotes that it is an API
    func.__is_api__ = True
    
    return func def fixture_api(func):
    ''' Decorator to mark the method as a fixture-only object.
    '''
    ''' Decorator to automatically mark the object as the normal thing.
    '''
    
    # Huh, well, this is easy.
    func.__is_fixture__ = True
    return func def fixture_noop(func):
    ''' Create a no-op version of the function to use as its fixture.
    Should be used as a decorator above @public_api.
    '''
    signature = inspect.Signature.from_callable(func)
    
    if inspect.iscoroutinefunction(func):
        @func.fixture
        @functools.wraps(func)
        async def noop_fixture(*args, __signature=signature, **kwargs):
            # Make sure the passed args, kwargs are correct, but otherwise,
            # do nothing.
            __signature.bind(*args, **kwargs)
    
    else:
        @func.fixture
        @functools.wraps(func)
        def noop_fixture(*args, __signature=signature, **kwargs):
            # Make sure the passed args, kwargs are correct, but otherwise,
            # do nothing.
            __signature.bind(*args, **kwargs)
        
    return noop_fixture def fixture_return(val):
    ''' Use as a decorator to make a fixture that returns a constant
    value.
    '''
    # Make the actual closure
    def decorator_closure(func):
        signature = inspect.Signature.from_callable(func)
        
        if inspect.iscoroutinefunction(func):
            @func.fixture
            @functools.wraps(func)
            async def fixt(*args, __signature=signature, __rval=val, **kwargs):
                # Make sure the passed args, kwargs are correct, but otherwise,
                # do nothing.
                __signature.bind(*args, **kwargs)
                return __rval
        
        else:
            @func.fixture
            @functools.wraps(func)
            def fixt(*args, __signature=signature, __rval=val, **kwargs):
                # Make sure the passed args, kwargs are correct, but otherwise,
                # do nothing.
                __signature.bind(*args, **kwargs)
                return __rval
            
        return fixt
    
    # Send it back into the decorator
    return decorator_closure class RequestError(CommsError):
    ''' Raised when something goes wrong with IPC (bad commands, etc).
    '''
    pass class RequestFinished(RequestError):
    ''' Raised when something goes wrong with IPC (bad commands, etc).
    '''
    pass class RequestUnknown(RequestError):
    ''' Raised when a request code is unknown.
    '''
    pass class ConnectionClosed(CommsError):
    ''' Raised when something goes wrong with IPC (bad commands, etc).
    '''
    pass class ProtocolVersionError(CommsError):
    ''' Raised when a client/server sends an unsupported version to the
    server/client, respectively.
    '''
    pass class _BijectDict:
    ''' A bijective dictionary. Aka, a dictionary where one key
    corresponds to exactly one value, and one value to exactly one key.
    
    Implemented as two dicts, with forward and backwards versions.
    
    Threadsafe.
    '''
    
    def __init__(self, *args, **kwargs):
        self._opslock = threading.RLock()
        self._fwd = dict(*args, **kwargs)
        # Make sure no values repeat and that all are hashable
        if len(list(self._fwd.values())) != len(set(self._fwd.values())):
            raise TypeError('_BijectDict values must be hashable and unique.')
        self._rev = {value: key for key, value in self._fwd.items()}
    
    def __getitem__(self, key):
        with self._opslock:
            try:
                return self._fwd[key]
            except KeyError:
                return self._rev[key]
    
    def __setitem__(self, key, value):
        with self._opslock:
            # Remove any previous connections with these values
            if value in self._fwd:
                raise ValueError('Value already exists as a forward key.')
            if key in self._rev:
                raise ValueError('Key already exists as a forward value.')
            # Note: this isn't perfectly atomic, as it won't restore a previous
            # value that we just failed to replace.
            try:
                self._fwd[key] = value
                self._rev[value] = key
            except Exception:
                # Default to None when popping to avoid KeyError
                self._fwd.pop(key, None)
                self._rev.pop(value, None)
                raise

    def __delitem__(self, key):
        with self._opslock:
            try:
                value = self._fwd.pop(key, None)
                del self._rev[value]
            except KeyError:
                value = self._rev.pop(key, None)
                del self._fwd[value]

    def __len__(self):
        return len(self._fwd)
        
    def __contains__(self, key):
        with self._opslock:
            return key in self._fwd or key in self._rev
            
    def __iter__(self):
        ''' Pass iter directly on to self._fwd
        '''
        return iter(self._fwd)
        
    def __reversed__(self):
        ''' Mappings don't have a strict order, so re-purpose reversed
        to look in the opposite direction (the self._rev dict).
        '''
        return iter(self._rev) def ensure_equal_len(iterable, msg=''):
    ''' Ensures that all members of an iterable have the same length.
    Raises ValueError if not, using the passed msg. Returns the length.
    '''
    bulk_len = None
    for item in iterable:
        # Check length first, so that we only check bulk_len's definition once,
        # saving a few bytecode ops (just because it took me less time to think
        # of that than to write this comment explaining the ordering)
        if len(item) != bulk_len:
            
            # NOW make sure this isn't the first thing we're comparing to!
            if bulk_len is None:
                bulk_len = len(item)
                
            # If so, lengths don't match; raise.
            else:
                raise ValueError(msg)
                
    return bulk_len
import logging
import asyncio
import websockets
import traceback
import weakref
import base64
import loopa
import certifi
import ssl
import time
import random
from collections import namedtuple
from .hypothetical import API
from .hypothetical import public_api
from .hypothetical import fixture_api
from .hypothetical import fixture_noop
from .hypothetical import fixture_return
from .exceptions import RequestError
from .exceptions import RequestFinished
from .exceptions import RequestUnknown
from .exceptions import ConnectionClosed
from .exceptions import ProtocolVersionError
from .utils import _BijectDict
from .utils import ensure_equal_len
        # ALL_CONNECTIONS.add(self)
        
        # Memoize our repr as the hex repr of our id(self)
        self._repr = '<' + type(self).__name__ + ' ' + hex(id(self)) + '>'
        # Memoize a urlsafe base64 string of our id(self) for str
        self._str = str(
            # Change the bytes int to base64
            base64.urlsafe_b64encode(
                # Convert our id(self) to a bytes integer equal to the maximum
                # length of ID on a 64-bit system
                id(self).to_bytes(byteorder='big', length=8)
            ),
            # And then re-cast as a native str
            'utf-8'
        )
        # And log our creation.
        logger.info('CONN ' + str(self) + ' CREATED.')
        
    @__init__.fixture
    def __init__(self, msg_iterator=None, *args, **kwargs):
        ''' Add in an isalive flag and an iterator to simulate message
        reciept.
        '''
        super(_ConnectionBase.__fixture__, self).__init__(*args, **kwargs)
        self._isalive = True
        self._msg_iterator = msg_iterator
        
    def terminate(self):
        ''' Remove our circular reference, which (assuming all other
        refs were weak) will result in our garbage collection.
        Idempotent.
        '''
        # Note: if this gets any more complicated, then we should register an
        # atext cleanup to call terminate. For now, just let GC remove it.
        try:
            del self._ref
            
        except AttributeError:
            logger.debug(
                'Attempted to terminate connection twice. Check for other ' +
                'strong references to the connection.'
            )
            
    def __bool__(self):
        ''' Return True if the connection is still active and False
        otherwise.
        '''
        return hasattr(self, '_ref')
        
    def __repr__(self):
        ''' Make a slightly nicer version of repr, since connections are
        ephemeral and we cannot reproduce them anyways.
        '''
        # Example: <_AutoresponderSession 0x52b2978>
        return self._repr
        
    def __str__(self):
        ''' Make an even more concise version of str, just giving the
        id, as a constant-length base64 string.
        '''
        return self._str
            
    @classmethod
    def desc_str(cls, *args, **kwargs):
        ''' Override this to explain where the connection is supposed to
        go, for use in debug scenarios.
        
        *args and **kwargs should match those used in cls.new()
        '''
        return cls.__name__ + '(*' + str(args) + ', **' + str(kwargs) + ')'
        
    async def listener(self, receiver):
        ''' Once a connection has been created, call this to listen for
        a message until the connection is terminated. Put it in a loop
        to listen forever.
        '''
        try:
            msg = await self.recv()
        
        except ConnectionClosed:
            logger.info(
                'CONN ' + str(self) + ' closed at listener.'
            )
            raise
            
        else:
            logger.debug('CONN ' + str(self) + ' message received.')
            
            try:
                # When we pass to the receiver, make sure we give them a strong
                # reference, so hashing and stuff continues to work.
                await receiver(self, msg)
                
            except asyncio.CancelledError:
                logger.debug('CONN ' + str(self) + ' receive cancelled.')
                raise
            
            except Exception:
                logger.error(
                    'CONN ' + str(self) + ' Listener receiver ' +
                    'raised w/ traceback:\n' + ''.join(traceback.format_exc())
                )
                raise
                
    async def listen_forever(self, receiver):
        ''' Listens until the connection terminates.
        '''
        # Wait until terminate is called.
        try:
            while self:
                # Juuust in case things go south, add this to help
                # cancellation.
                await asyncio.sleep(0)
                await self.listener(receiver)
        
        # Catch this so we don't log a huge traceback on it.
        except ConnectionClosed as exc:
            logger.debug('CONN ' + str(self) + ' close message: ' + str(exc))
        
    @classmethod

    @fixture_api