Source code for net.connect

# -*- coding: utf-8 -*-
"""
Connect Module
--------------

Contains the connect decorator and should have nothing else.
"""

__all__ = [
    'connect'
]

# std imports
from functools import wraps

# package imports
from net import LOGGER

# 3rd party
from termcolor import colored

# package imports
from .peer import Peer, _Peer


# noinspection PyShadowingNames
[docs]def connect(tag=None): """ Registers a function as a connection. This will be tagged and registered with the Peer server. The tag is a base64 encoded path to the function or can be manually tagged with the tag parameter. Tagging a named function allows you to interconnect functions between code bases. For example, a connected function with no tag is tied to the ``func.__module__`` + ``func.__name__``. This means the peers will only know which functions are compatible based on the namespace staying the same. .. code-block:: python # app version 1 running on PeerA app/ module/ function # app version 2 running on PeerB app/ module/ function2 <- # renamed from function In the above example, PeerA could make a request to PeerB to execute "app.module.function". But that function no longer exists as far as PeerB is concerned. The source code and functionality could be exactly the same, but the logical location is different and therefore will fail. .. code-block:: python # app version 1 running on PeerA app/ module/ function (tagged: "MyTaggedFunction") # app version 2 running on PeerB app/ module/ function2 (tagged: "MyTaggedFunction") In the above example, we have tagged function and function2 with the same tag, "MyTaggedFunction". Now when PeerA requests to execute, it will request that PeerB executes "MyTaggedFunction" which is attached to the new renamed function. Standard no tagging .. code-block:: python @net.connect() def your_function(some_value): return some_value Custom tagging .. code-block:: python @net.connect("MyTaggedFunction") def your_function(some_value): return some_value """ def wrapper(func): # grab the local peer peer = Peer() # register the function with the peer handler connection_name = peer.register_connection(func, tag if tag else None) @wraps(func) def interface(*args, **kwargs): # execute the function as is if this is being run by the local peer if not kwargs.get('peer'): LOGGER.debug("{0} execution on {1}".format( colored("Local", 'green', attrs=['bold']), colored(str(peer.friendly_id), 'magenta')) ) # run the target connection locally response = func(*args, **kwargs) # This is to simulate the Peer environment as far as processing # the flags processor = peer.process_flags(response) if processor: terminate = processor(connection_name, peer.friendly_id) if terminate: return terminate # this response should be the same as if it was remote. But just # in the context of the local host. This means I must encode the # response and then decode it which is what the remote host will # do. response = peer.encode(response) return peer.decode(response) encoded_address = kwargs.get('peer') remote_peer_address = peer.decode_id(encoded_address) LOGGER.debug("{0} execution on {1}".format( colored("Remote", 'blue', attrs=['bold']), colored(remote_peer_address, 'magenta')) ) # clean out the peer argument from the kwargs and make request response = peer.request(encoded_address, connection_name, args, kwargs) # handle error catching if isinstance(response, dict): if response.get('payload') and response.get('payload') == 'error': # unpack the traceback and raise an exception full_error = "RemoteError\n" + response['traceback'] LOGGER.error(full_error) raise Exception(full_error) # return the response return response return interface return wrapper