Here's a rough sketch of a sage_import
that works similarly to the Python import statement on .sage
files, including searching sys.path
for the file (which by default includes your current working directory), but this part is optional I suppose.
# sage_import.py
import imp
import inspect
import os
import sys
import sage.all
def sage_import(modname, fromlist=None, namespace=None):
"""
Import a .sage module from the filename <modname>.sage
Returns the resulting Python module. If ``fromlist`` is given, returns
just those members of the module into the global namespace where the
function was called, or the given namespace.
"""
filename = modname + '.sage'
for path in sys.path:
modpath = os.path.join(path, filename)
if os.path.isfile(modpath):
break
else:
raise ImportError('no file {} on sys.path'.format(filename))
with open(modpath) as fobj:
code = sage.all.preparse(fobj.read())
mod = imp.new_module(modname)
mod.__file__ = modpath
# Fill with all the default Sage globals
# We could just do a dict.update but we want to exclude dunder
# and private attributes I guess
for k, v in sage.all.__dict__.items():
if not k.startswith('_'):
mod.__dict__[k] = v
exec code in mod.__dict__
if namespace is None:
namespace = inspect.currentframe().f_back.f_globals
if fromlist is not None:
# First check that each name in fromlist exists before adding
# any of them to the given namespace.
for name in fromlist:
if name not in mod.__dict__:
raise ImportError('cannot import name {!r} from {}'.format(
name, filename))
for name in fromlist:
namespace[name] = mod.__dict__[name]
else:
namespace[modname] = mod
Given a file in my current directory named a.sage
containing:
# a.sage
a = 1 / 2
I can use this either in Sage or in a standard Python interpreter like
>>> from sage_import import sage_import
>>> sage_import('a', fromlist=['a'])
>>> a
1/2
>>> type(a)
<type 'sage.rings.rational.Rational'>
This version works for Python 2. It can be adapted to Python 3 with some small tweaks.
There is an open issue to build this functionality directly into Sage so that you can use the standard import
statement to import .sage modules. This can be done but it requires a bit of care. I think a prototype was started at one point but it still hasn't been submitted.
Maybe you can post your hack as an edit to your post?