Skip to content

Commit

Permalink
OWPythonScript: Avoid meta class
Browse files Browse the repository at this point in the history
  • Loading branch information
janezd committed Jul 29, 2017
1 parent f0d4a88 commit 3a8b64b
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 47 deletions.
6 changes: 5 additions & 1 deletion Orange/canvas/scheme/widgetsscheme.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
:bases:
"""
import inspect
import sys
import logging
import traceback
Expand Down Expand Up @@ -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'",
Expand Down
82 changes: 38 additions & 44 deletions Orange/widgets/data/owpythonscript.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import inspect
import sys
import os
import code
Expand Down Expand Up @@ -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")])
Expand Down
9 changes: 7 additions & 2 deletions Orange/widgets/tests/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import os
import time
import unittest
import inspect
from unittest.mock import Mock

import numpy as np
Expand Down Expand Up @@ -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
Expand All @@ -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)
Expand Down

0 comments on commit 3a8b64b

Please sign in to comment.