diff --git a/IPython/core/crashhandler.py b/IPython/core/crashhandler.py index fa588f0..128389f 100644 --- a/IPython/core/crashhandler.py +++ b/IPython/core/crashhandler.py @@ -190,7 +190,7 @@ class IPythonCrashHandler(CrashHandler): def __init__(self,IP): # Set here which of the IPython authors should be listed as contact - AUTHOR_CONTACT = 'Ville' + AUTHOR_CONTACT = 'Fernando' # Set argument defaults app_name = 'IPython' diff --git a/IPython/core/hooks.py b/IPython/core/hooks.py index c6225c0..97cce7c 100644 --- a/IPython/core/hooks.py +++ b/IPython/core/hooks.py @@ -43,8 +43,9 @@ somewhere in your configuration files or ipython command line. from IPython.core import ipapi -import os,bisect -from IPython.utils.genutils import Term,shell +import os, bisect +import sys +from IPython.utils.genutils import Term, shell from pprint import PrettyPrinter # List here all the default hooks. For now it's just the editor functions @@ -53,7 +54,8 @@ from pprint import PrettyPrinter __all__ = ['editor', 'fix_error_editor', 'synchronize_with_editor', 'result_display', 'input_prefilter', 'shutdown_hook', 'late_startup_hook', 'generate_prompt', 'generate_output_prompt','shell_hook', - 'show_in_pager','pre_prompt_hook', 'pre_runcode_hook'] + 'show_in_pager','pre_prompt_hook', 'pre_runcode_hook', + 'clipboard_get'] # vds: << pformat = PrettyPrinter().pformat @@ -109,7 +111,7 @@ def fix_error_editor(self,filename,linenum,column,msg): # vds: >> def synchronize_with_editor(self, filename, linenum, column): - pass + pass # vds: << class CommandChainDispatcher: @@ -243,5 +245,22 @@ def pre_prompt_hook(self): def pre_runcode_hook(self): """ Executed before running the (prefiltered) code in IPython """ return None - +def clipboard_get(self): + """ Get text from the clipboard. + """ + from IPython.lib.clipboard import ( + osx_clipboard_get, tkinter_clipboard_get, + win32_clipboard_get + ) + if sys.platform == 'win32': + chain = [win32_clipboard_get, tkinter_clipboard_get] + elif sys.platform == 'darwin': + chain = [osx_clipboard_get, tkinter_clipboard_get] + else: + chain = [tkinter_clipboard_get] + dispatcher = CommandChainDispatcher() + for func in chain: + dispatcher.add(func) + text = dispatcher() + return text diff --git a/IPython/core/iplib.py b/IPython/core/iplib.py index fa635c8..85fcfec 100644 --- a/IPython/core/iplib.py +++ b/IPython/core/iplib.py @@ -927,8 +927,8 @@ class InteractiveShell(object,Magic): Certain history lists are also initialized here, as they effectively act as user namespaces. - Note - ---- + Notes + ----- All data structures here are only filled in, they are NOT reset by this method. If they were not empty before, data will simply be added to therm. @@ -1315,8 +1315,8 @@ class InteractiveShell(object,Magic): def user_setup(self,ipythondir,rc_suffix,mode='install'): """Install the user configuration directory. - Note - ---- + Notes + ----- DEPRECATED: use the top-level user_setup() function instead. """ return user_setup(ipythondir,rc_suffix,mode) diff --git a/IPython/core/magic.py b/IPython/core/magic.py index aa9fff3..122e865 100644 --- a/IPython/core/magic.py +++ b/IPython/core/magic.py @@ -1707,7 +1707,12 @@ Currently the magic system has the following functions:\n""" # (leaving dangling references). self.shell.cache_main_mod(prog_ns,filename) # update IPython interactive namespace - del prog_ns['__name__'] + + # Some forms of read errors on the file may mean the + # __name__ key was never set; using pop we don't have to + # worry about a possible KeyError. + prog_ns.pop('__name__', None) + self.shell.user_ns.update(prog_ns) finally: # It's a bit of a mystery why, but __builtins__ can change from @@ -3262,6 +3267,61 @@ Defaulting color scheme to 'NoColor'""" page(self.shell.pycolorize(cont), screen_lines=self.shell.rc.screen_length) + def _rerun_pasted(self): + """ Rerun a previously pasted command. + """ + b = self.user_ns.get('pasted_block', None) + if b is None: + raise UsageError('No previous pasted block available') + print "Re-executing '%s...' (%d chars)"% (b.split('\n',1)[0], len(b)) + exec b in self.user_ns + + def _get_pasted_lines(self, sentinel): + """ Yield pasted lines until the user enters the given sentinel value. + """ + from IPython.core import iplib + print "Pasting code; enter '%s' alone on the line to stop." % sentinel + while True: + l = iplib.raw_input_original(':') + if l == sentinel: + return + else: + yield l + + def _strip_pasted_lines_for_code(self, raw_lines): + """ Strip non-code parts of a sequence of lines to return a block of + code. + """ + # Regular expressions that declare text we strip from the input: + strip_re = [r'^\s*In \[\d+\]:', # IPython input prompt + r'^\s*(\s?>)+', # Python input prompt + r'^\s*\.{3,}', # Continuation prompts + r'^\++', + ] + + strip_from_start = map(re.compile,strip_re) + + lines = [] + for l in raw_lines: + for pat in strip_from_start: + l = pat.sub('',l) + lines.append(l) + + block = "\n".join(lines) + '\n' + #print "block:\n",block + return block + + def _execute_block(self, block, par): + """ Execute a block, or store it in a variable, per the user's request. + """ + if not par: + b = textwrap.dedent(block) + self.user_ns['pasted_block'] = b + exec b in self.user_ns + else: + self.user_ns[par] = SList(block.splitlines()) + print "Block assigned to '%s'" % par + def magic_cpaste(self, parameter_s=''): """Allows you to paste & execute a pre-formatted code block from clipboard. @@ -3287,50 +3347,60 @@ Defaulting color scheme to 'NoColor'""" will be what was just pasted. IPython statements (magics, shell escapes) are not supported (yet). + + See also + -------- + paste: automatically pull code from clipboard. """ + opts,args = self.parse_options(parameter_s,'rs:',mode='string') par = args.strip() if opts.has_key('r'): - b = self.user_ns.get('pasted_block', None) - if b is None: - raise UsageError('No previous pasted block available') - print "Re-executing '%s...' (%d chars)"% (b.split('\n',1)[0], len(b)) - exec b in self.user_ns + self._rerun_pasted() return sentinel = opts.get('s','--') - # Regular expressions that declare text we strip from the input: - strip_re = [r'^\s*In \[\d+\]:', # IPython input prompt - r'^\s*(\s?>)+', # Python input prompt - r'^\s*\.{3,}', # Continuation prompts - r'^\++', - ] + block = self._strip_pasted_lines_for_code( + self._get_pasted_lines(sentinel)) - strip_from_start = map(re.compile,strip_re) + self._execute_block(block, par) + + def magic_paste(self, parameter_s=''): + """Allows you to paste & execute a pre-formatted code block from clipboard. - from IPython.core import iplib - lines = [] - print "Pasting code; enter '%s' alone on the line to stop." % sentinel - while 1: - l = iplib.raw_input_original(':') - if l ==sentinel: - break - - for pat in strip_from_start: - l = pat.sub('',l) - lines.append(l) - - block = "\n".join(lines) + '\n' - #print "block:\n",block - if not par: - b = textwrap.dedent(block) - self.user_ns['pasted_block'] = b - exec b in self.user_ns - else: - self.user_ns[par] = SList(block.splitlines()) - print "Block assigned to '%s'" % par - + The text is pulled directly from the clipboard without user + intervention. + + The block is dedented prior to execution to enable execution of method + definitions. '>' and '+' characters at the beginning of a line are + ignored, to allow pasting directly from e-mails, diff files and + doctests (the '...' continuation prompt is also stripped). The + executed block is also assigned to variable named 'pasted_block' for + later editing with '%edit pasted_block'. + + You can also pass a variable name as an argument, e.g. '%paste foo'. + This assigns the pasted block to variable 'foo' as string, without + dedenting or executing it (preceding >>> and + is still stripped) + + '%paste -r' re-executes the block previously entered by cpaste. + + IPython statements (magics, shell escapes) are not supported (yet). + + See also + -------- + cpaste: manually paste code into terminal until you mark its end. + """ + opts,args = self.parse_options(parameter_s,'r:',mode='string') + par = args.strip() + if opts.has_key('r'): + self._rerun_pasted() + return + + text = self.shell.hooks.clipboard_get() + block = self._strip_pasted_lines_for_code(text.splitlines()) + self._execute_block(block, par) + def magic_quickref(self,arg): """ Show a quick reference sheet """ import IPython.core.usage diff --git a/IPython/core/release.py b/IPython/core/release.py index 5a6e465..cc6f716 100644 --- a/IPython/core/release.py +++ b/IPython/core/release.py @@ -2,8 +2,8 @@ """Release data for the IPython project.""" #***************************************************************************** -# Copyright (C) 2001-2006 Fernando Perez -# +# Copyright (C) 2008-2009 The IPython Development Team +# Copyright (C) 2001-2008 Fernando Perez # Copyright (c) 2001 Janko Hauser and Nathaniel Gray # # @@ -20,10 +20,10 @@ name = 'ipython' # because bdist_rpm does not accept dashes (an RPM) convention, and # bdist_deb does not accept underscores (a Debian convention). -development = True # change this to False to do a release +development = False # change this to False to do a release version_base = '0.10' branch = 'ipython' -revision = '1163' +revision = '1188' if development: if branch == 'ipython': @@ -100,7 +100,7 @@ site `_. license = 'BSD' -authors = {'Fernando' : ('Fernando Perez','fperez@colorado.edu'), +authors = {'Fernando' : ('Fernando Perez','fperez.net@gmail.com'), 'Janko' : ('Janko Hauser','jhauser@zscout.de'), 'Nathan' : ('Nathaniel Gray','n8gray@caltech.edu'), 'Ville' : ('Ville Vainio','vivainio@gmail.com'), diff --git a/IPython/core/tests/test_magic.py b/IPython/core/tests/test_magic.py index 8b98936..8644e4c 100644 --- a/IPython/core/tests/test_magic.py +++ b/IPython/core/tests/test_magic.py @@ -25,14 +25,14 @@ def test_rehashx(): _ip.magic('rehashx') # Practically ALL ipython development systems will have more than 10 aliases - assert len(_ip.IP.alias_table) > 10 + yield (nt.assert_true, len(_ip.IP.alias_table) > 10) for key, val in _ip.IP.alias_table.items(): # we must strip dots from alias names - assert '.' not in key + nt.assert_true('.' not in key) # rehashx must fill up syscmdlist scoms = _ip.db['syscmdlist'] - assert len(scoms) > 10 + yield (nt.assert_true, len(scoms) > 10) def doctest_hist_f(): @@ -42,7 +42,7 @@ def doctest_hist_f(): In [10]: tfile = tempfile.mktemp('.py','tmp-ipython-') - In [11]: %history -n -f $tfile 3 + In [11]: %hist -n -f $tfile 3 """ @@ -51,9 +51,12 @@ def doctest_hist_r(): XXX - This test is not recording the output correctly. Not sure why... + In [20]: 'hist' in _ip.IP.lsmagic() + Out[20]: True + In [6]: x=1 - In [7]: hist -n -r 2 + In [7]: %hist -n -r 2 x=1 # random hist -n -r 2 # random """ @@ -94,12 +97,13 @@ def test_shist(): @dec.skipif_not_numpy def test_numpy_clear_array_undec(): + from IPython.extensions import clearcmd + _ip.ex('import numpy as np') _ip.ex('a = np.empty(2)') - - yield nt.assert_true,'a' in _ip.user_ns + yield (nt.assert_true, 'a' in _ip.user_ns) _ip.magic('clear array') - yield nt.assert_false,'a' in _ip.user_ns + yield (nt.assert_false, 'a' in _ip.user_ns) @dec.skip() @@ -159,7 +163,6 @@ def doctest_run_ns2(): tclass.py: deleting object: C-first_pass """ -@dec.skip_win32 def doctest_run_builtins(): """Check that %run doesn't damage __builtins__ via a doctest. @@ -172,24 +175,34 @@ def doctest_run_builtins(): In [2]: bid1 = id(__builtins__) - In [3]: f = tempfile.NamedTemporaryFile() + In [3]: fname = tempfile.mkstemp()[1] + + In [3]: f = open(fname,'w') In [4]: f.write('pass\\n') In [5]: f.flush() - In [6]: print 'B1:',type(__builtins__) - B1: + In [6]: print type(__builtins__) + - In [7]: %run $f.name + In [7]: %run "$fname" + + In [7]: f.close() In [8]: bid2 = id(__builtins__) - In [9]: print 'B2:',type(__builtins__) - B2: + In [9]: print type(__builtins__) + In [10]: bid1 == bid2 Out[10]: True + + In [12]: try: + ....: os.unlink(fname) + ....: except: + ....: pass + ....: """ # For some tests, it will be handy to organize them in a class with a common @@ -199,23 +212,18 @@ class TestMagicRun(object): def setup(self): """Make a valid python temp file.""" - f = tempfile.NamedTemporaryFile() + fname = tempfile.mkstemp()[1] + f = open(fname,'w') f.write('pass\n') f.flush() self.tmpfile = f + self.fname = fname def run_tmpfile(self): # This fails on Windows if self.tmpfile.name has spaces or "~" in it. # See below and ticket https://bugs.launchpad.net/bugs/366353 - _ip.magic('run %s' % self.tmpfile.name) - - # See https://bugs.launchpad.net/bugs/366353 - @dec.skip_if_not_win32 - def test_run_tempfile_path(self): - tt.assert_equals(True,False,"%run doesn't work with tempfile paths on win32.") + _ip.magic('run "%s"' % self.fname) - # See https://bugs.launchpad.net/bugs/366353 - @dec.skip_win32 def test_builtins_id(self): """Check that %run doesn't damage __builtins__ """ @@ -225,8 +233,6 @@ class TestMagicRun(object): bid2 = id(_ip.user_ns['__builtins__']) tt.assert_equals(bid1, bid2) - # See https://bugs.launchpad.net/bugs/366353 - @dec.skip_win32 def test_builtins_type(self): """Check that the type of __builtins__ doesn't change with %run. @@ -237,8 +243,6 @@ class TestMagicRun(object): self.run_tmpfile() tt.assert_equals(type(_ip.user_ns['__builtins__']),type(sys)) - # See https://bugs.launchpad.net/bugs/366353 - @dec.skip_win32 def test_prompts(self): """Test that prompts correctly generate after %run""" self.run_tmpfile() @@ -247,3 +251,52 @@ class TestMagicRun(object): def teardown(self): self.tmpfile.close() + try: + os.unlink(self.fname) + except: + # On Windows, even though we close the file, we still can't delete + # it. I have no clue why + pass + +# Multiple tests for clipboard pasting +def test_paste(): + + def paste(txt): + hooks.clipboard_get = lambda : txt + _ip.magic('paste') + + # Inject fake clipboard hook but save original so we can restore it later + hooks = _ip.IP.hooks + user_ns = _ip.user_ns + original_clip = hooks.clipboard_get + + try: + # This try/except with an emtpy except clause is here only because + # try/yield/finally is invalid syntax in Python 2.4. This will be + # removed when we drop 2.4-compatibility, and the emtpy except below + # will be changed to a finally. + + # Run tests with fake clipboard function + user_ns.pop('x', None) + paste('x=1') + yield (nt.assert_equal, user_ns['x'], 1) + + user_ns.pop('x', None) + paste('>>> x=2') + yield (nt.assert_equal, user_ns['x'], 2) + + paste(""" + >>> x = [1,2,3] + >>> y = [] + >>> for i in x: + ... y.append(i**2) + ... + """) + yield (nt.assert_equal, user_ns['x'], [1,2,3]) + yield (nt.assert_equal, user_ns['y'], [1,4,9]) + except: + pass + + # This should be in a finally clause, instead of the bare except above. + # Restore original hook + hooks.clipboard_get = original_clip diff --git a/IPython/core/ultratb.py b/IPython/core/ultratb.py index 48150b7..4a22aa8 100644 --- a/IPython/core/ultratb.py +++ b/IPython/core/ultratb.py @@ -91,7 +91,7 @@ from inspect import getsourcefile, getfile, getmodule,\ # IPython's own modules # Modified pdb which doesn't damage IPython's readline handling from IPython.utils import PyColorize -from IPython.core import debugger +from IPython.core import debugger, ipapi from IPython.utils.ipstruct import Struct from IPython.core.excolors import exception_colors from IPython.utils.genutils import Term,uniq_stable,error,info @@ -268,10 +268,12 @@ def _formatTracebackLines(lnum, index, lines, Colors, lvals=None,scheme=None): # This lets us get fully syntax-highlighted tracebacks. if scheme is None: - try: - scheme = __IPYTHON__.rc.colors - except: + ipinst = ipapi.get() + if ipinst is not None: + scheme = ipinst.IP.rc.colors + else: scheme = DEFAULT_SCHEME + _line_format = _parser.format2 for line in lines: @@ -490,7 +492,9 @@ class ListTB(TBTools): # vds:>> if have_filedata: - __IPYTHON__.hooks.synchronize_with_editor(filename, lineno, 0) + ipinst = ipapi.get() + if ipinst is not None: + ipinst.IP.hooks.synchronize_with_editor(filename, lineno, 0) # vds:<< return list @@ -810,7 +814,9 @@ class VerboseTB(TBTools): filepath, lnum = records[-1][1:3] #print "file:", str(file), "linenb", str(lnum) # dbg filepath = os.path.abspath(filepath) - __IPYTHON__.hooks.synchronize_with_editor(filepath, lnum, 0) + ipinst = ipapi.get() + if ipinst is not None: + ipinst.IP.hooks.synchronize_with_editor(filepath, lnum, 0) # vds: << # return all our info assembled as a single string diff --git a/IPython/deathrow/twshell.py b/IPython/deathrow/twshell.py index 06fd047..6ad78fe 100644 --- a/IPython/deathrow/twshell.py +++ b/IPython/deathrow/twshell.py @@ -2,6 +2,9 @@ XXX - This module is missing proper docs. """ +# Tell nose to skip this module +__test__ = {} + import sys from twisted.internet import reactor, threads diff --git a/IPython/extensions/ipy_autoreload.py b/IPython/extensions/ipy_autoreload.py index 7a9c0e6..98dd9db 100644 --- a/IPython/extensions/ipy_autoreload.py +++ b/IPython/extensions/ipy_autoreload.py @@ -1,7 +1,7 @@ """ IPython extension: autoreload modules before executing the next line -Try:: +Try:: %autoreload? @@ -32,7 +32,7 @@ PY_COMPILED_EXT = _get_compiled_ext() class ModuleReloader(object): failed = {} """Modules that failed to reload: {module: mtime-on-failed-reload, ...}""" - + modules = {} """Modules specially marked as autoreloadable.""" @@ -44,39 +44,39 @@ class ModuleReloader(object): old_objects = {} """(module-name, name) -> weakref, for replacing old code objects""" - + def check(self, check_all=False): """Check whether some modules need to be reloaded.""" - + if check_all or self.check_all: modules = sys.modules.keys() else: modules = self.modules.keys() - + for modname in modules: m = sys.modules.get(modname, None) if modname in self.skip_modules: continue - + if not hasattr(m, '__file__'): continue - + if m.__name__ == '__main__': # we cannot reload(__main__) continue - + filename = m.__file__ dirname = os.path.dirname(filename) path, ext = os.path.splitext(filename) - + if ext.lower() == '.py': ext = PY_COMPILED_EXT filename = os.path.join(dirname, path + PY_COMPILED_EXT) - + if ext != PY_COMPILED_EXT: continue - + try: pymtime = os.stat(filename[:-1]).st_mtime if pymtime <= os.stat(filename).st_mtime: @@ -85,7 +85,7 @@ class ModuleReloader(object): continue except OSError: continue - + try: superreload(m, reload, self.old_objects) if filename[:-1] in self.failed: @@ -118,12 +118,12 @@ def update_class(old, new): new_obj = getattr(new, key) except AttributeError: # obsolete attribute: remove it - try: + try: delattr(old, key) except (AttributeError, TypeError): pass continue - + if update_generic(old_obj, new_obj): continue try: @@ -146,9 +146,9 @@ UPDATE_RULES = [ (lambda a, b: isinstance2(a, b, types.TypeType), update_class), (lambda a, b: isinstance2(a, b, types.FunctionType), - update_function), + update_function), (lambda a, b: isinstance2(a, b, property), - update_property), + update_property), (lambda a, b: isinstance2(a, b, types.MethodType), lambda a, b: update_function(a.im_func, b.im_func)), ] @@ -168,15 +168,15 @@ class StrongRef(object): def superreload(module, reload=reload, old_objects={}): """Enhanced version of the builtin reload function. - + superreload remembers objects previously in the module, and - upgrades the class dictionary of every old class in the module - upgrades the code object of every old function and method - clears the module's namespace before reloading - + """ - + # collect old objects in the module for name, obj in module.__dict__.items(): if not hasattr(obj, '__module__') or obj.__module__ != module.__name__: @@ -199,7 +199,7 @@ def superreload(module, reload=reload, old_objects={}): except (TypeError, AttributeError, KeyError): pass module = reload(module) - + # iterate over all objects and update functions & classes for name, new_obj in module.__dict__.items(): key = (module.__name__, name) @@ -248,40 +248,46 @@ def disable_autoreload(): def autoreload_f(self, parameter_s=''): r""" %autoreload => Reload modules automatically - + %autoreload Reload all modules (except those excluded by %aimport) automatically now. - + + %autoreload 0 + Disable automatic reloading. + %autoreload 1 Reload all modules imported with %aimport every time before executing the Python code typed. - + %autoreload 2 Reload all modules (except those excluded by %aimport) every time before executing the Python code typed. - - Reloading Python modules in a reliable way is in general difficult, - and unexpected things may occur. %autoreload tries to work - around common pitfalls by replacing code objects of functions - previously in the module with new versions. This makes the following - things to work: + + Reloading Python modules in a reliable way is in general + difficult, and unexpected things may occur. %autoreload tries to + work around common pitfalls by replacing function code objects and + parts of classes previously in the module with new versions. This + makes the following things to work: - Functions and classes imported via 'from xxx import foo' are upgraded to new versions when 'xxx' is reloaded. + - Methods and properties of classes are upgraded on reload, so that calling 'c.foo()' on an object 'c' created before the reload causes the new code for 'foo' to be executed. - + Some of the known remaining caveats are: - + - Replacing code objects does not always succeed: changing a @property in a class to an ordinary method or a method to a member variable can cause problems (but in old objects only). + - Functions that are removed (eg. via monkey-patching) from a module before it is reloaded are not upgraded. + - C extension modules cannot be reloaded, and so cannot be autoreloaded. - + """ if parameter_s == '': reloader.check(True) @@ -307,7 +313,7 @@ def aimport_f(self, parameter_s=''): Mark module 'foo' to not be autoreloaded for %autoreload 1 """ - + modname = parameter_s if not modname: to_reload = reloader.modules.keys() @@ -329,12 +335,15 @@ def aimport_f(self, parameter_s=''): except KeyError: pass reloader.modules[modname] = True - mod = __import__(modname) - ip.to_user_ns({modname: mod}) + # Inject module to user namespace; handle also submodules properly + __import__(modname) + basename = modname.split('.')[0] + mod = sys.modules[basename] + ip.to_user_ns({basename: mod}) def init(): ip.expose_magic('autoreload', autoreload_f) ip.expose_magic('aimport', aimport_f) ip.set_hook('pre_runcode_hook', runcode_hook) - + init() diff --git a/IPython/frontend/asyncfrontendbase.py b/IPython/frontend/asyncfrontendbase.py index cc7ada6..1171a83 100644 --- a/IPython/frontend/asyncfrontendbase.py +++ b/IPython/frontend/asyncfrontendbase.py @@ -3,6 +3,9 @@ Base front end class for all async frontends. """ __docformat__ = "restructuredtext en" +# Tell nose to skip this module +__test__ = {} + #------------------------------------------------------------------------------- # Copyright (C) 2008 The IPython Development Team # @@ -10,20 +13,25 @@ __docformat__ = "restructuredtext en" # the file COPYING, distributed as part of this software. #------------------------------------------------------------------------------- - #------------------------------------------------------------------------------- # Imports #------------------------------------------------------------------------------- +# Third-party +from twisted.python.failure import Failure +from zope.interface import implements, classProvides + +# From IPython from IPython.external import guid -from zope.interface import Interface, Attribute, implements, classProvides -from twisted.python.failure import Failure -from IPython.frontend.frontendbase import ( - FrontEndBase, IFrontEnd, IFrontEndFactory) +from IPython.frontend.frontendbase import (FrontEndBase, IFrontEnd, + IFrontEndFactory) from IPython.kernel.core.history import FrontEndHistory from IPython.kernel.engineservice import IEngineCore +#----------------------------------------------------------------------------- +# Classes and functions +#----------------------------------------------------------------------------- class AsyncFrontEndBase(FrontEndBase): """ @@ -41,8 +49,7 @@ class AsyncFrontEndBase(FrontEndBase): self.history = FrontEndHistory(input_cache=['']) else: self.history = history - - + def execute(self, block, blockID=None): """Execute the block and return the deferred result. @@ -73,5 +80,3 @@ class AsyncFrontEndBase(FrontEndBase): errback=self.render_error) return d - - diff --git a/IPython/frontend/cocoa/plugin/setup.py b/IPython/frontend/cocoa/plugin/setup.py index 8393085..11fdd25 100644 --- a/IPython/frontend/cocoa/plugin/setup.py +++ b/IPython/frontend/cocoa/plugin/setup.py @@ -19,17 +19,17 @@ __docformat__ = "restructuredtext en" from setuptools import setup infoPlist = dict( - CFBundleDevelopmentRegion='English', - CFBundleIdentifier='org.scipy.ipython.cocoa_frontend', - NSPrincipalClass='IPythonCocoaController', + CFBundleDevelopmentRegion='English', + CFBundleIdentifier='org.scipy.ipython.cocoa_frontend', + NSPrincipalClass='IPythonCocoaController', ) setup( - plugin=['IPythonCocoaFrontendLoader.py'], + plugin=['IPythonCocoaFrontendLoader.py'], setup_requires=['py2app'], - options=dict(py2app=dict( - plist=infoPlist, - site_packages=True, - excludes=['IPython','twisted','PyObjCTools'] - )), + options=dict(py2app=dict( + plist=infoPlist, + site_packages=True, + excludes=['IPython','twisted','PyObjCTools'] + )), ) \ No newline at end of file diff --git a/IPython/frontend/linefrontendbase.py b/IPython/frontend/linefrontendbase.py index 1cb1ad6..18e0ba8 100644 --- a/IPython/frontend/linefrontendbase.py +++ b/IPython/frontend/linefrontendbase.py @@ -97,8 +97,8 @@ class LineFrontEndBase(FrontEndBase): ---------- line : string - Result - ------ + Returns + ------- The replacement for the line and the list of possible completions. """ completions = self.shell.complete(line) diff --git a/IPython/frontend/prefilterfrontend.py b/IPython/frontend/prefilterfrontend.py index 989c752..d7227cb 100644 --- a/IPython/frontend/prefilterfrontend.py +++ b/IPython/frontend/prefilterfrontend.py @@ -65,8 +65,8 @@ class PrefilterFrontEnd(LineFrontEndBase): debug = False def __init__(self, ipython0=None, argv=None, *args, **kwargs): - """ Parameters: - ----------- + """ Parameters + ---------- ipython0: an optional ipython0 instance to use for command prefiltering and completion. diff --git a/IPython/frontend/tests/test_asyncfrontendbase.py b/IPython/frontend/tests/test_asyncfrontendbase.py index fb497c8..b52659b 100644 --- a/IPython/frontend/tests/test_asyncfrontendbase.py +++ b/IPython/frontend/tests/test_asyncfrontendbase.py @@ -3,6 +3,9 @@ """This file contains unittests for the asyncfrontendbase module.""" __docformat__ = "restructuredtext en" + +# Tell nose to skip this module +__test__ = {} #--------------------------------------------------------------------------- # Copyright (C) 2008 The IPython Development Team @@ -10,20 +13,21 @@ __docformat__ = "restructuredtext en" # Distributed under the terms of the BSD License. The full license is in # the file COPYING, distributed as part of this software. #--------------------------------------------------------------------------- - + #--------------------------------------------------------------------------- # Imports #--------------------------------------------------------------------------- -# Tell nose to skip this module -__test__ = {} - from twisted.trial import unittest + from IPython.frontend.asyncfrontendbase import AsyncFrontEndBase from IPython.frontend import frontendbase from IPython.kernel.engineservice import EngineService from IPython.testing.parametric import Parametric, parametric +#----------------------------------------------------------------------------- +# Classes and functions +#----------------------------------------------------------------------------- class FrontEndCallbackChecker(AsyncFrontEndBase): """FrontEndBase subclass for checking callbacks""" @@ -106,4 +110,3 @@ class TestAsyncFrontendBase(unittest.TestCase): def test_history_returns_none_at_startup(self): self.assert_(self.fb.get_history_previous("")==None) self.assert_(self.fb.get_history_next()==None) - diff --git a/IPython/frontend/tests/test_prefilterfrontend.py b/IPython/frontend/tests/test_prefilterfrontend.py index de11c91..4e1b05d 100644 --- a/IPython/frontend/tests/test_prefilterfrontend.py +++ b/IPython/frontend/tests/test_prefilterfrontend.py @@ -15,6 +15,7 @@ __docformat__ = "restructuredtext en" from copy import copy, deepcopy from cStringIO import StringIO import string +import sys from nose.tools import assert_equal @@ -23,22 +24,6 @@ from IPython.core.ipapi import get as get_ipython0 from IPython.testing.plugin.ipdoctest import default_argv -def safe_deepcopy(d): - """ Deep copy every key of the given dict, when possible. Elsewhere - do a copy. - """ - copied_d = dict() - for key, value in d.iteritems(): - try: - copied_d[key] = deepcopy(value) - except: - try: - copied_d[key] = copy(value) - except: - copied_d[key] = value - return copied_d - - class TestPrefilterFrontEnd(PrefilterFrontEnd): input_prompt_template = string.Template('') @@ -72,17 +57,34 @@ def isolate_ipython0(func): with arguments. """ def my_func(): - iplib = get_ipython0() - if iplib is None: + ip0 = get_ipython0() + if ip0 is None: return func() - ipython0 = iplib.IP - global_ns = safe_deepcopy(ipython0.user_global_ns) - user_ns = safe_deepcopy(ipython0.user_ns) + # We have a real ipython running... + user_ns = ip0.IP.user_ns + user_global_ns = ip0.IP.user_global_ns + + # Previously the isolation was attempted with a deep copy of the user + # dicts, but we found cases where this didn't work correctly. I'm not + # quite sure why, but basically it did damage the user namespace, such + # that later tests stopped working correctly. Instead we use a simpler + # approach, just computing the list of added keys to the namespace and + # eliminating those afterwards. Existing keys that may have been + # modified remain modified. So far this has proven to be robust. + + # Compute set of old local/global keys + old_locals = set(user_ns.keys()) + old_globals = set(user_global_ns.keys()) try: out = func() finally: - ipython0.user_ns = user_ns - ipython0.user_global_ns = global_ns + # Find new keys, and if any, remove them + new_locals = set(user_ns.keys()) - old_locals + new_globals = set(user_global_ns.keys()) - old_globals + for k in new_locals: + del user_ns[k] + for k in new_globals: + del user_global_ns[k] # Undo the hack at creation of PrefilterFrontEnd from IPython.core import iplib iplib.InteractiveShell.isthreaded = False @@ -97,7 +99,7 @@ def test_execution(): """ Test execution of a command. """ f = TestPrefilterFrontEnd() - f.input_buffer = 'print 1' + f.input_buffer = 'print(1)' f._on_enter() out_value = f.out.getvalue() assert_equal(out_value, '1\n') @@ -228,7 +230,14 @@ def test_completion_indexing(): f._on_enter() f.input_buffer = 'a[0].' f.complete_current_input() - assert_equal(f.input_buffer, 'a[0].__') + + if sys.version_info[:2] >= (2,6): + # In Python 2.6, ints picked up a few non __ methods, so now there are + # no completions. + assert_equal(f.input_buffer, 'a[0].') + else: + # Right answer for 2.4/2.5 + assert_equal(f.input_buffer, 'a[0].__') @isolate_ipython0 @@ -238,8 +247,13 @@ def test_completion_equal(): f = TestPrefilterFrontEnd() f.input_buffer = 'a=1.' f.complete_current_input() - assert_equal(f.input_buffer, 'a=1.__') - + if sys.version_info[:2] >= (2,6): + # In Python 2.6, ints picked up a few non __ methods, so now there are + # no completions. + assert_equal(f.input_buffer, 'a=1.') + else: + # Right answer for 2.4/2.5 + assert_equal(f.input_buffer, 'a=1.__') if __name__ == '__main__': diff --git a/IPython/frontend/wx/console_widget.py b/IPython/frontend/wx/console_widget.py index be20eff..b3b7245 100644 --- a/IPython/frontend/wx/console_widget.py +++ b/IPython/frontend/wx/console_widget.py @@ -447,29 +447,30 @@ class ConsoleWidget(editwindow.EditWindow): # different callbacks share local variables? # Intercept some specific keys. - if event.KeyCode == ord('L') and event.ControlDown() : + key_code = event.GetKeyCode() + if key_code == ord('L') and event.ControlDown() : self.scroll_to_bottom() - elif event.KeyCode == ord('K') and event.ControlDown() : + elif key_code == ord('K') and event.ControlDown() : self.input_buffer = '' - elif event.KeyCode == ord('A') and event.ControlDown() : + elif key_code == ord('A') and event.ControlDown() : self.GotoPos(self.GetLength()) self.SetSelectionStart(self.current_prompt_pos) self.SetSelectionEnd(self.GetCurrentPos()) catched = True - elif event.KeyCode == ord('E') and event.ControlDown() : + elif key_code == ord('E') and event.ControlDown() : self.GotoPos(self.GetLength()) catched = True - elif event.KeyCode == wx.WXK_PAGEUP: + elif key_code == wx.WXK_PAGEUP: self.ScrollPages(-1) - elif event.KeyCode == wx.WXK_PAGEDOWN: + elif key_code == wx.WXK_PAGEDOWN: self.ScrollPages(1) - elif event.KeyCode == wx.WXK_HOME: + elif key_code == wx.WXK_HOME: self.GotoPos(self.GetLength()) - elif event.KeyCode == wx.WXK_END: + elif key_code == wx.WXK_END: self.GotoPos(self.GetLength()) - elif event.KeyCode == wx.WXK_UP and event.ShiftDown(): + elif key_code == wx.WXK_UP and event.ShiftDown(): self.ScrollLines(-1) - elif event.KeyCode == wx.WXK_DOWN and event.ShiftDown(): + elif key_code == wx.WXK_DOWN and event.ShiftDown(): self.ScrollLines(1) else: catched = False @@ -477,13 +478,12 @@ class ConsoleWidget(editwindow.EditWindow): if self.AutoCompActive(): event.Skip() else: - if event.KeyCode in (13, wx.WXK_NUMPAD_ENTER) and \ - event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN, - wx.MOD_SHIFT): + if key_code in (13, wx.WXK_NUMPAD_ENTER): + # XXX: not catching modifiers, to be wx2.6-compatible catched = True if not self.enter_catched: self.CallTipCancel() - if event.Modifiers == wx.MOD_SHIFT: + if event.ShiftDown(): # Try to force execution self.GotoPos(self.GetLength()) self.write('\n' + self.continuation_prompt(), @@ -493,19 +493,18 @@ class ConsoleWidget(editwindow.EditWindow): self._on_enter() self.enter_catched = True - elif event.KeyCode == wx.WXK_HOME: - if event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN): + elif key_code == wx.WXK_HOME: + if not event.ShiftDown(): self.GotoPos(self.current_prompt_pos) catched = True - - elif event.Modifiers == wx.MOD_SHIFT: + else: # FIXME: This behavior is not ideal: if the selection # is already started, it will jump. self.SetSelectionStart(self.current_prompt_pos) self.SetSelectionEnd(self.GetCurrentPos()) catched = True - elif event.KeyCode == wx.WXK_UP: + elif key_code == wx.WXK_UP: if self.GetCurrentLine() > self.current_prompt_line: if self.GetCurrentLine() == self.current_prompt_line + 1 \ and self.GetColumn(self.GetCurrentPos()) < \ @@ -515,18 +514,18 @@ class ConsoleWidget(editwindow.EditWindow): event.Skip() catched = True - elif event.KeyCode in (wx.WXK_LEFT, wx.WXK_BACK): + elif key_code in (wx.WXK_LEFT, wx.WXK_BACK): if not self._keep_cursor_in_buffer(self.GetCurrentPos() - 1): event.Skip() catched = True - elif event.KeyCode == wx.WXK_RIGHT: + elif key_code == wx.WXK_RIGHT: if not self._keep_cursor_in_buffer(self.GetCurrentPos() + 1): event.Skip() catched = True - elif event.KeyCode == wx.WXK_DELETE: + elif key_code == wx.WXK_DELETE: if not self._keep_cursor_in_buffer(self.GetCurrentPos() - 1): event.Skip() catched = True @@ -535,7 +534,7 @@ class ConsoleWidget(editwindow.EditWindow): # Put the cursor back in the edit region if not self._keep_cursor_in_buffer(): if not (self.GetCurrentPos() == self.GetLength() - and event.KeyCode == wx.WXK_DELETE): + and key_code == wx.WXK_DELETE): event.Skip() catched = True diff --git a/IPython/frontend/wx/wx_frontend.py b/IPython/frontend/wx/wx_frontend.py index 854c47e..e22c91e 100644 --- a/IPython/frontend/wx/wx_frontend.py +++ b/IPython/frontend/wx/wx_frontend.py @@ -389,7 +389,8 @@ class WxController(ConsoleWidget, PrefilterFrontEnd): """ # FIXME: This method needs to be broken down in smaller ones. current_line_num = self.GetCurrentLine() - if event.KeyCode in (ord('c'), ord('C')) and event.ControlDown(): + key_code = event.GetKeyCode() + if key_code in (ord('c'), ord('C')) and event.ControlDown(): # Capture Control-C if self._input_state == 'subprocess': if self.debug: @@ -403,40 +404,39 @@ class WxController(ConsoleWidget, PrefilterFrontEnd): # XXX: We need to make really sure we # get back to a prompt. elif self._input_state == 'subprocess' and ( - ( event.KeyCode<256 and - not event.ControlDown() ) + ( key_code <256 and not event.ControlDown() ) or - ( event.KeyCode in (ord('d'), ord('D')) and + ( key_code in (ord('d'), ord('D')) and event.ControlDown())): # We are running a process, we redirect keys. ConsoleWidget._on_key_down(self, event, skip=skip) - char = chr(event.KeyCode) + char = chr(key_code) # Deal with some inconsistency in wx keycodes: if char == '\r': char = '\n' elif not event.ShiftDown(): char = char.lower() - if event.ControlDown() and event.KeyCode in (ord('d'), ord('D')): + if event.ControlDown() and key_code in (ord('d'), ord('D')): char = '\04' self._running_process.process.stdin.write(char) self._running_process.process.stdin.flush() - elif event.KeyCode in (ord('('), 57, 53): + elif key_code in (ord('('), 57, 53): # Calltips event.Skip() self.do_calltip() - elif self.AutoCompActive() and not event.KeyCode == ord('\t'): + elif self.AutoCompActive() and not key_code == ord('\t'): event.Skip() - if event.KeyCode in (wx.WXK_BACK, wx.WXK_DELETE): + if key_code in (wx.WXK_BACK, wx.WXK_DELETE): wx.CallAfter(self._popup_completion, create=True) - elif not event.KeyCode in (wx.WXK_UP, wx.WXK_DOWN, wx.WXK_LEFT, + elif not key_code in (wx.WXK_UP, wx.WXK_DOWN, wx.WXK_LEFT, wx.WXK_RIGHT, wx.WXK_ESCAPE): wx.CallAfter(self._popup_completion) else: # Up history - if event.KeyCode == wx.WXK_UP and ( - ( current_line_num == self.current_prompt_line and - event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) ) - or event.ControlDown() ): + if key_code == wx.WXK_UP and ( + event.ControlDown() or + current_line_num == self.current_prompt_line + ): new_buffer = self.get_history_previous( self.input_buffer) if new_buffer is not None: @@ -445,23 +445,24 @@ class WxController(ConsoleWidget, PrefilterFrontEnd): # Go to first line, for seemless history up. self.GotoPos(self.current_prompt_pos) # Down history - elif event.KeyCode == wx.WXK_DOWN and ( - ( current_line_num == self.LineCount -1 and - event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) ) - or event.ControlDown() ): + elif key_code == wx.WXK_DOWN and ( + event.ControlDown() or + current_line_num == self.LineCount -1 + ): new_buffer = self.get_history_next() if new_buffer is not None: self.input_buffer = new_buffer # Tab-completion - elif event.KeyCode == ord('\t'): + elif key_code == ord('\t'): current_line, current_line_num = self.CurLine - if not re.match(r'^\s*$', current_line): + if not re.match(r'^%s\s*$' % self.continuation_prompt(), + current_line): self.complete_current_input() if self.AutoCompActive(): wx.CallAfter(self._popup_completion, create=True) else: event.Skip() - elif event.KeyCode == wx.WXK_BACK: + elif key_code == wx.WXK_BACK: # If characters where erased, check if we have to # remove a line. # XXX: What about DEL? @@ -496,7 +497,7 @@ class WxController(ConsoleWidget, PrefilterFrontEnd): def _on_key_up(self, event, skip=True): """ Called when any key is released. """ - if event.KeyCode in (59, ord('.')): + if event.GetKeyCode() in (59, ord('.')): # Intercepting '.' event.Skip() wx.CallAfter(self._popup_completion, create=True) diff --git a/IPython/gui/wx/ipshell_nonblocking.py b/IPython/gui/wx/ipshell_nonblocking.py index aef5206..2fa6e6a 100644 --- a/IPython/gui/wx/ipshell_nonblocking.py +++ b/IPython/gui/wx/ipshell_nonblocking.py @@ -46,7 +46,7 @@ class _Helper(object): def __call__(self, *args, **kwds): class DummyWriter(object): - '''Dumy class to handle help output''' + '''Dumy class to handle help output''' def __init__(self, pager): self._pager = pager diff --git a/IPython/gui/wx/ipython_view.py b/IPython/gui/wx/ipython_view.py old mode 100755 new mode 100644 index 2319b28..2fc44c3 --- a/IPython/gui/wx/ipython_view.py +++ b/IPython/gui/wx/ipython_view.py @@ -76,12 +76,12 @@ class WxNonBlockingIPShell(NonBlockingIPShell): """ A replacement from python's raw_input. """ self.answer = None - if(self._threading == True): - wx.CallAfter(self._yesNoBox, prompt) - while self.answer is None: - time.sleep(.1) + if(self._threading == True): + wx.CallAfter(self._yesNoBox, prompt) + while self.answer is None: + time.sleep(.1) else: - self._yesNoBox(prompt) + self._yesNoBox(prompt) return self.answer def _yesNoBox(self, prompt): diff --git a/IPython/kernel/contexts.py b/IPython/kernel/contexts.py index 183da3a..45eeb2b 100644 --- a/IPython/kernel/contexts.py +++ b/IPython/kernel/contexts.py @@ -26,7 +26,7 @@ import sys from twisted.internet.error import ConnectionRefusedError -from IPython.ultraTB import _fixed_getinnerframes, findsource +from IPython.core.ultratb import _fixed_getinnerframes, findsource from IPython.core import ipapi from IPython.kernel import error diff --git a/IPython/kernel/core/interpreter.py b/IPython/kernel/core/interpreter.py index 05cec24..56f8ce7 100644 --- a/IPython/kernel/core/interpreter.py +++ b/IPython/kernel/core/interpreter.py @@ -29,7 +29,7 @@ import sys import traceback # Local imports. -from IPython.kernel.core import ultraTB +from IPython.core import ultratb from IPython.kernel.core.display_trap import DisplayTrap from IPython.kernel.core.macro import Macro from IPython.kernel.core.prompts import CachedOutput @@ -167,9 +167,9 @@ class Interpreter(object): formatters=self.traceback_formatters) # This is used temporarily for reformating exceptions in certain - # cases. It will go away once the ultraTB stuff is ported + # cases. It will go away once the ultratb stuff is ported # to ipython1 - self.tbHandler = ultraTB.FormattedTB(color_scheme='NoColor', + self.tbHandler = ultratb.FormattedTB(color_scheme='NoColor', mode='Context', tb_offset=2) @@ -729,8 +729,8 @@ class Interpreter(object): def error(self, text): """ Pass an error message back to the shell. - Preconditions - ------------- + Notes + ----- This should only be called when self.message is set. In other words, when code is being executed. diff --git a/IPython/kernel/core/notification.py b/IPython/kernel/core/notification.py index 7762d3a..ee9701e 100644 --- a/IPython/kernel/core/notification.py +++ b/IPython/kernel/core/notification.py @@ -21,8 +21,8 @@ __test__ = {} class NotificationCenter(object): """Synchronous notification center - Example - ------- + Examples + -------- >>> import IPython.kernel.core.notification as notification >>> def callback(theType, theSender, args={}): ... print theType,theSender,args @@ -47,10 +47,10 @@ class NotificationCenter(object): def post_notification(self, theType, sender, **kwargs): """Post notification (type,sender,**kwargs) to all registered - observers. - - Implementation - -------------- + observers. + + Implementation notes: + * If no registered observers, performance is O(1). * Notificaiton order is undefined. * Notifications are posted synchronously. @@ -122,4 +122,4 @@ class NotificationCenter(object): -sharedCenter = NotificationCenter() \ No newline at end of file +sharedCenter = NotificationCenter() diff --git a/IPython/kernel/core/ultraTB.py b/IPython/kernel/core/ultraTB.py deleted file mode 100644 index 268e2aa..0000000 --- a/IPython/kernel/core/ultraTB.py +++ /dev/null @@ -1,1067 +0,0 @@ -# -*- coding: utf-8 -*- -""" -ultraTB.py -- Spice up your tracebacks! - -* ColorTB -I've always found it a bit hard to visually parse tracebacks in Python. The -ColorTB class is a solution to that problem. It colors the different parts of a -traceback in a manner similar to what you would expect from a syntax-highlighting -text editor. - -Installation instructions for ColorTB: - import sys,ultraTB - sys.excepthook = ultraTB.ColorTB() - -* VerboseTB -I've also included a port of Ka-Ping Yee's "cgitb.py" that produces all kinds -of useful info when a traceback occurs. Ping originally had it spit out HTML -and intended it for CGI programmers, but why should they have all the fun? I -altered it to spit out colored text to the terminal. It's a bit overwhelming, -but kind of neat, and maybe useful for long-running programs that you believe -are bug-free. If a crash *does* occur in that type of program you want details. -Give it a shot--you'll love it or you'll hate it. - -Note: - - The Verbose mode prints the variables currently visible where the exception - happened (shortening their strings if too long). This can potentially be - very slow, if you happen to have a huge data structure whose string - representation is complex to compute. Your computer may appear to freeze for - a while with cpu usage at 100%. If this occurs, you can cancel the traceback - with Ctrl-C (maybe hitting it more than once). - - If you encounter this kind of situation often, you may want to use the - Verbose_novars mode instead of the regular Verbose, which avoids formatting - variables (but otherwise includes the information and context given by - Verbose). - - -Installation instructions for ColorTB: - import sys,ultraTB - sys.excepthook = ultraTB.VerboseTB() - -Note: Much of the code in this module was lifted verbatim from the standard -library module 'traceback.py' and Ka-Ping Yee's 'cgitb.py'. - -* Color schemes -The colors are defined in the class TBTools through the use of the -ColorSchemeTable class. Currently the following exist: - - - NoColor: allows all of this module to be used in any terminal (the color - escapes are just dummy blank strings). - - - Linux: is meant to look good in a terminal like the Linux console (black - or very dark background). - - - LightBG: similar to Linux but swaps dark/light colors to be more readable - in light background terminals. - -You can implement other color schemes easily, the syntax is fairly -self-explanatory. Please send back new schemes you develop to the author for -possible inclusion in future releases. -""" - -#***************************************************************************** -# Copyright (C) 2001 Nathaniel Gray -# Copyright (C) 2001-2004 Fernando Perez -# -# Distributed under the terms of the BSD License. The full license is in -# the file COPYING, distributed as part of this software. -#***************************************************************************** - -# Required modules -import inspect -import keyword -import linecache -import os -import pydoc -import re -import string -import sys -import time -import tokenize -import traceback -import types - -# For purposes of monkeypatching inspect to fix a bug in it. -from inspect import getsourcefile, getfile, getmodule,\ - ismodule, isclass, ismethod, isfunction, istraceback, isframe, iscode - - -# IPython's own modules -# Modified pdb which doesn't damage IPython's readline handling -from IPython.utils import PyColorize -from IPython.core import debugger -from IPython.utils.ipstruct import Struct -from IPython.core.excolors import exception_colors -from IPython.utils.genutils import Term,uniq_stable,error,info - -# Globals -# amount of space to put line numbers before verbose tracebacks -INDENT_SIZE = 8 - -# Default color scheme. This is used, for example, by the traceback -# formatter. When running in an actual IPython instance, the user's rc.colors -# value is used, but havinga module global makes this functionality available -# to users of ultraTB who are NOT running inside ipython. -DEFAULT_SCHEME = 'NoColor' - -#--------------------------------------------------------------------------- -# Code begins - -# Utility functions -def inspect_error(): - """Print a message about internal inspect errors. - - These are unfortunately quite common.""" - - error('Internal Python error in the inspect module.\n' - 'Below is the traceback from this internal error.\n') - - -def findsource(object): - """Return the entire source file and starting line number for an object. - - The argument may be a module, class, method, function, traceback, frame, - or code object. The source code is returned as a list of all the lines - in the file and the line number indexes a line in that list. An IOError - is raised if the source code cannot be retrieved. - - FIXED version with which we monkeypatch the stdlib to work around a bug.""" - - file = getsourcefile(object) or getfile(object) - # If the object is a frame, then trying to get the globals dict from its - # module won't work. Instead, the frame object itself has the globals - # dictionary. - globals_dict = None - if inspect.isframe(object): - # XXX: can this ever be false? - globals_dict = object.f_globals - else: - module = getmodule(object, file) - if module: - globals_dict = module.__dict__ - lines = linecache.getlines(file, globals_dict) - if not lines: - raise IOError('could not get source code') - - if ismodule(object): - return lines, 0 - - if isclass(object): - name = object.__name__ - pat = re.compile(r'^(\s*)class\s*' + name + r'\b') - # make some effort to find the best matching class definition: - # use the one with the least indentation, which is the one - # that's most probably not inside a function definition. - candidates = [] - for i in range(len(lines)): - match = pat.match(lines[i]) - if match: - # if it's at toplevel, it's already the best one - if lines[i][0] == 'c': - return lines, i - # else add whitespace to candidate list - candidates.append((match.group(1), i)) - if candidates: - # this will sort by whitespace, and by line number, - # less whitespace first - candidates.sort() - return lines, candidates[0][1] - else: - raise IOError('could not find class definition') - - if ismethod(object): - object = object.im_func - if isfunction(object): - object = object.func_code - if istraceback(object): - object = object.tb_frame - if isframe(object): - object = object.f_code - if iscode(object): - if not hasattr(object, 'co_firstlineno'): - raise IOError('could not find function definition') - pat = re.compile(r'^(\s*def\s)|(.*(? 0: - if pmatch(lines[lnum]): break - lnum -= 1 - - return lines, lnum - raise IOError('could not find code object') - -# Monkeypatch inspect to apply our bugfix. This code only works with py25 -if sys.version_info[:2] >= (2,5): - inspect.findsource = findsource - -def fix_frame_records_filenames(records): - """Try to fix the filenames in each record from inspect.getinnerframes(). - - Particularly, modules loaded from within zip files have useless filenames - attached to their code object, and inspect.getinnerframes() just uses it. - """ - fixed_records = [] - for frame, filename, line_no, func_name, lines, index in records: - # Look inside the frame's globals dictionary for __file__, which should - # be better. - better_fn = frame.f_globals.get('__file__', None) - if isinstance(better_fn, str): - # Check the type just in case someone did something weird with - # __file__. It might also be None if the error occurred during - # import. - filename = better_fn - fixed_records.append((frame, filename, line_no, func_name, lines, index)) - return fixed_records - - -def _fixed_getinnerframes(etb, context=1,tb_offset=0): - import linecache - LNUM_POS, LINES_POS, INDEX_POS = 2, 4, 5 - - records = fix_frame_records_filenames(inspect.getinnerframes(etb, context)) - - # If the error is at the console, don't build any context, since it would - # otherwise produce 5 blank lines printed out (there is no file at the - # console) - rec_check = records[tb_offset:] - try: - rname = rec_check[0][1] - if rname == '' or rname.endswith(''): - return rec_check - except IndexError: - pass - - aux = traceback.extract_tb(etb) - assert len(records) == len(aux) - for i, (file, lnum, _, _) in zip(range(len(records)), aux): - maybeStart = lnum-1 - context//2 - start = max(maybeStart, 0) - end = start + context - lines = linecache.getlines(file)[start:end] - # pad with empty lines if necessary - if maybeStart < 0: - lines = (['\n'] * -maybeStart) + lines - if len(lines) < context: - lines += ['\n'] * (context - len(lines)) - buf = list(records[i]) - buf[LNUM_POS] = lnum - buf[INDEX_POS] = lnum - 1 - start - buf[LINES_POS] = lines - records[i] = tuple(buf) - return records[tb_offset:] - -# Helper function -- largely belongs to VerboseTB, but we need the same -# functionality to produce a pseudo verbose TB for SyntaxErrors, so that they -# can be recognized properly by ipython.el's py-traceback-line-re -# (SyntaxErrors have to be treated specially because they have no traceback) - -_parser = PyColorize.Parser() - -def _formatTracebackLines(lnum, index, lines, Colors, lvals=None,scheme=None): - numbers_width = INDENT_SIZE - 1 - res = [] - i = lnum - index - - # This lets us get fully syntax-highlighted tracebacks. - if scheme is None: - try: - # Again, reference to a global __IPYTHON__ that doesn't exist. - # XXX - scheme = __IPYTHON__.rc.colors - except: - scheme = DEFAULT_SCHEME - _line_format = _parser.format2 - - for line in lines: - new_line, err = _line_format(line,'str',scheme) - if not err: line = new_line - - if i == lnum: - # This is the line with the error - pad = numbers_width - len(str(i)) - if pad >= 3: - marker = '-'*(pad-3) + '-> ' - elif pad == 2: - marker = '> ' - elif pad == 1: - marker = '>' - else: - marker = '' - num = marker + str(i) - line = '%s%s%s %s%s' %(Colors.linenoEm, num, - Colors.line, line, Colors.Normal) - else: - num = '%*s' % (numbers_width,i) - line = '%s%s%s %s' %(Colors.lineno, num, - Colors.Normal, line) - - res.append(line) - if lvals and i == lnum: - res.append(lvals + '\n') - i = i + 1 - return res - - -#--------------------------------------------------------------------------- -# Module classes -class TBTools: - """Basic tools used by all traceback printer classes.""" - - def __init__(self,color_scheme = 'NoColor',call_pdb=False): - # Whether to call the interactive pdb debugger after printing - # tracebacks or not - self.call_pdb = call_pdb - - # Create color table - self.color_scheme_table = exception_colors() - - self.set_colors(color_scheme) - self.old_scheme = color_scheme # save initial value for toggles - - if call_pdb: - self.pdb = debugger.Pdb(self.color_scheme_table.active_scheme_name) - else: - self.pdb = None - - def set_colors(self,*args,**kw): - """Shorthand access to the color table scheme selector method.""" - - # Set own color table - self.color_scheme_table.set_active_scheme(*args,**kw) - # for convenience, set Colors to the active scheme - self.Colors = self.color_scheme_table.active_colors - # Also set colors of debugger - if hasattr(self,'pdb') and self.pdb is not None: - self.pdb.set_colors(*args,**kw) - - def color_toggle(self): - """Toggle between the currently active color scheme and NoColor.""" - - if self.color_scheme_table.active_scheme_name == 'NoColor': - self.color_scheme_table.set_active_scheme(self.old_scheme) - self.Colors = self.color_scheme_table.active_colors - else: - self.old_scheme = self.color_scheme_table.active_scheme_name - self.color_scheme_table.set_active_scheme('NoColor') - self.Colors = self.color_scheme_table.active_colors - -#--------------------------------------------------------------------------- -class ListTB(TBTools): - """Print traceback information from a traceback list, with optional color. - - Calling: requires 3 arguments: - (etype, evalue, elist) - as would be obtained by: - etype, evalue, tb = sys.exc_info() - if tb: - elist = traceback.extract_tb(tb) - else: - elist = None - - It can thus be used by programs which need to process the traceback before - printing (such as console replacements based on the code module from the - standard library). - - Because they are meant to be called without a full traceback (only a - list), instances of this class can't call the interactive pdb debugger.""" - - def __init__(self,color_scheme = 'NoColor'): - TBTools.__init__(self,color_scheme = color_scheme,call_pdb=0) - - def __call__(self, etype, value, elist): - Term.cout.flush() - print >> Term.cerr, self.text(etype,value,elist) - Term.cerr.flush() - - def text(self,etype, value, elist,context=5): - """Return a color formatted string with the traceback info.""" - - Colors = self.Colors - out_string = ['%s%s%s\n' % (Colors.topline,'-'*60,Colors.Normal)] - if elist: - out_string.append('Traceback %s(most recent call last)%s:' % \ - (Colors.normalEm, Colors.Normal) + '\n') - out_string.extend(self._format_list(elist)) - lines = self._format_exception_only(etype, value) - for line in lines[:-1]: - out_string.append(" "+line) - out_string.append(lines[-1]) - return ''.join(out_string) - - def _format_list(self, extracted_list): - """Format a list of traceback entry tuples for printing. - - Given a list of tuples as returned by extract_tb() or - extract_stack(), return a list of strings ready for printing. - Each string in the resulting list corresponds to the item with the - same index in the argument list. Each string ends in a newline; - the strings may contain internal newlines as well, for those items - whose source text line is not None. - - Lifted almost verbatim from traceback.py - """ - - Colors = self.Colors - list = [] - for filename, lineno, name, line in extracted_list[:-1]: - item = ' File %s"%s"%s, line %s%d%s, in %s%s%s\n' % \ - (Colors.filename, filename, Colors.Normal, - Colors.lineno, lineno, Colors.Normal, - Colors.name, name, Colors.Normal) - if line: - item = item + ' %s\n' % line.strip() - list.append(item) - # Emphasize the last entry - filename, lineno, name, line = extracted_list[-1] - item = '%s File %s"%s"%s, line %s%d%s, in %s%s%s%s\n' % \ - (Colors.normalEm, - Colors.filenameEm, filename, Colors.normalEm, - Colors.linenoEm, lineno, Colors.normalEm, - Colors.nameEm, name, Colors.normalEm, - Colors.Normal) - if line: - item = item + '%s %s%s\n' % (Colors.line, line.strip(), - Colors.Normal) - list.append(item) - return list - - def _format_exception_only(self, etype, value): - """Format the exception part of a traceback. - - The arguments are the exception type and value such as given by - sys.exc_info()[:2]. The return value is a list of strings, each ending - in a newline. Normally, the list contains a single string; however, - for SyntaxError exceptions, it contains several lines that (when - printed) display detailed information about where the syntax error - occurred. The message indicating which exception occurred is the - always last string in the list. - - Also lifted nearly verbatim from traceback.py - """ - - have_filedata = False - Colors = self.Colors - list = [] - try: - stype = Colors.excName + etype.__name__ + Colors.Normal - except AttributeError: - stype = etype # String exceptions don't get special coloring - if value is None: - list.append( str(stype) + '\n') - else: - if etype is SyntaxError: - try: - msg, (filename, lineno, offset, line) = value - except: - have_filedata = False - else: - have_filedata = True - #print 'filename is',filename # dbg - if not filename: filename = "" - list.append('%s File %s"%s"%s, line %s%d%s\n' % \ - (Colors.normalEm, - Colors.filenameEm, filename, Colors.normalEm, - Colors.linenoEm, lineno, Colors.Normal )) - if line is not None: - i = 0 - while i < len(line) and line[i].isspace(): - i = i+1 - list.append('%s %s%s\n' % (Colors.line, - line.strip(), - Colors.Normal)) - if offset is not None: - s = ' ' - for c in line[i:offset-1]: - if c.isspace(): - s = s + c - else: - s = s + ' ' - list.append('%s%s^%s\n' % (Colors.caret, s, - Colors.Normal) ) - value = msg - s = self._some_str(value) - if s: - list.append('%s%s:%s %s\n' % (str(stype), Colors.excName, - Colors.Normal, s)) - else: - list.append('%s\n' % str(stype)) - - # This is being commented out for now as the __IPYTHON__ variable - # referenced here is not resolved and causes massive test failures - # and errors. B. Granger, 04/2009. XXX - # See https://bugs.launchpad.net/bugs/362137 - # # vds:>> - # if have_filedata: - # __IPYTHON__.hooks.synchronize_with_editor(filename, lineno, 0) - # # vds:<< - - return list - - def _some_str(self, value): - # Lifted from traceback.py - try: - return str(value) - except: - return '' % type(value).__name__ - -#---------------------------------------------------------------------------- -class VerboseTB(TBTools): - """A port of Ka-Ping Yee's cgitb.py module that outputs color text instead - of HTML. Requires inspect and pydoc. Crazy, man. - - Modified version which optionally strips the topmost entries from the - traceback, to be used with alternate interpreters (because their own code - would appear in the traceback).""" - - def __init__(self,color_scheme = 'Linux',tb_offset=0,long_header=0, - call_pdb = 0, include_vars=1): - """Specify traceback offset, headers and color scheme. - - Define how many frames to drop from the tracebacks. Calling it with - tb_offset=1 allows use of this handler in interpreters which will have - their own code at the top of the traceback (VerboseTB will first - remove that frame before printing the traceback info).""" - TBTools.__init__(self,color_scheme=color_scheme,call_pdb=call_pdb) - self.tb_offset = tb_offset - self.long_header = long_header - self.include_vars = include_vars - - def text(self, etype, evalue, etb, context=5): - """Return a nice text document describing the traceback.""" - - # some locals - try: - etype = etype.__name__ - except AttributeError: - pass - Colors = self.Colors # just a shorthand + quicker name lookup - ColorsNormal = Colors.Normal # used a lot - col_scheme = self.color_scheme_table.active_scheme_name - indent = ' '*INDENT_SIZE - em_normal = '%s\n%s%s' % (Colors.valEm, indent,ColorsNormal) - undefined = '%sundefined%s' % (Colors.em, ColorsNormal) - exc = '%s%s%s' % (Colors.excName,etype,ColorsNormal) - - # some internal-use functions - def text_repr(value): - """Hopefully pretty robust repr equivalent.""" - # this is pretty horrible but should always return *something* - try: - return pydoc.text.repr(value) - except KeyboardInterrupt: - raise - except: - try: - return repr(value) - except KeyboardInterrupt: - raise - except: - try: - # all still in an except block so we catch - # getattr raising - name = getattr(value, '__name__', None) - if name: - # ick, recursion - return text_repr(name) - klass = getattr(value, '__class__', None) - if klass: - return '%s instance' % text_repr(klass) - except KeyboardInterrupt: - raise - except: - return 'UNRECOVERABLE REPR FAILURE' - def eqrepr(value, repr=text_repr): return '=%s' % repr(value) - def nullrepr(value, repr=text_repr): return '' - - # meat of the code begins - try: - etype = etype.__name__ - except AttributeError: - pass - - if self.long_header: - # Header with the exception type, python version, and date - pyver = 'Python ' + string.split(sys.version)[0] + ': ' + sys.executable - date = time.ctime(time.time()) - - head = '%s%s%s\n%s%s%s\n%s' % (Colors.topline, '-'*75, ColorsNormal, - exc, ' '*(75-len(str(etype))-len(pyver)), - pyver, string.rjust(date, 75) ) - head += "\nA problem occured executing Python code. Here is the sequence of function"\ - "\ncalls leading up to the error, with the most recent (innermost) call last." - else: - # Simplified header - head = '%s%s%s\n%s%s' % (Colors.topline, '-'*75, ColorsNormal,exc, - string.rjust('Traceback (most recent call last)', - 75 - len(str(etype)) ) ) - frames = [] - # Flush cache before calling inspect. This helps alleviate some of the - # problems with python 2.3's inspect.py. - linecache.checkcache() - # Drop topmost frames if requested - try: - # Try the default getinnerframes and Alex's: Alex's fixes some - # problems, but it generates empty tracebacks for console errors - # (5 blanks lines) where none should be returned. - #records = inspect.getinnerframes(etb, context)[self.tb_offset:] - #print 'python records:', records # dbg - records = _fixed_getinnerframes(etb, context,self.tb_offset) - #print 'alex records:', records # dbg - except: - - # FIXME: I've been getting many crash reports from python 2.3 - # users, traceable to inspect.py. If I can find a small test-case - # to reproduce this, I should either write a better workaround or - # file a bug report against inspect (if that's the real problem). - # So far, I haven't been able to find an isolated example to - # reproduce the problem. - inspect_error() - traceback.print_exc(file=Term.cerr) - info('\nUnfortunately, your original traceback can not be constructed.\n') - return '' - - # build some color string templates outside these nested loops - tpl_link = '%s%%s%s' % (Colors.filenameEm,ColorsNormal) - tpl_call = 'in %s%%s%s%%s%s' % (Colors.vName, Colors.valEm, - ColorsNormal) - tpl_call_fail = 'in %s%%s%s(***failed resolving arguments***)%s' % \ - (Colors.vName, Colors.valEm, ColorsNormal) - tpl_local_var = '%s%%s%s' % (Colors.vName, ColorsNormal) - tpl_global_var = '%sglobal%s %s%%s%s' % (Colors.em, ColorsNormal, - Colors.vName, ColorsNormal) - tpl_name_val = '%%s %s= %%s%s' % (Colors.valEm, ColorsNormal) - tpl_line = '%s%%s%s %%s' % (Colors.lineno, ColorsNormal) - tpl_line_em = '%s%%s%s %%s%s' % (Colors.linenoEm,Colors.line, - ColorsNormal) - - # now, loop over all records printing context and info - abspath = os.path.abspath - for frame, file, lnum, func, lines, index in records: - #print '*** record:',file,lnum,func,lines,index # dbg - try: - file = file and abspath(file) or '?' - except OSError: - # if file is '' or something not in the filesystem, - # the abspath call will throw an OSError. Just ignore it and - # keep the original file string. - pass - link = tpl_link % file - try: - args, varargs, varkw, locals = inspect.getargvalues(frame) - except: - # This can happen due to a bug in python2.3. We should be - # able to remove this try/except when 2.4 becomes a - # requirement. Bug details at http://python.org/sf/1005466 - inspect_error() - traceback.print_exc(file=Term.cerr) - info("\nIPython's exception reporting continues...\n") - - if func == '?': - call = '' - else: - # Decide whether to include variable details or not - var_repr = self.include_vars and eqrepr or nullrepr - try: - call = tpl_call % (func,inspect.formatargvalues(args, - varargs, varkw, - locals,formatvalue=var_repr)) - except KeyError: - # Very odd crash from inspect.formatargvalues(). The - # scenario under which it appeared was a call to - # view(array,scale) in NumTut.view.view(), where scale had - # been defined as a scalar (it should be a tuple). Somehow - # inspect messes up resolving the argument list of view() - # and barfs out. At some point I should dig into this one - # and file a bug report about it. - inspect_error() - traceback.print_exc(file=Term.cerr) - info("\nIPython's exception reporting continues...\n") - call = tpl_call_fail % func - - # Initialize a list of names on the current line, which the - # tokenizer below will populate. - names = [] - - def tokeneater(token_type, token, start, end, line): - """Stateful tokeneater which builds dotted names. - - The list of names it appends to (from the enclosing scope) can - contain repeated composite names. This is unavoidable, since - there is no way to disambguate partial dotted structures until - the full list is known. The caller is responsible for pruning - the final list of duplicates before using it.""" - - # build composite names - if token == '.': - try: - names[-1] += '.' - # store state so the next token is added for x.y.z names - tokeneater.name_cont = True - return - except IndexError: - pass - if token_type == tokenize.NAME and token not in keyword.kwlist: - if tokeneater.name_cont: - # Dotted names - names[-1] += token - tokeneater.name_cont = False - else: - # Regular new names. We append everything, the caller - # will be responsible for pruning the list later. It's - # very tricky to try to prune as we go, b/c composite - # names can fool us. The pruning at the end is easy - # to do (or the caller can print a list with repeated - # names if so desired. - names.append(token) - elif token_type == tokenize.NEWLINE: - raise IndexError - # we need to store a bit of state in the tokenizer to build - # dotted names - tokeneater.name_cont = False - - def linereader(file=file, lnum=[lnum], getline=linecache.getline): - line = getline(file, lnum[0]) - lnum[0] += 1 - return line - - # Build the list of names on this line of code where the exception - # occurred. - try: - # This builds the names list in-place by capturing it from the - # enclosing scope. - tokenize.tokenize(linereader, tokeneater) - except IndexError: - # signals exit of tokenizer - pass - except tokenize.TokenError,msg: - _m = ("An unexpected error occurred while tokenizing input\n" - "The following traceback may be corrupted or invalid\n" - "The error message is: %s\n" % msg) - error(_m) - - # prune names list of duplicates, but keep the right order - unique_names = uniq_stable(names) - - # Start loop over vars - lvals = [] - if self.include_vars: - for name_full in unique_names: - name_base = name_full.split('.',1)[0] - if name_base in frame.f_code.co_varnames: - if locals.has_key(name_base): - try: - value = repr(eval(name_full,locals)) - except: - value = undefined - else: - value = undefined - name = tpl_local_var % name_full - else: - if frame.f_globals.has_key(name_base): - try: - value = repr(eval(name_full,frame.f_globals)) - except: - value = undefined - else: - value = undefined - name = tpl_global_var % name_full - lvals.append(tpl_name_val % (name,value)) - if lvals: - lvals = '%s%s' % (indent,em_normal.join(lvals)) - else: - lvals = '' - - level = '%s %s\n' % (link,call) - - if index is None: - frames.append(level) - else: - frames.append('%s%s' % (level,''.join( - _formatTracebackLines(lnum,index,lines,Colors,lvals, - col_scheme)))) - - # Get (safely) a string form of the exception info - try: - etype_str,evalue_str = map(str,(etype,evalue)) - except: - # User exception is improperly defined. - etype,evalue = str,sys.exc_info()[:2] - etype_str,evalue_str = map(str,(etype,evalue)) - # ... and format it - exception = ['%s%s%s: %s' % (Colors.excName, etype_str, - ColorsNormal, evalue_str)] - if type(evalue) is types.InstanceType: - try: - names = [w for w in dir(evalue) if isinstance(w, basestring)] - except: - # Every now and then, an object with funny inernals blows up - # when dir() is called on it. We do the best we can to report - # the problem and continue - _m = '%sException reporting error (object with broken dir())%s:' - exception.append(_m % (Colors.excName,ColorsNormal)) - etype_str,evalue_str = map(str,sys.exc_info()[:2]) - exception.append('%s%s%s: %s' % (Colors.excName,etype_str, - ColorsNormal, evalue_str)) - names = [] - for name in names: - value = text_repr(getattr(evalue, name)) - exception.append('\n%s%s = %s' % (indent, name, value)) - - # This is being commented out for now as the __IPYTHON__ variable - # referenced here is not resolved and causes massive test failures - # and errors. B. Granger, 04/2009. XXX - # See https://bugs.launchpad.net/bugs/362137 - # # vds: >> - # if records: - # filepath, lnum = records[-1][1:3] - # #print "file:", str(file), "linenb", str(lnum) # dbg - # filepath = os.path.abspath(filepath) - # __IPYTHON__.hooks.synchronize_with_editor(filepath, lnum, 0) - # # vds: << - - # return all our info assembled as a single string - return '%s\n\n%s\n%s' % (head,'\n'.join(frames),''.join(exception[0]) ) - - def debugger(self,force=False): - """Call up the pdb debugger if desired, always clean up the tb - reference. - - Keywords: - - - force(False): by default, this routine checks the instance call_pdb - flag and does not actually invoke the debugger if the flag is false. - The 'force' option forces the debugger to activate even if the flag - is false. - - If the call_pdb flag is set, the pdb interactive debugger is - invoked. In all cases, the self.tb reference to the current traceback - is deleted to prevent lingering references which hamper memory - management. - - Note that each call to pdb() does an 'import readline', so if your app - requires a special setup for the readline completers, you'll have to - fix that by hand after invoking the exception handler.""" - - if force or self.call_pdb: - if self.pdb is None: - self.pdb = debugger.Pdb( - self.color_scheme_table.active_scheme_name) - # the system displayhook may have changed, restore the original - # for pdb - dhook = sys.displayhook - sys.displayhook = sys.__displayhook__ - self.pdb.reset() - # Find the right frame so we don't pop up inside ipython itself - if hasattr(self,'tb'): - etb = self.tb - else: - etb = self.tb = sys.last_traceback - while self.tb.tb_next is not None: - self.tb = self.tb.tb_next - try: - if etb and etb.tb_next: - etb = etb.tb_next - self.pdb.botframe = etb.tb_frame - self.pdb.interaction(self.tb.tb_frame, self.tb) - finally: - sys.displayhook = dhook - - if hasattr(self,'tb'): - del self.tb - - def handler(self, info=None): - (etype, evalue, etb) = info or sys.exc_info() - self.tb = etb - Term.cout.flush() - print >> Term.cerr, self.text(etype, evalue, etb) - Term.cerr.flush() - - # Changed so an instance can just be called as VerboseTB_inst() and print - # out the right info on its own. - def __call__(self, etype=None, evalue=None, etb=None): - """This hook can replace sys.excepthook (for Python 2.1 or higher).""" - if etb is None: - self.handler() - else: - self.handler((etype, evalue, etb)) - try: - self.debugger() - except KeyboardInterrupt: - print "\nKeyboardInterrupt" - -#---------------------------------------------------------------------------- -class FormattedTB(VerboseTB,ListTB): - """Subclass ListTB but allow calling with a traceback. - - It can thus be used as a sys.excepthook for Python > 2.1. - - Also adds 'Context' and 'Verbose' modes, not available in ListTB. - - Allows a tb_offset to be specified. This is useful for situations where - one needs to remove a number of topmost frames from the traceback (such as - occurs with python programs that themselves execute other python code, - like Python shells). """ - - def __init__(self, mode = 'Plain', color_scheme='Linux', - tb_offset = 0,long_header=0,call_pdb=0,include_vars=0): - - # NEVER change the order of this list. Put new modes at the end: - self.valid_modes = ['Plain','Context','Verbose'] - self.verbose_modes = self.valid_modes[1:3] - - VerboseTB.__init__(self,color_scheme,tb_offset,long_header, - call_pdb=call_pdb,include_vars=include_vars) - self.set_mode(mode) - - def _extract_tb(self,tb): - if tb: - return traceback.extract_tb(tb) - else: - return None - - def text(self, etype, value, tb,context=5,mode=None): - """Return formatted traceback. - - If the optional mode parameter is given, it overrides the current - mode.""" - - if mode is None: - mode = self.mode - if mode in self.verbose_modes: - # verbose modes need a full traceback - return VerboseTB.text(self,etype, value, tb,context=5) - else: - # We must check the source cache because otherwise we can print - # out-of-date source code. - linecache.checkcache() - # Now we can extract and format the exception - elist = self._extract_tb(tb) - if len(elist) > self.tb_offset: - del elist[:self.tb_offset] - return ListTB.text(self,etype,value,elist) - - def set_mode(self,mode=None): - """Switch to the desired mode. - - If mode is not specified, cycles through the available modes.""" - - if not mode: - new_idx = ( self.valid_modes.index(self.mode) + 1 ) % \ - len(self.valid_modes) - self.mode = self.valid_modes[new_idx] - elif mode not in self.valid_modes: - raise ValueError, 'Unrecognized mode in FormattedTB: <'+mode+'>\n'\ - 'Valid modes: '+str(self.valid_modes) - else: - self.mode = mode - # include variable details only in 'Verbose' mode - self.include_vars = (self.mode == self.valid_modes[2]) - - # some convenient shorcuts - def plain(self): - self.set_mode(self.valid_modes[0]) - - def context(self): - self.set_mode(self.valid_modes[1]) - - def verbose(self): - self.set_mode(self.valid_modes[2]) - -#---------------------------------------------------------------------------- -class AutoFormattedTB(FormattedTB): - """A traceback printer which can be called on the fly. - - It will find out about exceptions by itself. - - A brief example: - - AutoTB = AutoFormattedTB(mode = 'Verbose',color_scheme='Linux') - try: - ... - except: - AutoTB() # or AutoTB(out=logfile) where logfile is an open file object - """ - def __call__(self,etype=None,evalue=None,etb=None, - out=None,tb_offset=None): - """Print out a formatted exception traceback. - - Optional arguments: - - out: an open file-like object to direct output to. - - - tb_offset: the number of frames to skip over in the stack, on a - per-call basis (this overrides temporarily the instance's tb_offset - given at initialization time. """ - - if out is None: - out = Term.cerr - Term.cout.flush() - if tb_offset is not None: - tb_offset, self.tb_offset = self.tb_offset, tb_offset - print >> out, self.text(etype, evalue, etb) - self.tb_offset = tb_offset - else: - print >> out, self.text(etype, evalue, etb) - out.flush() - try: - self.debugger() - except KeyboardInterrupt: - print "\nKeyboardInterrupt" - - def text(self,etype=None,value=None,tb=None,context=5,mode=None): - if etype is None: - etype,value,tb = sys.exc_info() - self.tb = tb - return FormattedTB.text(self,etype,value,tb,context=5,mode=mode) - -#--------------------------------------------------------------------------- -# A simple class to preserve Nathan's original functionality. -class ColorTB(FormattedTB): - """Shorthand to initialize a FormattedTB in Linux colors mode.""" - def __init__(self,color_scheme='Linux',call_pdb=0): - FormattedTB.__init__(self,color_scheme=color_scheme, - call_pdb=call_pdb) - -#---------------------------------------------------------------------------- -# module testing (minimal) -if __name__ == "__main__": - def spam(c, (d, e)): - x = c + d - y = c * d - foo(x, y) - - def foo(a, b, bar=1): - eggs(a, b + bar) - - def eggs(f, g, z=globals()): - h = f + g - i = f - g - return h / i - - print '' - print '*** Before ***' - try: - print spam(1, (2, 3)) - except: - traceback.print_exc() - print '' - - handler = ColorTB() - print '*** ColorTB ***' - try: - print spam(1, (2, 3)) - except: - apply(handler, sys.exc_info() ) - print '' - - handler = VerboseTB() - print '*** VerboseTB ***' - try: - print spam(1, (2, 3)) - except: - apply(handler, sys.exc_info() ) - print '' - diff --git a/IPython/kernel/core/util.py b/IPython/kernel/core/util.py index 1dceb4a..ae69642 100644 --- a/IPython/kernel/core/util.py +++ b/IPython/kernel/core/util.py @@ -106,6 +106,9 @@ def make_quoted_expr(s): def system_shell(cmd, verbose=False, debug=False, header=''): """ Execute a command in the system shell; always return None. + This returns None so it can be conveniently used in interactive loops + without getting the return value (typically 0) printed many times. + Parameters ---------- cmd : str @@ -117,11 +120,6 @@ def system_shell(cmd, verbose=False, debug=False, header=''): header : str Header to print to screen prior to the executed command. No extra newlines are added. - - Description - ----------- - This returns None so it can be conveniently used in interactive loops - without getting the return value (typically 0) printed many times. """ if verbose or debug: diff --git a/IPython/kernel/engineservice.py b/IPython/kernel/engineservice.py index b400320..e5dadb3 100644 --- a/IPython/kernel/engineservice.py +++ b/IPython/kernel/engineservice.py @@ -23,6 +23,10 @@ method that automatically added methods to engines. __docformat__ = "restructuredtext en" +# Tell nose to skip this module. I don't think we need this as nose +# shouldn't ever be run on this! +__test__ = {} + #------------------------------------------------------------------------------- # Copyright (C) 2008 The IPython Development Team # @@ -34,12 +38,9 @@ __docformat__ = "restructuredtext en" # Imports #------------------------------------------------------------------------------- -# Tell nose to skip the testing of this module -__test__ = {} - -import os, sys, copy +import copy +import sys import cPickle as pickle -from new import instancemethod from twisted.application import service from twisted.internet import defer, reactor @@ -47,11 +48,7 @@ from twisted.python import log, failure, components import zope.interface as zi from IPython.kernel.core.interpreter import Interpreter -from IPython.kernel import newserialized, error, util -from IPython.kernel.util import printer -from IPython.kernel.twistedutil import gatherBoth, DeferredList -from IPython.kernel import codeutil - +from IPython.kernel import newserialized, error #------------------------------------------------------------------------------- # Interface specification for the Engine diff --git a/IPython/kernel/error.py b/IPython/kernel/error.py index d91f9e0..77db614 100644 --- a/IPython/kernel/error.py +++ b/IPython/kernel/error.py @@ -4,6 +4,9 @@ __docformat__ = "restructuredtext en" +# Tell nose to skip this module +__test__ = {} + #------------------------------------------------------------------------------- # Copyright (C) 2008 The IPython Development Team # @@ -14,9 +17,9 @@ __docformat__ = "restructuredtext en" #------------------------------------------------------------------------------- # Imports #------------------------------------------------------------------------------- +from twisted.python import failure from IPython.kernel.core import error -from twisted.python import failure #------------------------------------------------------------------------------- # Error classes diff --git a/IPython/kernel/multiengineclient.py b/IPython/kernel/multiengineclient.py index 71a0bf8..5058165 100644 --- a/IPython/kernel/multiengineclient.py +++ b/IPython/kernel/multiengineclient.py @@ -86,14 +86,13 @@ class PendingResult(object): A user should not create a `PendingResult` instance by hand. - Methods - ======= + Methods: * `get_result` * `add_callback` - Properties - ========== + Properties: + * `r` """ diff --git a/IPython/kernel/newserialized.py b/IPython/kernel/newserialized.py index 38ba3c7..155ed0c 100644 --- a/IPython/kernel/newserialized.py +++ b/IPython/kernel/newserialized.py @@ -5,6 +5,9 @@ __docformat__ = "restructuredtext en" +# Tell nose to skip this module +__test__ = {} + #------------------------------------------------------------------------------- # Copyright (C) 2008 The IPython Development Team # @@ -18,8 +21,8 @@ __docformat__ = "restructuredtext en" import cPickle as pickle -from zope.interface import Interface, implements from twisted.python import components +from zope.interface import Interface, implements try: import numpy @@ -28,6 +31,10 @@ except ImportError: from IPython.kernel.error import SerializationError +#----------------------------------------------------------------------------- +# Classes and functions +#----------------------------------------------------------------------------- + class ISerialized(Interface): def getData(): diff --git a/IPython/kernel/scripts/ipcluster.py b/IPython/kernel/scripts/ipcluster.py old mode 100755 new mode 100644 diff --git a/IPython/kernel/task.py b/IPython/kernel/task.py index 79a691b..924d052 100644 --- a/IPython/kernel/task.py +++ b/IPython/kernel/task.py @@ -414,7 +414,7 @@ class ResultNS(object): This can be a bad idea, as it may corrupt standard behavior of the ns object. - Example + Examples -------- >>> ns = ResultNS({'a':17,'foo':range(3)}) diff --git a/IPython/lib/clipboard.py b/IPython/lib/clipboard.py new file mode 100644 index 0000000..89bf456 --- /dev/null +++ b/IPython/lib/clipboard.py @@ -0,0 +1,56 @@ +""" Utilities for accessing the platform's clipboard. +""" + +import subprocess +import sys + +from IPython.core.ipapi import TryNext + + +def win32_clipboard_get(): + """ Get the current clipboard's text on Windows. + + Requires Mark Hammond's pywin32 extensions. + """ + try: + import win32clipboard + except ImportError: + message = ("Getting text from the clipboard requires the pywin32 " + "extensions: http://sourceforge.net/projects/pywin32/") + raise TryNext(message) + win32clipboard.OpenClipboard() + text = win32clipboard.GetClipboardData(win32clipboard.CF_TEXT) + # FIXME: convert \r\n to \n? + win32clipboard.CloseClipboard() + return text + +def osx_clipboard_get(): + """ Get the clipboard's text on OS X. + """ + p = subprocess.Popen(['pbpaste', '-Prefer', 'ascii'], + stdout=subprocess.PIPE) + text, stderr = p.communicate() + # Text comes in with old Mac \r line endings. Change them to \n. + text = text.replace('\r', '\n') + return text + +def tkinter_clipboard_get(): + """ Get the clipboard's text using Tkinter. + + This is the default on systems that are not Windows or OS X. It may + interfere with other UI toolkits and should be replaced with an + implementation that uses that toolkit. + """ + try: + import Tkinter + except ImportError: + message = ("Getting text from the clipboard on this platform " + "requires Tkinter.") + raise TryNext(message) + root = Tkinter.Tk() + root.withdraw() + text = root.clipboard_get() + root.destroy() + return text + + diff --git a/IPython/lib/demo.py b/IPython/lib/demo.py index 69d17b0..d5989e6 100644 --- a/IPython/lib/demo.py +++ b/IPython/lib/demo.py @@ -111,6 +111,13 @@ has a few useful methods for navigation, like again(), edit(), jump(), seek() and back(). It can be reset for a new run via reset() or reloaded from disk (in case you've edited the source) via reload(). See their docstrings below. +Note: To make this simpler to explore, a file called "demo-exercizer.py" has +been added to the "docs/examples/core" directory. Just cd to this directory in +an IPython session, and type:: + + %run demo-exercizer.py + +and then follow the directions. Example ======= @@ -125,7 +132,7 @@ print 'Hello, welcome to an interactive IPython demo.' # The mark below defines a block boundary, which is a point where IPython will # stop execution and return to the interactive prompt. The dashes are actually # optional and used only as a visual aid to clearly separate blocks while -editing the demo code. +# editing the demo code. # stop x = 1 @@ -169,7 +176,7 @@ import shlex import sys from IPython.utils.PyColorize import Parser -from IPython.utils.genutils import marquee, file_read, file_readlines +from IPython.utils.genutils import marquee, file_read, file_readlines, Term __all__ = ['Demo','IPythonDemo','LineDemo','IPythonLineDemo','DemoError'] @@ -185,7 +192,7 @@ class Demo(object): re_auto = re_mark('auto') re_auto_all = re_mark('auto_all') - def __init__(self,fname,arg_str='',auto_all=None): + def __init__(self,src,title='',arg_str='',auto_all=None): """Make a new demo object. To run the demo, simply call the object. See the module docstring for full details and an example (you can use @@ -193,9 +200,14 @@ class Demo(object): Inputs: - - fname = filename. + - src is either a file, or file-like object, or a + string that can be resolved to a filename. Optional inputs: + + - title: a string to use as the demo name. Of most use when the demo + you are making comes from an object that has no filename, or if you + want an alternate denotation distinct from the filename. - arg_str(''): a string of arguments, internally converted to a list just like sys.argv, so the demo script can see a similar @@ -207,9 +219,24 @@ class Demo(object): can be changed at runtime simply by reassigning it to a boolean value. """ - - self.fname = fname - self.sys_argv = [fname] + shlex.split(arg_str) + if hasattr(src, "read"): + # It seems to be a file or a file-like object + self.fobj = src + self.fname = "from a file-like object" + if title == '': + self.title = "from a file-like object" + else: + self.title = title + else: + # Assume it's a string or something that can be converted to one + self.fobj = open(src) + self.fname = src + if title == '': + (filepath, filename) = os.path.split(src) + self.title = filename + else: + self.title = title + self.sys_argv = [src] + shlex.split(arg_str) self.auto_all = auto_all # get a few things from ipython. While it's a bit ugly design-wise, @@ -228,7 +255,7 @@ class Demo(object): def reload(self): """Reload source from disk and initialize state.""" # read data and parse into blocks - self.src = file_read(self.fname) + self.src = self.fobj.read() src_b = [b.strip() for b in self.re_stop.split(self.src) if b] self._silent = [bool(self.re_silent.findall(b)) for b in src_b] self._auto = [bool(self.re_auto.findall(b)) for b in src_b] @@ -277,7 +304,7 @@ class Demo(object): if index is None: if self.finished: - print 'Demo finished. Use reset() if you want to rerun it.' + print >>Term.cout, 'Demo finished. Use .reset() if you want to rerun it.' return None index = self.block_index else: @@ -346,26 +373,27 @@ class Demo(object): if index is None: return - print self.marquee('<%s> block # %s (%s remaining)' % - (self.fname,index,self.nblocks-index-1)) - sys.stdout.write(self.src_blocks_colored[index]) + print >>Term.cout, self.marquee('<%s> block # %s (%s remaining)' % + (self.title,index,self.nblocks-index-1)) + print >>Term.cout,(self.src_blocks_colored[index]) sys.stdout.flush() def show_all(self): """Show entire demo on screen, block by block""" - fname = self.fname + fname = self.title + title = self.title nblocks = self.nblocks silent = self._silent marquee = self.marquee for index,block in enumerate(self.src_blocks_colored): if silent[index]: - print marquee('<%s> SILENT block # %s (%s remaining)' % - (fname,index,nblocks-index-1)) + print >>Term.cout, marquee('<%s> SILENT block # %s (%s remaining)' % + (title,index,nblocks-index-1)) else: - print marquee('<%s> block # %s (%s remaining)' % - (fname,index,nblocks-index-1)) - print block, + print >>Term.cout, marquee('<%s> block # %s (%s remaining)' % + (title,index,nblocks-index-1)) + print >>Term.cout, block, sys.stdout.flush() def runlines(self,source): @@ -390,18 +418,18 @@ class Demo(object): next_block = self.src_blocks[index] self.block_index += 1 if self._silent[index]: - print marquee('Executing silent block # %s (%s remaining)' % + print >>Term.cout, marquee('Executing silent block # %s (%s remaining)' % (index,self.nblocks-index-1)) else: self.pre_cmd() self.show(index) if self.auto_all or self._auto[index]: - print marquee('output:') + print >>Term.cout, marquee('output:') else: - print marquee('Press to quit, to execute...'), + print >>Term.cout, marquee('Press to quit, to execute...'), ans = raw_input().strip() if ans: - print marquee('Block NOT executed') + print >>Term.cout, marquee('Block NOT executed') return try: save_argv = sys.argv @@ -419,10 +447,10 @@ class Demo(object): if self.block_index == self.nblocks: mq1 = self.marquee('END OF DEMO') if mq1: - # avoid spurious prints if empty marquees are used - print - print mq1 - print self.marquee('Use reset() if you want to rerun it.') + # avoid spurious print >>Term.cout,s if empty marquees are used + print >>Term.cout + print >>Term.cout, mq1 + print >>Term.cout, self.marquee('Use .reset() if you want to rerun it.') self.finished = True # These methods are meant to be overridden by subclasses who may wish to @@ -471,9 +499,9 @@ class LineDemo(Demo): def reload(self): """Reload source from disk and initialize state.""" # read data and parse into blocks - src_b = [l for l in file_readlines(self.fname) if l.strip()] + src_b = [l for l in self.fobj.readline() if l.strip()] nblocks = len(src_b) - self.src = os.linesep.join(file_readlines(self.fname)) + self.src = os.linesep.join(self.fobj.readlines()) self._silent = [False]*nblocks self._auto = [True]*nblocks self.auto_all = True @@ -494,29 +522,29 @@ class IPythonLineDemo(IPythonDemo,LineDemo): class ClearMixin(object): """Use this mixin to make Demo classes with less visual clutter. - + Demos using this mixin will clear the screen before every block and use blank marquees. - + Note that in order for the methods defined here to actually override those of the classes it's mixed with, it must go /first/ in the inheritance tree. For example: - + class ClearIPDemo(ClearMixin,IPythonDemo): pass - + will provide an IPythonDemo class with the mixin's features. """ def marquee(self,txt='',width=78,mark='*'): """Blank marquee that returns '' no matter what the input.""" return '' - + def pre_cmd(self): """Method called before executing each block. - + This one simply clears the screen.""" - os.system('clear') - + from IPython.utils.platutils import term_clear + term_clear() class ClearDemo(ClearMixin,Demo): pass diff --git a/IPython/testing/attic/ipdoctest.py b/IPython/testing/attic/ipdoctest.py deleted file mode 100755 index 44b89a1..0000000 --- a/IPython/testing/attic/ipdoctest.py +++ /dev/null @@ -1,800 +0,0 @@ -#!/usr/bin/env python -"""IPython-enhanced doctest module with unittest integration. - -This module is heavily based on the standard library's doctest module, but -enhances it with IPython support. This enables docstrings to contain -unmodified IPython input and output pasted from real IPython sessions. - -It should be possible to use this module as a drop-in replacement for doctest -whenever you wish to use IPython input. - -Since the module absorbs all normal doctest functionality, you can use a mix of -both plain Python and IPython examples in any given module, though not in the -same docstring. - -See a simple example at the bottom of this code which serves as self-test and -demonstration code. Simply run this file (use -v for details) to run the -tests. - -This module also contains routines to ease the integration of doctests with -regular unittest-based testing. In particular, see the DocTestLoader class and -the makeTestSuite utility function. - - -Limitations: - - - When generating examples for use as doctests, make sure that you have - pretty-printing OFF. This can be done either by starting ipython with the - flag '--nopprint', by setting pprint to 0 in your ipythonrc file, or by - interactively disabling it with %Pprint. This is required so that IPython - output matches that of normal Python, which is used by doctest for internal - execution. - - - Do not rely on specific prompt numbers for results (such as using - '_34==True', for example). For IPython tests run via an external process - the prompt numbers may be different, and IPython tests run as normal python - code won't even have these special _NN variables set at all. - - - IPython functions that produce output as a side-effect of calling a system - process (e.g. 'ls') can be doc-tested, but they must be handled in an - external IPython process. Such doctests must be tagged with: - - # ipdoctest: EXTERNAL - - so that the testing machinery handles them differently. Since these are run - via pexpect in an external process, they can't deal with exceptions or other - fancy featurs of regular doctests. You must limit such tests to simple - matching of the output. For this reason, I recommend you limit these kinds - of doctests to features that truly require a separate process, and use the - normal IPython ones (which have all the features of normal doctests) for - everything else. See the examples at the bottom of this file for a - comparison of what can be done with both types. -""" - -# Standard library imports -import __builtin__ -import doctest -import inspect -import os -import re -import sys -import unittest - -from doctest import * - -from IPython.utils import genutils -from IPython.core import ipapi - -########################################################################### -# -# We must start our own ipython object and heavily muck with it so that all the -# modifications IPython makes to system behavior don't send the doctest -# machinery into a fit. This code should be considered a gross hack, but it -# gets the job done. - -import IPython - -# Hack to restore __main__, which ipython modifies upon startup -_main = sys.modules.get('__main__') -ipython = IPython.Shell.IPShell(['--classic','--noterm_title']).IP -sys.modules['__main__'] = _main - -# Deactivate the various python system hooks added by ipython for -# interactive convenience so we don't confuse the doctest system -sys.displayhook = sys.__displayhook__ -sys.excepthook = sys.__excepthook__ - -# So that ipython magics and aliases can be doctested -__builtin__._ip = ipapi.get() - -# for debugging only!!! -#from IPython.Shell import IPShellEmbed;ipshell=IPShellEmbed(['--noterm_title']) # dbg - - -# runner -from IPython.lib.irunner import IPythonRunner -iprunner = IPythonRunner(echo=False) - -########################################################################### - -# A simple subclassing of the original with a different class name, so we can -# distinguish and treat differently IPython examples from pure python ones. -class IPExample(doctest.Example): pass - -class IPExternalExample(doctest.Example): - """Doctest examples to be run in an external process.""" - - def __init__(self, source, want, exc_msg=None, lineno=0, indent=0, - options=None): - # Parent constructor - doctest.Example.__init__(self,source,want,exc_msg,lineno,indent,options) - - # An EXTRA newline is needed to prevent pexpect hangs - self.source += '\n' - -class IPDocTestParser(doctest.DocTestParser): - """ - A class used to parse strings containing doctest examples. - - Note: This is a version modified to properly recognize IPython input and - convert any IPython examples into valid Python ones. - """ - # This regular expression is used to find doctest examples in a - # string. It defines three groups: `source` is the source code - # (including leading indentation and prompts); `indent` is the - # indentation of the first (PS1) line of the source code; and - # `want` is the expected output (including leading indentation). - - # Classic Python prompts or default IPython ones - _PS1_PY = r'>>>' - _PS2_PY = r'\.\.\.' - - _PS1_IP = r'In\ \[\d+\]:' - _PS2_IP = r'\ \ \ \.\.\.+:' - - _RE_TPL = r''' - # Source consists of a PS1 line followed by zero or more PS2 lines. - (?P - (?:^(?P [ ]*) (?P %s) .*) # PS1 line - (?:\n [ ]* (?P %s) .*)*) # PS2 lines - \n? # a newline - # Want consists of any non-blank lines that do not start with PS1. - (?P (?:(?![ ]*$) # Not a blank line - (?![ ]*%s) # Not a line starting with PS1 - (?![ ]*%s) # Not a line starting with PS2 - .*$\n? # But any other line - )*) - ''' - - _EXAMPLE_RE_PY = re.compile( _RE_TPL % (_PS1_PY,_PS2_PY,_PS1_PY,_PS2_PY), - re.MULTILINE | re.VERBOSE) - - _EXAMPLE_RE_IP = re.compile( _RE_TPL % (_PS1_IP,_PS2_IP,_PS1_IP,_PS2_IP), - re.MULTILINE | re.VERBOSE) - - def ip2py(self,source): - """Convert input IPython source into valid Python.""" - out = [] - newline = out.append - for line in source.splitlines(): - newline(ipython.prefilter(line,True)) - newline('') # ensure a closing newline, needed by doctest - return '\n'.join(out) - - def parse(self, string, name=''): - """ - Divide the given string into examples and intervening text, - and return them as a list of alternating Examples and strings. - Line numbers for the Examples are 0-based. The optional - argument `name` is a name identifying this string, and is only - used for error messages. - """ - string = string.expandtabs() - # If all lines begin with the same indentation, then strip it. - min_indent = self._min_indent(string) - if min_indent > 0: - string = '\n'.join([l[min_indent:] for l in string.split('\n')]) - - output = [] - charno, lineno = 0, 0 - - # Whether to convert the input from ipython to python syntax - ip2py = False - # Find all doctest examples in the string. First, try them as Python - # examples, then as IPython ones - terms = list(self._EXAMPLE_RE_PY.finditer(string)) - if terms: - # Normal Python example - Example = doctest.Example - else: - # It's an ipython example. Note that IPExamples are run - # in-process, so their syntax must be turned into valid python. - # IPExternalExamples are run out-of-process (via pexpect) so they - # don't need any filtering (a real ipython will be executing them). - terms = list(self._EXAMPLE_RE_IP.finditer(string)) - if re.search(r'#\s*ipdoctest:\s*EXTERNAL',string): - #print '-'*70 # dbg - #print 'IPExternalExample, Source:\n',string # dbg - #print '-'*70 # dbg - Example = IPExternalExample - else: - #print '-'*70 # dbg - #print 'IPExample, Source:\n',string # dbg - #print '-'*70 # dbg - Example = IPExample - ip2py = True - - for m in terms: - # Add the pre-example text to `output`. - output.append(string[charno:m.start()]) - # Update lineno (lines before this example) - lineno += string.count('\n', charno, m.start()) - # Extract info from the regexp match. - (source, options, want, exc_msg) = \ - self._parse_example(m, name, lineno,ip2py) - if Example is IPExternalExample: - options[doctest.NORMALIZE_WHITESPACE] = True - # Create an Example, and add it to the list. - if not self._IS_BLANK_OR_COMMENT(source): - output.append(Example(source, want, exc_msg, - lineno=lineno, - indent=min_indent+len(m.group('indent')), - options=options)) - # Update lineno (lines inside this example) - lineno += string.count('\n', m.start(), m.end()) - # Update charno. - charno = m.end() - # Add any remaining post-example text to `output`. - output.append(string[charno:]) - - return output - - def _parse_example(self, m, name, lineno,ip2py=False): - """ - Given a regular expression match from `_EXAMPLE_RE` (`m`), - return a pair `(source, want)`, where `source` is the matched - example's source code (with prompts and indentation stripped); - and `want` is the example's expected output (with indentation - stripped). - - `name` is the string's name, and `lineno` is the line number - where the example starts; both are used for error messages. - - Optional: - `ip2py`: if true, filter the input via IPython to convert the syntax - into valid python. - """ - - # Get the example's indentation level. - indent = len(m.group('indent')) - - # Divide source into lines; check that they're properly - # indented; and then strip their indentation & prompts. - source_lines = m.group('source').split('\n') - - # We're using variable-length input prompts - ps1 = m.group('ps1') - ps2 = m.group('ps2') - ps1_len = len(ps1) - - self._check_prompt_blank(source_lines, indent, name, lineno,ps1_len) - if ps2: - self._check_prefix(source_lines[1:], ' '*indent + ps2, name, lineno) - - source = '\n'.join([sl[indent+ps1_len+1:] for sl in source_lines]) - - if ip2py: - # Convert source input from IPython into valid Python syntax - source = self.ip2py(source) - - # Divide want into lines; check that it's properly indented; and - # then strip the indentation. Spaces before the last newline should - # be preserved, so plain rstrip() isn't good enough. - want = m.group('want') - want_lines = want.split('\n') - if len(want_lines) > 1 and re.match(r' *$', want_lines[-1]): - del want_lines[-1] # forget final newline & spaces after it - self._check_prefix(want_lines, ' '*indent, name, - lineno + len(source_lines)) - - # Remove ipython output prompt that might be present in the first line - want_lines[0] = re.sub(r'Out\[\d+\]: \s*?\n?','',want_lines[0]) - - want = '\n'.join([wl[indent:] for wl in want_lines]) - - # If `want` contains a traceback message, then extract it. - m = self._EXCEPTION_RE.match(want) - if m: - exc_msg = m.group('msg') - else: - exc_msg = None - - # Extract options from the source. - options = self._find_options(source, name, lineno) - - return source, options, want, exc_msg - - def _check_prompt_blank(self, lines, indent, name, lineno, ps1_len): - """ - Given the lines of a source string (including prompts and - leading indentation), check to make sure that every prompt is - followed by a space character. If any line is not followed by - a space character, then raise ValueError. - - Note: IPython-modified version which takes the input prompt length as a - parameter, so that prompts of variable length can be dealt with. - """ - space_idx = indent+ps1_len - min_len = space_idx+1 - for i, line in enumerate(lines): - if len(line) >= min_len and line[space_idx] != ' ': - raise ValueError('line %r of the docstring for %s ' - 'lacks blank after %s: %r' % - (lineno+i+1, name, - line[indent:space_idx], line)) - - -SKIP = register_optionflag('SKIP') - -class IPDocTestRunner(doctest.DocTestRunner): - """Modified DocTestRunner which can also run IPython tests. - - This runner is capable of handling IPython doctests that require - out-of-process output capture (such as system calls via !cmd or aliases). - Note however that because these tests are run in a separate process, many - of doctest's fancier capabilities (such as detailed exception analysis) are - not available. So try to limit such tests to simple cases of matching - actual output. - """ - - #///////////////////////////////////////////////////////////////// - # DocTest Running - #///////////////////////////////////////////////////////////////// - - def _run_iptest(self, test, out): - """ - Run the examples in `test`. Write the outcome of each example with one - of the `DocTestRunner.report_*` methods, using the writer function - `out`. Return a tuple `(f, t)`, where `t` is the number of examples - tried, and `f` is the number of examples that failed. The examples are - run in the namespace `test.globs`. - - IPython note: this is a modified version of the original __run() - private method to handle out-of-process examples. - """ - - if out is None: - out = sys.stdout.write - - # Keep track of the number of failures and tries. - failures = tries = 0 - - # Save the option flags (since option directives can be used - # to modify them). - original_optionflags = self.optionflags - - SUCCESS, FAILURE, BOOM = range(3) # `outcome` state - - check = self._checker.check_output - - # Process each example. - for examplenum, example in enumerate(test.examples): - - # If REPORT_ONLY_FIRST_FAILURE is set, then supress - # reporting after the first failure. - quiet = (self.optionflags & REPORT_ONLY_FIRST_FAILURE and - failures > 0) - - # Merge in the example's options. - self.optionflags = original_optionflags - if example.options: - for (optionflag, val) in example.options.items(): - if val: - self.optionflags |= optionflag - else: - self.optionflags &= ~optionflag - - # If 'SKIP' is set, then skip this example. - if self.optionflags & SKIP: - continue - - # Record that we started this example. - tries += 1 - if not quiet: - self.report_start(out, test, example) - - # Run the example in the given context (globs), and record - # any exception that gets raised. (But don't intercept - # keyboard interrupts.) - try: - # Don't blink! This is where the user's code gets run. - got = '' - # The code is run in an external process - got = iprunner.run_source(example.source,get_output=True) - except KeyboardInterrupt: - raise - except: - self.debugger.set_continue() # ==== Example Finished ==== - - outcome = FAILURE # guilty until proved innocent or insane - - if check(example.want, got, self.optionflags): - outcome = SUCCESS - - # Report the outcome. - if outcome is SUCCESS: - if not quiet: - self.report_success(out, test, example, got) - elif outcome is FAILURE: - if not quiet: - self.report_failure(out, test, example, got) - failures += 1 - elif outcome is BOOM: - if not quiet: - self.report_unexpected_exception(out, test, example, - exc_info) - failures += 1 - else: - assert False, ("unknown outcome", outcome) - - # Restore the option flags (in case they were modified) - self.optionflags = original_optionflags - - # Record and return the number of failures and tries. - - # Hack to access a parent private method by working around Python's - # name mangling (which is fortunately simple). - doctest.DocTestRunner._DocTestRunner__record_outcome(self,test, - failures, tries) - return failures, tries - - def run(self, test, compileflags=None, out=None, clear_globs=True): - """Run examples in `test`. - - This method will defer to the parent for normal Python examples, but it - will run IPython ones via pexpect. - """ - if not test.examples: - return - - if isinstance(test.examples[0],IPExternalExample): - self._run_iptest(test,out) - else: - DocTestRunner.run(self,test,compileflags,out,clear_globs) - - -class IPDebugRunner(IPDocTestRunner,doctest.DebugRunner): - """IPython-modified DebugRunner, see the original class for details.""" - - def run(self, test, compileflags=None, out=None, clear_globs=True): - r = IPDocTestRunner.run(self, test, compileflags, out, False) - if clear_globs: - test.globs.clear() - return r - - -class IPDocTestLoader(unittest.TestLoader): - """A test loader with IPython-enhanced doctest support. - - Instances of this loader will automatically add doctests found in a module - to the test suite returned by the loadTestsFromModule method. In - addition, at initialization time a string of doctests can be given to the - loader, enabling it to add doctests to a module which didn't have them in - its docstring, coming from an external source.""" - - - def __init__(self,dt_files=None,dt_modules=None,test_finder=None): - """Initialize the test loader. - - :Keywords: - - dt_files : list (None) - List of names of files to be executed as doctests. - - dt_modules : list (None) - List of module names to be scanned for doctests in their - docstrings. - - test_finder : instance (None) - Instance of a testfinder (see doctest for details). - """ - - if dt_files is None: dt_files = [] - if dt_modules is None: dt_modules = [] - self.dt_files = genutils.list_strings(dt_files) - self.dt_modules = genutils.list_strings(dt_modules) - if test_finder is None: - test_finder = doctest.DocTestFinder(parser=IPDocTestParser()) - self.test_finder = test_finder - - def loadTestsFromModule(self, module): - """Return a suite of all tests cases contained in the given module. - - If the loader was initialized with a doctests argument, then this - string is assigned as the module's docstring.""" - - # Start by loading any tests in the called module itself - suite = super(self.__class__,self).loadTestsFromModule(module) - - # Now, load also tests referenced at construction time as companion - # doctests that reside in standalone files - for fname in self.dt_files: - #print 'mod:',module # dbg - #print 'fname:',fname # dbg - #suite.addTest(doctest.DocFileSuite(fname)) - suite.addTest(doctest.DocFileSuite(fname,module_relative=False)) - # Add docstring tests from module, if given at construction time - for mod in self.dt_modules: - suite.addTest(doctest.DocTestSuite(mod, - test_finder=self.test_finder)) - - #ipshell() # dbg - return suite - -def my_import(name): - """Module importer - taken from the python documentation. - - This function allows importing names with dots in them.""" - - mod = __import__(name) - components = name.split('.') - for comp in components[1:]: - mod = getattr(mod, comp) - return mod - -def makeTestSuite(module_name,dt_files=None,dt_modules=None,idt=True): - """Make a TestSuite object for a given module, specified by name. - - This extracts all the doctests associated with a module using a - DocTestLoader object. - - :Parameters: - - - module_name: a string containing the name of a module with unittests. - - :Keywords: - - dt_files : list of strings - List of names of plain text files to be treated as doctests. - - dt_modules : list of strings - List of names of modules to be scanned for doctests in docstrings. - - idt : bool (True) - If True, return integrated doctests. This means that each filename - listed in dt_files is turned into a *single* unittest, suitable for - running via unittest's runner or Twisted's Trial runner. If false, the - dt_files parameter is returned unmodified, so that other test runners - (such as oilrun) can run the doctests with finer granularity. - """ - - mod = my_import(module_name) - if idt: - suite = IPDocTestLoader(dt_files,dt_modules).loadTestsFromModule(mod) - else: - suite = IPDocTestLoader(None,dt_modules).loadTestsFromModule(mod) - - if idt: - return suite - else: - return suite,dt_files - -# Copied from doctest in py2.5 and modified for our purposes (since they don't -# parametrize what we need) - -# For backward compatibility, a global instance of a DocTestRunner -# class, updated by testmod. -master = None - -def testmod(m=None, name=None, globs=None, verbose=None, - report=True, optionflags=0, extraglobs=None, - raise_on_error=False, exclude_empty=False): - """m=None, name=None, globs=None, verbose=None, report=True, - optionflags=0, extraglobs=None, raise_on_error=False, - exclude_empty=False - - Note: IPython-modified version which loads test finder and runners that - recognize IPython syntax in doctests. - - Test examples in docstrings in functions and classes reachable - from module m (or the current module if m is not supplied), starting - with m.__doc__. - - Also test examples reachable from dict m.__test__ if it exists and is - not None. m.__test__ maps names to functions, classes and strings; - function and class docstrings are tested even if the name is private; - strings are tested directly, as if they were docstrings. - - Return (#failures, #tests). - - See doctest.__doc__ for an overview. - - Optional keyword arg "name" gives the name of the module; by default - use m.__name__. - - Optional keyword arg "globs" gives a dict to be used as the globals - when executing examples; by default, use m.__dict__. A copy of this - dict is actually used for each docstring, so that each docstring's - examples start with a clean slate. - - Optional keyword arg "extraglobs" gives a dictionary that should be - merged into the globals that are used to execute examples. By - default, no extra globals are used. This is new in 2.4. - - Optional keyword arg "verbose" prints lots of stuff if true, prints - only failures if false; by default, it's true iff "-v" is in sys.argv. - - Optional keyword arg "report" prints a summary at the end when true, - else prints nothing at the end. In verbose mode, the summary is - detailed, else very brief (in fact, empty if all tests passed). - - Optional keyword arg "optionflags" or's together module constants, - and defaults to 0. This is new in 2.3. Possible values (see the - docs for details): - - DONT_ACCEPT_TRUE_FOR_1 - DONT_ACCEPT_BLANKLINE - NORMALIZE_WHITESPACE - ELLIPSIS - SKIP - IGNORE_EXCEPTION_DETAIL - REPORT_UDIFF - REPORT_CDIFF - REPORT_NDIFF - REPORT_ONLY_FIRST_FAILURE - - Optional keyword arg "raise_on_error" raises an exception on the - first unexpected exception or failure. This allows failures to be - post-mortem debugged. - - Advanced tomfoolery: testmod runs methods of a local instance of - class doctest.Tester, then merges the results into (or creates) - global Tester instance doctest.master. Methods of doctest.master - can be called directly too, if you want to do something unusual. - Passing report=0 to testmod is especially useful then, to delay - displaying a summary. Invoke doctest.master.summarize(verbose) - when you're done fiddling. - """ - global master - - # If no module was given, then use __main__. - if m is None: - # DWA - m will still be None if this wasn't invoked from the command - # line, in which case the following TypeError is about as good an error - # as we should expect - m = sys.modules.get('__main__') - - # Check that we were actually given a module. - if not inspect.ismodule(m): - raise TypeError("testmod: module required; %r" % (m,)) - - # If no name was given, then use the module's name. - if name is None: - name = m.__name__ - - #---------------------------------------------------------------------- - # fperez - make IPython finder and runner: - # Find, parse, and run all tests in the given module. - finder = DocTestFinder(exclude_empty=exclude_empty, - parser=IPDocTestParser()) - - if raise_on_error: - runner = IPDebugRunner(verbose=verbose, optionflags=optionflags) - else: - runner = IPDocTestRunner(verbose=verbose, optionflags=optionflags, - #checker=IPOutputChecker() # dbg - ) - - # /fperez - end of ipython changes - #---------------------------------------------------------------------- - - for test in finder.find(m, name, globs=globs, extraglobs=extraglobs): - runner.run(test) - - if report: - runner.summarize() - - if master is None: - master = runner - else: - master.merge(runner) - - return runner.failures, runner.tries - - -# Simple testing and example code -if __name__ == "__main__": - - def ipfunc(): - """ - Some ipython tests... - - In [1]: import os - - In [2]: cd / - / - - In [3]: 2+3 - Out[3]: 5 - - In [26]: for i in range(3): - ....: print i, - ....: print i+1, - ....: - 0 1 1 2 2 3 - - - Examples that access the operating system work: - - In [19]: cd /tmp - /tmp - - In [20]: mkdir foo_ipython - - In [21]: cd foo_ipython - /tmp/foo_ipython - - In [23]: !touch bar baz - - # We unfortunately can't just call 'ls' because its output is not - # seen by doctest, since it happens in a separate process - - In [24]: os.listdir('.') - Out[24]: ['bar', 'baz'] - - In [25]: cd /tmp - /tmp - - In [26]: rm -rf foo_ipython - - - It's OK to use '_' for the last result, but do NOT try to use IPython's - numbered history of _NN outputs, since those won't exist under the - doctest environment: - - In [7]: 3+4 - Out[7]: 7 - - In [8]: _+3 - Out[8]: 10 - """ - - def ipfunc_external(): - """ - Tests that must be run in an external process - - - # ipdoctest: EXTERNAL - - In [11]: for i in range(10): - ....: print i, - ....: print i+1, - ....: - 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 - - - In [1]: import os - - In [1]: print "hello" - hello - - In [19]: cd /tmp - /tmp - - In [20]: mkdir foo_ipython2 - - In [21]: cd foo_ipython2 - /tmp/foo_ipython2 - - In [23]: !touch bar baz - - In [24]: ls - bar baz - - In [24]: !ls - bar baz - - In [25]: cd /tmp - /tmp - - In [26]: rm -rf foo_ipython2 - """ - - def pyfunc(): - """ - Some pure python tests... - - >>> import os - - >>> 2+3 - 5 - - >>> for i in range(3): - ... print i, - ... print i+1, - ... - 0 1 1 2 2 3 - """ - - # Call the global testmod() just like you would with normal doctest - testmod() diff --git a/IPython/testing/attic/tcommon.py b/IPython/testing/attic/tcommon.py deleted file mode 100644 index 73f7362..0000000 --- a/IPython/testing/attic/tcommon.py +++ /dev/null @@ -1,36 +0,0 @@ -"""Common utilities for testing IPython. - -This file is meant to be used as - -from IPython.testing.tcommon import * - -by any test code. - -While a bit ugly, this helps us keep all testing facilities in one place, and -start coding standalone test scripts easily, which can then be pasted into the -larger test suites without any modifications required. -""" - -# Required modules and packages - -# Standard Python lib -import cPickle as pickle -import doctest -import math -import os -import sys -import unittest - -from pprint import pformat, pprint - -# From the IPython test lib -import tutils -from tutils import fullPath - -try: - import pexpect -except ImportError: - pexpect = None -else: - from IPython.testing.ipdoctest import IPDocTestLoader,makeTestSuite - diff --git a/IPython/testing/attic/testTEMPLATE.py b/IPython/testing/attic/testTEMPLATE.py deleted file mode 100755 index 2484a13..0000000 --- a/IPython/testing/attic/testTEMPLATE.py +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env python -# encoding: utf-8 -"""Simple template for unit tests. - -This file should be renamed to - -test_FEATURE.py - -so that it is recognized by the overall test driver (Twisted's 'trial'), which -looks for all test_*.py files in the current directory to extract tests from -them. -""" -__docformat__ = "restructuredtext en" - -#------------------------------------------------------------------------------- -# Copyright (C) 2005 Fernando Perez -# Brian E Granger -# Benjamin Ragan-Kelley -# -# Distributed under the terms of the BSD License. The full license is in -# the file COPYING, distributed as part of this software. -#------------------------------------------------------------------------------- - -#------------------------------------------------------------------------------- -# Imports -#------------------------------------------------------------------------------- - -from IPython.testing import tcommon -from IPython.testing.tcommon import * - -#------------------------------------------------------------------------------- -# Setup for inline and standalone doctests -#------------------------------------------------------------------------------- - - -# If you have standalone doctests in a separate file, set their names in the -# dt_files variable (as a single string or a list thereof). The mkPath call -# forms an absolute path based on the current file, it is not needed if you -# provide the full pahts. -dt_files = fullPath(__file__,[]) - - -# If you have any modules whose docstrings should be scanned for embedded tests -# as examples accorging to standard doctest practice, set them here (as a -# single string or a list thereof): -dt_modules = [] - -#------------------------------------------------------------------------------- -# Regular Unittests -#------------------------------------------------------------------------------- - -class FooTestCase(unittest.TestCase): - def test_foo(self): - pass - -#------------------------------------------------------------------------------- -# Regular Unittests -#------------------------------------------------------------------------------- - -# This ensures that the code will run either standalone as a script, or that it -# can be picked up by Twisted's `trial` test wrapper to run all the tests. -if tcommon.pexpect is not None: - if __name__ == '__main__': - unittest.main(testLoader=IPDocTestLoader(dt_files,dt_modules)) - else: - testSuite = lambda : makeTestSuite(__name__,dt_files,dt_modules) diff --git a/IPython/testing/attic/tstTEMPLATE_doctest.py b/IPython/testing/attic/tstTEMPLATE_doctest.py deleted file mode 100644 index a01b7f5..0000000 --- a/IPython/testing/attic/tstTEMPLATE_doctest.py +++ /dev/null @@ -1,16 +0,0 @@ -"""Run this file with - - irunner --python filename - -to generate valid doctest input. - -NOTE: make sure to ALWAYS have a blank line before comments, otherwise doctest -gets confused.""" - -#--------------------------------------------------------------------------- - -# Setup - all imports are done in tcommon -import tcommon; reload(tcommon) # for interactive use -from IPython.testing.tcommon import * - -# Doctest code begins here diff --git a/IPython/testing/attic/tstTEMPLATE_doctest.txt b/IPython/testing/attic/tstTEMPLATE_doctest.txt deleted file mode 100644 index 9f7d7e5..0000000 --- a/IPython/testing/attic/tstTEMPLATE_doctest.txt +++ /dev/null @@ -1,24 +0,0 @@ -Doctests for the ``XXX`` module -===================================== - -The way doctest loads these, the entire document is applied as a single test -rather than multiple individual ones, unfortunately. - - -Auto-generated tests --------------------- - -The tests below are generated from the companion file -test_toeplitz_doctest.py, which is run via IPython's irunner script to create -valid doctest input automatically. - -# Setup - all imports are done in tcommon ->>> from IPython.testing.tcommon import * - -# Rest of doctest goes here... - - -Manually generated tests ------------------------- - -These are one-off tests written by hand, copied from an interactive prompt. diff --git a/IPython/testing/decorators_numpy.py b/IPython/testing/decorators_numpy.py index 792dd00..540f3c7 100644 --- a/IPython/testing/decorators_numpy.py +++ b/IPython/testing/decorators_numpy.py @@ -45,10 +45,10 @@ def skipif(skip_condition=True, msg=None): Parameters ---------- skip_condition : bool or callable. - Flag to determine whether to skip test. If the condition is a - callable, it is used at runtime to dynamically make the decision. This - is useful for tests that may require costly imports, to delay the cost - until the test suite is actually executed. + Flag to determine whether to skip test. If the condition is a + callable, it is used at runtime to dynamically make the decision. This + is useful for tests that may require costly imports, to delay the cost + until the test suite is actually executed. msg : string Message to give on raising a SkipTest exception diff --git a/IPython/testing/iptest.py b/IPython/testing/iptest.py index 1401baf..306b9e0 100644 --- a/IPython/testing/iptest.py +++ b/IPython/testing/iptest.py @@ -24,6 +24,7 @@ import os import os.path as path import sys import subprocess +import tempfile import time import warnings @@ -50,6 +51,7 @@ def test_for(mod): 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') @@ -69,8 +71,9 @@ def make_exclude(): pjoin('IPython', 'frontend', 'process', 'winprocess.py'), pjoin('IPython_doctest_plugin'), pjoin('IPython', 'extensions', 'ipy_'), - pjoin('IPython', 'extensions', 'clearcmd'), + pjoin('IPython', 'extensions', 'PhysicalQInput'), pjoin('IPython', 'extensions', 'PhysicalQInteractive'), + pjoin('IPython', 'extensions', 'InterpreterPasteInput'), pjoin('IPython', 'extensions', 'scitedirector'), pjoin('IPython', 'extensions', 'numeric_formats'), pjoin('IPython', 'testing', 'attic'), @@ -88,6 +91,9 @@ def make_exclude(): if not have_gtk or not have_gobject: EXCLUDE.append(pjoin('IPython', 'lib', 'inputhookgtk')) + if not have_wx_aui: + EXCLUDE.append(pjoin('IPython', 'gui', 'wx', 'wxIPython')) + if not have_objc: EXCLUDE.append(pjoin('IPython', 'frontend', 'cocoa')) @@ -109,6 +115,27 @@ def make_exclude(): if not have_pexpect: EXCLUDE.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', + 'test_linefrontend')) + EXCLUDE.append(pjoin('IPython', 'frontend', 'tests', + 'test_frontendbase')) + EXCLUDE.append(pjoin('IPython', 'frontend', 'tests', + 'test_prefilterfrontend')) + EXCLUDE.append(pjoin('IPython', 'frontend', 'tests', + 'test_asyncfrontendbase')), + EXCLUDE.append(pjoin('IPython', 'testing', 'parametric')) + EXCLUDE.append(pjoin('IPython', 'testing', 'util')) + EXCLUDE.append(pjoin('IPython', 'testing', 'tests', + 'test_decorators_trial')) + # Skip shell always because of a bug in FakeModule. EXCLUDE.append(pjoin('IPython', 'core', 'shell')) @@ -118,6 +145,7 @@ def make_exclude(): return EXCLUDE + #----------------------------------------------------------------------------- # Functions and classes #----------------------------------------------------------------------------- @@ -196,9 +224,29 @@ class IPTester(object): # Assemble call self.call_args = self.runner+self.params - def run(self): - """Run the stored commands""" - return subprocess.call(self.call_args) + 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""" + return subprocess.call(self.call_args) def make_runners(): @@ -244,7 +292,7 @@ def run_iptestall(): t_start = time.time() for name,runner in runners.iteritems(): print '*'*77 - print 'IPython test set:', name + print 'IPython test group:',name res = runner.run() if res: failed[name] = res @@ -255,14 +303,14 @@ def run_iptestall(): # summarize results print print '*'*77 - print 'Ran %s test sets in %.3fs' % (nrunners, t_tests) + print 'Ran %s test groups in %.3fs' % (nrunners, t_tests) print if not failed: print 'OK' else: # 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 sets failed.' % (nfail, nrunners) + print 'ERROR - %s out of %s test groups failed.' % (nfail, nrunners) for name in failed: failed_runner = runners[name] print '-'*40 @@ -283,4 +331,4 @@ def main(): if __name__ == '__main__': - main() \ No newline at end of file + main() diff --git a/IPython/testing/plugin/ipdoctest.py b/IPython/testing/plugin/ipdoctest.py index 3428af4..47ccb77 100644 --- a/IPython/testing/plugin/ipdoctest.py +++ b/IPython/testing/plugin/ipdoctest.py @@ -208,6 +208,15 @@ def start_ipython(): _ip.IP.magic_run_ori = _ip.IP.magic_run _ip.IP.magic_run = im + # XXX - For some very bizarre reason, the loading of %history by default is + # failing. This needs to be fixed later, but for now at least this ensures + # that tests that use %hist run to completion. + from IPython.core import history + history.init_ipython(_ip) + if not hasattr(_ip.IP,'magic_history'): + raise RuntimeError("Can't load magics, aborting") + + # The start call MUST be made here. I'm not sure yet why it doesn't work if # it is made later, at plugin initialization time, but in all my tests, that's # the case. @@ -431,7 +440,29 @@ class DocTestCase(doctests.DocTestCase): _ip.IP.user_ns.update(self._dt_test.globs) self._dt_test.globs = _ip.IP.user_ns - doctests.DocTestCase.setUp(self) + super(DocTestCase, self).setUp() + + def tearDown(self): + # 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 + # + # delattr(__builtin__, self._result_var) + # + # without checking that the attribute really is there; it implicitly + # assumes it should have been set via displayhook. But if the + # displayhook was never called, this doesn't necessarily happen. I + # haven't been able to find a little self-contained example outside of + # ipython that would show the problem so I can report it to the nose + # team, but it does happen a lot in our code. + # + # So here, we just protect as narrowly as possible by trapping an + # attribute error whose message would be the name of self._result_var, + # and letting any other error propagate. + try: + super(DocTestCase, self).tearDown() + except AttributeError, exc: + if exc.args[0] != self._result_var: + raise # A simple subclassing of the original with a different class name, so we can diff --git a/IPython/testing/plugin/show_refs.py b/IPython/testing/plugin/show_refs.py index cbba10f..d829644 100644 --- a/IPython/testing/plugin/show_refs.py +++ b/IPython/testing/plugin/show_refs.py @@ -10,9 +10,10 @@ class C(object): pass #print 'deleting object...' # dbg -c = C() +if __name__ == '__main__': + c = C() -c_refs = gc.get_referrers(c) -ref_ids = map(id,c_refs) + c_refs = gc.get_referrers(c) + ref_ids = map(id,c_refs) -print 'c referrers:',map(type,c_refs) + print 'c referrers:',map(type,c_refs) diff --git a/IPython/testing/util.py b/IPython/testing/util.py index 7036650..5c4253f 100644 --- a/IPython/testing/util.py +++ b/IPython/testing/util.py @@ -21,7 +21,7 @@ from twisted.internet import defer class DeferredTestCase(unittest.TestCase): def assertDeferredEquals(self, deferred, expectedResult, - chainDeferred=None): + chainDeferred=None): """Calls assertEquals on the result of the deferred and expectedResult. chainDeferred can be used to pass in previous Deferred objects that @@ -32,7 +32,7 @@ class DeferredTestCase(unittest.TestCase): if chainDeferred is None: chainDeferred = defer.succeed(None) - + def gotResult(actualResult): self.assertEquals(actualResult, expectedResult) @@ -41,7 +41,7 @@ class DeferredTestCase(unittest.TestCase): return chainDeferred.addCallback(lambda _: deferred) def assertDeferredRaises(self, deferred, expectedException, - chainDeferred=None): + chainDeferred=None): """Calls assertRaises on the Failure of the deferred and expectedException. chainDeferred can be used to pass in previous Deferred objects that diff --git a/IPython/utils/genutils.py b/IPython/utils/genutils.py index 1fe1622..f1aa8de 100644 --- a/IPython/utils/genutils.py +++ b/IPython/utils/genutils.py @@ -881,7 +881,7 @@ def doctest_reload(): This routine: - - reloads doctest + - imports doctest but does NOT reload it (see below). - resets its global 'master' attribute to None, so that multiple uses of the module interactively don't produce cumulative reports. @@ -890,20 +890,20 @@ def doctest_reload(): modified displayhook. Doctest expects the default displayhook behavior deep down, so our modification breaks it completely. For this reason, a hard monkeypatch seems like a reasonable solution rather than asking - users to manually use a different doctest runner when under IPython.""" + users to manually use a different doctest runner when under IPython. - import doctest - reload(doctest) - doctest.master=None + Notes + ----- - try: - doctest.DocTestRunner - except AttributeError: - # This is only for python 2.3 compatibility, remove once we move to - # 2.4 only. - pass - else: - doctest.DocTestRunner.run = dhook_wrap(doctest.DocTestRunner.run) + This function *used to* reload doctest, but this has been disabled because + reloading doctest unconditionally can cause massive breakage of other + doctest-dependent modules already in memory, such as those for IPython's + own testing system. The name wasn't changed to avoid breaking people's + code, but the reload call isn't actually made anymore.""" + + import doctest + doctest.master = None + doctest.DocTestRunner.run = dhook_wrap(doctest.DocTestRunner.run) #---------------------------------------------------------------------------- class HomeDirError(Error): diff --git a/IPython/utils/platutils.py b/IPython/utils/platutils.py index 380652e..ab5c2a7 100644 --- a/IPython/utils/platutils.py +++ b/IPython/utils/platutils.py @@ -23,11 +23,6 @@ elif sys.platform == 'win32': import platutils_win32 as _platutils else: import platutils_dummy as _platutils - import warnings - warnings.warn("Platutils not available for platform '%s', some features may be missing" % - os.name) - del warnings - # Functionality that's logically common to all platforms goes here, each # platform-specific module only provides the bits that are OS-dependent. @@ -36,6 +31,9 @@ else: # there is a public, cross-platform way of toggling the term title control on # and off. We should make this a stateful object later on so that each user # can have its own instance if needed. +def term_clear(): + _platutils.term_clear() + def toggle_set_term_title(val): """Control whether set_term_title is active or not. @@ -103,4 +101,3 @@ def get_long_path_name(path): def freeze_term_title(): warnings.warn("This function is deprecated, use toggle_set_term_title()") _platutils.ignore_termtitle = True - diff --git a/IPython/utils/platutils_posix.py b/IPython/utils/platutils_posix.py index b306479..3c38dba 100644 --- a/IPython/utils/platutils_posix.py +++ b/IPython/utils/platutils_posix.py @@ -20,6 +20,7 @@ ignore_termtitle = True def _dummy_op(*a, **b): """ A no-op function """ + def _set_term_title_xterm(title): """ Change virtual terminal title in xterm-workalikes """ @@ -31,10 +32,16 @@ if os.environ.get('TERM','') == 'xterm': else: set_term_title = _dummy_op + def find_cmd(cmd): """Find the full path to a command using which.""" return os.popen('which %s' % cmd).read().strip() + def get_long_path_name(path): """Dummy no-op.""" return path + + +def term_clear(): + os.system('clear') diff --git a/IPython/utils/platutils_win32.py b/IPython/utils/platutils_win32.py index 66ae27f..9afc060 100644 --- a/IPython/utils/platutils_win32.py +++ b/IPython/utils/platutils_win32.py @@ -42,18 +42,26 @@ except ImportError: # non-zero return code signals error, don't try again ignore_termtitle = True + def find_cmd(cmd): """Find the full path to a .bat or .exe using the win32api module.""" try: - import win32api + from win32api import SearchPath except ImportError: raise ImportError('you need to have pywin32 installed for this to work') else: - try: - (path, offest) = win32api.SearchPath(os.environ['PATH'],cmd + '.exe') - except: - (path, offset) = win32api.SearchPath(os.environ['PATH'],cmd + '.bat') - return path + PATH = os.environ['PATH'] + extensions = ['.exe', '.com', '.bat', '.py'] + path = None + for ext in extensions: + try: + path = SearchPath(PATH,cmd + ext)[0] + except: + pass + if path is None: + raise OSError("command %r not found" % cmd) + else: + return path def get_long_path_name(path): @@ -80,3 +88,7 @@ def get_long_path_name(path): return path else: return buf.value + + +def term_clear(): + os.system('cls') diff --git a/docs/examples/core/demo-exercizer.py b/docs/examples/core/demo-exercizer.py new file mode 100644 index 0000000..4085a03 --- /dev/null +++ b/docs/examples/core/demo-exercizer.py @@ -0,0 +1,85 @@ +"""This is meant to be run from the IPython prompt: + +%run demo-exercizer.py + +It will create demo objects of the example that is embedded in demo.py in a +number of ways and allow you to see how they work, just follow the printed +directions.""" + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +# From std lib +import StringIO +import os +import shutil +import tempfile + +# From IPython +from IPython.demo import (Demo, IPythonDemo, LineDemo, IPythonLineDemo, + ClearDemo, ClearIPDemo) + +#----------------------------------------------------------------------------- +# Demo code +#----------------------------------------------------------------------------- + +example1 = """ +'''A simple interactive demo to illustrate the use of IPython's Demo class.''' + +print 'Hello, welcome to an interactive IPython demo.' + +# The mark below defines a block boundary, which is a point where IPython will +# stop execution and return to the interactive prompt. The dashes are actually +# optional and used only as a visual aid to clearly separate blocks while +# editing the demo code. +# stop + +x = 1 +y = 2 + +# stop + +# the mark below makes this block as silent +# silent + +print 'This is a silent block, which gets executed but not printed.' + +# stop +# auto +print 'This is an automatic block.' +print 'It is executed without asking for confirmation, but printed.' +z = x+y + +print 'z=',x + +# stop +# This is just another normal block. +print 'z is now:', z + +print 'bye!' +""" +fp = tempfile.mkdtemp(prefix = 'DemoTmp') +fd, filename = tempfile.mkstemp(prefix = 'demoExample1File', suffix = '.py', + dir = fp) +f = os.fdopen(fd, 'wt') + +f.write(example1) +f.close() + +my_d = Demo(filename) +my_cd = ClearDemo(filename) + +fobj = StringIO.StringIO(example1) +str_d = Demo(fobj, title='via stringio') + +print ''' +The example that is embeded in demo.py file has been used to create +the following 3 demos, and should now be available to use: + my_d() -- created from a file + my_cd() -- created from a file, a ClearDemo + str_d() -- same as above, but created via a StringIO object +Call by typing their name, (with parentheses), at the +ipython prompt, interact with the block, then call again +to run the next block. +''' diff --git a/docs/examples/kernel/task2.py b/docs/examples/kernel/task2.py old mode 100644 new mode 100755 index 661ad22..bfbe3e0 --- a/docs/examples/kernel/task2.py +++ b/docs/examples/kernel/task2.py @@ -3,6 +3,8 @@ from IPython.kernel import client import time +import sys +flush = sys.stdout.flush tc = client.TaskClient() mec = client.MultiEngineClient() @@ -16,6 +18,7 @@ for i in range(6): time.sleep(1.0) print "Queue status (vebose=False)" print tc.queue_status() + flush() for i in range(24): tc.run(client.StringTask('time.sleep(1)')) @@ -24,12 +27,14 @@ for i in range(6): time.sleep(1.0) print "Queue status (vebose=True)" print tc.queue_status(True) + flush() for i in range(12): tc.run(client.StringTask('time.sleep(2)')) print "Queue status (vebose=True)" print tc.queue_status(True) +flush() qs = tc.queue_status(True) sched = qs['scheduled'] @@ -41,4 +46,5 @@ for i in range(6): time.sleep(1.0) print "Queue status (vebose=True)" print tc.queue_status(True) + flush() diff --git a/docs/man/ipcluster.1 b/docs/man/ipcluster.1 new file mode 100644 index 0000000..d04adad --- /dev/null +++ b/docs/man/ipcluster.1 @@ -0,0 +1,48 @@ +.TH IPCLUSTER 1 "October 28, 2008" "" "" +.SH NAME +\fBipcluster \- IPython parallel computing cluster control tool +.SH SYNOPSIS +.nf +.fam C +\fBipcluster\fP {\fmpiexec,local,mpirun,pbs,ssh\fP} [\fIoptions\fP] +.fam T +.fi +.SH DESCRIPTION +ipcluster is a control tool for IPython's parallel computing functions. + +IPython cluster startup. This starts a controller and engines using various +approaches. Use the IPYTHONDIR environment variable to change your IPython +directory from the default of .ipython or _ipython. The log and security +subdirectories of your IPython directory will be used by this script for log +files and security files. +.SH POSITIONAL ARGUMENTS + +The first positional argument should be one of: {mpiexec, local, mpirun, pbs, +ssh}, which are the available cluster types. + +For detailed help on each, type "ipcluster TYPE --help". Briefly: + + local run a local cluster + mpirun run a cluster using mpirun (mpiexec also works) + mpiexec run a cluster using mpiexec (mpirun also works) + pbs run a pbs cluster + ssh run a cluster using ssh, should have ssh-keys setup +.SH OPTIONS +.TP +.B +\-h, \-\-help +show help message and exit +.SH EXAMPLE +ipcluster local -n 4 + +This command will start 4 IPython engines on the local computer. +.SH SEE ALSO +.BR ipython(1), ipcontroller(1), ipengine(1) +.br +.SH AUTHOR +\fBipcluster\fP is a tool that ships with IPython, created by +the IPython Development Team. +.PP +This manual page was written by Stephan Peijnik , +for the Debian project (but may be used by others). Modified by Fernando Perez + for inclusion in IPython. diff --git a/docs/man/ipcontroller.1 b/docs/man/ipcontroller.1 new file mode 100644 index 0000000..bb48081 --- /dev/null +++ b/docs/man/ipcontroller.1 @@ -0,0 +1,87 @@ +.TH IPCONTROLLER 1 "October 29, 2008" "" "" +.SH NAME +\fBipcontroller \- IPython parallel computing controller control tool +.SH SYNOPSIS +.nf +.fam C +\fBipengine\fP [\fIoptions\fP] +.fam T +.fi +.SH DESCRIPTION +ipcontroller is a control tool for IPython's parallel computing functions. +.SH OPTIONS +.TP +.B +\-h, \-\-help +show this help message and exit +.TP +.B +\-\-client\-ip=CLIENT_IP +the IP address or hostname the controller will listen on for +client connections +.TP +.B +\-\-client\-port=CLIENT_PORT +the port the controller will listen on for client connections +.TP +.B +\-\-client\-location=CLIENT_LOCATION +hostname or ip for clients to connect to +.TP +.B +\-x +turn off all client security +.TP +.B +\-\-client\-cert\-file=CLIENT_CERT_FILE +file to store the client SSL certificate +.TP +.B +\-\-task\-furl\-file=TASK_FURL_FILE +file to store the FURL for task clients to connect with +.TP +.B +\-\-multiengine\-furl\-file=MULTIENGINE_FURL_FILE +file to store the FURL for multiengine clients to connect with +.TP +.B +\-\-engine\-ip=ENGINE_IP +the IP address or hostname the controller will listen on for engine connections +.TP +.B +\-\-engine\-port=ENGINE_PORT +the port the controller will listen on for engine connections +.TP +.B +\-\-engine\-location=ENGINE_LOCATION +the IP address or hostname for engine to connect to +.TP +.B +\-y +turn off all engine security +.TP +.B +\-\-engine\-cert\-file=ENGINE_CERT_FILE +file to store the engine SSL certificate +.TP +.B +\-\-engine\-furl\-file=ENGINE_FURL_FILE +file to store the FURL for engines to connect with +.TP +.B +\-l LOGFILE, \-\-logfile=LOGFILE +log file name (default is stdout) +.TP +.B +\-r +try to reuse all furl files +.SH SEE ALSO +.BR ipython(1), ipcluster(1), ipengine(1) +.br +.SH AUTHOR +\fBipcontroller\fP is a tool that ships with IPython, created by +the IPython Development Team. +.PP +This manual page was written by Stephan Peijnik , +for the Debian project (but may be used by others). Modified by Fernando Perez + for inclusion in IPython. diff --git a/docs/man/ipengine.1 b/docs/man/ipengine.1 new file mode 100644 index 0000000..e65c368 --- /dev/null +++ b/docs/man/ipengine.1 @@ -0,0 +1,38 @@ +.TH IPENGINE 1 "October 28, 2008" "" "" +.SH NAME +\fBipengine \- IPython parallel computing engine control tool +.SH SYNOPSIS +.nf +.fam C +\fBipengine\fP [\fIoptions\fP] +.fam T +.fi +.SH DESCRIPTION +ipengine is a control tool for IPython's parallel computing functions. +.SH OPTIONS +.TP +.B +\-h, \-\-help +show this help message and exit +.TP +.B +\-\-furl\-file=FURL_FILE +The filename containing the FURL of the controller +.TP +.B +\-\-mpi=MPI +How to enable MPI (mpi4py, pytrilions, or empty string to disable) +.TP +.B +\-l LOGFILE, \-\-logfile=LOGFILE +log file name (defaults to stdout) +.SH SEE ALSO +.BR ipython(1), ipcluster(1), ipcontroller(1) +.br +.SH AUTHOR +\fBipengine\fP is a tool that ships with IPython, created by +the IPython Development Team. +.PP +This manual page was written by Stephan Peijnik , +for the Debian project (but may be used by others). Modified by Fernando Perez + for inclusion in IPython. diff --git a/docs/man/ipython-wx.1 b/docs/man/ipython-wx.1 new file mode 100644 index 0000000..78c7f54 --- /dev/null +++ b/docs/man/ipython-wx.1 @@ -0,0 +1,25 @@ +.TH IPYTHON-WX 1 "October 29, 2008" "" "" +.SH NAME +\fBipython-wx \- Graphical frontend that embeds a multithreaded IPython Shell +.SH SYNOPSIS +.nf +.fam C +\fBipython-wx\fP +.fam T +.fi +.SH DESCRIPTION +ipython-wx is, compared to ipythonx, a more advanced graphical frontend +to IPython. It is implemented using WxWidgets. +.SH OPTIONS +.TP +.B +ipython-wx does not accept any command line options. +.SH SEE ALSO +.BR ipython(1), ipythonx(1) +.br +.SH AUTHOR +\fBipython-wx\fP is a tool that ships with IPython, created by +the IPython Development Team. +.PP +This manual page was written by Stephan Peijnik , +for the Debian project (but may be used by others). diff --git a/docs/man/ipythonx.1 b/docs/man/ipythonx.1 new file mode 100644 index 0000000..979da98 --- /dev/null +++ b/docs/man/ipythonx.1 @@ -0,0 +1,30 @@ +.TH IPYTHONX 1 "October 29, 2008" "" "" +.SH NAME +\fBipythonx \- Simple graphical frontend to IPython, using WxWidgets. +.SH SYNOPSIS +.nf +.fam C +\fBipengine\fP [\fIoptions\fP] +.fam T +.fi +.SH DESCRIPTION +ipythonx is a simple graphical frontend to IPython, using WxWidgets. +.SH OPTIONS +.TP +.B +\-h, \-\-help +show this help message and exit +.TP +.B +\-d, \-\-debug +Enable debug messages for the wx frontend. +look for config files and profiles in this directory +.SH SEE ALSO +.BR ipython(1), ipython-wx(1) +.br +.SH AUTHOR +\fBipythonx\fP is a tool that ships with IPython, created by +the IPython Development Team. +.PP +This manual page was written by Stephan Peijnik , +for the Debian project (but may be used by others). diff --git a/docs/man/irunner.1 b/docs/man/irunner.1 new file mode 100644 index 0000000..14edf62 --- /dev/null +++ b/docs/man/irunner.1 @@ -0,0 +1,49 @@ +.TH IRUNNER 1 "April 24, 2007" "" "" +.SH NAME +\fBirunner \- interactive runner interface +.SH SYNOPSIS +.nf +.fam C +\fBirunner\fP [\fIoptions\fP] \fIfile_to_run\fP +.fam T +.fi +.SH DESCRIPTION +irunner is an interface to the various interactive runners +available in IPython's \fBirunner\fP module. +.PP +The already implemented runners are listed below; adding +one for a new program is a trivial task, see the source +for examples. +.SH OPTIONS +.TP +.B +\-h, \-\-help +show this help message and exit +.TP +.B +\-\-ipython +IPython interactive runner (default). +.TP +.B +\-\-python +Python interactive runner. +.TP +.B +\-\-sage +SAGE interactive runner. +.SH EXAMPLE +irunner.py \-\-python \-\- \-\-help +will pass \-\-help to the python runner. +Similarly, +irunner.py \-\-ipython \-\- \-\-interact script.ipy +.SH SEE ALSO +.BR ipython(1) +.br +.SH AUTHOR +\fBirunner\fP is an extension of Ken Schutte 's +script contributed on the ipython-user list: +http://scipy.net/pipermail/ipython-user/2006-May/001705.html +.PP +This manual page was written by Bernd Zeimetz , for the Debian +project (but may be used by others). Modified by Fernando Perez + for inclusion in IPython. diff --git a/docs/source/changes.txt b/docs/source/changes.txt index c0e146e..6e703d0 100644 --- a/docs/source/changes.txt +++ b/docs/source/changes.txt @@ -1,3 +1,11 @@ +.. Developers should add in this file, during each release cycle, information +.. about important changes they've made, in a summary format that's meant for +.. end users. For each release we normally have three sections: features, bug +.. fixes and api breakage. +.. Please remember to credit the authors of the contributions by name, +.. especially when they are new users or developers who do not regularly +.. participate in IPython's development. + .. _changes: ========== @@ -6,19 +14,27 @@ What's new .. contents:: .. - 1 Release 0.9.1 - 2 Release 0.9 + 1 Release dev + 1.1 New features + 1.2 Bug fixes + 1.3 Backwards incompatible changes + 2 Release 0.10 2.1 New features 2.2 Bug fixes 2.3 Backwards incompatible changes - 2.4 Changes merged in from IPython1 - 2.4.1 New features - 2.4.2 Bug fixes - 2.4.3 Backwards incompatible changes - 3 Release 0.8.4 - 4 Release 0.8.3 - 5 Release 0.8.2 - 6 Older releases + 3 Release 0.9.1 + 4 Release 0.9 + 4.1 New features + 4.2 Bug fixes + 4.3 Backwards incompatible changes + 4.4 Changes merged in from IPython1 + 4.4.1 New features + 4.4.2 Bug fixes + 4.4.3 Backwards incompatible changes + 5 Release 0.8.4 + 6 Release 0.8.3 + 7 Release 0.8.2 + 8 Older releases .. Release dev @@ -27,25 +43,139 @@ Release dev New features ------------ +Bug fixes +--------- + +Backwards incompatible changes +------------------------------ + + +Release 0.10 +============ + +This release brings months of slow but steady development, and will be the last +before a major restructuring and cleanup of IPython's internals that is already +under way. For this reason, we hope that 0.10 will be a stable and robust +release so that while users adapt to some of the API changes that will come +with the refactoring that will become IPython 0.11, they can safely use 0.10 in +all existing projects with minimal changes (if any). + +IPython 0.10 is now a medium-sized project, with roughly (as reported by David +Wheeler's :command:`sloccount` utility) 40750 lines of Python code, and a diff +between 0.9.1 and this release that contains almost 28000 lines of code and +documentation. Our documentation, in PDF format, is a 495-page long PDF +document (also available in HTML format, both generated from the same sources). + +Many users and developers contributed code, features, bug reports and ideas to +this release. Please do not hesitate in contacting us if we've failed to +acknowledge your contribution here. In particular, for this release we have +contribution from the following people, a mix of new and regular names (in +alphabetical order by first name): + +* Alexander Clausen: fix #341726. +* Brian Granger: lots of work everywhere (features, bug fixes, etc). +* Daniel Ashbrook: bug report on MemoryError during compilation, now fixed. +* Darren Dale: improvements to documentation build system, feedback, design + ideas. +* Fernando Perez: various places. +* Gaël Varoquaux: core code, ipythonx GUI, design discussions, etc. Lots... +* John Hunter: suggestions, bug fixes, feedback. +* Jorgen Stenarson: work on many fronts, tests, fixes, win32 support, etc. +* Laurent Dufréchou: many improvements to ipython-wx standalone app. +* Lukasz Pankowski: prefilter, `%edit`, demo improvements. +* Matt Foster: TextMate support in `%edit`. +* Nathaniel Smith: fix #237073. +* Pauli Virtanen: fixes and improvements to extensions, documentation. +* Prabhu Ramachandran: improvements to `%timeit`. +* Robert Kern: several extensions. +* Sameer D'Costa: help on critical bug #269966. +* Stephan Peijnik: feedback on Debian compliance and many man pages. +* Tom Fetherston: many improvements to :mod:`IPython.demo` module. +* Ville Vainio: lots of work everywhere (features, bug fixes, etc). +* Vishal Vasta: ssh support in ipcluster. +* Walter Doerwald: work on the :mod:`IPython.ipipe` system. + +Below we give an overview of new features, bug fixes and backwards-incompatible +changes. For a detailed account of every change made, feel free to view the +project log with :command:`bzr log`. + +New features +------------ + +* New `%paste` magic automatically extracts current contents of clipboard and + pastes it directly, while correctly handling code that is indented or + prepended with `>>>` or `...` python prompt markers. A very useful new + feature contributed by Robert Kern. + +* IPython 'demos', created with the :mod:`IPython.demo` module, can now be + created from files on disk or strings in memory. Other fixes and + improvements to the demo system, by Tom Fetherston. + +* Added :func:`find_cmd` function to :mod:`IPython.platutils` module, to find + commands in a cross-platform manner. + +* Many improvements and fixes to Gaël Varoquaux's :command:`ipythonx`, a + WX-based lightweight IPython instance that can be easily embedded in other WX + applications. These improvements have made it possible to now have an + embedded IPython in Mayavi and other tools. + +* :class:`MultiengineClient` objects now have a :meth:`benchmark` method. + +* The manual now includes a full set of auto-generated API documents from the + code sources, using Sphinx and some of our own support code. We are now + using the `Numpy Documentation Standard`_ for all docstrings, and we have + tried to update as many existing ones as possible to this format. + +.. _Numpy Documentation Standard: http://projects.scipy.org/numpy/wiki/CodingStyleGuidelines#docstring-standard + +* The new :mod:`IPython.Extensions.ipy_pretty` extension by Robert Kern + provides configurable pretty-printing. + +* Many improvements to the :command:`ipython-wx` standalone WX-based IPython + application by Laurent Dufréchou. It can optionally run in a thread, and + this can be toggled at runtime (allowing the loading of Matplotlib in a + running session without ill effects). + +* IPython includes a copy of Steven Bethard's argparse_ in the + :mod:`IPython.external` package, so we can use it internally and it is also + available to any IPython user. By installing it in this manner, we ensure + zero conflicts with any system-wide installation you may already have while + minimizing external dependencies for new users. + +* An improved and much more robust test suite, that runs groups of tests in + separate subprocesses using either Nose or Twisted's :command:`trial` runner + to ensure proper management of Twisted-using code. The test suite degrades + gracefully if optional dependencies are not available, so that the + :command:`iptest` command can be run with only Nose installed and nothing + else. We also have more and cleaner test decorators to better select tests + depending on runtime conditions, do setup/teardown, etc. + * The new ipcluster now has a fully working ssh mode that should work on Linux, Unix and OS X. Thanks to Vishal Vatsa for implementing this! * The wonderful TextMate editor can now be used with %edit on OS X. Thanks to Matt Foster for this patch. +* The documentation regarding parallel uses of IPython, including MPI and PBS, + has been significantly updated and improved. + +* The developer guidelines in the documentation have been updated to explain + our workflow using :command:`bzr` and Launchpad. + * Fully refactored :command:`ipcluster` command line program for starting IPython clusters. This new version is a complete rewrite and 1) is fully cross platform (we now use Twisted's process management), 2) has much - improved performance, 3) uses subcommands for different types of clusters, - 4) uses argparse for parsing command line options, 5) has better support - for starting clusters using :command:`mpirun`, 6) has experimental support - for starting engines using PBS. However, this new version of ipcluster - should be considered a technology preview. We plan on changing the API - in significant ways before it is final. + improved performance, 3) uses subcommands for different types of clusters, 4) + uses argparse for parsing command line options, 5) has better support for + starting clusters using :command:`mpirun`, 6) has experimental support for + starting engines using PBS. It can also reuse FURL files, by appropriately + passing options to its subcommands. However, this new version of ipcluster + should be considered a technology preview. We plan on changing the API in + significant ways before it is final. * The :mod:`argparse` module has been added to :mod:`IPython.external`. -* Fully description of the security model added to the docs. +* Full description of the security model added to the docs. * cd completer: show bookmarks if no other completions are available. @@ -55,13 +185,45 @@ New features [~]|1> _prompt_title = 'sudo!' sudo![~]|2> -* %edit: If you do '%edit pasted_block', pasted_block - variable gets updated with new data (so repeated - editing makes sense) +* %edit: If you do '%edit pasted_block', pasted_block variable gets updated + with new data (so repeated editing makes sense) Bug fixes --------- +* Fix #368719, removed top-level debian/ directory to make the job of Debian + packagers easier. + +* Fix #291143 by including man pages contributed by Stephan Peijnik from the + Debian project. + +* Fix #358202, effectively a race condition, by properly synchronizing file + creation at cluster startup time. + +* `%timeit` now handles correctly functions that take a long time to execute + even the first time, by not repeating them. + +* Fix #239054, releasing of references after exiting. + +* Fix #341726, thanks to Alexander Clausen. + +* Fix #269966. This long-standing and very difficult bug (which is actually a + problem in Python itself) meant long-running sessions would inevitably grow + in memory size, often with catastrophic consequences if users had large + objects in their scripts. Now, using `%run` repeatedly should not cause any + memory leaks. Special thanks to John Hunter and Sameer D'Costa for their + help with this bug. + +* Fix #295371, bug in `%history`. + +* Improved support for py2exe. + +* Fix #270856: IPython hangs with PyGTK + +* Fix #270998: A magic with no docstring breaks the '%magic magic' + +* fix #271684: -c startup commands screw up raw vs. native history + * Numerous bugs on Windows with the new ipcluster have been fixed. * The ipengine and ipcontroller scripts now handle missing furl files @@ -81,12 +243,26 @@ Bug fixes Backwards incompatible changes ------------------------------ +* `ipykit` and related files were unmaintained and have been removed. + +* The :func:`IPython.genutils.doctest_reload` does not actually call + `reload(doctest)` anymore, as this was causing many problems with the test + suite. It still resets `doctest.master` to None. + +* While we have not deliberately broken Python 2.4 compatibility, only minor + testing was done with Python 2.4, while 2.5 and 2.6 were fully tested. But + if you encounter problems with 2.4, please do report them as bugs. + +* The :command:`ipcluster` now requires a mode argument; for example to start a + cluster on the local machine with 4 engines, you must now type:: + + $ ipcluster local -n 4 + * The controller now has a ``-r`` flag that needs to be used if you want to reuse existing furl files. Otherwise they are deleted (the default). -* Remove ipy_leo.py. "easy_install ipython-extension" to get it. - (done to decouple it from ipython release cycle) - +* Remove ipy_leo.py. You can use :command:`easy_install ipython-extension` to + get it. (done to decouple it from ipython release cycle) Release 0.9.1 diff --git a/docs/source/config/initial_config.txt b/docs/source/config/initial_config.txt index eec4dd0..c9c12e9 100644 --- a/docs/source/config/initial_config.txt +++ b/docs/source/config/initial_config.txt @@ -175,11 +175,16 @@ In order to configure less as your default pager, do the following: properly interpret control sequences, which is how color information is given to your terminal. +For the bash shell, add to your ~/.bashrc file the lines:: + + export PAGER=less + export LESS=-r + For the csh or tcsh shells, add to your ~/.cshrc file the lines:: setenv PAGER less setenv LESS -r - + There is similar syntax for other Unix shells, look at your system documentation for details. diff --git a/docs/source/development/overview.txt b/docs/source/development/overview.txt index 642fbbd..46dcb2e 100644 --- a/docs/source/development/overview.txt +++ b/docs/source/development/overview.txt @@ -476,8 +476,17 @@ if we want to have a codebase that works directly on both 2.x and 3.x. 1. The syntax for exceptions changed (PEP 3110). The old `except exc, var` changed to `except exc as var`. At last - count there was 78 occurences of this usage in the codebase - + count there was 78 occurences of this usage in the codebase. This + is a particularly problematic issue, because it's not easy to + implement it in a 2.5-compatible way. + +Because it is quite difficult to support simultaneously Python 2.5 and 3.x, we +will likely at some point put out a release that requires strictly 2.6 and +abandons 2.5 compatibility. This will then allow us to port the code to using +:func:`print` as a function, `except exc as var` syntax, etc. But as of +version 0.11 at least, we will retain Python 2.5 compatibility. + + .. [Bazaar] Bazaar. http://bazaar-vcs.org/ .. [Launchpad] Launchpad. http://www.launchpad.net/ipython .. [reStructuredText] reStructuredText. http://docutils.sourceforge.net/rst.html diff --git a/docs/source/install/install.txt b/docs/source/install/install.txt index 43377b3..971c4eb 100644 --- a/docs/source/install/install.txt +++ b/docs/source/install/install.txt @@ -9,14 +9,16 @@ install all of its dependencies. Please let us know if you have problems installing IPython or any of its -dependencies. IPython requires Python version 2.4 or greater. Light testing -has been done on version 2.6, and so far everything looks fine. We have *not* -yet started to port IPython to Python 3.0, where the language changes are much -more significant. +dependencies. Officially, IPython requires Python version 2.5 or 2.6. We +have *not* yet started to port IPython to Python 3.0. .. warning:: - IPython will not work with Python 2.4 or below. + Officially, IPython supports Python versions 2.5 and 2.6. + + IPython 0.10 has only been well tested with Python 2.5 and 2.6. Parts of + it may work with Python 2.4, but we do not officially support Python 2.4 + anymore. If you need to use 2.4, you can still run IPython 0.9. Some of the installation approaches use the :mod:`setuptools` package and its :command:`easy_install` command line program. In many scenarios, this provides @@ -85,8 +87,8 @@ Windows There are a few caveats for Windows users. The main issue is that a basic ``python setup.py install`` approach won't create ``.bat`` file or Start Menu -shortcuts, which most users want. To get an installation with these, there are -two choices: +shortcuts, which most users want. To get an installation with these, you can +use any of the following alternatives: 1. Install using :command:`easy_install`. @@ -96,6 +98,16 @@ two choices: 3. Install from source, but using :mod:`setuptools` (``python setupegg.py install``). +IPython by default runs in a termninal window, but the normal terminal +application supplied by Microsoft Windows is very primitive. You may want to +download the excellent and free Console_ application instead, which is a far +superior tool. You can even configure Console to give you by default an +IPython tab, which is very convenient to create new IPython sessions directly +from the working terminal. + +.. _Console: http://sourceforge.net/projects/console + + Installing the development version ---------------------------------- diff --git a/docs/source/interactive/reference.txt b/docs/source/interactive/reference.txt index 2478756..4cf25f4 100644 --- a/docs/source/interactive/reference.txt +++ b/docs/source/interactive/reference.txt @@ -1329,8 +1329,9 @@ For stand-alone use of the feature in your programs which do not use IPython at all, put the following lines toward the top of your 'main' routine:: - import sys,IPython.ultraTB - sys.excepthook = IPython.ultraTB.FormattedTB(mode='Verbose', + import sys + from IPython.core import ultratb + sys.excepthook = ultratb.FormattedTB(mode='Verbose', color_scheme='Linux', call_pdb=1) The mode keyword can be either 'Verbose' or 'Plain', giving either very diff --git a/docs/source/parallel/parallel_mpi.txt b/docs/source/parallel/parallel_mpi.txt index 4df70f3..a443462 100644 --- a/docs/source/parallel/parallel_mpi.txt +++ b/docs/source/parallel/parallel_mpi.txt @@ -38,7 +38,7 @@ To use code that calls MPI, there are typically two things that MPI requires. There are a couple of ways that you can start the IPython engines and get these things to happen. Automatic starting using :command:`mpiexec` and :command:`ipcluster` -------------------------------------------------------------------- +-------------------------------------------------------------------- The easiest approach is to use the `mpiexec` mode of :command:`ipcluster`, which will first start a controller and then a set of engines using :command:`mpiexec`:: @@ -48,7 +48,7 @@ This approach is best as interrupting :command:`ipcluster` will automatically stop and clean up the controller and engines. Manual starting using :command:`mpiexec` ---------------------------------------- +---------------------------------------- If you want to start the IPython engines using the :command:`mpiexec`, just do:: @@ -154,4 +154,4 @@ compiled C, C++ and Fortran libraries that have been exposed to Python. .. [MPI] Message Passing Interface. http://www-unix.mcs.anl.gov/mpi/ .. [mpi4py] MPI for Python. mpi4py: http://mpi4py.scipy.org/ .. [OpenMPI] Open MPI. http://www.open-mpi.org/ -.. [PyTrilinos] PyTrilinos. http://trilinos.sandia.gov/packages/pytrilinos/ \ No newline at end of file +.. [PyTrilinos] PyTrilinos. http://trilinos.sandia.gov/packages/pytrilinos/ diff --git a/ipykit.py b/ipykit.py deleted file mode 100755 index 622494f..0000000 --- a/ipykit.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -""" IPykit launcher - -w/o args, this launches a full ipykit session. - -If the first arg is a .py script, it will be run WITHOUT ipython, -to facilitate running python scripts almost normally on machines w/o python -in their own process (as opposed to %run). - -""" - -import sys -if len(sys.argv) > 1 and sys.argv[1].endswith('.py'): - # shortcut for running ipykit.exe directly on a .py file - do not bother - # starting ipython, just handle as normal python scripts - sys.argv = sys.argv[1:] - execfile(sys.argv[0]) -else: - import IPython - IPython.Shell.start().mainloop() diff --git a/scripts/ipython_win_post_install.py b/scripts/ipython_win_post_install.py index 7769861..a965ee8 100755 --- a/scripts/ipython_win_post_install.py +++ b/scripts/ipython_win_post_install.py @@ -65,11 +65,7 @@ def install(): link = pjoin(ip_start_menu, 'scipy.lnk') cmd = '"%s" -p scipy' % ipybase mkshortcut(python,'IPython (scipy profile)',link,cmd) - - link = pjoin(ip_start_menu, 'IPython test suite.lnk') - cmd = '"%s" -vv' % pjoin(scripts, 'iptest') - mkshortcut(python,'Run the IPython test suite',link,cmd) - + link = pjoin(ip_start_menu, 'ipcontroller.lnk') cmd = '"%s" -xy' % pjoin(scripts, 'ipcontroller') mkshortcut(python,'IPython controller',link,cmd) diff --git a/setup.py b/setup.py index f572729..afca429 100755 --- a/setup.py +++ b/setup.py @@ -78,14 +78,38 @@ if len(sys.argv) >= 2 and sys.argv[1] in ('sdist','bdist_rpm'): #('docs/magic.tex', #['IPython/Magic.py'], #"cd doc && ./update_magic.sh" ), - + + ('docs/man/ipcluster.1.gz', + ['docs/man/ipcluster.1'], + 'cd docs/man && gzip -9c ipcluster.1 > ipcluster.1.gz'), + + ('docs/man/ipcontroller.1.gz', + ['docs/man/ipcontroller.1'], + 'cd docs/man && gzip -9c ipcontroller.1 > ipcontroller.1.gz'), + + ('docs/man/ipengine.1.gz', + ['docs/man/ipengine.1'], + 'cd docs/man && gzip -9c ipengine.1 > ipengine.1.gz'), + ('docs/man/ipython.1.gz', ['docs/man/ipython.1'], - "cd docs/man && gzip -9c ipython.1 > ipython.1.gz"), + 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz'), + + ('docs/man/ipython-wx.1.gz', + ['docs/man/ipython-wx.1'], + 'cd docs/man && gzip -9c ipython-wx.1 > ipython-wx.1.gz'), + + ('docs/man/ipythonx.1.gz', + ['docs/man/ipythonx.1'], + 'cd docs/man && gzip -9c ipythonx.1 > ipythonx.1.gz'), + + ('docs/man/irunner.1.gz', + ['docs/man/irunner.1'], + 'cd docs/man && gzip -9c irunner.1 > irunner.1.gz'), ('docs/man/pycolor.1.gz', ['docs/man/pycolor.1'], - "cd docs/man && gzip -9c pycolor.1 > pycolor.1.gz"), + 'cd docs/man && gzip -9c pycolor.1 > pycolor.1.gz'), ] # Only build the docs if sphinx is present diff --git a/setupbase.py b/setupbase.py index 972cafc..e40e535 100644 --- a/setupbase.py +++ b/setupbase.py @@ -57,7 +57,7 @@ def file_doesnt_endwith(test,endings): # Basic project information #--------------------------------------------------------------------------- -# Release.py contains version, authors, license, url, keywords, etc. +# release.py contains version, authors, license, url, keywords, etc. execfile(pjoin('IPython','core','release.py')) # Create a dict with the basic information @@ -227,6 +227,31 @@ def find_data_files(): return data_files + +def make_man_update_target(manpage): + """Return a target_update-compliant tuple for the given manpage. + + Parameters + ---------- + manpage : string + Name of the manpage, must include the section number (trailing number). + + Example + ------- + + >>> make_man_update_target('ipython.1') #doctest: +NORMALIZE_WHITESPACE + ('docs/man/ipython.1.gz', + ['docs/man/ipython.1'], + 'cd docs/man && gzip -9c ipython.1 > ipython.1.gz') + """ + man_dir = pjoin('docs', 'man') + manpage_gz = manpage + '.gz' + manpath = pjoin(man_dir, manpage) + manpath_gz = pjoin(man_dir, manpage_gz) + gz_cmd = ( "cd %(man_dir)s && gzip -9c %(manpage)s > %(manpage_gz)s" % + locals() ) + return (manpath_gz, [manpath], gz_cmd) + #--------------------------------------------------------------------------- # Find scripts #--------------------------------------------------------------------------- diff --git a/setupegg.py b/setupegg.py index 0924b19..8ed1c35 100755 --- a/setupegg.py +++ b/setupegg.py @@ -5,8 +5,6 @@ import sys # now, import setuptools and call the actual setup import setuptools -# print sys.argv -#sys.argv=['','bdist_egg'] execfile('setup.py') # clean up the junk left around by setuptools diff --git a/setupexe.py b/setupexe.py deleted file mode 100644 index 8a03114..0000000 --- a/setupexe.py +++ /dev/null @@ -1,107 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -r"""Setup script for exe distribution of IPython (does not require python). - -- Requires py2exe - -- install pyreadline *package dir* in ipython root directory by running: - -svn co http://ipython.scipy.org/svn/ipython/pyreadline/branches/maintenance_1.3/pyreadline/ -wget http://ipython.scipy.org/svn/ipython/pyreadline/branches/maintenance_1.3/readline.py - -OR (if you want the latest trunk): - -svn co http://ipython.scipy.org/svn/ipython/pyreadline/trunk/pyreadline - -- Create the distribution in 'dist' by running "python exesetup.py py2exe" - -- Run ipython.exe to go. - -""" - -#***************************************************************************** -# Copyright (C) 2001-2005 Fernando Perez -# -# Distributed under the terms of the BSD License. The full license is in -# the file COPYING, distributed as part of this software. -#***************************************************************************** - -# Stdlib imports -import os -import sys - -from glob import glob - - -# A few handy globals -isfile = os.path.isfile -pjoin = os.path.join - -from distutils.core import setup -from distutils import dir_util -import py2exe - -# update the manuals when building a source dist -# Release.py contains version, authors, license, url, keywords, etc. -execfile(pjoin('IPython','core','release.py')) - -# A little utility we'll need below, since glob() does NOT allow you to do -# exclusion on multiple endings! -def file_doesnt_endwith(test,endings): - """Return true if test is a file and its name does NOT end with any - of the strings listed in endings.""" - if not isfile(test): - return False - for e in endings: - if test.endswith(e): - return False - return True - - -egg_extra_kwds = {} - -# Call the setup() routine which does most of the work -setup(name = name, - options = { - 'py2exe': { - 'packages' : ['IPython', 'IPython.extensions', 'IPython.external', - 'pyreadline','config','core','deathrow','lib', - 'scripts','testing','utils'], - 'excludes' : ["Tkconstants","Tkinter","tcl",'IPython.igrid','wx', - 'wxPython','igrid', 'PyQt4', 'zope', 'Zope', 'Zope2', - '_curses','enthought.traits','gtk','qt', 'pydb','idlelib', - ] - - } - }, - version = version, - description = description, - long_description = long_description, - author = authors['Fernando'][0], - author_email = authors['Fernando'][1], - url = url, - download_url = download_url, - license = license, - platforms = platforms, - keywords = keywords, - console = ['ipykit.py'], - - # extra params needed for eggs - **egg_extra_kwds - ) - -minimal_conf = """ -import IPython.ipapi -ip = IPython.ipapi.get() - -ip.load('ipy_kitcfg') -import ipy_profile_sh -""" - -if not os.path.isdir("dist/_ipython"): - print "Creating simple _ipython dir" - os.mkdir("dist/_ipython") - open("dist/_ipython/ipythonrc.ini","w").write("# intentionally blank\n") - open("dist/_ipython/ipy_user_conf.py","w").write(minimal_conf) - if os.path.isdir('bin'): - dir_util.copy_tree('bin','dist/bin') diff --git a/tools/build_release b/tools/build_release new file mode 100755 index 0000000..45d6c4c --- /dev/null +++ b/tools/build_release @@ -0,0 +1,42 @@ +#!/usr/bin/env python +"""IPython release build script. +""" +from toollib import * + +# Get main ipython dir, this will raise if it doesn't pass some checks +ipdir = get_ipdir() +cd(ipdir) + +# Load release info +execfile(pjoin('IPython','core','release.py')) + +# Check that everything compiles +compile_tree() + +# Cleanup +for d in ['build','dist',pjoin('docs','build'),pjoin('docs','dist')]: + if os.path.isdir(d): + remove_tree(d) + +# Build source and binary distros +c('./setup.py sdist --formats=gztar') + +# Build version-specific RPMs, where we must use the --python option to ensure +# that the resulting RPM is really built with the requested python version (so +# things go to lib/python2.X/...) +c("python2.5 ./setup.py bdist_rpm --binary-only --release=py25 " + "--python=/usr/bin/python2.5") +c("python2.6 ./setup.py bdist_rpm --binary-only --release=py26 " + "--python=/usr/bin/python2.6") + +# Build eggs +c('python2.5 ./setupegg.py bdist_egg') +c('python2.6 ./setupegg.py bdist_egg') + +# Call the windows build separately, so that the extra Windows scripts don't +# get pulled into Unix builds (setup.py has code which checks for +# bdist_wininst) +c("python setup.py bdist_wininst --install-script=ipython_win_post_install.py") + +# Change name so retarded Vista runs the installer correctly +c("rename 's/linux-i686/win32-setup/' dist/*.exe") diff --git a/tools/check_sources.py b/tools/check_sources.py index 9d90f3c..5b64c6e 100755 --- a/tools/check_sources.py +++ b/tools/check_sources.py @@ -1,15 +1,54 @@ +#!/usr/bin/env python +"""Utility to look for hard tabs and \r characters in all sources. + +Usage: + +./check_sources.py + +It prints summaries and if chosen, line-by-line info of where \\t or \\r +characters can be found in our source tree. +""" + +# Config +# If true, all lines that have tabs are printed, with line number +full_report_tabs = True +# If true, all lines that have tabs are printed, with line number +full_report_rets = False + +# Code begins from IPython.external.path import path -fs = path('..').walkfiles('*.py') -for f in fs: +rets = [] +tabs = [] + +for f in path('..').walkfiles('*.py'): errs = '' cont = f.bytes() if '\t' in cont: errs+='t' + tabs.append(f) if '\r' in cont: errs+='r' + rets.append(f) if errs: print "%3s" % errs, f - + + if 't' in errs and full_report_tabs: + for ln,line in enumerate(f.lines()): + if '\t' in line: + print 'TAB:',ln,':',line, + + if 'r' in errs and full_report_rets: + for ln,line in enumerate(open(f.abspath(),'rb')): + if '\r' in line: + print 'RET:',ln,':',line, + +# Summary at the end, to call cleanup tools if necessary +if tabs: + print 'Hard tabs found. These can be cleaned with untabify:' + for f in tabs: print f, +if rets: + print 'Carriage returns (\\r) found in:' + for f in rets: print f, diff --git a/tools/compile.py b/tools/compile.py index 4a08400..be34c42 100755 --- a/tools/compile.py +++ b/tools/compile.py @@ -1,20 +1,19 @@ #!/usr/bin/env python -"""Call the compile script to check that all code we ship compiles correctly. -""" +"""Script to check that all code in a directory compiles correctly. -import os -import sys +Usage: + compile.py -vstr = '.'.join(map(str,sys.version_info[:2])) +This script verifies that all Python files in the directory where run, and all +of its subdirectories, compile correctly. -stat = os.system('python %s/lib/python%s/compileall.py .' % (sys.prefix,vstr)) +Before a release, call this script from the top-level directory. +""" + +import sys -print -if stat: - print '*** THERE WAS AN ERROR! ***' - print 'See messages above for the actual file that produced it.' -else: - print 'OK' +from toollib import compile_tree -sys.exit(stat) +if __name__ == '__main__': + compile_tree() diff --git a/tools/make_tarball.py b/tools/make_tarball.py index d143bd5..d934ad7 100755 --- a/tools/make_tarball.py +++ b/tools/make_tarball.py @@ -2,35 +2,22 @@ """Simple script to create a tarball with proper bzr version info. """ -import os,sys,shutil +import os +import sys +import shutil -basever = '0.9.0' +from toollib import * -def oscmd(c): - print ">",c - s = os.system(c) - if s: - print "Error",s - sys.exit(s) +c('python update_revnum.py') -def verinfo(): - - out = os.popen('bzr version-info') - pairs = (l.split(':',1) for l in out) - d = dict(((k,v.strip()) for (k,v) in pairs)) - return d - -basename = 'ipython' - -#tarname = '%s.r%s.tgz' % (basename, ver) -oscmd('python update_revnum.py') +execfile('../IPython/core/release.py') # defines version_base -ver = verinfo() +ver = version_info() if ver['branch-nick'] == 'ipython': - tarname = 'ipython-%s.bzr.r%s.tgz' % (basever, ver['revno']) + tarname = 'ipython-%s.bzr.r%s.tgz' % (version_base, ver['revno']) else: - tarname = 'ipython-%s.bzr.r%s.%s.tgz' % (basever, ver['revno'], + tarname = 'ipython-%s.bzr.r%s.%s.tgz' % (version_base, ver['revno'], ver['branch-nick']) -oscmd('bzr export ' + tarname) +c('bzr export ' + tarname) diff --git a/tools/mkrel.py b/tools/mkrel.py deleted file mode 100755 index 01c8a8a..0000000 --- a/tools/mkrel.py +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env python -"""IPython release script - -Create ipykit and exe installer - -requires py2exe -""" - -import os -import distutils.dir_util -import sys - -execfile('../IPython/Release.py') - -def c(cmd): - print ">",cmd - os.system(cmd) - -ipykit_name = "ipykit-%s" % version - -os.chdir('..') -if os.path.isdir('dist'): - distutils.dir_util.remove_tree('dist') -if os.path.isdir(ipykit_name): - distutils.dir_util.remove_tree(ipykit_name) - -if sys.platform == 'win32': - c("python exesetup.py py2exe") - - os.rename('dist',ipykit_name) - - c("zip -r %s.zip %s" % (ipykit_name, ipykit_name)) - -# Build source and binary distros -c('./setup.py sdist --formats=gztar') - -c("python2.4 ./setup.py bdist_rpm --binary-only --release=py24 --python=/usr/bin/python2.4") -c("python2.5 ./setup.py bdist_rpm --binary-only --release=py25 --python=/usr/bin/python2.5") - -# Build eggs -c('python2.4 ./eggsetup.py bdist_egg') -c('python2.5 ./eggsetup.py bdist_egg') - -c("python setup.py bdist_wininst --install-script=ipython_win_post_install.py") - -os.chdir('tools') -c('python make_tarball.py') - diff --git a/tools/release b/tools/release index 170515a..3eff29e 100755 --- a/tools/release +++ b/tools/release @@ -1,58 +1,43 @@ -#!/bin/sh -# IPython release script +#!/usr/bin/env python +"""IPython release script. -PYVER=`python -V 2>&1 | awk '{print $2}' | awk -F '.' '{print $1$2}' ` -version=`ipython -Version` -ipdir=~/ipython/ipython -ipbackupdir=~/ipython/backup +This should only be run at real release time. +""" -echo -echo "Releasing IPython version $version" -echo "==================================" +from toollib import * -# Perform local backup -cd $ipdir/tools -./make_tarball.py -mv ipython-*.tgz $ipbackupdir - -# Clean up build/dist directories -rm -rf $ipdir/build/* -rm -rf $ipdir/dist/* +# Get main ipython dir, this will raise if it doesn't pass some checks +ipdir = get_ipdir() +cd(ipdir) -# Build source and binary distros -cd $ipdir -./setup.py sdist --formats=gztar +# Load release info +execfile(pjoin('IPython','core','release.py')) -# Build version-specific RPMs, where we must use the --python option to ensure -# that the resulting RPM is really built with the requested python version (so -# things go to lib/python2.X/...) -#python2.4 ./setup.py bdist_rpm --binary-only --release=py24 --python=/usr/bin/python2.4 -#python2.5 ./setup.py bdist_rpm --binary-only --release=py25 --python=/usr/bin/python2.5 +# Where I keep static backups of each release +ipbackupdir = os.path.expanduser('~/ipython/backup') -# Build eggs -python2.4 ./setup_bdist_egg.py -python2.5 ./setup_bdist_egg.py +print +print "Releasing IPython version $version" +print "==================================" -# Call the windows build separately, so that the extra Windows scripts don't -# get pulled into Unix builds (setup.py has code which checks for -# bdist_wininst) -./setup.py bdist_wininst --install-script=ipython_win_post_install.py +# Perform local backup +c('./make_tarball.py') +c('mv ipython-*.tgz %s' % ipbackupdir) -# Change name so retarded Vista runs the installer correctly -rename 's/win32/win32-setup/' $ipdir/dist/*.exe +# Build release files +c('./mkrel.py %s' % ipdir) # Register with the Python Package Index (PyPI) -echo "Registering with PyPI..." -cd $ipdir -./setup.py register +print "Registering with PyPI..." +c('./setup.py register') # Upload all files -cd $ipdir/dist -echo "Uploading distribution files..." -scp * ipython@ipython.scipy.org:www/dist/ +cd('dist') +print "Uploading distribution files..." +c('scp * ipython@ipython.scipy.org:www/dist/') -echo "Uploading backup files..." -cd $ipbackupdir -scp `ls -1tr *tgz | tail -1` ipython@ipython.scipy.org:www/backup/ +print "Uploading backup files..." +cd(ipbackupdir) +c('scp `ls -1tr *tgz | tail -1` ipython@ipython.scipy.org:www/backup/') -echo "Done!" +print "Done!" diff --git a/tools/run_ipy_in_profiler.py b/tools/run_ipy_in_profiler.py index bcd0ef7..a58e0c5 100755 --- a/tools/run_ipy_in_profiler.py +++ b/tools/run_ipy_in_profiler.py @@ -1,3 +1,9 @@ +"""XXX - What exactly is the use of this script? + +I (fperez) tried it quickly and it doesn't work in its current form. Either it +needs to be fixed and documented or removed. +""" + import cProfile as profile import sys #import profile diff --git a/tools/setup_bdist_egg.py b/tools/setup_bdist_egg.py deleted file mode 100755 index 6736e39..0000000 --- a/tools/setup_bdist_egg.py +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env python -"""Wrapper to build IPython as an egg (setuptools format).""" - -import os -import sys - -# Add my local path to sys.path -home = os.environ['HOME'] -sys.path.insert(0,'%s/usr/local/lib/python%s/site-packages' % - (home,sys.version[:3])) - -# now, import setuptools and build the actual egg -import setuptools - -sys.argv=['','bdist_egg'] -execfile('setup.py') - -# clean up the junk left around by setuptools -os.system('rm -rf ipython.egg-info') diff --git a/tools/testrel b/tools/testrel deleted file mode 100755 index b6901e2..0000000 --- a/tools/testrel +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/sh - -# release test - -ipdir=$PWD/.. - -cd $ipdir - -# Clean up build/dist directories -rm -rf $ipdir/build/* -rm -rf $ipdir/dist/* - -# build source distros -cd $ipdir -./setup.py sdist --formats=gztar - -# Build rpms -python2.4 ./setup.py bdist_rpm --binary-only --release=py24 --python=/usr/bin/python2.4 -python2.5 ./setup.py bdist_rpm --binary-only --release=py25 --python=/usr/bin/python2.5 - -# Build eggs -python2.4 ./setup_bdist_egg.py -python2.5 ./setup_bdist_egg.py - -# Call the windows build separately, so that the extra Windows scripts don't -# get pulled into Unix builds (setup.py has code which checks for -# bdist_wininst) -./setup.py bdist_wininst --install-script=ipython_win_post_install.py - -# Change name so retarded Vista runs the installer correctly -rename 's/win32/win32-setup/' $ipdir/dist/*.exe diff --git a/tools/testupload b/tools/testupload index c2a19c0..a3a04db 100755 --- a/tools/testupload +++ b/tools/testupload @@ -1,5 +1,5 @@ #!/bin/sh - +# Simple upload script to push up into the testing directory a local build ipdir=$PWD/.. cd $ipdir/dist diff --git a/tools/toollib.py b/tools/toollib.py new file mode 100644 index 0000000..206bd7a --- /dev/null +++ b/tools/toollib.py @@ -0,0 +1,55 @@ +"""Various utilities common to IPython release and maintenance tools. +""" +# Library imports +import os +import sys + +from distutils.dir_util import remove_tree + +# Useful shorthands +pjoin = os.path.join +cd = os.chdir + +# Utility functions +def c(cmd): + """Run system command, raise SystemExit if it returns an error.""" + print "$",cmd + stat = os.system(cmd) + #stat = 0 # Uncomment this and comment previous to run in debug mode + if stat: + raise SystemExit("Command %s failed with code: %s" % (cmd, stat)) + + +def get_ipdir(): + """Get IPython directory from command line, or assume it's the one above.""" + + # Initialize arguments and check location + try: + ipdir = sys.argv[1] + except IndexError: + ipdir = '..' + + ipdir = os.path.abspath(ipdir) + + cd(ipdir) + if not os.path.isdir('IPython') and os.path.isfile('setup.py'): + raise SystemExit('Invalid ipython directory: %s' % ipdir) + return ipdir + + +def compile_tree(): + """Compile all Python files below current directory.""" + vstr = '.'.join(map(str,sys.version_info[:2])) + stat = os.system('python %s/lib/python%s/compileall.py .' % + (sys.prefix,vstr)) + if stat: + msg = '*** ERROR: Some Python files in tree do NOT compile! ***\n' + msg += 'See messages above for the actual file that produced it.\n' + raise SystemExit(msg) + + +def version_info(): + """Return bzr version info as a dict.""" + out = os.popen('bzr version-info') + pairs = (l.split(':',1) for l in out) + return dict(((k,v.strip()) for (k,v) in pairs)) diff --git a/tools/update_revnum.py b/tools/update_revnum.py index 1b6454c..7e03e74 100755 --- a/tools/update_revnum.py +++ b/tools/update_revnum.py @@ -1,23 +1,32 @@ #!/usr/bin/env python -""" Change the revision number in Release.py """ +"""Change the revision number in release.py + +This edits in-place release.py to update the revision number from bzr info. + +Usage: + +./update_revnum.py""" import os -import re,pprint +import pprint +import re -def verinfo(): - - out = os.popen('bzr version-info') - pairs = (l.split(':',1) for l in out) - d = dict(((k,v.strip()) for (k,v) in pairs)) - return d +from toollib import * -ver = verinfo() +if __name__ == '__main__': + ver = version_info() -pprint.pprint(ver) + pprint.pprint(ver) -rfile = open('../IPython/core/release.py','rb').read() -newcont = re.sub(r'revision\s*=.*', "revision = '%s'" % ver['revno'], rfile) + rfile = open('../IPython/core/release.py','rb').read() + newcont = re.sub(r'revision\s*=.*', + "revision = '%s'" % ver['revno'], + rfile) -newcont = re.sub(r'^branch\s*=[^=].*', "branch = '%s'" % ver['branch-nick'], newcont ) + newcont = re.sub(r'^branch\s*=[^=].*', + "branch = '%s'" % ver['branch-nick'], + newcont) -open('../IPython/core/release.py','wb').write(newcont) + f = open('../IPython/core/release.py','wb') + f.write(newcont) + f.close()