4. NodeWire Python (nwpython)

nwpython is a variant of python modified for dataflow programming. It is a language developed specifically for NodeWire. In nwpython, every statement is a dataflow assignment and the normal control flow constructs have only limited support.

nwpython is built on these 3 programming constructs:

  • Functions. This is a sequence of assignments. A function can have parameters and can return a value. It is similar to a python function except that it does not support control flow constructs.
  • Rules. A rule is a sequence of assignments with a condition. The assignments are executed whenever the condition is true. The condition is only evaluated when any of the dependent variables of the condition changes.
  • Sequencers. This is used to implement state machines. Each state may have assignments, rules or both. Only the rules in the current state are active at any given time.

4.1. Data Types

nwpython supports standard python data types:

  • Numbers
  • String
  • List
  • Tuple
  • Dictionary/Object

In addition, a special type for representing nodes is also supported. A node is an object whose members are mapped to the node’s ports. Assigning a value to a node’s attribute causes the value to be transmitted to the corresponding port.

Unlike python, nwpython supports using the object syntax for dictionaries:

someone = {
    name: 'Ahmad Sadiq',
    age: 35

someone.age = 20

4.2. Expressions and Data flow


An assignment results in data flow. The left and right hand side could be nodes that are on two separate devices. They can also be mere variables

k = 7
mynode.port = 0.5
myobj = { num: 4, text: 'val'}
myarray = [1,2,3]

Conditional Expression

classification='odd' if k%2!=0 else 'even'

List Comprehensions

numbers = [k for k in range(10)]

Dictionary Comprehensions

words = ['one', 'two', 'three']
nums = [1,2,3]
obj = {words[k]:nums[k] for k in range(3)}

4.3. Functions

Function definitions takes exactly the same syntax as python function definitions, except that loop structures are not allowed.

def split(sentence):
    spaces = [-1] + [i for i in range(len(sentence)) if sentence[i]==' '] + [len(sentence)]
    words = [sentence[spaces[i]+1:spaces[i+1]] for i in range(len(spaces)-1)]
    return words

k = split('this is a long sentence')

You can use ‘if’ expressions in a function:

def fn(i):
  if i==9:
    emp = 'nine'
  elif i==8:
    emp = 'eight'
  elif i==7:
    emp = 'seven'
    return False
  return emp

4.4. Rules

Rules are used to specify expressions that should be evaluated whenever a certain condition is true.

led = 0
delay = time

when time-delay>seconds(1):
  time = delay
  led = 1 if led == 0 else 1

Rules starts with the ‘when’ keyword followed by the condition. Each variable used in the condition is called a signal. And each time any of the signal changes, the condition is evaluated and if true the expressions are then evaluated as well.

When time functions such as seconds, minutes, hours and days are used in the condition, they cause the condition to be evaluated periodically. For example seconds causes the condition to be evaluated every second while hours causes it to be evaluated every hour.

4.5. Sequencers

sequencer blinky:
    led = 0
    delay = time
    goto toggle
    led = 1 if led == 0 else 1
    goto wait
    delay = time
    when time-delay>seconds(1): goto toggle

A sequencer is defined by using the ‘sequencer’ keyword followed by the name of the sequencer instance. Each state of the sequencer is defined by a label and can have assignments or rules under it. Only rules in the current state are active at any point in time. State transition happens by using ‘goto state’ within the sequencer or by assigning the sequencer name to the state from outside the sequencer:

blinky = wait

The initial state is always the first state, ‘init’ in this case.

4.6. Control Flow

Python’s if-elif-else and for statements are supported but only within the above 3 constructs.

when employee:
  if 'userid' in employee:
    emp = employee
    emp = copy(employee)
    emp.userid = 1

4.7. Built-In Functions


with one parameter

Converts number to string

with two parameters

Equivalent to python string’s format function.

str(‘{}-{}-{}’, time.year, time.month, time.mday)


Time format.

ftime('%X', time) # gets the time component of time
ftime('%x', time) # gets the date component of time


converts date to epoch.

epoch = date('2018-04-12', '%Y-%m-%d')


gets the absolute value


gets the length of the array.


reduce([1,2,3], lambda sum, x: sum+x)


sort([1,-2,3], lambda x: abs(x))


creates a node object. A node object is a reference to a remote node and can be used to communicated with the node. ports on the node are mapped to attributes of the object.

mynode = node(‘nodename’)

mynode = node(‘nodename’, ‘myinstance’)


converts string to integer


converts string to float


returns the type name of the parameter


makes a shallow copy of the object


makes a deep copy of the object


same as python’s range function


creates a node from script entered in interactive mode


executes an existing script


terminates a script

4.8. Special Variables


Returns the epoch.

You can also use time.second, time.minute, time.hour, time.month, time.year, time.mday, time.wday, amd time.yday to get various compoents of the epoch.


returns all the nodes currently registered to the instance. You can also monitor when new nodes are connected by using nodes as a signal:

when nodes:
    # new node connected
    no_nodes = len(nodes)


used inside a rule that has an input port as a signal, the node address where the current value of the port came from

inputs = ['led']
when led:
    sender.email = {subject:'led switched', body:'command successfully received'}


returns the name of the signal that triggered the current rule or sequencer state

a = 0
b = 0
c = 0

s = 0

when a or b or c:
    if signal == 'a':
    for i in range(10):
        s = s + i
        s = a + b + c


This variable is used inside a rule to represent the user interface node (usually a browser). It can be used to watch user variables (defined in the layout).

when me.username:
    me.notification = {
        subject:  'Welcome, ' + me.username,
        body: 'You have logged in'

to test this script, you can use the following layout:

<Input title="name" node="me" port="username" />


This is a signal that is set whenever the ui is loaded on the browser:


List of input port names


List of output port names


the database object

4.9. Data flow

The Rule and Sequencer constructs are used to implement data flow logic.

inputs = ['data']
data = 0
data2 = 0
items = []

when data:
  data2 = abs(data)

when data2:
  if data2 not in items:
    items =  items + [data2]

Each when construct implement a fixed logic which acts on data as it is passed through the pipeline.

The following script implements a data entry logic where each employee entered is stored in a list. But before then it passes through a 2-stage pipeline where it is checked first for missing userid and an autogenerated value provided if necessary. In the second stage, it is added to the list if an employee with the same userid is not already in the list, otherwise the corresponding list item is updated.

inputs = ['employee']
employee = {}
emp = {}
all_employees = []

when employee:
  if 'userid' in employee:
    emp = employee
    emp = copy(employee)
    emp.userid = reduce(all_employees, lambda max, x: x if x.userid>max.userid else max).userid+1

when emp:
  ee = [e for e in all_employees if e.userid==emp.userid]
  if len(ee)==0:
    all_employees = all_employees + [emp]
    index = [i for i in range(len(all_employees)) if all_employees[i].userid==emp.userid][0]
    all_employees[index] = emp

4.10. Accessing Databases and Data-logging

nwpython provides a built-in database that is based on mongoDB. Each instance is assigned an implicit database and it can create and access tables (collections) only within this implicit database

Create a database record

You can save a document to a collection by assigning it to the db object while specifying the collection name as follows:

db[table] = {
   name: 'Ahmad Sadiq',
   age: 35

where ‘table’ is the name of the collection. Note that each time you save a document , a uniques _id of 12 bytes hexadecimal number is added to it.

Retrieve a database record

to retrieve a document from a collection, use the mongoDB query syntax:

somepeople = db[table({name:'Ahmad Sadiq'})]

note that the return value is a list.

To return all the documents in a collection use:

allthepeople = db[table({})]

Update a database record

Saving a record while specifying an existing _id will update the record that matches the _id specified:

db[table] = {
   _id: '5abcfec2c6356b5e21df2894',
   name: 'Ahmad Sadiq',
   age: 20

4.11. The auto script

This script is automatically executed whenever the cloud service (re)starts. The cloud service could be restarted at anytime for maintenance or update. When this happens all your scripts that were running will be terminated. You can use the auto app to automatically restart all the scripts that you require to be running at all times.

The auto script must be saved under the project name ‘auto’.