##// END OF EJS Templates
More code review changes:...
Brian Granger -
Show More
@@ -0,0 +1,7
1 .. _extensions_cythonmagic:
2
3 ===========
4 cythonmagic
5 ===========
6
7 .. automodule:: IPython.extensions.cythonmagic
@@ -1,180 +1,181
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 Cython related magics.
4 4
5 5 Author:
6 6 * Brian Granger
7 7
8 8 Parts of this code were taken from Cython.inline.
9 9 """
10 10 #-----------------------------------------------------------------------------
11 11 # Copyright (C) 2010-2011, IPython Development Team.
12 12 #
13 13 # Distributed under the terms of the Modified BSD License.
14 14 #
15 15 # The full license is in the file COPYING.txt, distributed with this software.
16 16 #-----------------------------------------------------------------------------
17 17
18 import io
18 19 import os, sys
19 20 from importlib import import_module
20 21 import imp
21 22
22 23 try:
23 24 import hashlib
24 25 except ImportError:
25 26 import md5 as hashlib
26 27
27 28 from distutils.core import Distribution, Extension
28 29 from distutils.command.build_ext import build_ext
29 30
30 31 from IPython.core.magic import Magics, magics_class, cell_magic
31 32 from IPython.testing.skipdoctest import skip_doctest
32 33 from IPython.core.magic_arguments import (
33 34 argument, magic_arguments, parse_argstring
34 35 )
36 from IPython.utils import py3compat
35 37
36 38 import Cython
37 39 from Cython.Compiler.Errors import CompileError
38 40 from Cython.Compiler.Main import Context, default_options
39 41 from Cython.Build.Dependencies import cythonize
40 42
41 43
42 44 @magics_class
43 45 class CythonMagics(Magics):
44 46
45 47 def __init__(self, shell):
46 48 super(CythonMagics,self).__init__(shell)
47 49 self._reloads = {}
48 50 self._code_cache = {}
49 51
50 52 def _import_all(self, module):
51 53 for k,v in module.__dict__.items():
52 54 if not k.startswith('__'):
53 55 self.shell.push({k:v})
54 56
55 57 @cell_magic
56 58 def cython_inline(self, line, cell):
57 59 """Compile and run a Cython code cell using Cython.inline.
58 60
59 61 This magic simply passes the body of the cell to Cython.inline
60 62 and returns the result. If the variables `a` and `b` are defined
61 63 in the user's namespace, here is a simple example that returns
62 64 their sum::
63 65
64 66 %%cython_inline
65 67 return a+b
66 68
67 69 For most purposes, we recommend the usage of the `%%cython` magic.
68 70 """
69 71 locs = self.shell.user_global_ns
70 72 globs = self.shell.user_ns
71 73 return Cython.inline(cell, locals=locs, globals=globs)
72 74
73 75 @cell_magic
74 76 def cython_pyximport(self, line, cell):
75 77 """Compile and import a Cython code cell using pyximport.
76 78
77 79 The contents of the cell are written to a `.pyx` file in the current
78 80 working directory, which is then imported using `pyximport`. This
79 81 magic requires a module name to be passed::
80 82
81 83 %%cython_pyximport modulename
82 84 def f(x):
83 85 return 2.0*x
84 86
85 87 The compiled module is then imported and all of its symbols are injected into
86 88 the user's namespace. For most purposes, we recommend the usage of the
87 89 `%%cython` magic.
88 90 """
89 91 module_name = line.strip()
90 92 if not module_name:
91 93 raise ValueError('module name must be given')
92 94 fname = module_name + '.pyx'
93 with io.open(fname, 'w', encoding='utf-8'):
95 with io.open(fname, 'w', encoding='utf-8') as f:
94 96 f.write(cell)
95 97 if 'pyximport' not in sys.modules:
96 98 import pyximport
97 99 pyximport.install(reload_support=True)
98 100 if module_name in self._reloads:
99 101 module = self._reloads[module_name]
100 102 reload(module)
101 103 else:
102 104 module = import_module(module_name)
103 105 self._reloads[module_name] = module
104 106 self._import_all(module)
105 107
106 108 @magic_arguments()
107 109 @argument(
108 110 '-f', '--force', action='store_true', default=False,
109 111 help="Force the compilation of the pyx module even if it hasn't changed"
110 112 )
111 113 @cell_magic
112 114 def cython(self, line, cell):
113 115 """Compile and import everything from a Cython code cell.
114 116
115 117 The contents of the cell are written to a `.pyx` file in the
116 118 directory `IPYTHONDIR/cython` using a filename with the hash of the code.
117 119 This file is then cythonized and compiled. The resulting module
118 120 is imported and all of its symbols are injected into the user's
119 121 namespace. The usage is similar to that of `%%cython_pyximport` but
120 122 you don't have to pass a module name::
121 123
122 124 %%cython
123 125 def f(x):
124 126 return 2.0*x
125 127 """
126 128 args = parse_argstring(self.cython, line)
127 129 code = cell if cell.endswith('\n') else cell+'\n'
128 # distutils.Extension cannot handle sources that a unicode
129 lib_dir=str(os.path.join(self.shell.ipython_dir,'cython'))
130 lib_dir=os.path.join(self.shell.ipython_dir, 'cython')
130 131 cython_include_dirs=['.']
131 132 force=args.force
132 133 quiet=True
133 134 ctx = Context(cython_include_dirs, default_options)
134 135 key = code, sys.version_info, sys.executable, Cython.__version__
135 136 module_name = "_cython_magic_" + hashlib.md5(str(key).encode('utf-8')).hexdigest()
136 137 so_ext = [ ext for ext,_,mod_type in imp.get_suffixes() if mod_type == imp.C_EXTENSION ][0]
137 138 module_path = os.path.join(lib_dir, module_name+so_ext)
138 139
139 140 if not os.path.exists(lib_dir):
140 141 os.makedirs(lib_dir)
141 142
142 143 if force or not os.path.isfile(module_path):
143 144 cflags = []
144 145 c_include_dirs = []
145 146 if 'numpy' in code:
146 147 import numpy
147 148 c_include_dirs.append(numpy.get_include())
148 149 pyx_file = os.path.join(lib_dir, module_name + '.pyx')
149 with io.open(pyx_file, 'w', encoding='utf-8'):
150 pyx_file = py3compat.unicode_to_str(pyx_file, encoding=sys.getfilesystemencoding())
151 with io.open(pyx_file, 'w', encoding='utf-8') as f:
150 152 f.write(code)
151 print [pyx_file]
152 153 extension = Extension(
153 154 name = module_name,
154 155 sources = [pyx_file],
155 156 include_dirs = c_include_dirs,
156 157 extra_compile_args = cflags
157 158 )
158 159 build_extension = build_ext(Distribution())
159 160 build_extension.finalize_options()
160 161 try:
161 162 build_extension.extensions = cythonize([extension], ctx=ctx, quiet=quiet)
162 163 except CompileError:
163 164 return
164 165 build_extension.build_temp = os.path.dirname(pyx_file)
165 166 build_extension.build_lib = lib_dir
166 167 build_extension.run()
167 168 self._code_cache[key] = module_name
168 169
169 170 module = imp.load_dynamic(module_name, module_path)
170 171 self._import_all(module)
171 172
172 173
173 174 _loaded = False
174 175
175 176 def load_ipython_extension(ip):
176 177 """Load the extension in IPython."""
177 178 global _loaded
178 179 if not _loaded:
179 180 ip.register_magics(CythonMagics)
180 181 _loaded = True
@@ -1,45 +1,47
1 1 # -*- coding: utf-8 -*-
2 2 """Tests for the Cython magics extension."""
3 3
4 4 import os
5 5 import nose.tools as nt
6 6
7 from IPython.utils import py3compat
7 8
8 code = """def f(x):
9 code = py3compat.str_to_unicode("""def f(x):
9 10 return 2*x
10 """
11 """)
11 12
12 13 try:
13 14 import Cython
14 15 except:
15 16 __test__ = False
16 17
17 18 def setup():
18 19 ip = get_ipython()
19 20 ip.extension_manager.load_extension('cythonmagic')
20 21
21 22 def test_cython_inline():
22 23 ip = get_ipython()
23 24 ip.ex('a=10; b=20')
24 25 result = ip.run_cell_magic('cython_inline','','return a+b')
25 26 nt.assert_equals(result, 30)
26 27
27 28 def test_cython_pyximport():
28 29 module_name = '_test_cython_pyximport'
29 30 ip = get_ipython()
30 31 ip.run_cell_magic('cython_pyximport', module_name, code)
31 32 ip.ex('g = f(10)')
32 33 nt.assert_equals(ip.user_ns['g'], 20.0)
33 34 try:
34 35 os.remove(module_name+'.pyx')
35 36 except OSError:
36 37 pass
37 38
38 39 def test_cython():
39 40 ip = get_ipython()
40 41 ip.run_cell_magic('cython', '', code)
41 42 ip.ex('g = f(10)')
42 43 nt.assert_equals(ip.user_ns['g'], 20.0)
43 44
44 45
45 46
47
@@ -1,524 +1,528
1 1 # -*- coding: utf-8 -*-
2 2 """IPython Test Suite Runner.
3 3
4 4 This module provides a main entry point to a user script to test IPython
5 5 itself from the command line. There are two ways of running this script:
6 6
7 7 1. With the syntax `iptest all`. This runs our entire test suite by
8 8 calling this script (with different arguments) recursively. This
9 9 causes modules and package to be tested in different processes, using nose
10 10 or trial where appropriate.
11 11 2. With the regular nose syntax, like `iptest -vvs IPython`. In this form
12 12 the script simply calls nose, but with special command line flags and
13 13 plugins loaded.
14 14
15 15 """
16 16
17 17 #-----------------------------------------------------------------------------
18 18 # Copyright (C) 2009-2011 The IPython Development Team
19 19 #
20 20 # Distributed under the terms of the BSD License. The full license is in
21 21 # the file COPYING, distributed as part of this software.
22 22 #-----------------------------------------------------------------------------
23 23
24 24 #-----------------------------------------------------------------------------
25 25 # Imports
26 26 #-----------------------------------------------------------------------------
27 27
28 28 # Stdlib
29 29 import os
30 30 import os.path as path
31 31 import signal
32 32 import sys
33 33 import subprocess
34 34 import tempfile
35 35 import time
36 36 import warnings
37 37
38 38 # Note: monkeypatch!
39 39 # We need to monkeypatch a small problem in nose itself first, before importing
40 40 # it for actual use. This should get into nose upstream, but its release cycle
41 41 # is slow and we need it for our parametric tests to work correctly.
42 42 from IPython.testing import nosepatch
43 43 # Now, proceed to import nose itself
44 44 import nose.plugins.builtin
45 45 from nose.plugins.xunit import Xunit
46 46 from nose import SkipTest
47 47 from nose.core import TestProgram
48 48
49 49 # Our own imports
50 50 from IPython.utils.importstring import import_item
51 51 from IPython.utils.path import get_ipython_module_path
52 52 from IPython.utils.process import find_cmd, pycmd2argv
53 53 from IPython.utils.sysinfo import sys_info
54 54
55 55 from IPython.testing import globalipapp
56 56 from IPython.testing.plugin.ipdoctest import IPythonDoctest
57 57 from IPython.external.decorators import KnownFailure, knownfailureif
58 58
59 59 pjoin = path.join
60 60
61 61
62 62 #-----------------------------------------------------------------------------
63 63 # Globals
64 64 #-----------------------------------------------------------------------------
65 65
66 66
67 67 #-----------------------------------------------------------------------------
68 68 # Warnings control
69 69 #-----------------------------------------------------------------------------
70 70
71 71 # Twisted generates annoying warnings with Python 2.6, as will do other code
72 72 # that imports 'sets' as of today
73 73 warnings.filterwarnings('ignore', 'the sets module is deprecated',
74 74 DeprecationWarning )
75 75
76 76 # This one also comes from Twisted
77 77 warnings.filterwarnings('ignore', 'the sha module is deprecated',
78 78 DeprecationWarning)
79 79
80 80 # Wx on Fedora11 spits these out
81 81 warnings.filterwarnings('ignore', 'wxPython/wxWidgets release number mismatch',
82 82 UserWarning)
83 83
84 84 # ------------------------------------------------------------------------------
85 85 # Monkeypatch Xunit to count known failures as skipped.
86 86 # ------------------------------------------------------------------------------
87 87 def monkeypatch_xunit():
88 88 try:
89 89 knownfailureif(True)(lambda: None)()
90 90 except Exception as e:
91 91 KnownFailureTest = type(e)
92 92
93 93 def addError(self, test, err, capt=None):
94 94 if issubclass(err[0], KnownFailureTest):
95 95 err = (SkipTest,) + err[1:]
96 96 return self.orig_addError(test, err, capt)
97 97
98 98 Xunit.orig_addError = Xunit.addError
99 99 Xunit.addError = addError
100 100
101 101 #-----------------------------------------------------------------------------
102 102 # Logic for skipping doctests
103 103 #-----------------------------------------------------------------------------
104 104 def extract_version(mod):
105 105 return mod.__version__
106 106
107 107 def test_for(item, min_version=None, callback=extract_version):
108 108 """Test to see if item is importable, and optionally check against a minimum
109 109 version.
110 110
111 111 If min_version is given, the default behavior is to check against the
112 112 `__version__` attribute of the item, but specifying `callback` allows you to
113 113 extract the value you are interested in. e.g::
114 114
115 115 In [1]: import sys
116 116
117 117 In [2]: from IPython.testing.iptest import test_for
118 118
119 119 In [3]: test_for('sys', (2,6), callback=lambda sys: sys.version_info)
120 120 Out[3]: True
121 121
122 122 """
123 123 try:
124 124 check = import_item(item)
125 125 except (ImportError, RuntimeError):
126 126 # GTK reports Runtime error if it can't be initialized even if it's
127 127 # importable.
128 128 return False
129 129 else:
130 130 if min_version:
131 131 if callback:
132 132 # extra processing step to get version to compare
133 133 check = callback(check)
134 134
135 135 return check >= min_version
136 136 else:
137 137 return True
138 138
139 139 # Global dict where we can store information on what we have and what we don't
140 140 # have available at test run time
141 141 have = {}
142 142
143 143 have['curses'] = test_for('_curses')
144 144 have['matplotlib'] = test_for('matplotlib')
145 145 have['pexpect'] = test_for('IPython.external.pexpect')
146 146 have['pymongo'] = test_for('pymongo')
147 147 have['wx'] = test_for('wx')
148 148 have['wx.aui'] = test_for('wx.aui')
149 149 have['qt'] = test_for('IPython.external.qt')
150 150 have['sqlite3'] = test_for('sqlite3')
151 have['cython'] = test_for('Cython')
151 152
152 153 have['tornado'] = test_for('tornado.version_info', (2,1,0), callback=None)
153 154
154 155 if os.name == 'nt':
155 156 min_zmq = (2,1,7)
156 157 else:
157 158 min_zmq = (2,1,4)
158 159
159 160 def version_tuple(mod):
160 161 "turn '2.1.9' into (2,1,9), and '2.1dev' into (2,1,999)"
161 162 # turn 'dev' into 999, because Python3 rejects str-int comparisons
162 163 vs = mod.__version__.replace('dev', '.999')
163 164 tup = tuple([int(v) for v in vs.split('.') ])
164 165 return tup
165 166
166 167 have['zmq'] = test_for('zmq', min_zmq, version_tuple)
167 168
168 169 #-----------------------------------------------------------------------------
169 170 # Functions and classes
170 171 #-----------------------------------------------------------------------------
171 172
172 173 def report():
173 174 """Return a string with a summary report of test-related variables."""
174 175
175 176 out = [ sys_info(), '\n']
176 177
177 178 avail = []
178 179 not_avail = []
179 180
180 181 for k, is_avail in have.items():
181 182 if is_avail:
182 183 avail.append(k)
183 184 else:
184 185 not_avail.append(k)
185 186
186 187 if avail:
187 188 out.append('\nTools and libraries available at test time:\n')
188 189 avail.sort()
189 190 out.append(' ' + ' '.join(avail)+'\n')
190 191
191 192 if not_avail:
192 193 out.append('\nTools and libraries NOT available at test time:\n')
193 194 not_avail.sort()
194 195 out.append(' ' + ' '.join(not_avail)+'\n')
195 196
196 197 return ''.join(out)
197 198
198 199
199 200 def make_exclude():
200 201 """Make patterns of modules and packages to exclude from testing.
201 202
202 203 For the IPythonDoctest plugin, we need to exclude certain patterns that
203 204 cause testing problems. We should strive to minimize the number of
204 205 skipped modules, since this means untested code.
205 206
206 207 These modules and packages will NOT get scanned by nose at all for tests.
207 208 """
208 209 # Simple utility to make IPython paths more readably, we need a lot of
209 210 # these below
210 211 ipjoin = lambda *paths: pjoin('IPython', *paths)
211 212
212 213 exclusions = [ipjoin('external'),
213 214 pjoin('IPython_doctest_plugin'),
214 215 ipjoin('quarantine'),
215 216 ipjoin('deathrow'),
216 217 ipjoin('testing', 'attic'),
217 218 # This guy is probably attic material
218 219 ipjoin('testing', 'mkdoctests'),
219 220 # Testing inputhook will need a lot of thought, to figure out
220 221 # how to have tests that don't lock up with the gui event
221 222 # loops in the picture
222 223 ipjoin('lib', 'inputhook'),
223 224 # Config files aren't really importable stand-alone
224 225 ipjoin('config', 'default'),
225 226 ipjoin('config', 'profile'),
226 227 ]
227 228 if not have['sqlite3']:
228 229 exclusions.append(ipjoin('core', 'tests', 'test_history'))
229 230 exclusions.append(ipjoin('core', 'history'))
230 231 if not have['wx']:
231 232 exclusions.append(ipjoin('lib', 'inputhookwx'))
232 233
233 234 # We do this unconditionally, so that the test suite doesn't import
234 235 # gtk, changing the default encoding and masking some unicode bugs.
235 236 exclusions.append(ipjoin('lib', 'inputhookgtk'))
236 237 exclusions.append(ipjoin('zmq', 'gui', 'gtkembed'))
237 238
238 239 # These have to be skipped on win32 because the use echo, rm, cd, etc.
239 240 # See ticket https://github.com/ipython/ipython/issues/87
240 241 if sys.platform == 'win32':
241 242 exclusions.append(ipjoin('testing', 'plugin', 'test_exampleip'))
242 243 exclusions.append(ipjoin('testing', 'plugin', 'dtexample'))
243 244
244 245 if not have['pexpect']:
245 246 exclusions.extend([ipjoin('scripts', 'irunner'),
246 247 ipjoin('lib', 'irunner'),
247 248 ipjoin('lib', 'tests', 'test_irunner'),
248 249 ipjoin('frontend', 'terminal', 'console'),
249 250 ])
250 251
251 252 if not have['zmq']:
252 253 exclusions.append(ipjoin('zmq'))
253 254 exclusions.append(ipjoin('frontend', 'qt'))
254 255 exclusions.append(ipjoin('frontend', 'html'))
255 256 exclusions.append(ipjoin('frontend', 'consoleapp.py'))
256 257 exclusions.append(ipjoin('frontend', 'terminal', 'console'))
257 258 exclusions.append(ipjoin('parallel'))
258 259 elif not have['qt']:
259 260 exclusions.append(ipjoin('frontend', 'qt'))
260 261
261 262 if not have['pymongo']:
262 263 exclusions.append(ipjoin('parallel', 'controller', 'mongodb'))
263 264 exclusions.append(ipjoin('parallel', 'tests', 'test_mongodb'))
264 265
265 266 if not have['matplotlib']:
266 267 exclusions.extend([ipjoin('core', 'pylabtools'),
267 268 ipjoin('core', 'tests', 'test_pylabtools'),
268 269 ipjoin('zmq', 'pylab'),
269 270 ])
270 271
272 if not have['cython']:
273 exclusions.extend([ipjoin('extensions', 'cythonmagic')])
274
271 275 if not have['tornado']:
272 276 exclusions.append(ipjoin('frontend', 'html'))
273 277
274 278 # This is needed for the reg-exp to match on win32 in the ipdoctest plugin.
275 279 if sys.platform == 'win32':
276 280 exclusions = [s.replace('\\','\\\\') for s in exclusions]
277 281
278 282 return exclusions
279 283
280 284
281 285 class IPTester(object):
282 286 """Call that calls iptest or trial in a subprocess.
283 287 """
284 288 #: string, name of test runner that will be called
285 289 runner = None
286 290 #: list, parameters for test runner
287 291 params = None
288 292 #: list, arguments of system call to be made to call test runner
289 293 call_args = None
290 294 #: list, process ids of subprocesses we start (for cleanup)
291 295 pids = None
292 296 #: str, coverage xml output file
293 297 coverage_xml = None
294 298
295 299 def __init__(self, runner='iptest', params=None):
296 300 """Create new test runner."""
297 301 p = os.path
298 302 if runner == 'iptest':
299 303 iptest_app = get_ipython_module_path('IPython.testing.iptest')
300 304 self.runner = pycmd2argv(iptest_app) + sys.argv[1:]
301 305 else:
302 306 raise Exception('Not a valid test runner: %s' % repr(runner))
303 307 if params is None:
304 308 params = []
305 309 if isinstance(params, str):
306 310 params = [params]
307 311 self.params = params
308 312
309 313 # Assemble call
310 314 self.call_args = self.runner+self.params
311 315
312 316 # Find the section we're testing (IPython.foo)
313 317 for sect in self.params:
314 318 if sect.startswith('IPython'): break
315 319 else:
316 320 raise ValueError("Section not found", self.params)
317 321
318 322 if '--with-xunit' in self.call_args:
319 323 self.call_args.append('--xunit-file=%s' % path.abspath(sect+'.xunit.xml'))
320 324
321 325 if '--with-xml-coverage' in self.call_args:
322 326 self.coverage_xml = path.abspath(sect+".coverage.xml")
323 327 self.call_args.remove('--with-xml-coverage')
324 328 self.call_args = ["coverage", "run", "--source="+sect] + self.call_args[1:]
325 329
326 330 # Store pids of anything we start to clean up on deletion, if possible
327 331 # (on posix only, since win32 has no os.kill)
328 332 self.pids = []
329 333
330 334 if sys.platform == 'win32':
331 335 def _run_cmd(self):
332 336 # On Windows, use os.system instead of subprocess.call, because I
333 337 # was having problems with subprocess and I just don't know enough
334 338 # about win32 to debug this reliably. Os.system may be the 'old
335 339 # fashioned' way to do it, but it works just fine. If someone
336 340 # later can clean this up that's fine, as long as the tests run
337 341 # reliably in win32.
338 342 # What types of problems are you having. They may be related to
339 343 # running Python in unboffered mode. BG.
340 344 return os.system(' '.join(self.call_args))
341 345 else:
342 346 def _run_cmd(self):
343 347 # print >> sys.stderr, '*** CMD:', ' '.join(self.call_args) # dbg
344 348 subp = subprocess.Popen(self.call_args)
345 349 self.pids.append(subp.pid)
346 350 # If this fails, the pid will be left in self.pids and cleaned up
347 351 # later, but if the wait call succeeds, then we can clear the
348 352 # stored pid.
349 353 retcode = subp.wait()
350 354 self.pids.pop()
351 355 return retcode
352 356
353 357 def run(self):
354 358 """Run the stored commands"""
355 359 try:
356 360 retcode = self._run_cmd()
357 361 except:
358 362 import traceback
359 363 traceback.print_exc()
360 364 return 1 # signal failure
361 365
362 366 if self.coverage_xml:
363 367 subprocess.call(["coverage", "xml", "-o", self.coverage_xml])
364 368 return retcode
365 369
366 370 def __del__(self):
367 371 """Cleanup on exit by killing any leftover processes."""
368 372
369 373 if not hasattr(os, 'kill'):
370 374 return
371 375
372 376 for pid in self.pids:
373 377 try:
374 378 print 'Cleaning stale PID:', pid
375 379 os.kill(pid, signal.SIGKILL)
376 380 except OSError:
377 381 # This is just a best effort, if we fail or the process was
378 382 # really gone, ignore it.
379 383 pass
380 384
381 385
382 386 def make_runners():
383 387 """Define the top-level packages that need to be tested.
384 388 """
385 389
386 390 # Packages to be tested via nose, that only depend on the stdlib
387 391 nose_pkg_names = ['config', 'core', 'extensions', 'frontend', 'lib',
388 392 'scripts', 'testing', 'utils', 'nbformat' ]
389 393
390 394 if have['zmq']:
391 395 nose_pkg_names.append('zmq')
392 396 nose_pkg_names.append('parallel')
393 397
394 398 # For debugging this code, only load quick stuff
395 399 #nose_pkg_names = ['core', 'extensions'] # dbg
396 400
397 401 # Make fully qualified package names prepending 'IPython.' to our name lists
398 402 nose_packages = ['IPython.%s' % m for m in nose_pkg_names ]
399 403
400 404 # Make runners
401 405 runners = [ (v, IPTester('iptest', params=v)) for v in nose_packages ]
402 406
403 407 return runners
404 408
405 409
406 410 def run_iptest():
407 411 """Run the IPython test suite using nose.
408 412
409 413 This function is called when this script is **not** called with the form
410 414 `iptest all`. It simply calls nose with appropriate command line flags
411 415 and accepts all of the standard nose arguments.
412 416 """
413 417 # Apply our monkeypatch to Xunit
414 418 if '--with-xunit' in sys.argv and not hasattr(Xunit, 'orig_addError'):
415 419 monkeypatch_xunit()
416 420
417 421 warnings.filterwarnings('ignore',
418 422 'This will be removed soon. Use IPython.testing.util instead')
419 423
420 424 argv = sys.argv + [ '--detailed-errors', # extra info in tracebacks
421 425
422 426 '--with-ipdoctest',
423 427 '--ipdoctest-tests','--ipdoctest-extension=txt',
424 428
425 429 # We add --exe because of setuptools' imbecility (it
426 430 # blindly does chmod +x on ALL files). Nose does the
427 431 # right thing and it tries to avoid executables,
428 432 # setuptools unfortunately forces our hand here. This
429 433 # has been discussed on the distutils list and the
430 434 # setuptools devs refuse to fix this problem!
431 435 '--exe',
432 436 ]
433 437
434 438 if nose.__version__ >= '0.11':
435 439 # I don't fully understand why we need this one, but depending on what
436 440 # directory the test suite is run from, if we don't give it, 0 tests
437 441 # get run. Specifically, if the test suite is run from the source dir
438 442 # with an argument (like 'iptest.py IPython.core', 0 tests are run,
439 443 # even if the same call done in this directory works fine). It appears
440 444 # that if the requested package is in the current dir, nose bails early
441 445 # by default. Since it's otherwise harmless, leave it in by default
442 446 # for nose >= 0.11, though unfortunately nose 0.10 doesn't support it.
443 447 argv.append('--traverse-namespace')
444 448
445 449 # use our plugin for doctesting. It will remove the standard doctest plugin
446 450 # if it finds it enabled
447 451 plugins = [IPythonDoctest(make_exclude()), KnownFailure()]
448 452 # We need a global ipython running in this process
449 453 globalipapp.start_ipython()
450 454 # Now nose can run
451 455 TestProgram(argv=argv, addplugins=plugins)
452 456
453 457
454 458 def run_iptestall():
455 459 """Run the entire IPython test suite by calling nose and trial.
456 460
457 461 This function constructs :class:`IPTester` instances for all IPython
458 462 modules and package and then runs each of them. This causes the modules
459 463 and packages of IPython to be tested each in their own subprocess using
460 464 nose.
461 465 """
462 466
463 467 runners = make_runners()
464 468
465 469 # Run the test runners in a temporary dir so we can nuke it when finished
466 470 # to clean up any junk files left over by accident. This also makes it
467 471 # robust against being run in non-writeable directories by mistake, as the
468 472 # temp dir will always be user-writeable.
469 473 curdir = os.getcwdu()
470 474 testdir = tempfile.gettempdir()
471 475 os.chdir(testdir)
472 476
473 477 # Run all test runners, tracking execution time
474 478 failed = []
475 479 t_start = time.time()
476 480 try:
477 481 for (name, runner) in runners:
478 482 print '*'*70
479 483 print 'IPython test group:',name
480 484 res = runner.run()
481 485 if res:
482 486 failed.append( (name, runner) )
483 487 finally:
484 488 os.chdir(curdir)
485 489 t_end = time.time()
486 490 t_tests = t_end - t_start
487 491 nrunners = len(runners)
488 492 nfail = len(failed)
489 493 # summarize results
490 494 print
491 495 print '*'*70
492 496 print 'Test suite completed for system with the following information:'
493 497 print report()
494 498 print 'Ran %s test groups in %.3fs' % (nrunners, t_tests)
495 499 print
496 500 print 'Status:'
497 501 if not failed:
498 502 print 'OK'
499 503 else:
500 504 # If anything went wrong, point out what command to rerun manually to
501 505 # see the actual errors and individual summary
502 506 print 'ERROR - %s out of %s test groups failed.' % (nfail, nrunners)
503 507 for name, failed_runner in failed:
504 508 print '-'*40
505 509 print 'Runner failed:',name
506 510 print 'You may wish to rerun this one individually, with:'
507 511 print ' '.join(failed_runner.call_args)
508 512 print
509 513 # Ensure that our exit code indicates failure
510 514 sys.exit(1)
511 515
512 516
513 517 def main():
514 518 for arg in sys.argv[1:]:
515 519 if arg.startswith('IPython'):
516 520 # This is in-process
517 521 run_iptest()
518 522 else:
519 523 # This starts subprocesses
520 524 run_iptestall()
521 525
522 526
523 527 if __name__ == '__main__':
524 528 main()
@@ -1,76 +1,77
1 1 .. _extensions_overview:
2 2
3 3 ==================
4 4 IPython extensions
5 5 ==================
6 6
7 7 Configuration files are just the first level of customization that IPython
8 8 supports. The next level is that of extensions. An IPython extension is an
9 9 importable Python module that has a a few special function. By defining these
10 10 functions, users can customize IPython by accessing the actual runtime objects
11 11 of IPython. Here is a sample extension::
12 12
13 13 # myextension.py
14 14
15 15 def load_ipython_extension(ipython):
16 16 # The ``ipython`` argument is the currently active
17 17 # :class:`InteractiveShell` instance that can be used in any way.
18 18 # This allows you do to things like register new magics, plugins or
19 19 # aliases.
20 20
21 21 def unload_ipython_extension(ipython):
22 22 # If you want your extension to be unloadable, put that logic here.
23 23
24 24 This :func:`load_ipython_extension` function is called after your extension is
25 25 imported and the currently active :class:`InteractiveShell` instance is passed
26 26 as the only argument. You can do anything you want with IPython at that point.
27 27
28 28 The :func:`load_ipython_extension` will be called again is you load or reload
29 29 the extension again. It is up to the extension author to add code to manage
30 30 that.
31 31
32 32 Useful :class:`InteractiveShell` methods include :meth:`~IPython.core.interactiveshell.InteractiveShell.define_magic`,
33 33 :meth:`~IPython.core.interactiveshell.InteractiveShell.push` (to add variables to the user namespace) and
34 34 :meth:`~IPython.core.interactiveshell.InteractiveShell.drop_by_id` (to remove variables on unloading).
35 35
36 36 You can put your extension modules anywhere you want, as long as they can be
37 37 imported by Python's standard import mechanism. However, to make it easy to
38 38 write extensions, you can also put your extensions in
39 39 ``os.path.join(self.ipython_dir, 'extensions')``. This directory is added to
40 40 ``sys.path`` automatically.
41 41
42 42 Using extensions
43 43 ================
44 44
45 45 There are two ways you can tell IPython to use your extension:
46 46
47 47 1. Listing it in a configuration file.
48 48 2. Using the ``%load_ext`` magic function.
49 49
50 50 To load an extension called :file:`myextension.py` add the following logic
51 51 to your configuration file::
52 52
53 53 c.InteractiveShellApp.extensions = [
54 54 'myextension'
55 55 ]
56 56
57 57 To load that same extension at runtime, use the ``%load_ext`` magic:
58 58
59 59 .. sourcecode:: ipython
60 60
61 61 In [1]: %load_ext myextension
62 62
63 63 To summarize, in conjunction with configuration files and profiles, IPython
64 64 extensions give you complete and flexible control over your IPython
65 65 setup.
66 66
67 67 Extensions bundled with IPython
68 68 ===============================
69 69
70 70 .. toctree::
71 71 :maxdepth: 1
72 72
73 73 autoreload
74 cythonmagic
74 75 parallelmagic
75 76 storemagic
76 77 sympyprinting
General Comments 0
You need to be logged in to leave comments. Login now