From ed2f210ebe4baf5569eaa1d8bd1341075e89a444 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Thu, 31 Dec 2009 19:26:22 -0800 Subject: [PATCH 01/27] Add new testing support machinery with better parametric tests. Also included are new tools for doctests with ipython syntax. --- IPython/testing/iptest.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/IPython/testing/iptest.py b/IPython/testing/iptest.py index e2297874f..c2cf104ce 100644 --- a/IPython/testing/iptest.py +++ b/IPython/testing/iptest.py @@ -125,8 +125,6 @@ def make_exclude(): 'test_asyncfrontendbase')), EXCLUDE.append(pjoin('IPython', 'testing', 'parametric')) EXCLUDE.append(pjoin('IPython', 'testing', 'util')) - EXCLUDE.append(pjoin('IPython', 'testing', 'tests', - 'test_decorators_trial')) # This is needed for the reg-exp to match on win32 in the ipdoctest plugin. if sys.platform == 'win32': From dd4405c64683f17db226e1511c2699148a34fc3a Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Wed, 6 Jan 2010 01:56:27 -0800 Subject: [PATCH 02/27] Progress towards getting the test suite in shape again. Work all over the place to get more tests to pass. --- IPython/testing/iptest.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/IPython/testing/iptest.py b/IPython/testing/iptest.py index c2cf104ce..89a46a0a3 100644 --- a/IPython/testing/iptest.py +++ b/IPython/testing/iptest.py @@ -234,7 +234,12 @@ class IPTester(object): else: def run(self): """Run the stored commands""" - return subprocess.call(self.call_args) + try: + return subprocess.call(self.call_args) + except: + import traceback + traceback.print_exc() + return 1 # signal failure def make_runners(): From dabdb5361a140c7a545178453562d12978cbe7b1 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Thu, 7 Jan 2010 16:04:08 -0800 Subject: [PATCH 03/27] Work in multiple places to improve state of the test suite. With these changes, on my system now all the test sub-suites pass except for the Twisted one (see https://bugs.launchpad.net/ipython/+bug/504515 for details on that one). --- IPython/testing/iptest.py | 194 +++++++++++++++++++++----------------- 1 file changed, 107 insertions(+), 87 deletions(-) diff --git a/IPython/testing/iptest.py b/IPython/testing/iptest.py index 89a46a0a3..6c64a9f3d 100644 --- a/IPython/testing/iptest.py +++ b/IPython/testing/iptest.py @@ -31,11 +31,19 @@ import warnings import nose.plugins.builtin from nose.core import TestProgram -from IPython.utils.platutils import find_cmd -# from IPython.testing.plugin.ipdoctest import IPythonDoctest +from IPython.utils import genutils +from IPython.utils.platutils import find_cmd, FindCmdError pjoin = path.join +#----------------------------------------------------------------------------- +# Warnings control +#----------------------------------------------------------------------------- +# Twisted generates annoying warnings with Python 2.6, as will do other code +# that imports 'sets' as of today +warnings.filterwarnings('ignore', 'the sets module is deprecated', + DeprecationWarning ) + #----------------------------------------------------------------------------- # Logic for skipping doctests #----------------------------------------------------------------------------- @@ -63,10 +71,10 @@ have_gobject = test_for('gobject') def make_exclude(): - # For the IPythonDoctest plugin, we need to exclude certain patterns that cause - # testing problems. We should strive to minimize the number of skipped - # modules, since this means untested code. As the testing machinery - # solidifies, this list should eventually become empty. + # For the IPythonDoctest plugin, we need to exclude certain patterns that + # cause testing problems. We should strive to minimize the number of + # skipped modules, since this means untested code. As the testing + # machinery solidifies, this list should eventually become empty. EXCLUDE = [pjoin('IPython', 'external'), pjoin('IPython', 'frontend', 'process', 'winprocess.py'), pjoin('IPython_doctest_plugin'), @@ -137,6 +145,82 @@ def make_exclude(): # Functions and classes #----------------------------------------------------------------------------- +class IPTester(object): + """Call that calls iptest or trial in a subprocess. + """ + def __init__(self,runner='iptest',params=None): + """ """ + if runner == 'iptest': + # Find our own 'iptest' script OS-level entry point + try: + iptest_path = find_cmd('iptest') + except FindCmdError: + # Script not installed (may be the case for testing situations + # that are running from a source tree only), pull from internal + # path: + iptest_path = pjoin(genutils.get_ipython_package_dir(), + 'scripts','iptest') + self.runner = [iptest_path,'-v'] + else: + self.runner = [find_cmd('trial')] + if params is None: + params = [] + if isinstance(params,str): + params = [params] + self.params = params + + # Assemble call + self.call_args = self.runner+self.params + + if sys.platform == 'win32': + def _run_cmd(self): + # On Windows, use os.system instead of subprocess.call, because I + # was having problems with subprocess and I just don't know enough + # about win32 to debug this reliably. Os.system may be the 'old + # fashioned' way to do it, but it works just fine. If someone + # later can clean this up that's fine, as long as the tests run + # reliably in win32. + return os.system(' '.join(self.call_args)) + else: + def _run_cmd(self): + return subprocess.call(self.call_args) + + def run(self): + """Run the stored commands""" + try: + return self._run_cmd() + except: + import traceback + traceback.print_exc() + return 1 # signal failure + + +def make_runners(): + """Define the top-level packages that need to be tested. + """ + + nose_packages = ['config', 'core', 'extensions', 'frontend', 'lib', + 'scripts', 'testing', 'utils'] + trial_packages = ['kernel'] + #trial_packages = [] # dbg + + if have_wx: + nose_packages.append('gui') + + nose_packages = ['IPython.%s' % m for m in nose_packages ] + trial_packages = ['IPython.%s' % m for m in trial_packages ] + + # Make runners, most with nose + nose_testers = [IPTester(params=v) for v in nose_packages] + runners = dict(zip(nose_packages, nose_testers)) + # And add twisted ones if conditions are met + if have_zi and have_twisted and have_foolscap: + trial_testers = [IPTester('trial',params=v) for v in trial_packages] + runners.update(dict(zip(trial_packages,trial_testers))) + + return runners + + def run_iptest(): """Run the IPython test suite using nose. @@ -194,81 +278,6 @@ def run_iptest(): TestProgram(argv=argv,plugins=plugins) -class IPTester(object): - """Call that calls iptest or trial in a subprocess. - """ - def __init__(self,runner='iptest',params=None): - """ """ - if runner == 'iptest': - self.runner = ['iptest','-v'] - else: - self.runner = [find_cmd('trial')] - if params is None: - params = [] - if isinstance(params,str): - params = [params] - self.params = params - - # Assemble call - self.call_args = self.runner+self.params - - if sys.platform == 'win32': - def run(self): - """Run the stored commands""" - # On Windows, cd to temporary directory to run tests. Otherwise, - # Twisted's trial may not be able to execute 'trial IPython', since - # it will confuse the IPython module name with the ipython - # execution scripts, because the windows file system isn't case - # sensitive. - # We also use os.system instead of subprocess.call, because I was - # having problems with subprocess and I just don't know enough - # about win32 to debug this reliably. Os.system may be the 'old - # fashioned' way to do it, but it works just fine. If someone - # later can clean this up that's fine, as long as the tests run - # reliably in win32. - curdir = os.getcwd() - os.chdir(tempfile.gettempdir()) - stat = os.system(' '.join(self.call_args)) - os.chdir(curdir) - return stat - else: - def run(self): - """Run the stored commands""" - try: - return subprocess.call(self.call_args) - except: - import traceback - traceback.print_exc() - return 1 # signal failure - - -def make_runners(): - """Define the top-level packages that need to be tested. - """ - - nose_packages = ['config', 'core', 'extensions', - 'frontend', 'lib', - 'scripts', 'testing', 'utils'] - trial_packages = ['kernel'] - - if have_wx: - nose_packages.append('gui') - - nose_packages = ['IPython.%s' % m for m in nose_packages ] - trial_packages = ['IPython.%s' % m for m in trial_packages ] - - # Make runners - runners = dict() - - nose_runners = dict(zip(nose_packages, [IPTester(params=v) for v in nose_packages])) - if have_zi and have_twisted and have_foolscap: - trial_runners = dict(zip(trial_packages, [IPTester('trial',params=v) for v in trial_packages])) - runners.update(nose_runners) - runners.update(trial_runners) - - return runners - - def run_iptestall(): """Run the entire IPython test suite by calling nose and trial. @@ -280,15 +289,26 @@ def run_iptestall(): runners = make_runners() + # Run the test runners in a temporary dir so we can nuke it when finished + # to clean up any junk files left over by accident. This also makes it + # robust against being run in non-writeable directories by mistake, as the + # temp dir will always be user-writeable. + curdir = os.getcwd() + testdir = tempfile.gettempdir() + os.chdir(testdir) + # Run all test runners, tracking execution time failed = {} t_start = time.time() - for name,runner in runners.iteritems(): - print '*'*77 - print 'IPython test group:',name - res = runner.run() - if res: - failed[name] = res + try: + for name,runner in runners.iteritems(): + print '*'*77 + print 'IPython test group:',name + res = runner.run() + if res: + failed[name] = res + finally: + os.chdir(curdir) t_end = time.time() t_tests = t_end - t_start nrunners = len(runners) From e68e992d80124726025d72d977c6dfb98433a3ad Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Thu, 7 Jan 2010 16:47:46 -0800 Subject: [PATCH 04/27] Improve test suite robustness by cleaning up stale processes when possible. --- IPython/testing/iptest.py | 41 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/IPython/testing/iptest.py b/IPython/testing/iptest.py index 6c64a9f3d..91034a8e0 100644 --- a/IPython/testing/iptest.py +++ b/IPython/testing/iptest.py @@ -22,6 +22,7 @@ will change in the future. import os import os.path as path +import signal import sys import subprocess import tempfile @@ -148,8 +149,17 @@ def make_exclude(): class IPTester(object): """Call that calls iptest or trial in a subprocess. """ + #: string, name of test runner that will be called + runner = None + #: list, parameters for test runner + params = None + #: list, arguments of system call to be made to call test runner + call_args = None + #: list, process ids of subprocesses we start (for cleanup) + pids = None + def __init__(self,runner='iptest',params=None): - """ """ + """Create new test runner.""" if runner == 'iptest': # Find our own 'iptest' script OS-level entry point try: @@ -172,6 +182,10 @@ class IPTester(object): # Assemble call self.call_args = self.runner+self.params + # Store pids of anything we start to clean up on deletion, if possible + # (on posix only, since win32 has no os.kill) + self.pids = [] + if sys.platform == 'win32': def _run_cmd(self): # On Windows, use os.system instead of subprocess.call, because I @@ -183,7 +197,14 @@ class IPTester(object): return os.system(' '.join(self.call_args)) else: def _run_cmd(self): - return subprocess.call(self.call_args) + subp = subprocess.Popen(self.call_args) + self.pids.append(subp.pid) + # If this fails, the pid will be left in self.pids and cleaned up + # later, but if the wait call succeeds, then we can clear the + # stored pid. + retcode = subp.wait() + self.pids.pop() + return retcode def run(self): """Run the stored commands""" @@ -194,6 +215,22 @@ class IPTester(object): traceback.print_exc() return 1 # signal failure + def __del__(self): + """Cleanup on exit by killing any leftover processes.""" + + if not hasattr(os, 'kill'): + return + + for pid in self.pids: + try: + print 'Cleaning stale PID:', pid + os.kill(pid, signal.SIGKILL) + except OSError: + # This is just a best effort, if we fail or the process was + # really gone, ignore it. + pass + + def make_runners(): """Define the top-level packages that need to be tested. From 6d8161fac0151dd01fca8eb168a4764c11215a1a Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Thu, 7 Jan 2010 17:39:24 -0800 Subject: [PATCH 05/27] A few small fixes so ipythonx works, and PEP-8 cleanups I found along the way. --- IPython/testing/iptest.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/IPython/testing/iptest.py b/IPython/testing/iptest.py index 91034a8e0..c3d5ca658 100644 --- a/IPython/testing/iptest.py +++ b/IPython/testing/iptest.py @@ -228,9 +228,8 @@ class IPTester(object): except OSError: # This is just a best effort, if we fail or the process was # really gone, ignore it. - pass - - + pass + def make_runners(): """Define the top-level packages that need to be tested. From 72cb0a3c73434fa00bd16e543f6b3468c3b65935 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Sat, 9 Jan 2010 16:30:59 -0800 Subject: [PATCH 06/27] Massive amount of work to improve the test suite, restores doctests. After Brian's comments, I realized that our test machinery was NOT in reality running all the ipython-syntax doctests we have. This is now fixed. The test suite isn't completely passing, but this commit is for the underlying machinery. I will now work on fixing as many broken tests as I can. Fixes https://bugs.launchpad.net/ipython/+bug/505071 --- IPython/testing/iptest.py | 96 +++++++++++++++++++++------------------ 1 file changed, 53 insertions(+), 43 deletions(-) diff --git a/IPython/testing/iptest.py b/IPython/testing/iptest.py index c3d5ca658..2ca9591e3 100644 --- a/IPython/testing/iptest.py +++ b/IPython/testing/iptest.py @@ -16,6 +16,8 @@ For now, this script requires that both nose and twisted are installed. This will change in the future. """ +from __future__ import absolute_import + #----------------------------------------------------------------------------- # Module imports #----------------------------------------------------------------------------- @@ -34,6 +36,8 @@ from nose.core import TestProgram from IPython.utils import genutils from IPython.utils.platutils import find_cmd, FindCmdError +from . import globalipapp +from .plugin.ipdoctest import IPythonDoctest pjoin = path.join @@ -76,7 +80,11 @@ def make_exclude(): # cause testing problems. We should strive to minimize the number of # skipped modules, since this means untested code. As the testing # machinery solidifies, this list should eventually become empty. - EXCLUDE = [pjoin('IPython', 'external'), + + # Note that these exclusions only mean that the docstrings are not analyzed + # for examples to be run as tests, if there are other test functions in + # those modules, they do get run. + exclusions = [pjoin('IPython', 'external'), pjoin('IPython', 'frontend', 'process', 'winprocess.py'), pjoin('IPython_doctest_plugin'), pjoin('IPython', 'quarantine'), @@ -88,58 +96,58 @@ def make_exclude(): ] if not have_wx: - EXCLUDE.append(pjoin('IPython', 'gui')) - EXCLUDE.append(pjoin('IPython', 'frontend', 'wx')) - EXCLUDE.append(pjoin('IPython', 'lib', 'inputhookwx')) + exclusions.append(pjoin('IPython', 'gui')) + exclusions.append(pjoin('IPython', 'frontend', 'wx')) + exclusions.append(pjoin('IPython', 'lib', 'inputhookwx')) if not have_gtk or not have_gobject: - EXCLUDE.append(pjoin('IPython', 'lib', 'inputhookgtk')) + exclusions.append(pjoin('IPython', 'lib', 'inputhookgtk')) if not have_wx_aui: - EXCLUDE.append(pjoin('IPython', 'gui', 'wx', 'wxIPython')) + exclusions.append(pjoin('IPython', 'gui', 'wx', 'wxIPython')) if not have_objc: - EXCLUDE.append(pjoin('IPython', 'frontend', 'cocoa')) + exclusions.append(pjoin('IPython', 'frontend', 'cocoa')) if not sys.platform == 'win32': - EXCLUDE.append(pjoin('IPython', 'utils', 'platutils_win32')) + exclusions.append(pjoin('IPython', 'utils', 'platutils_win32')) # These have to be skipped on win32 because the use echo, rm, cd, etc. # See ticket https://bugs.launchpad.net/bugs/366982 if sys.platform == 'win32': - EXCLUDE.append(pjoin('IPython', 'testing', 'plugin', 'test_exampleip')) - EXCLUDE.append(pjoin('IPython', 'testing', 'plugin', 'dtexample')) + exclusions.append(pjoin('IPython', 'testing', 'plugin', 'test_exampleip')) + exclusions.append(pjoin('IPython', 'testing', 'plugin', 'dtexample')) if not os.name == 'posix': - EXCLUDE.append(pjoin('IPython', 'utils', 'platutils_posix')) + exclusions.append(pjoin('IPython', 'utils', 'platutils_posix')) if not have_pexpect: - EXCLUDE.append(pjoin('IPython', 'scripts', 'irunner')) + exclusions.append(pjoin('IPython', 'scripts', 'irunner')) # This is scary. We still have things in frontend and testing that # are being tested by nose that use twisted. We need to rethink # how we are isolating dependencies in testing. if not (have_twisted and have_zi and have_foolscap): - EXCLUDE.append(pjoin('IPython', 'frontend', 'asyncfrontendbase')) - EXCLUDE.append(pjoin('IPython', 'frontend', 'prefilterfrontend')) - EXCLUDE.append(pjoin('IPython', 'frontend', 'frontendbase')) - EXCLUDE.append(pjoin('IPython', 'frontend', 'linefrontendbase')) - EXCLUDE.append(pjoin('IPython', 'frontend', 'tests', + exclusions.append(pjoin('IPython', 'frontend', 'asyncfrontendbase')) + exclusions.append(pjoin('IPython', 'frontend', 'prefilterfrontend')) + exclusions.append(pjoin('IPython', 'frontend', 'frontendbase')) + exclusions.append(pjoin('IPython', 'frontend', 'linefrontendbase')) + exclusions.append(pjoin('IPython', 'frontend', 'tests', 'test_linefrontend')) - EXCLUDE.append(pjoin('IPython', 'frontend', 'tests', + exclusions.append(pjoin('IPython', 'frontend', 'tests', 'test_frontendbase')) - EXCLUDE.append(pjoin('IPython', 'frontend', 'tests', + exclusions.append(pjoin('IPython', 'frontend', 'tests', 'test_prefilterfrontend')) - EXCLUDE.append(pjoin('IPython', 'frontend', 'tests', + exclusions.append(pjoin('IPython', 'frontend', 'tests', 'test_asyncfrontendbase')), - EXCLUDE.append(pjoin('IPython', 'testing', 'parametric')) - EXCLUDE.append(pjoin('IPython', 'testing', 'util')) + exclusions.append(pjoin('IPython', 'testing', 'parametric')) + exclusions.append(pjoin('IPython', 'testing', 'util')) # This is needed for the reg-exp to match on win32 in the ipdoctest plugin. if sys.platform == 'win32': - EXCLUDE = [s.replace('\\','\\\\') for s in EXCLUDE] + exclusions = [s.replace('\\','\\\\') for s in exclusions] - return EXCLUDE + return exclusions #----------------------------------------------------------------------------- @@ -163,16 +171,16 @@ class IPTester(object): if runner == 'iptest': # Find our own 'iptest' script OS-level entry point try: - iptest_path = find_cmd('iptest') + iptest_path = os.path.abspath(find_cmd('iptest')) except FindCmdError: # Script not installed (may be the case for testing situations # that are running from a source tree only), pull from internal # path: iptest_path = pjoin(genutils.get_ipython_package_dir(), 'scripts','iptest') - self.runner = [iptest_path,'-v'] + self.runner = ['python', iptest_path, '-v'] else: - self.runner = [find_cmd('trial')] + self.runner = ['python', os.path.abspath(find_cmd('trial'))] if params is None: params = [] if isinstance(params,str): @@ -238,11 +246,13 @@ def make_runners(): nose_packages = ['config', 'core', 'extensions', 'frontend', 'lib', 'scripts', 'testing', 'utils'] trial_packages = ['kernel'] - #trial_packages = [] # dbg if have_wx: nose_packages.append('gui') + #nose_packages = ['core'] # dbg + #trial_packages = [] # dbg + nose_packages = ['IPython.%s' % m for m in nose_packages ] trial_packages = ['IPython.%s' % m for m in trial_packages ] @@ -268,16 +278,15 @@ def run_iptest(): warnings.filterwarnings('ignore', 'This will be removed soon. Use IPython.testing.util instead') - argv = sys.argv + [ - # Loading ipdoctest causes problems with Twisted. - # I am removing this as a temporary fix to get the - # test suite back into working shape. Our nose - # plugin needs to be gone through with a fine - # toothed comb to find what is causing the problem. - # '--with-ipdoctest', - # '--ipdoctest-tests','--ipdoctest-extension=txt', - # '--detailed-errors', - + argv = sys.argv + [ '--detailed-errors', + # Loading ipdoctest causes problems with Twisted, but + # our test suite runner now separates things and runs + # all Twisted tests with trial. + '--with-ipdoctest', + '--ipdoctest-tests','--ipdoctest-extension=txt', + + #'-x','-s', # dbg + # We add --exe because of setuptools' imbecility (it # blindly does chmod +x on ALL files). Nose does the # right thing and it tries to avoid executables, @@ -300,17 +309,18 @@ def run_iptest(): if not has_tests: argv.append('IPython') - # Construct list of plugins, omitting the existing doctest plugin, which - # ours replaces (and extends). - EXCLUDE = make_exclude() - plugins = [] - # plugins = [IPythonDoctest(EXCLUDE)] + ## # Construct list of plugins, omitting the existing doctest plugin, which + ## # ours replaces (and extends). + plugins = [IPythonDoctest(make_exclude())] for p in nose.plugins.builtin.plugins: plug = p() if plug.name == 'doctest': continue plugins.append(plug) + # We need a global ipython running in this process + globalipapp.start_ipython() + # Now nose can run TestProgram(argv=argv,plugins=plugins) From 22cb80d555bcbf19cabef37d38c3fbfe490617bc Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Sat, 9 Jan 2010 17:55:56 -0800 Subject: [PATCH 07/27] Fix config part of the test suite. --- IPython/testing/iptest.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/IPython/testing/iptest.py b/IPython/testing/iptest.py index 2ca9591e3..84a79849b 100644 --- a/IPython/testing/iptest.py +++ b/IPython/testing/iptest.py @@ -80,20 +80,20 @@ def make_exclude(): # cause testing problems. We should strive to minimize the number of # skipped modules, since this means untested code. As the testing # machinery solidifies, this list should eventually become empty. - - # Note that these exclusions only mean that the docstrings are not analyzed - # for examples to be run as tests, if there are other test functions in - # those modules, they do get run. + # These modules and packages will NOT get scanned by nose at all for tests exclusions = [pjoin('IPython', 'external'), - pjoin('IPython', 'frontend', 'process', 'winprocess.py'), - pjoin('IPython_doctest_plugin'), - pjoin('IPython', 'quarantine'), - pjoin('IPython', 'deathrow'), - pjoin('IPython', 'testing', 'attic'), - pjoin('IPython', 'testing', 'tools'), - pjoin('IPython', 'testing', 'mkdoctests'), - pjoin('IPython', 'lib', 'inputhook') - ] + pjoin('IPython', 'frontend', 'process', 'winprocess.py'), + pjoin('IPython_doctest_plugin'), + pjoin('IPython', 'quarantine'), + pjoin('IPython', 'deathrow'), + pjoin('IPython', 'testing', 'attic'), + pjoin('IPython', 'testing', 'tools'), + pjoin('IPython', 'testing', 'mkdoctests'), + pjoin('IPython', 'lib', 'inputhook'), + # Config files aren't really importable stand-alone + pjoin('IPython', 'config', 'default'), + pjoin('IPython', 'config', 'profile'), + ] if not have_wx: exclusions.append(pjoin('IPython', 'gui')) From f7422d653960daa4f30844ba5353de8f9e0ab5ff Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Sat, 9 Jan 2010 19:31:46 -0800 Subject: [PATCH 08/27] Improve setuptools support. Many thanks to Gael for the code/idea for the solution, see bug page for details. Fixes: https://bugs.launchpad.net/ipython/+bug/504968 --- setup.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/setup.py b/setup.py index e0179efc7..e4047b271 100755 --- a/setup.py +++ b/setup.py @@ -159,6 +159,14 @@ data_files = find_data_files() # Handle dependencies and setuptools specific things #--------------------------------------------------------------------------- +# For some commands, use setuptools. Note that we do NOT list install here! +# If you want a setuptools-enhanced install, just run 'setupegg.py install' +if len(set(('develop', 'sdist', 'release', 'bdist_egg', 'bdist_rpm', + 'bdist', 'bdist_dumb', 'bdist_wininst', 'install_egg_info', + 'build_sphinx', 'egg_info', 'easy_install', 'upload', + )).intersection(sys.argv)) > 0: + import setuptools + # This dict is used for passing extra arguments that are setuptools # specific to setup setuptools_extra_args = {} From 0e94b28cd862069dd9b11b6eeaefb597827cd5dc Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Tue, 12 Jan 2010 12:42:23 -0800 Subject: [PATCH 09/27] Remove accidentally introduced runtime nose dependencies. --- IPython/testing/iptest.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/IPython/testing/iptest.py b/IPython/testing/iptest.py index 84a79849b..e4bc7807d 100644 --- a/IPython/testing/iptest.py +++ b/IPython/testing/iptest.py @@ -22,6 +22,7 @@ from __future__ import absolute_import # Module imports #----------------------------------------------------------------------------- +# Stdlib import os import os.path as path import signal @@ -31,9 +32,16 @@ import tempfile import time import warnings +# Note: monkeypatch! +# We need to monkeypatch a small problem in nose itself first, before importing +# it for actual use. This should get into nose upstream, but its release cycle +# is slow and we need it for our parametric tests to work correctly. +from . import nosepatch +# Now, proceed to import nose itself import nose.plugins.builtin from nose.core import TestProgram +# Our own imports from IPython.utils import genutils from IPython.utils.platutils import find_cmd, FindCmdError from . import globalipapp From 2b7da9a1bd8876ddac150877e1ae3bd4322848b6 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Tue, 12 Jan 2010 17:36:00 -0800 Subject: [PATCH 10/27] Fix test suite when Twisted not available, cleanups to iptest for clarity. --- IPython/testing/iptest.py | 87 +++++++++++++++++++++------------------ 1 file changed, 46 insertions(+), 41 deletions(-) diff --git a/IPython/testing/iptest.py b/IPython/testing/iptest.py index e4bc7807d..3f143516a 100644 --- a/IPython/testing/iptest.py +++ b/IPython/testing/iptest.py @@ -83,73 +83,78 @@ have_gobject = test_for('gobject') def make_exclude(): - - # For the IPythonDoctest plugin, we need to exclude certain patterns that - # cause testing problems. We should strive to minimize the number of - # skipped modules, since this means untested code. As the testing - # machinery solidifies, this list should eventually become empty. - # These modules and packages will NOT get scanned by nose at all for tests - exclusions = [pjoin('IPython', 'external'), - pjoin('IPython', 'frontend', 'process', 'winprocess.py'), + """Make patterns of modules and packages to exclude from testing. + + For the IPythonDoctest plugin, we need to exclude certain patterns that + cause testing problems. We should strive to minimize the number of + skipped modules, since this means untested code. As the testing + machinery solidifies, this list should eventually become empty. + These modules and packages will NOT get scanned by nose at all for tests. + """ + # Simple utility to make IPython paths more readably, we need a lot of + # these below + ipjoin = lambda *paths: pjoin('IPython', *paths) + + exclusions = [ipjoin('external'), + ipjoin('frontend', 'process', 'winprocess.py'), pjoin('IPython_doctest_plugin'), - pjoin('IPython', 'quarantine'), - pjoin('IPython', 'deathrow'), - pjoin('IPython', 'testing', 'attic'), - pjoin('IPython', 'testing', 'tools'), - pjoin('IPython', 'testing', 'mkdoctests'), - pjoin('IPython', 'lib', 'inputhook'), + ipjoin('quarantine'), + ipjoin('deathrow'), + ipjoin('testing', 'attic'), + ipjoin('testing', 'tools'), + ipjoin('testing', 'mkdoctests'), + ipjoin('lib', 'inputhook'), # Config files aren't really importable stand-alone - pjoin('IPython', 'config', 'default'), - pjoin('IPython', 'config', 'profile'), + ipjoin('config', 'default'), + ipjoin('config', 'profile'), ] if not have_wx: - exclusions.append(pjoin('IPython', 'gui')) - exclusions.append(pjoin('IPython', 'frontend', 'wx')) - exclusions.append(pjoin('IPython', 'lib', 'inputhookwx')) + exclusions.append(ipjoin('gui')) + exclusions.append(ipjoin('frontend', 'wx')) + exclusions.append(ipjoin('lib', 'inputhookwx')) if not have_gtk or not have_gobject: - exclusions.append(pjoin('IPython', 'lib', 'inputhookgtk')) + exclusions.append(ipjoin('lib', 'inputhookgtk')) if not have_wx_aui: - exclusions.append(pjoin('IPython', 'gui', 'wx', 'wxIPython')) + exclusions.append(ipjoin('gui', 'wx', 'wxIPython')) if not have_objc: - exclusions.append(pjoin('IPython', 'frontend', 'cocoa')) + exclusions.append(ipjoin('frontend', 'cocoa')) if not sys.platform == 'win32': - exclusions.append(pjoin('IPython', 'utils', 'platutils_win32')) + exclusions.append(ipjoin('utils', 'platutils_win32')) # These have to be skipped on win32 because the use echo, rm, cd, etc. # See ticket https://bugs.launchpad.net/bugs/366982 if sys.platform == 'win32': - exclusions.append(pjoin('IPython', 'testing', 'plugin', 'test_exampleip')) - exclusions.append(pjoin('IPython', 'testing', 'plugin', 'dtexample')) + exclusions.append(ipjoin('testing', 'plugin', 'test_exampleip')) + exclusions.append(ipjoin('testing', 'plugin', 'dtexample')) if not os.name == 'posix': - exclusions.append(pjoin('IPython', 'utils', 'platutils_posix')) + exclusions.append(ipjoin('utils', 'platutils_posix')) if not have_pexpect: - exclusions.append(pjoin('IPython', 'scripts', 'irunner')) + exclusions.append(ipjoin('scripts', 'irunner')) # This is scary. We still have things in frontend and testing that # are being tested by nose that use twisted. We need to rethink # how we are isolating dependencies in testing. if not (have_twisted and have_zi and have_foolscap): - exclusions.append(pjoin('IPython', 'frontend', 'asyncfrontendbase')) - exclusions.append(pjoin('IPython', 'frontend', 'prefilterfrontend')) - exclusions.append(pjoin('IPython', 'frontend', 'frontendbase')) - exclusions.append(pjoin('IPython', 'frontend', 'linefrontendbase')) - exclusions.append(pjoin('IPython', 'frontend', 'tests', - 'test_linefrontend')) - exclusions.append(pjoin('IPython', 'frontend', 'tests', - 'test_frontendbase')) - exclusions.append(pjoin('IPython', 'frontend', 'tests', - 'test_prefilterfrontend')) - exclusions.append(pjoin('IPython', 'frontend', 'tests', - 'test_asyncfrontendbase')), - exclusions.append(pjoin('IPython', 'testing', 'parametric')) - exclusions.append(pjoin('IPython', 'testing', 'util')) + exclusions.extend( + [ipjoin('frontend', 'asyncfrontendbase'), + ipjoin('frontend', 'prefilterfrontend'), + ipjoin('frontend', 'frontendbase'), + ipjoin('frontend', 'linefrontendbase'), + ipjoin('frontend', 'tests', 'test_linefrontend'), + ipjoin('frontend', 'tests', 'test_frontendbase'), + ipjoin('frontend', 'tests', 'test_prefilterfrontend'), + ipjoin('frontend', 'tests', 'test_asyncfrontendbase'), + ipjoin('testing', 'parametric'), + ipjoin('testing', 'util'), + ipjoin('testing', 'tests', 'test_decorators_trial'), + ] ) # This is needed for the reg-exp to match on win32 in the ipdoctest plugin. if sys.platform == 'win32': From e3a6f8da77382682b507d85824c6f83e2f56e760 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Tue, 12 Jan 2010 17:39:42 -0800 Subject: [PATCH 11/27] Fix test failure when pexpect not available --- IPython/testing/iptest.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/IPython/testing/iptest.py b/IPython/testing/iptest.py index 3f143516a..d32f802f6 100644 --- a/IPython/testing/iptest.py +++ b/IPython/testing/iptest.py @@ -70,6 +70,7 @@ def test_for(mod): else: return True + have_curses = test_for('_curses') have_wx = test_for('wx') have_wx_aui = test_for('wx.aui') @@ -136,7 +137,8 @@ def make_exclude(): exclusions.append(ipjoin('utils', 'platutils_posix')) if not have_pexpect: - exclusions.append(ipjoin('scripts', 'irunner')) + exclusions.extend([ipjoin('scripts', 'irunner'), + ipjoin('lib', 'irunner')]) # This is scary. We still have things in frontend and testing that # are being tested by nose that use twisted. We need to rethink From 56d74fedba70e93ecdc771d5c8cdcd38fa9956c0 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Tue, 12 Jan 2010 21:03:16 -0800 Subject: [PATCH 12/27] Move cleanup to main setup.py, where it belongs. Distutils now generates .egg-info stuff even without setuptools, so we should do the cleanup in the main script. --- setup.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index e4047b271..c81f2a321 100755 --- a/setup.py +++ b/setup.py @@ -19,6 +19,7 @@ requires utilities which are not available under Windows.""" # Stdlib imports import os +import shutil import sys from glob import glob @@ -43,6 +44,21 @@ from setupbase import ( isfile = os.path.isfile pjoin = os.path.join +#----------------------------------------------------------------------------- +# Function definitions +#----------------------------------------------------------------------------- + +def cleanup(): + """Clean up the junk left around by the build process""" + if "develop" not in sys.argv: + try: + shutil.rmtree('ipython.egg-info') + except: + try: + os.unlink('ipython.egg-info') + except: + pass + #------------------------------------------------------------------------------- # Handle OS specific things #------------------------------------------------------------------------------- @@ -144,7 +160,6 @@ if len(sys.argv) >= 2 and sys.argv[1] in ('sdist','bdist_rpm'): ) [ target_update(*t) for t in to_update ] - #--------------------------------------------------------------------------- # Find all the packages, package data, scripts and data_files @@ -203,7 +218,6 @@ else: # just to make life easy for users. check_for_dependencies() - #--------------------------------------------------------------------------- # Do the actual setup now #--------------------------------------------------------------------------- @@ -214,5 +228,7 @@ setup_args['scripts'] = scripts setup_args['data_files'] = data_files setup_args.update(setuptools_extra_args) + if __name__ == '__main__': setup(**setup_args) + cleanup() From 91791416ab66f3e518bf5315760c09f0f06f8c9b Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Tue, 12 Jan 2010 23:21:21 -0800 Subject: [PATCH 13/27] Fixes for test suite in win32 when all dependencies (esp. Twisted) are installed. Also activated testing.tools to be picked up by the test suite (was excluded), this gives us a few more tests. Status: - On Linux, the full suite passes like before. - On Win32, now that we have Twisted, we're seeing a few failures, because I don't have the WinHPC server stuff. These should be easy for Brian to fix. There are also two tests where the Skip nose exception isn't recognized by Twisted, should also be easy. I'll file tickets for those. --- IPython/testing/iptest.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/IPython/testing/iptest.py b/IPython/testing/iptest.py index d32f802f6..3bcefa744 100644 --- a/IPython/testing/iptest.py +++ b/IPython/testing/iptest.py @@ -45,6 +45,7 @@ from nose.core import TestProgram from IPython.utils import genutils from IPython.utils.platutils import find_cmd, FindCmdError from . import globalipapp +from . import tools from .plugin.ipdoctest import IPythonDoctest pjoin = path.join @@ -57,6 +58,10 @@ pjoin = path.join warnings.filterwarnings('ignore', 'the sets module is deprecated', DeprecationWarning ) +# This one also comes from Twisted +warnings.filterwarnings('ignore', 'the sha module is deprecated', + DeprecationWarning) + #----------------------------------------------------------------------------- # Logic for skipping doctests #----------------------------------------------------------------------------- @@ -102,8 +107,11 @@ def make_exclude(): ipjoin('quarantine'), ipjoin('deathrow'), ipjoin('testing', 'attic'), - ipjoin('testing', 'tools'), + # This guy is probably attic material ipjoin('testing', 'mkdoctests'), + # Testing inputhook will need a lot of thought, to figure out + # how to have tests that don't lock up with the gui event + # loops in the picture ipjoin('lib', 'inputhook'), # Config files aren't really importable stand-alone ipjoin('config', 'default'), @@ -193,9 +201,9 @@ class IPTester(object): # path: iptest_path = pjoin(genutils.get_ipython_package_dir(), 'scripts','iptest') - self.runner = ['python', iptest_path, '-v'] + self.runner = tools.cmd2argv(iptest_path) + ['-v'] else: - self.runner = ['python', os.path.abspath(find_cmd('trial'))] + self.runner = tools.cmd2argv(os.path.abspath(find_cmd('trial'))) if params is None: params = [] if isinstance(params,str): From 59117c597edba98e894b27e055eb77ee1a5d1700 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Tue, 12 Jan 2010 23:58:30 -0800 Subject: [PATCH 14/27] Include 'kernel' in the nose tests as well; it picks up doctests that trial doesn't. --- IPython/testing/iptest.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/IPython/testing/iptest.py b/IPython/testing/iptest.py index 3bcefa744..258573db4 100644 --- a/IPython/testing/iptest.py +++ b/IPython/testing/iptest.py @@ -267,7 +267,11 @@ def make_runners(): """ nose_packages = ['config', 'core', 'extensions', 'frontend', 'lib', - 'scripts', 'testing', 'utils'] + 'scripts', 'testing', 'utils', + # Note that we list the kernel here, though the bulk of it + # is twisted-based, because nose picks up doctests that + # twisted doesn't. + 'kernel'] trial_packages = ['kernel'] if have_wx: From 1621f0a23f5383b244b8ff794328e55f7195da52 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Wed, 13 Jan 2010 00:32:13 -0800 Subject: [PATCH 15/27] Fix bug where python -c "import IPython; IPython.test()" could fail. Depending on where one was located when typing it, it could fail. Made all necessary paths absolute so this doesn't happen. --- IPython/testing/iptest.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/IPython/testing/iptest.py b/IPython/testing/iptest.py index 258573db4..fd2cb318c 100644 --- a/IPython/testing/iptest.py +++ b/IPython/testing/iptest.py @@ -199,8 +199,8 @@ class IPTester(object): # Script not installed (may be the case for testing situations # that are running from a source tree only), pull from internal # path: - iptest_path = pjoin(genutils.get_ipython_package_dir(), - 'scripts','iptest') + pak_dir = os.path.abspath(genutils.get_ipython_package_dir()) + iptest_path = pjoin(pak_dir, 'scripts', 'iptest') self.runner = tools.cmd2argv(iptest_path) + ['-v'] else: self.runner = tools.cmd2argv(os.path.abspath(find_cmd('trial'))) From ae70bfc440936ff218c3efc9c83b8c7618b5eb38 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Fri, 15 Jan 2010 01:06:34 -0800 Subject: [PATCH 16/27] Let iptest pass arguments correctly to nose (in-process or in subprocess). Fixes https://bugs.launchpad.net/ipython/+bug/507079 Also, remove relative imports from the iptest.py module so it can be run as a script as well under 2.5 or 2.6. --- IPython/testing/iptest.py | 81 ++++++++++++++------------------------- 1 file changed, 29 insertions(+), 52 deletions(-) diff --git a/IPython/testing/iptest.py b/IPython/testing/iptest.py index fd2cb318c..c3e3e075e 100644 --- a/IPython/testing/iptest.py +++ b/IPython/testing/iptest.py @@ -16,8 +16,6 @@ For now, this script requires that both nose and twisted are installed. This will change in the future. """ -from __future__ import absolute_import - #----------------------------------------------------------------------------- # Module imports #----------------------------------------------------------------------------- @@ -36,7 +34,7 @@ import warnings # We need to monkeypatch a small problem in nose itself first, before importing # it for actual use. This should get into nose upstream, but its release cycle # is slow and we need it for our parametric tests to work correctly. -from . import nosepatch +from IPython.testing import nosepatch # Now, proceed to import nose itself import nose.plugins.builtin from nose.core import TestProgram @@ -44,9 +42,9 @@ from nose.core import TestProgram # Our own imports from IPython.utils import genutils from IPython.utils.platutils import find_cmd, FindCmdError -from . import globalipapp -from . import tools -from .plugin.ipdoctest import IPythonDoctest +from IPython.testing import globalipapp +from IPython.testing import tools +from IPython.testing.plugin.ipdoctest import IPythonDoctest pjoin = path.join @@ -189,24 +187,18 @@ class IPTester(object): #: list, process ids of subprocesses we start (for cleanup) pids = None - def __init__(self,runner='iptest',params=None): + def __init__(self, runner='iptest', params=None): """Create new test runner.""" if runner == 'iptest': - # Find our own 'iptest' script OS-level entry point - try: - iptest_path = os.path.abspath(find_cmd('iptest')) - except FindCmdError: - # Script not installed (may be the case for testing situations - # that are running from a source tree only), pull from internal - # path: - pak_dir = os.path.abspath(genutils.get_ipython_package_dir()) - iptest_path = pjoin(pak_dir, 'scripts', 'iptest') - self.runner = tools.cmd2argv(iptest_path) + ['-v'] + # Find our own 'iptest' script OS-level entry point. Don't look + # system-wide, so we are sure we pick up *this one*. And pass + # through to subprocess call our own sys.argv + self.runner = tools.cmd2argv(__file__) + sys.argv[1:] else: self.runner = tools.cmd2argv(os.path.abspath(find_cmd('trial'))) if params is None: params = [] - if isinstance(params,str): + if isinstance(params, str): params = [params] self.params = params @@ -272,12 +264,13 @@ def make_runners(): # is twisted-based, because nose picks up doctests that # twisted doesn't. 'kernel'] + # The machinery in kernel needs twisted for real testing trial_packages = ['kernel'] if have_wx: nose_packages.append('gui') - #nose_packages = ['core'] # dbg + #nose_packages = ['config', 'utils'] # dbg #trial_packages = [] # dbg nose_packages = ['IPython.%s' % m for m in nose_packages ] @@ -285,11 +278,12 @@ def make_runners(): # Make runners, most with nose nose_testers = [IPTester(params=v) for v in nose_packages] - runners = dict(zip(nose_packages, nose_testers)) + runners = zip(nose_packages, nose_testers) + # And add twisted ones if conditions are met if have_zi and have_twisted and have_foolscap: - trial_testers = [IPTester('trial',params=v) for v in trial_packages] - runners.update(dict(zip(trial_packages,trial_testers))) + trial_testers = [IPTester('trial', params=v) for v in trial_packages] + runners.extend(zip(trial_packages, trial_testers)) return runners @@ -312,8 +306,6 @@ def run_iptest(): '--with-ipdoctest', '--ipdoctest-tests','--ipdoctest-extension=txt', - #'-x','-s', # dbg - # We add --exe because of setuptools' imbecility (it # blindly does chmod +x on ALL files). Nose does the # right thing and it tries to avoid executables, @@ -323,21 +315,9 @@ def run_iptest(): '--exe', ] - # Detect if any tests were required by explicitly calling an IPython - # submodule or giving a specific path - has_tests = False - for arg in sys.argv: - if 'IPython' in arg or arg.endswith('.py') or \ - (':' in arg and '.py' in arg): - has_tests = True - break - - # If nothing was specifically requested, test full IPython - if not has_tests: - argv.append('IPython') - ## # Construct list of plugins, omitting the existing doctest plugin, which - ## # ours replaces (and extends). + # Construct list of plugins, omitting the existing doctest plugin, which + # ours replaces (and extends). plugins = [IPythonDoctest(make_exclude())] for p in nose.plugins.builtin.plugins: plug = p() @@ -348,7 +328,7 @@ def run_iptest(): # We need a global ipython running in this process globalipapp.start_ipython() # Now nose can run - TestProgram(argv=argv,plugins=plugins) + TestProgram(argv=argv, plugins=plugins) def run_iptestall(): @@ -371,15 +351,15 @@ def run_iptestall(): os.chdir(testdir) # Run all test runners, tracking execution time - failed = {} + failed = [] t_start = time.time() try: - for name,runner in runners.iteritems(): - print '*'*77 + for (name, runner) in runners: + print '*'*70 print 'IPython test group:',name res = runner.run() if res: - failed[name] = res + failed.append( (name, runner) ) finally: os.chdir(curdir) t_end = time.time() @@ -388,7 +368,7 @@ def run_iptestall(): nfail = len(failed) # summarize results print - print '*'*77 + print '*'*70 print 'Ran %s test groups in %.3fs' % (nrunners, t_tests) print if not failed: @@ -397,8 +377,7 @@ def run_iptestall(): # If anything went wrong, point out what command to rerun manually to # see the actual errors and individual summary print 'ERROR - %s out of %s test groups failed.' % (nfail, nrunners) - for name in failed: - failed_runner = runners[name] + for name, failed_runner in failed: print '-'*40 print 'Runner failed:',name print 'You may wish to rerun this one individually, with:' @@ -407,13 +386,11 @@ def run_iptestall(): def main(): - if len(sys.argv) == 1: - run_iptestall() - else: - if sys.argv[1] == 'all': - run_iptestall() - else: + for arg in sys.argv[1:]: + if arg.startswith('IPython'): run_iptest() + else: + run_iptestall() if __name__ == '__main__': From 5756b33e52998cfd4cec77851af3bea9a3f21365 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Fri, 15 Jan 2010 02:00:11 -0800 Subject: [PATCH 17/27] Make it possible to run the tests from the source dir without installation. Also clean up the handling of command-line options for iptest, so that all options given at the top-level are correctly passed to nose. Fixes: https://bugs.launchpad.net/ipython/+bug/507079 --- IPython/testing/iptest.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/IPython/testing/iptest.py b/IPython/testing/iptest.py index c3e3e075e..90aa93e6a 100644 --- a/IPython/testing/iptest.py +++ b/IPython/testing/iptest.py @@ -30,6 +30,17 @@ import tempfile import time import warnings + +# Ugly, but necessary hack to ensure the test suite finds our version of +# IPython and not a possibly different one that may exist system-wide. +# Note that this must be done here, so the imports that come next work +# correctly even if IPython isn't installed yet. +p = os.path +ippath = p.abspath(p.join(p.dirname(__file__),'..','..')) +sys.path.insert(0, ippath) +#print 'ipp:', ippath # dbg +#import IPython; print 'IP file:', IPython.__file__ # dbg + # Note: monkeypatch! # We need to monkeypatch a small problem in nose itself first, before importing # it for actual use. This should get into nose upstream, but its release cycle @@ -101,6 +112,10 @@ def make_exclude(): exclusions = [ipjoin('external'), ipjoin('frontend', 'process', 'winprocess.py'), + # Deprecated old Shell and iplib modules, skip to avoid + # warnings + ipjoin('Shell'), + ipjoin('iplib'), pjoin('IPython_doctest_plugin'), ipjoin('quarantine'), ipjoin('deathrow'), @@ -193,7 +208,8 @@ class IPTester(object): # Find our own 'iptest' script OS-level entry point. Don't look # system-wide, so we are sure we pick up *this one*. And pass # through to subprocess call our own sys.argv - self.runner = tools.cmd2argv(__file__) + sys.argv[1:] + self.runner = tools.cmd2argv(os.path.abspath(__file__)) + \ + sys.argv[1:] else: self.runner = tools.cmd2argv(os.path.abspath(find_cmd('trial'))) if params is None: @@ -388,8 +404,10 @@ def run_iptestall(): def main(): for arg in sys.argv[1:]: if arg.startswith('IPython'): + # This is in-process run_iptest() else: + # This starts subprocesses run_iptestall() From 34dd139174a73549251f695dd08f2a9e3ababcf0 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Fri, 15 Jan 2010 14:41:10 -0800 Subject: [PATCH 18/27] Fixes to make test suite more robust on Fedora. I fond some spurious warnings on Fedora, extra noise on stdout and other small problems this commit fixes. The test suite now runs cleanly on Fedora11 without Twisted available. --- IPython/testing/iptest.py | 47 ++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/IPython/testing/iptest.py b/IPython/testing/iptest.py index 90aa93e6a..df4b41e3d 100644 --- a/IPython/testing/iptest.py +++ b/IPython/testing/iptest.py @@ -71,6 +71,10 @@ warnings.filterwarnings('ignore', 'the sets module is deprecated', warnings.filterwarnings('ignore', 'the sha module is deprecated', DeprecationWarning) +# Wx on Fedora11 spits these out +warnings.filterwarnings('ignore', 'wxPython/wxWidgets release number mismatch', + UserWarning) + #----------------------------------------------------------------------------- # Logic for skipping doctests #----------------------------------------------------------------------------- @@ -274,33 +278,34 @@ def make_runners(): """Define the top-level packages that need to be tested. """ - nose_packages = ['config', 'core', 'extensions', 'frontend', 'lib', - 'scripts', 'testing', 'utils', - # Note that we list the kernel here, though the bulk of it - # is twisted-based, because nose picks up doctests that - # twisted doesn't. - 'kernel'] + # Packages to be tested via nose, that only depend on the stdlib + nose_pkg_names = ['config', 'core', 'extensions', 'frontend', 'lib', + 'scripts', 'testing', 'utils' ] # The machinery in kernel needs twisted for real testing - trial_packages = ['kernel'] + trial_pkg_names = [] if have_wx: - nose_packages.append('gui') - - #nose_packages = ['config', 'utils'] # dbg - #trial_packages = [] # dbg - - nose_packages = ['IPython.%s' % m for m in nose_packages ] - trial_packages = ['IPython.%s' % m for m in trial_packages ] + nose_pkg_names.append('gui') - # Make runners, most with nose - nose_testers = [IPTester(params=v) for v in nose_packages] - runners = zip(nose_packages, nose_testers) - # And add twisted ones if conditions are met if have_zi and have_twisted and have_foolscap: - trial_testers = [IPTester('trial', params=v) for v in trial_packages] - runners.extend(zip(trial_packages, trial_testers)) - + # Note that we list the kernel here, though the bulk of it is + # twisted-based, because nose picks up doctests that twisted doesn't. + nose_pkg_names.append('kernel') + trial_pkg_names.append('kernel') + + # For debugging this code, only load quick stuff + #nose_pkg_names = ['config', 'utils'] # dbg + #trial_pkg_names = [] # dbg + + # Make fully qualified package names prepending 'IPython.' to our name lists + nose_packages = ['IPython.%s' % m for m in nose_pkg_names ] + trial_packages = ['IPython.%s' % m for m in trial_pkg_names ] + + # Make runners + runners = [ (v, IPTester('iptest', params=v)) for v in nose_packages ] + runners.extend([ (v, IPTester('trial', params=v)) for v in trial_packages ]) + return runners From 5740081c62edc4a65663ade421a4728e3dd9c9ea Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Sat, 16 Jan 2010 14:43:41 -0800 Subject: [PATCH 19/27] Fix tests to return consistent results regardless of how they are called. Now, calling: - 'python iptest.py' from source dir or from src/IPython/testing should give the same results - 'python iptest.py IPython.SOMETHING' from either place should also give the same results. --- IPython/testing/iptest.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/IPython/testing/iptest.py b/IPython/testing/iptest.py index df4b41e3d..890009975 100644 --- a/IPython/testing/iptest.py +++ b/IPython/testing/iptest.py @@ -38,8 +38,6 @@ import warnings p = os.path ippath = p.abspath(p.join(p.dirname(__file__),'..','..')) sys.path.insert(0, ippath) -#print 'ipp:', ippath # dbg -#import IPython; print 'IP file:', IPython.__file__ # dbg # Note: monkeypatch! # We need to monkeypatch a small problem in nose itself first, before importing @@ -240,6 +238,7 @@ class IPTester(object): return os.system(' '.join(self.call_args)) else: def _run_cmd(self): + #print >> sys.stderr, '*** CMD:', ' '.join(self.call_args) # dbg subp = subprocess.Popen(self.call_args) self.pids.append(subp.pid) # If this fails, the pid will be left in self.pids and cleaned up @@ -295,7 +294,7 @@ def make_runners(): trial_pkg_names.append('kernel') # For debugging this code, only load quick stuff - #nose_pkg_names = ['config', 'utils'] # dbg + #nose_pkg_names = ['core'] # dbg #trial_pkg_names = [] # dbg # Make fully qualified package names prepending 'IPython.' to our name lists @@ -320,7 +319,20 @@ def run_iptest(): warnings.filterwarnings('ignore', 'This will be removed soon. Use IPython.testing.util instead') - argv = sys.argv + [ '--detailed-errors', + argv = sys.argv + [ '--detailed-errors', # extra info in tracebacks + + # I don't fully understand why we need this one, but + # depending on what directory the test suite is run + # from, if we don't give it, 0 tests get run. + # Specifically, if the test suite is run from the + # source dir with an argument (like 'iptest.py + # IPython.core', 0 tests are run, even if the same call + # done in this directory works fine). It appears that + # if the requested package is in the current dir, + # nose bails early by default. Since it's otherwise + # harmless, leave it in by default. + '--traverse-namespace', + # Loading ipdoctest causes problems with Twisted, but # our test suite runner now separates things and runs # all Twisted tests with trial. From ecff1e9e3991db78cfad52599643f13711f3977e Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Sat, 16 Jan 2010 14:50:08 -0800 Subject: [PATCH 20/27] Make the test suite runnable without X11 connections. This fix makes the test suite work in settings like a screen session that can import but not initialize GTK. --- IPython/testing/iptest.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/IPython/testing/iptest.py b/IPython/testing/iptest.py index 890009975..4d5a9ccff 100644 --- a/IPython/testing/iptest.py +++ b/IPython/testing/iptest.py @@ -81,7 +81,9 @@ def test_for(mod): """Test to see if mod is importable.""" try: __import__(mod) - except ImportError: + except (ImportError, RuntimeError): + # GTK reports Runtime error if it can't be initialized even if it's + # importable. return False else: return True From 4041a88063f8eaee5c544050f28c63ed76d840c6 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Sat, 16 Jan 2010 16:57:22 -0800 Subject: [PATCH 21/27] Inform user at install time of minimal python requirements if not met. Fixes: https://bugs.launchpad.net/ipython/+bug/505090 --- setup.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index c81f2a321..f89324e1d 100755 --- a/setup.py +++ b/setup.py @@ -13,6 +13,23 @@ requires utilities which are not available under Windows.""" # the file COPYING, distributed as part of this software. #------------------------------------------------------------------------------- +#----------------------------------------------------------------------------- +# Minimal Python version sanity check +#----------------------------------------------------------------------------- + +import sys + +# This check is also made in IPython/__init__, don't forget to update both when +# changing Python version requirements. +if sys.version[0:3] < '2.5': + error = """\ +ERROR: 'IPython requires Python Version 2.5 or above.' +Exiting.""" + print >> sys.stderr, error + sys.exit(1) + +# At least we're on Python 2.5 or newer, move on. + #------------------------------------------------------------------------------- # Imports #------------------------------------------------------------------------------- @@ -20,7 +37,6 @@ requires utilities which are not available under Windows.""" # Stdlib imports import os import shutil -import sys from glob import glob @@ -30,6 +46,7 @@ if os.path.exists('MANIFEST'): os.remove('MANIFEST') from distutils.core import setup +# Our own imports from IPython.utils.genutils import target_update from setupbase import ( From e1ea03050461746668a7abe98d9875e5de419131 Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Sun, 17 Jan 2010 13:13:21 -0800 Subject: [PATCH 22/27] Robustness fixes in test suite machinery. Added a module-level INSTALLED flag, which can be set to false if the test suite is being run in-place (without ipython having been installed at all). This is because how we call and import things must be done differently depending on whether the code is installed or is being run in-place. The only ones that can know this reliably are the entry-point scripts, so those are responsible for setting this flag. Also made the code that validates ipython in subprocesses report errors better, by checking stderr for errors before validating stdout output, as anything on stderr will be likely informative of the real problem. --- IPython/testing/iptest.py | 66 ++++++++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 25 deletions(-) diff --git a/IPython/testing/iptest.py b/IPython/testing/iptest.py index 4d5a9ccff..c8bdce8be 100644 --- a/IPython/testing/iptest.py +++ b/IPython/testing/iptest.py @@ -57,6 +57,18 @@ from IPython.testing.plugin.ipdoctest import IPythonDoctest pjoin = path.join + +#----------------------------------------------------------------------------- +# Globals +#----------------------------------------------------------------------------- + +# By default, we assume IPython has been installed. But if the test suite is +# being run from a source tree that has NOT been installed yet, this flag can +# be set to False by the entry point scripts, to let us know that we must call +# the source tree versions of the scripts which manipulate sys.path instead of +# assuming that things exist system-wide. +INSTALLED = True + #----------------------------------------------------------------------------- # Warnings control #----------------------------------------------------------------------------- @@ -88,7 +100,6 @@ def test_for(mod): else: return True - have_curses = test_for('_curses') have_wx = test_for('wx') have_wx_aui = test_for('wx.aui') @@ -100,6 +111,9 @@ have_pexpect = test_for('pexpect') have_gtk = test_for('gtk') have_gobject = test_for('gobject') +#----------------------------------------------------------------------------- +# Functions and classes +#----------------------------------------------------------------------------- def make_exclude(): """Make patterns of modules and packages to exclude from testing. @@ -190,10 +204,6 @@ def make_exclude(): return exclusions -#----------------------------------------------------------------------------- -# Functions and classes -#----------------------------------------------------------------------------- - class IPTester(object): """Call that calls iptest or trial in a subprocess. """ @@ -208,14 +218,22 @@ class IPTester(object): def __init__(self, runner='iptest', params=None): """Create new test runner.""" + p = os.path if runner == 'iptest': - # Find our own 'iptest' script OS-level entry point. Don't look - # system-wide, so we are sure we pick up *this one*. And pass - # through to subprocess call our own sys.argv - self.runner = tools.cmd2argv(os.path.abspath(__file__)) + \ - sys.argv[1:] + if INSTALLED: + self.runner = tools.cmd2argv( + p.abspath(find_cmd('iptest'))) + sys.argv[1:] + else: + # Find our own 'iptest' script OS-level entry point. Don't + # look system-wide, so we are sure we pick up *this one*. And + # pass through to subprocess call our own sys.argv + ippath = p.abspath(p.join(p.dirname(__file__),'..','..')) + script = p.join(ippath, 'iptest.py') + self.runner = tools.cmd2argv(script) + sys.argv[1:] + else: - self.runner = tools.cmd2argv(os.path.abspath(find_cmd('trial'))) + # For trial, it needs to be installed system-wide + self.runner = tools.cmd2argv(p.abspath(find_cmd('trial'))) if params is None: params = [] if isinstance(params, str): @@ -296,7 +314,7 @@ def make_runners(): trial_pkg_names.append('kernel') # For debugging this code, only load quick stuff - #nose_pkg_names = ['core'] # dbg + #nose_pkg_names = ['core', 'extensions'] # dbg #trial_pkg_names = [] # dbg # Make fully qualified package names prepending 'IPython.' to our name lists @@ -322,19 +340,7 @@ def run_iptest(): 'This will be removed soon. Use IPython.testing.util instead') argv = sys.argv + [ '--detailed-errors', # extra info in tracebacks - - # I don't fully understand why we need this one, but - # depending on what directory the test suite is run - # from, if we don't give it, 0 tests get run. - # Specifically, if the test suite is run from the - # source dir with an argument (like 'iptest.py - # IPython.core', 0 tests are run, even if the same call - # done in this directory works fine). It appears that - # if the requested package is in the current dir, - # nose bails early by default. Since it's otherwise - # harmless, leave it in by default. - '--traverse-namespace', - + # Loading ipdoctest causes problems with Twisted, but # our test suite runner now separates things and runs # all Twisted tests with trial. @@ -350,6 +356,16 @@ def run_iptest(): '--exe', ] + if nose.__version__ >= '0.11': + # I don't fully understand why we need this one, but depending on what + # directory the test suite is run from, if we don't give it, 0 tests + # get run. Specifically, if the test suite is run from the source dir + # with an argument (like 'iptest.py IPython.core', 0 tests are run, + # even if the same call done in this directory works fine). It appears + # that if the requested package is in the current dir, nose bails early + # by default. Since it's otherwise harmless, leave it in by default + # for nose >= 0.11, though unfortunately nose 0.10 doesn't support it. + argv.append('--traverse-namespace') # Construct list of plugins, omitting the existing doctest plugin, which # ours replaces (and extends). From 8965698c562cd299d8ac95132ea01c8a36d10baa Mon Sep 17 00:00:00 2001 From: Fernando Perez Date: Sun, 17 Jan 2010 14:36:19 -0800 Subject: [PATCH 23/27] Added diagnostics printout at the end of the test suite. This will make it easier for us to understand problem reports from users. --- IPython/testing/iptest.py | 74 +++++++++++++++++++++++++++++---------- 1 file changed, 56 insertions(+), 18 deletions(-) diff --git a/IPython/testing/iptest.py b/IPython/testing/iptest.py index c8bdce8be..946d2c035 100644 --- a/IPython/testing/iptest.py +++ b/IPython/testing/iptest.py @@ -23,6 +23,7 @@ will change in the future. # Stdlib import os import os.path as path +import platform import signal import sys import subprocess @@ -49,6 +50,7 @@ import nose.plugins.builtin from nose.core import TestProgram # Our own imports +from IPython.core import release from IPython.utils import genutils from IPython.utils.platutils import find_cmd, FindCmdError from IPython.testing import globalipapp @@ -100,21 +102,54 @@ def test_for(mod): else: return True -have_curses = test_for('_curses') -have_wx = test_for('wx') -have_wx_aui = test_for('wx.aui') -have_zi = test_for('zope.interface') -have_twisted = test_for('twisted') -have_foolscap = test_for('foolscap') -have_objc = test_for('objc') -have_pexpect = test_for('pexpect') -have_gtk = test_for('gtk') -have_gobject = test_for('gobject') +# Global dict where we can store information on what we have and what we don't +# have available at test run time +have = {} + +have['curses'] = test_for('_curses') +have['wx'] = test_for('wx') +have['wx.aui'] = test_for('wx.aui') +have['zope.interface'] = test_for('zope.interface') +have['twisted'] = test_for('twisted') +have['foolscap'] = test_for('foolscap') +have['objc'] = test_for('objc') +have['pexpect'] = test_for('pexpect') +have['gtk'] = test_for('gtk') +have['gobject'] = test_for('gobject') #----------------------------------------------------------------------------- # Functions and classes #----------------------------------------------------------------------------- +def report(): + """Return a string with a summary report of test-related variables.""" + + out = [ genutils.sys_info() ] + + out.append('\nRunning from an installed IPython: %s\n' % INSTALLED) + + avail = [] + not_avail = [] + + for k, is_avail in have.items(): + if is_avail: + avail.append(k) + else: + not_avail.append(k) + + if avail: + out.append('\nTools and libraries available at test time:\n') + avail.sort() + out.append(' ' + ' '.join(avail)+'\n') + + if not_avail: + out.append('\nTools and libraries NOT available at test time:\n') + not_avail.sort() + out.append(' ' + ' '.join(not_avail)+'\n') + + return ''.join(out) + + def make_exclude(): """Make patterns of modules and packages to exclude from testing. @@ -149,18 +184,18 @@ def make_exclude(): ipjoin('config', 'profile'), ] - if not have_wx: + if not have['wx']: exclusions.append(ipjoin('gui')) exclusions.append(ipjoin('frontend', 'wx')) exclusions.append(ipjoin('lib', 'inputhookwx')) - if not have_gtk or not have_gobject: + if not have['gtk'] or not have['gobject']: exclusions.append(ipjoin('lib', 'inputhookgtk')) - if not have_wx_aui: + if not have['wx.aui']: exclusions.append(ipjoin('gui', 'wx', 'wxIPython')) - if not have_objc: + if not have['objc']: exclusions.append(ipjoin('frontend', 'cocoa')) if not sys.platform == 'win32': @@ -175,14 +210,14 @@ def make_exclude(): if not os.name == 'posix': exclusions.append(ipjoin('utils', 'platutils_posix')) - if not have_pexpect: + if not have['pexpect']: exclusions.extend([ipjoin('scripts', 'irunner'), ipjoin('lib', 'irunner')]) # This is scary. We still have things in frontend and testing that # are being tested by nose that use twisted. We need to rethink # how we are isolating dependencies in testing. - if not (have_twisted and have_zi and have_foolscap): + if not (have['twisted'] and have['zope.interface'] and have['foolscap']): exclusions.extend( [ipjoin('frontend', 'asyncfrontendbase'), ipjoin('frontend', 'prefilterfrontend'), @@ -303,11 +338,11 @@ def make_runners(): # The machinery in kernel needs twisted for real testing trial_pkg_names = [] - if have_wx: + if have['wx']: nose_pkg_names.append('gui') # And add twisted ones if conditions are met - if have_zi and have_twisted and have_foolscap: + if have['zope.interface'] and have['twisted'] and have['foolscap']: # Note that we list the kernel here, though the bulk of it is # twisted-based, because nose picks up doctests that twisted doesn't. nose_pkg_names.append('kernel') @@ -420,8 +455,11 @@ def run_iptestall(): # summarize results print print '*'*70 + print 'Test suite completed for system with the following information:' + print report() print 'Ran %s test groups in %.3fs' % (nrunners, t_tests) print + print 'Status:' if not failed: print 'OK' else: From e24997b0ba0c9098af480506727bdb8e9c0192f4 Mon Sep 17 00:00:00 2001 From: Brian Granger Date: Fri, 29 Jan 2010 16:24:13 -0800 Subject: [PATCH 24/27] Work to address the review comments on Fernando's branch. * Added comment about Magic(object) (r1224) * Moved InteractiveTB.set_mode from IPythonApp -> InteractiveShell (r1229) * Moved pylabtools.py to IPython/lib (r1229) * Cleaned up comments and copyrights in testing (r1233) * Added comment about ip.shell._ofind (r1237) * Removed "Bye." from quitter (r1240). * Refactored and removed :mod:`IPython.utils.genutils` and :mod:`IPython.utils.platutils`. These modules have been replaced by topical focused modules in :mod:`IPython.utils`. * Refactored tests in :mod:`IPython.utils.tests`. * Moved :func:`IPython.testing.tools.temp_pyfile` to :mod:`IPython.utils.io`. * Moved :func:`IPython.testing.tools.cmd2argv` to :func:`IPython.testing.tools.pycmd2argv` and documented the fact that this only works with Python based command line programs. * Created a new :func:`IPython.utils.path.get_ipython_module_path` to use in finding paths to IPython modules. --- IPython/testing/iptest.py | 34 +++++++++---------- IPython/utils/tests/test_io.py | 61 ++++++++++++++++++++++++++++++++++ setup.py | 2 +- 3 files changed, 79 insertions(+), 18 deletions(-) create mode 100644 IPython/utils/tests/test_io.py diff --git a/IPython/testing/iptest.py b/IPython/testing/iptest.py index 946d2c035..593f46fcf 100644 --- a/IPython/testing/iptest.py +++ b/IPython/testing/iptest.py @@ -17,13 +17,19 @@ will change in the future. """ #----------------------------------------------------------------------------- -# Module imports +# Copyright (C) 2009 The IPython Development Team +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports #----------------------------------------------------------------------------- # Stdlib import os import os.path as path -import platform import signal import sys import subprocess @@ -50,11 +56,11 @@ import nose.plugins.builtin from nose.core import TestProgram # Our own imports -from IPython.core import release -from IPython.utils import genutils -from IPython.utils.platutils import find_cmd, FindCmdError +from IPython.utils.path import get_ipython_module_path +from IPython.utils.process import find_cmd, pycmd2argv +from IPython.utils.sysinfo import sys_info + from IPython.testing import globalipapp -from IPython.testing import tools from IPython.testing.plugin.ipdoctest import IPythonDoctest pjoin = path.join @@ -124,7 +130,7 @@ have['gobject'] = test_for('gobject') def report(): """Return a string with a summary report of test-related variables.""" - out = [ genutils.sys_info() ] + out = [ sys_info() ] out.append('\nRunning from an installed IPython: %s\n' % INSTALLED) @@ -198,18 +204,12 @@ def make_exclude(): if not have['objc']: exclusions.append(ipjoin('frontend', 'cocoa')) - if not sys.platform == 'win32': - exclusions.append(ipjoin('utils', 'platutils_win32')) - # These have to be skipped on win32 because the use echo, rm, cd, etc. # See ticket https://bugs.launchpad.net/bugs/366982 if sys.platform == 'win32': exclusions.append(ipjoin('testing', 'plugin', 'test_exampleip')) exclusions.append(ipjoin('testing', 'plugin', 'dtexample')) - if not os.name == 'posix': - exclusions.append(ipjoin('utils', 'platutils_posix')) - if not have['pexpect']: exclusions.extend([ipjoin('scripts', 'irunner'), ipjoin('lib', 'irunner')]) @@ -256,19 +256,19 @@ class IPTester(object): p = os.path if runner == 'iptest': if INSTALLED: - self.runner = tools.cmd2argv( - p.abspath(find_cmd('iptest'))) + sys.argv[1:] + iptest_app = get_ipython_module_path('IPython.testing.iptest') + self.runner = pycmd2argv(iptest_app) + sys.argv[1:] else: # Find our own 'iptest' script OS-level entry point. Don't # look system-wide, so we are sure we pick up *this one*. And # pass through to subprocess call our own sys.argv ippath = p.abspath(p.join(p.dirname(__file__),'..','..')) script = p.join(ippath, 'iptest.py') - self.runner = tools.cmd2argv(script) + sys.argv[1:] + self.runner = pycmd2argv(script) + sys.argv[1:] else: # For trial, it needs to be installed system-wide - self.runner = tools.cmd2argv(p.abspath(find_cmd('trial'))) + self.runner = pycmd2argv(p.abspath(find_cmd('trial'))) if params is None: params = [] if isinstance(params, str): diff --git a/IPython/utils/tests/test_io.py b/IPython/utils/tests/test_io.py new file mode 100644 index 000000000..c47e78891 --- /dev/null +++ b/IPython/utils/tests/test_io.py @@ -0,0 +1,61 @@ +# encoding: utf-8 +"""Tests for io.py""" + +#----------------------------------------------------------------------------- +# Copyright (C) 2008 The IPython Development Team +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +import sys + +from cStringIO import StringIO + +import nose.tools as nt + +from IPython.testing import decorators as dec +from IPython.utils.io import Tee + +#----------------------------------------------------------------------------- +# Tests +#----------------------------------------------------------------------------- + + +def test_tee_simple(): + "Very simple check with stdout only" + chan = StringIO() + text = 'Hello' + tee = Tee(chan, channel='stdout') + print >> chan, text, + nt.assert_equal(chan.getvalue(), text) + + +class TeeTestCase(dec.ParametricTestCase): + + def tchan(self, channel, check='close'): + trap = StringIO() + chan = StringIO() + text = 'Hello' + + std_ori = getattr(sys, channel) + setattr(sys, channel, trap) + + tee = Tee(chan, channel=channel) + print >> chan, text, + setattr(sys, channel, std_ori) + trap_val = trap.getvalue() + nt.assert_equals(chan.getvalue(), text) + if check=='close': + tee.close() + else: + del tee + + def test(self): + for chan in ['stdout', 'stderr']: + for check in ['close', 'del']: + yield self.tchan(chan, check) diff --git a/setup.py b/setup.py index f89324e1d..2798796ec 100755 --- a/setup.py +++ b/setup.py @@ -47,7 +47,7 @@ if os.path.exists('MANIFEST'): os.remove('MANIFEST') from distutils.core import setup # Our own imports -from IPython.utils.genutils import target_update +from IPython.utils.path import target_update from setupbase import ( setup_args, From e5b30872ed6581fe9bf594f6a0b9841ef923f8d1 Mon Sep 17 00:00:00 2001 From: Brian Granger Date: Fri, 29 Jan 2010 21:01:10 -0800 Subject: [PATCH 25/27] More work addressing review comments for Fernando's branch. * :mod:`IPython.testing.globalipapp` now directly creates a :class:`~IPython.core.iplib.InteractiveShell` instance by passing it a configuration object, rather than creating an IPython application. * Updated everything in :mod:`IPython.frontend` and :mod:`IPython.gui` to use raw :class:`~IPython.core.iplib.InteractiveShell directly rather than creating an IPython application. * Updated the IPython sphinx extension to use raw :class:`~IPython.core.iplib.InteractiveShell directly rather than creating an IPython application. * Removed code from :mod:`IPython.extensions.pretty` that called :func:`get_ipython` (r1271). * Addressed comment on (r1284) about holding refs to deferreds in :mod:`IPython.kernel.ipclusterapp`. * Removed :mod:`IPython.kernel` from list of modules tested by nose in :mod:`IPython.testing.iptest`. (r1318) --- IPython/testing/iptest.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/IPython/testing/iptest.py b/IPython/testing/iptest.py index 593f46fcf..1558b60f2 100644 --- a/IPython/testing/iptest.py +++ b/IPython/testing/iptest.py @@ -343,9 +343,9 @@ def make_runners(): # And add twisted ones if conditions are met if have['zope.interface'] and have['twisted'] and have['foolscap']: - # Note that we list the kernel here, though the bulk of it is - # twisted-based, because nose picks up doctests that twisted doesn't. - nose_pkg_names.append('kernel') + # We only list IPython.kernel for testing using twisted.trial as + # nose and twisted.trial have conflicts that make the testing system + # unstable. trial_pkg_names.append('kernel') # For debugging this code, only load quick stuff From ba5e56c49f88304909b21d337dbf973e5e38428d Mon Sep 17 00:00:00 2001 From: Brian Granger Date: Sat, 30 Jan 2010 19:43:46 -0800 Subject: [PATCH 26/27] Removed the top-level iptest.py and INSTALLED logic. Fernando's branch introduced the ability to run the test suite from a top-level iptest.py script. This allowed IPython to be tested without installation. The implementation was fragile and we also decided that we *want* people to install IPython as part of testing it, so we removed this feature. --- IPython/testing/iptest.py | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/IPython/testing/iptest.py b/IPython/testing/iptest.py index 1558b60f2..28e2b229b 100644 --- a/IPython/testing/iptest.py +++ b/IPython/testing/iptest.py @@ -70,12 +70,6 @@ pjoin = path.join # Globals #----------------------------------------------------------------------------- -# By default, we assume IPython has been installed. But if the test suite is -# being run from a source tree that has NOT been installed yet, this flag can -# be set to False by the entry point scripts, to let us know that we must call -# the source tree versions of the scripts which manipulate sys.path instead of -# assuming that things exist system-wide. -INSTALLED = True #----------------------------------------------------------------------------- # Warnings control @@ -132,8 +126,6 @@ def report(): out = [ sys_info() ] - out.append('\nRunning from an installed IPython: %s\n' % INSTALLED) - avail = [] not_avail = [] @@ -255,17 +247,8 @@ class IPTester(object): """Create new test runner.""" p = os.path if runner == 'iptest': - if INSTALLED: - iptest_app = get_ipython_module_path('IPython.testing.iptest') - self.runner = pycmd2argv(iptest_app) + sys.argv[1:] - else: - # Find our own 'iptest' script OS-level entry point. Don't - # look system-wide, so we are sure we pick up *this one*. And - # pass through to subprocess call our own sys.argv - ippath = p.abspath(p.join(p.dirname(__file__),'..','..')) - script = p.join(ippath, 'iptest.py') - self.runner = pycmd2argv(script) + sys.argv[1:] - + iptest_app = get_ipython_module_path('IPython.testing.iptest') + self.runner = pycmd2argv(iptest_app) + sys.argv[1:] else: # For trial, it needs to be installed system-wide self.runner = pycmd2argv(p.abspath(find_cmd('trial'))) From 9d00d5e3da9ef56d143a58bdfd86af3ed8e0076b Mon Sep 17 00:00:00 2001 From: Brian Granger Date: Sun, 31 Jan 2010 12:06:20 -0800 Subject: [PATCH 27/27] Minor cleanup in iptest.py and growl.py. --- IPython/testing/iptest.py | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/IPython/testing/iptest.py b/IPython/testing/iptest.py index 28e2b229b..2f8453f15 100644 --- a/IPython/testing/iptest.py +++ b/IPython/testing/iptest.py @@ -37,15 +37,6 @@ import tempfile import time import warnings - -# Ugly, but necessary hack to ensure the test suite finds our version of -# IPython and not a possibly different one that may exist system-wide. -# Note that this must be done here, so the imports that come next work -# correctly even if IPython isn't installed yet. -p = os.path -ippath = p.abspath(p.join(p.dirname(__file__),'..','..')) -sys.path.insert(0, ippath) - # Note: monkeypatch! # We need to monkeypatch a small problem in nose itself first, before importing # it for actual use. This should get into nose upstream, but its release cycle @@ -74,6 +65,7 @@ pjoin = path.join #----------------------------------------------------------------------------- # Warnings control #----------------------------------------------------------------------------- + # Twisted generates annoying warnings with Python 2.6, as will do other code # that imports 'sets' as of today warnings.filterwarnings('ignore', 'the sets module is deprecated', @@ -150,11 +142,11 @@ def report(): def make_exclude(): """Make patterns of modules and packages to exclude from testing. - + For the IPythonDoctest plugin, we need to exclude certain patterns that cause testing problems. We should strive to minimize the number of - skipped modules, since this means untested code. As the testing - machinery solidifies, this list should eventually become empty. + skipped modules, since this means untested code. + These modules and packages will NOT get scanned by nose at all for tests. """ # Simple utility to make IPython paths more readably, we need a lot of @@ -249,9 +241,11 @@ class IPTester(object): if runner == 'iptest': iptest_app = get_ipython_module_path('IPython.testing.iptest') self.runner = pycmd2argv(iptest_app) + sys.argv[1:] - else: + elif runner == 'trial': # For trial, it needs to be installed system-wide self.runner = pycmd2argv(p.abspath(find_cmd('trial'))) + else: + raise Exception('Not a valid test runner: %s' % repr(runner)) if params is None: params = [] if isinstance(params, str): @@ -273,6 +267,8 @@ class IPTester(object): # fashioned' way to do it, but it works just fine. If someone # later can clean this up that's fine, as long as the tests run # reliably in win32. + # What types of problems are you having. They may be related to + # running Python in unboffered mode. BG. return os.system(' '.join(self.call_args)) else: def _run_cmd(self):