From 1ccffcb430d987657d089da7ae0b08cd7b119b13 Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Thu, 17 May 2012 20:44:18 +0100 Subject: [PATCH 01/10] Fix doctests that relied on dict ordering in IPython.utils --- IPython/utils/ipstruct.py | 26 ++++++++++++-------------- IPython/utils/jsonutil.py | 8 ++++---- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/IPython/utils/ipstruct.py b/IPython/utils/ipstruct.py index c39a021f4dc..3cc7f307735 100644 --- a/IPython/utils/ipstruct.py +++ b/IPython/utils/ipstruct.py @@ -59,8 +59,8 @@ def __init__(self, *args, **kw): >>> s.b 30 >>> s2 = Struct(s,c=30) - >>> s2.keys() - ['a', 'c', 'b'] + >>> sorted(s2.keys()) + ['a', 'b', 'c'] """ object.__setattr__(self, '_allownew', True) dict.__init__(self, *args, **kw) @@ -161,8 +161,8 @@ def __iadd__(self, other): >>> s = Struct(a=10,b=30) >>> s2 = Struct(a=20,c=40) >>> s += s2 - >>> s - {'a': 10, 'c': 40, 'b': 30} + >>> sorted(s.keys()) + ['a', 'b', 'c'] """ self.merge(other) return self @@ -176,8 +176,8 @@ def __add__(self,other): >>> s1 = Struct(a=10,b=30) >>> s2 = Struct(a=20,c=40) >>> s = s1 + s2 - >>> s - {'a': 10, 'c': 40, 'b': 30} + >>> sorted(s.keys()) + ['a', 'b', 'c'] """ sout = self.copy() sout.merge(other) @@ -241,10 +241,8 @@ def copy(self): >>> s = Struct(a=10,b=30) >>> s2 = s.copy() - >>> s2 - {'a': 10, 'b': 30} - >>> type(s2).__name__ - 'Struct' + >>> type(s2) is Struct + True """ return Struct(dict.copy(self)) @@ -348,8 +346,8 @@ def merge(self, __loc_data__=None, __conflict_solve=None, **kw): >>> s = Struct(a=10,b=30) >>> s2 = Struct(a=20,c=40) >>> s.merge(s2) - >>> s - {'a': 10, 'c': 40, 'b': 30} + >>> sorted(s.items()) + [('a', 10), ('b', 30), ('c', 40)] Now, show how to specify a conflict dict: @@ -357,8 +355,8 @@ def merge(self, __loc_data__=None, __conflict_solve=None, **kw): >>> s2 = Struct(a=20,b=40) >>> conflict = {'update':'a','add':'b'} >>> s.merge(s2,conflict) - >>> s - {'a': 20, 'b': 70} + >>> sorted(s.items()) + [('a', 20), ('b', 70)] """ data_dict = dict(__loc_data__,**kw) diff --git a/IPython/utils/jsonutil.py b/IPython/utils/jsonutil.py index 932c02b629f..caa39fbacc1 100644 --- a/IPython/utils/jsonutil.py +++ b/IPython/utils/jsonutil.py @@ -118,10 +118,10 @@ def json_clean(obj): 4 >>> json_clean(range(10)) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - >>> json_clean(dict(x=1, y=2)) - {'y': 2, 'x': 1} - >>> json_clean(dict(x=1, y=2, z=[1,2,3])) - {'y': 2, 'x': 1, 'z': [1, 2, 3]} + >>> sorted(json_clean(dict(x=1, y=2)).items()) + [('x', 1), ('y', 2)] + >>> sorted(json_clean(dict(x=1, y=2, z=[1,2,3])).items()) + [('x', 1), ('y', 2), ('z', [1, 2, 3])] >>> json_clean(True) True """ From 6fb51bdec380a8ce9dff633ff6f7b80e48ee5d10 Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Thu, 17 May 2012 20:49:33 +0100 Subject: [PATCH 02/10] Make repr-reliant test less specific --- IPython/utils/tests/test_jsonutil.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/IPython/utils/tests/test_jsonutil.py b/IPython/utils/tests/test_jsonutil.py index cb79f80c686..45247a05bd3 100644 --- a/IPython/utils/tests/test_jsonutil.py +++ b/IPython/utils/tests/test_jsonutil.py @@ -58,7 +58,8 @@ def test(): def test_lambda(): jc = json_clean(lambda : 1) - nt.assert_true(jc.startswith(' at ')) + assert isinstance(jc, str) + assert '' in jc json.dumps(jc) From 62dafdaec7f47bd368925360bf92ddeb8805832b Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Thu, 17 May 2012 20:54:03 +0100 Subject: [PATCH 03/10] Fix more cases relying on dict ordering in utils --- IPython/utils/frame.py | 4 ++-- IPython/utils/tests/test_traitlets.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/IPython/utils/frame.py b/IPython/utils/frame.py index 13f484d31fe..07cc5538bf2 100644 --- a/IPython/utils/frame.py +++ b/IPython/utils/frame.py @@ -39,11 +39,11 @@ def extract_vars(*names,**kw): In [2]: def func(x): ...: y = 1 - ...: print extract_vars('x','y') + ...: print sorted(extract_vars('x','y').items()) ...: In [3]: func('hello') - {'y': 1, 'x': 'hello'} + [('x', 'hello'), ('y', 1)] """ depth = kw.get('depth',0) diff --git a/IPython/utils/tests/test_traitlets.py b/IPython/utils/tests/test_traitlets.py index b14133c0a7b..2c2985cf376 100644 --- a/IPython/utils/tests/test_traitlets.py +++ b/IPython/utils/tests/test_traitlets.py @@ -370,8 +370,8 @@ class A(HasTraits): i = Int f = Float a = A() - self.assertEquals(a.trait_names(),['i','f']) - self.assertEquals(A.class_trait_names(),['i','f']) + self.assertEquals(sorted(a.trait_names()),['f','i']) + self.assertEquals(sorted(A.class_trait_names()),['f','i']) def test_trait_metadata(self): class A(HasTraits): From 040d936dbd49398e77a7079953b377686cc10a08 Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Thu, 17 May 2012 21:09:03 +0100 Subject: [PATCH 04/10] Fix tests relying on dict order in IPython.config --- IPython/config/configurable.py | 2 +- IPython/config/loader.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/IPython/config/configurable.py b/IPython/config/configurable.py index 867c4661d2d..f2bee7eb9ef 100644 --- a/IPython/config/configurable.py +++ b/IPython/config/configurable.py @@ -160,7 +160,7 @@ class defaults. final_help = [] final_help.append(u'%s options' % cls.__name__) final_help.append(len(final_help[0])*u'-') - for k,v in cls.class_traits(config=True).iteritems(): + for k,v in sorted(cls.class_traits(config=True).iteritems()): help = cls.class_get_trait_help(v, inst) final_help.append(help) return '\n'.join(final_help) diff --git a/IPython/config/loader.py b/IPython/config/loader.py index cef4f6569e5..8adf243cc57 100644 --- a/IPython/config/loader.py +++ b/IPython/config/loader.py @@ -417,8 +417,9 @@ def __init__(self, argv=None, aliases=None, flags=None): >>> from IPython.config.loader import KeyValueConfigLoader >>> cl = KeyValueConfigLoader() - >>> cl.load_config(["--A.name='brian'","--B.number=0"]) - {'A': {'name': 'brian'}, 'B': {'number': 0}} + >>> d = cl.load_config(["--A.name='brian'","--B.number=0"]) + >>> sorted(d.items()) + [('A', {'name': 'brian'}), ('B', {'number': 0})] """ self.clear() if argv is None: From fe1c7d7ccbc813516765c57c4e433b081509de1e Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Thu, 17 May 2012 22:35:18 +0100 Subject: [PATCH 05/10] Fix tests that depended on dictionary order in IPython.core --- IPython/core/excolors.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/IPython/core/excolors.py b/IPython/core/excolors.py index 3524fff4540..0efc1cdf5ac 100644 --- a/IPython/core/excolors.py +++ b/IPython/core/excolors.py @@ -31,10 +31,10 @@ def exception_colors(): >>> ec.set_active_scheme('NoColor') >>> ec.active_scheme_name 'NoColor' - >>> ec.active_colors.keys() - ['em', 'filenameEm', 'excName', 'valEm', 'nameEm', 'line', 'topline', - 'name', 'caret', 'val', 'vName', 'Normal', 'filename', 'linenoEm', - 'lineno', 'normalEm'] + >>> sorted(ec.active_colors.keys()) + ['Normal', 'caret', 'em', 'excName', 'filename', 'filenameEm', 'line', + 'lineno', 'linenoEm', 'name', 'nameEm', 'normalEm', 'topline', 'vName', + 'val', 'valEm'] """ ex_colors = ColorSchemeTable() From 7fcb72be5f92eed9165616af18fb2960227fb46b Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Thu, 17 May 2012 23:55:26 +0100 Subject: [PATCH 06/10] teardown changes to sys.path --- IPython/utils/tests/test_module_paths.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/IPython/utils/tests/test_module_paths.py b/IPython/utils/tests/test_module_paths.py index 6299097e632..f422bbcb13f 100644 --- a/IPython/utils/tests/test_module_paths.py +++ b/IPython/utils/tests/test_module_paths.py @@ -43,7 +43,6 @@ # old_syspath = sys.path -sys.path = [TMP_TEST_DIR] def make_empty_file(fname): f = open(fname, 'w') @@ -62,16 +61,19 @@ def setup(): make_empty_file(join(TMP_TEST_DIR, "xmod/sub.py")) make_empty_file(join(TMP_TEST_DIR, "pack.py")) make_empty_file(join(TMP_TEST_DIR, "packpyc.pyc")) + sys.path = [TMP_TEST_DIR] def teardown(): """Teardown testenvironment for the module: - Remove tempdir + - restore sys.path """ # Note: we remove the parent test dir, which is the root of all test # subdirs we may have created. Use shutil instead of os.removedirs, so # that non-empty directories are all recursively removed. shutil.rmtree(TMP_TEST_DIR) + sys.path = old_syspath def test_get_init_1(): From bb6dab9470c6f5206b0b49890c81d3061ea3f67a Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Wed, 23 May 2012 23:17:08 +0100 Subject: [PATCH 07/10] For doctests, globals must now be a real dictionary. --- IPython/testing/globalipapp.py | 62 ----------------------------- IPython/testing/plugin/ipdoctest.py | 17 ++++---- 2 files changed, 7 insertions(+), 72 deletions(-) diff --git a/IPython/testing/globalipapp.py b/IPython/testing/globalipapp.py index 44717ecd12b..82f752ccaa0 100644 --- a/IPython/testing/globalipapp.py +++ b/IPython/testing/globalipapp.py @@ -84,67 +84,6 @@ def _run_ns_sync(self,arg_s,runner=None): return get_ipython().magic_run_ori(arg_s, runner, finder) -class ipnsdict(dict): - """A special subclass of dict for use as an IPython namespace in doctests. - - This subclass adds a simple checkpointing capability so that when testing - machinery clears it (we use it as the test execution context), it doesn't - get completely destroyed. - - In addition, it can handle the presence of the '_' key in a special manner, - which is needed because of how Python's doctest machinery operates with - '_'. See constructor and :meth:`update` for details. - """ - - def __init__(self,*a): - dict.__init__(self,*a) - self._savedict = {} - # If this flag is True, the .update() method will unconditionally - # remove a key named '_'. This is so that such a dict can be used as a - # namespace in doctests that call '_'. - self.protect_underscore = False - - def clear(self): - dict.clear(self) - self.update(self._savedict) - - def _checkpoint(self): - self._savedict.clear() - self._savedict.update(self) - - def update(self,other): - self._checkpoint() - dict.update(self,other) - - if self.protect_underscore: - # If '_' is in the namespace, python won't set it when executing - # code *in doctests*, and we have multiple doctests that use '_'. - # So we ensure that the namespace is always 'clean' of it before - # it's used for test code execution. - # This flag is only turned on by the doctest machinery, so that - # normal test code can assume the _ key is updated like any other - # key and can test for its presence after cell executions. - self.pop('_', None) - - # The builtins namespace must *always* be the real __builtin__ module, - # else weird stuff happens. The main ipython code does have provisions - # to ensure this after %run, but since in this class we do some - # aggressive low-level cleaning of the execution namespace, we need to - # correct for that ourselves, to ensure consitency with the 'real' - # ipython. - self['__builtins__'] = builtin_mod - - def __delitem__(self, key): - """Part of the test suite checks that we can release all - references to an object. So we need to make sure that we're not - keeping a reference in _savedict.""" - dict.__delitem__(self, key) - try: - del self._savedict[key] - except KeyError: - pass - - def get_ipython(): # This will get replaced by the real thing once we start IPython below return start_ipython() @@ -189,7 +128,6 @@ def start_ipython(): # Create and initialize our test-friendly IPython instance. shell = TerminalInteractiveShell.instance(config=config, - user_ns=ipnsdict(), ) # A few more tweaks needed for playing nicely with doctests... diff --git a/IPython/testing/plugin/ipdoctest.py b/IPython/testing/plugin/ipdoctest.py index 50cc72be2c2..1571e31a9ea 100644 --- a/IPython/testing/plugin/ipdoctest.py +++ b/IPython/testing/plugin/ipdoctest.py @@ -19,7 +19,7 @@ # Module imports # From the standard library -import __builtin__ +import __builtin__ as builtin_mod import commands import doctest import inspect @@ -267,18 +267,18 @@ def runTest(self): def setUp(self): """Modified test setup that syncs with ipython namespace""" #print "setUp test", self._dt_test.examples # dbg - if isinstance(self._dt_test.examples[0],IPExample): + if isinstance(self._dt_test.examples[0], IPExample): # for IPython examples *only*, we swap the globals with the ipython # namespace, after updating it with the globals (which doctest # fills with the necessary info from the module being tested). self.user_ns_orig = {} self.user_ns_orig.update(_ip.user_ns) _ip.user_ns.update(self._dt_test.globs) + # We must remove the _ key in the namespace, so that Python's + # doctest code sets it naturally + _ip.user_ns.pop('_', None) + _ip.user_ns['__builtins__'] = builtin_mod self._dt_test.globs = _ip.user_ns - # IPython must protect the _ key in the namespace (it can't exist) - # so that Python's doctest code sets it naturally, so we enable - # this feature of our testing namespace. - _ip.user_ns.protect_underscore = True super(DocTestCase, self).setUp() @@ -286,13 +286,10 @@ def tearDown(self): # Undo the test.globs reassignment we made, so that the parent class # teardown doesn't destroy the ipython namespace - if isinstance(self._dt_test.examples[0],IPExample): + if isinstance(self._dt_test.examples[0], IPExample): self._dt_test.globs = self._dt_test_globs_ori _ip.user_ns.clear() _ip.user_ns.update(self.user_ns_orig) - # Restore the behavior of the '_' key in the user namespace to - # normal after each doctest, so that unittests behave normally - _ip.user_ns.protect_underscore = False # XXX - fperez: I am not sure if this is truly a bug in nose 0.11, but # it does look like one to me: its tearDown method tries to run From 463265ca8b86dda8e3ba1b43ec69bcacbbffce77 Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Wed, 23 May 2012 23:39:11 +0100 Subject: [PATCH 08/10] Call invalidate_caches() when testing installing extension. --- IPython/core/tests/test_magic.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/IPython/core/tests/test_magic.py b/IPython/core/tests/test_magic.py index 296d8953469..82101303ea4 100644 --- a/IPython/core/tests/test_magic.py +++ b/IPython/core/tests/test_magic.py @@ -15,6 +15,12 @@ from StringIO import StringIO from unittest import TestCase +try: + from importlib import invalidate_caches # Required from Python 3.3 +except ImportError: + def invalidate_caches(): + pass + import nose.tools as nt from IPython.core import magic @@ -449,6 +455,7 @@ def test_extension(): url = os.path.join(os.path.dirname(__file__), "daft_extension.py") _ip.magic("install_ext %s" % url) _ip.user_ns.pop('arq', None) + invalidate_caches() # Clear import caches _ip.magic("load_ext daft_extension") tt.assert_equal(_ip.user_ns['arq'], 185) _ip.magic("unload_ext daft_extension") From f0a0234df558f424bb3d077097c16aa97df2ae82 Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Wed, 23 May 2012 23:39:36 +0100 Subject: [PATCH 09/10] Ensure modules for autoreload have a __loader__ attribute. --- IPython/extensions/autoreload.py | 1 + 1 file changed, 1 insertion(+) diff --git a/IPython/extensions/autoreload.py b/IPython/extensions/autoreload.py index 6ca46356db6..9a9354aa4f5 100644 --- a/IPython/extensions/autoreload.py +++ b/IPython/extensions/autoreload.py @@ -372,6 +372,7 @@ def superreload(module, reload=reload, old_objects={}): old_name = module.__name__ module.__dict__.clear() module.__dict__['__name__'] = old_name + module.__dict__['__loader__'] = old_dict['__loader__'] except (TypeError, AttributeError, KeyError): pass From cf0836b24d3ca90d6b5913deaf464407c14dbb4e Mon Sep 17 00:00:00 2001 From: Thomas Kluyver Date: Sun, 27 May 2012 10:51:49 +0100 Subject: [PATCH 10/10] timeit.template uses new-style string formatting from Python 3.3 --- IPython/core/magics/execution.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/IPython/core/magics/execution.py b/IPython/core/magics/execution.py index 3dab02bffc3..177403f3634 100644 --- a/IPython/core/magics/execution.py +++ b/IPython/core/magics/execution.py @@ -774,7 +774,12 @@ def timeit(self, line='', cell=None): setup = timeit.reindent(stmt, 4) stmt = timeit.reindent(cell, 8) - src = timeit.template % dict(stmt=stmt, setup=setup) + # From Python 3.3, this template uses new-style string formatting. + if sys.version_info >= (3, 3): + src = timeit.template.format(stmt=stmt, setup=setup) + else: + src = timeit.template % dict(stmt=stmt, setup=setup) + # Track compilation time so it can be reported if too long # Minimum time above which compilation time will be reported tc_min = 0.1