From 7b6ec436b9e521a522065021d657f51291b75efa Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 20 Oct 2023 15:46:06 -0600 Subject: [PATCH 01/37] First pass at using the doctest module in get_example_data --- papyri/gen.py | 173 +++++++++++++++++++++----------------------------- 1 file changed, 72 insertions(+), 101 deletions(-) diff --git a/papyri/gen.py b/papyri/gen.py index 91174f5a..2730f574 100644 --- a/papyri/gen.py +++ b/papyri/gen.py @@ -10,6 +10,7 @@ from __future__ import annotations +import doctest import dataclasses import datetime import importlib @@ -30,6 +31,7 @@ from pathlib import Path from types import FunctionType, ModuleType from typing import Any, Dict, FrozenSet, List, MutableMapping, Optional, Sequence, Tuple +import io import jedi @@ -47,6 +49,7 @@ from rich.progress import BarColumn, Progress, TextColumn, track from there import print as print_ from velin.examples_section_utils import InOut, splitblank, splitcode +from matplotlib import _pylab_helpers from .common_ast import Node from .errors import ( @@ -290,10 +293,9 @@ def parse_script( class ExecutionStatus(Enum): - none = "None" - compiled = "compiled" - syntax_error = "syntax_error" - exec_error = "exception_in_exec" + success = "success" + failure = "failure" + unexpected_exception = "unexpected_exception" def _execute_inout(item): @@ -1126,13 +1128,11 @@ def get_example_data( The capturing of matplotlib figures is also limited. """ assert qa is not None - blocks = list(map(splitcode, splitblank(example_section))) + example_code = '\n'.join(example_section) example_section_data = Section([], None) import matplotlib.pyplot as plt import numpy as np - acc = "" - def _figure_names(): """ File system can be case insensitive, we are not. @@ -1144,110 +1144,61 @@ def _figure_names(): figure_names = _figure_names() - ns = {"np": np, "plt": plt, obj.__name__: obj} - ns.update(_get_implied_imports(obj)) + globs = {"np": np, "plt": plt, obj.__name__: obj} + globs.update(_get_implied_imports(obj)) for k, v in config.implied_imports.items(): - ns[k] = obj_from_qualname(v) - executor = BlockExecutor(ns) + globs[k] = obj_from_qualname(v) + all_figs = [] - # fig_managers = _pylab_helpers.Gcf.get_all_fig_managers() - fig_managers = executor.fig_man() - assert (len(fig_managers)) == 0, f"init fail in {qa} {len(fig_managers)}" wait_for_show = config.wait_for_plt_show if qa in config.exclude_jedi: config = config.replace(infer=False) log.debug(f"Turning off type inference for func {qa!r}") - chunks = (it for block in blocks for it in block) - with executor: - for item in chunks: - figs = [] - if not isinstance(item, InOut): - assert isinstance(item.out, list) - example_section_data.append(MText("\n".join(item.out))) - continue - script, out, ce_status = _execute_inout(item) - raise_in_fig = None - did_except = False - if config.exec and ce_status == ExecutionStatus.compiled.value: - if not wait_for_show: - # we should aways have 0 figures - # unless stated otherwise - assert len(fig_managers) == 0 - try: - res = object() - try: - res, fig_managers, sout, serr = executor.exec(script) - ce_status = "execed" - except Exception: - if "Traceback" not in "\n".join(out): - script = script.replace("\n", "\n>>> ") - script = ">>> " + script - - meta_ = obj.__code__ - obj_fname = meta_.co_filename - obj_lineno = meta_.co_firstlineno - obj_name = meta_.co_name - - example_section_split = "\n".join( - example_section - ).split(script) - err_lineno = example_section_split[0].count("\n") - log.exception( - "error in execution: " - f"{obj_fname}:{obj_lineno} ({full_qual(obj)}) in {obj_name}" - f"\n-> Example section line {err_lineno}:" - f"\n\n{script}\n", - ) - ce_status = "exception_in_exec" - if config.exec_failure != "fallback": - raise - if fig_managers and ( - ("plt.show" in script) or not wait_for_show - ): - raise_in_fig = True - for fig, figname in zip(executor.get_figs(), figure_names): - figs.append((figname, fig)) - plt.close("all") - raise_in_fig = False - - except Exception: - did_except = True - print_(f"exception executing... {qa}") - fig_managers = executor.fig_man() - if raise_in_fig or config.exec_failure != "fallback": - raise - finally: - if not wait_for_show: - if fig_managers: - for fig, figname in zip( - executor.get_figs(), figure_names - ): - figs.append((figname, fig)) - print_( - f"Still fig manager(s) open for {qa}: {figname}" - ) - plt.close("all") - fig_managers = executor.fig_man() - assert len(fig_managers) == 0, fig_managers + [ - did_except, - ] - # we've executed, we now want to compare output - # in the docstring with the one we produced. - if (out == repr(res)) or (res is None and out == []): - pass - else: - pass - # captured output differ TBD - entries = parse_script(script, ns=ns, prev=acc, config=config, where=qa) + + sys_stdout = sys.stdout + def dbg(*args): + for arg in args: + sys_stdout.write(f"{arg}\n") + sys_stdout.flush() + + class PapyriDocTestRunner(doctest.DocTestRunner): + def __init__(slf, *args, **kwargs): + super().__init__(*args, **kwargs) + slf.figs = [] + slf.fig_managers = _pylab_helpers.Gcf.get_all_fig_managers() + assert (len(slf.fig_managers)) == 0, f"init fail in {qa} {len(slf.fig_managers)}" + + def _get_tok_entries(slf, example): + entries = parse_script(example.source, ns=globs, prev="", config=config, where=qa) if entries is None: entries = [("jedi failed", "jedi failed")] entries = _add_classes(entries) tok_entries = [GenToken(*x) for x in entries] # type: ignore + return tok_entries + + def report_start(slf, out, test, example): + pass + + def report_success(slf, out, test, example, got): + tok_entries = slf._get_tok_entries(example) - acc += "\n" + script example_section_data.append( - Code(tok_entries, "\n".join(item.out), ce_status) + Code(tok_entries, got, ExecutionStatus.success) ) + + + slf.fig_managers = _pylab_helpers.Gcf.get_all_fig_managers() + figs = [] + if slf.fig_managers and ( + ("plt.show" in example.source) or not wait_for_show + ): + for fig, figname in zip(slf.fig_managers, figure_names): + buf = io.BytesIO() + fig.canvas.figure.savefig(buf, dpi=300) # , bbox_inches="tight" + buf.seek(0) + figs.append((figname, buf.read())) + plt.close("all") + for figname, _ in figs: example_section_data.append( Fig( @@ -1256,11 +1207,31 @@ def _figure_names(): ) ) ) - all_figs.extend(figs) + slf.figs.extend(figs) + + def report_unexpected_exception(slf, out, test, example, + exc_info): + tok_entries = slf._get_tok_entries(example) + example_section_data.append( + Code(tok_entries, exc_info, ExecutionStatus.unexpected_exception) + ) + + def report_failure(slf, out, test, example, got): + tok_entries = slf._get_tok_entries(example) + example_section_data.append( + Code(tok_entries, got, ExecutionStatus.failure) + ) + + filename = obj.__code__.co_filename + lineno = obj.__code__.co_firstlineno + doctests = doctest.DocTestParser().get_doctest(example_code, globs, + obj.__name__, filename, lineno) + doctest_runner = PapyriDocTestRunner() + + doctest_runner.run(doctests, out=lambda s: None) # TODO fix this if plt.close not called and still a ligering figure. - fig_managers = executor.fig_man() - if len(fig_managers) != 0: + if len(doctest_runner.fig_managers) != 0: print_(f"Unclosed figures in {qa}!!") plt.close("all") From 8e11f5281660a72718bb6d6decd0d2c857361972 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 20 Oct 2023 15:55:51 -0600 Subject: [PATCH 02/37] Fix matplotlib showing the plots instead of saving them in doctests --- papyri/gen.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/papyri/gen.py b/papyri/gen.py index 2730f574..0b12219e 100644 --- a/papyri/gen.py +++ b/papyri/gen.py @@ -1164,6 +1164,10 @@ def dbg(*args): class PapyriDocTestRunner(doctest.DocTestRunner): def __init__(slf, *args, **kwargs): super().__init__(*args, **kwargs) + import matplotlib + + matplotlib.use("agg") + slf.figs = [] slf.fig_managers = _pylab_helpers.Gcf.get_all_fig_managers() assert (len(slf.fig_managers)) == 0, f"init fail in {qa} {len(slf.fig_managers)}" From 3dd390bebd3208989332a3040d2c29079058a914 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 10 Nov 2023 16:13:04 -0700 Subject: [PATCH 03/37] Move the PapyriDocTestRunner class to the module level --- papyri/gen.py | 181 ++++++++++++++++++++++++++------------------------ 1 file changed, 96 insertions(+), 85 deletions(-) diff --git a/papyri/gen.py b/papyri/gen.py index 0b12219e..ca86ee81 100644 --- a/papyri/gen.py +++ b/papyri/gen.py @@ -987,6 +987,98 @@ def _normalize_see_also(see_also: List[Any], qa): return new_see_also + +class PapyriDocTestRunner(doctest.DocTestRunner): + def __init__(self, *args, gen, obj, qa, config, **kwargs): + self.gen = gen + self.obj = obj + self.qa = qa + self.config = config + self.example_section_data = Section([], None) + super().__init__(*args, **kwargs) + import matplotlib + import matplotlib.pyplot as plt + import numpy as np + + matplotlib.use("agg") + + self.globs = {"np": np, "plt": plt, obj.__name__: obj} + self.globs.update(_get_implied_imports(obj)) + for k, v in config.implied_imports.items(): + self.globs[k] = obj_from_qualname(v) + + self.figs = [] + self.fig_managers = _pylab_helpers.Gcf.get_all_fig_managers() + assert (len(self.fig_managers)) == 0, f"init fail in {self.qa} {len(self.fig_managers)}" + + def _get_tok_entries(self, example): + entries = parse_script(example.source, ns=self.globs, prev="", config=self.config, where=self.qa) + if entries is None: + entries = [("jedi failed", "jedi failed")] + entries = _add_classes(entries) + tok_entries = [GenToken(*x) for x in entries] # type: ignore + return tok_entries + + def _figure_names(self): + """ + File system can be case insensitive, we are not. + """ + for i in count(0): + pat = f"fig-{self.qa}-{i}" + sha = sha256(pat.encode()).hexdigest()[:8] + yield f"{pat}-{sha}.png" + + + def report_start(self, out, test, example): + pass + + def report_success(self, out, test, example, got): + import matplotlib.pyplot as plt + + tok_entries = self._get_tok_entries(example) + + self.example_section_data.append( + Code(tok_entries, got, ExecutionStatus.success) + ) + + figure_names = self._figure_names() + + wait_for_show = self.config.wait_for_plt_show + self.fig_managers = _pylab_helpers.Gcf.get_all_fig_managers() + figs = [] + if self.fig_managers and ( + ("plt.show" in example.source) or not wait_for_show + ): + for fig, figname in zip(self.fig_managers, figure_names): + buf = io.BytesIO() + fig.canvas.figure.savefig(buf, dpi=300) # , bbox_inches="tight" + buf.seek(0) + figs.append((figname, buf.read())) + plt.close("all") + + for figname, _ in figs: + self.example_section_data.append( + Fig( + RefInfo.from_untrusted( + self.gen.root, self.gen.version, "assets", figname + ) + ) + ) + self.figs.extend(figs) + + def report_unexpected_exception(self, out, test, example, + exc_info): + tok_entries = self._get_tok_entries(example) + self.example_section_data.append( + Code(tok_entries, exc_info, ExecutionStatus.unexpected_exception) + ) + + def report_failure(self, out, test, example, got): + tok_entries = self._get_tok_entries(example) + self.example_section_data.append( + Code(tok_entries, got, ExecutionStatus.failure) + ) + class Gen: """ Core class to generate docbundles for a given library. @@ -1129,28 +1221,10 @@ def get_example_data( """ assert qa is not None example_code = '\n'.join(example_section) - example_section_data = Section([], None) import matplotlib.pyplot as plt - import numpy as np - - def _figure_names(): - """ - File system can be case insensitive, we are not. - """ - for i in count(0): - pat = f"fig-{qa}-{i}" - sha = sha256(pat.encode()).hexdigest()[:8] - yield f"{pat}-{sha}.png" - figure_names = _figure_names() - - globs = {"np": np, "plt": plt, obj.__name__: obj} - globs.update(_get_implied_imports(obj)) - for k, v in config.implied_imports.items(): - globs[k] = obj_from_qualname(v) all_figs = [] - wait_for_show = config.wait_for_plt_show if qa in config.exclude_jedi: config = config.replace(infer=False) log.debug(f"Turning off type inference for func {qa!r}") @@ -1161,79 +1235,16 @@ def dbg(*args): sys_stdout.write(f"{arg}\n") sys_stdout.flush() - class PapyriDocTestRunner(doctest.DocTestRunner): - def __init__(slf, *args, **kwargs): - super().__init__(*args, **kwargs) - import matplotlib - - matplotlib.use("agg") - - slf.figs = [] - slf.fig_managers = _pylab_helpers.Gcf.get_all_fig_managers() - assert (len(slf.fig_managers)) == 0, f"init fail in {qa} {len(slf.fig_managers)}" - - def _get_tok_entries(slf, example): - entries = parse_script(example.source, ns=globs, prev="", config=config, where=qa) - if entries is None: - entries = [("jedi failed", "jedi failed")] - entries = _add_classes(entries) - tok_entries = [GenToken(*x) for x in entries] # type: ignore - return tok_entries - - def report_start(slf, out, test, example): - pass - - def report_success(slf, out, test, example, got): - tok_entries = slf._get_tok_entries(example) - - example_section_data.append( - Code(tok_entries, got, ExecutionStatus.success) - ) - - - slf.fig_managers = _pylab_helpers.Gcf.get_all_fig_managers() - figs = [] - if slf.fig_managers and ( - ("plt.show" in example.source) or not wait_for_show - ): - for fig, figname in zip(slf.fig_managers, figure_names): - buf = io.BytesIO() - fig.canvas.figure.savefig(buf, dpi=300) # , bbox_inches="tight" - buf.seek(0) - figs.append((figname, buf.read())) - plt.close("all") - - for figname, _ in figs: - example_section_data.append( - Fig( - RefInfo.from_untrusted( - self.root, self.version, "assets", figname - ) - ) - ) - slf.figs.extend(figs) - - def report_unexpected_exception(slf, out, test, example, - exc_info): - tok_entries = slf._get_tok_entries(example) - example_section_data.append( - Code(tok_entries, exc_info, ExecutionStatus.unexpected_exception) - ) - - def report_failure(slf, out, test, example, got): - tok_entries = slf._get_tok_entries(example) - example_section_data.append( - Code(tok_entries, got, ExecutionStatus.failure) - ) - filename = obj.__code__.co_filename lineno = obj.__code__.co_firstlineno - doctests = doctest.DocTestParser().get_doctest(example_code, globs, + doctest_runner = PapyriDocTestRunner(gen=self, obj=obj, qa=qa, config=config) + doctests = doctest.DocTestParser().get_doctest(example_code, doctest_runner.globs, obj.__name__, filename, lineno) - doctest_runner = PapyriDocTestRunner() doctest_runner.run(doctests, out=lambda s: None) + example_section_data = doctest_runner.example_section_data + # TODO fix this if plt.close not called and still a ligering figure. if len(doctest_runner.fig_managers) != 0: print_(f"Unclosed figures in {qa}!!") From 0bbbabe8b79dc16597a0365fc03c5712ebb98d27 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 10 Nov 2023 17:58:59 -0700 Subject: [PATCH 04/37] Handle filename and lineno not being accessible They aren't actually that important for doctests because they are only used for the reporting, which we are bypassing anyways. --- papyri/gen.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/papyri/gen.py b/papyri/gen.py index ca86ee81..41ea0f13 100644 --- a/papyri/gen.py +++ b/papyri/gen.py @@ -1235,11 +1235,20 @@ def dbg(*args): sys_stdout.write(f"{arg}\n") sys_stdout.flush() - filename = obj.__code__.co_filename - lineno = obj.__code__.co_firstlineno + try: + filename = inspect.getfile(obj) + except TypeError: + filename = None + try: + lineno = inspect.getsourcelines(obj)[1] + except (TypeError, OSError): + lineno = None + doctest_runner = PapyriDocTestRunner(gen=self, obj=obj, qa=qa, config=config) - doctests = doctest.DocTestParser().get_doctest(example_code, doctest_runner.globs, - obj.__name__, filename, lineno) + doctests = doctest.DocTestParser().get_doctest(example_code, + doctest_runner.globs, + obj.__name__, filename, + lineno) doctest_runner.run(doctests, out=lambda s: None) From d65f57c98a5d68b305d8ec18afc53157c32cd101 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 10 Nov 2023 17:59:27 -0700 Subject: [PATCH 05/37] Fix a spelling error --- papyri/gen.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/papyri/gen.py b/papyri/gen.py index 41ea0f13..af29753e 100644 --- a/papyri/gen.py +++ b/papyri/gen.py @@ -1254,7 +1254,7 @@ def dbg(*args): example_section_data = doctest_runner.example_section_data - # TODO fix this if plt.close not called and still a ligering figure. + # TODO fix this if plt.close not called and still a lingering figure. if len(doctest_runner.fig_managers) != 0: print_(f"Unclosed figures in {qa}!!") plt.close("all") From 471d729c68da38109a857f6e47a52b6060c6635a Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 10 Nov 2023 18:07:00 -0700 Subject: [PATCH 06/37] Fix generation of doctest plot figures --- papyri/gen.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/papyri/gen.py b/papyri/gen.py index af29753e..50826b0a 100644 --- a/papyri/gen.py +++ b/papyri/gen.py @@ -1259,7 +1259,7 @@ def dbg(*args): print_(f"Unclosed figures in {qa}!!") plt.close("all") - return processed_example_data(example_section_data), all_figs + return processed_example_data(example_section_data), doctest_runner.figs def clean(self, where: Path): """ From bbcb40e1855f2b54f9cff7943687036890367d53 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 10 Nov 2023 18:20:56 -0700 Subject: [PATCH 07/37] Remove unused variable --- papyri/gen.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/papyri/gen.py b/papyri/gen.py index 50826b0a..0b461a44 100644 --- a/papyri/gen.py +++ b/papyri/gen.py @@ -1223,8 +1223,6 @@ def get_example_data( example_code = '\n'.join(example_section) import matplotlib.pyplot as plt - - all_figs = [] if qa in config.exclude_jedi: config = config.replace(infer=False) log.debug(f"Turning off type inference for func {qa!r}") From 29c66acccaf0f19ee351b4c2427998115b80cd11 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 10 Nov 2023 18:21:02 -0700 Subject: [PATCH 08/37] Use ELLIPSIS option flag Doesn't do anything right now because success/failure isn't saved --- papyri/gen.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/papyri/gen.py b/papyri/gen.py index 0b461a44..5656c59e 100644 --- a/papyri/gen.py +++ b/papyri/gen.py @@ -1242,7 +1242,10 @@ def dbg(*args): except (TypeError, OSError): lineno = None - doctest_runner = PapyriDocTestRunner(gen=self, obj=obj, qa=qa, config=config) + doctest_runner = PapyriDocTestRunner(gen=self, obj=obj, qa=qa, + config=config, + # TODO: Make optionflags configurable + optionflags=doctest.ELLIPSIS) doctests = doctest.DocTestParser().get_doctest(example_code, doctest_runner.globs, obj.__name__, filename, From a13f7e49bbd55bafd4f272f894777d0902b3fae5 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 20 Oct 2023 15:46:06 -0600 Subject: [PATCH 09/37] First pass at using the doctest module in get_example_data --- papyri/gen.py | 173 +++++++++++++++++++++----------------------------- 1 file changed, 72 insertions(+), 101 deletions(-) diff --git a/papyri/gen.py b/papyri/gen.py index e75634ce..d116a4cd 100644 --- a/papyri/gen.py +++ b/papyri/gen.py @@ -10,6 +10,7 @@ from __future__ import annotations +import doctest import dataclasses import datetime import importlib @@ -30,6 +31,7 @@ from pathlib import Path from types import FunctionType, ModuleType from typing import Any, Dict, FrozenSet, List, MutableMapping, Optional, Sequence, Tuple +import io import jedi @@ -44,6 +46,7 @@ from rich.progress import BarColumn, Progress, TextColumn, track from there import print as print_ from velin.examples_section_utils import InOut, splitblank, splitcode +from matplotlib import _pylab_helpers from .common_ast import Node from .errors import ( @@ -287,10 +290,9 @@ def parse_script( class ExecutionStatus(Enum): - none = "None" - compiled = "compiled" - syntax_error = "syntax_error" - exec_error = "exception_in_exec" + success = "success" + failure = "failure" + unexpected_exception = "unexpected_exception" def _execute_inout(item): @@ -1135,13 +1137,11 @@ def get_example_data( The capturing of matplotlib figures is also limited. """ assert qa is not None - blocks = list(map(splitcode, splitblank(example_section))) + example_code = '\n'.join(example_section) example_section_data = Section([], None) import matplotlib.pyplot as plt import numpy as np - acc = "" - def _figure_names(): """ File system can be case insensitive, we are not. @@ -1153,110 +1153,61 @@ def _figure_names(): figure_names = _figure_names() - ns = {"np": np, "plt": plt, obj.__name__: obj} - ns.update(_get_implied_imports(obj)) + globs = {"np": np, "plt": plt, obj.__name__: obj} + globs.update(_get_implied_imports(obj)) for k, v in config.implied_imports.items(): - ns[k] = obj_from_qualname(v) - executor = BlockExecutor(ns) + globs[k] = obj_from_qualname(v) + all_figs = [] - # fig_managers = _pylab_helpers.Gcf.get_all_fig_managers() - fig_managers = executor.fig_man() - assert (len(fig_managers)) == 0, f"init fail in {qa} {len(fig_managers)}" wait_for_show = config.wait_for_plt_show if qa in config.exclude_jedi: config = config.replace(infer=False) log.debug(f"Turning off type inference for func {qa!r}") - chunks = (it for block in blocks for it in block) - with executor: - for item in chunks: - figs = [] - if not isinstance(item, InOut): - assert isinstance(item.out, list) - example_section_data.append(MText("\n".join(item.out))) - continue - script, out, ce_status = _execute_inout(item) - raise_in_fig = None - did_except = False - if config.exec and ce_status == ExecutionStatus.compiled.value: - if not wait_for_show: - # we should aways have 0 figures - # unless stated otherwise - assert len(fig_managers) == 0 - try: - res = object() - try: - res, fig_managers, sout, serr = executor.exec(script) - ce_status = "execed" - except Exception: - if "Traceback" not in "\n".join(out): - script = script.replace("\n", "\n>>> ") - script = ">>> " + script - - meta_ = obj.__code__ - obj_fname = meta_.co_filename - obj_lineno = meta_.co_firstlineno - obj_name = meta_.co_name - - example_section_split = "\n".join( - example_section - ).split(script) - err_lineno = example_section_split[0].count("\n") - log.exception( - "error in execution: " - f"{obj_fname}:{obj_lineno} ({full_qual(obj)}) in {obj_name}" - f"\n-> Example section line {err_lineno}:" - f"\n\n{script}\n", - ) - ce_status = "exception_in_exec" - if config.exec_failure != "fallback": - raise - if fig_managers and ( - ("plt.show" in script) or not wait_for_show - ): - raise_in_fig = True - for fig, figname in zip(executor.get_figs(), figure_names): - figs.append((figname, fig)) - plt.close("all") - raise_in_fig = False - - except Exception: - did_except = True - print_(f"exception executing... {qa}") - fig_managers = executor.fig_man() - if raise_in_fig or config.exec_failure != "fallback": - raise - finally: - if not wait_for_show: - if fig_managers: - for fig, figname in zip( - executor.get_figs(), figure_names - ): - figs.append((figname, fig)) - print_( - f"Still fig manager(s) open for {qa}: {figname}" - ) - plt.close("all") - fig_managers = executor.fig_man() - assert len(fig_managers) == 0, fig_managers + [ - did_except, - ] - # we've executed, we now want to compare output - # in the docstring with the one we produced. - if (out == repr(res)) or (res is None and out == []): - pass - else: - pass - # captured output differ TBD - entries = parse_script(script, ns=ns, prev=acc, config=config, where=qa) + + sys_stdout = sys.stdout + def dbg(*args): + for arg in args: + sys_stdout.write(f"{arg}\n") + sys_stdout.flush() + + class PapyriDocTestRunner(doctest.DocTestRunner): + def __init__(slf, *args, **kwargs): + super().__init__(*args, **kwargs) + slf.figs = [] + slf.fig_managers = _pylab_helpers.Gcf.get_all_fig_managers() + assert (len(slf.fig_managers)) == 0, f"init fail in {qa} {len(slf.fig_managers)}" + + def _get_tok_entries(slf, example): + entries = parse_script(example.source, ns=globs, prev="", config=config, where=qa) if entries is None: entries = [("jedi failed", "jedi failed")] entries = _add_classes(entries) tok_entries = [GenToken(*x) for x in entries] # type: ignore + return tok_entries + + def report_start(slf, out, test, example): + pass + + def report_success(slf, out, test, example, got): + tok_entries = slf._get_tok_entries(example) - acc += "\n" + script example_section_data.append( - Code(tok_entries, "\n".join(item.out), ce_status) + Code(tok_entries, got, ExecutionStatus.success) ) + + + slf.fig_managers = _pylab_helpers.Gcf.get_all_fig_managers() + figs = [] + if slf.fig_managers and ( + ("plt.show" in example.source) or not wait_for_show + ): + for fig, figname in zip(slf.fig_managers, figure_names): + buf = io.BytesIO() + fig.canvas.figure.savefig(buf, dpi=300) # , bbox_inches="tight" + buf.seek(0) + figs.append((figname, buf.read())) + plt.close("all") + for figname, _ in figs: example_section_data.append( Fig( @@ -1265,11 +1216,31 @@ def _figure_names(): ) ) ) - all_figs.extend(figs) + slf.figs.extend(figs) + + def report_unexpected_exception(slf, out, test, example, + exc_info): + tok_entries = slf._get_tok_entries(example) + example_section_data.append( + Code(tok_entries, exc_info, ExecutionStatus.unexpected_exception) + ) + + def report_failure(slf, out, test, example, got): + tok_entries = slf._get_tok_entries(example) + example_section_data.append( + Code(tok_entries, got, ExecutionStatus.failure) + ) + + filename = obj.__code__.co_filename + lineno = obj.__code__.co_firstlineno + doctests = doctest.DocTestParser().get_doctest(example_code, globs, + obj.__name__, filename, lineno) + doctest_runner = PapyriDocTestRunner() + + doctest_runner.run(doctests, out=lambda s: None) # TODO fix this if plt.close not called and still a ligering figure. - fig_managers = executor.fig_man() - if len(fig_managers) != 0: + if len(doctest_runner.fig_managers) != 0: print_(f"Unclosed figures in {qa}!!") plt.close("all") From 9cdb1aee6107196641fc84c5979aa84ad15d6068 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 20 Oct 2023 15:55:51 -0600 Subject: [PATCH 10/37] Fix matplotlib showing the plots instead of saving them in doctests --- papyri/gen.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/papyri/gen.py b/papyri/gen.py index d116a4cd..c541f0c9 100644 --- a/papyri/gen.py +++ b/papyri/gen.py @@ -1173,6 +1173,10 @@ def dbg(*args): class PapyriDocTestRunner(doctest.DocTestRunner): def __init__(slf, *args, **kwargs): super().__init__(*args, **kwargs) + import matplotlib + + matplotlib.use("agg") + slf.figs = [] slf.fig_managers = _pylab_helpers.Gcf.get_all_fig_managers() assert (len(slf.fig_managers)) == 0, f"init fail in {qa} {len(slf.fig_managers)}" From 0fba48d21eeed4286a80eda404efe01aa57ea329 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 10 Nov 2023 16:13:04 -0700 Subject: [PATCH 11/37] Move the PapyriDocTestRunner class to the module level --- papyri/gen.py | 181 ++++++++++++++++++++++++++------------------------ 1 file changed, 96 insertions(+), 85 deletions(-) diff --git a/papyri/gen.py b/papyri/gen.py index c541f0c9..f893e13e 100644 --- a/papyri/gen.py +++ b/papyri/gen.py @@ -996,6 +996,98 @@ def _normalize_see_also(see_also: Section, qa: str): return new_see_also + +class PapyriDocTestRunner(doctest.DocTestRunner): + def __init__(self, *args, gen, obj, qa, config, **kwargs): + self.gen = gen + self.obj = obj + self.qa = qa + self.config = config + self.example_section_data = Section([], None) + super().__init__(*args, **kwargs) + import matplotlib + import matplotlib.pyplot as plt + import numpy as np + + matplotlib.use("agg") + + self.globs = {"np": np, "plt": plt, obj.__name__: obj} + self.globs.update(_get_implied_imports(obj)) + for k, v in config.implied_imports.items(): + self.globs[k] = obj_from_qualname(v) + + self.figs = [] + self.fig_managers = _pylab_helpers.Gcf.get_all_fig_managers() + assert (len(self.fig_managers)) == 0, f"init fail in {self.qa} {len(self.fig_managers)}" + + def _get_tok_entries(self, example): + entries = parse_script(example.source, ns=self.globs, prev="", config=self.config, where=self.qa) + if entries is None: + entries = [("jedi failed", "jedi failed")] + entries = _add_classes(entries) + tok_entries = [GenToken(*x) for x in entries] # type: ignore + return tok_entries + + def _figure_names(self): + """ + File system can be case insensitive, we are not. + """ + for i in count(0): + pat = f"fig-{self.qa}-{i}" + sha = sha256(pat.encode()).hexdigest()[:8] + yield f"{pat}-{sha}.png" + + + def report_start(self, out, test, example): + pass + + def report_success(self, out, test, example, got): + import matplotlib.pyplot as plt + + tok_entries = self._get_tok_entries(example) + + self.example_section_data.append( + Code(tok_entries, got, ExecutionStatus.success) + ) + + figure_names = self._figure_names() + + wait_for_show = self.config.wait_for_plt_show + self.fig_managers = _pylab_helpers.Gcf.get_all_fig_managers() + figs = [] + if self.fig_managers and ( + ("plt.show" in example.source) or not wait_for_show + ): + for fig, figname in zip(self.fig_managers, figure_names): + buf = io.BytesIO() + fig.canvas.figure.savefig(buf, dpi=300) # , bbox_inches="tight" + buf.seek(0) + figs.append((figname, buf.read())) + plt.close("all") + + for figname, _ in figs: + self.example_section_data.append( + Fig( + RefInfo.from_untrusted( + self.gen.root, self.gen.version, "assets", figname + ) + ) + ) + self.figs.extend(figs) + + def report_unexpected_exception(self, out, test, example, + exc_info): + tok_entries = self._get_tok_entries(example) + self.example_section_data.append( + Code(tok_entries, exc_info, ExecutionStatus.unexpected_exception) + ) + + def report_failure(self, out, test, example, got): + tok_entries = self._get_tok_entries(example) + self.example_section_data.append( + Code(tok_entries, got, ExecutionStatus.failure) + ) + class Gen: """ Core class to generate docbundles for a given library. @@ -1138,28 +1230,10 @@ def get_example_data( """ assert qa is not None example_code = '\n'.join(example_section) - example_section_data = Section([], None) import matplotlib.pyplot as plt - import numpy as np - - def _figure_names(): - """ - File system can be case insensitive, we are not. - """ - for i in count(0): - pat = f"fig-{qa}-{i}" - sha = sha256(pat.encode()).hexdigest()[:8] - yield f"{pat}-{sha}.png" - figure_names = _figure_names() - - globs = {"np": np, "plt": plt, obj.__name__: obj} - globs.update(_get_implied_imports(obj)) - for k, v in config.implied_imports.items(): - globs[k] = obj_from_qualname(v) all_figs = [] - wait_for_show = config.wait_for_plt_show if qa in config.exclude_jedi: config = config.replace(infer=False) log.debug(f"Turning off type inference for func {qa!r}") @@ -1170,79 +1244,16 @@ def dbg(*args): sys_stdout.write(f"{arg}\n") sys_stdout.flush() - class PapyriDocTestRunner(doctest.DocTestRunner): - def __init__(slf, *args, **kwargs): - super().__init__(*args, **kwargs) - import matplotlib - - matplotlib.use("agg") - - slf.figs = [] - slf.fig_managers = _pylab_helpers.Gcf.get_all_fig_managers() - assert (len(slf.fig_managers)) == 0, f"init fail in {qa} {len(slf.fig_managers)}" - - def _get_tok_entries(slf, example): - entries = parse_script(example.source, ns=globs, prev="", config=config, where=qa) - if entries is None: - entries = [("jedi failed", "jedi failed")] - entries = _add_classes(entries) - tok_entries = [GenToken(*x) for x in entries] # type: ignore - return tok_entries - - def report_start(slf, out, test, example): - pass - - def report_success(slf, out, test, example, got): - tok_entries = slf._get_tok_entries(example) - - example_section_data.append( - Code(tok_entries, got, ExecutionStatus.success) - ) - - - slf.fig_managers = _pylab_helpers.Gcf.get_all_fig_managers() - figs = [] - if slf.fig_managers and ( - ("plt.show" in example.source) or not wait_for_show - ): - for fig, figname in zip(slf.fig_managers, figure_names): - buf = io.BytesIO() - fig.canvas.figure.savefig(buf, dpi=300) # , bbox_inches="tight" - buf.seek(0) - figs.append((figname, buf.read())) - plt.close("all") - - for figname, _ in figs: - example_section_data.append( - Fig( - RefInfo.from_untrusted( - self.root, self.version, "assets", figname - ) - ) - ) - slf.figs.extend(figs) - - def report_unexpected_exception(slf, out, test, example, - exc_info): - tok_entries = slf._get_tok_entries(example) - example_section_data.append( - Code(tok_entries, exc_info, ExecutionStatus.unexpected_exception) - ) - - def report_failure(slf, out, test, example, got): - tok_entries = slf._get_tok_entries(example) - example_section_data.append( - Code(tok_entries, got, ExecutionStatus.failure) - ) - filename = obj.__code__.co_filename lineno = obj.__code__.co_firstlineno - doctests = doctest.DocTestParser().get_doctest(example_code, globs, + doctest_runner = PapyriDocTestRunner(gen=self, obj=obj, qa=qa, config=config) + doctests = doctest.DocTestParser().get_doctest(example_code, doctest_runner.globs, obj.__name__, filename, lineno) - doctest_runner = PapyriDocTestRunner() doctest_runner.run(doctests, out=lambda s: None) + example_section_data = doctest_runner.example_section_data + # TODO fix this if plt.close not called and still a ligering figure. if len(doctest_runner.fig_managers) != 0: print_(f"Unclosed figures in {qa}!!") From 24389a43c8fe5bddcb4da4f866dd8bd97659e6f0 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 10 Nov 2023 17:58:59 -0700 Subject: [PATCH 12/37] Handle filename and lineno not being accessible They aren't actually that important for doctests because they are only used for the reporting, which we are bypassing anyways. --- papyri/gen.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/papyri/gen.py b/papyri/gen.py index f893e13e..5aa2f37c 100644 --- a/papyri/gen.py +++ b/papyri/gen.py @@ -1244,11 +1244,20 @@ def dbg(*args): sys_stdout.write(f"{arg}\n") sys_stdout.flush() - filename = obj.__code__.co_filename - lineno = obj.__code__.co_firstlineno + try: + filename = inspect.getfile(obj) + except TypeError: + filename = None + try: + lineno = inspect.getsourcelines(obj)[1] + except (TypeError, OSError): + lineno = None + doctest_runner = PapyriDocTestRunner(gen=self, obj=obj, qa=qa, config=config) - doctests = doctest.DocTestParser().get_doctest(example_code, doctest_runner.globs, - obj.__name__, filename, lineno) + doctests = doctest.DocTestParser().get_doctest(example_code, + doctest_runner.globs, + obj.__name__, filename, + lineno) doctest_runner.run(doctests, out=lambda s: None) From d946cc648a53b010dd136edfdbc95597fd304ef1 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 10 Nov 2023 17:59:27 -0700 Subject: [PATCH 13/37] Fix a spelling error --- papyri/gen.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/papyri/gen.py b/papyri/gen.py index 5aa2f37c..c6f3ff62 100644 --- a/papyri/gen.py +++ b/papyri/gen.py @@ -1263,7 +1263,7 @@ def dbg(*args): example_section_data = doctest_runner.example_section_data - # TODO fix this if plt.close not called and still a ligering figure. + # TODO fix this if plt.close not called and still a lingering figure. if len(doctest_runner.fig_managers) != 0: print_(f"Unclosed figures in {qa}!!") plt.close("all") From 877026f7447dca6d415336081c53913ff5b248c6 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 10 Nov 2023 18:07:00 -0700 Subject: [PATCH 14/37] Fix generation of doctest plot figures --- papyri/gen.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/papyri/gen.py b/papyri/gen.py index c6f3ff62..82ad1238 100644 --- a/papyri/gen.py +++ b/papyri/gen.py @@ -1268,7 +1268,7 @@ def dbg(*args): print_(f"Unclosed figures in {qa}!!") plt.close("all") - return processed_example_data(example_section_data), all_figs + return processed_example_data(example_section_data), doctest_runner.figs def clean(self, where: Path): """ From cbd7fc0da7d03b5d95526093b380773d2ec3f85d Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 10 Nov 2023 18:20:56 -0700 Subject: [PATCH 15/37] Remove unused variable --- papyri/gen.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/papyri/gen.py b/papyri/gen.py index 82ad1238..60959cb0 100644 --- a/papyri/gen.py +++ b/papyri/gen.py @@ -1232,8 +1232,6 @@ def get_example_data( example_code = '\n'.join(example_section) import matplotlib.pyplot as plt - - all_figs = [] if qa in config.exclude_jedi: config = config.replace(infer=False) log.debug(f"Turning off type inference for func {qa!r}") From 83be405a20b4442e21c7c310562331f5ef956eb9 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 10 Nov 2023 18:21:02 -0700 Subject: [PATCH 16/37] Use ELLIPSIS option flag Doesn't do anything right now because success/failure isn't saved --- papyri/gen.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/papyri/gen.py b/papyri/gen.py index 60959cb0..3c746694 100644 --- a/papyri/gen.py +++ b/papyri/gen.py @@ -1251,7 +1251,10 @@ def dbg(*args): except (TypeError, OSError): lineno = None - doctest_runner = PapyriDocTestRunner(gen=self, obj=obj, qa=qa, config=config) + doctest_runner = PapyriDocTestRunner(gen=self, obj=obj, qa=qa, + config=config, + # TODO: Make optionflags configurable + optionflags=doctest.ELLIPSIS) doctests = doctest.DocTestParser().get_doctest(example_code, doctest_runner.globs, obj.__name__, filename, From 1a0caee4d19dbd766a4d85296790b02a302c4f3c Mon Sep 17 00:00:00 2001 From: Matthias Bussonnier Date: Fri, 17 Nov 2023 11:05:14 +0100 Subject: [PATCH 17/37] add debug print --- papyri/gen.py | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/papyri/gen.py b/papyri/gen.py index 3c746694..13428fd9 100644 --- a/papyri/gen.py +++ b/papyri/gen.py @@ -1251,16 +1251,28 @@ def dbg(*args): except (TypeError, OSError): lineno = None - doctest_runner = PapyriDocTestRunner(gen=self, obj=obj, qa=qa, - config=config, - # TODO: Make optionflags configurable - optionflags=doctest.ELLIPSIS) - doctests = doctest.DocTestParser().get_doctest(example_code, - doctest_runner.globs, - obj.__name__, filename, - lineno) - - doctest_runner.run(doctests, out=lambda s: None) + doctest_runner = PapyriDocTestRunner( + gen=self, + obj=obj, + qa=qa, + config=config, + # TODO: Make optionflags configurable + optionflags=doctest.ELLIPSIS, + ) + doctests = doctest.DocTestParser().get_doctest( + example_code, doctest_runner.globs, obj.__name__, filename, lineno + ) + + + stdout = sys.stdout + + def debugprint(*args): + """ + version of print that capture current stdout to use during testing to debug + """ + stdout.write(" ".join(str(x) for x in args) + "\n") + + doctest_runner.run(doctests, out=debugprint) example_section_data = doctest_runner.example_section_data From 98304d248b5dfdc6a7ed3f2626ee38493fa62ec9 Mon Sep 17 00:00:00 2001 From: Matthias Bussonnier Date: Fri, 17 Nov 2023 11:08:41 +0100 Subject: [PATCH 18/37] debug log --- papyri/gen.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/papyri/gen.py b/papyri/gen.py index 13428fd9..54f95004 100644 --- a/papyri/gen.py +++ b/papyri/gen.py @@ -1075,8 +1075,8 @@ def report_success(self, out, test, example, got): ) self.figs.extend(figs) - def report_unexpected_exception(self, out, test, example, - exc_info): + def report_unexpected_exception(self, out, test, example, exc_info): + out("Unexpected exception", exc_info) tok_entries = self._get_tok_entries(example) self.example_section_data.append( Code(tok_entries, exc_info, ExecutionStatus.unexpected_exception) From 236c309cf4f3ffb715b83682cf88681247e306eb Mon Sep 17 00:00:00 2001 From: Matthias Bussonnier Date: Mon, 20 Nov 2023 15:00:04 +0100 Subject: [PATCH 19/37] cleanup --- papyri/gen.py | 24 ++++++++++-------------- requirements-dev.txt | 1 - requirements.txt | 1 - 3 files changed, 10 insertions(+), 16 deletions(-) diff --git a/papyri/gen.py b/papyri/gen.py index 54f95004..24b1a696 100644 --- a/papyri/gen.py +++ b/papyri/gen.py @@ -45,7 +45,6 @@ from rich.logging import RichHandler from rich.progress import BarColumn, Progress, TextColumn, track from there import print as print_ -from velin.examples_section_utils import InOut, splitblank, splitcode from matplotlib import _pylab_helpers from .common_ast import Node @@ -85,10 +84,6 @@ ) from .vref import NumpyDocString -# delayed import - -from .myst_ast import MText - class ErrorCollector: def __init__(self, config: Config, log): @@ -996,7 +991,6 @@ def _normalize_see_also(see_also: Section, qa: str): return new_see_also - class PapyriDocTestRunner(doctest.DocTestRunner): def __init__(self, *args, gen, obj, qa, config, **kwargs): self.gen = gen @@ -1018,10 +1012,14 @@ def __init__(self, *args, gen, obj, qa, config, **kwargs): self.figs = [] self.fig_managers = _pylab_helpers.Gcf.get_all_fig_managers() - assert (len(self.fig_managers)) == 0, f"init fail in {self.qa} {len(self.fig_managers)}" + assert ( + len(self.fig_managers) + ) == 0, f"init fail in {self.qa} {len(self.fig_managers)}" def _get_tok_entries(self, example): - entries = parse_script(example.source, ns=self.globs, prev="", config=self.config, where=self.qa) + entries = parse_script( + example.source, ns=self.globs, prev="", config=self.config, where=self.qa + ) if entries is None: entries = [("jedi failed", "jedi failed")] entries = _add_classes(entries) @@ -1037,7 +1035,6 @@ def _figure_names(self): sha = sha256(pat.encode()).hexdigest()[:8] yield f"{pat}-{sha}.png" - def report_start(self, out, test, example): pass @@ -1055,9 +1052,7 @@ def report_success(self, out, test, example, got): wait_for_show = self.config.wait_for_plt_show self.fig_managers = _pylab_helpers.Gcf.get_all_fig_managers() figs = [] - if self.fig_managers and ( - ("plt.show" in example.source) or not wait_for_show - ): + if self.fig_managers and (("plt.show" in example.source) or not wait_for_show): for fig, figname in zip(self.fig_managers, figure_names): buf = io.BytesIO() fig.canvas.figure.savefig(buf, dpi=300) # , bbox_inches="tight" @@ -1088,6 +1083,7 @@ def report_failure(self, out, test, example, got): Code(tok_entries, got, ExecutionStatus.failure) ) + class Gen: """ Core class to generate docbundles for a given library. @@ -1229,7 +1225,7 @@ def get_example_data( The capturing of matplotlib figures is also limited. """ assert qa is not None - example_code = '\n'.join(example_section) + example_code = "\n".join(example_section) import matplotlib.pyplot as plt if qa in config.exclude_jedi: @@ -1237,6 +1233,7 @@ def get_example_data( log.debug(f"Turning off type inference for func {qa!r}") sys_stdout = sys.stdout + def dbg(*args): for arg in args: sys_stdout.write(f"{arg}\n") @@ -1263,7 +1260,6 @@ def dbg(*args): example_code, doctest_runner.globs, obj.__name__, filename, lineno ) - stdout = sys.stdout def debugprint(*args): diff --git a/requirements-dev.txt b/requirements-dev.txt index ef9c8106..b3c44218 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -2,7 +2,6 @@ pytest pytest-cov flit matplotlib -velin>=0.0.5 pytest-trio tree_sitter coverage diff --git a/requirements.txt b/requirements.txt index 792dc91d..2325653b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,4 +12,3 @@ rich there tree_sitter typer -velin From 841ac12acb930c043685c2dbce89904a48820199 Mon Sep 17 00:00:00 2001 From: Matthias Bussonnier Date: Mon, 20 Nov 2023 15:02:56 +0100 Subject: [PATCH 20/37] ... to >>> --- papyri/examples.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/papyri/examples.py b/papyri/examples.py index b3437e96..d39fd816 100644 --- a/papyri/examples.py +++ b/papyri/examples.py @@ -107,13 +107,13 @@ def example1( Examples -------- >>> import matplotlib.pyplot as plt - ... fig, ax = plt.subplots() - ... ax.plot(range(10)) + >>> fig, ax = plt.subplots() + >>> ax.plot(range(10)) And now >>> ax.plot(range(5, 15)) - ... plt.show() + >>> plt.show() """ return "ok" @@ -124,24 +124,24 @@ def example2(): Examples -------- >>> import numpy as np - ... import matplotlib.pyplot as plt - ... from scipy.interpolate import UnivariateSpline + >>> import matplotlib.pyplot as plt + >>> from scipy.interpolate import UnivariateSpline >>> x = np.linspace(-3, 3, 50) - ... y = np.exp(-(x ** 2)) + 0.1 * np.random.randn(50) - ... plt.plot(x, y, "ro", ms=5) + >>> y = np.exp(-(x ** 2)) + 0.1 * np.random.randn(50) + >>> plt.plot(x, y, "ro", ms=5) Use the default value for the smoothing parameter: >>> spl = UnivariateSpline(x, y) - ... xs = np.linspace(-3, 3, 1000) - ... plt.plot(xs, spl(xs), "g", lw=3) + >>> xs = np.linspace(-3, 3, 1000) + >>> plt.plot(xs, spl(xs), "g", lw=3) Manually change the amount of smoothing: >>> spl.set_smoothing_factor(0.5) - ... plt.plot(xs, spl(xs), "b", lw=3) - ... plt.show() + >>> plt.plot(xs, spl(xs), "b", lw=3) + >>> plt.show() """ pass From ef6780eccff2ec4c14c5b642f8b81fb559110f20 Mon Sep 17 00:00:00 2001 From: Matthias Bussonnier Date: Mon, 27 Nov 2023 11:59:33 +0100 Subject: [PATCH 21/37] debug and execute config --- examples/dask.toml | 1 + examples/distributed.toml | 1 + papyri/gen.py | 6 ++++-- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/examples/dask.toml b/examples/dask.toml index 000ff9d1..2c7a2d89 100644 --- a/examples/dask.toml +++ b/examples/dask.toml @@ -39,3 +39,4 @@ exclude = [ "dask.utils:Dispatch", #docs_path = "~/dev/dask/docs/source" exec_failure = 'fallback' +execute_doctests = false diff --git a/examples/distributed.toml b/examples/distributed.toml index aeb6e2fa..8262e687 100644 --- a/examples/distributed.toml +++ b/examples/distributed.toml @@ -18,3 +18,4 @@ exclude = [ "distributed.shuffle._comms:CommShardsBuffer", ] #docs_path = "~/dev/dask/docs/source" exec_failure = 'fallback' +execute_doctests = false diff --git a/papyri/gen.py b/papyri/gen.py index 24b1a696..ca9ed1e2 100644 --- a/papyri/gen.py +++ b/papyri/gen.py @@ -425,6 +425,7 @@ class Config: expected_errors: Dict[str, List[str]] = dataclasses.field(default_factory=dict) early_error: bool = True fail_unseen_error: bool = False + execute_doctests: bool = True def replace(self, **kwargs): return dataclasses.replace(self, **kwargs) @@ -1071,7 +1072,7 @@ def report_success(self, out, test, example, got): self.figs.extend(figs) def report_unexpected_exception(self, out, test, example, exc_info): - out("Unexpected exception", exc_info) + out(f"Unexpected exception after running example in `{self.qa}`", exc_info) tok_entries = self._get_tok_entries(example) self.example_section_data.append( Code(tok_entries, exc_info, ExecutionStatus.unexpected_exception) @@ -1268,7 +1269,8 @@ def debugprint(*args): """ stdout.write(" ".join(str(x) for x in args) + "\n") - doctest_runner.run(doctests, out=debugprint) + if config.execute_doctests: + doctest_runner.run(doctests, out=debugprint) example_section_data = doctest_runner.example_section_data From 507e37cd43165622c7092579cbbbb6d1b9376a99 Mon Sep 17 00:00:00 2001 From: Matthias Bussonnier Date: Mon, 27 Nov 2023 12:07:34 +0100 Subject: [PATCH 22/37] don't execute in pandas --- examples/pandas.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/pandas.toml b/examples/pandas.toml index 21a97eb9..524403d1 100644 --- a/examples/pandas.toml +++ b/examples/pandas.toml @@ -5,6 +5,7 @@ exec_failure = "fallback" submodules = [] exclude = [ ] +execute_doctests = false [pandas.expected_errors] ValueError = [ "pandas.io.gbq:read_gbq", From cfbacb7ec0ab22514301e02e1d210c1806885e4f Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 1 Dec 2023 15:10:30 -0700 Subject: [PATCH 23/37] Add Section.extend --- papyri/take2.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/papyri/take2.py b/papyri/take2.py index 50016965..53111ca5 100644 --- a/papyri/take2.py +++ b/papyri/take2.py @@ -306,6 +306,9 @@ def __iter__(self): def append(self, item): self.children.append(item) + def extend(self, items): + self.children.extend(items) + def empty(self): return len(self.children) == 0 From 3e2409df67db46b72c341fe535daba8406099054 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 1 Dec 2023 15:10:41 -0700 Subject: [PATCH 24/37] Split example sections into blocks This makes narrative text in example sections be handled correctly. --- papyri/gen.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/papyri/gen.py b/papyri/gen.py index 5656c59e..96f772bc 100644 --- a/papyri/gen.py +++ b/papyri/gen.py @@ -1221,6 +1221,7 @@ def get_example_data( """ assert qa is not None example_code = '\n'.join(example_section) + blocks = example_code.split("\n\n") import matplotlib.pyplot as plt if qa in config.exclude_jedi: @@ -1246,15 +1247,18 @@ def dbg(*args): config=config, # TODO: Make optionflags configurable optionflags=doctest.ELLIPSIS) - doctests = doctest.DocTestParser().get_doctest(example_code, - doctest_runner.globs, - obj.__name__, filename, - lineno) - - doctest_runner.run(doctests, out=lambda s: None) - - example_section_data = doctest_runner.example_section_data - + example_section_data = Section([], None) + + for block in blocks: + doctests = doctest.DocTestParser().get_doctest(block, + doctest_runner.globs, + obj.__name__, filename, + lineno) + if doctests.examples: + doctest_runner.run(doctests, out=lambda s: None) + example_section_data.extend(doctest_runner.example_section_data) + else: + example_section_data.append(MText(block)) # TODO fix this if plt.close not called and still a lingering figure. if len(doctest_runner.fig_managers) != 0: print_(f"Unclosed figures in {qa}!!") From a2da2d8031a13cdfc197204f00dd7e3d204ea39d Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 1 Dec 2023 15:38:41 -0700 Subject: [PATCH 25/37] Fix errors from running doctests on SciPy --- papyri/gen.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/papyri/gen.py b/papyri/gen.py index 6b03a026..ccb2e319 100644 --- a/papyri/gen.py +++ b/papyri/gen.py @@ -1013,10 +1013,6 @@ def __init__(self, *args, gen, obj, qa, config, **kwargs): self.globs[k] = obj_from_qualname(v) self.figs = [] - self.fig_managers = _pylab_helpers.Gcf.get_all_fig_managers() - assert ( - len(self.fig_managers) - ) == 0, f"init fail in {self.qa} {len(self.fig_managers)}" def _get_tok_entries(self, example): entries = parse_script( @@ -1052,10 +1048,10 @@ def report_success(self, out, test, example, got): figure_names = self._figure_names() wait_for_show = self.config.wait_for_plt_show - self.fig_managers = _pylab_helpers.Gcf.get_all_fig_managers() + fig_managers = _pylab_helpers.Gcf.get_all_fig_managers() figs = [] - if self.fig_managers and (("plt.show" in example.source) or not wait_for_show): - for fig, figname in zip(self.fig_managers, figure_names): + if fig_managers and (("plt.show" in example.source) or not wait_for_show): + for fig, figname in zip(fig_managers, figure_names): buf = io.BytesIO() fig.canvas.figure.savefig(buf, dpi=300) # , bbox_inches="tight" buf.seek(0) @@ -1070,7 +1066,7 @@ def report_success(self, out, test, example, got): ) ) ) - self.figs.extend(figs) + figs.extend(figs) def report_unexpected_exception(self, out, test, example, exc_info): out(f"Unexpected exception after running example in `{self.qa}`", exc_info) @@ -1272,7 +1268,8 @@ def debugprint(*args): else: example_section_data.append(MText(block)) # TODO fix this if plt.close not called and still a lingering figure. - if len(doctest_runner.fig_managers) != 0: + fig_managers = _pylab_helpers.Gcf.get_all_fig_managers() + if len(fig_managers) != 0: print_(f"Unclosed figures in {qa}!!") plt.close("all") From 9bf10fb71ac24ce348796cc87e6186d996351ad1 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 15 Dec 2023 14:47:42 -0700 Subject: [PATCH 26/37] Fix the --no-exec flag --- papyri/gen.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/papyri/gen.py b/papyri/gen.py index a82173a3..253ccb9b 100644 --- a/papyri/gen.py +++ b/papyri/gen.py @@ -1331,7 +1331,7 @@ def debugprint(*args): doctest_runner.globs, obj.__name__, filename, lineno) - if doctests.examples: + if config.exec and doctests.examples: doctest_runner.run(doctests, out=debugprint) example_section_data.extend(doctest_runner.example_section_data) else: From a1c8cdb25dccf71aa21e94468468c27607006743 Mon Sep 17 00:00:00 2001 From: Matthias Bussonnier Date: Sat, 16 Dec 2023 14:13:08 +0100 Subject: [PATCH 27/37] Excluse `set_numeric_ops` which seem to be the root of crash. --- examples/numpy.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/numpy.toml b/examples/numpy.toml index 938cd9a5..0deacc1a 100644 --- a/examples/numpy.toml +++ b/examples/numpy.toml @@ -7,6 +7,7 @@ exclude = [ ] execute_exclude_patterns = [ +'numpy:set_numeric_ops', 'numpy._', 'numpy.testing._priv', 'numpy.errstate', From db1dbb4bad73b405611b7bbb4e57e508dba4a96b Mon Sep 17 00:00:00 2001 From: Matthias Bussonnier Date: Sat, 16 Dec 2023 14:13:42 +0100 Subject: [PATCH 28/37] reforamt to please linters --- papyri/gen.py | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/papyri/gen.py b/papyri/gen.py index 253ccb9b..a29d5fa9 100644 --- a/papyri/gen.py +++ b/papyri/gen.py @@ -89,6 +89,7 @@ if True: from .myst_ast import MText + class ErrorCollector: _expected_unseen: Dict[str, Any] errored: bool @@ -1292,7 +1293,7 @@ def get_example_data( The capturing of matplotlib figures is also limited. """ assert qa is not None - example_code = '\n'.join(example_section) + example_code = "\n".join(example_section) blocks = example_code.split("\n\n") import matplotlib.pyplot as plt @@ -1301,6 +1302,7 @@ def get_example_data( log.debug(f"Turning off type inference for func {qa!r}") sys_stdout = sys.stdout + def dbg(*args): for arg in args: sys_stdout.write(f"{arg}\n") @@ -1315,11 +1317,16 @@ def dbg(*args): except (TypeError, OSError): lineno = None - doctest_runner = PapyriDocTestRunner(gen=self, obj=obj, qa=qa, - config=config, - # TODO: Make optionflags configurable - optionflags=doctest.ELLIPSIS) + doctest_runner = PapyriDocTestRunner( + gen=self, + obj=obj, + qa=qa, + config=config, + # TODO: Make optionflags configurable + optionflags=doctest.ELLIPSIS, + ) example_section_data = Section([], None) + def debugprint(*args): """ version of print that capture current stdout to use during testing to debug @@ -1327,10 +1334,9 @@ def debugprint(*args): sys_stdout.write(" ".join(str(x) for x in args) + "\n") for block in blocks: - doctests = doctest.DocTestParser().get_doctest(block, - doctest_runner.globs, - obj.__name__, filename, - lineno) + doctests = doctest.DocTestParser().get_doctest( + block, doctest_runner.globs, obj.__name__, filename, lineno + ) if config.exec and doctests.examples: doctest_runner.run(doctests, out=debugprint) example_section_data.extend(doctest_runner.example_section_data) From 4d114ff8f46af8293a655c9fd96ebae5e3237812 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Thu, 4 Jan 2024 14:32:25 -0700 Subject: [PATCH 29/37] Fix the doctest runner not maintaining the namespace across different blocks --- papyri/gen.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/papyri/gen.py b/papyri/gen.py index 186ccadf..770e7b89 100644 --- a/papyri/gen.py +++ b/papyri/gen.py @@ -1327,10 +1327,12 @@ def debugprint(*args): block, doctest_runner.globs, obj.__name__, filename, lineno ) if config.exec and doctests.examples: - doctest_runner.run(doctests, out=debugprint) + doctest_runner.run(doctests, out=debugprint, clear_globs=False) + doctest_runner.globs.update(doctests.globs) example_section_data.extend(doctest_runner.example_section_data) else: example_section_data.append(MText(block)) + # TODO fix this if plt.close not called and still a lingering figure. fig_managers = _pylab_helpers.Gcf.get_all_fig_managers() if len(fig_managers) != 0: From 4100b5e05b13f1ec3043be7665802348ae6d27cf Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Thu, 4 Jan 2024 14:55:24 -0700 Subject: [PATCH 30/37] Use DocTestParser to parse doctest blocks This is more robust and correct than doing code.split('\n\n'). --- papyri/gen.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/papyri/gen.py b/papyri/gen.py index 770e7b89..61fee031 100644 --- a/papyri/gen.py +++ b/papyri/gen.py @@ -1283,7 +1283,6 @@ def get_example_data( """ assert qa is not None example_code = "\n".join(example_section) - blocks = example_code.split("\n\n") import matplotlib.pyplot as plt if qa in config.exclude_jedi: @@ -1322,14 +1321,19 @@ def debugprint(*args): """ sys_stdout.write(" ".join(str(x) for x in args) + "\n") + blocks = doctest.DocTestParser().parse(example_code, name=qa) for block in blocks: - doctests = doctest.DocTestParser().get_doctest( - block, doctest_runner.globs, obj.__name__, filename, lineno - ) - if config.exec and doctests.examples: - doctest_runner.run(doctests, out=debugprint, clear_globs=False) - doctest_runner.globs.update(doctests.globs) - example_section_data.extend(doctest_runner.example_section_data) + if isinstance(block, doctest.Example): + doctests = doctest.DocTest([block], + globs=doctest_runner.globs, + name=qa, filename=filename, + lineno=lineno, docstring=example_code) + if exec: + doctest_runner.run(doctests, out=debugprint, clear_globs=False) + doctest_runner.globs.update(doctests.globs) + example_section_data.extend(doctest_runner.example_section_data) + else: + example_section_data.append(MText(block.source)) else: example_section_data.append(MText(block)) From dea1888dceb476b4b39f66f1cb27af4f38d161c0 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Thu, 4 Jan 2024 15:08:40 -0700 Subject: [PATCH 31/37] Don't generate text blocks for empty strings --- papyri/gen.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/papyri/gen.py b/papyri/gen.py index 61fee031..4d242654 100644 --- a/papyri/gen.py +++ b/papyri/gen.py @@ -1334,7 +1334,7 @@ def debugprint(*args): example_section_data.extend(doctest_runner.example_section_data) else: example_section_data.append(MText(block.source)) - else: + elif block: example_section_data.append(MText(block)) # TODO fix this if plt.close not called and still a lingering figure. From 708dac96d1bb1270c0160c455bb61f38ef0b00a7 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Thu, 4 Jan 2024 16:10:33 -0700 Subject: [PATCH 32/37] Fix duplicate code entries in example section --- papyri/gen.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/papyri/gen.py b/papyri/gen.py index 4d242654..0cf8477f 100644 --- a/papyri/gen.py +++ b/papyri/gen.py @@ -1058,7 +1058,7 @@ def __init__(self, *args, gen, obj, qa, config, **kwargs): self.obj = obj self.qa = qa self.config = config - self.example_section_data = Section([], None) + self._example_section_data = Section([], None) super().__init__(*args, **kwargs) import matplotlib import matplotlib.pyplot as plt @@ -1100,7 +1100,7 @@ def report_success(self, out, test, example, got): tok_entries = self._get_tok_entries(example) - self.example_section_data.append( + self._example_section_data.append( Code(tok_entries, got, ExecutionStatus.success) ) @@ -1118,7 +1118,7 @@ def report_success(self, out, test, example, got): plt.close("all") for figname, _ in figs: - self.example_section_data.append( + self._example_section_data.append( Fig( RefInfo.from_untrusted( self.gen.root, self.gen.version, "assets", figname @@ -1130,16 +1130,20 @@ def report_success(self, out, test, example, got): def report_unexpected_exception(self, out, test, example, exc_info): out(f"Unexpected exception after running example in `{self.qa}`", exc_info) tok_entries = self._get_tok_entries(example) - self.example_section_data.append( + self._example_section_data.append( Code(tok_entries, exc_info, ExecutionStatus.unexpected_exception) ) def report_failure(self, out, test, example, got): tok_entries = self._get_tok_entries(example) - self.example_section_data.append( + self._example_section_data.append( Code(tok_entries, got, ExecutionStatus.failure) ) + def get_example_section_data(self): + example_section_data = self._example_section_data + self._example_section_data = Section([], None) + return example_section_data class Gen: """ @@ -1331,7 +1335,7 @@ def debugprint(*args): if exec: doctest_runner.run(doctests, out=debugprint, clear_globs=False) doctest_runner.globs.update(doctests.globs) - example_section_data.extend(doctest_runner.example_section_data) + example_section_data.extend(doctest_runner.get_example_section_data()) else: example_section_data.append(MText(block.source)) elif block: From 96502c76a8797ea5c68eac0dbed509d711635055 Mon Sep 17 00:00:00 2001 From: Matthias Bussonnier Date: Wed, 10 Jan 2024 10:21:15 +0100 Subject: [PATCH 33/37] Fix extending figures. --- examples/pandas.toml | 3 +++ papyri/gen.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/examples/pandas.toml b/examples/pandas.toml index 3371492e..50391966 100644 --- a/examples/pandas.toml +++ b/examples/pandas.toml @@ -44,3 +44,6 @@ WrongTypeAtField = [ "pandas.core.resample:Resampler.interpolate", "pandas.core.generic:NDFrame.to_pickle", ] + +[global.implied_imports] +pd = 'pandas' diff --git a/papyri/gen.py b/papyri/gen.py index 0cf8477f..e3781393 100644 --- a/papyri/gen.py +++ b/papyri/gen.py @@ -1125,7 +1125,7 @@ def report_success(self, out, test, example, got): ) ) ) - figs.extend(figs) + self.figs.extend(figs) def report_unexpected_exception(self, out, test, example, exc_info): out(f"Unexpected exception after running example in `{self.qa}`", exc_info) From eb498cb0a33ec0fafdc6b88c588efd53384b6d56 Mon Sep 17 00:00:00 2001 From: M Bussonnier Date: Wed, 10 Jan 2024 01:24:50 -0800 Subject: [PATCH 34/37] Apply suggestions from code review --- papyri/gen.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/papyri/gen.py b/papyri/gen.py index e3781393..10f4a6a6 100644 --- a/papyri/gen.py +++ b/papyri/gen.py @@ -1332,7 +1332,7 @@ def debugprint(*args): globs=doctest_runner.globs, name=qa, filename=filename, lineno=lineno, docstring=example_code) - if exec: + if config.exec: doctest_runner.run(doctests, out=debugprint, clear_globs=False) doctest_runner.globs.update(doctests.globs) example_section_data.extend(doctest_runner.get_example_section_data()) From 4621cbaded8177b61b04cc513d05b6d908f4ac04 Mon Sep 17 00:00:00 2001 From: Matthias Bussonnier Date: Wed, 10 Jan 2024 10:32:50 +0100 Subject: [PATCH 35/37] Rename config.exec to execute_doctests. --- examples/IPython.toml | 2 +- examples/matplotlib.toml | 2 +- examples/networkx.toml | 2 +- examples/papyri.toml | 2 +- examples/scipy.toml | 2 +- papyri/gen.py | 32 +++++++++++++++++++------------- 6 files changed, 24 insertions(+), 18 deletions(-) diff --git a/examples/IPython.toml b/examples/IPython.toml index 35eb248e..fa06ab24 100644 --- a/examples/IPython.toml +++ b/examples/IPython.toml @@ -3,7 +3,7 @@ module = 'IPython' logo = "img/ipython-logo.png" exec_failure = "fallback" docs_path = "~/dev/IPython/docs/source" -exec = true +execute_doctests = true execute_exclude_patterns = [ 'IPython.lib.display:Audio', 'IPython.core.display_functions:display' diff --git a/examples/matplotlib.toml b/examples/matplotlib.toml index c530b99a..d20d199d 100644 --- a/examples/matplotlib.toml +++ b/examples/matplotlib.toml @@ -91,7 +91,7 @@ examples_exclude = [ "lines_bars_and_markers/marker_reference.py", "lines_bars_and_markers/multivariate_marker_plot.py", ] -exec = true +execute_doctests = true [global.expected_errors] IncorrectInternalDocsLen = [ diff --git a/examples/networkx.toml b/examples/networkx.toml index 6a950c35..c68e553f 100644 --- a/examples/networkx.toml +++ b/examples/networkx.toml @@ -6,7 +6,7 @@ submodules = [] # docs_path = '~/dev/numpy/doc/source' -exec = true +execute_doctests = true exclude_jedi = ['networkx.algorithms.planarity.PlanarEmbedding'] exec_failure = 'fallback' source = 'https://github.com/networkx/networkx' diff --git a/examples/papyri.toml b/examples/papyri.toml index 44f40a0b..c9885c29 100644 --- a/examples/papyri.toml +++ b/examples/papyri.toml @@ -4,7 +4,7 @@ submodules = ['examples'] examples_folder = '~/dev/papyri/docs/examples/' logo = "../papyri-logo.png" docs_path = "~/dev/papyri/docs" -exec = true +execute_doctests = true exec_failure = 'raise' exclude= ["papyri.utils:FullQual","papyri.utils:Cannonical" ] [global.directives] diff --git a/examples/scipy.toml b/examples/scipy.toml index 45ce12b5..bc54d768 100644 --- a/examples/scipy.toml +++ b/examples/scipy.toml @@ -1,7 +1,7 @@ [global] module = 'scipy' logo = "img/scipy_logo.png" -exec = true +execute_doctests = true submodules = [ "signal","misc"] exclude = [ # OTHER. diff --git a/papyri/gen.py b/papyri/gen.py index 10f4a6a6..de0e5c71 100644 --- a/papyri/gen.py +++ b/papyri/gen.py @@ -406,7 +406,6 @@ class Config: exclude: Sequence[str] = () # list of dotted object name to exclude from collection examples_folder: Optional[str] = None # < to path ? submodules: Sequence[str] = () - exec: bool = False source: Optional[str] = None homepage: Optional[str] = None docs: Optional[str] = None @@ -514,7 +513,7 @@ def gen_main( conf["fail_unseen_error"] = fail_unseen_error config = Config(**conf, dry_run=dry_run, dummy_progress=dummy_progress) if exec_ is not None: - config.exec = exec_ + config.execute_doctests = exec_ if infer is not None: config.infer = infer @@ -1145,6 +1144,7 @@ def get_example_section_data(self): self._example_section_data = Section([], None) return example_section_data + class Gen: """ Core class to generate a DocBundle for a given library. @@ -1221,7 +1221,7 @@ def filter(self, record): self._doctree: Dict[str, str] = {} def get_example_data( - self, example_section, *, obj, qa: str, config, log + self, example_section, *, obj: Any, qa: str, config: Config, log: logging.Logger ) -> Tuple[Section, List[Any]]: """Extract example section data from a NumpyDocString @@ -1243,7 +1243,7 @@ def get_example_data( have to be imported imported in docstrings. This should become a high level option at some point. Note that for method classes, the class should be made available but currently is not. - qa + qa : str The fully qualified name of current object config : Config Current configuration @@ -1328,14 +1328,20 @@ def debugprint(*args): blocks = doctest.DocTestParser().parse(example_code, name=qa) for block in blocks: if isinstance(block, doctest.Example): - doctests = doctest.DocTest([block], - globs=doctest_runner.globs, - name=qa, filename=filename, - lineno=lineno, docstring=example_code) - if config.exec: + doctests = doctest.DocTest( + [block], + globs=doctest_runner.globs, + name=qa, + filename=filename, + lineno=lineno, + docstring=example_code, + ) + if config.execute_doctests: doctest_runner.run(doctests, out=debugprint, clear_globs=False) doctest_runner.globs.update(doctests.globs) - example_section_data.extend(doctest_runner.get_example_section_data()) + example_section_data.extend( + doctest_runner.get_example_section_data() + ) else: example_section_data.append(MText(block.source)) elif block: @@ -1818,7 +1824,7 @@ def collect_examples(self, folder: Path, config): script = example.read_text() ce_status = "None" figs = [] - if config.exec: + if config.execute_doctests: with executor: try: executor.exec(script, name=str(example)) @@ -2154,8 +2160,8 @@ def collect_api_docs(self, root: str, limit_to: List[str]) -> None: continue if not isinstance(target_item, ModuleType): arbitrary = [] - ex = self.config.exec - if self.config.exec and any( + ex = self.config.execute_doctests + if self.config.execute_doctests and any( qa.startswith(pat) for pat in self.config.execute_exclude_patterns ): ex = False From 45a2fa3542ceb373da5e1a50f96be6ebcc01977e Mon Sep 17 00:00:00 2001 From: Matthias Bussonnier Date: Wed, 10 Jan 2024 10:32:50 +0100 Subject: [PATCH 36/37] Rename config.exec to execute_doctests. --- examples/numpy.toml | 2 +- examples/xarray.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/numpy.toml b/examples/numpy.toml index 264905de..b0ced366 100644 --- a/examples/numpy.toml +++ b/examples/numpy.toml @@ -63,7 +63,7 @@ submodules = [ docs_path = '~/dev/numpy/doc/source' -exec = true +execute_doctests = true narrative_exclude = [ 'doc/source/reference/arrays.ndarray.rst', 'doc/source/user/how-to-how-to.rst', diff --git a/examples/xarray.toml b/examples/xarray.toml index 0045c74a..ba3629f4 100644 --- a/examples/xarray.toml +++ b/examples/xarray.toml @@ -5,7 +5,7 @@ execute_exclude_patterns = [] submodules = [] -exec = true +execute_doctests = true exclude = [ ] exec_failure = 'fallback' From 81071b9acda4418897eded9808bbe30ef42a529c Mon Sep 17 00:00:00 2001 From: Matthias Bussonnier Date: Wed, 10 Jan 2024 10:49:38 +0100 Subject: [PATCH 37/37] rename exec in a few more places --- papyri/gen.py | 2 +- papyri/tests/test_gen.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/papyri/gen.py b/papyri/gen.py index de0e5c71..21a94c26 100644 --- a/papyri/gen.py +++ b/papyri/gen.py @@ -2172,7 +2172,7 @@ def collect_api_docs(self, root: str, limit_to: List[str]) -> None: target_item, ndoc, qa=qa, - config=self.config.replace(exec=ex), + config=self.config.replace(execute_doctests=ex), aliases=collector.aliases[qa], api_object=api_object, ) diff --git a/papyri/tests/test_gen.py b/papyri/tests/test_gen.py index 2b526434..f91b4e4f 100644 --- a/papyri/tests/test_gen.py +++ b/papyri/tests/test_gen.py @@ -22,7 +22,7 @@ def test_find_beyond_decorators(): For example the lru_decorator. """ - config = Config(exec=True, infer=True) + config = Config(execute_doctests=True, infer=True) gen = Gen(dummy_progress=True, config=config) api_object = APIObjectInfo("function", "", None, None, qa=None) @@ -88,7 +88,7 @@ def test_infer(): ], ) def test_numpy(module, submodules, objects): - config = Config(exec=False, infer=False, submodules=submodules) + config = Config(execute_doctests=False, infer=False, submodules=submodules) gen = Gen(dummy_progress=True, config=config) with tempfile.TemporaryDirectory() as tempdir: @@ -116,7 +116,7 @@ def test_numpy(module, submodules, objects): ], ) def test_numpy_2(module, submodules, objects): - config = Config(exec=False, infer=False, submodules=submodules) + config = Config(execute_doctests=False, infer=False, submodules=submodules) gen = Gen(dummy_progress=True, config=config) gen.collect_package_metadata(