def SagePreparseTransformer(line): r""" EXAMPLES:: sage: from sage.repl.interpreter import SagePreparseTransformer sage: spt = SagePreparseTransformer() sage: spt.push('1+1r+2.3^2.3r') "Integer(1)+1+RealNumber('2.3')**2.3" sage: preparser(False) sage: spt.push('2.3^2') '2.3^2' TESTS: Check that syntax errors in the preparser do not crash IPython, see :trac:`14961`. :: sage: preparser(True) sage: bad_syntax = "R.<t> = QQ{]" sage: preparse(bad_syntax) Traceback (most recent call last): ... SyntaxError: Mismatched ']' sage: from sage.repl.interpreter import get_test_shell sage: shell = get_test_shell() sage: shell.run_cell(bad_syntax) File "<string>", line unknown SyntaxError: Mismatched ']' <BLANKLINE> sage: shell.quit() """ if _do_preparse and not line.startswith('%'): return preparse(line) else: return line
def crun(s, evaluator): """ Profile single statement. - ``s`` -- string. Sage code to profile. - ``evaluator`` -- callable to evaluate. EXAMPLES:: sage: import sage.misc.gperftools as gperf sage: ev = lambda ex:eval(ex, globals(), locals()) sage: gperf.crun('gperf.run_100ms()', evaluator=ev) # optional - gperftools PROFILE: interrupts/evictions/bytes = ... Using local file ... Using local file ... """ prof = Profiler() from sage.repl.preparse import preparse py_s = preparse(s) prof.start() try: evaluator(py_s) finally: prof.stop() prof.top()
def completions(s, globs, format=False, width=90, system="None"): """ Return a list of completions in the given context. INPUT: - ``globs`` - a string:object dictionary; context in which to search for completions, e.g., :func:`globals()` - ``format`` - a bool (default: False); whether to tabulate the list - ``width`` - an int; character width of the table - ``system`` - a string (default: 'None'); system prefix for the completions OUTPUT: - a list of strings, if ``format`` is False, or a string """ if system not in ['sage', 'python']: prepend = system + '.' s = prepend + s else: prepend = '' n = len(s) if n == 0: return '(empty string)' try: if not '.' in s and not '(' in s: v = [x for x in globs.keys() if x[:n] == s] + \ [x for x in __builtins__.keys() if x[:n] == s] else: if not ')' in s: i = s.rfind('.') method = s[i+1:] obj = s[:i] n = len(method) else: obj = preparse(s) method = '' try: O = eval(obj, globs) D = dir(O) try: D += O.trait_names() except (AttributeError, TypeError): pass if method == '': v = [obj + '.'+x for x in D if x and x[0] != '_'] else: v = [obj + '.'+x for x in D if x[:n] == method] except Exception, msg: v = [] v = list(set(v)) # make unique v.sort()
def preparse_imports_from_sage(self, line): """ Finds occurrences of strings such as ``sage(object)`` in *line*, converts ``object`` to :attr:`shell.interface`, and replaces those strings with their identifier in the new system. This also works with strings such as ``maxima(object)`` if :attr:`shell.interface` is ``maxima``. :param line: the line to transform :type line: string EXAMPLES:: sage: from sage.repl.interpreter import interface_shell_embed, InterfaceShellTransformer sage: shell = interface_shell_embed(maxima) sage: ift = InterfaceShellTransformer(shell=shell, config=shell.config, prefilter_manager=shell.prefilter_manager) sage: ift.shell.ex('a = 3') sage: ift.preparse_imports_from_sage('2 + sage(a)') '2 + sage0 ' sage: maxima.eval('sage0') '3' sage: ift.preparse_imports_from_sage('2 + maxima(a)') # maxima calls set_seed on startup which is why 'sage0' will becomes 'sage4' and not just 'sage1' '2 + sage4 ' sage: ift.preparse_imports_from_sage('2 + gap(a)') '2 + gap(a)' Since :trac:`28439`, this also works with more complicated expressions containing nested parentheses:: sage: shell = interface_shell_embed(gap) sage: shell.user_ns = locals() sage: ift = InterfaceShellTransformer(shell=shell, config=shell.config, prefilter_manager=shell.prefilter_manager) sage: line = '2 + sage((1+2)*gap(-(5-3)^2).sage()) - gap(1+(2-1))' sage: line = ift.preparse_imports_from_sage(line) sage: gap.eval(line) '-12' """ new_line = [] pos = 0 while True: m = self._sage_import_re.search(line, pos) if not m: new_line.append(line[pos:]) break expr_start, expr_end = containing_block(line, m.end() - 1, delimiters=['()']) expr = preparse(line[expr_start + 1:expr_end - 1]) result = self.shell.interface(eval(expr, self.shell.user_ns)) self.temporary_objects.add(result) new_line += [line[pos:m.start()], result.name()] pos = expr_end return ' '.join(new_line)
def runsnake(command): """ Graphical profiling with ``runsnake`` INPUT: - ``command`` -- the command to be run as a string. EXAMPLES:: sage: runsnake("list(SymmetricGroup(3))") # optional - runsnake ``command`` is first preparsed (see :func:`preparse`):: sage: runsnake('for x in range(1,4): print(x^2)') # optional - runsnake 1 4 9 :func:`runsnake` requires the program ``runsnake``. Due to non trivial dependencies (python-wxgtk, ...), installing it within the Sage distribution is unpractical. Hence, we recommend installing it with the system wide Python. On Ubuntu 10.10, this can be done with:: > sudo apt-get install python-profiler python-wxgtk2.8 python-setuptools > sudo easy_install RunSnakeRun See the ``runsnake`` website for instructions for other platforms. :func:`runsnake` further assumes that the system wide Python is installed in ``/usr/bin/python``. .. SEEALSO:: - `The runsnake website <http://www.vrplumber.com/programming/runsnakerun/>`_ - ``%prun`` - :class:`Profiler` """ import cProfile import os from sage.misc.temporary_file import tmp_filename from sage.misc.misc import get_main_globals from sage.repl.preparse import preparse tmpfile = tmp_filename() cProfile.runctx(preparse(command.lstrip().rstrip()), get_main_globals(), locals(), filename=tmpfile) os.system("/usr/bin/python -E `which runsnake` %s &" % tmpfile)
def SagePreparseTransformer(lines): r""" EXAMPLES:: sage: from sage.repl.interpreter import SagePreparseTransformer sage: spt = SagePreparseTransformer sage: spt(['1+1r+2.3^2.3r\n']) ["Integer(1)+1+RealNumber('2.3')**2.3\n"] sage: preparser(False) sage: spt(['2.3^2\n']) ['2.3^2\n'] TESTS: Check that syntax errors in the preparser do not crash IPython, see :trac:`14961`. :: sage: preparser(True) sage: bad_syntax = "R.<t> = QQ{]" sage: preparse(bad_syntax) Traceback (most recent call last): ... SyntaxError: Mismatched ']' sage: from sage.repl.interpreter import get_test_shell sage: shell = get_test_shell() sage: shell.run_cell(bad_syntax) File "<string>", line unknown SyntaxError: Mismatched ']' <BLANKLINE> sage: shell.quit() Make sure the quote state is carried over across subsequent lines in order to avoid interfering with multi-line strings, see :trac:`30417`. :: sage: SagePreparseTransformer(["'''\n", 'abc-1-2\n', "'''\n"]) ["'''\n", 'abc-1-2\n', "'''\n"] sage: # instead of ["'''\n", 'abc-Integer(1)-Integer(2)\n', "'''\n"] .. NOTE:: IPython may call this function more than once for the same input lines. So when debugging the preparser, print outs may be duplicated. If using IPython >= 7.17, try: ``sage.repl.interpreter.SagePreparseTransformer.has_side_effects = True`` """ if _do_preparse: # IPython ensures the input lines end with a newline, and it expects # the same of the output lines. lines = preparse(''.join(lines)).splitlines(keepends=True) return lines
def handle(self): print(f'Request from {self.client_address}') err_lvl = ErrLvl.OKAY err_msg = None result = None # self.request is the TCP socket connected to the client conn = DmlSocket(sock=self.request) try: msg = conn.recvall_str() except Exception as e: err_lvl = ErrLvl.RECV err_msg = str(e) err_msg += '\n' + traceback.format_exc() if err_lvl == ErrLvl.OKAY: try: parsed = preparse(msg) except Exception as e: err_lvl = ErrLvl.PREPARSE err_msg = str(e) err_msg += '\n' + "*" * 40 err_msg += '\n*** Received message: ***' err_msg += '\n' + "*" * 40 err_msg += '\n' + msg err_msg += '\n' + "*" * 40 err_msg += '\n\n' + traceback.format_exc() if err_lvl == ErrLvl.OKAY: # Redirect stdout to my stdout to capture into a string sys.stdout = mystdout = StringIO() try: eval(compile(parsed, '<cmdline>', 'exec')) result = mystdout.getvalue().strip( ) # Get result from mystdout except Exception as e: err_lvl = ErrLvl.EXECUTE err_msg = str(e) err_msg += '\n' + "*" * 40 err_msg += '\n*** Pre-parsed Sage Code: ***' err_msg += '\n' + "*" * 40 err_msg += '\n' + parsed err_msg += '\n' + "*" * 40 err_msg += '\n\n' + traceback.format_exc() # Restore stdout sys.stdout = sys.__stdout__ response = { 'err_lvl': err_lvl, 'err_msg': err_msg, 'result': result, } resp_j = json.dumps(response) conn.sendall_str(resp_j)
def SagePreparseTransformer(lines): r""" EXAMPLES:: sage: from sage.repl.interpreter import SagePreparseTransformer sage: spt = SagePreparseTransformer sage: spt(['1+1r+2.3^2.3r']) ["Integer(1)+1+RealNumber('2.3')**2.3"] sage: preparser(False) sage: spt(['2.3^2']) ['2.3^2'] TESTS: Check that syntax errors in the preparser do not crash IPython, see :trac:`14961`. :: sage: preparser(True) sage: bad_syntax = "R.<t> = QQ{]" sage: preparse(bad_syntax) Traceback (most recent call last): ... SyntaxError: Mismatched ']' sage: from sage.repl.interpreter import get_test_shell sage: shell = get_test_shell() sage: shell.run_cell(bad_syntax) File "<string>", line unknown SyntaxError: Mismatched ']' <BLANKLINE> sage: shell.quit() Make sure the quote state is carried over across subsequent lines in order to avoid interfering with multi-line strings, see :trac:`30417`. :: sage: SagePreparseTransformer(["'''", 'abc-1-2', "'''"]) ["'''", 'abc-1-2', "'''"] sage: # instead of ["'''", 'abc-Integer(1)-Integer(2)', "'''"] """ lines_out = [] reset = True for line in lines: if _do_preparse and not line.startswith('%'): lines_out += [preparse(line, reset=reset)] reset = False else: lines_out += [line] return lines_out
def runsnake(command): """ Graphical profiling with ``runsnake`` INPUT: - ``command`` -- the command to be run as a string. EXAMPLES:: sage: runsnake("list(SymmetricGroup(3))") # optional - runsnake ``command`` is first preparsed (see :func:`preparse`):: sage: runsnake('for x in range(1,4): print(x^2)') # optional - runsnake 1 4 9 :func:`runsnake` requires the program ``runsnake``. Due to non trivial dependencies (python-wxgtk, ...), installing it within the Sage distribution is unpractical. Hence, we recommend installing it with the system wide Python. On Ubuntu 10.10, this can be done with:: > sudo apt-get install python-profiler python-wxgtk2.8 python-setuptools > sudo easy_install RunSnakeRun See the ``runsnake`` website for instructions for other platforms. :func:`runsnake` further assumes that the system wide Python is installed in ``/usr/bin/python``. .. seealso:: - `The runsnake website <http://www.vrplumber.com/programming/runsnakerun/>`_ - ``%prun`` - :class:`Profiler` """ import cProfile import os from sage.misc.temporary_file import tmp_filename from sage.misc.misc import get_main_globals from sage.repl.preparse import preparse tmpfile = tmp_filename() cProfile.runctx(preparse(command.lstrip().rstrip()), get_main_globals(), locals(), filename=tmpfile) os.system("/usr/bin/python -E `which runsnake` %s &" % tmpfile)
def preparse_imports_from_sage(self, line): """ Finds occurrences of strings such as ``sage(object)`` in *line*, converts ``object`` to :attr:`shell.interface`, and replaces those strings with their identifier in the new system. This also works with strings such as ``maxima(object)`` if :attr:`shell.interface` is ``maxima``. :param line: the line to transform :type line: string .. warning:: This does not parse nested parentheses correctly. Thus, lines like ``sage(a.foo())`` will not work correctly. This can't be done in generality with regular expressions. EXAMPLES:: sage: from sage.repl.interpreter import interface_shell_embed, InterfaceShellTransformer sage: shell = interface_shell_embed(maxima) sage: ift = InterfaceShellTransformer(shell=shell, config=shell.config, prefilter_manager=shell.prefilter_manager) sage: ift.shell.ex('a = 3') sage: ift.preparse_imports_from_sage('2 + sage(a)') '2 + sage0 ' sage: maxima.eval('sage0') '3' sage: ift.preparse_imports_from_sage('2 + maxima(a)') '2 + sage1 ' sage: ift.preparse_imports_from_sage('2 + gap(a)') '2 + gap(a)' """ for sage_code in self._sage_import_re.findall(line): expr = preparse(sage_code) result = self.shell.interface(eval(expr, self.shell.user_ns)) self.temporary_objects.add(result) line = self._sage_import_re.sub(' ' + result.name() + ' ', line, 1) return line
def parse(self, string, *args): r""" A Sage specialization of :class:`doctest.DocTestParser`. INPUT: - ``string`` -- the string to parse. - ``name`` -- optional string giving the name identifying string, to be used in error messages. OUTPUT: - A list consisting of strings and :class:`doctest.Example` instances. There will be at least one string between successive examples (exactly one unless or long or optional tests are removed), and it will begin and end with a string. EXAMPLES:: sage: from sage.doctest.parsing import SageDocTestParser sage: DTP = SageDocTestParser(True, ('sage','magma','guava')) sage: example = 'Explanatory text::\n\n sage: E = magma("EllipticCurve([1, 1, 1, -10, -10])") # optional: magma\n\nLater text' sage: parsed = DTP.parse(example) sage: parsed[0] 'Explanatory text::\n\n' sage: parsed[1].sage_source 'E = magma("EllipticCurve([1, 1, 1, -10, -10])") # optional: magma\n' sage: parsed[2] '\nLater text' If the doctest parser is not created to accept a given optional argument, the corresponding examples will just be removed:: sage: DTP2 = SageDocTestParser(True, ('sage',)) sage: parsed2 = DTP2.parse(example) sage: parsed2 ['Explanatory text::\n\n', '\nLater text'] You can mark doctests as having a particular tolerance:: sage: example2 = 'sage: gamma(1.6) # tol 2.0e-11\n0.893515349287690' sage: ex = DTP.parse(example2)[1] sage: ex.sage_source 'gamma(1.6) # tol 2.0e-11\n' sage: ex.want u'0.893515349287690\n' sage: type(ex.want) <class 'sage.doctest.parsing.MarkedOutput'> sage: ex.want.tol 2.000000000000000000?e-11 You can use continuation lines:: sage: s = "sage: for i in range(4):\n....: print(i)\n....:\n" sage: ex = DTP2.parse(s)[1] sage: ex.source 'for i in range(Integer(4)):\n print(i)\n' Sage currently accepts backslashes as indicating that the end of the current line should be joined to the next line. This feature allows for breaking large integers over multiple lines but is not standard for Python doctesting. It's not guaranteed to persist, but works in Sage 5.5:: sage: n = 1234\ ....: 5678 sage: print(n) 12345678 sage: type(n) <type 'sage.rings.integer.Integer'> It also works without the line continuation:: sage: m = 8765\ 4321 sage: print(m) 87654321 """ # Hack for non-standard backslash line escapes accepted by the current # doctest system. m = backslash_replacer.search(string) while m is not None: next_prompt = find_sage_prompt.search(string,m.end()) g = m.groups() if next_prompt: future = string[m.end():next_prompt.start()] + '\n' + string[next_prompt.start():] else: future = string[m.end():] string = string[:m.start()] + g[0] + "sage:" + g[1] + future m = backslash_replacer.search(string,m.start()) string = find_sage_prompt.sub(r"\1>>> sage: ", string) string = find_sage_continuation.sub(r"\1...", string) res = doctest.DocTestParser.parse(self, string, *args) filtered = [] for item in res: if isinstance(item, doctest.Example): optional_tags = parse_optional_tags(item.source) if optional_tags: for tag in optional_tags: self.optionals[tag] += 1 if (('not implemented' in optional_tags) or ('not tested' in optional_tags)): continue if 'long time' in optional_tags: if self.long: optional_tags.remove('long time') else: continue if self.optional_tags is not True: extra = optional_tags - self.optional_tags # set difference if extra: if not('external' in self.optional_tags and available_software.issuperset(extra)): continue elif self.optional_only: self.optionals['sage'] += 1 continue item.want = parse_tolerance(item.source, item.want) if item.source.startswith("sage: "): item.sage_source = item.source[6:] if item.sage_source.lstrip().startswith('#'): continue item.source = preparse(item.sage_source) filtered.append(item) return filtered
def trace(code, preparse=True): r""" Evaluate Sage code using the interactive tracer and return the result. The string ``code`` must be a valid expression enclosed in quotes (no assignments - the result of the expression is returned). In the Sage notebook this just raises a NotImplementedException. INPUT: - ``code`` - str - ``preparse`` - bool (default: True); if True, run expression through the Sage preparser. REMARKS: This function is extremely powerful! For example, if you want to step through each line of execution of, e.g., ``factor(100)``, type :: sage: trace("factor(100)") # not tested then at the (Pdb) prompt type ``s`` (or ``step``), then press return over and over to step through every line of Python that is called in the course of the above computation. Type ``?`` at any time for help on how to use the debugger (e.g., ``l`` lists 11 lines around the current line; ``bt`` gives a back trace, etc.). Setting a break point: If you have some code in a file and would like to drop into the debugger at a given point, put the following code at that point in the file: ``import pdb; pdb.set_trace()`` For an article on how to use the Python debugger, see http://www.onlamp.com/pub/a/python/2005/09/01/debugger.html TESTS: The only real way to test this is via pexpect spawning a sage subprocess that uses IPython. :: sage: import pexpect sage: s = pexpect.spawn('sage') sage: _ = s.sendline("trace('print(factor(10))'); print(3+97)") sage: _ = s.sendline("s"); _ = s.sendline("c"); sage: _ = s.expect('100', timeout=90) Seeing the ipdb prompt and the 2 \* 5 in the output below is a strong indication that the trace command worked correctly. :: sage: print(s.before[s.before.find('--'):]) --... ipdb> c 2 * 5 We test what happens in notebook embedded mode:: sage: sage.plot.plot.EMBEDDED_MODE = True sage: trace('print(factor(10))') Traceback (most recent call last): ... NotImplementedError: the trace command is not implemented in the Sage notebook; you must use the command line. """ from sage.plot.plot import EMBEDDED_MODE if EMBEDDED_MODE: raise NotImplementedError("the trace command is not implemented in the Sage notebook; you must use the command line.") from IPython.core.debugger import Pdb pdb = Pdb() try: ipython = get_ipython() except NameError: raise NotImplementedError("the trace command can only be run from the Sage command-line") from sage.repl.preparse import preparse code = preparse(code) return pdb.run(code, ipython.user_ns)
def parse_p_adic(s): s = s.strip().replace(' ','') #Try to read p-adic number as: #<expression of rational number> + O(p^e) cQp = re.compile(r'^([\+\-\*/\^\d]*)\+O\((\d+)\^(\-?\d+)\)$') matchQp = cQp.match(s) if matchQp != None: #Given searchterm is a p-adic number: a, p, e = matchQp.groups() p = ZZ(p) e = ZZ(e) #print("a,p,e:",a,p,e) #print("preparse(a):",preparse(a)) A = eval(preparse(a)) #print("A:",A) prec = e + min(0,-A.valuation(p)) Q_p = Qp(p,prec=prec) result = Q_p(A).add_bigoh(e) return result #Try to read p-adic number as: #O(p^e) cQp = re.compile(r'^O\((\d+)\^(\-?\d+)\)$') matchQp = cQp.match(s) if matchQp != None: #Given searchterm is a p-adic number: p, e = matchQp.groups() p = ZZ(p) e = ZZ(e) prec = e Q_p = Qp(p,prec=prec) result = Q_p(0).add_bigoh(e) return result #Try to read p-adic number as: #Qp:digits cQp2 = re.compile(r'^[qQzZ](\d+)[: ](\-?)((?:\d*\.)?)(\d*)$') matchQp2 = cQp2.match(s) if matchQp2 != None: p, sign, digits0, digits1 = matchQp2.groups() lenp = ZZ(len(p)) p = ZZ(p) print("p,sign,digits0,digits1:",p,sign,digits0,digits1) print("lenp:",lenp) lend0 = ZZ(len(digits0)) lend1 = ZZ(len(digits1)) if lend0 % lenp == 0 and lend1 % lenp == 0: num_digits0 = ZZ(lend0/lenp) num_digits1 = ZZ(lend1/lenp) prec = num_digits0 + num_digits1 Q_p = Qp(p, prec = prec) result = Q_p(0) for i in range(num_digits0): print(i,ZZ(digits0[lenp*i:lenp*(i+1)])) result += Q_p(ZZ(digits0[lenp*i:lenp*(i+1)]) * p**(i-num_digits0)) for i in range(num_digits1): print(i,ZZ(digits1[lenp*i:lenp*(i+1)])) result += Q_p(ZZ(digits1[lenp*i:lenp*(i+1)]) * p**i) result = result.add_bigoh(num_digits1) if sign == '-': result = -result print("result:",result) return result return None
def parse(self, string, *args): r""" A Sage specialization of :class:`doctest.DocTestParser`. INPUT: - ``string`` -- the string to parse. - ``name`` -- optional string giving the name identifying string, to be used in error messages. OUTPUT: - A list consisting of strings and :class:`doctest.Example` instances. There will be at least one string between successive examples (exactly one unless or long or optional tests are removed), and it will begin and end with a string. EXAMPLES:: sage: from sage.doctest.parsing import SageDocTestParser sage: DTP = SageDocTestParser(('sage','magma','guava')) sage: example = 'Explanatory text::\n\n sage: E = magma("EllipticCurve([1, 1, 1, -10, -10])") # optional: magma\n\nLater text' sage: parsed = DTP.parse(example) sage: parsed[0] 'Explanatory text::\n\n' sage: parsed[1].sage_source 'E = magma("EllipticCurve([1, 1, 1, -10, -10])") # optional: magma\n' sage: parsed[2] '\nLater text' If the doctest parser is not created to accept a given optional argument, the corresponding examples will just be removed:: sage: DTP2 = SageDocTestParser(('sage',)) sage: parsed2 = DTP2.parse(example) sage: parsed2 ['Explanatory text::\n\n', '\nLater text'] You can mark doctests as having a particular tolerance:: sage: example2 = 'sage: gamma(1.6) # tol 2.0e-11\n0.893515349287690' sage: ex = DTP.parse(example2)[1] sage: ex.sage_source 'gamma(1.6) # tol 2.0e-11\n' sage: ex.want u'0.893515349287690\n' sage: type(ex.want) <class 'sage.doctest.parsing.MarkedOutput'> sage: ex.want.tol 2.000000000000000000...?e-11 You can use continuation lines:: sage: s = "sage: for i in range(4):\n....: print(i)\n....:\n" sage: ex = DTP2.parse(s)[1] sage: ex.source 'for i in range(Integer(4)):\n print(i)\n' Sage currently accepts backslashes as indicating that the end of the current line should be joined to the next line. This feature allows for breaking large integers over multiple lines but is not standard for Python doctesting. It's not guaranteed to persist, but works in Sage 5.5:: sage: n = 1234\ ....: 5678 sage: print(n) 12345678 sage: type(n) <type 'sage.rings.integer.Integer'> It also works without the line continuation:: sage: m = 8765\ 4321 sage: print(m) 87654321 Test that :trac:`26575` is resolved:: sage: example3 = 'sage: Zp(5,4,print_mode="digits")(5)\n...00010' sage: parsed3 = DTP.parse(example3) sage: dte = parsed3[1] sage: dte.sage_source 'Zp(5,4,print_mode="digits")(5)\n' sage: dte.want '...00010\n' """ # Hack for non-standard backslash line escapes accepted by the current # doctest system. m = backslash_replacer.search(string) while m is not None: next_prompt = find_sage_prompt.search(string, m.end()) g = m.groups() if next_prompt: future = string[m.end():next_prompt.start( )] + '\n' + string[next_prompt.start():] else: future = string[m.end():] string = string[:m.start()] + g[0] + "sage:" + g[1] + future m = backslash_replacer.search(string, m.start()) replace_ellipsis = not python_prompt.search(string) if replace_ellipsis: # There are no >>> prompts, so we can allow ... to begin the output # We do so by replacing ellipses with a special tag, then putting them back after parsing string = find_python_continuation.sub(r"\1" + ellipsis_tag + r"\2", string) string = find_sage_prompt.sub(r"\1>>> sage: ", string) string = find_sage_continuation.sub(r"\1...", string) res = doctest.DocTestParser.parse(self, string, *args) filtered = [] for item in res: if isinstance(item, doctest.Example): optional_tags = parse_optional_tags(item.source) if optional_tags: for tag in optional_tags: self.optionals[tag] += 1 if (('not implemented' in optional_tags) or ('not tested' in optional_tags)): continue if 'long time' in optional_tags: if self.long: optional_tags.remove('long time') else: continue if self.optional_tags is not True: extra = optional_tags - self.optional_tags # set difference if extra: if not ('external' in self.optional_tags and available_software.issuperset(extra)): continue elif self.optional_only: self.optionals['sage'] += 1 continue if replace_ellipsis: item.want = item.want.replace(ellipsis_tag, "...") if item.exc_msg is not None: item.exc_msg = item.exc_msg.replace( ellipsis_tag, "...") item.want = parse_tolerance(item.source, item.want) if item.source.startswith("sage: "): item.sage_source = item.source[6:] if item.sage_source.lstrip().startswith('#'): continue item.source = preparse(item.sage_source) filtered.append(item) return filtered
def trace(code, preparse=True): r""" Evaluate Sage code using the interactive tracer and return the result. The string ``code`` must be a valid expression enclosed in quotes (no assignments - the result of the expression is returned). In the Sage notebook this just raises a NotImplementedException. INPUT: - ``code`` - str - ``preparse`` - bool (default: True); if True, run expression through the Sage preparser. REMARKS: This function is extremely powerful! For example, if you want to step through each line of execution of, e.g., ``factor(100)``, type :: sage: trace("factor(100)") # not tested then at the (Pdb) prompt type ``s`` (or ``step``), then press return over and over to step through every line of Python that is called in the course of the above computation. Type ``?`` at any time for help on how to use the debugger (e.g., ``l`` lists 11 lines around the current line; ``bt`` gives a back trace, etc.). Setting a break point: If you have some code in a file and would like to drop into the debugger at a given point, put the following code at that point in the file: ``import pdb; pdb.set_trace()`` For an article on how to use the Python debugger, see http://www.onlamp.com/pub/a/python/2005/09/01/debugger.html TESTS: For tests we disable garbage collection, see :trac:`21258` :: sage: import gc sage: gc.disable() The only real way to test this is via pexpect spawning a sage subprocess that uses IPython:: 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) Seeing the ipdb prompt and the 2 \* 5 in the output below is a strong indication that the trace command worked correctly:: sage: print(s.before[s.before.find(b'--'):].decode()) --... ipdb> c 2 * 5 We test what happens in notebook embedded mode:: sage: sage.plot.plot.EMBEDDED_MODE = True sage: trace('print(factor(10))') Traceback (most recent call last): ... NotImplementedError: the trace command is not implemented in the Sage notebook; you must use the command line. Re-enable garbage collection:: sage: gc.enable() """ from sage.plot.plot import EMBEDDED_MODE if EMBEDDED_MODE: raise NotImplementedError( "the trace command is not implemented in the Sage notebook; you must use the command line." ) from IPython.core.debugger import Pdb pdb = Pdb() try: ipython = get_ipython() except NameError: raise NotImplementedError( "the trace command can only be run from the Sage command-line") from sage.repl.preparse import preparse code = preparse(code) return pdb.run(code, ipython.user_ns)
def sage_eval(source, locals=None, cmds='', preparse=True): r""" Obtain a Sage object from the input string by evaluating it using Sage. This means calling eval after preparsing and with globals equal to everything included in the scope of ``from sage.all import *``.). INPUT: - ``source`` - a string or object with a _sage_ method - ``locals`` - evaluate in namespace of sage.all plus the locals dictionary - ``cmds`` - string; sequence of commands to be run before source is evaluated. - ``preparse`` - (default: True) if True, preparse the string expression. EXAMPLES: This example illustrates that preparsing is applied. :: sage: eval('2^3') 1 sage: sage_eval('2^3') 8 However, preparsing can be turned off. :: sage: sage_eval('2^3', preparse=False) 1 Note that you can explicitly define variables and pass them as the second option:: sage: x = PolynomialRing(RationalField(),"x").gen() sage: sage_eval('x^2+1', locals={'x':x}) x^2 + 1 This example illustrates that evaluation occurs in the context of ``from sage.all import *``. Even though bernoulli has been redefined in the local scope, when calling ``sage_eval`` the default value meaning of bernoulli is used. Likewise for QQ below. :: sage: bernoulli = lambda x : x^2 sage: bernoulli(6) 36 sage: eval('bernoulli(6)') 36 sage: sage_eval('bernoulli(6)') 1/42 :: sage: QQ = lambda x : x^2 sage: QQ(2) 4 sage: sage_eval('QQ(2)') 2 sage: parent(sage_eval('QQ(2)')) Rational Field This example illustrates setting a variable for use in evaluation. :: sage: x = 5 sage: eval('4/3 + x', {'x': 25}) # py2 26 sage: eval('4//3 + x', {'x': 25}) # py3 26 sage: sage_eval('4/3 + x', locals={'x': 25}) 79/3 You can also specify a sequence of commands to be run before the expression is evaluated:: sage: sage_eval('p', cmds='K.<x> = QQ[]\np = x^2 + 1') x^2 + 1 If you give commands to execute and a dictionary of variables, then the dictionary will be modified by assignments in the commands:: sage: vars = {} sage: sage_eval('None', cmds='y = 3', locals=vars) sage: vars['y'], parent(vars['y']) (3, Integer Ring) You can also specify the object to evaluate as a tuple. A 2-tuple is assumed to be a pair of a command sequence and an expression; a 3-tuple is assumed to be a triple of a command sequence, an expression, and a dictionary holding local variables. (In this case, the given dictionary will not be modified by assignments in the commands.) :: sage: sage_eval(('f(x) = x^2', 'f(3)')) 9 sage: vars = {'rt2': sqrt(2.0)} sage: sage_eval(('rt2 += 1', 'rt2', vars)) 2.41421356237309 sage: vars['rt2'] 1.41421356237310 This example illustrates how ``sage_eval`` can be useful when evaluating the output of other computer algebra systems. :: sage: R.<x> = PolynomialRing(RationalField()) sage: gap.eval('R:=PolynomialRing(Rationals,["x"]);') 'Rationals[x]' sage: ff = gap.eval('x:=IndeterminatesOfPolynomialRing(R);; f:=x^2+1;'); ff 'x^2+1' sage: sage_eval(ff, locals={'x':x}) x^2 + 1 sage: eval(ff) Traceback (most recent call last): ... RuntimeError: Use ** for exponentiation, not '^', which means xor in Python, and has the wrong precedence. Here you can see eval simply will not work but ``sage_eval`` will. TESTS: We get a nice minimal error message for syntax errors, that still points to the location of the error (in the input string):: sage: sage_eval('RR(22/7]') Traceback (most recent call last): ... File "<string>", line 1 RR(Integer(22)/Integer(7)] ^ SyntaxError: unexpected EOF while parsing :: sage: sage_eval('None', cmds='$x = $y[3] # Does Perl syntax work?') Traceback (most recent call last): ... File "<string>", line 1 $x = $y[Integer(3)] # Does Perl syntax work? ^ SyntaxError: invalid syntax """ if isinstance(source, (list, tuple)): cmds = source[0] if len(source) > 2: locals = copy(source[2]) source = source[1] if not isinstance(source, six.string_types): raise TypeError("source must be a string.") if locals is None: locals = {} import sage.all if len(cmds): cmd_seq = cmds + '\n_sage_eval_returnval_ = ' + source if preparse: cmd_seq = preparser.preparse_file(cmd_seq) else: if preparse: source = preparser.preparse(source) if len(cmds): exec(cmd_seq, sage.all.__dict__, locals) return locals['_sage_eval_returnval_'] else: return eval(source, sage.all.__dict__, locals)
def sage_timeit(stmt, globals_dict=None, preparse=None, number=0, repeat=3, precision=3, seconds=False): """nodetex Accurately measure the wall time required to execute ``stmt``. INPUT: - ``stmt`` -- a text string. - ``globals_dict`` -- a dictionary or ``None`` (default). Evaluate ``stmt`` in the context of the globals dictionary. If not set, the current ``globals()`` dictionary is used. - ``preparse`` -- (default: use globals preparser default) if ``True`` preparse ``stmt`` using the Sage preparser. - ``number`` -- integer, (optional, default: 0), number of loops. - ``repeat`` -- integer, (optional, default: 3), number of repetition. - ``precision`` -- integer, (optional, default: 3), precision of output time. - ``seconds`` -- boolean (default: ``False``). Whether to just return time in seconds. OUTPUT: An instance of ``SageTimeitResult`` unless the optional parameter ``seconds=True`` is passed. In that case, the elapsed time in seconds is returned as a floating-point number. EXAMPLES:: sage: from sage.misc.sage_timeit import sage_timeit sage: sage_timeit('3^100000', globals(), preparse=True, number=50) # random output '50 loops, best of 3: 1.97 ms per loop' sage: sage_timeit('3^100000', globals(), preparse=False, number=50) # random output '50 loops, best of 3: 67.1 ns per loop' sage: a = 10 sage: sage_timeit('a^2', globals(), number=50) # random output '50 loops, best of 3: 4.26 us per loop' If you only want to see the timing and not have access to additional information, just use the ``timeit`` object:: sage: timeit('10^2', number=50) 50 loops, best of 3: ... per loop Using sage_timeit gives you more information though:: sage: s = sage_timeit('10^2', globals(), repeat=1000) sage: len(s.series) 1000 sage: mean(s.series) # random output 3.1298141479492283e-07 sage: min(s.series) # random output 2.9258728027343752e-07 sage: t = stats.TimeSeries(s.series) sage: t.scale(10^6).plot_histogram(bins=20,figsize=[12,6], ymax=2) Graphics object consisting of 20 graphics primitives The input expression can contain newlines (but doctests cannot, so we use ``os.linesep`` here):: sage: from sage.misc.sage_timeit import sage_timeit sage: from os import linesep as CR sage: # sage_timeit(r'a = 2\\nb=131\\nfactor(a^b-1)') sage: sage_timeit('a = 2' + CR + 'b=131' + CR + 'factor(a^b-1)', ....: globals(), number=10) 10 loops, best of 3: ... per loop Test to make sure that ``timeit`` behaves well with output:: sage: timeit("print 'Hi'", number=50) 50 loops, best of 3: ... per loop If you want a machine-readable output, use the ``seconds=True`` option:: sage: timeit("print 'Hi'", seconds=True) # random output 1.42555236816e-06 sage: t = timeit("print 'Hi'", seconds=True) sage: t #r random output 3.6010742187499999e-07 TESTS: Make sure that garbage collection is re-enabled after an exception occurs in timeit:: sage: def f(): raise ValueError sage: import gc sage: gc.isenabled() True sage: timeit("f()") Traceback (most recent call last): ... ValueError sage: gc.isenabled() True """ import time, math import timeit as timeit_ import sage.repl.interpreter as interpreter import sage.repl.preparse as preparser number=int(number) repeat=int(repeat) precision=int(precision) if preparse is None: preparse = interpreter._do_preparse if preparse: stmt = preparser.preparse(stmt) if stmt == "": return '' units = ["s", "ms", "\xc2\xb5s", "ns"] scaling = [1, 1e3, 1e6, 1e9] timer = timeit_.Timer() # this code has tight coupling to the inner workings of timeit.Timer, # but is there a better way to achieve that the code stmt has access # to the shell namespace? src = timeit_.template % {'stmt': timeit_.reindent(stmt, 8), 'setup': "pass", 'init' : ''} code = compile(src, "<magic-timeit>", "exec") ns = {} if not globals_dict: globals_dict = globals() exec(code, globals_dict, ns) timer.inner = ns["inner"] try: import sys f = sys.stdout sys.stdout = open('/dev/null', 'w') if number == 0: # determine number so that 0.2 <= total time < 2.0 number = 1 for i in range(1, 5): number *= 5 if timer.timeit(number) >= 0.2: break series = [s/number for s in timer.repeat(repeat, number)] best = min(series) finally: sys.stdout.close() sys.stdout = f import gc gc.enable() if seconds: return best if best > 0.0: order = min(-int(math.floor(math.log10(best)) // 3), 3) else: order = 3 stats = (number, repeat, precision, best * scaling[order], units[order]) return SageTimeitResult(stats,series=series)
def sage_timeit(stmt, globals_dict=None, preparse=None, number=0, repeat=3, precision=3, seconds=False): """nodetex Accurately measure the wall time required to execute ``stmt``. INPUT: - ``stmt`` -- a text string. - ``globals_dict`` -- a dictionary or ``None`` (default). Evaluate ``stmt`` in the context of the globals dictionary. If not set, the current ``globals()`` dictionary is used. - ``preparse`` -- (default: use globals preparser default) if ``True`` preparse ``stmt`` using the Sage preparser. - ``number`` -- integer, (optional, default: 0), number of loops. - ``repeat`` -- integer, (optional, default: 3), number of repetition. - ``precision`` -- integer, (optional, default: 3), precision of output time. - ``seconds`` -- boolean (default: ``False``). Whether to just return time in seconds. OUTPUT: An instance of ``SageTimeitResult`` unless the optional parameter ``seconds=True`` is passed. In that case, the elapsed time in seconds is returned as a floating-point number. EXAMPLES:: sage: from sage.misc.sage_timeit import sage_timeit sage: sage_timeit('3^100000', globals(), preparse=True, number=50) # random output '50 loops, best of 3: 1.97 ms per loop' sage: sage_timeit('3^100000', globals(), preparse=False, number=50) # random output '50 loops, best of 3: 67.1 ns per loop' sage: a = 10 sage: sage_timeit('a^2', globals(), number=50) # random output '50 loops, best of 3: 4.26 us per loop' If you only want to see the timing and not have access to additional information, just use the ``timeit`` object:: sage: timeit('10^2', number=50) 50 loops, best of 3: ... per loop Using sage_timeit gives you more information though:: sage: s = sage_timeit('10^2', globals(), repeat=1000) sage: len(s.series) 1000 sage: mean(s.series) # random output 3.1298141479492283e-07 sage: min(s.series) # random output 2.9258728027343752e-07 sage: t = stats.TimeSeries(s.series) sage: t.scale(10^6).plot_histogram(bins=20,figsize=[12,6], ymax=2) Graphics object consisting of 20 graphics primitives The input expression can contain newlines (but doctests cannot, so we use ``os.linesep`` here):: sage: from sage.misc.sage_timeit import sage_timeit sage: from os import linesep as CR sage: # sage_timeit(r'a = 2\\nb=131\\nfactor(a^b-1)') sage: sage_timeit('a = 2' + CR + 'b=131' + CR + 'factor(a^b-1)', ....: globals(), number=10) 10 loops, best of 3: ... per loop Test to make sure that ``timeit`` behaves well with output:: sage: timeit("print('Hi')", number=50) 50 loops, best of 3: ... per loop If you want a machine-readable output, use the ``seconds=True`` option:: sage: timeit("print('Hi')", seconds=True) # random output 1.42555236816e-06 sage: t = timeit("print('Hi')", seconds=True) sage: t #r random output 3.6010742187499999e-07 TESTS: Make sure that garbage collection is re-enabled after an exception occurs in timeit:: sage: def f(): raise ValueError sage: import gc sage: gc.isenabled() True sage: timeit("f()") Traceback (most recent call last): ... ValueError sage: gc.isenabled() True """ import time, math import timeit as timeit_ import sage.repl.interpreter as interpreter import sage.repl.preparse as preparser number = int(number) repeat = int(repeat) precision = int(precision) if preparse is None: preparse = interpreter._do_preparse if preparse: stmt = preparser.preparse(stmt) if stmt == "": return '' units = ["s", "ms", "\xc2\xb5s", "ns"] scaling = [1, 1e3, 1e6, 1e9] timer = timeit_.Timer() # this code has tight coupling to the inner workings of timeit.Timer, # but is there a better way to achieve that the code stmt has access # to the shell namespace? if six.PY2: src = timeit_.template % {'stmt': timeit_.reindent(stmt, 8), 'setup': "pass", 'init': ''} else: src = timeit_.template.format(stmt=timeit_.reindent(stmt, 8), setup="pass", init='') code = compile(src, "<magic-timeit>", "exec") ns = {} if not globals_dict: globals_dict = globals() exec(code, globals_dict, ns) timer.inner = ns["inner"] try: import sys f = sys.stdout sys.stdout = open('/dev/null', 'w') if number == 0: # determine number so that 0.2 <= total time < 2.0 number = 1 for i in range(1, 5): number *= 5 if timer.timeit(number) >= 0.2: break series = [s/number for s in timer.repeat(repeat, number)] best = min(series) finally: sys.stdout.close() sys.stdout = f import gc gc.enable() if seconds: return best if best > 0.0: order = min(-int(math.floor(math.log10(best)) // 3), 3) else: order = 3 stats = (number, repeat, precision, best * scaling[order], units[order]) return SageTimeitResult(stats,series=series)