session.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463
  1. """Tools for setting up interactive sessions. """
  2. from sympy.external.gmpy import GROUND_TYPES
  3. from sympy.external.importtools import version_tuple
  4. from sympy.interactive.printing import init_printing
  5. from sympy.utilities.misc import ARCH
  6. preexec_source = """\
  7. from sympy import *
  8. x, y, z, t = symbols('x y z t')
  9. k, m, n = symbols('k m n', integer=True)
  10. f, g, h = symbols('f g h', cls=Function)
  11. init_printing()
  12. """
  13. verbose_message = """\
  14. These commands were executed:
  15. %(source)s
  16. Documentation can be found at https://docs.sympy.org/%(version)s
  17. """
  18. no_ipython = """\
  19. Couldn't locate IPython. Having IPython installed is greatly recommended.
  20. See http://ipython.scipy.org for more details. If you use Debian/Ubuntu,
  21. just install the 'ipython' package and start isympy again.
  22. """
  23. def _make_message(ipython=True, quiet=False, source=None):
  24. """Create a banner for an interactive session. """
  25. from sympy import __version__ as sympy_version
  26. from sympy import SYMPY_DEBUG
  27. import sys
  28. import os
  29. if quiet:
  30. return ""
  31. python_version = "%d.%d.%d" % sys.version_info[:3]
  32. if ipython:
  33. shell_name = "IPython"
  34. else:
  35. shell_name = "Python"
  36. info = ['ground types: %s' % GROUND_TYPES]
  37. cache = os.getenv('SYMPY_USE_CACHE')
  38. if cache is not None and cache.lower() == 'no':
  39. info.append('cache: off')
  40. if SYMPY_DEBUG:
  41. info.append('debugging: on')
  42. args = shell_name, sympy_version, python_version, ARCH, ', '.join(info)
  43. message = "%s console for SymPy %s (Python %s-%s) (%s)\n" % args
  44. if source is None:
  45. source = preexec_source
  46. _source = ""
  47. for line in source.split('\n')[:-1]:
  48. if not line:
  49. _source += '\n'
  50. else:
  51. _source += '>>> ' + line + '\n'
  52. doc_version = sympy_version
  53. if 'dev' in doc_version:
  54. doc_version = "dev"
  55. else:
  56. doc_version = "%s/" % doc_version
  57. message += '\n' + verbose_message % {'source': _source,
  58. 'version': doc_version}
  59. return message
  60. def int_to_Integer(s):
  61. """
  62. Wrap integer literals with Integer.
  63. This is based on the decistmt example from
  64. http://docs.python.org/library/tokenize.html.
  65. Only integer literals are converted. Float literals are left alone.
  66. Examples
  67. ========
  68. >>> from sympy import Integer # noqa: F401
  69. >>> from sympy.interactive.session import int_to_Integer
  70. >>> s = '1.2 + 1/2 - 0x12 + a1'
  71. >>> int_to_Integer(s)
  72. '1.2 +Integer (1 )/Integer (2 )-Integer (0x12 )+a1 '
  73. >>> s = 'print (1/2)'
  74. >>> int_to_Integer(s)
  75. 'print (Integer (1 )/Integer (2 ))'
  76. >>> exec(s)
  77. 0.5
  78. >>> exec(int_to_Integer(s))
  79. 1/2
  80. """
  81. from tokenize import generate_tokens, untokenize, NUMBER, NAME, OP
  82. from io import StringIO
  83. def _is_int(num):
  84. """
  85. Returns true if string value num (with token NUMBER) represents an integer.
  86. """
  87. # XXX: Is there something in the standard library that will do this?
  88. if '.' in num or 'j' in num.lower() or 'e' in num.lower():
  89. return False
  90. return True
  91. result = []
  92. g = generate_tokens(StringIO(s).readline) # tokenize the string
  93. for toknum, tokval, _, _, _ in g:
  94. if toknum == NUMBER and _is_int(tokval): # replace NUMBER tokens
  95. result.extend([
  96. (NAME, 'Integer'),
  97. (OP, '('),
  98. (NUMBER, tokval),
  99. (OP, ')')
  100. ])
  101. else:
  102. result.append((toknum, tokval))
  103. return untokenize(result)
  104. def enable_automatic_int_sympification(shell):
  105. """
  106. Allow IPython to automatically convert integer literals to Integer.
  107. """
  108. import ast
  109. old_run_cell = shell.run_cell
  110. def my_run_cell(cell, *args, **kwargs):
  111. try:
  112. # Check the cell for syntax errors. This way, the syntax error
  113. # will show the original input, not the transformed input. The
  114. # downside here is that IPython magic like %timeit will not work
  115. # with transformed input (but on the other hand, IPython magic
  116. # that doesn't expect transformed input will continue to work).
  117. ast.parse(cell)
  118. except SyntaxError:
  119. pass
  120. else:
  121. cell = int_to_Integer(cell)
  122. old_run_cell(cell, *args, **kwargs)
  123. shell.run_cell = my_run_cell
  124. def enable_automatic_symbols(shell):
  125. """Allow IPython to automatically create symbols (``isympy -a``). """
  126. # XXX: This should perhaps use tokenize, like int_to_Integer() above.
  127. # This would avoid re-executing the code, which can lead to subtle
  128. # issues. For example:
  129. #
  130. # In [1]: a = 1
  131. #
  132. # In [2]: for i in range(10):
  133. # ...: a += 1
  134. # ...:
  135. #
  136. # In [3]: a
  137. # Out[3]: 11
  138. #
  139. # In [4]: a = 1
  140. #
  141. # In [5]: for i in range(10):
  142. # ...: a += 1
  143. # ...: print b
  144. # ...:
  145. # b
  146. # b
  147. # b
  148. # b
  149. # b
  150. # b
  151. # b
  152. # b
  153. # b
  154. # b
  155. #
  156. # In [6]: a
  157. # Out[6]: 12
  158. #
  159. # Note how the for loop is executed again because `b` was not defined, but `a`
  160. # was already incremented once, so the result is that it is incremented
  161. # multiple times.
  162. import re
  163. re_nameerror = re.compile(
  164. "name '(?P<symbol>[A-Za-z_][A-Za-z0-9_]*)' is not defined")
  165. def _handler(self, etype, value, tb, tb_offset=None):
  166. """Handle :exc:`NameError` exception and allow injection of missing symbols. """
  167. if etype is NameError and tb.tb_next and not tb.tb_next.tb_next:
  168. match = re_nameerror.match(str(value))
  169. if match is not None:
  170. # XXX: Make sure Symbol is in scope. Otherwise you'll get infinite recursion.
  171. self.run_cell("%(symbol)s = Symbol('%(symbol)s')" %
  172. {'symbol': match.group("symbol")}, store_history=False)
  173. try:
  174. code = self.user_ns['In'][-1]
  175. except (KeyError, IndexError):
  176. pass
  177. else:
  178. self.run_cell(code, store_history=False)
  179. return None
  180. finally:
  181. self.run_cell("del %s" % match.group("symbol"),
  182. store_history=False)
  183. stb = self.InteractiveTB.structured_traceback(
  184. etype, value, tb, tb_offset=tb_offset)
  185. self._showtraceback(etype, value, stb)
  186. shell.set_custom_exc((NameError,), _handler)
  187. def init_ipython_session(shell=None, argv=[], auto_symbols=False, auto_int_to_Integer=False):
  188. """Construct new IPython session. """
  189. import IPython
  190. if version_tuple(IPython.__version__) >= version_tuple('0.11'):
  191. if not shell:
  192. # use an app to parse the command line, and init config
  193. # IPython 1.0 deprecates the frontend module, so we import directly
  194. # from the terminal module to prevent a deprecation message from being
  195. # shown.
  196. if version_tuple(IPython.__version__) >= version_tuple('1.0'):
  197. from IPython.terminal import ipapp
  198. else:
  199. from IPython.frontend.terminal import ipapp
  200. app = ipapp.TerminalIPythonApp()
  201. # don't draw IPython banner during initialization:
  202. app.display_banner = False
  203. app.initialize(argv)
  204. shell = app.shell
  205. if auto_symbols:
  206. enable_automatic_symbols(shell)
  207. if auto_int_to_Integer:
  208. enable_automatic_int_sympification(shell)
  209. return shell
  210. else:
  211. from IPython.Shell import make_IPython
  212. return make_IPython(argv)
  213. def init_python_session():
  214. """Construct new Python session. """
  215. from code import InteractiveConsole
  216. class SymPyConsole(InteractiveConsole):
  217. """An interactive console with readline support. """
  218. def __init__(self):
  219. ns_locals = dict()
  220. InteractiveConsole.__init__(self, locals=ns_locals)
  221. try:
  222. import rlcompleter
  223. import readline
  224. except ImportError:
  225. pass
  226. else:
  227. import os
  228. import atexit
  229. readline.set_completer(rlcompleter.Completer(ns_locals).complete)
  230. readline.parse_and_bind('tab: complete')
  231. if hasattr(readline, 'read_history_file'):
  232. history = os.path.expanduser('~/.sympy-history')
  233. try:
  234. readline.read_history_file(history)
  235. except OSError:
  236. pass
  237. atexit.register(readline.write_history_file, history)
  238. return SymPyConsole()
  239. def init_session(ipython=None, pretty_print=True, order=None,
  240. use_unicode=None, use_latex=None, quiet=False, auto_symbols=False,
  241. auto_int_to_Integer=False, str_printer=None, pretty_printer=None,
  242. latex_printer=None, argv=[]):
  243. """
  244. Initialize an embedded IPython or Python session. The IPython session is
  245. initiated with the --pylab option, without the numpy imports, so that
  246. matplotlib plotting can be interactive.
  247. Parameters
  248. ==========
  249. pretty_print: boolean
  250. If True, use pretty_print to stringify;
  251. if False, use sstrrepr to stringify.
  252. order: string or None
  253. There are a few different settings for this parameter:
  254. lex (default), which is lexographic order;
  255. grlex, which is graded lexographic order;
  256. grevlex, which is reversed graded lexographic order;
  257. old, which is used for compatibility reasons and for long expressions;
  258. None, which sets it to lex.
  259. use_unicode: boolean or None
  260. If True, use unicode characters;
  261. if False, do not use unicode characters.
  262. use_latex: boolean or None
  263. If True, use latex rendering if IPython GUI's;
  264. if False, do not use latex rendering.
  265. quiet: boolean
  266. If True, init_session will not print messages regarding its status;
  267. if False, init_session will print messages regarding its status.
  268. auto_symbols: boolean
  269. If True, IPython will automatically create symbols for you.
  270. If False, it will not.
  271. The default is False.
  272. auto_int_to_Integer: boolean
  273. If True, IPython will automatically wrap int literals with Integer, so
  274. that things like 1/2 give Rational(1, 2).
  275. If False, it will not.
  276. The default is False.
  277. ipython: boolean or None
  278. If True, printing will initialize for an IPython console;
  279. if False, printing will initialize for a normal console;
  280. The default is None, which automatically determines whether we are in
  281. an ipython instance or not.
  282. str_printer: function, optional, default=None
  283. A custom string printer function. This should mimic
  284. sympy.printing.sstrrepr().
  285. pretty_printer: function, optional, default=None
  286. A custom pretty printer. This should mimic sympy.printing.pretty().
  287. latex_printer: function, optional, default=None
  288. A custom LaTeX printer. This should mimic sympy.printing.latex()
  289. This should mimic sympy.printing.latex().
  290. argv: list of arguments for IPython
  291. See sympy.bin.isympy for options that can be used to initialize IPython.
  292. See Also
  293. ========
  294. sympy.interactive.printing.init_printing: for examples and the rest of the parameters.
  295. Examples
  296. ========
  297. >>> from sympy import init_session, Symbol, sin, sqrt
  298. >>> sin(x) #doctest: +SKIP
  299. NameError: name 'x' is not defined
  300. >>> init_session() #doctest: +SKIP
  301. >>> sin(x) #doctest: +SKIP
  302. sin(x)
  303. >>> sqrt(5) #doctest: +SKIP
  304. ___
  305. \\/ 5
  306. >>> init_session(pretty_print=False) #doctest: +SKIP
  307. >>> sqrt(5) #doctest: +SKIP
  308. sqrt(5)
  309. >>> y + x + y**2 + x**2 #doctest: +SKIP
  310. x**2 + x + y**2 + y
  311. >>> init_session(order='grlex') #doctest: +SKIP
  312. >>> y + x + y**2 + x**2 #doctest: +SKIP
  313. x**2 + y**2 + x + y
  314. >>> init_session(order='grevlex') #doctest: +SKIP
  315. >>> y * x**2 + x * y**2 #doctest: +SKIP
  316. x**2*y + x*y**2
  317. >>> init_session(order='old') #doctest: +SKIP
  318. >>> x**2 + y**2 + x + y #doctest: +SKIP
  319. x + y + x**2 + y**2
  320. >>> theta = Symbol('theta') #doctest: +SKIP
  321. >>> theta #doctest: +SKIP
  322. theta
  323. >>> init_session(use_unicode=True) #doctest: +SKIP
  324. >>> theta # doctest: +SKIP
  325. \u03b8
  326. """
  327. import sys
  328. in_ipython = False
  329. if ipython is not False:
  330. try:
  331. import IPython
  332. except ImportError:
  333. if ipython is True:
  334. raise RuntimeError("IPython is not available on this system")
  335. ip = None
  336. else:
  337. try:
  338. from IPython import get_ipython
  339. ip = get_ipython()
  340. except ImportError:
  341. ip = None
  342. in_ipython = bool(ip)
  343. if ipython is None:
  344. ipython = in_ipython
  345. if ipython is False:
  346. ip = init_python_session()
  347. mainloop = ip.interact
  348. else:
  349. ip = init_ipython_session(ip, argv=argv, auto_symbols=auto_symbols,
  350. auto_int_to_Integer=auto_int_to_Integer)
  351. if version_tuple(IPython.__version__) >= version_tuple('0.11'):
  352. # runsource is gone, use run_cell instead, which doesn't
  353. # take a symbol arg. The second arg is `store_history`,
  354. # and False means don't add the line to IPython's history.
  355. ip.runsource = lambda src, symbol='exec': ip.run_cell(src, False)
  356. # Enable interactive plotting using pylab.
  357. try:
  358. ip.enable_pylab(import_all=False)
  359. except Exception:
  360. # Causes an import error if matplotlib is not installed.
  361. # Causes other errors (depending on the backend) if there
  362. # is no display, or if there is some problem in the
  363. # backend, so we have a bare "except Exception" here
  364. pass
  365. if not in_ipython:
  366. mainloop = ip.mainloop
  367. if auto_symbols and (not ipython or version_tuple(IPython.__version__) < version_tuple('0.11')):
  368. raise RuntimeError("automatic construction of symbols is possible only in IPython 0.11 or above")
  369. if auto_int_to_Integer and (not ipython or version_tuple(IPython.__version__) < version_tuple('0.11')):
  370. raise RuntimeError("automatic int to Integer transformation is possible only in IPython 0.11 or above")
  371. _preexec_source = preexec_source
  372. ip.runsource(_preexec_source, symbol='exec')
  373. init_printing(pretty_print=pretty_print, order=order,
  374. use_unicode=use_unicode, use_latex=use_latex, ip=ip,
  375. str_printer=str_printer, pretty_printer=pretty_printer,
  376. latex_printer=latex_printer)
  377. message = _make_message(ipython, quiet, _preexec_source)
  378. if not in_ipython:
  379. print(message)
  380. mainloop()
  381. sys.exit('Exiting ...')
  382. else:
  383. print(message)
  384. import atexit
  385. atexit.register(lambda: print("Exiting ...\n"))