Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow Cython compilation to open a web browser containing the annotated HTML file #38946

Merged
merged 12 commits into from
Jan 4, 2025
54 changes: 52 additions & 2 deletions src/sage/misc/cython.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
import re
import sys
import shutil
import webbrowser
from pathlib import Path

from sage.env import (SAGE_LOCAL, cython_aliases,
sage_include_directories)
Expand Down Expand Up @@ -78,9 +80,16 @@
sequence_number = {}


def _webbrowser_open_file(path):
"""
Open a html file in a web browser.
"""
webbrowser.open(Path(path).as_uri())

Check warning on line 87 in src/sage/misc/cython.py

View check run for this annotation

Codecov / codecov/patch

src/sage/misc/cython.py#L87

Added line #L87 was not covered by tests


def cython(filename, verbose=0, compile_message=False,
use_cache=False, create_local_c_file=False, annotate=True, sage_namespace=True,
create_local_so_file=False):
use_cache=False, create_local_c_file=False, annotate=True, view_annotate=False,
view_annotate_callback=_webbrowser_open_file, sage_namespace=True, create_local_so_file=False):
r"""
Compile a Cython file. This converts a Cython file to a C (or C++ file),
and then compiles that. The .c file and the .so file are
Expand Down Expand Up @@ -110,6 +119,14 @@
in the temporary directory, but if ``create_local_c_file`` is also True,
then save a copy of the .html file in the current directory.

- ``view_annotate`` -- boolean (default: ``False``); if ``True``, open the
annotated html file in a web browser

- ``view_annotate_callback`` -- function; a function that takes a string
being the path to the html file. This can be overridden to change
what to do with the annotated html file. Have no effect unless
``view_annotate`` is ``True``.

user202729 marked this conversation as resolved.
Show resolved Hide resolved
- ``sage_namespace`` -- boolean (default: ``True``); if ``True``, import
``sage.all``

Expand Down Expand Up @@ -226,6 +243,34 @@
....: from sage.misc.cachefunc cimport cache_key
....: ''')

Test ``view_annotate``::

sage: cython('''
....: def f(int n):
....: return n*n
....: ''', view_annotate=True) # optional -- webbrowser

::

sage: cython('''
....: def f(int n):
....: return n*n
....: ''', view_annotate=True, annotate=False)
Traceback (most recent call last):
...
ValueError: cannot view annotated file without creating it

::

sage: collected_paths = []
sage: cython('''
....: def f(int n):
....: return n*n
....: ''', view_annotate=True, view_annotate_callback=collected_paths.append)
sage: collected_paths
['...']
sage: len(collected_paths)
1
"""
if not filename.endswith('pyx'):
print("Warning: file (={}) should have extension .pyx".format(filename), file=sys.stderr)
Expand Down Expand Up @@ -381,6 +426,11 @@
shutil.copy(os.path.join(target_dir, name + ".html"),
os.curdir)

if view_annotate:
if not annotate:
raise ValueError("cannot view annotated file without creating it")
view_annotate_callback(os.path.join(target_dir, name + ".html"))

