From 4b188c73b0515d6a391d5b5f2c9ae4bea526aa5b Mon Sep 17 00:00:00 2001 From: Luke Campagnola Date: Wed, 6 Sep 2017 09:09:35 -0700 Subject: [PATCH 1/9] Add disconnect() and SignalBlock - make it possible to retrieve previous versions of reloaded objects (needed by disconnect) --- pyqtgraph/functions.py | 46 ++++++++++++++++++++++++++++++++++++++++-- pyqtgraph/reload.py | 44 +++++++++++++++++++++++++++++++++------- 2 files changed, 81 insertions(+), 9 deletions(-) diff --git a/pyqtgraph/functions.py b/pyqtgraph/functions.py index 1aed6ace..4b4231c8 100644 --- a/pyqtgraph/functions.py +++ b/pyqtgraph/functions.py @@ -14,8 +14,8 @@ import sys, struct from .python2_3 import asUnicode, basestring from .Qt import QtGui, QtCore, USE_PYSIDE from . import getConfigOption, setConfigOptions -from . import debug - +from . import debug, reload +from .reload import getPreviousVersion Colors = { @@ -2412,3 +2412,45 @@ def toposort(deps, nodes=None, seen=None, stack=None, depth=0): sorted.extend( toposort(deps, deps[n], seen, stack+[n], depth=depth+1)) sorted.append(n) return sorted + + +def disconnect(signal, slot): + """Disconnect a Qt signal from a slot. + + This method augments Qt's Signal.disconnect(): + + * Return bool indicating whether disconnection was successful, rather than + raising an exception + * Attempt to disconnect prior versions of the slot when using pg.reload + """ + while True: + try: + signal.disconnect(slot) + return True + except TypeError, RuntimeError: + slot = reload.getPreviousVersion(slot) + if slot is None: + return False + + +class SignalBlock(object): + """Class used to temporarily block a Qt signal connection:: + + with SignalBlock(signal, slot): + # do something that emits a signal; it will + # not be delivered to slot + """ + def __init__(self, signal, slot): + self.signal = signal + self.slot = slot + + def __enter__(self): + self.reconnect = disconnect(self.signal, self.slot) + return self + + def __exit__(self, *args): + if self.reconnect: + self.signal.connect(self.slot) + + + diff --git a/pyqtgraph/reload.py b/pyqtgraph/reload.py index ccf83913..5aa2ed68 100644 --- a/pyqtgraph/reload.py +++ b/pyqtgraph/reload.py @@ -22,13 +22,14 @@ Does NOT: """ -import inspect, os, sys, gc, traceback +import inspect, os, sys, gc, traceback, types try: import __builtin__ as builtins except ImportError: import builtins from .debug import printExc + def reloadAll(prefix=None, debug=False): """Automatically reload everything whose __file__ begins with prefix. - Skips reload if the file has not been updated (if .pyc is newer than .py) @@ -97,7 +98,9 @@ def reload(module, debug=False, lists=False, dicts=False): if debug: print(" Updating class %s.%s (0x%x -> 0x%x)" % (module.__name__, k, id(old), id(new))) updateClass(old, new, debug) - + # don't put this inside updateClass because it is reentrant. + new.__previous_reload_version__ = old + elif inspect.isfunction(old): depth = updateFunction(old, new, debug) if debug: @@ -152,7 +155,6 @@ def updateFunction(old, new, debug, depth=0, visited=None): ## 1) find all instances of the old class and set instance.__class__ to the new class ## 2) update all old class methods to use code from the new class methods def updateClass(old, new, debug): - ## Track town all instances and subclasses of old refs = gc.get_referrers(old) for ref in refs: @@ -174,13 +176,20 @@ def updateClass(old, new, debug): ## This seems to work. Is there any reason not to? ## Note that every time we reload, the class hierarchy becomes more complex. ## (and I presume this may slow things down?) - ref.__bases__ = ref.__bases__[:ind] + (new,old) + ref.__bases__[ind+1:] + newBases = ref.__bases__[:ind] + (new,old) + ref.__bases__[ind+1:] + try: + ref.__bases__ = newBases + except TypeError: + print(" Error setting bases for class %s" % ref) + print(" old bases: %s" % repr(ref.__bases__)) + print(" new bases: %s" % repr(newBases)) + raise if debug: print(" Changed superclass for %s" % safeStr(ref)) #else: #if debug: #print " Ignoring reference", type(ref) - except: + except Exception: print("Error updating reference (%s) for class change (%s -> %s)" % (safeStr(ref), safeStr(old), safeStr(new))) raise @@ -199,6 +208,8 @@ def updateClass(old, new, debug): if hasattr(oa, 'im_func') and hasattr(na, 'im_func') and oa.__func__ is not na.__func__: depth = updateFunction(oa.__func__, na.__func__, debug) + if not hasattr(na.__func__, '__previous_reload_method__'): + na.__func__.__previous_reload_method__ = oa # important for managing signal connection #oa.im_class = new ## bind old method to new class ## not allowed if debug: extra = "" @@ -208,6 +219,8 @@ def updateClass(old, new, debug): ## And copy in new functions that didn't exist previously for attr in dir(new): + if attr == '__previous_reload_version__': + continue if not hasattr(old, attr): if debug: print(" Adding missing attribute %s" % attr) @@ -223,14 +236,31 @@ def updateClass(old, new, debug): def safeStr(obj): try: s = str(obj) - except: + except Exception: try: s = repr(obj) - except: + except Exception: s = "" % (safeStr(type(obj)), id(obj)) return s +def getPreviousVersion(obj): + """Return the previous version of *obj*, or None if this object has not + been reloaded. + """ + if isinstance(obj, type) or inspect.isfunction(obj): + return getattr(obj, '__previous_reload_version__', None) + elif inspect.ismethod(obj): + if obj.im_self is None: + # unbound method + return getattr(obj.__func__, '__previous_reload_method__', None) + else: + oldmethod = getattr(obj.__func__, '__previous_reload_method__', None) + if oldmethod is None: + return None + self = obj.im_self + cls = oldmethod.im_class + return types.MethodType(oldmethod.__func__, self, cls) From db890b8ed84a7c1c66b4dd17a0969dc1d741636a Mon Sep 17 00:00:00 2001 From: Luke Campagnola Date: Wed, 6 Sep 2017 09:13:56 -0700 Subject: [PATCH 2/9] Add unit test for reload(); make travis use --assert=reinterp (because assert=rewrite does not work with reload) --- .travis.yml | 3 +- pyqtgraph/tests/test_reload.py | 74 ++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 pyqtgraph/tests/test_reload.py diff --git a/.travis.yml b/.travis.yml index c4a67ac3..1c075090 100644 --- a/.travis.yml +++ b/.travis.yml @@ -143,7 +143,8 @@ script: # Run unit tests - start_test "unit tests"; - PYTHONPATH=. py.test --cov pyqtgraph -sv; + # NOTE: tests/test_reload.py breaks without --assert=reinterp|plain + PYTHONPATH=. py.test --cov --assert=reinterp pyqtgraph -sv; check_output "unit tests"; - echo "test script finished. Current directory:" - pwd diff --git a/pyqtgraph/tests/test_reload.py b/pyqtgraph/tests/test_reload.py new file mode 100644 index 00000000..4b670fb6 --- /dev/null +++ b/pyqtgraph/tests/test_reload.py @@ -0,0 +1,74 @@ +import tempfile, os, sys, shutil, atexit +import pyqtgraph as pg +import pyqtgraph.reload +pgpath = os.path.join(os.path.dirname(pg.__file__), '..') + +# make temporary directory to write module code +path = tempfile.mkdtemp() +sys.path.insert(0, path) +def cleanup(): + shutil.rmtree(path) +atexit.register(cleanup) + + +code = """ +import sys +sys.path.append('{path}') + +import pyqtgraph as pg + +class C(pg.QtCore.QObject): + sig = pg.QtCore.Signal() + def fn(self): + print("{msg}") + +""" + +def test_reload(): + # write a module + mod = os.path.join(path, 'reload_test.py') + open(mod, 'w').write(code.format(path=path, msg="C.fn() Version1")) + + # import the new module + import reload_test + + c = reload_test.C() + c.sig.connect(c.fn) + v1 = (reload_test.C, reload_test.C.sig, reload_test.C.fn, reload_test.C.fn.__func__, c.sig, c.fn, c.fn.__func__) + + + + # write again and reload + open(mod, 'w').write(code.format(path=path, msg="C.fn() Version2")) + os.remove(mod+'c') + pg.reload.reloadAll(path, debug=True) + v2 = (reload_test.C, reload_test.C.sig, reload_test.C.fn, reload_test.C.fn.__func__, c.sig, c.fn, c.fn.__func__) + + assert c.fn.im_class is v2[0] + oldcfn = pg.reload.getPreviousVersion(c.fn) + assert oldcfn.im_class is v1[0] + assert oldcfn.im_func is v1[2].im_func + assert oldcfn.im_self is c + + + + # write again and reload + open(mod, 'w').write(code.format(path=path, msg="C.fn() Version2")) + os.remove(mod+'c') + pg.reload.reloadAll(path, debug=True) + v3 = (reload_test.C, reload_test.C.sig, reload_test.C.fn, reload_test.C.fn.__func__, c.sig, c.fn, c.fn.__func__) + + #for i in range(len(old)): + #print id(old[i]), id(new1[i]), id(new2[i]), old[i], new1[i] + + cfn1 = pg.reload.getPreviousVersion(c.fn) + cfn2 = pg.reload.getPreviousVersion(cfn1) + + assert cfn1.im_class is v2[0] + assert cfn1.im_func is v2[2].im_func + assert cfn1.im_self is c + assert cfn2.im_class is v1[0] + assert cfn2.im_func is v1[2].im_func + assert cfn2.im_self is c + c.sig.disconnect(cfn2) + From 4bca2ae87916cc21bac659db1a72d5253dd55155 Mon Sep 17 00:00:00 2001 From: Luke Campagnola Date: Wed, 6 Sep 2017 09:39:26 -0700 Subject: [PATCH 3/9] fix reload test pycache removal Ad a warning about using assert=rewrite --- pyqtgraph/tests/test_reload.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/pyqtgraph/tests/test_reload.py b/pyqtgraph/tests/test_reload.py index 4b670fb6..3f2195e2 100644 --- a/pyqtgraph/tests/test_reload.py +++ b/pyqtgraph/tests/test_reload.py @@ -24,6 +24,14 @@ class C(pg.QtCore.QObject): """ +def remove_cache(mod): + if os.path.isfile(mod+'c'): + os.remove(mod+'c') + cachedir = os.path.join(os.path.dirname(mod), '__pycache__') + if os.path.isdir(cachedir): + shutil.rmtree(cachedir) + + def test_reload(): # write a module mod = os.path.join(path, 'reload_test.py') @@ -40,12 +48,16 @@ def test_reload(): # write again and reload open(mod, 'w').write(code.format(path=path, msg="C.fn() Version2")) - os.remove(mod+'c') + remove_cache(mod) pg.reload.reloadAll(path, debug=True) v2 = (reload_test.C, reload_test.C.sig, reload_test.C.fn, reload_test.C.fn.__func__, c.sig, c.fn, c.fn.__func__) assert c.fn.im_class is v2[0] oldcfn = pg.reload.getPreviousVersion(c.fn) + if oldcfn is None: + # Function did not reload; are we using pytest's assertion rewriting? + raise Exception("Function did not reload. (This can happen when using py.test" + " with assertion rewriting; use --assert=reinterp or --assert=plain)") assert oldcfn.im_class is v1[0] assert oldcfn.im_func is v1[2].im_func assert oldcfn.im_self is c @@ -54,7 +66,7 @@ def test_reload(): # write again and reload open(mod, 'w').write(code.format(path=path, msg="C.fn() Version2")) - os.remove(mod+'c') + remove_cache(mod) pg.reload.reloadAll(path, debug=True) v3 = (reload_test.C, reload_test.C.sig, reload_test.C.fn, reload_test.C.fn.__func__, c.sig, c.fn, c.fn.__func__) From 1bd97c67e29c1c642bc9a41e0413f7723d638172 Mon Sep 17 00:00:00 2001 From: Luke Campagnola Date: Wed, 6 Sep 2017 09:42:59 -0700 Subject: [PATCH 4/9] Fix travis.yml --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 1c075090..0a6cf699 100644 --- a/.travis.yml +++ b/.travis.yml @@ -143,7 +143,6 @@ script: # Run unit tests - start_test "unit tests"; - # NOTE: tests/test_reload.py breaks without --assert=reinterp|plain PYTHONPATH=. py.test --cov --assert=reinterp pyqtgraph -sv; check_output "unit tests"; - echo "test script finished. Current directory:" From 7e5b40d265f1e8b0fa2c2b9c96a4d91ef9571599 Mon Sep 17 00:00:00 2001 From: Luke Campagnola Date: Wed, 6 Sep 2017 09:58:42 -0700 Subject: [PATCH 5/9] Switch to assert=plain; apparently assert=reinterp is no longer supported --- .travis.yml | 2 +- pyqtgraph/tests/test_reload.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0a6cf699..4478f0a8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -143,7 +143,7 @@ script: # Run unit tests - start_test "unit tests"; - PYTHONPATH=. py.test --cov --assert=reinterp pyqtgraph -sv; + PYTHONPATH=. py.test --cov --assert=plain pyqtgraph -sv; check_output "unit tests"; - echo "test script finished. Current directory:" - pwd diff --git a/pyqtgraph/tests/test_reload.py b/pyqtgraph/tests/test_reload.py index 3f2195e2..7ceed546 100644 --- a/pyqtgraph/tests/test_reload.py +++ b/pyqtgraph/tests/test_reload.py @@ -57,7 +57,7 @@ def test_reload(): if oldcfn is None: # Function did not reload; are we using pytest's assertion rewriting? raise Exception("Function did not reload. (This can happen when using py.test" - " with assertion rewriting; use --assert=reinterp or --assert=plain)") + " with assertion rewriting; use --assert=plain for this test.)") assert oldcfn.im_class is v1[0] assert oldcfn.im_func is v1[2].im_func assert oldcfn.im_self is c From 60a48ed2e4ef9fcecaffca33e009f4e0a35bd972 Mon Sep 17 00:00:00 2001 From: Luke Campagnola Date: Thu, 26 Apr 2018 13:22:47 -0700 Subject: [PATCH 6/9] reload tests pass in python 3 --- pyqtgraph/functions.py | 2 +- pyqtgraph/reload.py | 46 ++++++++++++++++-------- pyqtgraph/tests/test_reload.py | 65 ++++++++++++++++++++++++---------- 3 files changed, 79 insertions(+), 34 deletions(-) diff --git a/pyqtgraph/functions.py b/pyqtgraph/functions.py index 21e6c006..8857c052 100644 --- a/pyqtgraph/functions.py +++ b/pyqtgraph/functions.py @@ -2444,7 +2444,7 @@ def disconnect(signal, slot): try: signal.disconnect(slot) return True - except TypeError, RuntimeError: + except (TypeError, RuntimeError): slot = reload.getPreviousVersion(slot) if slot is None: return False diff --git a/pyqtgraph/reload.py b/pyqtgraph/reload.py index 5aa2ed68..f682a025 100644 --- a/pyqtgraph/reload.py +++ b/pyqtgraph/reload.py @@ -21,15 +21,18 @@ Does NOT: print module.someObject """ - +from __future__ import print_function import inspect, os, sys, gc, traceback, types try: - import __builtin__ as builtins + from builtins import reload as orig_reload except ImportError: - import builtins + from importlib import reload as orig_reload from .debug import printExc +py3 = sys.version_info >= (3,) + + def reloadAll(prefix=None, debug=False): """Automatically reload everything whose __file__ begins with prefix. - Skips reload if the file has not been updated (if .pyc is newer than .py) @@ -80,7 +83,7 @@ def reload(module, debug=False, lists=False, dicts=False): ## make a copy of the old module dictionary, reload, then grab the new module dictionary for comparison oldDict = module.__dict__.copy() - builtins.reload(module) + orig_reload(module) newDict = module.__dict__ ## Allow modules access to the old dictionary after they reload @@ -130,6 +133,9 @@ def updateFunction(old, new, debug, depth=0, visited=None): old.__code__ = new.__code__ old.__defaults__ = new.__defaults__ + if hasattr(old, '__kwdefaults'): + old.__kwdefaults__ = new.__kwdefaults__ + old.__doc__ = new.__doc__ if visited is None: visited = [] @@ -154,6 +160,8 @@ def updateFunction(old, new, debug, depth=0, visited=None): ## For classes: ## 1) find all instances of the old class and set instance.__class__ to the new class ## 2) update all old class methods to use code from the new class methods + + def updateClass(old, new, debug): ## Track town all instances and subclasses of old refs = gc.get_referrers(old) @@ -198,7 +206,8 @@ def updateClass(old, new, debug): ## but it fixes a few specific cases (pyqt signals, for one) for attr in dir(old): oa = getattr(old, attr) - if inspect.ismethod(oa): + if (py3 and inspect.isfunction(oa)) or inspect.ismethod(oa): + # note python2 has unbound methods, whereas python3 just uses plain functions try: na = getattr(new, attr) except AttributeError: @@ -206,11 +215,14 @@ def updateClass(old, new, debug): print(" Skipping method update for %s; new class does not have this attribute" % attr) continue - if hasattr(oa, 'im_func') and hasattr(na, 'im_func') and oa.__func__ is not na.__func__: - depth = updateFunction(oa.__func__, na.__func__, debug) - if not hasattr(na.__func__, '__previous_reload_method__'): - na.__func__.__previous_reload_method__ = oa # important for managing signal connection - #oa.im_class = new ## bind old method to new class ## not allowed + ofunc = getattr(oa, '__func__', oa) # in py2 we have to get the __func__ from unbound method, + nfunc = getattr(na, '__func__', na) # in py3 the attribute IS the function + + if ofunc is not nfunc: + depth = updateFunction(ofunc, nfunc, debug) + if not hasattr(nfunc, '__previous_reload_method__'): + nfunc.__previous_reload_method__ = oa # important for managing signal connection + #oa.__class__ = new ## bind old method to new class ## not allowed if debug: extra = "" if depth > 0: @@ -251,16 +263,22 @@ def getPreviousVersion(obj): if isinstance(obj, type) or inspect.isfunction(obj): return getattr(obj, '__previous_reload_version__', None) elif inspect.ismethod(obj): - if obj.im_self is None: + if obj.__self__ is None: # unbound method return getattr(obj.__func__, '__previous_reload_method__', None) else: oldmethod = getattr(obj.__func__, '__previous_reload_method__', None) if oldmethod is None: return None - self = obj.im_self - cls = oldmethod.im_class - return types.MethodType(oldmethod.__func__, self, cls) + self = obj.__self__ + oldfunc = getattr(oldmethod, '__func__', oldmethod) + if hasattr(oldmethod, 'im_class'): + # python 2 + cls = oldmethod.im_class + return types.MethodType(oldfunc, self, cls) + else: + # python 3 + return types.MethodType(oldfunc, self) diff --git a/pyqtgraph/tests/test_reload.py b/pyqtgraph/tests/test_reload.py index 7ceed546..57924d60 100644 --- a/pyqtgraph/tests/test_reload.py +++ b/pyqtgraph/tests/test_reload.py @@ -1,14 +1,23 @@ -import tempfile, os, sys, shutil, atexit +import tempfile, os, sys, shutil import pyqtgraph as pg import pyqtgraph.reload + + pgpath = os.path.join(os.path.dirname(pg.__file__), '..') # make temporary directory to write module code -path = tempfile.mkdtemp() -sys.path.insert(0, path) -def cleanup(): +path = None + +def setup_module(): + # make temporary directory to write module code + global path + path = tempfile.mkdtemp() + sys.path.insert(0, path) + +def teardown_module(): + global path shutil.rmtree(path) -atexit.register(cleanup) + sys.path.remove(path) code = """ @@ -33,6 +42,8 @@ def remove_cache(mod): def test_reload(): + py3 = sys.version_info >= (3,) + # write a module mod = os.path.join(path, 'reload_test.py') open(mod, 'w').write(code.format(path=path, msg="C.fn() Version1")) @@ -42,7 +53,10 @@ def test_reload(): c = reload_test.C() c.sig.connect(c.fn) - v1 = (reload_test.C, reload_test.C.sig, reload_test.C.fn, reload_test.C.fn.__func__, c.sig, c.fn, c.fn.__func__) + if py3: + v1 = (reload_test.C, reload_test.C.sig, reload_test.C.fn, c.sig, c.fn, c.fn.__func__) + else: + v1 = (reload_test.C, reload_test.C.sig, reload_test.C.fn, reload_test.C.fn.__func__, c.sig, c.fn, c.fn.__func__) @@ -50,25 +64,34 @@ def test_reload(): open(mod, 'w').write(code.format(path=path, msg="C.fn() Version2")) remove_cache(mod) pg.reload.reloadAll(path, debug=True) - v2 = (reload_test.C, reload_test.C.sig, reload_test.C.fn, reload_test.C.fn.__func__, c.sig, c.fn, c.fn.__func__) + if py3: + v2 = (reload_test.C, reload_test.C.sig, reload_test.C.fn, c.sig, c.fn, c.fn.__func__) + else: + v2 = (reload_test.C, reload_test.C.sig, reload_test.C.fn, reload_test.C.fn.__func__, c.sig, c.fn, c.fn.__func__) - assert c.fn.im_class is v2[0] + if not py3: + assert c.fn.im_class is v2[0] oldcfn = pg.reload.getPreviousVersion(c.fn) if oldcfn is None: # Function did not reload; are we using pytest's assertion rewriting? raise Exception("Function did not reload. (This can happen when using py.test" " with assertion rewriting; use --assert=plain for this test.)") - assert oldcfn.im_class is v1[0] - assert oldcfn.im_func is v1[2].im_func - assert oldcfn.im_self is c - + if py3: + assert oldcfn.__func__ is v1[2] + else: + assert oldcfn.im_class is v1[0] + assert oldcfn.__func__ is v1[2].__func__ + assert oldcfn.__self__ is c # write again and reload open(mod, 'w').write(code.format(path=path, msg="C.fn() Version2")) remove_cache(mod) pg.reload.reloadAll(path, debug=True) - v3 = (reload_test.C, reload_test.C.sig, reload_test.C.fn, reload_test.C.fn.__func__, c.sig, c.fn, c.fn.__func__) + if py3: + v3 = (reload_test.C, reload_test.C.sig, reload_test.C.fn, c.sig, c.fn, c.fn.__func__) + else: + v3 = (reload_test.C, reload_test.C.sig, reload_test.C.fn, reload_test.C.fn.__func__, c.sig, c.fn, c.fn.__func__) #for i in range(len(old)): #print id(old[i]), id(new1[i]), id(new2[i]), old[i], new1[i] @@ -76,11 +99,15 @@ def test_reload(): cfn1 = pg.reload.getPreviousVersion(c.fn) cfn2 = pg.reload.getPreviousVersion(cfn1) - assert cfn1.im_class is v2[0] - assert cfn1.im_func is v2[2].im_func - assert cfn1.im_self is c - assert cfn2.im_class is v1[0] - assert cfn2.im_func is v1[2].im_func - assert cfn2.im_self is c + if py3: + assert cfn1.__func__ is v2[2] + assert cfn2.__func__ is v1[2] + else: + assert cfn1.__func__ is v2[2].__func__ + assert cfn2.__func__ is v1[2].__func__ + assert cfn1.im_class is v2[0] + assert cfn2.im_class is v1[0] + assert cfn1.__self__ is c + assert cfn2.__self__ is c c.sig.disconnect(cfn2) From 986ccb81f489e791b442f1296e0efe27bbfda601 Mon Sep 17 00:00:00 2001 From: Luke Campagnola Date: Thu, 26 Apr 2018 13:28:51 -0700 Subject: [PATCH 7/9] fix reload import --- pyqtgraph/reload.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pyqtgraph/reload.py b/pyqtgraph/reload.py index f682a025..766ec9d0 100644 --- a/pyqtgraph/reload.py +++ b/pyqtgraph/reload.py @@ -23,11 +23,11 @@ Does NOT: from __future__ import print_function import inspect, os, sys, gc, traceback, types -try: - from builtins import reload as orig_reload -except ImportError: - from importlib import reload as orig_reload from .debug import printExc +try: + from importlib import reload as orig_reload +except ImportError: + orig_reload = reload py3 = sys.version_info >= (3,) From 6e392c2e0eae4adbd353e01c45b2134f8e585d8d Mon Sep 17 00:00:00 2001 From: Luke Campagnola Date: Thu, 26 Apr 2018 13:49:34 -0700 Subject: [PATCH 8/9] Fix disconnect test for pyside --- pyqtgraph/tests/test_reload.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyqtgraph/tests/test_reload.py b/pyqtgraph/tests/test_reload.py index 57924d60..b90072f1 100644 --- a/pyqtgraph/tests/test_reload.py +++ b/pyqtgraph/tests/test_reload.py @@ -109,5 +109,6 @@ def test_reload(): assert cfn2.im_class is v1[0] assert cfn1.__self__ is c assert cfn2.__self__ is c - c.sig.disconnect(cfn2) + + pg.functions.disconnect(c.sig, c.fn) From a1145b5cbfc1db32d8dffb7ffaa1b1d18b843923 Mon Sep 17 00:00:00 2001 From: Luke Campagnola Date: Thu, 26 Apr 2018 14:43:20 -0700 Subject: [PATCH 9/9] Fix pytest assertion rewrite conflicting with reload by giving temporary module a different name --- .travis.yml | 2 +- pyqtgraph/tests/test_reload.py | 26 ++++++++++++++------------ 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1c422f19..acfde8ec 100644 --- a/.travis.yml +++ b/.travis.yml @@ -143,7 +143,7 @@ script: # Run unit tests - start_test "unit tests"; - PYTHONPATH=. py.test --cov --assert=plain pyqtgraph -sv; + PYTHONPATH=. py.test --cov pyqtgraph -sv; check_output "unit tests"; - echo "test script finished. Current directory:" - pwd diff --git a/pyqtgraph/tests/test_reload.py b/pyqtgraph/tests/test_reload.py index b90072f1..6adbeeb6 100644 --- a/pyqtgraph/tests/test_reload.py +++ b/pyqtgraph/tests/test_reload.py @@ -45,29 +45,31 @@ def test_reload(): py3 = sys.version_info >= (3,) # write a module - mod = os.path.join(path, 'reload_test.py') - open(mod, 'w').write(code.format(path=path, msg="C.fn() Version1")) + mod = os.path.join(path, 'reload_test_mod.py') + print("\nRELOAD FILE:", mod) + open(mod, 'w').write(code.format(path=pgpath, msg="C.fn() Version1")) # import the new module - import reload_test + import reload_test_mod + print("RELOAD MOD:", reload_test_mod.__file__) - c = reload_test.C() + c = reload_test_mod.C() c.sig.connect(c.fn) if py3: - v1 = (reload_test.C, reload_test.C.sig, reload_test.C.fn, c.sig, c.fn, c.fn.__func__) + v1 = (reload_test_mod.C, reload_test_mod.C.sig, reload_test_mod.C.fn, c.sig, c.fn, c.fn.__func__) else: - v1 = (reload_test.C, reload_test.C.sig, reload_test.C.fn, reload_test.C.fn.__func__, c.sig, c.fn, c.fn.__func__) + v1 = (reload_test_mod.C, reload_test_mod.C.sig, reload_test_mod.C.fn, reload_test_mod.C.fn.__func__, c.sig, c.fn, c.fn.__func__) # write again and reload - open(mod, 'w').write(code.format(path=path, msg="C.fn() Version2")) + open(mod, 'w').write(code.format(path=pgpath, msg="C.fn() Version2")) remove_cache(mod) pg.reload.reloadAll(path, debug=True) if py3: - v2 = (reload_test.C, reload_test.C.sig, reload_test.C.fn, c.sig, c.fn, c.fn.__func__) + v2 = (reload_test_mod.C, reload_test_mod.C.sig, reload_test_mod.C.fn, c.sig, c.fn, c.fn.__func__) else: - v2 = (reload_test.C, reload_test.C.sig, reload_test.C.fn, reload_test.C.fn.__func__, c.sig, c.fn, c.fn.__func__) + v2 = (reload_test_mod.C, reload_test_mod.C.sig, reload_test_mod.C.fn, reload_test_mod.C.fn.__func__, c.sig, c.fn, c.fn.__func__) if not py3: assert c.fn.im_class is v2[0] @@ -85,13 +87,13 @@ def test_reload(): # write again and reload - open(mod, 'w').write(code.format(path=path, msg="C.fn() Version2")) + open(mod, 'w').write(code.format(path=pgpath, msg="C.fn() Version2")) remove_cache(mod) pg.reload.reloadAll(path, debug=True) if py3: - v3 = (reload_test.C, reload_test.C.sig, reload_test.C.fn, c.sig, c.fn, c.fn.__func__) + v3 = (reload_test_mod.C, reload_test_mod.C.sig, reload_test_mod.C.fn, c.sig, c.fn, c.fn.__func__) else: - v3 = (reload_test.C, reload_test.C.sig, reload_test.C.fn, reload_test.C.fn.__func__, c.sig, c.fn, c.fn.__func__) + v3 = (reload_test_mod.C, reload_test_mod.C.sig, reload_test_mod.C.fn, reload_test_mod.C.fn.__func__, c.sig, c.fn, c.fn.__func__) #for i in range(len(old)): #print id(old[i]), id(new1[i]), id(new2[i]), old[i], new1[i]