123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463 |
- """Tools for setting up interactive sessions. """
- from sympy.external.gmpy import GROUND_TYPES
- from sympy.external.importtools import version_tuple
- from sympy.interactive.printing import init_printing
- from sympy.utilities.misc import ARCH
- preexec_source = """\
- from sympy import *
- x, y, z, t = symbols('x y z t')
- k, m, n = symbols('k m n', integer=True)
- f, g, h = symbols('f g h', cls=Function)
- init_printing()
- """
- verbose_message = """\
- These commands were executed:
- %(source)s
- Documentation can be found at https://docs.sympy.org/%(version)s
- """
- no_ipython = """\
- Couldn't locate IPython. Having IPython installed is greatly recommended.
- See http://ipython.scipy.org for more details. If you use Debian/Ubuntu,
- just install the 'ipython' package and start isympy again.
- """
- def _make_message(ipython=True, quiet=False, source=None):
- """Create a banner for an interactive session. """
- from sympy import __version__ as sympy_version
- from sympy import SYMPY_DEBUG
- import sys
- import os
- if quiet:
- return ""
- python_version = "%d.%d.%d" % sys.version_info[:3]
- if ipython:
- shell_name = "IPython"
- else:
- shell_name = "Python"
- info = ['ground types: %s' % GROUND_TYPES]
- cache = os.getenv('SYMPY_USE_CACHE')
- if cache is not None and cache.lower() == 'no':
- info.append('cache: off')
- if SYMPY_DEBUG:
- info.append('debugging: on')
- args = shell_name, sympy_version, python_version, ARCH, ', '.join(info)
- message = "%s console for SymPy %s (Python %s-%s) (%s)\n" % args
- if source is None:
- source = preexec_source
- _source = ""
- for line in source.split('\n')[:-1]:
- if not line:
- _source += '\n'
- else:
- _source += '>>> ' + line + '\n'
- doc_version = sympy_version
- if 'dev' in doc_version:
- doc_version = "dev"
- else:
- doc_version = "%s/" % doc_version
- message += '\n' + verbose_message % {'source': _source,
- 'version': doc_version}
- return message
- def int_to_Integer(s):
- """
- Wrap integer literals with Integer.
- This is based on the decistmt example from
- http://docs.python.org/library/tokenize.html.
- Only integer literals are converted. Float literals are left alone.
- Examples
- ========
- >>> from sympy import Integer # noqa: F401
- >>> from sympy.interactive.session import int_to_Integer
- >>> s = '1.2 + 1/2 - 0x12 + a1'
- >>> int_to_Integer(s)
- '1.2 +Integer (1 )/Integer (2 )-Integer (0x12 )+a1 '
- >>> s = 'print (1/2)'
- >>> int_to_Integer(s)
- 'print (Integer (1 )/Integer (2 ))'
- >>> exec(s)
- 0.5
- >>> exec(int_to_Integer(s))
- 1/2
- """
- from tokenize import generate_tokens, untokenize, NUMBER, NAME, OP
- from io import StringIO
- def _is_int(num):
- """
- Returns true if string value num (with token NUMBER) represents an integer.
- """
- # XXX: Is there something in the standard library that will do this?
- if '.' in num or 'j' in num.lower() or 'e' in num.lower():
- return False
- return True
- result = []
- g = generate_tokens(StringIO(s).readline) # tokenize the string
- for toknum, tokval, _, _, _ in g:
- if toknum == NUMBER and _is_int(tokval): # replace NUMBER tokens
- result.extend([
- (NAME, 'Integer'),
- (OP, '('),
- (NUMBER, tokval),
- (OP, ')')
- ])
- else:
- result.append((toknum, tokval))
- return untokenize(result)
- def enable_automatic_int_sympification(shell):
- """
- Allow IPython to automatically convert integer literals to Integer.
- """
- import ast
- old_run_cell = shell.run_cell
- def my_run_cell(cell, *args, **kwargs):
- try:
- # Check the cell for syntax errors. This way, the syntax error
- # will show the original input, not the transformed input. The
- # downside here is that IPython magic like %timeit will not work
- # with transformed input (but on the other hand, IPython magic
- # that doesn't expect transformed input will continue to work).
- ast.parse(cell)
- except SyntaxError:
- pass
- else:
- cell = int_to_Integer(cell)
- old_run_cell(cell, *args, **kwargs)
- shell.run_cell = my_run_cell
- def enable_automatic_symbols(shell):
- """Allow IPython to automatically create symbols (``isympy -a``). """
- # XXX: This should perhaps use tokenize, like int_to_Integer() above.
- # This would avoid re-executing the code, which can lead to subtle
- # issues. For example:
- #
- # In [1]: a = 1
- #
- # In [2]: for i in range(10):
- # ...: a += 1
- # ...:
- #
- # In [3]: a
- # Out[3]: 11
- #
- # In [4]: a = 1
- #
- # In [5]: for i in range(10):
- # ...: a += 1
- # ...: print b
- # ...:
- # b
- # b
- # b
- # b
- # b
- # b
- # b
- # b
- # b
- # b
- #
- # In [6]: a
- # Out[6]: 12
- #
- # Note how the for loop is executed again because `b` was not defined, but `a`
- # was already incremented once, so the result is that it is incremented
- # multiple times.
- import re
- re_nameerror = re.compile(
- "name '(?P<symbol>[A-Za-z_][A-Za-z0-9_]*)' is not defined")
- def _handler(self, etype, value, tb, tb_offset=None):
- """Handle :exc:`NameError` exception and allow injection of missing symbols. """
- if etype is NameError and tb.tb_next and not tb.tb_next.tb_next:
- match = re_nameerror.match(str(value))
- if match is not None:
- # XXX: Make sure Symbol is in scope. Otherwise you'll get infinite recursion.
- self.run_cell("%(symbol)s = Symbol('%(symbol)s')" %
- {'symbol': match.group("symbol")}, store_history=False)
- try:
- code = self.user_ns['In'][-1]
- except (KeyError, IndexError):
- pass
- else:
- self.run_cell(code, store_history=False)
- return None
- finally:
- self.run_cell("del %s" % match.group("symbol"),
- store_history=False)
- stb = self.InteractiveTB.structured_traceback(
- etype, value, tb, tb_offset=tb_offset)
- self._showtraceback(etype, value, stb)
- shell.set_custom_exc((NameError,), _handler)
- def init_ipython_session(shell=None, argv=[], auto_symbols=False, auto_int_to_Integer=False):
- """Construct new IPython session. """
- import IPython
- if version_tuple(IPython.__version__) >= version_tuple('0.11'):
- if not shell:
- # use an app to parse the command line, and init config
- # IPython 1.0 deprecates the frontend module, so we import directly
- # from the terminal module to prevent a deprecation message from being
- # shown.
- if version_tuple(IPython.__version__) >= version_tuple('1.0'):
- from IPython.terminal import ipapp
- else:
- from IPython.frontend.terminal import ipapp
- app = ipapp.TerminalIPythonApp()
- # don't draw IPython banner during initialization:
- app.display_banner = False
- app.initialize(argv)
- shell = app.shell
- if auto_symbols:
- enable_automatic_symbols(shell)
- if auto_int_to_Integer:
- enable_automatic_int_sympification(shell)
- return shell
- else:
- from IPython.Shell import make_IPython
- return make_IPython(argv)
- def init_python_session():
- """Construct new Python session. """
- from code import InteractiveConsole
- class SymPyConsole(InteractiveConsole):
- """An interactive console with readline support. """
- def __init__(self):
- ns_locals = dict()
- InteractiveConsole.__init__(self, locals=ns_locals)
- try:
- import rlcompleter
- import readline
- except ImportError:
- pass
- else:
- import os
- import atexit
- readline.set_completer(rlcompleter.Completer(ns_locals).complete)
- readline.parse_and_bind('tab: complete')
- if hasattr(readline, 'read_history_file'):
- history = os.path.expanduser('~/.sympy-history')
- try:
- readline.read_history_file(history)
- except OSError:
- pass
- atexit.register(readline.write_history_file, history)
- return SymPyConsole()
- def init_session(ipython=None, pretty_print=True, order=None,
- use_unicode=None, use_latex=None, quiet=False, auto_symbols=False,
- auto_int_to_Integer=False, str_printer=None, pretty_printer=None,
- latex_printer=None, argv=[]):
- """
- Initialize an embedded IPython or Python session. The IPython session is
- initiated with the --pylab option, without the numpy imports, so that
- matplotlib plotting can be interactive.
- Parameters
- ==========
- pretty_print: boolean
- If True, use pretty_print to stringify;
- if False, use sstrrepr to stringify.
- order: string or None
- There are a few different settings for this parameter:
- lex (default), which is lexographic order;
- grlex, which is graded lexographic order;
- grevlex, which is reversed graded lexographic order;
- old, which is used for compatibility reasons and for long expressions;
- None, which sets it to lex.
- use_unicode: boolean or None
- If True, use unicode characters;
- if False, do not use unicode characters.
- use_latex: boolean or None
- If True, use latex rendering if IPython GUI's;
- if False, do not use latex rendering.
- quiet: boolean
- If True, init_session will not print messages regarding its status;
- if False, init_session will print messages regarding its status.
- auto_symbols: boolean
- If True, IPython will automatically create symbols for you.
- If False, it will not.
- The default is False.
- auto_int_to_Integer: boolean
- If True, IPython will automatically wrap int literals with Integer, so
- that things like 1/2 give Rational(1, 2).
- If False, it will not.
- The default is False.
- ipython: boolean or None
- If True, printing will initialize for an IPython console;
- if False, printing will initialize for a normal console;
- The default is None, which automatically determines whether we are in
- an ipython instance or not.
- str_printer: function, optional, default=None
- A custom string printer function. This should mimic
- sympy.printing.sstrrepr().
- pretty_printer: function, optional, default=None
- A custom pretty printer. This should mimic sympy.printing.pretty().
- latex_printer: function, optional, default=None
- A custom LaTeX printer. This should mimic sympy.printing.latex()
- This should mimic sympy.printing.latex().
- argv: list of arguments for IPython
- See sympy.bin.isympy for options that can be used to initialize IPython.
- See Also
- ========
- sympy.interactive.printing.init_printing: for examples and the rest of the parameters.
- Examples
- ========
- >>> from sympy import init_session, Symbol, sin, sqrt
- >>> sin(x) #doctest: +SKIP
- NameError: name 'x' is not defined
- >>> init_session() #doctest: +SKIP
- >>> sin(x) #doctest: +SKIP
- sin(x)
- >>> sqrt(5) #doctest: +SKIP
- ___
- \\/ 5
- >>> init_session(pretty_print=False) #doctest: +SKIP
- >>> sqrt(5) #doctest: +SKIP
- sqrt(5)
- >>> y + x + y**2 + x**2 #doctest: +SKIP
- x**2 + x + y**2 + y
- >>> init_session(order='grlex') #doctest: +SKIP
- >>> y + x + y**2 + x**2 #doctest: +SKIP
- x**2 + y**2 + x + y
- >>> init_session(order='grevlex') #doctest: +SKIP
- >>> y * x**2 + x * y**2 #doctest: +SKIP
- x**2*y + x*y**2
- >>> init_session(order='old') #doctest: +SKIP
- >>> x**2 + y**2 + x + y #doctest: +SKIP
- x + y + x**2 + y**2
- >>> theta = Symbol('theta') #doctest: +SKIP
- >>> theta #doctest: +SKIP
- theta
- >>> init_session(use_unicode=True) #doctest: +SKIP
- >>> theta # doctest: +SKIP
- \u03b8
- """
- import sys
- in_ipython = False
- if ipython is not False:
- try:
- import IPython
- except ImportError:
- if ipython is True:
- raise RuntimeError("IPython is not available on this system")
- ip = None
- else:
- try:
- from IPython import get_ipython
- ip = get_ipython()
- except ImportError:
- ip = None
- in_ipython = bool(ip)
- if ipython is None:
- ipython = in_ipython
- if ipython is False:
- ip = init_python_session()
- mainloop = ip.interact
- else:
- ip = init_ipython_session(ip, argv=argv, auto_symbols=auto_symbols,
- auto_int_to_Integer=auto_int_to_Integer)
- if version_tuple(IPython.__version__) >= version_tuple('0.11'):
- # runsource is gone, use run_cell instead, which doesn't
- # take a symbol arg. The second arg is `store_history`,
- # and False means don't add the line to IPython's history.
- ip.runsource = lambda src, symbol='exec': ip.run_cell(src, False)
- # Enable interactive plotting using pylab.
- try:
- ip.enable_pylab(import_all=False)
- except Exception:
- # Causes an import error if matplotlib is not installed.
- # Causes other errors (depending on the backend) if there
- # is no display, or if there is some problem in the
- # backend, so we have a bare "except Exception" here
- pass
- if not in_ipython:
- mainloop = ip.mainloop
- if auto_symbols and (not ipython or version_tuple(IPython.__version__) < version_tuple('0.11')):
- raise RuntimeError("automatic construction of symbols is possible only in IPython 0.11 or above")
- if auto_int_to_Integer and (not ipython or version_tuple(IPython.__version__) < version_tuple('0.11')):
- raise RuntimeError("automatic int to Integer transformation is possible only in IPython 0.11 or above")
- _preexec_source = preexec_source
- ip.runsource(_preexec_source, symbol='exec')
- init_printing(pretty_print=pretty_print, order=order,
- use_unicode=use_unicode, use_latex=use_latex, ip=ip,
- str_printer=str_printer, pretty_printer=pretty_printer,
- latex_printer=latex_printer)
- message = _make_message(ipython, quiet, _preexec_source)
- if not in_ipython:
- print(message)
- mainloop()
- sys.exit('Exiting ...')
- else:
- print(message)
- import atexit
- atexit.register(lambda: print("Exiting ...\n"))
|