|
"""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 = """\ |
|
Could not 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 |
|
https://docs.python.org/3/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. |
|
""" |
|
|
|
if '.' in num or 'j' in num.lower() or 'e' in num.lower(): |
|
return False |
|
return True |
|
|
|
result = [] |
|
g = generate_tokens(StringIO(s).readline) |
|
for toknum, tokval, _, _, _ in g: |
|
if toknum == NUMBER and _is_int(tokval): |
|
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: |
|
|
|
|
|
|
|
|
|
|
|
ast.parse(cell) |
|
except SyntaxError: |
|
pass |
|
else: |
|
cell = int_to_Integer(cell) |
|
return 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``). """ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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: |
|
|
|
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: |
|
|
|
|
|
|
|
|
|
if version_tuple(IPython.__version__) >= version_tuple('1.0'): |
|
from IPython.terminal import ipapp |
|
else: |
|
from IPython.frontend.terminal import ipapp |
|
app = ipapp.TerminalIPythonApp() |
|
|
|
|
|
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 = {} |
|
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'): |
|
|
|
|
|
|
|
ip.runsource = lambda src, symbol='exec': ip.run_cell(src, False) |
|
|
|
|
|
try: |
|
ip.enable_pylab(import_all=False) |
|
except Exception: |
|
|
|
|
|
|
|
|
|
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")) |
|
|