canvas 0.4.0__py3-none-any.whl → 0.5.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of canvas might be problematic. Click here for more details.
- {canvas-0.4.0.dist-info → canvas-0.5.0.dist-info}/METADATA +3 -2
- {canvas-0.4.0.dist-info → canvas-0.5.0.dist-info}/RECORD +49 -12
- canvas_cli/templates/plugins/default/{{ cookiecutter.__project_slug }}/protocols/my_protocol.py +2 -6
- canvas_generated/messages/events_pb2.py +2 -2
- canvas_generated/messages/events_pb2.pyi +108 -0
- canvas_sdk/effects/patient_chart_summary_configuration.py +1 -0
- canvas_sdk/v1/data/detected_issue.py +52 -0
- canvas_sdk/v1/data/protocol_override.py +58 -0
- canvas_sdk/value_set/__init__.py +0 -0
- canvas_sdk/value_set/v2022/__init__.py +0 -0
- plugin_runner/authentication.py +3 -7
- plugin_runner/plugin_runner.py +48 -26
- plugin_runner/sandbox.py +22 -8
- plugin_runner/tests/__init__.py +0 -0
- plugin_runner/tests/data/plugins/.gitkeep +0 -0
- plugin_runner/tests/fixtures/plugins/example_plugin/CANVAS_MANIFEST.json +29 -0
- plugin_runner/tests/fixtures/plugins/example_plugin/README.md +12 -0
- plugin_runner/tests/fixtures/plugins/example_plugin/__init__.py +0 -0
- plugin_runner/tests/fixtures/plugins/example_plugin/protocols/__init__.py +0 -0
- plugin_runner/tests/fixtures/plugins/example_plugin/protocols/my_protocol.py +18 -0
- plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v1/CANVAS_MANIFEST.json +29 -0
- plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v1/README.md +12 -0
- plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v1/other_module/__init__.py +0 -0
- plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v1/other_module/base.py +3 -0
- plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v1/protocols/__init__.py +0 -0
- plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v1/protocols/my_protocol.py +18 -0
- plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v2/CANVAS_MANIFEST.json +29 -0
- plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v2/README.md +12 -0
- plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v2/other_module/__init__.py +0 -0
- plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v2/other_module/base.py +6 -0
- plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v2/protocols/__init__.py +0 -0
- plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v2/protocols/my_protocol.py +18 -0
- plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v3/CANVAS_MANIFEST.json +29 -0
- plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v3/README.md +12 -0
- plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v3/other_module/__init__.py +0 -0
- plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v3/other_module/base.py +8 -0
- plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v3/protocols/__init__.py +0 -0
- plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v3/protocols/my_protocol.py +18 -0
- plugin_runner/tests/fixtures/plugins/test_module_imports_plugin/CANVAS_MANIFEST.json +29 -0
- plugin_runner/tests/fixtures/plugins/test_module_imports_plugin/README.md +12 -0
- plugin_runner/tests/fixtures/plugins/test_module_imports_plugin/other_module/__init__.py +0 -0
- plugin_runner/tests/fixtures/plugins/test_module_imports_plugin/other_module/base.py +3 -0
- plugin_runner/tests/fixtures/plugins/test_module_imports_plugin/protocols/__init__.py +0 -0
- plugin_runner/tests/fixtures/plugins/test_module_imports_plugin/protocols/my_protocol.py +18 -0
- plugin_runner/tests/test_plugin_runner.py +208 -0
- plugin_runner/tests/test_sandbox.py +113 -0
- settings.py +23 -0
- {canvas-0.4.0.dist-info → canvas-0.5.0.dist-info}/WHEEL +0 -0
- {canvas-0.4.0.dist-info → canvas-0.5.0.dist-info}/entry_points.txt +0 -0
plugin_runner/plugin_runner.py
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
-
import importlib.util
|
|
3
2
|
import json
|
|
4
3
|
import os
|
|
5
4
|
import pathlib
|
|
5
|
+
import pkgutil
|
|
6
6
|
import signal
|
|
7
7
|
import sys
|
|
8
8
|
import time
|
|
9
9
|
import traceback
|
|
10
10
|
from collections import defaultdict
|
|
11
11
|
from types import FrameType
|
|
12
|
-
from typing import Any, AsyncGenerator, Optional, TypedDict
|
|
12
|
+
from typing import Any, AsyncGenerator, Optional, TypedDict
|
|
13
13
|
|
|
14
14
|
import grpc
|
|
15
15
|
import statsd
|
|
@@ -30,17 +30,7 @@ from logger import log
|
|
|
30
30
|
from plugin_runner.authentication import token_for_plugin
|
|
31
31
|
from plugin_runner.plugin_synchronizer import publish_message
|
|
32
32
|
from plugin_runner.sandbox import Sandbox
|
|
33
|
-
|
|
34
|
-
ENV = os.getenv("ENV", "development")
|
|
35
|
-
|
|
36
|
-
IS_PRODUCTION = ENV == "production"
|
|
37
|
-
|
|
38
|
-
MANIFEST_FILE_NAME = "CANVAS_MANIFEST.json"
|
|
39
|
-
|
|
40
|
-
SECRETS_FILE_NAME = "SECRETS.json"
|
|
41
|
-
|
|
42
|
-
# specify a local plugin directory for development
|
|
43
|
-
PLUGIN_DIRECTORY = "/plugin-runner/custom-plugins" if IS_PRODUCTION else "./custom-plugins"
|
|
33
|
+
from settings import MANIFEST_FILE_NAME, PLUGIN_DIRECTORY, SECRETS_FILE_NAME
|
|
44
34
|
|
|
45
35
|
# when we import plugins we'll use the module name directly so we need to add the plugin
|
|
46
36
|
# directory to the path
|
|
@@ -51,7 +41,7 @@ sys.path.append(PLUGIN_DIRECTORY)
|
|
|
51
41
|
LOADED_PLUGINS: dict = {}
|
|
52
42
|
|
|
53
43
|
# a global dictionary of events to protocol class names
|
|
54
|
-
EVENT_PROTOCOL_MAP: dict =
|
|
44
|
+
EVENT_PROTOCOL_MAP: dict[str, list] = defaultdict(list)
|
|
55
45
|
|
|
56
46
|
|
|
57
47
|
class DataAccess(TypedDict):
|
|
@@ -113,7 +103,7 @@ class PluginRunner(PluginRunnerServicer):
|
|
|
113
103
|
event_start_time = time.time()
|
|
114
104
|
event_type = request.type
|
|
115
105
|
event_name = EventType.Name(event_type)
|
|
116
|
-
relevant_plugins = EVENT_PROTOCOL_MAP
|
|
106
|
+
relevant_plugins = EVENT_PROTOCOL_MAP[event_name]
|
|
117
107
|
|
|
118
108
|
if event_type in [EventType.PLUGIN_CREATED, EventType.PLUGIN_UPDATED]:
|
|
119
109
|
plugin_name = request.target
|
|
@@ -197,18 +187,50 @@ def handle_hup_cb(_signum: int, _frame: Optional[FrameType]) -> None:
|
|
|
197
187
|
load_plugins()
|
|
198
188
|
|
|
199
189
|
|
|
200
|
-
def
|
|
190
|
+
def find_modules(base_path: pathlib.Path, prefix: str | None = None) -> list[str]:
|
|
191
|
+
"""Find all modules in the specified package path."""
|
|
192
|
+
modules: list[str] = []
|
|
193
|
+
|
|
194
|
+
for file_finder, module_name, is_pkg in pkgutil.iter_modules(
|
|
195
|
+
[base_path.as_posix()],
|
|
196
|
+
):
|
|
197
|
+
if is_pkg:
|
|
198
|
+
modules = modules + find_modules(
|
|
199
|
+
base_path / module_name,
|
|
200
|
+
prefix=f"{prefix}.{module_name}" if prefix else module_name,
|
|
201
|
+
)
|
|
202
|
+
else:
|
|
203
|
+
modules.append(f"{prefix}.{module_name}" if prefix else module_name)
|
|
204
|
+
|
|
205
|
+
return modules
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def sandbox_from_package(package_path: pathlib.Path) -> dict[str, Any]:
|
|
209
|
+
"""Sandbox the code execution."""
|
|
210
|
+
package_name = package_path.name
|
|
211
|
+
available_modules = find_modules(package_path)
|
|
212
|
+
sandboxes = {}
|
|
213
|
+
|
|
214
|
+
for module_name in available_modules:
|
|
215
|
+
result = sandbox_from_module(package_path, module_name)
|
|
216
|
+
full_module_name = f"{package_name}.{module_name}"
|
|
217
|
+
sandboxes[full_module_name] = result
|
|
218
|
+
|
|
219
|
+
return sandboxes
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def sandbox_from_module(package_path: pathlib.Path, module_name: str) -> Any:
|
|
201
223
|
"""Sandbox the code execution."""
|
|
202
|
-
|
|
224
|
+
module_path = package_path / str(module_name.replace(".", "/") + ".py")
|
|
203
225
|
|
|
204
|
-
if not
|
|
205
|
-
raise
|
|
226
|
+
if not module_path.exists():
|
|
227
|
+
raise ModuleNotFoundError(f'Could not load module "{module_name}"')
|
|
206
228
|
|
|
207
|
-
|
|
208
|
-
source_code = origin.read_text()
|
|
229
|
+
source_code = module_path.read_text()
|
|
209
230
|
|
|
210
|
-
|
|
231
|
+
full_module_name = f"{package_path.name}.{module_name}"
|
|
211
232
|
|
|
233
|
+
sandbox = Sandbox(source_code, module_name=full_module_name)
|
|
212
234
|
return sandbox.execute()
|
|
213
235
|
|
|
214
236
|
|
|
@@ -240,6 +262,7 @@ def load_or_reload_plugin(path: pathlib.Path) -> None:
|
|
|
240
262
|
# TODO add existing schema validation from Michela here
|
|
241
263
|
try:
|
|
242
264
|
protocols = manifest_json["components"]["protocols"]
|
|
265
|
+
results = sandbox_from_package(path)
|
|
243
266
|
except Exception as e:
|
|
244
267
|
log.error(f'Unable to load plugin "{name}": {str(e)}')
|
|
245
268
|
return
|
|
@@ -258,7 +281,7 @@ def load_or_reload_plugin(path: pathlib.Path) -> None:
|
|
|
258
281
|
if name_and_class in LOADED_PLUGINS:
|
|
259
282
|
log.info(f"Reloading plugin '{name_and_class}'")
|
|
260
283
|
|
|
261
|
-
result =
|
|
284
|
+
result = results[protocol_module]
|
|
262
285
|
|
|
263
286
|
LOADED_PLUGINS[name_and_class]["active"] = True
|
|
264
287
|
|
|
@@ -268,7 +291,7 @@ def load_or_reload_plugin(path: pathlib.Path) -> None:
|
|
|
268
291
|
else:
|
|
269
292
|
log.info(f"Loading plugin '{name_and_class}'")
|
|
270
293
|
|
|
271
|
-
result =
|
|
294
|
+
result = results[protocol_module]
|
|
272
295
|
|
|
273
296
|
LOADED_PLUGINS[name_and_class] = {
|
|
274
297
|
"active": True,
|
|
@@ -285,8 +308,7 @@ def load_or_reload_plugin(path: pathlib.Path) -> None:
|
|
|
285
308
|
|
|
286
309
|
def refresh_event_type_map() -> None:
|
|
287
310
|
"""Ensure the event subscriptions are up to date."""
|
|
288
|
-
|
|
289
|
-
EVENT_PROTOCOL_MAP = defaultdict(list)
|
|
311
|
+
EVENT_PROTOCOL_MAP.clear()
|
|
290
312
|
|
|
291
313
|
for name, plugin in LOADED_PLUGINS.items():
|
|
292
314
|
if hasattr(plugin["class"], "RESPONDS_TO"):
|
plugin_runner/sandbox.py
CHANGED
|
@@ -75,12 +75,6 @@ def _is_known_module(name: str) -> bool:
|
|
|
75
75
|
return any(name.startswith(m) for m in ALLOWED_MODULES)
|
|
76
76
|
|
|
77
77
|
|
|
78
|
-
def _safe_import(name: str, *args: Any, **kwargs: Any) -> Any:
|
|
79
|
-
if not _is_known_module(name):
|
|
80
|
-
raise ImportError(f"{name!r} is not an allowed import.")
|
|
81
|
-
return __import__(name, *args, **kwargs)
|
|
82
|
-
|
|
83
|
-
|
|
84
78
|
def _unrestricted(_ob: Any, *args: Any, **kwargs: Any) -> Any:
|
|
85
79
|
"""Return the given object, unmodified."""
|
|
86
80
|
return _ob
|
|
@@ -96,6 +90,7 @@ class Sandbox:
|
|
|
96
90
|
|
|
97
91
|
source_code: str
|
|
98
92
|
namespace: str
|
|
93
|
+
module_name: str | None
|
|
99
94
|
|
|
100
95
|
class Transformer(RestrictingNodeTransformer):
|
|
101
96
|
"""A node transformer for customizing the sandbox compiler."""
|
|
@@ -204,12 +199,20 @@ class Sandbox:
|
|
|
204
199
|
# Impossible Case only ctx Load, Store and Del are defined in ast.
|
|
205
200
|
raise NotImplementedError(f"Unknown ctx type: {type(node.ctx)}")
|
|
206
201
|
|
|
207
|
-
def __init__(
|
|
202
|
+
def __init__(
|
|
203
|
+
self, source_code: str, namespace: str | None = None, module_name: str | None = None
|
|
204
|
+
) -> None:
|
|
208
205
|
if source_code is None:
|
|
209
206
|
raise TypeError("source_code may not be None")
|
|
207
|
+
self.module_name = module_name
|
|
210
208
|
self.namespace = namespace or "protocols"
|
|
211
209
|
self.source_code = source_code
|
|
212
210
|
|
|
211
|
+
@cached_property
|
|
212
|
+
def package_name(self) -> str | None:
|
|
213
|
+
"""Return the root package name."""
|
|
214
|
+
return self.module_name.split(".")[0] if self.module_name else None
|
|
215
|
+
|
|
213
216
|
@cached_property
|
|
214
217
|
def scope(self) -> dict[str, Any]:
|
|
215
218
|
"""Return the scope used for evaluation."""
|
|
@@ -217,7 +220,7 @@ class Sandbox:
|
|
|
217
220
|
"__builtins__": {
|
|
218
221
|
**safe_builtins.copy(),
|
|
219
222
|
**utility_builtins.copy(),
|
|
220
|
-
"__import__": _safe_import,
|
|
223
|
+
"__import__": self._safe_import,
|
|
221
224
|
"classmethod": builtins.classmethod,
|
|
222
225
|
"staticmethod": builtins.staticmethod,
|
|
223
226
|
"any": builtins.any,
|
|
@@ -263,6 +266,17 @@ class Sandbox:
|
|
|
263
266
|
"""Return warnings encountered when compiling the source code."""
|
|
264
267
|
return cast(tuple[str, ...], self.compile_result.warnings)
|
|
265
268
|
|
|
269
|
+
def _is_known_module(self, name: str) -> bool:
|
|
270
|
+
return bool(
|
|
271
|
+
_is_known_module(name)
|
|
272
|
+
or (self.package_name and name.split(".")[0] == self.package_name)
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
def _safe_import(self, name: str, *args: Any, **kwargs: Any) -> Any:
|
|
276
|
+
if not (self._is_known_module(name)):
|
|
277
|
+
raise ImportError(f"{name!r} is not an allowed import.")
|
|
278
|
+
return __import__(name, *args, **kwargs)
|
|
279
|
+
|
|
266
280
|
def execute(self) -> dict:
|
|
267
281
|
"""Execute the given code in a restricted sandbox."""
|
|
268
282
|
if self.errors:
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"sdk_version": "0.1.4",
|
|
3
|
+
"plugin_version": "0.0.1",
|
|
4
|
+
"name": "example_plugin",
|
|
5
|
+
"description": "Edit the description in CANVAS_MANIFEST.json",
|
|
6
|
+
"components": {
|
|
7
|
+
"protocols": [
|
|
8
|
+
{
|
|
9
|
+
"class": "example_plugin.protocols.my_protocol:Protocol",
|
|
10
|
+
"description": "A protocol that does xyz...",
|
|
11
|
+
"data_access": {
|
|
12
|
+
"event": "",
|
|
13
|
+
"read": [],
|
|
14
|
+
"write": []
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
],
|
|
18
|
+
"commands": [],
|
|
19
|
+
"content": [],
|
|
20
|
+
"effects": [],
|
|
21
|
+
"views": []
|
|
22
|
+
},
|
|
23
|
+
"secrets": [],
|
|
24
|
+
"tags": {},
|
|
25
|
+
"references": [],
|
|
26
|
+
"license": "",
|
|
27
|
+
"diagram": false,
|
|
28
|
+
"readme": "./README.md"
|
|
29
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
==============
|
|
2
|
+
example_plugin
|
|
3
|
+
==============
|
|
4
|
+
|
|
5
|
+
## Description
|
|
6
|
+
|
|
7
|
+
A description of this plugin
|
|
8
|
+
|
|
9
|
+
### Important Note!
|
|
10
|
+
|
|
11
|
+
The CANVAS_MANIFEST.json is used when installing your plugin. Please ensure it
|
|
12
|
+
gets updated if you add, remove, or rename protocols.
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from canvas_sdk.effects import Effect, EffectType
|
|
2
|
+
from canvas_sdk.events import EventType
|
|
3
|
+
from canvas_sdk.protocols import BaseProtocol
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Protocol(BaseProtocol):
|
|
7
|
+
"""
|
|
8
|
+
You should put a helpful description of this protocol's behavior here.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
# Name the event type you wish to run in response to
|
|
12
|
+
RESPONDS_TO = EventType.Name(EventType.UNKNOWN)
|
|
13
|
+
|
|
14
|
+
NARRATIVE_STRING = "I was inserted from my plugin's protocol."
|
|
15
|
+
|
|
16
|
+
def compute(self) -> list[Effect]:
|
|
17
|
+
"""This method gets called when an event of the type RESPONDS_TO is fired."""
|
|
18
|
+
return [Effect(type=EffectType.LOG, payload="Hello, world!")]
|
plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v1/CANVAS_MANIFEST.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"sdk_version": "0.1.4",
|
|
3
|
+
"plugin_version": "0.0.1",
|
|
4
|
+
"name": "test_module_imports_outside_plugin_v1",
|
|
5
|
+
"description": "Edit the description in CANVAS_MANIFEST.json",
|
|
6
|
+
"components": {
|
|
7
|
+
"protocols": [
|
|
8
|
+
{
|
|
9
|
+
"class": "test_module_imports_outside_plugin_v1.protocols.my_protocol:Protocol",
|
|
10
|
+
"description": "A protocol that does xyz...",
|
|
11
|
+
"data_access": {
|
|
12
|
+
"event": "",
|
|
13
|
+
"read": [],
|
|
14
|
+
"write": []
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
],
|
|
18
|
+
"commands": [],
|
|
19
|
+
"content": [],
|
|
20
|
+
"effects": [],
|
|
21
|
+
"views": []
|
|
22
|
+
},
|
|
23
|
+
"secrets": [],
|
|
24
|
+
"tags": {},
|
|
25
|
+
"references": [],
|
|
26
|
+
"license": "",
|
|
27
|
+
"diagram": false,
|
|
28
|
+
"readme": "./README.md"
|
|
29
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
==========================
|
|
2
|
+
test_module_imports_outside_plugin_v1
|
|
3
|
+
==========================
|
|
4
|
+
|
|
5
|
+
## Description
|
|
6
|
+
|
|
7
|
+
A description of this plugin
|
|
8
|
+
|
|
9
|
+
### Important Note!
|
|
10
|
+
|
|
11
|
+
The CANVAS_MANIFEST.json is used when installing your plugin. Please ensure it
|
|
12
|
+
gets updated if you add, remove, or rename protocols.
|
plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v1/other_module/__init__.py
ADDED
|
File without changes
|
plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v1/protocols/__init__.py
ADDED
|
File without changes
|
plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v1/protocols/my_protocol.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from test_module_imports_plugin.other_module.base import import_me
|
|
2
|
+
|
|
3
|
+
from canvas_sdk.effects import Effect, EffectType
|
|
4
|
+
from canvas_sdk.events import EventType
|
|
5
|
+
from canvas_sdk.protocols import BaseProtocol
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Protocol(BaseProtocol):
|
|
9
|
+
"""
|
|
10
|
+
You should put a helpful description of this protocol's behavior here.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
# Name the event type you wish to run in response to
|
|
14
|
+
RESPONDS_TO = EventType.Name(EventType.UNKNOWN)
|
|
15
|
+
|
|
16
|
+
def compute(self) -> list[Effect]:
|
|
17
|
+
"""This method gets called when an event of the type RESPONDS_TO is fired."""
|
|
18
|
+
return [Effect(type=EffectType.LOG, payload=import_me())]
|
plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v2/CANVAS_MANIFEST.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"sdk_version": "0.1.4",
|
|
3
|
+
"plugin_version": "0.0.1",
|
|
4
|
+
"name": "test_module_imports_outside_plugin_v2",
|
|
5
|
+
"description": "Edit the description in CANVAS_MANIFEST.json",
|
|
6
|
+
"components": {
|
|
7
|
+
"protocols": [
|
|
8
|
+
{
|
|
9
|
+
"class": "test_module_imports_outside_plugin_v2.protocols.my_protocol:Protocol",
|
|
10
|
+
"description": "A protocol that does xyz...",
|
|
11
|
+
"data_access": {
|
|
12
|
+
"event": "",
|
|
13
|
+
"read": [],
|
|
14
|
+
"write": []
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
],
|
|
18
|
+
"commands": [],
|
|
19
|
+
"content": [],
|
|
20
|
+
"effects": [],
|
|
21
|
+
"views": []
|
|
22
|
+
},
|
|
23
|
+
"secrets": [],
|
|
24
|
+
"tags": {},
|
|
25
|
+
"references": [],
|
|
26
|
+
"license": "",
|
|
27
|
+
"diagram": false,
|
|
28
|
+
"readme": "./README.md"
|
|
29
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
==========================
|
|
2
|
+
test_module_imports_outside_plugin_v2
|
|
3
|
+
==========================
|
|
4
|
+
|
|
5
|
+
## Description
|
|
6
|
+
|
|
7
|
+
A description of this plugin
|
|
8
|
+
|
|
9
|
+
### Important Note!
|
|
10
|
+
|
|
11
|
+
The CANVAS_MANIFEST.json is used when installing your plugin. Please ensure it
|
|
12
|
+
gets updated if you add, remove, or rename protocols.
|
plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v2/other_module/__init__.py
ADDED
|
File without changes
|
plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v2/protocols/__init__.py
ADDED
|
File without changes
|
plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v2/protocols/my_protocol.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from test_module_imports_outside_plugin_v2.other_module.base import import_me
|
|
2
|
+
|
|
3
|
+
from canvas_sdk.effects import Effect, EffectType
|
|
4
|
+
from canvas_sdk.events import EventType
|
|
5
|
+
from canvas_sdk.protocols import BaseProtocol
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Protocol(BaseProtocol):
|
|
9
|
+
"""
|
|
10
|
+
You should put a helpful description of this protocol's behavior here.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
# Name the event type you wish to run in response to
|
|
14
|
+
RESPONDS_TO = EventType.Name(EventType.UNKNOWN)
|
|
15
|
+
|
|
16
|
+
def compute(self) -> list[Effect]:
|
|
17
|
+
"""This method gets called when an event of the type RESPONDS_TO is fired."""
|
|
18
|
+
return [Effect(type=EffectType.LOG, payload=import_me())]
|
plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v3/CANVAS_MANIFEST.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"sdk_version": "0.1.4",
|
|
3
|
+
"plugin_version": "0.0.1",
|
|
4
|
+
"name": "test_module_imports_outside_plugin_v3",
|
|
5
|
+
"description": "Edit the description in CANVAS_MANIFEST.json",
|
|
6
|
+
"components": {
|
|
7
|
+
"protocols": [
|
|
8
|
+
{
|
|
9
|
+
"class": "test_module_imports_outside_plugin_v3.protocols.my_protocol:Protocol",
|
|
10
|
+
"description": "A protocol that does xyz...",
|
|
11
|
+
"data_access": {
|
|
12
|
+
"event": "",
|
|
13
|
+
"read": [],
|
|
14
|
+
"write": []
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
],
|
|
18
|
+
"commands": [],
|
|
19
|
+
"content": [],
|
|
20
|
+
"effects": [],
|
|
21
|
+
"views": []
|
|
22
|
+
},
|
|
23
|
+
"secrets": [],
|
|
24
|
+
"tags": {},
|
|
25
|
+
"references": [],
|
|
26
|
+
"license": "",
|
|
27
|
+
"diagram": false,
|
|
28
|
+
"readme": "./README.md"
|
|
29
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
==========================
|
|
2
|
+
test_module_imports_outside_plugin_v3
|
|
3
|
+
==========================
|
|
4
|
+
|
|
5
|
+
## Description
|
|
6
|
+
|
|
7
|
+
A description of this plugin
|
|
8
|
+
|
|
9
|
+
### Important Note!
|
|
10
|
+
|
|
11
|
+
The CANVAS_MANIFEST.json is used when installing your plugin. Please ensure it
|
|
12
|
+
gets updated if you add, remove, or rename protocols.
|
plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v3/other_module/__init__.py
ADDED
|
File without changes
|
plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v3/protocols/__init__.py
ADDED
|
File without changes
|
plugin_runner/tests/fixtures/plugins/test_module_imports_outside_plugin_v3/protocols/my_protocol.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from test_module_imports_outside_plugin_v3.other_module.base import import_me
|
|
2
|
+
|
|
3
|
+
from canvas_sdk.effects import Effect, EffectType
|
|
4
|
+
from canvas_sdk.events import EventType
|
|
5
|
+
from canvas_sdk.protocols import BaseProtocol
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Protocol(BaseProtocol):
|
|
9
|
+
"""
|
|
10
|
+
You should put a helpful description of this protocol's behavior here.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
# Name the event type you wish to run in response to
|
|
14
|
+
RESPONDS_TO = EventType.Name(EventType.UNKNOWN)
|
|
15
|
+
|
|
16
|
+
def compute(self) -> list[Effect]:
|
|
17
|
+
"""This method gets called when an event of the type RESPONDS_TO is fired."""
|
|
18
|
+
return [Effect(type=EffectType.LOG, payload=import_me())]
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"sdk_version": "0.1.4",
|
|
3
|
+
"plugin_version": "0.0.1",
|
|
4
|
+
"name": "test_module_imports_plugin",
|
|
5
|
+
"description": "Edit the description in CANVAS_MANIFEST.json",
|
|
6
|
+
"components": {
|
|
7
|
+
"protocols": [
|
|
8
|
+
{
|
|
9
|
+
"class": "test_module_imports_plugin.protocols.my_protocol:Protocol",
|
|
10
|
+
"description": "A protocol that does xyz...",
|
|
11
|
+
"data_access": {
|
|
12
|
+
"event": "",
|
|
13
|
+
"read": [],
|
|
14
|
+
"write": []
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
],
|
|
18
|
+
"commands": [],
|
|
19
|
+
"content": [],
|
|
20
|
+
"effects": [],
|
|
21
|
+
"views": []
|
|
22
|
+
},
|
|
23
|
+
"secrets": [],
|
|
24
|
+
"tags": {},
|
|
25
|
+
"references": [],
|
|
26
|
+
"license": "",
|
|
27
|
+
"diagram": false,
|
|
28
|
+
"readme": "./README.md"
|
|
29
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
==========================
|
|
2
|
+
test_module_imports_plugin
|
|
3
|
+
==========================
|
|
4
|
+
|
|
5
|
+
## Description
|
|
6
|
+
|
|
7
|
+
A description of this plugin
|
|
8
|
+
|
|
9
|
+
### Important Note!
|
|
10
|
+
|
|
11
|
+
The CANVAS_MANIFEST.json is used when installing your plugin. Please ensure it
|
|
12
|
+
gets updated if you add, remove, or rename protocols.
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from test_module_imports_plugin.other_module.base import import_me
|
|
2
|
+
|
|
3
|
+
from canvas_sdk.effects import Effect, EffectType
|
|
4
|
+
from canvas_sdk.events import EventType
|
|
5
|
+
from canvas_sdk.protocols import BaseProtocol
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Protocol(BaseProtocol):
|
|
9
|
+
"""
|
|
10
|
+
You should put a helpful description of this protocol's behavior here.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
# Name the event type you wish to run in response to
|
|
14
|
+
RESPONDS_TO = EventType.Name(EventType.UNKNOWN)
|
|
15
|
+
|
|
16
|
+
def compute(self) -> list[Effect]:
|
|
17
|
+
"""This method gets called when an event of the type RESPONDS_TO is fired."""
|
|
18
|
+
return [Effect(type=EffectType.LOG, payload=import_me())]
|