Пример #1
0
    def push(self, line):
        """
        EXAMPLES::

            sage: from sage.misc.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: bad_syntax = "R.<t> = QQ{]"
            sage: preparse(bad_syntax)
            Traceback (most recent call last):
            ...
            SyntaxError: Mismatched ']'
            sage: from sage.misc.interpreter import get_test_shell
            sage: shell = get_test_shell()
            sage: shell.run_cell(bad_syntax)
            Traceback (most recent call last):
            ...
            SyntaxError: Mismatched ']'
        """
        if line is None:
            self.reset()
        elif do_preparse and not line.startswith('%'):
            return preparse(line)
        else:
            return line
Пример #2
0
def preparse_imports_from_sage(interface, line, locals={}):
    """
    The input line is being fed to the given interface.
    This function extracts any "sage(zzz)"'s, parses
    them, and replaces them by appropriate objects
    in the interface.   This is used for moving
    objects from Sage back into the interface.
    """
    import sage_eval
    i = line.find('%s('%interface_name)
    n = len(interface_name)
    if i == -1:
        i = line.find('sage(')
        n = 4
        if i == -1:
            return line
    j = i + line[i:].find(')')
    expr = line[i+n+1:j]
    expr = preparser.preparse(expr)
    s = 'import sage.misc.preparser_ipython; \
         sage.misc.preparser_ipython._v_ = sage.misc.preparser_ipython.interface(%s)'%expr
    #print s
    __IPYTHON__.runsource(s)
    #print _v_
    line = line[:i] + _v_.name() + line[j+2:]
    return line
Пример #3
0
    def __call__(self, line, line_number):
        """
        Transform ``line``.

        INPUT:

        - ``line`` -- string. The line to be transformed.

        OUTPUT:

        A string, the transformed line.

        EXAMPLES::

            sage: from sage.misc.interpreter import SagePreparseTransformer
            sage: spt = SagePreparseTransformer()
            sage: spt('2', 0)
            'Integer(2)'
            sage: preparser(False)
            sage: spt('2', 0)
            '2'
            sage: preparser(True)
        """
        if do_preparse and not line.startswith('%'):
            # we use preparse_file instead of just preparse because preparse_file
            # automatically prepends attached files
            return preparse(line, reset=(line_number==0))
        else:
            return line
Пример #4
0
    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.misc.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)'
        """
        from sage_eval import sage_eval
        for sage_code in self._sage_import_re.findall(line):
            expr = preparse(sage_code)
            result = self.shell.interface(sage_eval(expr, self.shell.user_ns))
            self.temporary_objects.append(result)
            line = self._sage_import_re.sub(' ' + result.name() + ' ', line, 1)
        return line
Пример #5
0
def preparse_ipython(line, reset=True):
    """
    TESTS:
        
        Make sure #10933 is fixed
        
    ::
    
        sage: sage.misc.preparser_ipython.preparse_ipython("def foo(str):\n   time gp(str)\n\nprint gp('1')")
        'def foo(str):\n   __time__=misc.cputime(); __wall__=misc.walltime(); gp(str); print "Time: CPU %.2f s, Wall: %.2f s"%(misc.cputime(__time__), misc.walltime(__wall__))\n\nprint gp(\'1\')'

    """
    global num_lines
    global q_lines
    
    L = line.lstrip()
    if L.startswith('%'):
        # This should be installed as an Ipython magic command,
        # but I don't know how yet...
        L = L[1:].strip()
        import sage.interfaces.all
        if L.lower() in sage.interfaces.all.interfaces:
            switch_interface(L.lower())
            return "''"
        else:
            # only preparse non-magic lines
            return line

        
    if interface is None:
    #We could remove do_time=True if #10933 get's fixed upstream
        return preparser.preparse(line, reset=reset, do_time=True) 

    if L.startswith('?') or L.endswith('?'):
        L = L.strip('?')
        interface.help(L)
        return ''

    line = preparse_imports_from_sage(interface, line)
    line = line.rstrip()
    ends_in_backslash = line.endswith('\\')
    if ends_in_backslash:
        line = line.rstrip('\\')
        num_lines += 1
    else:
        if interface_name in ['gap', 'magma', 'kash', 'singular']:
            if not line.endswith(';'):
                line += ';'
        elif interface_name == 'mathematica':
            line = 'InputForm[%s]'%line

    if ends_in_backslash:
        q_lines.append(line)
    else:
        if len(q_lines) > 0:
            line = ''.join(q_lines) + line
        q_lines = []
        # TODO: do sage substitutions here 
        #t = interface._eval_line(line)
        t = interface.eval(line)
        
    import sage.misc.interpreter
    if ends_in_backslash:

        sage.misc.interpreter.set_sage_prompt('.'*len(interface_name))
    
    else:

        sage.misc.interpreter.set_sage_prompt('%s'%interface_name)
        #print t
        #__IPYTHON__.output_hist[len(__IPYTHON__.input_hist_raw)] = t

    # TODO: this is a very lazy temporary bug fix.
    # Nobody uses this logging stuff anymore, anyways, because
    # of the Sage notebook.
    try:
        return """logstr(%r)"""%t
    except UnboundLocalError:
        return 'logstr("")'
Пример #6
0
def sage_timeit(stmt, globals_dict=None, preparse=None, number=0, repeat=3, precision=3, seconds=False):
    """
    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)

    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 timeit as timeit_, time, math, preparser, interpreter
    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"}
    code = compile(src, "<magic-timeit>", "exec")
    ns = {}
    if not globals_dict:
        globals_dict = globals()
    exec code in 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)
