Skip to content

Commit d1e7e2c

Browse files
authored
Add notebook and eval cmd, various other improvements
1 parent ce641cc commit d1e7e2c

File tree

12 files changed

+192
-27
lines changed

12 files changed

+192
-27
lines changed

src/sage/cli/__init__.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
import logging
55
import sys
66

7+
from sage.cli.eval_cmd import EvalCmd
78
from sage.cli.interactive_shell_cmd import InteractiveShellCmd
9+
from sage.cli.notebook_cmd import JupyterNotebookCmd
810
from sage.cli.options import CliOptions
911
from sage.cli.version_cmd import VersionCmd
1012

@@ -24,17 +26,19 @@ def main() -> int:
2426
)
2527

2628
VersionCmd.extend_parser(parser)
29+
JupyterNotebookCmd.extend_parser(parser)
30+
EvalCmd.extend_parser(parser)
2731

2832
if not input_args:
29-
InteractiveShellCmd(CliOptions()).run()
33+
return InteractiveShellCmd(CliOptions()).run()
3034

3135
args = parser.parse_args(input_args)
3236
options = CliOptions(**vars(args))
3337

3438
logging.basicConfig(level=logging.DEBUG if options.verbose else logging.INFO)
3539

40+
if args.command:
41+
return EvalCmd(options).run()
42+
elif args.notebook:
43+
return JupyterNotebookCmd(options).run()
3644
return InteractiveShellCmd(options).run()
37-
38-
39-
if __name__ == "__main__":
40-
sys.exit(main())

src/sage/cli/__main__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import sys
2+
3+
from sage.cli import main
4+
5+
sys.exit(main())

src/sage/cli/eval_cmd.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import argparse
2+
3+
from sage.cli.options import CliOptions
4+
from sage.repl.preparse import preparse
5+
from sage.all import sage_globals
6+
7+
8+
class EvalCmd:
9+
@staticmethod
10+
def extend_parser(parser: argparse.ArgumentParser):
11+
r"""
12+
Extend the parser with the "run" command.
13+
14+
INPUT:
15+
16+
- ``parsers`` -- the parsers to extend.
17+
18+
OUTPUT:
19+
20+
- the extended parser.
21+
"""
22+
parser.add_argument(
23+
"-c",
24+
"--command",
25+
nargs="?",
26+
help="execute the given command as sage code",
27+
)
28+
29+
def __init__(self, options: CliOptions):
30+
r"""
31+
Initialize the command.
32+
"""
33+
self.options = options
34+
35+
def run(self) -> int:
36+
r"""
37+
Execute the given command.
38+
"""
39+
code = preparse(self.options.command)
40+
try:
41+
eval(compile(code, "<cmdline>", "exec"), sage_globals())
42+
except Exception as e:
43+
print(f"An error occurred while executing the command: {e}")
44+
return 1
45+
return 0

src/sage/cli/eval_cmd_test.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from sage.cli.eval_cmd import EvalCmd
2+
from sage.cli.options import CliOptions
3+
4+
5+
def test_eval_cmd_print(capsys):
6+
options = CliOptions(command="print(3^33)")
7+
eval_cmd = EvalCmd(options)
8+
9+
result = eval_cmd.run()
10+
captured = capsys.readouterr()
11+
assert captured.out == "5559060566555523\n"
12+
assert result == 0
13+
14+
def test_eval_cmd_invalid_command(capsys):
15+
options = CliOptions(command="invalid_command")
16+
eval_cmd = EvalCmd(options)
17+
18+
result = eval_cmd.run()
19+
captured = capsys.readouterr()
20+
assert "An error occurred while executing the command: name 'invalid_command' is not defined" in captured.out
21+
assert result == 1

src/sage/cli/meson.build

Lines changed: 0 additions & 8 deletions
This file was deleted.

src/sage/cli/notebook_cmd.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import argparse
2+
3+
from sage.cli.options import CliOptions
4+
5+
6+
class JupyterNotebookCmd:
7+
@staticmethod
8+
def extend_parser(parser: argparse.ArgumentParser):
9+
r"""
10+
Extend the parser with the Jupyter notebook command.
11+
12+
INPUT:
13+
14+
- ``parsers`` -- the parsers to extend.
15+
16+
OUTPUT:
17+
18+
- the extended parser.
19+
"""
20+
parser.add_argument(
21+
"-n",
22+
"--notebook",
23+
nargs="?",
24+
const="jupyter",
25+
choices=["jupyter", "jupyterlab"],
26+
default="jupyter",
27+
help="start the Jupyter notebook server (default: jupyter)",
28+
)
29+
30+
def __init__(self, options: CliOptions):
31+
r"""
32+
Initialize the command.
33+
"""
34+
self.options = options
35+
36+
def run(self) -> int:
37+
r"""
38+
Start the Jupyter notebook server.
39+
"""
40+
if self.options.notebook == "jupyter":
41+
try:
42+
# notebook 6
43+
from notebook.notebookapp import main
44+
except ImportError:
45+
# notebook 7
46+
from notebook.app import main
47+
elif self.options.notebook == "jupyterlab":
48+
from jupyterlab.labapp import main
49+
else:
50+
raise ValueError(f"Unknown notebook type: {self.options.notebook}")
51+
52+
return main([])

