Skip to content

Commit d7d230c

Browse files
authored
Merge pull request #4974 from ales-erjavec/test-cleanup
[FIX] Tests: PyQt5 5.15 compatible cleanup
2 parents 1277566 + 3649edf commit d7d230c

File tree

5 files changed

+67
-34
lines changed

5 files changed

+67
-34
lines changed

Orange/widgets/data/owpythonscript.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
import keyword
55
import itertools
66
import unicodedata
7+
import weakref
78
from functools import reduce
8-
from collections import defaultdict
99
from unittest.mock import patch
1010

1111
from typing import Optional, List, TYPE_CHECKING
@@ -437,7 +437,7 @@ class Outputs:
437437
# anyway. If this causes any problems in the future, replace this with
438438
# shared_namespaces = {} and thus use a common namespace for all instances
439439
# of # PythonScript even if they are in different schemata.
440-
shared_namespaces = defaultdict(dict)
440+
shared_namespaces = weakref.WeakKeyDictionary()
441441

442442
class Error(OWWidget.Error):
443443
pass
@@ -727,7 +727,7 @@ def saveScript(self):
727727
f.close()
728728

729729
def initial_locals_state(self):
730-
d = self.shared_namespaces[self.signalManager].copy()
730+
d = self.shared_namespaces.setdefault(self.signalManager, {}).copy()
731731
for name in self.signal_names:
732732
value = getattr(self, name)
733733
all_values = list(value.values())
@@ -740,7 +740,7 @@ def update_namespace(self, namespace):
740740
not_saved = reduce(set.union,
741741
({f"in_{name}s", f"in_{name}", f"out_{name}"}
742742
for name in self.signal_names))
743-
self.shared_namespaces[self.signalManager].update(
743+
self.shared_namespaces.setdefault(self.signalManager, {}).update(
744744
{name: value for name, value in namespace.items()
745745
if name not in not_saved})
746746

Orange/widgets/data/tests/test_owpythonscript.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ def setUp(self):
2020
self.learner = LogisticRegressionLearner()
2121
self.model = self.learner(self.iris)
2222

23+
def tearDown(self):
24+
# clear sys.last_*, these are set/used by interactive interpreter
25+
sys.last_type = sys.last_value = sys.last_traceback = None
26+
super().tearDown()
27+
2328
def test_inputs(self):
2429
"""Check widget's inputs"""
2530
for input_, data in (("Data", self.iris),

Orange/widgets/tests/test_gui.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ def setUp(self):
2828
self.view = gui.listView(
2929
self.widget.controlArea, self.widget, "foo", model=self.attrs)
3030

31+
def tearDown(self) -> None:
32+
self.widget.deleteLater()
33+
del self.widget
34+
3135
def test_select_callback(self):
3236
widget = self.widget
3337
view = self.view

Orange/widgets/unsupervised/owsom.py

Lines changed: 38 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
import numpy as np
55
import scipy.sparse as sp
66

7-
from AnyQt.QtCore import Qt, QRectF, pyqtSignal as Signal, QObject, QThread
7+
from AnyQt.QtCore import Qt, QRectF, pyqtSignal as Signal, QObject, QThread, \
8+
pyqtSlot as Slot
89
from AnyQt.QtGui import QTransform, QPen, QBrush, QColor, QPainter, \
910
QPainterPath, QKeyEvent
1011
from AnyQt.QtWidgets import \
@@ -498,6 +499,7 @@ def redraw_selection(self, marks=None):
498499
def restart_som_pressed(self):
499500
if self._optimizer_thread is not None:
500501
self.stop_optimization = True
502+
self._optimizer.stop_optimization = True
501503
else:
502504
self.start_som()
503505

@@ -678,26 +680,29 @@ def _recompute_som(self):
678680
if self.cont_x is None:
679681
return
680682

683+
som = SOM(
684+
self.size_x, self.size_y,
685+
hexagonal=self.hexagonal,
686+
pca_init=self.initialization == 0,
687+
random_seed=0 if self.initialization == 2 else None
688+
)
689+
681690
class Optimizer(QObject):
682691
update = Signal(float, np.ndarray, np.ndarray)
683692
done = Signal(SOM)
684693
stopped = Signal()
694+
stop_optimization = False
685695

686-
def __init__(self, data, widget):
696+
def __init__(self, data, som):
687697
super().__init__()
688-
self.som = SOM(
689-
widget.size_x, widget.size_y,
690-
hexagonal=widget.hexagonal,
691-
pca_init=widget.initialization == 0,
692-
random_seed=0 if widget.initialization == 2 else None)
698+
self.som = som
693699
self.data = data
694-
self.widget = widget
695700

696701
def callback(self, progress):
697702
self.update.emit(
698703
progress,
699704
self.som.weights.copy(), self.som.ssum_weights.copy())
700-
return not self.widget.stop_optimization
705+
return not self.stop_optimization
701706

702707
def run(self):
703708
try:
@@ -708,44 +713,47 @@ def run(self):
708713
self.done.emit(self.som)
709714
self.stopped.emit()
710715

711-
def update(_progress, weights, ssum_weights):
712-
progressbar.advance()
713-
self._assign_instances(weights, ssum_weights)
714-
self._redraw()
715-
716-
def done(som):
717-
self.enable_controls(True)
718-
progressbar.finish()
719-
self._assign_instances(som.weights, som.ssum_weights)
720-
self._redraw()
721-
# This is the first time we know what was selected (assuming that
722-
# initialization is not set to random)
723-
if self.__pending_selection is not None:
724-
self.on_selection_change(self.__pending_selection)
725-
self.__pending_selection = None
726-
self.update_output()
727-
728716
def thread_finished():
729717
self._optimizer = None
730718
self._optimizer_thread = None
731719

732-
progressbar = gui.ProgressBar(self, N_ITERATIONS)
720+
self.progressBarInit()
733721

734-
self._optimizer = Optimizer(self.cont_x, self)
722+
self._optimizer = Optimizer(self.cont_x, som)
735723
self._optimizer_thread = QThread()
736724
self._optimizer_thread.setStackSize(5 * 2 ** 20)
737-
self._optimizer.update.connect(update)
738-
self._optimizer.done.connect(done)
725+
self._optimizer.update.connect(self.__update)
726+
self._optimizer.done.connect(self.__done)
739727
self._optimizer.stopped.connect(self._optimizer_thread.quit)
740728
self._optimizer.moveToThread(self._optimizer_thread)
741729
self._optimizer_thread.started.connect(self._optimizer.run)
742730
self._optimizer_thread.finished.connect(thread_finished)
743731
self.stop_optimization = False
744732
self._optimizer_thread.start()
745733

734+
@Slot(float, object, object)
735+
def __update(self, _progress, weights, ssum_weights):
736+
self.progressBarSet(_progress)
737+
self._assign_instances(weights, ssum_weights)
738+
self._redraw()
739+
740+
@Slot(object)
741+
def __done(self, som):
742+
self.enable_controls(True)
743+
self.progressBarFinished()
744+
self._assign_instances(som.weights, som.ssum_weights)
745+
self._redraw()
746+
# This is the first time we know what was selected (assuming that
747+
# initialization is not set to random)
748+
if self.__pending_selection is not None:
749+
self.on_selection_change(self.__pending_selection)
750+
self.__pending_selection = None
751+
self.update_output()
752+
746753
def stop_optimization_and_wait(self):
747754
if self._optimizer_thread is not None:
748755
self.stop_optimization = True
756+
self._optimizer.stop_optimization = True
749757
self._optimizer_thread.quit()
750758
self._optimizer_thread.wait()
751759
self._optimizer_thread = None

Orange/widgets/visualize/tests/test_owscatterplotbase.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,15 +46,31 @@ def get_palette(self):
4646
else:
4747
return colorpalettes.DefaultDiscretePalette
4848

49+
@staticmethod
50+
def reset_mocks():
51+
for m in MockWidget.__dict__.values():
52+
if isinstance(m, Mock):
53+
m.reset_mock()
54+
4955

5056
class TestOWScatterPlotBase(WidgetTest):
5157
def setUp(self):
58+
super().setUp()
5259
self.master = MockWidget()
5360
self.graph = OWScatterPlotBase(self.master)
5461

5562
self.xy = (np.arange(10, dtype=float), np.arange(10, dtype=float))
5663
self.master.get_coordinates_data = lambda: self.xy
5764

65+
def tearDown(self):
66+
self.master.onDeleteWidget()
67+
self.master.deleteLater()
68+
# Clear mocks as they keep ref to widget instance when called
69+
MockWidget.reset_mocks()
70+
del self.master
71+
del self.graph
72+
super().tearDown()
73+
5874
# pylint: disable=keyword-arg-before-vararg
5975
def setRange(self, rect=None, *_, **__):
6076
if isinstance(rect, QRectF):

0 commit comments

Comments
 (0)