"from __future__ import print_function" fails on Sage scripts

Hello, Sage Community.

I am trying to use the "print" function in a .sage script, so I have added the line

from __future__ import print_function


as the first line to be included in my .sage file. Unfortunately, when running sage test.sage, Sage preparses the document and creates an auxiliary file "test.sage.py", which makes an import, then predefines some constants, and finally adds my preparsed code. As a consequence, the from __future__ import print_function is not the first line, and I get the following error message:

File "test.sage.py", line 6
from __future__ import print_function
SyntaxError: from __future__ imports must occur at the beginning of the file


Here is a MWE. The file "test.sage" containing:

from __future__ import print_function
print(1+1)


is preparsed to "test.sage.py" containing:

# This file was *autogenerated* from the file test.sage
from sage.all_cmdline import *   # import sage library

_sage_const_1 = Integer(1)
from __future__ import print_function

print(_sage_const_1 +_sage_const_1 )


Of course, I could manually add this line to the .sage.py file and then execute it, but this could be tedious in my case for two reasons: 1. I have a lot of files which I have to modify and rerun every 15 minutes. 2. I also need this process to be automatic to be able to execute it with automatically generated script and even sageTeX.

edit retag close merge delete

1

Just use .py files. Preparsing is not really useful once you are used to sage.

( 2019-04-24 19:42:26 +0200 )edit

The problem is that Sage executes .py files as purely Python files, so commands like plot are not recognized. Of course I can use Sage as a library an import it into the .py file, but unfortunately, the software I am working with require to first build .sage files.

( 2019-04-24 21:16:32 +0200 )edit

Then just import what you need. The command "import_statements" is very useful to find the required imports.

( 2019-04-24 22:18:42 +0200 )edit
1

This seems like something that could be fixed in the .sage file preparser.

( 2019-04-24 22:51:28 +0200 )edit

Do you need to include the line from __future__ import print_function? print(x) should work fine without it.

( 2019-04-24 22:55:44 +0200 )edit

Sort by » oldest newest most voted

Here is a patch posted to https://trac.sagemath.org/ticket/27719 (plus I added a doctest there, too):

diff --git a/src/bin/sage-preparse b/src/bin/sage-preparse
index 2d87e73dca..5103fe657b 100755
--- a/src/bin/sage-preparse
+++ b/src/bin/sage-preparse
@@ -61,6 +61,7 @@ AUTOGEN_MSG = "# This file was *autogenerated* from the file "
# We want to save the leading white space so that we can maintain
# correct indentation in the preparsed file.
load_or_attach = re.compile(r"^(?P<lws>\s*)(load|attach)\s+(?P<files>.*)$") +future_imports = re.compile("^\s*(from __future__ import .*)$")

def do_preparse(f, files_before=[]):
"""
@@ -126,6 +127,12 @@ def do_preparse(f, files_before=[]):
# Preparse the body
body = preparse_file(body)

+    # Check for "from __future__ import ..." statements. Those
+    # statements need to come at the top of the file (after the
+    # module-level docstring is okay), so we separate them from the
+    # body.
+    future_imports, body = find_future_imports(body)
+

@@ -138,6 +145,8 @@ def do_preparse(f, files_before=[]):
f.write(coding)
f.write('\n')
+        f.write(future_imports)
+        f.write('\n')
f.write(sage_incl)
f.write('\n')
f.write(body)
@@ -196,6 +205,26 @@ def find_position_right_after_module_docstring(G):
return pos_after_line(i)

+def find_future_imports(G):
+    """
+    Parse a file G, looking for "from __future import ...".
+
+    Return a tuple: (the import statements, the file G with those
+    statements removed)
+
+    INPUT:
+        G -- a string; a file loaded in from disk
+    """
+    import_statements = ''
+    new_G = ''
+    for t in G.split('\n'):
+        z = future_imports.match(t)
+        if z:
+            import_statements += z.group(1) + '\n'
+        else:
+            new_G += t + '\n'
+    return (import_statements, new_G)
+

"""

more

Thank you very much. I really have to learn how to make commits.

I have one observation, though: The first line of the docstring for find_future_imports says "__future" instead of "__future__"

( 2019-04-25 06:11:22 +0200 )edit

Thank you, fixed.

( 2019-04-25 16:39:00 +0200 )edit