From 3a8b64be17ce4976645acd41bc0d546622ac3ea6 Mon Sep 17 00:00:00 2001 From: janezd Date: Sat, 29 Jul 2017 22:15:15 +0200 Subject: [PATCH] OWPythonScript: Avoid meta class --- Orange/canvas/scheme/widgetsscheme.py | 6 +- Orange/widgets/data/owpythonscript.py | 82 +++++++++++++-------------- Orange/widgets/tests/base.py | 9 ++- 3 files changed, 50 insertions(+), 47 deletions(-) diff --git a/Orange/canvas/scheme/widgetsscheme.py b/Orange/canvas/scheme/widgetsscheme.py index 6c19e3a4277..b502b6c73ff 100644 --- a/Orange/canvas/scheme/widgetsscheme.py +++ b/Orange/canvas/scheme/widgetsscheme.py @@ -16,6 +16,7 @@ :bases: """ +import inspect import sys import logging import traceback @@ -819,7 +820,10 @@ def process_signals_for_widget(self, node, widget, signals): app.setOverrideCursor(Qt.WaitCursor) try: - handler(*args) + if "signal" in inspect.signature(handler).parameters: + handler(*args, signal=link.sink_channel) + else: + handler(*args) except Exception: sys.excepthook(*sys.exc_info()) log.exception("Error calling '%s' of '%s'", diff --git a/Orange/widgets/data/owpythonscript.py b/Orange/widgets/data/owpythonscript.py index 39248b81819..581ccf1f24d 100644 --- a/Orange/widgets/data/owpythonscript.py +++ b/Orange/widgets/data/owpythonscript.py @@ -1,3 +1,4 @@ +import inspect import sys import os import code @@ -351,57 +352,50 @@ def select_row(view, row): QItemSelectionModel.ClearAndSelect) -class OWPythonScriptMeta(type(widget.OWWidget)): - inputs = (("data", Table), ("learner", Learner), ("classifier", Model), - ("object", object)) - outputs = inputs - - # pylint: disable=no-self-argument - def __prepare__(cls_name, bases, **kwargs): - inputs = OWPythonScriptMeta.inputs - outputs = OWPythonScriptMeta.outputs - - inputs_cls = type( - "Inputs", (object, ), - {"in_" + name: Input("in_" + name, type_, - default=type_ is not object, multiple=True) - for name, type_ in inputs}) - - outputs_cls = type( - "Outputs", (object,), - {"out_" + name: Output("out_" + name, type_) - for name, type_ in outputs}) - - namespace = {"Inputs": inputs_cls, - "input_names": ["in_" + name for name, _ in inputs], - "Outputs": outputs_cls, - "output_names": ["out_" + name for name, _ in outputs]} - - for name, _ in inputs: - # `name` is needed to circumvent Python closure bug - def handler(self, obj, id=None, name=name): - id = id[0] - dic = getattr(self, "in_" + name) - if obj is None: - if id in dic.keys(): - del dic[id] - else: - dic[id] = obj - handler.__name__ = "set_" + name - decorator = getattr(inputs_cls, "in_" + name) - namespace.setdefault("set_" + name, decorator(handler)) - return namespace +def _create_signals(handler, inputs, outputs): + class Inputs: + pass + + class Outputs: + pass + + for name, type_ in inputs: + inp = Input("in_" + name, type_, + default=type_ is not object, multiple=True) + setattr(Inputs, "in_" + name, inp) + inp(handler) + + for name, type_ in outputs: + out = Output("out_" + name, type_) + setattr(Outputs, "out_" + name, out) + return Inputs, Outputs -# I've no idea why pylint gives warning about this meta class -# pylint: disable=invalid-metaclass -class OWPythonScript(widget.OWWidget, metaclass=OWPythonScriptMeta): + +class OWPythonScript(widget.OWWidget): name = "Python Script" description = "Write a Python script and run it on input data or models." icon = "icons/PythonScript.svg" priority = 3150 - # Signals are set up in the meta class + input_signals = (("data", Table), ("learner", Learner), + ("classifier", Model), ("object", object)) + input_names = ["in_" + name for name, _ in input_signals] + + output_signals = input_signals + output_names = ["out_" + name for name, _ in output_signals] + + def signal_handler(self, obj, id=None, signal=None): + id = id[0] + dic = getattr(self, signal) + if obj is None: + if id in dic.keys(): + del dic[id] + else: + dic[id] = obj + + Inputs, Outputs = _create_signals(signal_handler, + input_signals, output_signals) libraryListSource = \ Setting([Script("Hello world", "print('Hello world')\n")]) diff --git a/Orange/widgets/tests/base.py b/Orange/widgets/tests/base.py index 7f47f5101c1..521d5b5ecaf 100644 --- a/Orange/widgets/tests/base.py +++ b/Orange/widgets/tests/base.py @@ -2,6 +2,7 @@ import os import time import unittest +import inspect from unittest.mock import Mock import numpy as np @@ -196,7 +197,7 @@ def send_signal(self, input, value, *args, widget=None, wait=-1): Parameters ---------- - input_name : str + input : str value : Object id : int channel id, used for inputs with flag Multiple @@ -215,7 +216,11 @@ def send_signal(self, input, value, *args, widget=None, wait=-1): else: raise ValueError("'{}' is not an input name for widget {}" .format(input, type(widget).__name__)) - getattr(widget, input.handler)(value, *args) + handler = getattr(widget, input.handler) + if "signal" in inspect.signature(handler).parameters: + handler(value, *args, signal=input.name) + else: + handler(value, *args) widget.handleNewSignals() if wait >= 0 and widget.isBlocking(): spy = QSignalSpy(widget.blockingStateChanged)