Пример #7
0
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})
        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, basestring):
        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 in sage.all.__dict__, locals
        return locals['_sage_eval_returnval_']
    else:
        return eval(source, sage.all.__dict__, locals)
Пример #8
0
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")

    import preparser
    code = preparser.preparse(code)
    return pdb.run(code, ipython.user_ns)
Пример #9
0
 def reset(self):
     preparse('', reset=True)
Пример #10
0
def preparse_ipython(line, reset=True):
    global num_lines
    global q_lines
    
    L = line.lstrip()
    if L.startswith('%'):
        # This should be installed as an Ipython magic command,
        # but I don't know how yet...
        L = L[1:].strip()
        import sage.interfaces.all
        if L.lower() in sage.interfaces.all.interfaces:
            switch_interface(L.lower())
            return "''"
        else:
            # only preparse non-magic lines
            return line

        
    if interface is None:
        return preparser.preparse(line, reset=reset)

    if L.startswith('?') or L.endswith('?'):
        L = L.strip('?')
        interface.help(L)
        return ''

    line = preparse_imports_from_sage(interface, line)
    line = line.rstrip()
    ends_in_backslash = line.endswith('\\')
    if ends_in_backslash:
        line = line.rstrip('\\')
        num_lines += 1
    else:
        if interface_name in ['gap', 'magma', 'kash', 'singular']:
            if not line.endswith(';'):
                line += ';'
            if magma_colon_equals and interface_name == 'magma':
                line = line.replace(':=','=').replace('=',':=')
        elif interface_name == 'mathematica':
            line = 'InputForm[%s]'%line

    if ends_in_backslash:
        q_lines.append(line)
    else:
        if len(q_lines) > 0:
            line = ''.join(q_lines) + line
        q_lines = []
        # TODO: do sage substitutions here 
        #t = interface._eval_line(line)
        t = interface.eval(line)
        
    import sage.misc.interpreter
    if ends_in_backslash:

        sage.misc.interpreter.set_sage_prompt('.'*len(interface_name))
    
    else:

        sage.misc.interpreter.set_sage_prompt('%s'%interface_name)
        #print t
        #__IPYTHON__.output_hist[len(__IPYTHON__.input_hist_raw)] = t

    # TODO: this is a very lazy temporary bug fix.
    # Nobody uses this logging stuff anymore, anyways, because
    # of the SAGE notebook.
    try:
        return """logstr(%r)"""%t
    except UnboundLocalError:
        return 'logstr("")'
Пример #11
0
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 debuger, 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')
    
    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."

    import IPython.Debugger 
    pdb = IPython.Debugger.Pdb()
    
    import IPython.ipapi 
    _ip = IPython.ipapi.get() 
        
    import preparser

    code = preparser.preparse(code)
    return pdb.run(code, _ip.user_ns)
Пример #12
0
def sage_timeit(stmt, globals, preparse=None, number = 0, repeat = 3, precision = 3):
    """
    INPUT:
        stmt -- a text string which may
        globals -- evaluate stmt in the context of the globals dictionary
        preparse -- (default: use global preparser default) if True preparse
                    stmt using the Sage preparser.
    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'

    It's usually better to use the timeit object, usually:
        sage: timeit('10^2', number=50)
        50 loops, best of 3: ... per loop

    The input expression can contain newlines:
        sage: from sage.misc.sage_timeit import sage_timeit
        sage: sage_timeit("a = 2\nb=131\nfactor(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


    Make sure that garbage collection is renabled after an exception
    occurs in timeit.

    TESTS:
        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

    """
    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"}
    code = compile(src, "<magic-timeit>", "exec")
    ns = {}
    exec code in globals, 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

        best = min(timer.repeat(repeat, number)) / number

    finally:
        sys.stdout.close()
        sys.stdout = f
        import gc
        gc.enable()

    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)