test_ipython.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. """Tests of tools for setting up interactive IPython sessions. """
  2. from sympy.interactive.session import (init_ipython_session,
  3. enable_automatic_symbols, enable_automatic_int_sympification)
  4. from sympy.core import Symbol, Rational, Integer
  5. from sympy.external import import_module
  6. from sympy.testing.pytest import raises
  7. # TODO: The code below could be made more granular with something like:
  8. #
  9. # @requires('IPython', version=">=0.11")
  10. # def test_automatic_symbols(ipython):
  11. ipython = import_module("IPython", min_module_version="0.11")
  12. if not ipython:
  13. #bin/test will not execute any tests now
  14. disabled = True
  15. # WARNING: These tests will modify the existing IPython environment. IPython
  16. # uses a single instance for its interpreter, so there is no way to isolate
  17. # the test from another IPython session. It also means that if this test is
  18. # run twice in the same Python session it will fail. This isn't usually a
  19. # problem because the test suite is run in a subprocess by default, but if the
  20. # tests are run with subprocess=False it can pollute the current IPython
  21. # session. See the discussion in issue #15149.
  22. def test_automatic_symbols():
  23. # NOTE: Because of the way the hook works, you have to use run_cell(code,
  24. # True). This means that the code must have no Out, or it will be printed
  25. # during the tests.
  26. app = init_ipython_session()
  27. app.run_cell("from sympy import *")
  28. enable_automatic_symbols(app)
  29. symbol = "verylongsymbolname"
  30. assert symbol not in app.user_ns
  31. app.run_cell("a = %s" % symbol, True)
  32. assert symbol not in app.user_ns
  33. app.run_cell("a = type(%s)" % symbol, True)
  34. assert app.user_ns['a'] == Symbol
  35. app.run_cell("%s = Symbol('%s')" % (symbol, symbol), True)
  36. assert symbol in app.user_ns
  37. # Check that built-in names aren't overridden
  38. app.run_cell("a = all == __builtin__.all", True)
  39. assert "all" not in app.user_ns
  40. assert app.user_ns['a'] is True
  41. # Check that SymPy names aren't overridden
  42. app.run_cell("import sympy")
  43. app.run_cell("a = factorial == sympy.factorial", True)
  44. assert app.user_ns['a'] is True
  45. def test_int_to_Integer():
  46. # XXX: Warning, don't test with == here. 0.5 == Rational(1, 2) is True!
  47. app = init_ipython_session()
  48. app.run_cell("from sympy import Integer")
  49. app.run_cell("a = 1")
  50. assert isinstance(app.user_ns['a'], int)
  51. enable_automatic_int_sympification(app)
  52. app.run_cell("a = 1/2")
  53. assert isinstance(app.user_ns['a'], Rational)
  54. app.run_cell("a = 1")
  55. assert isinstance(app.user_ns['a'], Integer)
  56. app.run_cell("a = int(1)")
  57. assert isinstance(app.user_ns['a'], int)
  58. app.run_cell("a = (1/\n2)")
  59. assert app.user_ns['a'] == Rational(1, 2)
  60. # TODO: How can we test that the output of a SyntaxError is the original
  61. # input, not the transformed input?
  62. def test_ipythonprinting():
  63. # Initialize and setup IPython session
  64. app = init_ipython_session()
  65. app.run_cell("ip = get_ipython()")
  66. app.run_cell("inst = ip.instance()")
  67. app.run_cell("format = inst.display_formatter.format")
  68. app.run_cell("from sympy import Symbol")
  69. # Printing without printing extension
  70. app.run_cell("a = format(Symbol('pi'))")
  71. app.run_cell("a2 = format(Symbol('pi')**2)")
  72. # Deal with API change starting at IPython 1.0
  73. if int(ipython.__version__.split(".")[0]) < 1:
  74. assert app.user_ns['a']['text/plain'] == "pi"
  75. assert app.user_ns['a2']['text/plain'] == "pi**2"
  76. else:
  77. assert app.user_ns['a'][0]['text/plain'] == "pi"
  78. assert app.user_ns['a2'][0]['text/plain'] == "pi**2"
  79. # Load printing extension
  80. app.run_cell("from sympy import init_printing")
  81. app.run_cell("init_printing()")
  82. # Printing with printing extension
  83. app.run_cell("a = format(Symbol('pi'))")
  84. app.run_cell("a2 = format(Symbol('pi')**2)")
  85. # Deal with API change starting at IPython 1.0
  86. if int(ipython.__version__.split(".")[0]) < 1:
  87. assert app.user_ns['a']['text/plain'] in ('\N{GREEK SMALL LETTER PI}', 'pi')
  88. assert app.user_ns['a2']['text/plain'] in (' 2\n\N{GREEK SMALL LETTER PI} ', ' 2\npi ')
  89. else:
  90. assert app.user_ns['a'][0]['text/plain'] in ('\N{GREEK SMALL LETTER PI}', 'pi')
  91. assert app.user_ns['a2'][0]['text/plain'] in (' 2\n\N{GREEK SMALL LETTER PI} ', ' 2\npi ')
  92. def test_print_builtin_option():
  93. # Initialize and setup IPython session
  94. app = init_ipython_session()
  95. app.run_cell("ip = get_ipython()")
  96. app.run_cell("inst = ip.instance()")
  97. app.run_cell("format = inst.display_formatter.format")
  98. app.run_cell("from sympy import Symbol")
  99. app.run_cell("from sympy import init_printing")
  100. app.run_cell("a = format({Symbol('pi'): 3.14, Symbol('n_i'): 3})")
  101. # Deal with API change starting at IPython 1.0
  102. if int(ipython.__version__.split(".")[0]) < 1:
  103. text = app.user_ns['a']['text/plain']
  104. raises(KeyError, lambda: app.user_ns['a']['text/latex'])
  105. else:
  106. text = app.user_ns['a'][0]['text/plain']
  107. raises(KeyError, lambda: app.user_ns['a'][0]['text/latex'])
  108. # XXX: How can we make this ignore the terminal width? This test fails if
  109. # the terminal is too narrow.
  110. assert text in ("{pi: 3.14, n_i: 3}",
  111. '{n\N{LATIN SUBSCRIPT SMALL LETTER I}: 3, \N{GREEK SMALL LETTER PI}: 3.14}',
  112. "{n_i: 3, pi: 3.14}",
  113. '{\N{GREEK SMALL LETTER PI}: 3.14, n\N{LATIN SUBSCRIPT SMALL LETTER I}: 3}')
  114. # If we enable the default printing, then the dictionary's should render
  115. # as a LaTeX version of the whole dict: ${\pi: 3.14, n_i: 3}$
  116. app.run_cell("inst.display_formatter.formatters['text/latex'].enabled = True")
  117. app.run_cell("init_printing(use_latex=True)")
  118. app.run_cell("a = format({Symbol('pi'): 3.14, Symbol('n_i'): 3})")
  119. # Deal with API change starting at IPython 1.0
  120. if int(ipython.__version__.split(".")[0]) < 1:
  121. text = app.user_ns['a']['text/plain']
  122. latex = app.user_ns['a']['text/latex']
  123. else:
  124. text = app.user_ns['a'][0]['text/plain']
  125. latex = app.user_ns['a'][0]['text/latex']
  126. assert text in ("{pi: 3.14, n_i: 3}",
  127. '{n\N{LATIN SUBSCRIPT SMALL LETTER I}: 3, \N{GREEK SMALL LETTER PI}: 3.14}',
  128. "{n_i: 3, pi: 3.14}",
  129. '{\N{GREEK SMALL LETTER PI}: 3.14, n\N{LATIN SUBSCRIPT SMALL LETTER I}: 3}')
  130. assert latex == r'$\displaystyle \left\{ n_{i} : 3, \ \pi : 3.14\right\}$'
  131. # Objects with an _latex overload should also be handled by our tuple
  132. # printer.
  133. app.run_cell("""\
  134. class WithOverload:
  135. def _latex(self, printer):
  136. return r"\\LaTeX"
  137. """)
  138. app.run_cell("a = format((WithOverload(),))")
  139. # Deal with API change starting at IPython 1.0
  140. if int(ipython.__version__.split(".")[0]) < 1:
  141. latex = app.user_ns['a']['text/latex']
  142. else:
  143. latex = app.user_ns['a'][0]['text/latex']
  144. assert latex == r'$\displaystyle \left( \LaTeX,\right)$'
  145. app.run_cell("inst.display_formatter.formatters['text/latex'].enabled = True")
  146. app.run_cell("init_printing(use_latex=True, print_builtin=False)")
  147. app.run_cell("a = format({Symbol('pi'): 3.14, Symbol('n_i'): 3})")
  148. # Deal with API change starting at IPython 1.0
  149. if int(ipython.__version__.split(".")[0]) < 1:
  150. text = app.user_ns['a']['text/plain']
  151. raises(KeyError, lambda: app.user_ns['a']['text/latex'])
  152. else:
  153. text = app.user_ns['a'][0]['text/plain']
  154. raises(KeyError, lambda: app.user_ns['a'][0]['text/latex'])
  155. # Note : In Python 3 we have one text type: str which holds Unicode data
  156. # and two byte types bytes and bytearray.
  157. # Python 3.3.3 + IPython 0.13.2 gives: '{n_i: 3, pi: 3.14}'
  158. # Python 3.3.3 + IPython 1.1.0 gives: '{n_i: 3, pi: 3.14}'
  159. assert text in ("{pi: 3.14, n_i: 3}", "{n_i: 3, pi: 3.14}")
  160. def test_builtin_containers():
  161. # Initialize and setup IPython session
  162. app = init_ipython_session()
  163. app.run_cell("ip = get_ipython()")
  164. app.run_cell("inst = ip.instance()")
  165. app.run_cell("format = inst.display_formatter.format")
  166. app.run_cell("inst.display_formatter.formatters['text/latex'].enabled = True")
  167. app.run_cell("from sympy import init_printing, Matrix")
  168. app.run_cell('init_printing(use_latex=True, use_unicode=False)')
  169. # Make sure containers that shouldn't pretty print don't.
  170. app.run_cell('a = format((True, False))')
  171. app.run_cell('import sys')
  172. app.run_cell('b = format(sys.flags)')
  173. app.run_cell('c = format((Matrix([1, 2]),))')
  174. # Deal with API change starting at IPython 1.0
  175. if int(ipython.__version__.split(".")[0]) < 1:
  176. assert app.user_ns['a']['text/plain'] == '(True, False)'
  177. assert 'text/latex' not in app.user_ns['a']
  178. assert app.user_ns['b']['text/plain'][:10] == 'sys.flags('
  179. assert 'text/latex' not in app.user_ns['b']
  180. assert app.user_ns['c']['text/plain'] == \
  181. """\
  182. [1] \n\
  183. ([ ],)
  184. [2] \
  185. """
  186. assert app.user_ns['c']['text/latex'] == '$\\displaystyle \\left( \\left[\\begin{matrix}1\\\\2\\end{matrix}\\right],\\right)$'
  187. else:
  188. assert app.user_ns['a'][0]['text/plain'] == '(True, False)'
  189. assert 'text/latex' not in app.user_ns['a'][0]
  190. assert app.user_ns['b'][0]['text/plain'][:10] == 'sys.flags('
  191. assert 'text/latex' not in app.user_ns['b'][0]
  192. assert app.user_ns['c'][0]['text/plain'] == \
  193. """\
  194. [1] \n\
  195. ([ ],)
  196. [2] \
  197. """
  198. assert app.user_ns['c'][0]['text/latex'] == '$\\displaystyle \\left( \\left[\\begin{matrix}1\\\\2\\end{matrix}\\right],\\right)$'
  199. def test_matplotlib_bad_latex():
  200. # Initialize and setup IPython session
  201. app = init_ipython_session()
  202. app.run_cell("import IPython")
  203. app.run_cell("ip = get_ipython()")
  204. app.run_cell("inst = ip.instance()")
  205. app.run_cell("format = inst.display_formatter.format")
  206. app.run_cell("from sympy import init_printing, Matrix")
  207. app.run_cell("init_printing(use_latex='matplotlib')")
  208. # The png formatter is not enabled by default in this context
  209. app.run_cell("inst.display_formatter.formatters['image/png'].enabled = True")
  210. # Make sure no warnings are raised by IPython
  211. app.run_cell("import warnings")
  212. # IPython.core.formatters.FormatterWarning was introduced in IPython 2.0
  213. if int(ipython.__version__.split(".")[0]) < 2:
  214. app.run_cell("warnings.simplefilter('error')")
  215. else:
  216. app.run_cell("warnings.simplefilter('error', IPython.core.formatters.FormatterWarning)")
  217. # This should not raise an exception
  218. app.run_cell("a = format(Matrix([1, 2, 3]))")
  219. # issue 9799
  220. app.run_cell("from sympy import Piecewise, Symbol, Eq")
  221. app.run_cell("x = Symbol('x'); pw = format(Piecewise((1, Eq(x, 0)), (0, True)))")
  222. def test_override_repr_latex():
  223. # Initialize and setup IPython session
  224. app = init_ipython_session()
  225. app.run_cell("import IPython")
  226. app.run_cell("ip = get_ipython()")
  227. app.run_cell("inst = ip.instance()")
  228. app.run_cell("format = inst.display_formatter.format")
  229. app.run_cell("inst.display_formatter.formatters['text/latex'].enabled = True")
  230. app.run_cell("from sympy import init_printing")
  231. app.run_cell("from sympy import Symbol")
  232. app.run_cell("init_printing(use_latex=True)")
  233. app.run_cell("""\
  234. class SymbolWithOverload(Symbol):
  235. def _repr_latex_(self):
  236. return r"Hello " + super()._repr_latex_() + " world"
  237. """)
  238. app.run_cell("a = format(SymbolWithOverload('s'))")
  239. if int(ipython.__version__.split(".")[0]) < 1:
  240. latex = app.user_ns['a']['text/latex']
  241. else:
  242. latex = app.user_ns['a'][0]['text/latex']
  243. assert latex == r'Hello $\displaystyle s$ world'