diff --git a/src/sage/doctest/forker.py b/src/sage/doctest/forker.py index 2654016..d3daed6 100644 --- a/src/sage/doctest/forker.py +++ b/src/sage/doctest/forker.py @@ -116,10 +116,6 @@ def init_sage(): from sage.structure.debug_options import debug debug.refine_category_hash_check = True - # Disable IPython colors during doctests - from sage.repl.interpreter import DEFAULT_SAGE_CONFIG - DEFAULT_SAGE_CONFIG.TerminalInteractiveShell.colors = 'NoColor' - # We import readline before forking, otherwise Pdb doesn't work # os OS X: http://trac.sagemath.org/14289 import readline @@ -1088,15 +1084,13 @@ class SageDocTestRunner(doctest.DocTestRunner): sage: _ = sage0.eval("import doctest, sys, os, multiprocessing, subprocess") sage: _ = sage0.eval("from sage.doctest.parsing import SageOutputChecker") sage: _ = sage0.eval("import sage.doctest.forker as sdf") - sage: _ = sage0.eval("sdf.init_sage()") sage: _ = sage0.eval("from sage.doctest.control import DocTestDefaults") sage: _ = sage0.eval("DD = DocTestDefaults(debug=True)") sage: _ = sage0.eval("ex1 = doctest.Example('a = 17', '')") sage: _ = sage0.eval("ex2 = doctest.Example('2*a', '1')") sage: _ = sage0.eval("DT = doctest.DocTest([ex1,ex2], globals(), 'doubling', None, 0, None)") sage: _ = sage0.eval("DTR = sdf.SageDocTestRunner(SageOutputChecker(), verbose=False, sage_options=DD, optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS)") - sage: sage0._prompt = r"debug: " - sage: print(sage0.eval("DTR.run(DT, clear_globs=False)")) # indirect doctest + sage: print(sage0.eval("sdf.init_sage(); DTR.run(DT, clear_globs=False)")) # indirect doctest ********************************************************************** Line 1, in doubling Failed example: @@ -1110,7 +1104,6 @@ class SageDocTestRunner(doctest.DocTestRunner): ... sage: sage0.eval("a") '...17' - sage: sage0._prompt = "sage: " sage: sage0.eval("quit") 'Returning to doctests...TestResults(failed=1, attempted=2)' """ @@ -1144,13 +1137,14 @@ class SageDocTestRunner(doctest.DocTestRunner): print(src) if ex.want: print(doctest._indent(ex.want[:-1])) - from sage.repl.interpreter import DEFAULT_SAGE_CONFIG + from sage.repl.configuration import sage_ipython_config + from sage.repl.prompts import DebugPrompts from IPython.terminal.embed import InteractiveShellEmbed - import copy - cfg = copy.deepcopy(DEFAULT_SAGE_CONFIG) - prompt_config = cfg.PromptManager - prompt_config.in_template = 'debug: ' - prompt_config.in2_template = '.....: ' + cfg = sage_ipython_config.default() + # Currently this doesn't work: prompts only work in pty + # We keep simple_prompt=True, prompts will be "In [0]:" + # cfg.InteractiveShell.prompts_class = DebugPrompts + # cfg.InteractiveShell.simple_prompt = False shell = InteractiveShellEmbed(config=cfg, banner1='', user_ns=dict(globs)) shell(header='', stack_depth=2) except KeyboardInterrupt: @@ -1248,6 +1242,7 @@ class SageDocTestRunner(doctest.DocTestRunner): sage: _ = sage0.eval("ex = doctest.Example('E = EllipticCurve([0,0]); E', 'A singular Elliptic Curve')") sage: _ = sage0.eval("DT = doctest.DocTest([ex], globals(), 'singular_curve', None, 0, None)") sage: _ = sage0.eval("DTR = sdf.SageDocTestRunner(SageOutputChecker(), verbose=False, sage_options=DD, optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS)") + sage: old_prompt = sage0._prompt sage: sage0._prompt = r"\(Pdb\) " sage: sage0.eval("DTR.run(DT, clear_globs=False)") # indirect doctest '... ArithmeticError("invariants " + str(ainvs) + " define a singular curve")' @@ -1257,7 +1252,7 @@ class SageDocTestRunner(doctest.DocTestRunner): '...EllipticCurve_field.__init__(self, K, ainvs)' sage: sage0.eval("p ainvs") '(0, 0, 0, 0, 0)' - sage: sage0._prompt = "sage: " + sage: sage0._prompt = old_prompt sage: sage0.eval("quit") 'TestResults(failed=1, attempted=1)' """ diff --git a/src/sage/doctest/test.py b/src/sage/doctest/test.py index d69136f..a6cc4d0 100644 --- a/src/sage/doctest/test.py +++ b/src/sage/doctest/test.py @@ -360,7 +360,7 @@ Test the ``--debug`` option:: s...: b = 5 s...: a + b 8 - debug: + In [1]: Returning to doctests... ********************************************************************** diff --git a/src/sage/interfaces/sage0.py b/src/sage/interfaces/sage0.py index 87bfc0b..77cd151 100644 --- a/src/sage/interfaces/sage0.py +++ b/src/sage/interfaces/sage0.py @@ -18,6 +18,7 @@ from __future__ import absolute_import import cPickle import os +import re from .expect import Expect, ExpectElement, FunctionElement import sage.repl.preparse @@ -146,10 +147,17 @@ class Sage(ExtraTabCompletion, Expect): if init_code is None: init_code = ['from sage.all import *', 'import cPickle'] else: - # Disable the IPython history (implemented as SQLite database) - # to avoid problems with locking. - command = "sage-ipython --HistoryManager.hist_file=:memory: --colors=NoColor" - prompt = "sage: " + command = ' '.join([ + 'sage-ipython', + # Disable the IPython history (implemented as SQLite database) + # to avoid problems with locking. + '--HistoryManager.hist_file=:memory:', + # Disable everything that prints ANSI codes + '--colors=NoColor', + '--no-term-title', + '--simple-prompt', + ]) + prompt = re.compile('In \[\d+\]: ') if init_code is None: init_code = ['import cPickle'] diff --git a/src/sage/misc/trace.py b/src/sage/misc/trace.py index 0da17e7..efcaa33 100644 --- a/src/sage/misc/trace.py +++ b/src/sage/misc/trace.py @@ -54,6 +54,7 @@ def trace(code, preparse=True): sage: import pexpect sage: s = pexpect.spawn('sage') sage: _ = s.sendline("trace('print(factor(10))'); print(3+97)") + sage: _ = s.expect('ipdb>', timeout=90) sage: _ = s.sendline("s"); _ = s.sendline("c"); sage: _ = s.expect('100', timeout=90) diff --git a/src/sage/repl/configuration.py b/src/sage/repl/configuration.py new file mode 100644 index 0000000..5034039 --- /dev/null +++ b/src/sage/repl/configuration.py @@ -0,0 +1,152 @@ +r""" +Sage's IPython Configuration +""" + +#***************************************************************************** +# Copyright (C) 2016 Volker Braun +# +# Distributed under the terms of the GNU General Public License (GPL) +# as published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from __future__ import absolute_import + +import sys +import copy +from traitlets.config.loader import Config + +from sage.repl.prompts import SagePrompts + + +# Name of the Sage IPython extension +SAGE_EXTENSION = 'sage' + + +class SageIpythonConfiguration(object): + + def _doctest_mode(self): + """ + Whether we are in doctest mode + + This returns ``True`` during doctests. + + EXAMPLES:: + + sage: from sage.repl.configuration import sage_ipython_config + sage: sage_ipython_config._doctest_mode() + True + """ + from sage.doctest import DOCTEST_MODE + return DOCTEST_MODE + + def _allow_ansi(self): + """ + Whether to allow ANSI escape sequences + + This returns ``False`` during doctests to avoid ANSI escape + sequences. + + EXAMPLES:: + + sage: from sage.repl.configuration import sage_ipython_config + sage: sage_ipython_config._allow_ansi() + False + """ + return (not self._doctest_mode()) and sys.stdout.isatty() + + def colors(self): + """ + Return the IPython color palette + + This returns ``'NoColor'`` during doctests to avoid ANSI escape + sequences. + + EXAMPLES:: + + sage: from sage.repl.configuration import sage_ipython_config + sage: sage_ipython_config.simple_prompt() + True + """ + return 'LightBG' if self._allow_ansi() else 'NoColor' + + def simple_prompt(self): + """ + Return whether to use the simple prompt + + This returns ``True`` during doctests to avoid ANSI escape sequences. + + EXAMPLES:: + + sage: from sage.repl.configuration import sage_ipython_config + sage: sage_ipython_config.simple_prompt() + True + """ + return not self._allow_ansi() + + def term_title(self): + """ + Return whether to set the terminal title + + This returns false during doctests to avoid ANSI escape sequences. + + EXAMPLES:: + + sage: from sage.repl.configuration import sage_ipython_config + sage: sage_ipython_config.term_title() + False + """ + return self._allow_ansi() + + def default(self): + """ + Return a new default configuration object + + EXAMPLES:: + + sage: from sage.repl.configuration import sage_ipython_config + sage: sage_ipython_config.default() + {'InteractiveShell': {'colors': ... + """ + from sage.repl.interpreter import SageTerminalInteractiveShell + cfg = Config( + TerminalIPythonApp=Config( + display_banner=False, + verbose_crash=True, + test_shell=False, + shell_class=SageTerminalInteractiveShell, + ), + InteractiveShell=Config( + prompts_class=SagePrompts, + ast_node_interactivity='all', + colors=self.colors(), + simple_prompt=self.simple_prompt(), + term_title=self.term_title(), + confirm_exit=False, + separate_in='' + ), + InteractiveShellApp=Config(extensions=[SAGE_EXTENSION]), + ) + if self._doctest_mode(): + # Using the file-backed history causes problems in parallel tests + cfg.HistoryManager = Config(hist_file=':memory:') + return cfg + + def copy(self): + """ + Return a copy of the current configuration + + EXAMPLES:: + + sage: from sage.repl.configuration import sage_ipython_config + sage: sage_ipython_config.copy() + {'InteractiveShell': {'colors': ... + """ + try: + return copy.deepcopy(get_ipython().config) + except NameError: + return self.default() + + +sage_ipython_config = SageIpythonConfiguration() diff --git a/src/sage/repl/interpreter.py b/src/sage/repl/interpreter.py index e0499c7..1f4eda7 100644 --- a/src/sage/repl/interpreter.py +++ b/src/sage/repl/interpreter.py @@ -102,18 +102,15 @@ Check that Cython source code appears in tracebacks:: #***************************************************************************** -import copy import os import re -import sys from sage.repl.preparse import preparse +from sage.repl.prompts import SagePrompts, InterfacePrompts -from traitlets.config.loader import Config from traitlets import Bool, Type from sage.env import SAGE_LOCAL - -SAGE_EXTENSION = 'sage' +from sage.repl.configuration import sage_ipython_config, SAGE_EXTENSION def embedded(): """ @@ -370,31 +367,7 @@ class SageTestShell(SageShellOverride, TerminalInteractiveShell): rc = super(SageTestShell, self).run_cell(*args, **kwds) -################################################################### -# Default configuration -################################################################### - -DEFAULT_SAGE_CONFIG = Config( - PromptManager = Config( - in_template = 'sage: ', - in2_template = '....: ', - justify = False, - out_template = ''), - TerminalIPythonApp = Config( - display_banner = False, - verbose_crash = True, - test_shell = False, - shell_class = SageTerminalInteractiveShell, - ), - InteractiveShell = Config( - ast_node_interactivity = 'all', - colors = 'LightBG' if sys.stdout.isatty() else 'NoColor', - confirm_exit = False, - separate_in = ''), - InteractiveShellApp = Config(extensions=[SAGE_EXTENSION]), -) - - + ################################################################### # Transformers used in the SageInputSplitter ################################################################### @@ -614,19 +587,17 @@ def interface_shell_embed(interface): sage: shell = interface_shell_embed(gap) sage: shell.run_cell('List( [1..10], IsPrime )') [ false, true, true, false, true, false, true, false, false, false ] - - """ - try: - cfg = copy.deepcopy(get_ipython().config) - except NameError: - cfg = copy.deepcopy(DEFAULT_SAGE_CONFIG) - cfg.PromptManager['in_template'] = interface.name() + ': ' - cfg.PromptManager['in2_template'] = len(interface.name())*'.' + ': ' + ) failed: + AttributeError: type object 'ExecutionResult' has no attribute '__qualname__'> + Note that the repr error is https://github.com/ipython/ipython/issues/9756 + """ + cfg = sage_ipython_config.copy() ipshell = InteractiveShellEmbed(config=cfg, banner1='\n --> Switching to %s <--\n\n'%interface, - exit_msg = '\n --> Exiting back to Sage <--\n') + exit_msg='\n --> Exiting back to Sage <--\n') ipshell.interface = interface + ipshell.prompts = InterfacePrompts(interface.name()) while ipshell.prefilter_manager.transformers: ipshell.prefilter_manager.transformers.pop() @@ -669,7 +640,7 @@ def get_test_shell(): sage: out + err '' """ - config = copy.deepcopy(DEFAULT_SAGE_CONFIG) + config = sage_ipython_config.default() config.TerminalIPythonApp.test_shell = True config.TerminalIPythonApp.shell_class = SageTestShell app = SageTerminalApp.instance(config=config) @@ -748,12 +719,9 @@ class SageTerminalApp(TerminalIPythonApp): sage: os.environ['IPYTHONDIR'] = IPYTHONDIR """ super(SageTerminalApp, self).load_config_file(*args, **kwds) - - newconfig = copy.deepcopy(DEFAULT_SAGE_CONFIG) - + newconfig = sage_ipython_config.default() # merge in the config loaded from file newconfig.merge(self.config) - self.config = newconfig def init_shell(self): @@ -767,7 +735,7 @@ class SageTerminalApp(TerminalIPythonApp): EXAMPLES:: - sage: from sage.repl.interpreter import SageTerminalApp, DEFAULT_SAGE_CONFIG + sage: from sage.repl.interpreter import SageTerminalApp sage: app = SageTerminalApp.instance() sage: app.shell @@ -776,7 +744,6 @@ class SageTerminalApp(TerminalIPythonApp): self.shell = self.shell_class.instance( parent=self, config=self.config, - display_banner=False, profile_dir=self.profile_dir, ipython_dir=self.ipython_dir) self.shell.configurables.append(self) diff --git a/src/sage/repl/ipython_tests.py b/src/sage/repl/ipython_tests.py index 0fa568d..2bb34cd 100644 --- a/src/sage/repl/ipython_tests.py +++ b/src/sage/repl/ipython_tests.py @@ -100,6 +100,12 @@ Next, test the pinfo2 magic for Cython code:: ... File: .../sage/tests/stl_vector.pyx Type: type + +Test that there are no warnings being ignored internally:: + + sage: import warnings + sage: warnings.simplefilter('error'); get_test_shell() + ''' diff --git a/src/sage/repl/prompts.py b/src/sage/repl/prompts.py new file mode 100644 index 0000000..e885730 --- /dev/null +++ b/src/sage/repl/prompts.py @@ -0,0 +1,92 @@ +r""" +Sage Commandline Prompts +""" + +#***************************************************************************** +# Copyright (C) 2016 Volker Braun +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from pygments.token import Token +from IPython.terminal.prompts import Prompts + + +class SagePrompts(Prompts): + + def in_prompt_tokens(self, cli=None): + return [ + (Token.Prompt, 'sage: '), + ] + + def continuation_prompt_tokens(self, cli=None, width=None): + return [ + (Token.Prompt, '....: '), + ] + + def rewrite_prompt_tokens(self): + return [ + (Token.Prompt, '----> '), + ] + + def out_prompt_tokens(self): + return [ + (Token.OutPrompt, ''), + ] + + +class InterfacePrompts(Prompts): + + def __init__(self, interface_name): + self.__name = interface_name + self.__width = len(interface_name) + + def in_prompt_tokens(self, cli=None): + return [ + (Token.Prompt, self.__name + ': '), + ] + + def continuation_prompt_tokens(self, cli=None, width=None): + return [ + (Token.Prompt, '.' * self.__width + ': '), + ] + + def rewrite_prompt_tokens(self): + return [ + (Token.Prompt, '-' * self.__width + '> '), + ] + + def out_prompt_tokens(self): + return [ + (Token.OutPrompt, ''), + ] + + +class DebugPrompts(Prompts): + + def in_prompt_tokens(self, cli=None): + return [ + (Token.Prompt, 'debug: '), + ] + + def continuation_prompt_tokens(self, cli=None, width=None): + return [ + (Token.Prompt, '.....: '), + ] + + def rewrite_prompt_tokens(self): + return [ + (Token.Prompt, '-----> '), + ] + + def out_prompt_tokens(self): + return [ + (Token.OutPrompt, ''), + ] + + + diff --git a/src/sage/tests/cmdline.py b/src/sage/tests/cmdline.py index 0d15d82..417ec5e 100644 --- a/src/sage/tests/cmdline.py +++ b/src/sage/tests/cmdline.py @@ -385,7 +385,7 @@ def test_executable(args, input="", timeout=100.0, **kwds): ********************************************************************** Previously executed commands: s...: assert True == False - debug: + In [1]: Returning to doctests... **********************************************************************