2022-04-04 01:00:53 +00:00
|
|
|
# Example of using a Python script as a Nushell plugin
|
2021-12-18 15:52:27 +00:00
|
|
|
#
|
|
|
|
# The example uses JSON encoding but it should be a similar process using
|
2022-09-28 22:06:43 +00:00
|
|
|
# msgpack to move data between Nushell and the plugin. The only difference
|
|
|
|
# would be that you need to use msgpack relative lib(like msgpack) to
|
|
|
|
# decode and encode information that is read and written to stdin and stdout
|
2021-12-18 15:52:27 +00:00
|
|
|
#
|
|
|
|
# To register the plugin use:
|
2022-09-28 22:06:43 +00:00
|
|
|
# register <path-to-py-file>
|
2021-12-18 15:52:27 +00:00
|
|
|
#
|
Fix typos by codespell (#7600)
# Description
Found via `codespell -S target -L
crate,ser,numer,falsy,ro,te,nd,bu,ndoes,statics,ons,fo,rouge,pard`
# User-Facing Changes
None.
# Tests + Formatting
None and done.
# After Submitting
None.
2022-12-26 07:31:26 +00:00
|
|
|
# Be careful with the spans. Miette will crash if a span is outside the
|
2021-12-18 15:52:27 +00:00
|
|
|
# size of the contents vector. For this example we are using 0 and 1, which will
|
|
|
|
# point to the beginning of the contents vector. We strongly suggest using the span
|
|
|
|
# found in the plugin call head
|
|
|
|
#
|
2022-04-04 01:00:53 +00:00
|
|
|
# The plugin will be run using the active Python implementation. If you are in
|
|
|
|
# a Python environment, that is the Python version that is used
|
2021-12-18 15:52:27 +00:00
|
|
|
#
|
|
|
|
# Note: To keep the plugin simple and without dependencies, the dictionaries that
|
2022-04-04 01:00:53 +00:00
|
|
|
# represent the data transferred between Nushell and the plugin are kept as
|
|
|
|
# native Python dictionaries. The encoding and decoding process could be improved
|
2021-12-18 15:52:27 +00:00
|
|
|
# by using libraries like pydantic and marshmallow
|
|
|
|
#
|
2021-12-18 18:13:56 +00:00
|
|
|
# This plugin uses python3
|
2021-12-18 15:52:27 +00:00
|
|
|
# Note: To debug plugins write to stderr using sys.stderr.write
|
|
|
|
import sys
|
|
|
|
import json
|
|
|
|
|
|
|
|
|
|
|
|
def signatures():
|
|
|
|
"""
|
2022-04-04 01:00:53 +00:00
|
|
|
Multiple signatures can be sent to Nushell. Each signature will be registered
|
|
|
|
as a different plugin function in Nushell.
|
2021-12-18 15:52:27 +00:00
|
|
|
|
|
|
|
In your plugin logic you can use the name of the signature to indicate what
|
|
|
|
operation should be done with the plugin
|
|
|
|
"""
|
|
|
|
return {
|
|
|
|
"Signature": [
|
|
|
|
{
|
|
|
|
"name": "nu-python",
|
2022-04-04 01:00:53 +00:00
|
|
|
"usage": "Signature test for Python",
|
2021-12-18 15:52:27 +00:00
|
|
|
"extra_usage": "",
|
2022-08-06 03:57:31 +00:00
|
|
|
"input_type": "Any",
|
|
|
|
"output_type": "Any",
|
2021-12-18 15:52:27 +00:00
|
|
|
"required_positional": [
|
|
|
|
{
|
|
|
|
"name": "a",
|
|
|
|
"desc": "required integer value",
|
|
|
|
"shape": "Int",
|
|
|
|
"var_id": None,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"name": "b",
|
|
|
|
"desc": "required string value",
|
|
|
|
"shape": "String",
|
|
|
|
"var_id": None,
|
|
|
|
},
|
|
|
|
],
|
|
|
|
"optional_positional": [
|
|
|
|
{
|
|
|
|
"name": "opt",
|
|
|
|
"desc": "Optional number",
|
|
|
|
"shape": "Int",
|
|
|
|
"var_id": None,
|
|
|
|
}
|
|
|
|
],
|
|
|
|
"rest_positional": {
|
|
|
|
"name": "rest",
|
|
|
|
"desc": "rest value string",
|
|
|
|
"shape": "String",
|
|
|
|
"var_id": None,
|
|
|
|
},
|
2022-12-15 20:37:12 +00:00
|
|
|
"vectorizes_over_list": False,
|
2021-12-18 15:52:27 +00:00
|
|
|
"named": [
|
2021-12-18 19:25:17 +00:00
|
|
|
{
|
|
|
|
"long": "help",
|
|
|
|
"short": "h",
|
|
|
|
"arg": None,
|
|
|
|
"required": False,
|
2022-10-26 16:36:42 +00:00
|
|
|
"desc": "Display the help message for this command",
|
2021-12-18 19:25:17 +00:00
|
|
|
"var_id": None
|
|
|
|
},
|
2021-12-18 15:52:27 +00:00
|
|
|
{
|
|
|
|
"long": "flag",
|
|
|
|
"short": "f",
|
|
|
|
"arg": None,
|
|
|
|
"required": False,
|
|
|
|
"desc": "a flag for the signature",
|
|
|
|
"var_id": None,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"long": "named",
|
|
|
|
"short": "n",
|
|
|
|
"arg": "String",
|
|
|
|
"required": False,
|
|
|
|
"desc": "named string",
|
|
|
|
"var_id": None,
|
|
|
|
},
|
|
|
|
],
|
2022-12-15 20:37:12 +00:00
|
|
|
"input_output_types": [["Any", "Any"]],
|
|
|
|
"allow_variants_without_examples": True,
|
2022-04-04 01:00:53 +00:00
|
|
|
"search_terms": ["Python", "Example"],
|
2021-12-18 15:52:27 +00:00
|
|
|
"is_filter": False,
|
|
|
|
"creates_scope": False,
|
2022-12-25 00:31:44 +00:00
|
|
|
"allows_unknown_args": False,
|
2021-12-18 15:52:27 +00:00
|
|
|
"category": "Experimental",
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
def process_call(plugin_call):
|
|
|
|
"""
|
|
|
|
plugin_call is a dictionary with the information from the call
|
|
|
|
It should contain:
|
|
|
|
- The name of the call
|
|
|
|
- The call data which includes the positional and named values
|
2022-04-04 01:00:53 +00:00
|
|
|
- The input from the pipeline
|
2021-12-18 15:52:27 +00:00
|
|
|
|
|
|
|
Use this information to implement your plugin logic
|
|
|
|
"""
|
|
|
|
# Pretty printing the call to stderr
|
2021-12-19 10:00:31 +00:00
|
|
|
sys.stderr.write(json.dumps(plugin_call, indent=4))
|
2021-12-18 15:52:27 +00:00
|
|
|
sys.stderr.write("\n")
|
|
|
|
|
2022-04-04 01:00:53 +00:00
|
|
|
# Creates a Value of type List that will be encoded and sent to Nushell
|
2021-12-18 15:52:27 +00:00
|
|
|
return {
|
|
|
|
"Value": {
|
|
|
|
"List": {
|
|
|
|
"vals": [
|
|
|
|
{
|
|
|
|
"Record": {
|
|
|
|
"cols": ["one", "two", "three"],
|
|
|
|
"vals": [
|
|
|
|
{
|
|
|
|
"Int": {
|
|
|
|
"val": 0,
|
|
|
|
"span": {"start": 0, "end": 1},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Int": {
|
|
|
|
"val": 0,
|
|
|
|
"span": {"start": 0, "end": 1},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Int": {
|
|
|
|
"val": 0,
|
|
|
|
"span": {"start": 0, "end": 1},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
],
|
|
|
|
"span": {"start": 0, "end": 1},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Record": {
|
|
|
|
"cols": ["one", "two", "three"],
|
|
|
|
"vals": [
|
|
|
|
{
|
|
|
|
"Int": {
|
|
|
|
"val": 0,
|
|
|
|
"span": {"start": 0, "end": 1},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Int": {
|
|
|
|
"val": 1,
|
|
|
|
"span": {"start": 0, "end": 1},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Int": {
|
|
|
|
"val": 2,
|
|
|
|
"span": {"start": 0, "end": 1},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
],
|
|
|
|
"span": {"start": 0, "end": 1},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Record": {
|
|
|
|
"cols": ["one", "two", "three"],
|
|
|
|
"vals": [
|
|
|
|
{
|
|
|
|
"Int": {
|
|
|
|
"val": 0,
|
|
|
|
"span": {"start": 0, "end": 1},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Int": {
|
|
|
|
"val": 2,
|
|
|
|
"span": {"start": 0, "end": 1},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Int": {
|
|
|
|
"val": 4,
|
|
|
|
"span": {"start": 0, "end": 1},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
],
|
|
|
|
"span": {"start": 0, "end": 1},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Record": {
|
|
|
|
"cols": ["one", "two", "three"],
|
|
|
|
"vals": [
|
|
|
|
{
|
|
|
|
"Int": {
|
|
|
|
"val": 0,
|
|
|
|
"span": {"start": 0, "end": 1},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Int": {
|
|
|
|
"val": 3,
|
|
|
|
"span": {"start": 0, "end": 1},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Int": {
|
|
|
|
"val": 6,
|
|
|
|
"span": {"start": 0, "end": 1},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
],
|
|
|
|
"span": {"start": 0, "end": 1},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Record": {
|
|
|
|
"cols": ["one", "two", "three"],
|
|
|
|
"vals": [
|
|
|
|
{
|
|
|
|
"Int": {
|
|
|
|
"val": 0,
|
|
|
|
"span": {"start": 0, "end": 1},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Int": {
|
|
|
|
"val": 4,
|
|
|
|
"span": {"start": 0, "end": 1},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Int": {
|
|
|
|
"val": 8,
|
|
|
|
"span": {"start": 0, "end": 1},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
],
|
|
|
|
"span": {"start": 0, "end": 1},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Record": {
|
|
|
|
"cols": ["one", "two", "three"],
|
|
|
|
"vals": [
|
|
|
|
{
|
|
|
|
"Int": {
|
|
|
|
"val": 0,
|
|
|
|
"span": {"start": 0, "end": 1},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Int": {
|
|
|
|
"val": 5,
|
|
|
|
"span": {"start": 0, "end": 1},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Int": {
|
|
|
|
"val": 10,
|
|
|
|
"span": {"start": 0, "end": 1},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
],
|
|
|
|
"span": {"start": 0, "end": 1},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Record": {
|
|
|
|
"cols": ["one", "two", "three"],
|
|
|
|
"vals": [
|
|
|
|
{
|
|
|
|
"Int": {
|
|
|
|
"val": 0,
|
|
|
|
"span": {"start": 0, "end": 1},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Int": {
|
|
|
|
"val": 6,
|
|
|
|
"span": {"start": 0, "end": 1},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Int": {
|
|
|
|
"val": 12,
|
|
|
|
"span": {"start": 0, "end": 1},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
],
|
|
|
|
"span": {"start": 0, "end": 1},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Record": {
|
|
|
|
"cols": ["one", "two", "three"],
|
|
|
|
"vals": [
|
|
|
|
{
|
|
|
|
"Int": {
|
|
|
|
"val": 0,
|
|
|
|
"span": {"start": 0, "end": 1},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Int": {
|
|
|
|
"val": 7,
|
|
|
|
"span": {"start": 0, "end": 1},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Int": {
|
|
|
|
"val": 14,
|
|
|
|
"span": {"start": 0, "end": 1},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
],
|
|
|
|
"span": {"start": 0, "end": 1},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Record": {
|
|
|
|
"cols": ["one", "two", "three"],
|
|
|
|
"vals": [
|
|
|
|
{
|
|
|
|
"Int": {
|
|
|
|
"val": 0,
|
|
|
|
"span": {"start": 0, "end": 1},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Int": {
|
|
|
|
"val": 8,
|
|
|
|
"span": {"start": 0, "end": 1},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Int": {
|
|
|
|
"val": 16,
|
|
|
|
"span": {"start": 0, "end": 1},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
],
|
|
|
|
"span": {"start": 0, "end": 1},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Record": {
|
|
|
|
"cols": ["one", "two", "three"],
|
|
|
|
"vals": [
|
|
|
|
{
|
|
|
|
"Int": {
|
|
|
|
"val": 0,
|
|
|
|
"span": {"start": 0, "end": 1},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Int": {
|
|
|
|
"val": 9,
|
|
|
|
"span": {"start": 0, "end": 1},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Int": {
|
|
|
|
"val": 18,
|
|
|
|
"span": {"start": 0, "end": 1},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
],
|
|
|
|
"span": {"start": 0, "end": 1},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
],
|
|
|
|
"span": {"start": 0, "end": 1},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-09-28 22:06:43 +00:00
|
|
|
def tell_nushell_encoding():
|
|
|
|
sys.stdout.write(chr(4))
|
|
|
|
for ch in "json":
|
|
|
|
sys.stdout.write(chr(ord(ch)))
|
|
|
|
sys.stdout.flush()
|
|
|
|
|
|
|
|
|
2021-12-18 15:52:27 +00:00
|
|
|
def plugin():
|
2022-09-28 22:06:43 +00:00
|
|
|
tell_nushell_encoding()
|
2021-12-18 15:52:27 +00:00
|
|
|
call_str = ",".join(sys.stdin.readlines())
|
|
|
|
plugin_call = json.loads(call_str)
|
|
|
|
|
|
|
|
if plugin_call == "Signature":
|
|
|
|
signature = json.dumps(signatures())
|
|
|
|
sys.stdout.write(signature)
|
|
|
|
|
|
|
|
elif "CallInfo" in plugin_call:
|
|
|
|
response = process_call(plugin_call)
|
|
|
|
sys.stdout.write(json.dumps(response))
|
|
|
|
|
|
|
|
else:
|
2022-04-04 01:00:53 +00:00
|
|
|
# Use this error format if you want to return an error back to Nushell
|
2021-12-18 15:52:27 +00:00
|
|
|
error = {
|
|
|
|
"Error": {
|
|
|
|
"label": "ERROR from plugin",
|
|
|
|
"msg": "error message pointing to call head span",
|
|
|
|
"span": {"start": 0, "end": 1},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sys.stdout.write(json.dumps(error))
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
plugin()
|