"end": 100957
},
"positional": [],
"named": []
},
"input": {
"Value": {
"String": {
"val": "hello",
"span": {
"start": 100953,
"end": 100957
}
}
}
}
}
}
```
That is, we passed len the string "hello" and it replied with the following [`PipelineData`](plugin_protocol_reference.md#pipelinedata-plugin-call-response) response:
```json
{
"PipelineData": {
"Value": {
"Int": {
"val": 5,
"span": {
"start": 100953,
"end": 100957
}
}
}
}
}
```
with the integer 5 along with preserving source span information that may be useful for error messages later.
When implementing a plugin in a non-Rust language like Python, you must manage this input and output serialization. Please refer to the [protocol documentation](plugin_protocol_reference.md) for more specific details on the protocol itself.
## Creating a plugin (in Python)
Using our knowledge from the previous section, we can also create plugins in other programming languages, although you will not benefit from the plugin interface libraries that ship with Nu. In this section, we'll write the same `len` plugin in Python.
First, let's look at the full plugin:
```python
#!/usr/bin/env python3
import json
import sys
def signature():
return {
"sig": {
"name": "len",
"description": "calculates the length of its input",
"extra_description": "",
"search_terms": [],
"required_positional": [],
"optional_positional": [],
"rest_positional": None,
"vectorizes_over_list": False,
"named": [],
"input_type": "String",
"output_type":"Int",
"input_output_types":[],
"allow_variants_without_examples": True,
"is_filter": False,
"creates_scope": False,
"allows_unknown_args":False,
"category":"Default"
},
"examples": []
}
def send_encoder():
sys.stdout.write(chr(4))
for ch in "json":
sys.stdout.write(chr(ord(ch)))
sys.stdout.flush()
def send_hello():
hello = {
"Hello": {
"protocol": "nu-plugin",
"version": "0.90.2",
"features": []
}
}
sys.stdout.writelines([json.dumps(hello)])
sys.stdout.flush()
def send_response(id, response):
msg = {
"CallResponse": [id, response]
}
sys.stdout.writelines([json.dumps(msg)])
sys.stdout.flush()
def send_error(id, error_msg, span):
error = {
"Error": {
"label": "Len Error",
"msg": error_msg,
"span": span,
}
}
send_response(id, error)
def handle_call(id, call_info):
try:
input = call_info["input"]["Value"]["String"]
output = {
"PipelineData": {
"Value": {
"Int": {
"val": len(input["val"]),
"span": input["span"]
}
}
}
}
send_response(id, output)
except:
send_error(
id,
"Could not process input",
call_info["call"]["head"]["span"]
)
if __name__ == "__main__":
send_encoder()
send_hello()
for line in sys.stdin:
input = json.loads(line)
if "Hello" in input:
pass
elif input == "Goodbye":
break
elif "Call" in input:
[id, call] = input["Call"]
if call == "Metadata":
send_response(id, {
"Metadata": {
"version": "0.1.0",
}
})
elif call == "Signature":
send_response(id, {"Signature": [signature()]})
elif "Run" in call:
handle_call(id, call["Run"])