# This emulates running "setup.py build" with the correct options
user202729 marked this conversation as resolved.
Show resolved Hide resolved
#
# setuptools plugins considered harmful:
Expand Down
81 changes: 80 additions & 1 deletion src/sage/repl/ipython_extension.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,23 @@
"""

from IPython.core.magic import Magics, magics_class, line_magic, cell_magic
from IPython.core.display import HTML
from IPython.core.getipython import get_ipython

from sage.repl.load import load_wrap
from sage.env import SAGE_IMPORTALL, SAGE_STARTUP_FILE
from sage.misc.lazy_import import LazyImport
from sage.misc.misc import run_once


def _running_in_notebook():
try:
from ipykernel.zmqshell import ZMQInteractiveShell
except ImportError:
return False
return isinstance(get_ipython(), ZMQInteractiveShell)

Check warning on line 82 in src/sage/repl/ipython_extension.py

View check run for this annotation

Codecov / codecov/patch

src/sage/repl/ipython_extension.py#L78-L82

Added lines #L78 - L82 were not covered by tests


@magics_class
class SageMagics(Magics):

Expand Down Expand Up @@ -348,6 +358,12 @@
This is syntactic sugar on the
:func:`~sage.misc.cython.cython_compile` function.

Note that there is also the ``%%cython`` cell magic provided by Cython,
which can be loaded with ``%load_ext cython``, see
`Cython documentation <https://cython.readthedocs.io/en/latest/src/userguide/source_files_and_compilation.html#compiling-with-a-jupyter-notebook>`_
for more details.
The semantic is slightly different from the version provided by Sage.

INPUT:

- ``line`` -- parsed as keyword arguments. The allowed arguments are:
Expand All @@ -357,12 +373,20 @@
- ``--use-cache``
- ``--create-local-c-file``
- ``--annotate``
- ``--view-annotate``
- ``--sage-namespace``
- ``--create-local-so-file``
- ``--no-compile-message``, ``--no-use-cache``, etc.

See :func:`~sage.misc.cython.cython` for details.

If ``--view-annotate`` is given, the annotation is either displayed
inline in the Sage notebook or opened in a new web browser, depending
on whether the Sage notebook is used.

kwankyu marked this conversation as resolved.
Show resolved Hide resolved
You can override the selection by specifying
``--view-annotate=webbrowser`` or ``--view-annotate=displayhtml``.

kwankyu marked this conversation as resolved.
Show resolved Hide resolved
- ``cell`` -- string; the Cython source code to process
user202729 marked this conversation as resolved.
Show resolved Hide resolved

OUTPUT: none; the Cython code is compiled and loaded
Expand Down Expand Up @@ -403,6 +427,45 @@
....: ''')
UsageError: unrecognized arguments: --help

Test ``--view-annotate`` invalid arguments::

sage: # needs sage.misc.cython
sage: shell.run_cell('''
....: %%cython --view-annotate=xx
....: print(1)
....: ''')
UsageError: argument --view-annotate: invalid choice: 'xx' (choose from 'none', 'auto', 'webbrowser', 'displayhtml')

Test ``--view-annotate=displayhtml`` (note that in a notebook environment
an inline HTML frame will be displayed)::

sage: # needs sage.misc.cython
sage: shell.run_cell('''
....: %%cython --view-annotate=displayhtml
....: print(1)
....: ''')
1
<IPython.core.display.HTML object>

Test ``--view-annotate=webbrowser``::

sage: # needs sage.misc.cython webbrowser
sage: shell.run_cell('''
....: %%cython --view-annotate
....: print(1)
....: ''')
1
sage: shell.run_cell('''
....: %%cython --view-annotate=auto
....: print(1)
....: ''') # --view-annotate=auto is undocumented feature, equivalent to --view-annotate
1
sage: shell.run_cell('''
....: %%cython --view-annotate=webbrowser
....: print(1)
....: ''')
1

Test invalid quotes::

sage: # needs sage.misc.cython
Expand Down Expand Up @@ -434,10 +497,26 @@
parser.add_argument("--use-cache", action=argparse.BooleanOptionalAction)
parser.add_argument("--create-local-c-file", action=argparse.BooleanOptionalAction)
parser.add_argument("--annotate", action=argparse.BooleanOptionalAction)
parser.add_argument("--view-annotate", choices=["none", "auto", "webbrowser", "displayhtml"],
nargs="?", const="auto", default="none")
parser.add_argument("--sage-namespace", action=argparse.BooleanOptionalAction)
parser.add_argument("--create-local-so-file", action=argparse.BooleanOptionalAction)
args = parser.parse_args(shlex.split(line))
return cython_compile(cell, **{k: v for k, v in args.__dict__.items() if v is not None})
view_annotate = args.view_annotate
del args.view_annotate
if view_annotate == "auto":
if _running_in_notebook():
view_annotate = "displayhtml"

Check warning on line 509 in src/sage/repl/ipython_extension.py

View check run for this annotation

Codecov / codecov/patch

src/sage/repl/ipython_extension.py#L508-L509

Added lines #L508 - L509 were not covered by tests
else:
view_annotate = "webbrowser"

Check warning on line 511 in src/sage/repl/ipython_extension.py

View check run for this annotation

Codecov / codecov/patch

src/sage/repl/ipython_extension.py#L511

Added line #L511 was not covered by tests
args_dict = {k: v for k, v in args.__dict__.items() if v is not None}
if view_annotate != "none":
args_dict["view_annotate"] = True
if view_annotate == "displayhtml":
path_to_annotate_html_container = []
cython_compile(cell, **args_dict, view_annotate_callback=path_to_annotate_html_container.append)
return HTML(filename=path_to_annotate_html_container[0])
return cython_compile(cell, **args_dict)

@cell_magic
def fortran(self, line, cell):
Expand Down
Loading