diff --git a/src/sage/cli/__init__.py b/src/sage/cli/__init__.py index 8f0cac437ef..a8755673fbf 100644 --- a/src/sage/cli/__init__.py +++ b/src/sage/cli/__init__.py @@ -9,6 +9,7 @@ from sage.cli.notebook_cmd import JupyterNotebookCmd from sage.cli.options import CliOptions from sage.cli.version_cmd import VersionCmd +from sage.cli.run_file_cmd import RunFileCmd def main() -> int: @@ -41,6 +42,7 @@ def main() -> int: VersionCmd.extend_parser(parser) JupyterNotebookCmd.extend_parser(parser) EvalCmd.extend_parser(parser) + RunFileCmd.extend_parser(parser) if not input_args: return InteractiveShellCmd(CliOptions()).run() @@ -50,7 +52,9 @@ def main() -> int: logging.basicConfig(level=logging.DEBUG if options.verbose else logging.INFO) - if args.command: + if args.file: + return RunFileCmd(options).run() + elif args.command: return EvalCmd(options).run() elif args.notebook: return JupyterNotebookCmd(options).run() diff --git a/src/sage/cli/options.py b/src/sage/cli/options.py index 36c210915f2..70cd2b99eef 100644 --- a/src/sage/cli/options.py +++ b/src/sage/cli/options.py @@ -21,3 +21,6 @@ class CliOptions: """The command to execute.""" command: str | None = None + + """The file to execute.""" + file: str | None = None diff --git a/src/sage/cli/run_file_cmd.py b/src/sage/cli/run_file_cmd.py new file mode 100644 index 00000000000..91348979f31 --- /dev/null +++ b/src/sage/cli/run_file_cmd.py @@ -0,0 +1,50 @@ +import argparse + +from sage.cli.options import CliOptions +from sage.repl.preparse import preparse_file_named +from sage.repl.load import load_cython +from sage.misc.temporary_file import tmp_filename +from sage.all import sage_globals + + +class RunFileCmd: + @staticmethod + def extend_parser(parser: argparse.ArgumentParser): + r""" + Extend the parser with the "run file" command. + + INPUT: + + - ``parsers`` -- the parsers to extend. + + OUTPUT: + + - the extended parser. + """ + parser.add_argument( + "file", + nargs="?", + help="execute the given file as sage code", + ) + + def __init__(self, options: CliOptions): + r""" + Initialize the command. + """ + self.options = options + + def run(self) -> int: + r""" + Execute the given command. + """ + input_file = preparse_file_named(self.options.file) if self.options.file.endswith('.sage') else self.options.file + try: + if self.options.file.endswith('.pyx') or self.options.file.endswith('.spyx'): + s = load_cython(input_file) + eval(compile(s, tmp_filename(), 'exec'), sage_globals()) + else: + eval(compile(open(input_file, 'rb').read(), input_file, 'exec'), sage_globals()) + except Exception as e: + print(f"An error occurred while executing the file: {e}") + return 1 + return 0