Installable Python package coming soon.
To access the client module, we recommend directly just copying the contents of this directory to your Python project and use it as a module within your project.
If you want to install the package in development mode and want it to be accessible to other project spaces, you can install it. From this directory:
pip install -e .Then, add the full path to this module to your PYTHONPATH, and add that to your .zshrc or .bashrc file.
echo 'export PYTHONPATH="${PYTHONPATH}:/.../nsb_beta/python"' >> ~/.zshrc
To use the client API, import the module:
import nsb_client as nsbWhen receiving or fetching payloads, NSB will return None when no payload is
available for the operation or return a MessageEntry object when a payload is
available. This object is implemented to hold metadata about the payload and
includes the following attributes:
source(str): the identifier for the original source of this payloaddestination(str): the identifier for the final destination of this payloadpayload(bytes): the actual payload that is being retrievedpayload_size(int): the original size of the payload
You can initialize a NSBAppClient using its constructor:
nsb_conn = nsb.NSBAppClient(identifier, server_address, server_port)Parameters:
identifier(str): A unique identifier for this NSB application client instance. This identifier must match the corresponding identifier used within the NSB system and simulator for proper coordination when simulator mode is PER_NODE.server_address(str): The network address (IP address or hostname) where the NSB daemon is running.server_port(int): The port number on which the NSB daemon is listening for client connections.
Upon constructing the application client, this method will initialize with the NSB Daemon (which must be running at time of execution), connect to the database if configured to do so, and identify itself within the NSB system. We recommend having clients persist throughout the duration of a simulation in PULL mode, and it is required in PUSH mode.
You can send a payload through the NSBAppClient's send method:
nsb_conn.send(dest_id, payload)Parameters:
dest_id(str): The identifier of the destination NSB client that should receive the payload.payload: The data payload to send to the destination client.
Returns:
str: The key returned from storing the message in the database, if database storage is configured. ReturnsNoneif no database is used.
When this method is called, it creates an NSB SEND message containing the
destination information and payload, then transmits it to the NSB daemon. This
is a fire-and-forget operation that does not wait for or expect a response from
the daemon. If the NSB system is configured to use database storage, the message
will be stored and the unique key will be routed across NSB to be used for
retrieval during the simulator client's fetch operation. Capturing the key at
the application client is not necessary, but can be useful for debugging.
You can receive a payload through the NSBAppClient's receive method:
received = nsb_conn.receive()
if received:
payload = received.payload
# Process payload.
...This method has a secondary signature that allows the client to receive on
behalf of another dest_id and can also specify a timeout for server
communication, but this signature is not necessary.
received = nsb_conn.receive(dest_id, timeout)Parameters:
dest_id(str|None, optional): The identifier of the destination NSB client to receive messages for. Defaults toNone, which automatically assumes the destination is the current client (self).timeout(int|None, optional): The amount of time in seconds to wait for incoming data.Nonedenotes waiting indefinitely (blocking behavior), while0enables polling behavior (non-blocking). Defaults toNone.
Returns:
MessageEntry|None: The MessageEntry struct containing the received payload and metadata if a message is found, otherwiseNone.
Behavior varies by system mode:
- In PULL mode: This method creates an NSB RECEIVE message with the specified destination information and sends it to the daemon. The daemon responds with either a MESSAGE code containing the retrieved payload or a NO_MESSAGE code if no message is available. The full MessageEntry is returned to provide access to both payload and metadata.
- In PUSH mode: This method awaits incoming messages on the
Comms.Channels.RECVchannel usingselectwith the specified timeout. For polling behavior, settimeout=0. For indefinite listening (recommended for asynchronous listener implementations), usetimeout=Nonewhich will block until a message arrives.
NSB's Python client library provides an asynchronous method of receiving
payloads in listen:
async def some_coroutine(...):
...
received = await nsb_conn.listen()
if received:
payload = received.payload
# Process payload.
...Returns:
MessageEntry|None: The MessageEntry struct containing the received payload and metadata if a message is found, otherwiseNone.
This method is a coroutine that can be used in asynchronous calls. Its
implementation is similar to the receive method, but leverages asynchronous
listening instead. As the name suggests, this method is recommended for
implementing asynchronous listener logic.
You can initialize a NSBSimClient using its constructor in pretty much the
same way as its application counterpart:
nsb_conn = nsb.NSBSimClient(identifier, server_address, server_port)Parameters:
identifier(str): A unique identifier for this NSB simulator client instance. This identifier must match the corresponding identifier used within the NSB system and application client for proper coordination when simulator mode is PER_NODE.server_address(str): The network address (IP address or hostname) where the NSB daemon is running.server_port(int): The port number on which the NSB daemon is listening for client connections.
Just like the application client, upon constructing the application client, this method will initialize with the NSB Daemon (which must be running at time of execution), connect to the database if configured to do so, and identify itself within the NSB system.
NOTE: When the simulator mode is set to SYSTEM_WIDE, only one simulator client can connect to the daemon, as it as assumed that there is a global simulator client being used in the system. Also, as with the application client, we recommend having clients persist throughout the duration of a simulation in PULL mode, and it is required in PUSH mode.
To fetch messages to transmit over the simulated network, you can use the
fetch method:
fetched = nsb_conn.fetch()
if fetched:
src_id, dest_id, payload = fetched.source, fetched.destination, fetched.payload
# Transmit over the simulated network.
...Parameters:
src_id(str|None, optional): The identifier of the target source to fetch messages from. Defaults toNone, which will result in fetching the most recent message regardless of source, though this can be overwritten (see description below).timeout(int|None, optional): The amount of time in seconds to wait to receive data.Nonedenotes waiting indefinitely (blocking behavior), while0denotes polling behavior (non-blocking). Defaults toNoneand gets overwritten with the system default response timeout time.
Returns:
MessageEntry|None: The MessageEntry struct containing the fetched payload and metadata if a message is found, otherwiseNone.
Like with the NSBAppClient.receive method, behavior varies based on mode, In
system PULL mode, When this method is called, it creates an NSB FETCH message with the specified
source information and sends it to the daemon. The daemon responds with either
a MESSAGE code containing the fetched payload or a NO_MESSAGE code if no
message is available. If a message is found, the complete MessageEntry struct is
returned to provide access to both the payload and associated metadata. In PUSH mode,
this method just awaits the forwarded payloads.
When the system simulator mode is PER_NODE, the src_id gets overwritten with
the client's own identifier, as it is only fetching on its own behalf.
NSB's Python client library provides an asynchronous method of fetching
payloads in listen, similar to the method of the same name in NSBAppClient:
async def some_simulator_coroutine(...):
...
received = await nsb_conn.listen()
if received:
src_id, dest_id, payload = received.src_id, received_dest_id, received.payload
# Send payload from source to destination in network.
...Returns:
MessageEntry|None: The MessageEntry struct containing the fetched payload and metadata if a message is found, otherwiseNone.
This method is a coroutine that can be used in asynchronous calls. Its
implementation is similar to the fetch method, but leverages asynchronous
listening instead. As the name suggests, this method is recommended for
implementing asynchronous fetcher logic.
When a payload arrives at the destination node in the simulated network, you can
inform NSB and make it available for receiving on the NSBAppClient side of
things:
# Payload arrived at destination.
...
nsb_conn.post(src_id, dest_id, payload)Parameters:
src_id(str): The identifier of the source NSB client.dest_id(str): The identifier of the destination NSB client.payload(bytes): The payload data to post to the destination.
This method is intended to be used when a payload has finished being processed and the simulator client needs to hand it back to NSB. When called, it creates an NSB POST message containing the source, destination, and payload information, then transmits it to the daemon.
The code has been commented with Doxygen-style comment blocks for your convenience. You can use Doxygen to generate documentation as you wish.