From a699113e1575e0379736228734d6ae6c43f4edb6 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Sat, 9 Nov 2024 18:14:25 +0700 Subject: [PATCH 01/11] Allow passing view_annotate=True to Cython compilation --- src/sage/misc/cython.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/sage/misc/cython.py b/src/sage/misc/cython.py index c542e0d1919..01f3d7f580d 100644 --- a/src/sage/misc/cython.py +++ b/src/sage/misc/cython.py @@ -24,6 +24,7 @@ import re import sys import shutil +import webbrowser from sage.env import (SAGE_LOCAL, cython_aliases, sage_include_directories) @@ -79,8 +80,8 @@ def _standard_libs_libdirs_incdirs_aliases(): 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, + 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 @@ -110,6 +111,9 @@ def cython(filename, verbose=0, compile_message=False, 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 using :func:`webbrowser.open` + - ``sage_namespace`` -- boolean (default: ``True``); if ``True``, import ``sage.all`` @@ -392,6 +396,11 @@ def cython(filename, verbose=0, compile_message=False, 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") + webbrowser.open(os.path.join(target_dir, name + ".html")) + # This emulates running "setup.py build" with the correct options # # setuptools plugins considered harmful: From 64890af2100c4b36df1edd54c919382b34a2eaf7 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Sun, 10 Nov 2024 18:19:54 +0700 Subject: [PATCH 02/11] Add tests for view_annotate --- src/sage/misc/cython.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/sage/misc/cython.py b/src/sage/misc/cython.py index 01f3d7f580d..3016167c0be 100644 --- a/src/sage/misc/cython.py +++ b/src/sage/misc/cython.py @@ -241,6 +241,23 @@ def cython(filename, verbose=0, compile_message=False, RuntimeError: Error compiling Cython file: ... ...: 'sage/misc.pxd' not found + + 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 """ if not filename.endswith('pyx'): print("Warning: file (={}) should have extension .pyx".format(filename), file=sys.stderr) From 0f386e9fff4a4ce2e649fda912b8e1b27656bd9e Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Thu, 14 Nov 2024 21:42:23 +0700 Subject: [PATCH 03/11] Add view-annotate to IPython magic --- src/sage/repl/ipython_extension.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sage/repl/ipython_extension.py b/src/sage/repl/ipython_extension.py index 329a7b9b95a..15a6c0a7edc 100644 --- a/src/sage/repl/ipython_extension.py +++ b/src/sage/repl/ipython_extension.py @@ -357,6 +357,7 @@ def cython(self, line, cell): - ``--use-cache`` - ``--create-local-c-file`` - ``--annotate`` + - ``--view-annotate`` - ``--sage-namespace`` - ``--create-local-so-file`` - ``--no-compile-message``, ``--no-use-cache``, etc. @@ -434,6 +435,7 @@ def error(self, message): 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", action=argparse.BooleanOptionalAction) 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)) From f90425dbd93b47eb8cf709ddbdbb1b8ad9161554 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Sat, 7 Dec 2024 23:14:29 +0700 Subject: [PATCH 04/11] Allow view-annotate to display in Sage notebook --- src/sage/misc/cython.py | 21 +++++++++++-- src/sage/repl/ipython_extension.py | 48 ++++++++++++++++++++++++++++-- 2 files changed, 65 insertions(+), 4 deletions(-) diff --git a/src/sage/misc/cython.py b/src/sage/misc/cython.py index 3016167c0be..9b3e2428865 100644 --- a/src/sage/misc/cython.py +++ b/src/sage/misc/cython.py @@ -81,7 +81,7 @@ def _standard_libs_libdirs_incdirs_aliases(): def cython(filename, verbose=0, compile_message=False, use_cache=False, create_local_c_file=False, annotate=True, view_annotate=False, - sage_namespace=True, create_local_so_file=False): + view_annotate_callback=webbrowser.open, 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 @@ -114,6 +114,11 @@ def cython(filename, verbose=0, compile_message=False, - ``view_annotate`` -- boolean (default: ``False``); if ``True``, open the annotated html file in a web browser using :func:`webbrowser.open` + - ``view_annotate_callback`` -- function (default: ``webbrowser.open``); 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`` + - ``sage_namespace`` -- boolean (default: ``True``); if ``True``, import ``sage.all`` @@ -258,6 +263,18 @@ def cython(filename, verbose=0, compile_message=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) @@ -416,7 +433,7 @@ def cython(filename, verbose=0, compile_message=False, if view_annotate: if not annotate: raise ValueError("Cannot view annotated file without creating it") - webbrowser.open(os.path.join(target_dir, name + ".html")) + view_annotate_callback(os.path.join(target_dir, name + ".html")) # This emulates running "setup.py build" with the correct options # diff --git a/src/sage/repl/ipython_extension.py b/src/sage/repl/ipython_extension.py index 15a6c0a7edc..34d7d15c36f 100644 --- a/src/sage/repl/ipython_extension.py +++ b/src/sage/repl/ipython_extension.py @@ -65,6 +65,8 @@ """ from IPython.core.magic import Magics, magics_class, line_magic, cell_magic +from IPython.core.display import HTML +from IPython import get_ipython from sage.repl.load import load_wrap from sage.env import SAGE_IMPORTALL, SAGE_STARTUP_FILE @@ -72,6 +74,14 @@ 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) + + @magics_class class SageMagics(Magics): @@ -364,6 +374,16 @@ def cython(self, line, cell): See :func:`~sage.misc.cython.cython` for details. + For ``--view-annotate``, the following additional choices are allowed: + + - ``--view-annotate=none`` (default) + - ``--view-annotate=auto`` (same as ``--view-annotate``); select one of the + choices below automatically + - ``--view-annotate=webbrowser``; open the annotation in a web browser + (preferred if the Sage command line is used) + - ``--view-annotate=displayhtml``; display the annotation inline in the notebook + (preferred if the Sage notebook is used) + - ``cell`` -- string; the Cython source code to process OUTPUT: none; the Cython code is compiled and loaded @@ -404,6 +424,15 @@ def cython(self, line, cell): ....: ''') 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 invalid quotes:: sage: # needs sage.misc.cython @@ -435,11 +464,26 @@ def error(self, message): 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", 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" + else: + view_annotate = "webbrowser" + 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): From 011fd6caa4864a88b0f49817bb0a99fc7e80b755 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Sat, 7 Dec 2024 23:29:18 +0700 Subject: [PATCH 05/11] Fix pyright --- src/sage/repl/ipython_extension.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/repl/ipython_extension.py b/src/sage/repl/ipython_extension.py index 34d7d15c36f..9e1645e22df 100644 --- a/src/sage/repl/ipython_extension.py +++ b/src/sage/repl/ipython_extension.py @@ -66,7 +66,7 @@ from IPython.core.magic import Magics, magics_class, line_magic, cell_magic from IPython.core.display import HTML -from IPython import get_ipython +from IPython.core.getipython import get_ipython from sage.repl.load import load_wrap from sage.env import SAGE_IMPORTALL, SAGE_STARTUP_FILE From 0fc93c2951025b7161d001b81fb338a9660cbd9b Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Thu, 12 Dec 2024 21:14:11 +0700 Subject: [PATCH 06/11] Add a test for view-annotate=displayhtml --- src/sage/repl/ipython_extension.py | 36 ++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/sage/repl/ipython_extension.py b/src/sage/repl/ipython_extension.py index 9e1645e22df..c159f193ec5 100644 --- a/src/sage/repl/ipython_extension.py +++ b/src/sage/repl/ipython_extension.py @@ -358,6 +358,12 @@ def cython(self, line, cell): 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 `_ + for more details. + The semantic is slightly different from the version provided by Sage. + INPUT: - ``line`` -- parsed as keyword arguments. The allowed arguments are: @@ -433,6 +439,36 @@ def cython(self, line, cell): ....: ''') 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 + + + 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) + ....: ''') + 1 + sage: shell.run_cell(''' + ....: %%cython --view-annotate=webbrowser + ....: print(1) + ....: ''') + 1 + Test invalid quotes:: sage: # needs sage.misc.cython From 1551def6d18f82cd8cc91c6b8afe69b7a15c87c1 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Sun, 22 Dec 2024 20:29:34 +0700 Subject: [PATCH 07/11] Retrigger CI From 0069cf13bd260864fce7c2d15363e4f390930f69 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Mon, 23 Dec 2024 12:48:35 +0700 Subject: [PATCH 08/11] Apply suggested changes --- src/sage/misc/cython.py | 6 +++--- src/sage/repl/ipython_extension.py | 9 ++++----- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/sage/misc/cython.py b/src/sage/misc/cython.py index f77b5afcdd5..fde36372712 100644 --- a/src/sage/misc/cython.py +++ b/src/sage/misc/cython.py @@ -117,7 +117,7 @@ def cython(filename, verbose=0, compile_message=False, - ``view_annotate_callback`` -- function (default: ``webbrowser.open``); 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`` + ``view_annotate`` is ``True``. - ``sage_namespace`` -- boolean (default: ``True``); if ``True``, import ``sage.all`` @@ -250,7 +250,7 @@ def cython(filename, verbose=0, compile_message=False, ....: ''', view_annotate=True, annotate=False) Traceback (most recent call last): ... - ValueError: Cannot view annotated file without creating it + ValueError: cannot view annotated file without creating it :: @@ -420,7 +420,7 @@ def cython(filename, verbose=0, compile_message=False, if view_annotate: if not annotate: - raise ValueError("Cannot view annotated file without creating it") + 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 diff --git a/src/sage/repl/ipython_extension.py b/src/sage/repl/ipython_extension.py index c159f193ec5..4f75c563253 100644 --- a/src/sage/repl/ipython_extension.py +++ b/src/sage/repl/ipython_extension.py @@ -382,13 +382,12 @@ def cython(self, line, cell): For ``--view-annotate``, the following additional choices are allowed: - - ``--view-annotate=none`` (default) - - ``--view-annotate=auto`` (same as ``--view-annotate``); select one of the - choices below automatically + - ``--view-annotate=none`` (default); do not view the annotated html file + - ``--view-annotate`` (alternatively ``--view-annotate=auto``); select one of the + choices below automatically: use ``webbrowser`` in the Sage command line + and ``displayhtml`` in the Sage notebook - ``--view-annotate=webbrowser``; open the annotation in a web browser - (preferred if the Sage command line is used) - ``--view-annotate=displayhtml``; display the annotation inline in the notebook - (preferred if the Sage notebook is used) - ``cell`` -- string; the Cython source code to process From eb50b657af40a9d11b044a16d7ea7f0747033d98 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Mon, 23 Dec 2024 16:42:09 +0700 Subject: [PATCH 09/11] Simplify documentation of --view-annotate --- src/sage/repl/ipython_extension.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/sage/repl/ipython_extension.py b/src/sage/repl/ipython_extension.py index 4f75c563253..6c28c03f3da 100644 --- a/src/sage/repl/ipython_extension.py +++ b/src/sage/repl/ipython_extension.py @@ -380,14 +380,12 @@ def cython(self, line, cell): See :func:`~sage.misc.cython.cython` for details. - For ``--view-annotate``, the following additional choices are allowed: + If ``--view-annotate`` is given, the annotation is either displayed + inline in the Sage notebook or opened in a new web browser, depends + on whether the Sage notebook is used. - - ``--view-annotate=none`` (default); do not view the annotated html file - - ``--view-annotate`` (alternatively ``--view-annotate=auto``); select one of the - choices below automatically: use ``webbrowser`` in the Sage command line - and ``displayhtml`` in the Sage notebook - - ``--view-annotate=webbrowser``; open the annotation in a web browser - - ``--view-annotate=displayhtml``; display the annotation inline in the notebook + You can override the selection by specifying + ``--view-annotate=webbrowser`` or ``--view-annotate=displayhtml``. - ``cell`` -- string; the Cython source code to process @@ -460,7 +458,7 @@ def cython(self, line, cell): 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 From d3e23a70403a598a0421a4549bde02ac8d975d65 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Mon, 23 Dec 2024 18:45:45 +0700 Subject: [PATCH 10/11] Apply suggested changes --- src/sage/repl/ipython_extension.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/repl/ipython_extension.py b/src/sage/repl/ipython_extension.py index 6c28c03f3da..0818b0033b2 100644 --- a/src/sage/repl/ipython_extension.py +++ b/src/sage/repl/ipython_extension.py @@ -381,7 +381,7 @@ def cython(self, line, cell): 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, depends + inline in the Sage notebook or opened in a new web browser, depending on whether the Sage notebook is used. You can override the selection by specifying From 61166967838d8e8aba0baf3f22d5dcea28775936 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Mon, 23 Dec 2024 20:33:05 +0700 Subject: [PATCH 11/11] Convert file path to URI before pass to webbrowser.open() --- src/sage/misc/cython.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/sage/misc/cython.py b/src/sage/misc/cython.py index fde36372712..29c013eab19 100644 --- a/src/sage/misc/cython.py +++ b/src/sage/misc/cython.py @@ -25,6 +25,7 @@ import sys import shutil import webbrowser +from pathlib import Path from sage.env import (SAGE_LOCAL, cython_aliases, sage_include_directories) @@ -79,9 +80,16 @@ def _standard_libs_libdirs_incdirs_aliases(): sequence_number = {} +def _webbrowser_open_file(path): + """ + Open a html file in a web browser. + """ + webbrowser.open(Path(path).as_uri()) + + def cython(filename, verbose=0, compile_message=False, use_cache=False, create_local_c_file=False, annotate=True, view_annotate=False, - view_annotate_callback=webbrowser.open, sage_namespace=True, create_local_so_file=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 @@ -112,11 +120,11 @@ def cython(filename, verbose=0, compile_message=False, 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 using :func:`webbrowser.open` + annotated html file in a web browser - - ``view_annotate_callback`` -- function (default: ``webbrowser.open``); 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_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``. - ``sage_namespace`` -- boolean (default: ``True``); if ``True``, import