1# -*- coding: utf-8 -*- 2# tag: ipython 3 4"""Tests for the Cython magics extension.""" 5 6from __future__ import absolute_import 7 8import os 9import sys 10from contextlib import contextmanager 11from Cython.Build import IpythonMagic 12from Cython.TestUtils import CythonTest 13 14try: 15 import IPython.testing.globalipapp 16 from IPython.utils import py3compat 17except ImportError: 18 # Disable tests and fake helpers for initialisation below. 19 class _py3compat(object): 20 def str_to_unicode(self, s): 21 return s 22 23 py3compat = _py3compat() 24 25 def skip_if_not_installed(_): 26 return None 27else: 28 def skip_if_not_installed(c): 29 return c 30 31try: 32 # disable IPython history thread before it gets started to avoid having to clean it up 33 from IPython.core.history import HistoryManager 34 HistoryManager.enabled = False 35except ImportError: 36 pass 37 38code = py3compat.str_to_unicode("""\ 39def f(x): 40 return 2*x 41""") 42 43cython3_code = py3compat.str_to_unicode("""\ 44def f(int x): 45 return 2 / x 46 47def call(x): 48 return f(*(x,)) 49""") 50 51pgo_cython3_code = cython3_code + py3compat.str_to_unicode("""\ 52def main(): 53 for _ in range(100): call(5) 54main() 55""") 56 57 58if sys.platform == 'win32': 59 # not using IPython's decorators here because they depend on "nose" 60 try: 61 from unittest import skip as skip_win32 62 except ImportError: 63 # poor dev's silent @unittest.skip() 64 def skip_win32(dummy): 65 def _skip_win32(func): 66 return None 67 return _skip_win32 68else: 69 def skip_win32(dummy): 70 def _skip_win32(func): 71 def wrapper(*args, **kwargs): 72 func(*args, **kwargs) 73 return wrapper 74 return _skip_win32 75 76 77@skip_if_not_installed 78class TestIPythonMagic(CythonTest): 79 80 @classmethod 81 def setUpClass(cls): 82 CythonTest.setUpClass() 83 cls._ip = IPython.testing.globalipapp.get_ipython() 84 85 def setUp(self): 86 CythonTest.setUp(self) 87 self._ip.extension_manager.load_extension('cython') 88 89 def test_cython_inline(self): 90 ip = self._ip 91 ip.ex('a=10; b=20') 92 result = ip.run_cell_magic('cython_inline', '', 'return a+b') 93 self.assertEqual(result, 30) 94 95 @skip_win32('Skip on Windows') 96 def test_cython_pyximport(self): 97 ip = self._ip 98 module_name = '_test_cython_pyximport' 99 ip.run_cell_magic('cython_pyximport', module_name, code) 100 ip.ex('g = f(10)') 101 self.assertEqual(ip.user_ns['g'], 20.0) 102 ip.run_cell_magic('cython_pyximport', module_name, code) 103 ip.ex('h = f(-10)') 104 self.assertEqual(ip.user_ns['h'], -20.0) 105 try: 106 os.remove(module_name + '.pyx') 107 except OSError: 108 pass 109 110 def test_cython(self): 111 ip = self._ip 112 ip.run_cell_magic('cython', '', code) 113 ip.ex('g = f(10)') 114 self.assertEqual(ip.user_ns['g'], 20.0) 115 116 def test_cython_name(self): 117 # The Cython module named 'mymodule' defines the function f. 118 ip = self._ip 119 ip.run_cell_magic('cython', '--name=mymodule', code) 120 # This module can now be imported in the interactive namespace. 121 ip.ex('import mymodule; g = mymodule.f(10)') 122 self.assertEqual(ip.user_ns['g'], 20.0) 123 124 def test_cython_language_level(self): 125 # The Cython cell defines the functions f() and call(). 126 ip = self._ip 127 ip.run_cell_magic('cython', '', cython3_code) 128 ip.ex('g = f(10); h = call(10)') 129 if sys.version_info[0] < 3: 130 self.assertEqual(ip.user_ns['g'], 2 // 10) 131 self.assertEqual(ip.user_ns['h'], 2 // 10) 132 else: 133 self.assertEqual(ip.user_ns['g'], 2.0 / 10.0) 134 self.assertEqual(ip.user_ns['h'], 2.0 / 10.0) 135 136 def test_cython3(self): 137 # The Cython cell defines the functions f() and call(). 138 ip = self._ip 139 ip.run_cell_magic('cython', '-3', cython3_code) 140 ip.ex('g = f(10); h = call(10)') 141 self.assertEqual(ip.user_ns['g'], 2.0 / 10.0) 142 self.assertEqual(ip.user_ns['h'], 2.0 / 10.0) 143 144 def test_cython2(self): 145 # The Cython cell defines the functions f() and call(). 146 ip = self._ip 147 ip.run_cell_magic('cython', '-2', cython3_code) 148 ip.ex('g = f(10); h = call(10)') 149 self.assertEqual(ip.user_ns['g'], 2 // 10) 150 self.assertEqual(ip.user_ns['h'], 2 // 10) 151 152 @skip_win32('Skip on Windows') 153 def test_cython3_pgo(self): 154 # The Cython cell defines the functions f() and call(). 155 ip = self._ip 156 ip.run_cell_magic('cython', '-3 --pgo', pgo_cython3_code) 157 ip.ex('g = f(10); h = call(10); main()') 158 self.assertEqual(ip.user_ns['g'], 2.0 / 10.0) 159 self.assertEqual(ip.user_ns['h'], 2.0 / 10.0) 160 161 @skip_win32('Skip on Windows') 162 def test_extlibs(self): 163 ip = self._ip 164 code = py3compat.str_to_unicode(""" 165from libc.math cimport sin 166x = sin(0.0) 167 """) 168 ip.user_ns['x'] = 1 169 ip.run_cell_magic('cython', '-l m', code) 170 self.assertEqual(ip.user_ns['x'], 0) 171 172 173 def test_cython_verbose(self): 174 ip = self._ip 175 ip.run_cell_magic('cython', '--verbose', code) 176 ip.ex('g = f(10)') 177 self.assertEqual(ip.user_ns['g'], 20.0) 178 179 def test_cython_verbose_thresholds(self): 180 @contextmanager 181 def mock_distutils(): 182 class MockLog: 183 DEBUG = 1 184 INFO = 2 185 thresholds = [INFO] 186 187 def set_threshold(self, val): 188 self.thresholds.append(val) 189 return self.thresholds[-2] 190 191 192 new_log = MockLog() 193 old_log = IpythonMagic.distutils.log 194 try: 195 IpythonMagic.distutils.log = new_log 196 yield new_log 197 finally: 198 IpythonMagic.distutils.log = old_log 199 200 ip = self._ip 201 with mock_distutils() as verbose_log: 202 ip.run_cell_magic('cython', '--verbose', code) 203 ip.ex('g = f(10)') 204 self.assertEqual(ip.user_ns['g'], 20.0) 205 self.assertEquals([verbose_log.INFO, verbose_log.DEBUG, verbose_log.INFO], 206 verbose_log.thresholds) 207 208 with mock_distutils() as normal_log: 209 ip.run_cell_magic('cython', '', code) 210 ip.ex('g = f(10)') 211 self.assertEqual(ip.user_ns['g'], 20.0) 212 self.assertEquals([normal_log.INFO], normal_log.thresholds) 213