Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 0 additions & 17 deletions src/sage/doctest/forker.py
Original file line number Diff line number Diff line change
Expand Up @@ -2477,19 +2477,6 @@ class DocTestTask():
['cputime', 'err', 'failures', 'optionals', 'tests', 'walltime', 'walltime_skips']
"""

extra_globals = {}
"""
Extra objects to place in the global namespace in which tests are run.
Normally this should be empty but there are special cases where it may
be useful.

For example, in Sage versions 9.1 and earlier, on Python 3 add
``long`` as an alias for ``int`` so that tests that use the
``long`` built-in (of which there are many) still pass. We did
this so that the test suite could run on Python 3 while Python 2
was still the default.
"""

def __init__(self, source):
"""
Initialization.
Expand Down Expand Up @@ -2614,10 +2601,6 @@ def _run(self, runner, options, results):
# Remove '__package__' item from the globals since it is not
# always in the globals in an actual Sage session.
dict_all.pop('__package__', None)

# Add any other special globals for testing purposes only
dict_all.update(self.extra_globals)

sage_namespace = RecordingDict(dict_all)
sage_namespace['__name__'] = '__main__'
doctests, extras = self.source.create_doctests(sage_namespace)
Expand Down
66 changes: 40 additions & 26 deletions src/sage/misc/session.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ This saves a dictionary with ``w`` as one of the keys::

sage: z = load(os.path.join(d.name, 'session'))
sage: list(z)
['d', 'w']
['w', 'd']
sage: z['w']
2/3

Expand Down Expand Up @@ -68,19 +68,19 @@ AUTHOR:
import builtins
import types

# We want the caller's locals, but locals() is emulated in Cython
cdef caller_locals = builtins.locals

# Sage imports
from sage.misc.persist import load, save, loads, dumps
from sage.misc.lazy_import import LazyImport

# We want the caller's locals, but locals() is emulated in Cython
cdef caller_locals = builtins.locals

# This module-scope variables is used to save the
# global state of the sage environment at the moment
# before the user starts typing or running code.

state_at_init = None

CythonFunctionType = type(lambda: None)

def init(state=None):
"""
Expand Down Expand Up @@ -163,9 +163,13 @@ def _is_new_var(x, v, hidden):
# definitely new.
if x not in state_at_init:
return True
# A lazy import that was there at init time is not new
if isinstance(v, LazyImport):
return False
# A variable could also be new even if it was there at init, say if
# its value changed.
return x not in state_at_init or state_at_init[x] is not v
return state_at_init[x] is not v


def show_identifiers(hidden=False):
r"""
Expand Down Expand Up @@ -196,7 +200,7 @@ def show_identifiers(hidden=False):
sage: a = 10
sage: factor = 20
sage: show_identifiers()
['a', 'factor']
['factor', 'a']

To get the actual value of a variable from the list, use the
:func:`globals()` function.::
Expand All @@ -210,27 +214,21 @@ def show_identifiers(hidden=False):

sage: _hello = 10
sage: show_identifiers()
['a', 'factor']
['factor', 'a']
sage: '_hello' in show_identifiers(hidden=True)
True

Many of the hidden variables are part of the IPython command history, at
least in command line mode.::

sage: show_identifiers(hidden=True) # random output
['__', '_i', '_6', '_4', '_3', '_1', '_ii', '__doc__', '__builtins__', '___', '_9', '__name__', '_', 'a', '_i12', '_i14', 'factor', '__file__', '_hello', '_i13', '_i11', '_i10', '_i15', '_i5', '_13', '_10', '_iii', '_i9', '_i8', '_i7', '_i6', '_i4', '_i3', '_i2', '_i1', '_init_cmdline', '_14']
['__builtin__', '_ih', '_oh', '_dh', 'exit', 'quit', '_', '__', '___',
'_i', '_ii', '_iii', '_i1', 'factor', '_i2', '_2', '_i3', 'a', '_i4',
'_i5', '_5', '_i6', '_6', '_i7', '_hello', '_i8', '_8', '_i9', '_9',
'_i10']
"""
from sage.doctest.forker import DocTestTask
state = caller_locals()
# Ignore extra variables injected into the global namespace by the doctest
# runner
_none = object()

def _in_extra_globals(name, val):
return val == DocTestTask.extra_globals.get(name, _none)

return sorted([x for x, v in state.items() if _is_new_var(x, v, hidden)
and not _in_extra_globals(x, v)])
return [x for x, v in state.items() if _is_new_var(x, v, hidden)]


def save_session(name='sage_session', verbose=False):
Expand Down Expand Up @@ -293,28 +291,44 @@ def save_session(name='sage_session', verbose=False):
sage: f = lambda x : x^2
sage: save_session(tmp_f)
sage: save_session(tmp_f, verbose=True)
Saving...
Not saving f: f is a function, method, class or type
...
Not saving f: f is a function or method

Something similar happens for cython-defined functions::

sage: g = cython_lambda('double x', 'x*x + 1.5')
sage: save_session(tmp_f, verbose=True)
Saving...
Not saving g: g is a function, method, class or type
...
Not saving g: g is a cython function or method

And the same for a lazy import::

sage: from sage.misc.lazy_import import LazyImport
sage: lazy_ZZ = LazyImport('sage.rings.integer_ring', 'ZZ')
sage: save_session(tmp_f, verbose=True)
...
Not saving lazy_ZZ: lazy_ZZ is a lazy import
"""
state = caller_locals()
# This dict D will contain the session -- as a dict -- that we will save to disk.
D = {}
# We iterate only over the new variables that were defined in this
# session, since those are the only ones we will save.
for k in show_identifiers(hidden = True):
for k in show_identifiers(hidden=True):
try:
x = state[k]
if isinstance(x, (types.FunctionType, types.BuiltinFunctionType, types.BuiltinMethodType, CythonFunctionType, type)):
raise TypeError('{} is a function, method, class or type'.format(k))

if isinstance(x, type):
raise TypeError('{} is a class or type'.format(k))

if isinstance(x, (types.FunctionType, types.BuiltinFunctionType, types.BuiltinMethodType)):
raise TypeError('{} is a function or method'.format(k))

if getattr(type(x), '__name__', None) == 'cython_function_or_method':
raise TypeError('{} is a cython function or method'.format(k))

if isinstance(x, LazyImport):
raise TypeError('{} is a lazy import'.format(k))

# We attempt to pickle *and* unpickle every variable to
# make *certain* that we can pickled D at the end below.
Expand Down