src/sage/cli/notebook_cmd_test.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import argparse
2+
3+
import pytest
4+
5+
from sage.cli.notebook_cmd import JupyterNotebookCmd
6+
7+
8+
def test_jupyter_as_default():
9+
parser = argparse.ArgumentParser()
10+
JupyterNotebookCmd.extend_parser(parser)
11+
args = parser.parse_args(["--notebook"])
12+
assert args.notebook == "jupyter"
13+
14+
def test_jupyter_explicitly():
15+
parser = argparse.ArgumentParser()
16+
JupyterNotebookCmd.extend_parser(parser)
17+
args = parser.parse_args(["--notebook", "jupyter"])
18+
assert args.notebook == "jupyter"
19+
20+
def test_jupyterlab_explicitly():
21+
parser = argparse.ArgumentParser()
22+
JupyterNotebookCmd.extend_parser(parser)
23+
args = parser.parse_args(["--notebook", "jupyterlab"])
24+
assert args.notebook == "jupyterlab"
25+
26+
def test_invalid_notebook_choice():
27+
parser = argparse.ArgumentParser()
28+
JupyterNotebookCmd.extend_parser(parser)
29+
with pytest.raises(SystemExit):
30+
parser.parse_args(["--notebook", "invalid"])
31+
32+
def test_help():
33+
parser = argparse.ArgumentParser()
34+
JupyterNotebookCmd.extend_parser(parser)
35+
assert parser.format_usage() == "usage: pytest [-h] [-n [{jupyter,jupyterlab}]]\n"

src/sage/cli/options.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,13 @@
55
class CliOptions:
66
"""
77
A TypedDict for command-line interface options.
8-
9-
Attributes:
10-
verbose (bool): Indicates whether verbose output is enabled.
118
"""
129

10+
"""Indicates whether verbose output is enabled."""
1311
verbose: bool = False
12+
13+
"""The notebook type to start."""
14+
notebook: str = "jupyter"
15+
16+
"""The command to execute."""
17+
command: str | None = None

src/sage/meson.build

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,4 +142,4 @@ subdir('symbolic')
142142
subdir('tests')
143143
subdir('dynamics')
144144
subdir('sat')
145-
subdir('cli')
145+
install_subdir('cli', install_dir: sage_install_dir)

src/sage/misc/banner.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
# ****************************************************************************
1414
import sys
1515

16-
from sage.env import (SAGE_VERSION, SAGE_VERSION_BANNER, SAGE_BANNER)
16+
from sage.env import SAGE_BANNER, SAGE_VERSION
17+
from sage.version import banner as sage_banner
1718

1819

1920
def version():
@@ -25,12 +26,16 @@ def version():
2526
EXAMPLES::
2627
2728
sage: version()
29+
doctest:warning
30+
...
31+
DeprecationWarning: Use sage.version instead.
32+
...
2833
'SageMath version ..., Release Date: ...'
2934
"""
3035
from sage.misc.superseded import deprecation
3136

32-
deprecation(1, "Use sage.version instead.")
33-
return SAGE_VERSION_BANNER
37+
deprecation(39015, "Use sage.version instead.")
38+
return sage_banner
3439

3540

3641
def banner_text(full=True):
@@ -57,13 +62,13 @@ def banner_text(full=True):
5762
SageMath version ..., Release Date: ...
5863
"""
5964
if not full:
60-
return version()
65+
return sage_banner
6166

6267
bars = "─" * 68
6368
s = []
6469
a = s.append
6570
a('┌' + bars + '┐')
66-
a("\n│ %-66s │\n" % version())
71+
a("\n│ %-66s │\n" % sage_banner)
6772
python_version = sys.version_info[:3]
6873
a("│ %-66s │\n" % 'Using Python {}.{}.{}. Type "help()" for help.'.format(*python_version))
6974
a('└' + bars + '┘')

0 commit comments

Comments
 (0)