API Reference¶
Core functionality¶
-
class
across.Connection¶ Connection lets you run Python code in a remote Python process, and vice versa.
To create a connection, use one of
from_*methods documented below. Be aware thatfrom_*methods typically establish connection in background. If you want to make sure that connection handshake was successful, you should usewithstatement:# __enter__ waits for handshake to complete # __exit__ takes care of calling conn.close() with Connection.from_tcp('wonderland', 1865) as conn: conn.call(os.mkdir, '/home/alice')
However, waiting for handshake to complete is optional, so the above can be rewritten as:
conn = Connection.from_tcp('wonderland', 1865) try: conn.call(os.mkdir, '/home/alice') finally: conn.close()
Connections are multithreaded.
-
classmethod
from_tcp(host, port, *, options=None)¶ Connect to a remote host over TCP.
options, if specified, should be an instance ofOptionsclass.
-
classmethod
from_unix(path, *, options=None)¶ Connect over Unix domain sockets.
options, if specified, should be an instance ofOptionsclass.
-
classmethod
from_socket(sock, *, options=None)¶ Wrap an already connected socket in a
Connectionobject.This method may be used by server implementations to handle accepted sockets.
sockshould be a socket created withsocket.socket()function.options, if specified, should be an instance ofOptionsclass.
-
classmethod
from_pipes(in_pipe, out_pipe, *, options=None)¶ Wrap a pair of input/output pipes in a
Connectionobject.in_pipeandout_pipeshould be file objects obtained e.g. by passingos.pipe()descriptors toos.fdopen()function.options, if specified, should be an instance ofOptionsclass.
-
classmethod
from_stdio(*, options=None)¶ Create a connection over process standard input/output pipes.
options, if specified, should be an instance ofOptionsclass.
-
classmethod
from_command(args, *, options=None)¶ Spawn a child process with a given list of command line arguments (first being an executable path) and communicate with it over standard input/output pipes.
options, if specified, should be an instance ofOptionsclass.
-
classmethod
from_shell(script, *, options=None)¶ Spawn a shell process executing a given shell script, and communicate with it over standard input/output pipes.
options, if specified, should be an instance ofOptionsclass.
-
classmethod
from_process(proc, *, options=None)¶ Wrap an existing child process in a
Connectionobject.procshould be an instance ofsubprocess.Popenclass.options, if specified, should be an instance ofOptionsclass.
-
export(*modules)¶ Make one or more local modules importable remotely.
Only top-level modules may be exported. When a module is exported, all its submodules are considered exported as well.
This method may be used to make functions/classes defined in a local
__main__module accessible remotely.When importing a module remotely, local exported modules take precedence over modules installed remotely.
For this method to work, the connection on the other side must be created with
accept_exported_modulesoption set toTrue. This is a safety precaution to prevent a connection from injecting its local modules into a remote process that shared among many connections.
-
call(func, /, *args, **kwargs)¶ Call a given function remotely with supplied arguments, and pass back function return value / raised exception.
All arguments provided to
call()method, as well as its return value / raised exception, must be pickleable.Apart from an exception raised by a given function, this method may also raise
OperationErrorto indicate an internal error (pickling / unpickling error, lost connection with a remote process, etc.).
-
call_ref(func, /, *args, **kwargs)¶ Similar to
call(), but instead of passing back function return value directly, return a proxy referencing it. SeeProxyfor more information.call_ref()is useful when a given function returns an unpickleable object:fh = conn.call(open, 'extinct_species', 'w') fh.write('dinosaurs\n') fh.write('unicorns\n') fh.close()
-
replicate(obj)¶ Create a remote copy of a given object, and return a proxy referencing it.
For
replicate()method to work,objmust be pickleable. This method may raiseOperationErrorto indicate an internal error (pickling / unpickling error, lost connection with a remote process, etc.).
-
execute(source, /, **vars)¶ Execute a snippet of Python code remotely, and return result of the last expression.
conn.execute('2 + 2') conn.execute('two = 2; two + two') conn.execute('x + x', x=2)
-
is_connected()¶ Return a boolean value indicating whether connection with a remote site is still alive.
-
cancel()¶ Cancel all operations and forcibly disconnect from a remote Python process.
This method may be called at any time, and can be used to abort e.g.
__enter__orclose()methods being called in a different thread.close()aftercancel()will still take care of freeing all the resources, but clean protocol shutdown will be skipped.
-
close()¶ Close the connection to a remote process, and free all resources allocated by the connection.
If, between opening and closing the connection, a fatal error occurred, then this method raises an exception (e.g.
ProtocolError). Exception will be raised also after a remote process died during communication, orcancel()was called on a remote connection.
-
wait()¶ Wait for remote process to close the connection.
-
classmethod
-
across.get_bios()¶ Get a Python script that, when executed via
python -c, creates a connection over standard input/output pipes. This script has no library dependencies, so it can be remotely executed (e.g. viassh) on machines where across is not installed:from shlex import quote cmd = 'python3 -c ' + quote(get_bios()) with Connection.from_command(['ssh', 'wonderland', cmd]): conn.call(os.mkdir, '/home/alice')
A remote connection object constructed this way has
accept_exported_modulesoption set toTrue.
-
class
across.Options(**options)¶ With options you can tweak connections for your specific use case.
-
timeout¶ Timeout for communication operations: sending/receiving data, connecting to a remote host, etc. The default value tries to keep a balance between detecting broken connections fast and avoiding false positives. Currently, it’s one minute.
-
accept_exported_modules¶ Boolean value indicating whether modules from a remote connection site can be imported into the current process (see
Connection.export()). Defaults toFalse.
-
copy(**options)¶ Create a copy of the current options, optionally overriding some values.
-
-
class
across.Proxy¶ Proxies act as handles to objects living in remote Python processes. All method calls on a proxy are forwarded to a remote object, and executed there.
Example:
cheeses = conn.call_ref(list) cheeses.append('brie') cheeses.append('gouda')
Proxies can also be passed as arguments to remote functions:
def append_to(col, item): col.append(item) cheeses = conn.call_ref(list) append_to(cheeses, 'cheddar')
It is also possible to retrieve a copy of a referenced object:
import copy local_cheeses = copy.deepcopy(cheeses)
Not every method is forwarded by proxies. Following methods are supported:
public methods, that is, those which name doesn’t start with an underscore (
_);container special methods:
__len__,__contains__,__getitem__,__setitem__,__delitem__;__call__.
To retrieve connection associated with a proxy, use
get_proxy_connection()function.
-
across.get_proxy_connection(proxy)¶ Return a connection object associated with a given proxy.
-
across.ref(obj)¶ Wrap a given object in a special marker indicating that the object should be passed / returned to a remote process as a proxy.
def create_list(): return ref([]) remote_cheeses = conn.call(create_list) remote_cheeses.append('gorgonzola')
def append_feta(cheeses): cheeses.append('feta') local_cheeses = [] conn.call(append_feta, ref(local_cheeses))
-
across.get_connection()¶ Return a connection associated with the current thread.
-
exception
across.OperationError¶ OperationError, and its subclass –DisconnectError– are raised byConnectionmethods to indicate an internal operation error. Internal connection errors fall into two categories:non-fatal errors cause only a single operation (e.g. a single invocation of
Connection.call()method) to fail, and are signalled by raisingOperationErrordirectly; an example of a non-fatal error is a pickling / unpickling error;fatal errors break the whole connection, meaning that all successive operations on that connection will fail as well; fatal errors are signalled by raising
DisconnectError; an example of a fatal error is an I/O error while communicating with a remote process.
-
exception
across.DisconnectError¶ Subclass of
OperationError, raised in case of fatal connection errors.
-
exception
across.ProtocolError¶ Exception indicating that an internal protocol error occurred while communicating with a remote process.
-
across.set_debug_level(level)¶ Print logs to standard error output. Following values of
levelare recognized:0– no output (default setting);1– print INFO, WARNING and ERROR messages;2– print additionally DEBUG messages.
Logging integration¶
-
class
across.logging.AcrossHandler(conn=None)¶ Create a log handler suitable for use with
loggingmodule. All messages logged through this handler will be forwarded to the corresponding logger in the remote process referenced by a given connection.Example:
import logging import across import across.logging logger = logging.getLogger('rainbow') def remote_main(): # set up logging logger.setLevel(logging.DEBUG) handler = across.logging.AcrossHandler() handler.setLevel(logging.DEBUG) logger.addHandler(handler) # do some stuff here logger.info('Doing some stuff') def main(): # set up logging logger.setLevel(logging.DEBUG) handler = logging.StreamHandler() handler.setLevel(logging.DEBUG) logger.addHandler(handler) with across.Connection(...) as conn: conn.call(remote_main)
Servers¶
-
class
across.servers.ConnectionHandler¶ Abstract base class for connection handlers. Connection handlers are responsible for wrapping accepted sockets in
Connectionobjects, and managing a pool of running connections.Example:
with LocalConnectionHandler() as handler: sock = socket.socket() sock.bind(('wonderland', 1865)) sock.listen(socket.SOMAXCONN) while True: handler.handle_socket(sock.accept()[0])
-
handle_socket(sock)¶ Create
Connectionobject around a given socket.sockshould be a socket object created withsocket.socket()function.
-
-
class
across.servers.LocalConnectionHandler(*, options=None)¶ Subclass of
ConnectionHandlerthat creates connections in the current process.options, if specified, are passed to created connections. The value ofoptionsshould be an instance ofOptionsclass.
-
class
across.servers.ProcessConnectionHandler(*, options=None)¶ Subclass of
ConnectionHandlerthat creates a child process for each accepted socket.options, if specified, are passed to created connections. The value ofoptionsshould be an instance ofOptionsclass.
-
class
across.servers.BIOSConnectionHandler(*, options=None)¶ Similar to
ProcessConnectionHandler, but created child processes do not import a local version of across library. Instead, across sources are fetched from a remote peer.options, if specified, should be an instance ofOptionsclass.Options.timeoutspecifies how longclose()will wait for child processes to terminate gracefully before killing them. Other options are ignored.
-
across.servers.run_tcp(host, port, *, handler=None)¶ Accept connections on a given TCP endpoint until
KeyboardInterruptis received.handler, if provided, should be an instance ofConnectionHandlerclass. Defaults toLocalConnectionHandler.
-
across.servers.run_unix(path, *, handler=None)¶ Create Unix domain socket and accept connections until
KeyboardInterruptis received.handler, if provided, should be an instance ofConnectionHandlerclass. Defaults toLocalConnectionHandler.