Introduction¶
SiPyCo (Simple Python Communications) is a library for writing networked Python programs. It was originally part of ARTIQ, and was split out to enable light-weight programs to be written without a dependency on ARTIQ.
API documentation¶
sipyco.pyon
module¶
This module provides serialization and deserialization functions for Python objects. Its main features are:
Human-readable format compatible with the Python syntax.
Each object is serialized on a single line, with only ASCII characters.
Supports all basic Python data structures: None, booleans, integers, floats, complex numbers, strings, tuples, lists, dictionaries.
Those data types are accurately reconstructed (unlike JSON where e.g. tuples become lists, and dictionary keys are turned into strings).
Supports Numpy arrays. (Converted to be C-contiguous as required.)
The main rationale for this new custom serializer (instead of using JSON) is that JSON does not support Numpy and more generally cannot be extended with other data types while keeping a concise syntax. Here we can use the Python function call syntax to express special data types.
- sipyco.pyon.decode(s)¶
Parses a string in the Python syntax, reconstructs the corresponding object, and returns it. Shouldn’t be used with untrusted inputs, as it can cause vulnerability against injection attacks.
- sipyco.pyon.encode(x, pretty=False)¶
Serializes a Python object and returns the corresponding string in Python syntax.
- sipyco.pyon.load_file(filename)¶
Parses the specified file and returns the decoded Python object.
- sipyco.pyon.store_file(filename, x)¶
Encodes a Python object and writes it to the specified file.
sipyco.pc_rpc
module¶
This module provides a remote procedure call (RPC) mechanism over sockets
between conventional computers (PCs) running Python. It strives to be
transparent and uses sipyco.pyon
internally so that e.g. Numpy
arrays can be easily used.
Note that the server operates on copies of objects provided by the client,
and modifications to mutable types are not written back. For example, if the
client passes a list as a parameter of an RPC method, and that method
append()s
an element to the list, the element is not appended to the
client’s list.
- class sipyco.pc_rpc.AsyncioClient¶
This class is similar to
sipyco.pc_rpc.Client
, but usesasyncio
instead of blocking calls.All RPC methods are coroutines.
Concurrent access from different asyncio tasks is supported; all calls use a single lock.
- close_rpc()¶
Closes the connection to the RPC server.
No further method calls should be done after this method is called.
- async connect_rpc(host, port, target_name=<class 'sipyco.pc_rpc.AutoTarget'>)¶
Connects to the server. This cannot be done in __init__ because this method is a coroutine. See
sipyco.pc_rpc.Client
for a description of the parameters.
- get_local_host()¶
Returns the address of the local end of the connection.
- get_rpc_id()¶
Returns a tuple (target_names, description) containing the identification information of the server.
- get_selected_target()¶
Returns the selected target, or
None
if no target has been selected yet.
- async select_rpc_target(target_name)¶
Selects a RPC target by name. This function should be called exactly once if the connection was created with
target_name=None
.
- class sipyco.pc_rpc.AutoTarget¶
Use this as target value in clients for them to automatically connect to the target exposed by the server. Servers must have only one target.
- class sipyco.pc_rpc.BestEffortClient(host, port, target_name, firstcon_timeout=1.0, retry=5.0)¶
This class is similar to
sipyco.pc_rpc.Client
, but network errors are suppressed and connections are retried in the background.RPC calls that failed because of network errors return
None
. Other RPC calls are blocking and return the correct value.- Parameters:
firstcon_timeout – Timeout to use during the first (blocking) connection attempt at object initialization.
retry – Amount of time to wait between retries when reconnecting in the background.
- close_rpc()¶
Closes the connection to the RPC server.
No further method calls should be done after this method is called.
- class sipyco.pc_rpc.Client(host, port, target_name=<class 'sipyco.pc_rpc.AutoTarget'>, timeout=None)¶
This class proxies the methods available on the server so that they can be used as if they were local methods.
For example, if the server provides method
foo
, andc
is a localClient
object, then the method can be called as:result = c.foo(param1, param2)
The parameters and the result are automatically transferred from the server.
Only methods are supported. Attributes must be accessed by providing and using “get” and/or “set” methods on the server side.
At object initialization, the connection to the remote server is automatically attempted. The user must call
close_rpc()
to free resources properly after initialization completes successfully.- Parameters:
host – Identifier of the server. The string can represent a hostname or a IPv4 or IPv6 address (see
socket.create_connection
in the Python standard library).port – TCP port to use.
target_name – Target name to select.
IncompatibleServer
is raised if the target does not exist. UseAutoTarget
for automatic selection if the server has only one target. UseNone
to skip selecting a target. The list of targets can then be retrieved usingget_rpc_id()
and then one can be selected later usingselect_rpc_target()
.timeout – Socket operation timeout. Use
None
for blocking (default),0
for non-blocking, and a finite value to raisesocket.timeout
if an operation does not complete within the given time. See alsosocket.create_connection()
andsocket.settimeout()
in the Python standard library. A timeout in the middle of a RPC can break subsequent RPCs (from the same client).
- close_rpc()¶
Closes the connection to the RPC server.
No further method calls should be done after this method is called.
- get_local_host()¶
Returns the address of the local end of the connection.
- get_rpc_id()¶
Returns a tuple (target_names, description) containing the identification information of the server.
- get_selected_target()¶
Returns the selected target, or
None
if no target has been selected yet.
- select_rpc_target(target_name)¶
Selects a RPC target by name. This function should be called exactly once if the object was created with
target_name=None
.
- exception sipyco.pc_rpc.IncompatibleServer¶
Raised by the client when attempting to connect to a server that does not have the expected target.
- class sipyco.pc_rpc.Server(targets, description=None, builtin_terminate=False, allow_parallel=False)¶
This class creates a TCP server that handles requests coming from Client objects (whether
Client
,BestEffortClient
, orAsyncioClient
).The server is designed using
asyncio
so that it can easily support multiple connections without the locking issues that arise in multi-threaded applications. Multiple connection support is useful even in simple cases: it allows new connections to be be accepted even when the previous client failed to properly shut down its connection.If a target method is a coroutine, it is awaited and its return value is sent to the RPC client. If
allow_parallel
is true, multiple target coroutines may be executed in parallel (one per RPC client), otherwise a lock ensures that the calls from several clients are executed sequentially.- Parameters:
targets – A dictionary of objects providing the RPC methods to be exposed to the client. Keys are names identifying each object. Clients select one of these objects using its name upon connection.
description – An optional human-readable string giving more information about the server.
builtin_terminate – If set, the server provides a built-in
terminate
method that unblocks any tasks waiting onwait_terminate
. This is useful to handle server termination requests from clients.allow_parallel – Allow concurrent asyncio calls to the target’s methods.
- sipyco.pc_rpc.simple_server_loop(targets, host, port, description=None, allow_parallel=False, *, loop=None)¶
Runs a server until an exception is raised (e.g. the user hits Ctrl-C) or termination is requested by a client.
See
sipyco.pc_rpc.Server
for a description of the parameters.
sipyco.fire_and_forget
module¶
- class sipyco.fire_and_forget.FFProxy(target)¶
Proxies a target object and runs its methods in the background.
All method calls to this object are forwarded to the target and executed in a background thread. Method calls return immediately. Exceptions from the target method are turned into warnings. At most one method from the target object may be executed in the background; if a new call is submitted while the previous one is still executing, a warning is printed and the new call is dropped.
This feature is typically used by RPC clients to wrap slow and non-critical RPCs, when avoiding blocking is more important than reliability.
- ff_join()¶
Waits until any background method finishes its execution.
sipyco.sync_struct
module¶
This module helps synchronizing a mutable Python structure owned and modified by one process (the publisher) with copies of it (the subscribers) in different processes and possibly different machines.
Synchronization is achieved by sending a full copy of the structure to each
subscriber upon connection (initialization), followed by dictionaries
describing each modification made to the structure (mods, see
ModAction
).
Structures must be PYON serializable and contain only lists, dicts, and immutable types. Lists and dicts can be nested arbitrarily.
- class sipyco.sync_struct.ModAction(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)¶
Describes the type of incremental modification.
Mods are represented by a dictionary
m
.m["action"]
describes the type of modification, as per this enum, serialized as a string if required.The path (member field) the change applies to is given in
m["path"]
as a list; elements give successive levels of indexing. (There is nopath
on initial initialization.)Details on the modification are stored in additional data fields specific to each type.
For example, this represents appending the value
42
to an arraydata.counts[0]
:{ "action": "append", "path": ["data", "counts", 0], "x": 42 }
- append = 'append'¶
Appends x to target list.
- delitem = 'delitem'¶
Removes target’s key.
- init = 'init'¶
A full copy of the data is sent in struct; no path given.
- insert = 'insert'¶
Inserts x into target list at index i.
- pop = 'pop'¶
Removes index i from target list.
- setitem = 'setitem'¶
Sets target’s key to value.
- class sipyco.sync_struct.Notifier(backing_struct, root=None, path=[])¶
Encapsulates a structure whose changes need to be published.
All mutations to the structure must be made through the
Notifier
. The original structure must only be accessed for reads.In addition to the list methods below, the
Notifier
supports the index syntax for modification and deletion of elements. Modification of nested structures can be also done using the index syntax, for example:>>> n = Notifier([]) >>> n.append([]) >>> n[0].append(42) >>> n.raw_view [[42]]
This class does not perform any network I/O and is meant to be used with e.g. the
Publisher
for this purpose. Only one publisher at most can be associated with aNotifier
.- Parameters:
backing_struct – Structure to encapsulate.
- append(x)¶
Append to a list.
- insert(i, x)¶
Insert an element into a list.
- pop(i=-1)¶
Pop an element from a list. The returned element is not encapsulated in a
Notifier
and its mutations are no longer tracked.
- raw_view¶
The raw data encapsulated (read-only!).
- class sipyco.sync_struct.Publisher(notifiers)¶
A network server that publish changes to structures encapsulated in a
Notifier
.- Parameters:
notifiers – A dictionary containing the notifiers to associate with the
Publisher
. The keys of the dictionary are the names of the notifiers to be used withSubscriber
.
- class sipyco.sync_struct.Subscriber(notifier_name, target_builder, notify_cb=None, disconnect_cb=None)¶
An asyncio-based client to connect to a
Publisher
.- Parameters:
notifier_name – Name of the notifier to subscribe to.
target_builder – A function called during initialization that takes the object received from the publisher and returns the corresponding local structure to use. Can be identity.
notify_cb – An optional function called every time a mod is received from the publisher. The mod is passed as parameter. The function is called after the mod has been processed. A list of functions may also be used, and they will be called in turn.
disconnect_cb – An optional function called when disconnection happens from external causes (i.e. not when
close
is called).
- sipyco.sync_struct.process_mod(target, mod)¶
Apply a mod to the target, mutating it.
- sipyco.sync_struct.update_from_dict(target, source)¶
Updates notifier contents from given source dictionary.
Only the necessary changes are performed; unchanged fields are not written. (Currently, modifications are only performed at the top level. That is, whenever there is a change to a child array/struct the entire member is updated instead of choosing a more optimal set of mods.)
sipyco.remote_exec
module¶
This module provides facilities for experiment to execute code remotely on RPC servers.
The remotely executed code has direct access to the resources on the remote end, so it can transfer vlarge amounts of data with them, and only exchange higher-level, processed data with the client (and over the network).
RPC servers with support for remote execution contain an additional target
that gives RPC access to instances of RemoteExecServer
. One such instance
is created per client connection and manages one Python namespace in which the
client can execute arbitrary code by calling the methods of
RemoteExecServer
.
The namespaces are initialized with the following global values:
controller_driver
- the target object of the RPC server.
controller_initial_namespace
- a server-wide dictionary copied when initializing a new namespace.all values from
controller_initial_namespace
.
With ARTIQ, access to a controller with support for remote execution is done through an additional device database entry of this form:
"$REXEC_DEVICE_NAME": {
"type": "controller_aux_target",
"controller": "$CONTROLLER_DEVICE_NAME",
"target_name": "$TARGET_NAME_FOR_REXEC"
}
Specifying target_name
is mandatory in all device database entries for all
controllers with remote execution support.
- class sipyco.remote_exec.RemoteExecServer(initial_namespace)¶
RPC target created at each connection by controllers with remote execution support. Manages one Python namespace and provides RPCs for code execution.
- add_code(code)¶
Executes the specified code in the namespace.
- Parameters:
code – a string containing valid Python code
- call(function, *args, **kwargs)¶
Calls a function in the namespace, passing it positional and keyword arguments, and returns its value.
- Parameters:
function – a string containing the name of the function to execute.
- sipyco.remote_exec.connect_global_rpc(controller_rexec, host=None, port=3251, target='master_dataset_db', name='dataset_db')¶
Creates a global RPC client in a RPC server that is used across all remote execution connections.
The default parameters are designed to connects to an ARTIQ dataset database (i.e. gives direct dataset access to experiment code remotely executing in controllers).
If a global object with the same name already exists, the function does nothing.
- Parameters:
controller_rexec – the RPC client connected to the server’s remote execution interface.
host – the host name to connect the RPC client to. Default is the local end of the remote execution interface (typically, the ARTIQ master).
port – TCP port to connect the RPC client to.
target – name of the RPC target.
name – name of the object to insert into the global namespace.
- sipyco.remote_exec.simple_rexec_server_loop(target_name, target, host, port, description=None)¶
Runs a server with remote execution support, until an exception is raised (e.g. the user hits Ctrl-C) or termination is requested by a client.
sipyco.common_args
module¶
- sipyco.common_args.verbosity_args(parser)¶
Adds -v/-q arguments that increase or decrease the default logging levels. Repeat for higher levels.
sipyco.asyncio_tools
module¶
- class sipyco.asyncio_tools.AsyncioServer¶
Generic TCP server based on asyncio.
Users of this class must derive from it and define the
_handle_connection_cr()
method/coroutine.- async start(host, port)¶
Starts the server.
The user must call
stop()
to free resources properly after this method completes successfully.This method is a coroutine.
- Parameters:
host – Bind address of the server (see
asyncio.start_server
from the Python standard library).port – TCP port to bind to.
- async stop()¶
Stops the server.
- sipyco.asyncio_tools.atexit_register_coroutine(coroutine, *, loop=None)¶
loop must be specified unless this is called from a running event loop
sipyco.logging_tools
module¶
- class sipyco.logging_tools.LogForwarder(host, port, reconnect_timer=5.0, queue_size=1000, **kwargs)¶
- emit(record)¶
Do whatever it takes to actually log the specified logging record.
This version is intended to be implemented by subclasses and so raises a NotImplementedError.
- class sipyco.logging_tools.MultilineFormatter¶
- format(record)¶
Format the specified record as text.
The record’s attribute dictionary is used as the operand to a string formatting operation which yields the returned string. Before formatting the dictionary, a couple of preparatory steps are carried out. The message attribute of the record is computed using LogRecord.getMessage(). If the formatting string uses the time (as determined by a call to usesTime(), formatTime() is called to format the event time. If there is exception information, it is formatted using formatException() and appended to the message.
- class sipyco.logging_tools.Server¶
Remote logging TCP server.
Log entries are in the format:
source:levelno<total_lines>:name:message continuation... ...continuation
Remote Procedure Call tool¶
This tool is the preferred way of handling simple RPC servers. Instead of writing a client for simple cases, you can simply use this tool to call remote functions of an RPC server.
Listing existing targets
The
list-targets
sub-command will print to standard output the target list of the remote server:$ sipyco_rpctool hostname port list-targets
Listing callable functions
The
list-methods
sub-command will print to standard output a sorted list of the functions you can call on the remote server’s target.The list will contain function names, signatures (arguments) and docstrings.
If the server has only one target, you can do:
$ sipyco_rpctool hostname port list-methods
Otherwise you need to specify the target, using the
-t target
option:$ sipyco_rpctool hostname port list-methods -t target_name
Remotely calling a function
The
call
sub-command will call a function on the specified remote server’s target, passing the specified arguments. Like with the previous sub-command, you only need to provide the target name (with-t target
) if the server hosts several targets.The following example will call the
set_attenuation
method of the Lda controller with the argument5
:$ sipyco_rpctool ::1 3253 call -t lda set_attenuation 5
In general, to call a function named
f
with N arguments named respectivelyx1, x2, ..., xN
you can do:$ sipyco_rpctool hostname port call -t target f x1 x2 ... xN
You can use Python syntax to compute arguments as they will be passed to the
eval()
primitive. The numpy package is available in the namespace asnp
. Beware to use quotes to separate arguments which use spaces:$ sipyco_rpctool hostname port call -t target f '3 * 4 + 2' True '[1, 2]' $ sipyco_rpctool ::1 3256 call load_sample_values 'np.array([1.0, 2.0], dtype=float)'
If the called function has a return value, it will get printed to the standard output if the value is not None like in the standard python interactive console:
$ sipyco_rpctool ::1 3253 call get_attenuation 5.0
Command-line details:
ARTIQ RPC tool
usage: sipyco_rpctool [-h]
SERVER PORT {list-targets,list-methods,call,interactive}
...
Positional Arguments¶
- SERVER
hostname or IP of the controller to connect to
- PORT
TCP port to use to connect to the controller
- action
Possible choices: list-targets, list-methods, call, interactive
Sub-commands¶
list-targets¶
list existing targets
sipyco_rpctool list-targets [-h]
list-methods¶
list target’s methods
sipyco_rpctool list-methods [-h] [-t TARGET]
Named Arguments¶
- -t, --target
target name
call¶
call a target’s method
sipyco_rpctool call [-h] [-t TARGET] METHOD ...
Positional Arguments¶
- METHOD
method name
- ARGS
arguments
Named Arguments¶
- -t, --target
target name
interactive¶
enter interactive mode (default)
sipyco_rpctool interactive [-h] [-t TARGET]
Named Arguments¶
- -t, --target
target name