Spaces:
Build error
Build error
# -*- coding: utf-8 -*- | |
# tag: ipython | |
"""Tests for the Cython magics extension.""" | |
from __future__ import absolute_import | |
import os | |
import io | |
import sys | |
from contextlib import contextmanager | |
from unittest import skipIf | |
from Cython.Build import IpythonMagic | |
from Cython.TestUtils import CythonTest | |
from Cython.Compiler.Annotate import AnnotationCCodeWriter | |
try: | |
import IPython.testing.globalipapp | |
except ImportError: | |
# Disable tests and fake helpers for initialisation below. | |
def skip_if_not_installed(_): | |
return None | |
else: | |
def skip_if_not_installed(c): | |
return c | |
# not using IPython's decorators here because they depend on "nose" | |
skip_win32 = skipIf(sys.platform == 'win32', "Skip on Windows") | |
skip_py27 = skipIf(sys.version_info[:2] == (2,7), "Disabled in Py2.7") | |
try: | |
# disable IPython history thread before it gets started to avoid having to clean it up | |
from IPython.core.history import HistoryManager | |
HistoryManager.enabled = False | |
except ImportError: | |
pass | |
def capture_output(): | |
backup = sys.stdout, sys.stderr | |
try: | |
replacement = [ | |
io.TextIOWrapper(io.BytesIO(), encoding=sys.stdout.encoding), | |
io.TextIOWrapper(io.BytesIO(), encoding=sys.stderr.encoding), | |
] | |
sys.stdout, sys.stderr = replacement | |
output = [] | |
yield output | |
finally: | |
sys.stdout, sys.stderr = backup | |
for wrapper in replacement: | |
wrapper.seek(0) # rewind | |
output.append(wrapper.read()) | |
wrapper.close() | |
code = u"""\ | |
def f(x): | |
return 2*x | |
""" | |
cython3_code = u"""\ | |
def f(int x): | |
return 2 / x | |
def call(x): | |
return f(*(x,)) | |
""" | |
pgo_cython3_code = cython3_code + u"""\ | |
def main(): | |
for _ in range(100): call(5) | |
main() | |
""" | |
compile_error_code = u'''\ | |
cdef extern from *: | |
""" | |
xxx a=1; | |
""" | |
int a; | |
def doit(): | |
return a | |
''' | |
compile_warning_code = u'''\ | |
cdef extern from *: | |
""" | |
#pragma message ( "CWarning" ) | |
int a = 42; | |
""" | |
int a; | |
def doit(): | |
return a | |
''' | |
class TestIPythonMagic(CythonTest): | |
def setUpClass(cls): | |
CythonTest.setUpClass() | |
cls._ip = IPython.testing.globalipapp.get_ipython() | |
def setUp(self): | |
CythonTest.setUp(self) | |
self._ip.extension_manager.load_extension('cython') | |
def test_cython_inline(self): | |
ip = self._ip | |
ip.ex('a=10; b=20') | |
result = ip.run_cell_magic('cython_inline', '', 'return a+b') | |
self.assertEqual(result, 30) | |
def test_cython_pyximport(self): | |
ip = self._ip | |
module_name = '_test_cython_pyximport' | |
ip.run_cell_magic('cython_pyximport', module_name, code) | |
ip.ex('g = f(10)') | |
self.assertEqual(ip.user_ns['g'], 20.0) | |
ip.run_cell_magic('cython_pyximport', module_name, code) | |
ip.ex('h = f(-10)') | |
self.assertEqual(ip.user_ns['h'], -20.0) | |
try: | |
os.remove(module_name + '.pyx') | |
except OSError: | |
pass | |
def test_cython(self): | |
ip = self._ip | |
ip.run_cell_magic('cython', '', code) | |
ip.ex('g = f(10)') | |
self.assertEqual(ip.user_ns['g'], 20.0) | |
def test_cython_name(self): | |
# The Cython module named 'mymodule' defines the function f. | |
ip = self._ip | |
ip.run_cell_magic('cython', '--name=mymodule', code) | |
# This module can now be imported in the interactive namespace. | |
ip.ex('import mymodule; g = mymodule.f(10)') | |
self.assertEqual(ip.user_ns['g'], 20.0) | |
def test_cython_language_level(self): | |
# The Cython cell defines the functions f() and call(). | |
ip = self._ip | |
ip.run_cell_magic('cython', '', cython3_code) | |
ip.ex('g = f(10); h = call(10)') | |
if sys.version_info[0] < 3: | |
self.assertEqual(ip.user_ns['g'], 2 // 10) | |
self.assertEqual(ip.user_ns['h'], 2 // 10) | |
else: | |
self.assertEqual(ip.user_ns['g'], 2.0 / 10.0) | |
self.assertEqual(ip.user_ns['h'], 2.0 / 10.0) | |
def test_cython3(self): | |
# The Cython cell defines the functions f() and call(). | |
ip = self._ip | |
ip.run_cell_magic('cython', '-3', cython3_code) | |
ip.ex('g = f(10); h = call(10)') | |
self.assertEqual(ip.user_ns['g'], 2.0 / 10.0) | |
self.assertEqual(ip.user_ns['h'], 2.0 / 10.0) | |
def test_cython2(self): | |
# The Cython cell defines the functions f() and call(). | |
ip = self._ip | |
ip.run_cell_magic('cython', '-2', cython3_code) | |
ip.ex('g = f(10); h = call(10)') | |
self.assertEqual(ip.user_ns['g'], 2 // 10) | |
self.assertEqual(ip.user_ns['h'], 2 // 10) | |
def test_cython_compile_error_shown(self): | |
ip = self._ip | |
with capture_output() as out: | |
ip.run_cell_magic('cython', '-3', compile_error_code) | |
captured_out, captured_err = out | |
# it could be that c-level output is captured by distutil-extension | |
# (and not by us) and is printed to stdout: | |
captured_all = captured_out + "\n" + captured_err | |
self.assertTrue("error" in captured_all, msg="error in " + captured_all) | |
def test_cython_link_error_shown(self): | |
ip = self._ip | |
with capture_output() as out: | |
ip.run_cell_magic('cython', '-3 -l=xxxxxxxx', code) | |
captured_out, captured_err = out | |
# it could be that c-level output is captured by distutil-extension | |
# (and not by us) and is printed to stdout: | |
captured_all = captured_out + "\n!" + captured_err | |
self.assertTrue("error" in captured_all, msg="error in " + captured_all) | |
def test_cython_warning_shown(self): | |
ip = self._ip | |
with capture_output() as out: | |
# force rebuild, otherwise no warning as after the first success | |
# no build step is performed | |
ip.run_cell_magic('cython', '-3 -f', compile_warning_code) | |
captured_out, captured_err = out | |
# check that warning was printed to stdout even if build hasn't failed | |
self.assertTrue("CWarning" in captured_out) | |
# Not strictly broken in Py2.7 but currently fails in CI due to C compiler issues. | |
def test_cython3_pgo(self): | |
# The Cython cell defines the functions f() and call(). | |
ip = self._ip | |
ip.run_cell_magic('cython', '-3 --pgo', pgo_cython3_code) | |
ip.ex('g = f(10); h = call(10); main()') | |
self.assertEqual(ip.user_ns['g'], 2.0 / 10.0) | |
self.assertEqual(ip.user_ns['h'], 2.0 / 10.0) | |
def test_extlibs(self): | |
ip = self._ip | |
code = u""" | |
from libc.math cimport sin | |
x = sin(0.0) | |
""" | |
ip.user_ns['x'] = 1 | |
ip.run_cell_magic('cython', '-l m', code) | |
self.assertEqual(ip.user_ns['x'], 0) | |
def test_cython_verbose(self): | |
ip = self._ip | |
ip.run_cell_magic('cython', '--verbose', code) | |
ip.ex('g = f(10)') | |
self.assertEqual(ip.user_ns['g'], 20.0) | |
def test_cython_verbose_thresholds(self): | |
def mock_distutils(): | |
class MockLog: | |
DEBUG = 1 | |
INFO = 2 | |
thresholds = [INFO] | |
def set_threshold(self, val): | |
self.thresholds.append(val) | |
return self.thresholds[-2] | |
new_log = MockLog() | |
old_log = IpythonMagic.distutils.log | |
try: | |
IpythonMagic.distutils.log = new_log | |
yield new_log | |
finally: | |
IpythonMagic.distutils.log = old_log | |
ip = self._ip | |
with mock_distutils() as verbose_log: | |
ip.run_cell_magic('cython', '--verbose', code) | |
ip.ex('g = f(10)') | |
self.assertEqual(ip.user_ns['g'], 20.0) | |
self.assertEqual([verbose_log.INFO, verbose_log.DEBUG, verbose_log.INFO], | |
verbose_log.thresholds) | |
with mock_distutils() as normal_log: | |
ip.run_cell_magic('cython', '', code) | |
ip.ex('g = f(10)') | |
self.assertEqual(ip.user_ns['g'], 20.0) | |
self.assertEqual([normal_log.INFO], normal_log.thresholds) | |
def test_cython_no_annotate(self): | |
ip = self._ip | |
html = ip.run_cell_magic('cython', '', code) | |
self.assertTrue(html is None) | |
def test_cython_annotate(self): | |
ip = self._ip | |
html = ip.run_cell_magic('cython', '--annotate', code) | |
# somewhat brittle way to differentiate between annotated htmls | |
# with/without complete source code: | |
self.assertTrue(AnnotationCCodeWriter.COMPLETE_CODE_TITLE not in html.data) | |
def test_cython_annotate_default(self): | |
ip = self._ip | |
html = ip.run_cell_magic('cython', '-a', code) | |
# somewhat brittle way to differentiate between annotated htmls | |
# with/without complete source code: | |
self.assertTrue(AnnotationCCodeWriter.COMPLETE_CODE_TITLE not in html.data) | |
def test_cython_annotate_complete_c_code(self): | |
ip = self._ip | |
html = ip.run_cell_magic('cython', '--annotate-fullc', code) | |
# somewhat brittle way to differentiate between annotated htmls | |
# with/without complete source code: | |
self.assertTrue(AnnotationCCodeWriter.COMPLETE_CODE_TITLE in html.data) | |